From 340bc37374448f8eb9d22b97bdc8199908a076d5 Mon Sep 17 00:00:00 2001 From: N-Pex Date: Wed, 10 Jul 2024 14:51:27 +0200 Subject: [PATCH 01/18] When deleting room, set "join_rules" to "private". On user side, if we have an invite to a room that turns "private", reject the invite (via leave) --- src/services/matrix.service.js | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/services/matrix.service.js b/src/services/matrix.service.js index 102df72..e4bba6d 100644 --- a/src/services/matrix.service.js +++ b/src/services/matrix.service.js @@ -406,6 +406,18 @@ export default { } break; + case "m.room.join_rules": + { + const room = this.matrixClient.getRoom(event.getRoomId()); + if (room && room.getJoinRule() == "private" && room.selfMembership == "invite") { + // We have an invite to a room that's now "private"? This is most probably a deleted DM room. + // Reject the invite, i.e. call "leave" on it. + console.error("Room made private while we have an invite! Leave..."); + this.matrixClient.leave(room.roomId); + } + } + break; + case STATE_EVENT_ROOM_DELETED: { const room = this.matrixClient.getRoom(event.getRoomId()); @@ -494,7 +506,7 @@ export default { // each time! var updatedRooms = this.matrixClient.getVisibleRooms(); updatedRooms = updatedRooms.filter((room) => { - return room.selfMembership && (room.selfMembership == "invite" || room.selfMembership == "join"); + return room.selfMembership && (room.selfMembership == "invite" || room.selfMembership == "join") && room.currentState.getStateEvents(STATE_EVENT_ROOM_DELETED).length == 0; }); updatedRooms.forEach((room) => { if (!room.avatar) { @@ -580,7 +592,7 @@ export default { }, leaveRoom(roomId) { - return this.matrixClient.leave(roomId, undefined).then(() => { + return this.matrixClient.leave(roomId).then(() => { this.$store.commit("setCurrentRoomId", null); this.rooms = this.rooms.filter((room) => { room.roomId != roomId; @@ -805,7 +817,7 @@ export default { //console.log("Purge: set invite only"); statusCallback(this.$t("room.purge_set_room_state")); - withRetry(() => this.matrixClient.sendStateEvent(roomId, "m.room.join_rules", { join_rule: "invite" }, "")) + withRetry(() => this.matrixClient.sendStateEvent(roomId, "m.room.join_rules", { join_rule: "private" }, "")) .then(() => { //console.log("Purge: forbid guest access"); return withRetry(() => this.matrixClient.sendStateEvent( @@ -887,6 +899,8 @@ export default { var invited = room.getMembersWithMembership("invite"); var allMembers = joined.concat(invited); + const me = allMembers.find((m) => m.userId == self.currentUserId); + const kickFirstMember = (members) => { //console.log(`Kicking ${members.length} members`); statusCallback( @@ -904,7 +918,16 @@ export default { } else { // Slight pause to avoid rate limiting. return sleep(0.1) - .then(() => withRetry(() => this.matrixClient.kick(roomId, member.userId, "Room Deleted"))) + .then(() => withRetry(() => { + if (member.membership == "invite" && me && me.powerLevel <= member.powerLevel) { + // The user is invited, but we can't kick them because of power levels. + // Send a new invite with reason set to "Room Deleted". + // The client will be sent stripped room state, and can from that see the + // join_rule of "private". It will then "leave", i.e. reject the invite. + return this.matrixClient.invite(roomId, member.userId, "Room Deleted"); + } + return this.matrixClient.kick(roomId, member.userId, "Room Deleted") + })) .then(() => kickFirstMember(members.slice(1))); } }; From fd01a95a65c27cddede3ed8581beeaf2eddc7fb2 Mon Sep 17 00:00:00 2001 From: N-Pex Date: Wed, 10 Jul 2024 14:56:45 +0200 Subject: [PATCH 02/18] Remove debug message --- src/services/matrix.service.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/services/matrix.service.js b/src/services/matrix.service.js index e4bba6d..0ac52aa 100644 --- a/src/services/matrix.service.js +++ b/src/services/matrix.service.js @@ -412,7 +412,6 @@ export default { if (room && room.getJoinRule() == "private" && room.selfMembership == "invite") { // We have an invite to a room that's now "private"? This is most probably a deleted DM room. // Reject the invite, i.e. call "leave" on it. - console.error("Room made private while we have an invite! Leave..."); this.matrixClient.leave(room.roomId); } } From 809151f9d94d480e17a30c48c36af05a7a766641 Mon Sep 17 00:00:00 2001 From: Weblate Translation Memory Date: Thu, 18 Jul 2024 00:23:49 +0000 Subject: [PATCH 03/18] Translated using Weblate (Chinese (Simplified)) Currently translated at 93.8% (352 of 375 strings) Translation: Guardian Project/Keanu Weblite Translate-URL: https://hosted.weblate.org/projects/guardianproject/keanu-weblite/zh_Hans/ --- src/assets/translations/zh_Hans.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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": "离开", From 6203af822121e1c03649ef4e0d4f631babb9a188 Mon Sep 17 00:00:00 2001 From: Weblate Translation Memory Date: Thu, 18 Jul 2024 00:16:49 +0000 Subject: [PATCH 04/18] Translated using Weblate (French) Currently translated at 62.1% (233 of 375 strings) Translation: Guardian Project/Keanu Weblite Translate-URL: https://hosted.weblate.org/projects/guardianproject/keanu-weblite/fr/ --- src/assets/translations/fr.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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", From 8ff2e46646ade92285c7cff7af6c0250984850dc Mon Sep 17 00:00:00 2001 From: Weblate Translation Memory Date: Wed, 17 Jul 2024 23:56:17 +0000 Subject: [PATCH 05/18] Translated using Weblate (Italian) Currently translated at 60.5% (227 of 375 strings) Translation: Guardian Project/Keanu Weblite Translate-URL: https://hosted.weblate.org/projects/guardianproject/keanu-weblite/it/ --- src/assets/translations/it.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/assets/translations/it.json b/src/assets/translations/it.json index c08b927..91289aa 100644 --- a/src/assets/translations/it.json +++ b/src/assets/translations/it.json @@ -218,7 +218,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", From 6c0f0ac11b1abd6009c61a897b864a749ebf679a Mon Sep 17 00:00:00 2001 From: Weblate Translation Memory Date: Thu, 18 Jul 2024 00:21:13 +0000 Subject: [PATCH 06/18] Translated using Weblate (Finnish) Currently translated at 45.0% (169 of 375 strings) Translation: Guardian Project/Keanu Weblite Translate-URL: https://hosted.weblate.org/projects/guardianproject/keanu-weblite/fi/ --- src/assets/translations/fi.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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", From 1998bf2b64ff484c4b6927b15443c4eca9087725 Mon Sep 17 00:00:00 2001 From: N-Pex Date: Fri, 19 Jul 2024 10:50:34 +0200 Subject: [PATCH 07/18] Select whole name on focus when choosing identity --- src/components/CreateRoom.vue | 26 +++++++++++++++++++++++--- src/components/Join.vue | 10 ++++------ 2 files changed, 27 insertions(+), 9 deletions(-) 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"> From b9acda11573c4e1d2e09346862a892496b77bc62 Mon Sep 17 00:00:00 2001 From: N-Pex Date: Fri, 19 Jul 2024 14:38:06 +0200 Subject: [PATCH 08/18] Allow three dot menu to be opened on another message... without closing the first. --- src/components/Chat.vue | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/components/Chat.vue b/src/components/Chat.vue index 133caa5..0396bad 100644 --- a/src/components/Chat.vue +++ b/src/components/Chat.vue @@ -1740,10 +1740,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 +1768,7 @@ export default { if (this.showContextMenu) { this.showContextMenu = false; this.showContextMenuAnchor = null; + this.selectedEvent = null; e.preventDefault(); } }, From 66ea1c4fdc5179f74ea4ac9ac2e12d5b362886ee Mon Sep 17 00:00:00 2001 From: N-Pex Date: Mon, 22 Jul 2024 10:38:07 +0200 Subject: [PATCH 09/18] Add "auto_join_rooms" flag --- README.md | 2 +- src/assets/translations/en.json | 1 + src/components/Join.vue | 9 ++++++++- src/components/messages/ContactJoin.vue | 23 ++++++++++++++++++++++- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2292d13..04bb34e 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ The app loads runtime configutation from the server at "./config.json" and merge * **accentColor** - The accent color of the app UI. Use a HTML-style color value string, like "#ff0080". * **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. - +* **auto_join_rooms** - Whether to show the normal "join" screen or just auto join the user to the room link. Default to false. ### Sticker short codes - To enable sticker short codes, follow these steps: * Run the "create sticker config" script using "npm run create-sticker-config " diff --git a/src/assets/translations/en.json b/src/assets/translations/en.json index 7e4424a..50c591e 100644 --- a/src/assets/translations/en.json +++ b/src/assets/translations/en.json @@ -142,6 +142,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/components/Join.vue b/src/components/Join.vue index 938952b..7bf3e52 100644 --- a/src/components/Join.vue +++ b/src/components/Join.vue @@ -306,7 +306,14 @@ export default { return roomName ? roomName : ""; }, getRoomInfo() { - if (this.roomId.startsWith("#")) { + if (this.$config.auto_join_rooms) { + // 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/messages/ContactJoin.vue b/src/components/messages/ContactJoin.vue index d8aa6d8..ec89512 100644 --- a/src/components/messages/ContactJoin.vue +++ b/src/components/messages/ContactJoin.vue @@ -1,6 +1,14 @@ @@ -10,6 +18,19 @@ import messageMixin from "./messageMixin"; export default { mixins: [messageMixin], + methods: { + viewProfile() { + this.$navigation.push({ name: "Profile" }, 1); + }, + roomCreatedByYou() { + const createEvent = this.room && this.room.currentState.getStateEvents("m.room.create", ""); + if (createEvent) { + const creatorId = createEvent.getContent().creator; + return (creatorId == this.$matrix.currentUserId); + } + return false; + }, + } }; From 56a50452595df27d01fbc8ffe6400268165f10f5 Mon Sep 17 00:00:00 2001 From: N-Pex Date: Mon, 22 Jul 2024 10:39:53 +0200 Subject: [PATCH 10/18] Tweak context menu --- src/components/Chat.vue | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/Chat.vue b/src/components/Chat.vue index 0396bad..0fa3de7 100644 --- a/src/components/Chat.vue +++ b/src/components/Chat.vue @@ -37,7 +37,6 @@
- +
@@ -478,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: [], @@ -830,6 +831,7 @@ export default { this.typingMembers = []; this.initialLoadDone = false; this.hideRoomWelcomeHeader = false; + this.newlyJoinedRoom = false; // Stop RR timer this.stopRRTimer(); @@ -957,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); 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..943f0e0 100644 --- a/src/components/welcome_headers/WelcomeHeaderChannelUser.vue +++ b/src/components/welcome_headers/WelcomeHeaderChannelUser.vue @@ -1,5 +1,11 @@