diff --git a/README.md b/README.md index 2292d13..525bb3b 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,6 @@ The app loads runtime configutation from the server at "./config.json" and merge * **show_status_messages** - Whether to show only user joins/leaves and display name updates, or the full range of room status updates. Possible values are "never" (only the above), "moderators" (moderators will see all status updates) or "always" (everyone will see all status updates). Defaults to "always". * **maxSizeAutoDownloads** - Attachments smaller than this will be auto downloaded. Default is 10Mb. - ### Sticker short codes - To enable sticker short codes, follow these steps: * Run the "create sticker config" script using "npm run create-sticker-config " * Insert the resulting config blob into the "shortCodeStickers" value of the config file (assets/config.json) diff --git a/package.json b/package.json index 6018d05..30c3f5c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keanuapp-weblite", - "version": "0.1.39", + "version": "0.1.40", "private": true, "scripts": { "serve": "vue-cli-service serve", diff --git a/package.json.bak b/package.json.bak index 984a842..6018d05 100644 --- a/package.json.bak +++ b/package.json.bak @@ -1,6 +1,6 @@ { "name": "keanuapp-weblite", - "version": "0.1.38", + "version": "0.1.39", "private": true, "scripts": { "serve": "vue-cli-service serve", diff --git a/src/App.vue b/src/App.vue index 32b5f2b..0a685ba 100644 --- a/src/App.vue +++ b/src/App.vue @@ -25,6 +25,7 @@ type="list-item-avatar-two-line, divider, list-item-three-line, card-heading" v-if="showLoadingScreen" > + @@ -34,10 +35,14 @@ import stickers from "./plugins/stickers"; import { registerServiceWorker, notificationCount, windowNotificationPermission } from "./plugins/notificationAndServiceWorker.js" import logoMixin from "./components/logoMixin"; import { mapState } from 'vuex' +import UnsupportedBrowserAlert from "./components/UnsupportedBrowserAlert.vue"; export default { name: "App", mixins: [logoMixin], + components: { + UnsupportedBrowserAlert + }, data() { return { loading: true, diff --git a/src/assets/translations/en.json b/src/assets/translations/en.json index 7e4424a..1f6e8ab 100644 --- a/src/assets/translations/en.json +++ b/src/assets/translations/en.json @@ -19,7 +19,9 @@ "days": "1 day ago | {n} days ago" }, "close": "close", - "notify": "Notify" + "notify": "Notify", + "different_browser_title": "Try different browser", + "different_browser_content": "Some features may break. Copy and open link in a different browser." }, "menu": { "start_private_chat": "Direct Message with this user", @@ -142,6 +144,7 @@ "join_channel": "All set! Invite people to join you: {link}", "info_retention": "🕓 Messages sent within {time} are viewable by anyone with the link.", "info_retention_user": "🕓 Messages older than {time} will be deleted from the history.", + "info_auto_join": "Welcome to {room}.\nYou are joining as {you}.", "change": "Change" }, "new_room": { diff --git a/src/assets/translations/fi.json b/src/assets/translations/fi.json index 4f09ce2..02ae77a 100644 --- a/src/assets/translations/fi.json +++ b/src/assets/translations/fi.json @@ -174,7 +174,8 @@ "message_retention_1_day": "1 päivä", "message_retention_8_hours": "8 tuntia", "message_retention_1_hour": "1 tunti", - "read_only_room": "Lue Ainoastaan" + "read_only_room": "Lue Ainoastaan", + "moderation": "Moderointi" }, "power_level": { "restricted": "rajoitettu", diff --git a/src/assets/translations/fr.json b/src/assets/translations/fr.json index 3d3fac1..4962f73 100644 --- a/src/assets/translations/fr.json +++ b/src/assets/translations/fr.json @@ -18,7 +18,8 @@ "delete": "Supprimer", "done": "Terminé", "user_kick_and_ban": "Exclure", - "user_make_admin": "Rendre administrateur" + "user_make_admin": "Rendre administrateur", + "direct_chat": "Discussion directe" }, "language_display_name": "français", "message": { @@ -222,7 +223,8 @@ "message_retention_1_day": "1 jour", "message_retention_8_hours": "8 heures", "message_retention_1_hour": "1 heure", - "read_only_room": "Lecture seule" + "read_only_room": "Lecture seule", + "moderation": "Modération" }, "room_info_sheet": { "this_room": "Ce salon", diff --git a/src/assets/translations/it.json b/src/assets/translations/it.json index c08b927..ddf2b7a 100644 --- a/src/assets/translations/it.json +++ b/src/assets/translations/it.json @@ -125,7 +125,8 @@ "send_verification": "Invia email di verifica", "invalid_message": "Nome utente o password non validi", "accept_terms": "Accetto", - "resend_verification": "Rispedisci email di verifica" + "resend_verification": "Rispedisci email di verifica", + "token_not_valid": "Token non valido" }, "profile": { "title": "Il mio profilo", @@ -218,7 +219,8 @@ "message_retention_1_day": "1 giorno", "message_retention_8_hours": "8 ore", "message_retention_1_hour": "1 ora", - "read_only_room": "Sola lettura" + "read_only_room": "Sola lettura", + "moderation": "Moderazione" }, "voice_recorder": { "failed_to_record": "Impossibile registrare l’audio", diff --git a/src/assets/translations/pt_BR.json b/src/assets/translations/pt_BR.json index 6e39e1a..f820e40 100644 --- a/src/assets/translations/pt_BR.json +++ b/src/assets/translations/pt_BR.json @@ -155,7 +155,8 @@ "change": "Mudança", "join_channel": "Tudo pronto! Convide pessoas para se juntarem a você: {link}", "info_retention": "🕓 As mensagens enviadas dentro de {time} podem ser visualizadas por qualquer pessoa com o link.", - "info_retention_user": "As mensagens mais antigas que {time} serão excluídas do histórico." + "info_retention_user": "As mensagens mais antigas que {time} serão excluídas do histórico.", + "info_auto_join": "Bem-vindo à {room}.\nVocê está entrando como {you}." }, "new_room": { "new_room": "Nova sala", diff --git a/src/assets/translations/ru.json b/src/assets/translations/ru.json index 363aaef..45ef8e2 100644 --- a/src/assets/translations/ru.json +++ b/src/assets/translations/ru.json @@ -268,7 +268,8 @@ "info_permissions": "Вы можете в любой момент изменить \"разрешение на присоединение\" в настройках комнаты.", "info": "Добро пожаловать! Вот несколько вещей, которые нужно знать о вашей комнате:", "direct_private_chat": "Личное сообщение", - "info_retention_user": "🕓 Сообщения старше {time} будут удалены из истории." + "info_retention_user": "🕓 Сообщения старше {time} будут удалены из истории.", + "info_auto_join": "Добро пожаловать в {room}.\nВы присоединяетесь как {you}." }, "device_list": { "blocked": "Заблокированный", diff --git a/src/assets/translations/zh_Hans.json b/src/assets/translations/zh_Hans.json index 135e33e..b931c95 100644 --- a/src/assets/translations/zh_Hans.json +++ b/src/assets/translations/zh_Hans.json @@ -49,7 +49,8 @@ "message_retention_8_hours": "8 小时", "message_retention_1_hour": "1 小时", "message_retention_2_week": "2周", - "message_retention_1_week": "1周" + "message_retention_1_week": "1周", + "moderation": "调节面板" }, "leave": { "leave": "离开", diff --git a/src/components/Chat.vue b/src/components/Chat.vue index 133caa5..b165bcb 100644 --- a/src/components/Chat.vue +++ b/src/components/Chat.vue @@ -37,7 +37,6 @@
- +
@@ -479,6 +478,7 @@ export default { /** If we just created this room, show a small welcome header with info */ hideRoomWelcomeHeader: false, + newlyJoinedRoom: false, /** An array of recent emojis. Used in the "message operations" popup. */ recentEmojis: [], @@ -831,6 +831,7 @@ export default { this.typingMembers = []; this.initialLoadDone = false; this.hideRoomWelcomeHeader = false; + this.newlyJoinedRoom = false; // Stop RR timer this.stopRRTimer(); @@ -862,8 +863,8 @@ export default { this.onRoomJoined(this.readMarker); } }, - showMessageOperations() { - if (this.showMessageOperations) { + showMessageOperations(show) { + if (show) { this.$nextTick(() => { // Calculate where to show the context menu. // @@ -958,6 +959,16 @@ export default { }, onRoomJoined(initialEventId) { + // If our own join event is less than a minute old, consider this a "newly joined" room. + // + // Previously tried to look at initialEventId, but it seems like "this.room.getEventReadUpTo(this.$matrix.currentUserId, false)" + // always returns an event id? Strange. I would expect it to be null on a fresh room. + // + const joinEvent = this.room && this.room.currentState.getStateEvents("m.room.member", this.$matrix.currentUserId); + if (joinEvent) { + this.newlyJoinedRoom = joinEvent.getLocalAge() < 1 * 60000 /* 1 minute */; + } + // Listen to events this.$matrix.on("Room.timeline", this.onEvent); this.$matrix.on("RoomMember.typing", this.onUserTyping); @@ -1044,7 +1055,7 @@ export default { this.$navigation.push( { name: "Join", - params: { roomId: util.sanitizeRoomId(this.roomAliasOrId) }, + params: { roomId: util.sanitizeRoomId(this.roomAliasOrId), join: this.$route.params.join }, }, 0 ); @@ -1740,10 +1751,17 @@ export default { showContextMenuForEvent(e) { const event = e.event; - this.selectedEvent = event; - this.updateRecentEmojis(); - this.showContextMenu = !this.showContextMenu; - this.showContextMenuAnchor = e.anchor; + if (this.selectedEvent == event) { + this.showContextMenu = !this.showContextMenu; + } else { + this.showContextMenu = false; + this.$nextTick(() => { + this.selectedEvent = event; + this.updateRecentEmojis(); + this.showContextMenu = true; + this.showContextMenuAnchor = e.anchor; + }) + } }, showAvatarMenuForEvent(e) { @@ -1761,6 +1779,7 @@ export default { if (this.showContextMenu) { this.showContextMenu = false; this.showContextMenuAnchor = null; + this.selectedEvent = null; e.preventDefault(); } }, diff --git a/src/components/CreateRoom.vue b/src/components/CreateRoom.vue index 7835d2d..662a00b 100644 --- a/src/components/CreateRoom.vue +++ b/src/components/CreateRoom.vue @@ -105,6 +105,9 @@ + + + @@ -112,9 +115,11 @@
{{ $t("join.choose_name") }}
+ :value="selectedProfile"> @@ -308,7 +306,14 @@ export default { return roomName ? roomName : ""; }, getRoomInfo() { - if (this.roomId.startsWith("#")) { + if (this.$route.params.join) { + // Auto-join room + this.waitingForRoomCreation = true; + this.$nextTick(() => { + this.handleJoin(); + }); + } + else if (this.roomId.startsWith("#")) { this.$matrix .getPublicRoomInfo(this.roomId) .then((room) => { diff --git a/src/components/UnsupportedBrowserAlert.vue b/src/components/UnsupportedBrowserAlert.vue new file mode 100644 index 0000000..e8b3b71 --- /dev/null +++ b/src/components/UnsupportedBrowserAlert.vue @@ -0,0 +1,53 @@ + + + \ No newline at end of file diff --git a/src/components/roomInfoMixin.js b/src/components/roomInfoMixin.js index b8796a6..a8752c9 100644 --- a/src/components/roomInfoMixin.js +++ b/src/components/roomInfoMixin.js @@ -1,4 +1,4 @@ -import utils from "../plugins/utils"; +import utils, { ROOM_TYPE_CHANNEL } from "../plugins/utils"; import roomTypeMixin from "./roomTypeMixin"; export default { @@ -96,7 +96,8 @@ export default { this.room.getCanonicalAlias(), this.room.roomId, this.room.name, - utils.roomDisplayTypeToQueryParam(this.room, this.roomDisplayType) + utils.roomDisplayTypeToQueryParam(this.room, this.roomDisplayType), + this.roomDisplayType == ROOM_TYPE_CHANNEL /* Auto join for channels */ ); } return null; diff --git a/src/components/welcome_headers/WelcomeHeaderChannel.vue b/src/components/welcome_headers/WelcomeHeaderChannel.vue index ae913ad..6d6c163 100644 --- a/src/components/welcome_headers/WelcomeHeaderChannel.vue +++ b/src/components/welcome_headers/WelcomeHeaderChannel.vue @@ -20,12 +20,8 @@ - -
-
diff --git a/src/components/welcome_headers/WelcomeHeaderChannelUser.vue b/src/components/welcome_headers/WelcomeHeaderChannelUser.vue index 30af81a..be9257b 100644 --- a/src/components/welcome_headers/WelcomeHeaderChannelUser.vue +++ b/src/components/welcome_headers/WelcomeHeaderChannelUser.vue @@ -1,5 +1,11 @@