From a700e199908a667dd9920eba7da58c324f98c19d Mon Sep 17 00:00:00 2001 From: N Pex Date: Tue, 14 Jan 2025 11:14:11 +0000 Subject: [PATCH] Updated New Room Page --- README.md | 3 + src/assets/css/_variables.scss | 1 + src/assets/css/chat.scss | 64 +- src/assets/css/create.scss | 71 +- src/assets/css/typography.scss | 76 +++ src/assets/icons/ic_camera.vue | 17 + src/assets/icons/room_avatar_placeholder.vue | 12 + src/assets/icons/room_type_channel.vue | 21 + src/assets/icons/room_type_filedrop.vue | 10 + src/assets/icons/room_type_room.vue | 9 + src/assets/translations/en.json | 41 +- src/components/Chat.vue | 13 +- src/components/Create.vue | 135 ++++ src/components/CreateChannel.vue | 286 +++----- src/components/CreateFileDrop.vue | 195 ++++++ src/components/CreateRoom.vue | 613 +++++------------- src/components/GetLink.vue | 26 +- src/components/Home.vue | 5 +- src/components/MessageRetentionDialog.vue | 47 +- src/components/YouAre.vue | 2 +- src/components/create/CreateRoomAvatar.vue | 78 +++ src/components/createRoomMixin.js | 155 +++++ src/components/file_mode/FileDropLayout.vue | 5 +- .../messages/MessageIncomingThread.vue | 4 +- src/components/roomInfoMixin.js | 33 +- src/components/roomRetentionMixin.js | 48 ++ src/components/sendAttachmentsMixin.js | 1 - src/router/index.js | 37 +- src/services/config.service.js | 10 + src/services/matrix.service.js | 8 + 30 files changed, 1263 insertions(+), 763 deletions(-) create mode 100644 src/assets/css/typography.scss create mode 100644 src/assets/icons/ic_camera.vue create mode 100644 src/assets/icons/room_avatar_placeholder.vue create mode 100644 src/assets/icons/room_type_channel.vue create mode 100644 src/assets/icons/room_type_filedrop.vue create mode 100644 src/assets/icons/room_type_room.vue create mode 100644 src/components/Create.vue create mode 100644 src/components/CreateFileDrop.vue create mode 100644 src/components/create/CreateRoomAvatar.vue create mode 100644 src/components/createRoomMixin.js create mode 100644 src/components/roomRetentionMixin.js diff --git a/README.md b/README.md index 525bb3b..1eb2c6e 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,9 @@ 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. +* **roomTypes** - Available room types. This affects what is shown on the /create route, as well as access to routes /createroom, /createchannel and /createfiledrop. It should be an array with possible values "group_chat", "channel" or "file_drop". + + Defaults to all values, ["group_chat", "channel", "file_drop"]. ### 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/css/_variables.scss b/src/assets/css/_variables.scss index 88fa7b5..50c55e4 100644 --- a/src/assets/css/_variables.scss +++ b/src/assets/css/_variables.scss @@ -4,6 +4,7 @@ $app-background: #f6f6f6; $main-desktop-width: 900px; $dialog-desktop-width: 940px; $very-very-purple: #536dfe; +$light-purple: #8E9FF8; $lighter-gray: #FDFBF9; $chat-background: $background; diff --git a/src/assets/css/chat.scss b/src/assets/css/chat.scss index c355851..9a501df 100644 --- a/src/assets/css/chat.scss +++ b/src/assets/css/chat.scss @@ -1066,7 +1066,7 @@ body { .option-text { font-size: 13 * $chat-text-size; - border-top: 1px solid #e1e1e1; + // border-top: 1px solid #e1e1e1; padding-top: 8px; } @@ -1075,6 +1075,17 @@ body { font-size: 16 * $chat-text-size; } +.back-button { + font-weight: bold; + font-size: 12 * $chat-text-size; + [dir="rtl"] & { + .v-icon { + // Mirror the icon + transform: scale(-1, 1); + } + } +} + .header-button-left { position: absolute; top: 0px; @@ -1281,57 +1292,6 @@ body { } } -.create-room { - background-color: $background; - - .v-avatar { - border: 1px solid #808080 !important; - } - - .section { - background-color: white; - border-radius: 20px; - padding: 20px; - border: 1px solid #808080 !important; - position: relative; - } - - .options { - display: flex; - width: 100%; - justify-content: flex-end; - color: black; - font-size: 14 * $chat-text-size; - font-weight: bold; - margin-left: 10px; - [dir="rtl"] & { - margin-left: initial; - margin-right: 10px; - } - text-transform: none !important; - } - - .room-option { - .v-input { - margin: 0px; - } - } - - .option-warning { - background: linear-gradient(0deg, #FFF3F3, #FFF3F3), #FFFBED; - border-radius: 8px; - padding: 18px; - font-family: 'Inter', sans-serif; - font-style: normal; - font-weight: 400; - font-size: 14px; - line-height: 17px; - .v-icon { - margin-right: 16px; - } - } -} - .room-link .v-input__slot::before { /* Remove text underline */ border-style: none !important; diff --git a/src/assets/css/create.scss b/src/assets/css/create.scss index 5e696f8..aab863d 100644 --- a/src/assets/css/create.scss +++ b/src/assets/css/create.scss @@ -1,8 +1,64 @@ @import "@/assets/css/main.scss"; + .create-root { background-color: $background; + user-select: none; + .v-avatar { + border: 1px solid #808080 !important; + } + + .section { + background-color: white; + border-radius: 20px; + padding: 20px; + border: 1px solid #808080 !important; + position: relative; + } + + .options { + display: flex; + width: 100%; + justify-content: flex-end; + color: black; + font-size: 14 * $chat-text-size; + font-weight: bold; + margin-left: 10px; + [dir="rtl"] & { + margin-left: initial; + margin-right: 10px; + } + text-transform: none !important; + } + + .room-option { + .v-input { + margin: 0px; + } + } + + .option-warning { + background: linear-gradient(0deg, #FFF3F3, #FFF3F3), #FFFBED; + border-radius: 8px; + padding: 18px; + font-family: 'Inter', sans-serif; + font-style: normal; + font-weight: 400; + font-size: 14px; + line-height: 17px; + .v-icon { + margin-right: 16px; + } + } + + + /* Make secondary inputs (e.g. topic) */ + .v-input.v-text-field--filled.v-text-field--rounded .v-input__slot { + background-color: #f5f5f5; + border-radius: 4px; + } + .create-loggedin { text-align: center; button { @@ -34,15 +90,12 @@ .create-title { color: #000; - text-align: center; - font-family: "Poppins"; - font-size: 28px; - font-style: normal; - font-weight: 700; - line-height: 108.5%; /* 30.38px */ letter-spacing: -0.8px; white-space: pre-line; margin-top: 50px; + &.no-icon { + margin-top: 122px; + } } .create-info { @@ -135,4 +188,10 @@ flex-direction: column; margin-top: -20px; } + + .v-btn:not(.back-button) { + border-radius: 24px; + min-height: 48px !important; + min-width: 200px !important; + } } diff --git a/src/assets/css/typography.scss b/src/assets/css/typography.scss new file mode 100644 index 0000000..050656f --- /dev/null +++ b/src/assets/css/typography.scss @@ -0,0 +1,76 @@ +@import "@/assets/css/main.scss"; + +.h3-bold { + font-family: "Poppins"; + font-style: normal; + font-size: 24 * $chat-text-size; + font-weight: 700; + line-height: 28.8 * $chat-text-size; + text-align: center; + text-underline-position: from-font; + text-decoration-skip-ink: none; +} + +.paragraph { + font-family: "Inter"; + font-size: 16 * $chat-text-size; + font-weight: 400; + line-height: 18.72 * $chat-text-size; + letter-spacing: 0.40 * $chat-text-size; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: rgba(0,0,0, 0.7); + &.paragraph-button { + color: $very-very-purple; + } +} + +.paragraph-bold { + font-family: "Inter"; + font-size: 16 * $chat-text-size; + font-weight: 600; + line-height: 18.72 * $chat-text-size; + letter-spacing: 0.40 * $chat-text-size; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: rgba(0,0,0); + margin-bottom: 3 * $chat-text-size; +} + +.caption-1 { + font-family: "Inter"; + font-size: 14 * $chat-text-size; + font-weight: 400; + line-height: 16.38 * $chat-text-size; + letter-spacing: 0.40 * $chat-text-size; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: rgba(0,0,0,0.65); +} + +.caption-1-gray { + font-family: "Inter"; + font-size: 14 * $chat-text-size; + font-weight: 400; + line-height: 16.38 * $chat-text-size; + letter-spacing: 0.40 * $chat-text-size; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: rgba(0,0,0,0.65); +} + +.caption-2 { + font-family: "Inter"; + font-size: 10 * $chat-text-size; + font-weight: 400; + line-height: 11.7 * $chat-text-size; + letter-spacing: 0.40 * $chat-text-size; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + color: rgba(0,0,0,0.60); +} diff --git a/src/assets/icons/ic_camera.vue b/src/assets/icons/ic_camera.vue new file mode 100644 index 0000000..34c6516 --- /dev/null +++ b/src/assets/icons/ic_camera.vue @@ -0,0 +1,17 @@ + \ No newline at end of file diff --git a/src/assets/icons/room_avatar_placeholder.vue b/src/assets/icons/room_avatar_placeholder.vue new file mode 100644 index 0000000..9e11596 --- /dev/null +++ b/src/assets/icons/room_avatar_placeholder.vue @@ -0,0 +1,12 @@ + diff --git a/src/assets/icons/room_type_channel.vue b/src/assets/icons/room_type_channel.vue new file mode 100644 index 0000000..8a6fb84 --- /dev/null +++ b/src/assets/icons/room_type_channel.vue @@ -0,0 +1,21 @@ + \ No newline at end of file diff --git a/src/assets/icons/room_type_filedrop.vue b/src/assets/icons/room_type_filedrop.vue new file mode 100644 index 0000000..d5aaa5a --- /dev/null +++ b/src/assets/icons/room_type_filedrop.vue @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/src/assets/icons/room_type_room.vue b/src/assets/icons/room_type_room.vue new file mode 100644 index 0000000..639751b --- /dev/null +++ b/src/assets/icons/room_type_room.vue @@ -0,0 +1,9 @@ + diff --git a/src/assets/translations/en.json b/src/assets/translations/en.json index 2bf8494..55dc05b 100644 --- a/src/assets/translations/en.json +++ b/src/assets/translations/en.json @@ -150,11 +150,12 @@ "change": "Change" }, "new_room": { + "title": "New Group Chat", "new_room": "New Room", "create": "Create", "next": "Next", "name_room": "Room name", - "room_topic": "Add a description if you like", + "room_topic": "Add a description if you'd like", "join_permissions": "Join permissions", "set_join_permissions": "Set Join Permissions", "join_permissions_info": "These permissions determine how people can join the room and how easily others can be invited. They can be changed anytime.", @@ -166,8 +167,7 @@ "invite_info": "Only people added", "invite_description": "Choose from a list or search by account ID", "status_creating": "Creating room", - "status_avatar_total": "Uploading avatar: {count} of {total}", - "status_avatar": "Uploading avatar: {count}", + "status_avatar": "Uploading avatar", "room_name_limit_error_msg": "Maximum 50 characters allowed", "colon_not_allowed": "Colon is not allowed", "options": "Options" @@ -212,13 +212,32 @@ "share_qr": "Share QR", "qr_image_copied": "Image copied to clipboard" }, + "create": { + "title": "Choose an experience", + "room_type_room_name": "Group Chat", + "room_type_room_description": "Connect a group of people", + "room_type_channel_name": "Channel", + "room_type_channel_description": "Broadcast news and ideas", + "room_type_filedrop_name": "File Drop", + "room_type_filedrop_description": "Receive files and tips", + "field_required": "This field is required", + "topic_too_long": "Maximum 500 characters allowed" + }, "createchannel": { "title": "Create a Channel", "info": "Broadcast news or knowledge in any format—video, podcast, text, pictures or PDFs.", "channel_name": "Name your channel", "channel_topic": "Describe it", - "name_required": "Channel name is required", - "error_channel": "Failed to create channel" + "channel_topic_label": "Your description will be displayed when people join your channel.", + "error_channel": "Failed to create channel", + "status_creating": "Creating channel" + }, + "createfiledrop": { + "title": "Create a File Drop", + "info": "File drops are a safe space to receive files from anyone.", + "filedrop_name": "Name your file drop", + "error_filedrop": "Failed to create file drop", + "status_creating": "Creating file drop" }, "profile": { "title": "My Profile", @@ -331,8 +350,9 @@ "download_chat": "Download chat", "read_only_room": "Read Only", "read_only_room_info": "Only admins and moderators are allowed to send to the room.", - "message_retention": "Message History", - "message_retention_info": "Messages sent within this time frame are viewable by anyone with the link.", + "message_retention": "Limit History", + "message_retention_info": "Set a limit for how long to keep messages", + "limit_history_info": "Keep messages for {period}", "message_retention_none": "Off", "message_retention_4_week": "4 weeks", "message_retention_2_week": "2 weeks", @@ -340,12 +360,13 @@ "message_retention_1_day": "1 day", "message_retention_8_hours": "8 hours", "message_retention_1_hour": "1 Hour", - "make_public": "Make Public", - "make_public_warning": "warning: Full message history will be visible to new participants", "direct_link": "My Direct Link", "direct_link_desc": "It's ready to share! A new direct room will open each time someone opens the link.", "shared_room_number": "You share {count} rooms with {name}", - "shared_room_number_more": "You share more than {count} rooms with {name}" + "shared_room_number_more": "You share more than {count} rooms with {name}", + "message_history": "Message History", + "message_history_info": "Allow people to see messages sent before they joined", + "message_history_warning": "warning: Full message history will be visible to new participants" }, "room_info_sheet": { "this_room": "This room", diff --git a/src/components/Chat.vue b/src/components/Chat.vue index e11bd68..46b6aba 100644 --- a/src/components/Chat.vue +++ b/src/components/Chat.vue @@ -30,6 +30,7 @@ v-on:remove-file="currentFileInputs.splice($event, 1)" v-on:reset="resetAttachments" :attachments="currentFileInputs" + v-on:close="closeFileMode" />
{ + this.sendingAttachments = []; this.currentFileInputs = null; this.attachmentCaption = undefined; this.sendingStatus = this.sendStatuses.INITIAL; @@ -2113,6 +2114,14 @@ export default { } } } + }, + + closeFileMode() { + this.resetAttachments(); + this.$matrix.leaveRoomAndNavigate(this.room.roomId) + .catch((err) => { + console.log("Error leaving", err); + }); } }, }; diff --git a/src/components/Create.vue b/src/components/Create.vue new file mode 100644 index 0000000..4c71e98 --- /dev/null +++ b/src/components/Create.vue @@ -0,0 +1,135 @@ + + + + + \ No newline at end of file diff --git a/src/components/CreateChannel.vue b/src/components/CreateChannel.vue index 4a42063..dd24da6 100644 --- a/src/components/CreateChannel.vue +++ b/src/components/CreateChannel.vue @@ -1,39 +1,46 @@ @@ -47,14 +54,19 @@ import util, { ROOM_TYPE_CHANNEL } from "../plugins/utils"; import YouAre from "../components/YouAre.vue"; import User from "../models/user"; import { CHANNEL_POWER_LEVELS } from "../services/matrix.service"; +import createRoomMixin from "./createRoomMixin"; +import CreateRoomAvatar from "./create/CreateRoomAvatar.vue"; export default { name: "CreateChannel", - mixins: [rememberMeMixin, logoMixin], - components: { InteractiveAuth, YouAre }, + mixins: [rememberMeMixin, logoMixin, createRoomMixin], + components: { InteractiveAuth, YouAre, CreateRoomAvatar }, data() { return this.defaultData(); }, + mounted() { + this.$store.commit("setCurrentRoomId", null); + }, computed: { loggedIn() { return this.$store.state.auth.status.loggedIn; @@ -63,53 +75,80 @@ export default { return this.$store.state.auth.user; }, }, - watch: { - }, methods: { defaultData() { return { - roomName: "", - roomTopic: "", - isValid: false, - loading: false, - message: null, currentLoginServer: "", loadingLoginFlows: false, loginFlows: null, showQRCopiedToast: false, - roomAvatar: null, - roomAvatarFile: null, }; }, - goHome() { - this.$navigation.push({ name: "Home" }, -1); - }, goToLoginPage() { - this.$navigation.push({ name: "Login", params: { showCreateRoomOption: false, redirect: "GetLink" } }, 1); + this.$navigation.push({ name: "Login", params: { showCreateRoomOption: false, redirect: "CreateChannel" } }, 1); }, - handleNext() { + preCreateRoom() { const prefix = this.$config.userIdPrefix; const user = new User(util.randomUser(prefix), util.randomPass(), false); - - // Reset errors - this.message = null; - this.loading = true; - const login = this.loggedIn ? Promise.resolve(this.currentUser) : this.loadLoginFlows().then(() => { return this.$store.dispatch("createUser", { user, registrationFlowHandler: this.$refs.interactiveAuth.registrationFlowHandler }) }); - login - .then( - (ignoreduser) => { - return this.createRoom(); - }) - .catch( - (ignorederror) => { - this.message = this.$t("createchannel.error_channel"); - }) - .finally(() => { - this.loading = false; - }) + return login; + }, + generateAliasForRoom() { + return true; + }, + getRoomCreationOptions() { + const createRoomOptions = { + visibility: "private", // Not listed! + name: this.roomName, + preset: "public_chat", + initial_state: + [ + { + type: "m.room.history_visibility", + state_key: "", + content: { + history_visibility: "shared" + } + }, + { + type: "m.room.retention", + state_key: "", + content: { + max_lifetime: 3600 * 24 * 7 * 1000 + } + } + ] + }; + if (this.roomTopic && this.roomTopic.length > 0) { + // Add topic + createRoomOptions.topic = this.roomTopic; + } + + createRoomOptions.creation_content = { + type: ROOM_TYPE_CHANNEL + } + + // Set power level event. Need to do that here, because we might not have the userId when the options object is created. + const powerLevels = {}; + powerLevels[this.$matrix.currentUserId] = 100; + let powerLevelContent = { + users: powerLevels, + events_default: 50, + state_default: 50, + events: CHANNEL_POWER_LEVELS + } + createRoomOptions.initial_state.push( + { + type: "m.room.power_levels", + state_key: "", + content: powerLevelContent + }); + return createRoomOptions; + }, + handleNext() { + this.createRoom(this.$t("createchannel.status_creating"), this.$t("createchannel.error_channel")); }, loadLoginFlows() { var user = Object.assign({}, this.user); @@ -138,138 +177,11 @@ export default { supportedLoginFlow(flow) { return ["m.login.password"].includes(flow.type); }, - createRoom() { - const createRoomOptions = { - visibility: "private", // Not listed! - name: this.roomName, - preset: "public_chat", - initial_state: - [ - // { - // type: "m.room.encryption", - // state_key: "", - // content: { - // algorithm: "m.megolm.v1.aes-sha2", - // }, - // }, - { - type: "m.room.history_visibility", - state_key: "", - content: { - history_visibility: "shared" - } - }, - { - type: "m.room.retention", - state_key: "", - content: { - max_lifetime: 3600 * 24 * 7 * 1000 - } - } - ] - }; - if (this.roomTopic && this.roomTopic.length > 0) { - // Add topic - createRoomOptions.topic = this.roomTopic; - } - - createRoomOptions.creation_content = { - type: ROOM_TYPE_CHANNEL - } - - let roomId = ""; - - return util - .getUniqueAliasForRoomName( - this.$matrix.matrixClient, - this.roomName, - this.$matrix.currentUserMXDomain - ) - .then((alias) => { - createRoomOptions.room_alias_name = alias; - }) - .then(() => { - - // Set power level event. Need to do that here, because we might not have the userId when the options object is created. - const powerLevels = {}; - powerLevels[this.$matrix.currentUserId] = 100; - let powerLevelContent = { - users: powerLevels, - events_default: 50, - state_default: 50, - events: CHANNEL_POWER_LEVELS - } - createRoomOptions.initial_state.push( - { - type: "m.room.power_levels", - state_key: "", - content: powerLevelContent - }); - - return this.$matrix.matrixClient - .createRoom(createRoomOptions) - .then(({ room_id, room_alias }) => { - roomId = room_alias || room_id; - if (!this.roomAvatarFile) { - return true; - } - const self = this; - return util.setRoomAvatar( - this.$matrix.matrixClient, - room_id, - this.roomAvatarFile, - (p) => { - if (p.total) { - self.status = this.$t("new_room.status_avatar_total", { - count: p.loaded || 0, - total: p.total, - }); - } else { - self.status = this.$t("new_room.status_avatar", { - count: p.loaded || 0, - }); - } - } - ); - }); - }) - .then(() => { - this.$navigation.push( - { - name: "Chat", - params: { roomId: util.sanitizeRoomId(roomId) }, - }, - -1 - ); - }) - }, - - /** - * Show picker to select room avatar file - */ - showAvatarPicker() { - this.$refs.avatar.click(); - }, - - /** - * Handle picked avatar - */ - handlePickedAvatar(event) { - if (event.target.files && event.target.files[0]) { - var reader = new FileReader(); - reader.onload = (e) => { - this.roomAvatar = e.target.result; - this.roomAvatarFile = event.target.files[0]; - }; - reader.readAsDataURL(event.target.files[0]); - } - }, - - }, }; \ No newline at end of file diff --git a/src/components/CreateFileDrop.vue b/src/components/CreateFileDrop.vue new file mode 100644 index 0000000..4185dbf --- /dev/null +++ b/src/components/CreateFileDrop.vue @@ -0,0 +1,195 @@ + + + + + \ No newline at end of file diff --git a/src/components/CreateRoom.vue b/src/components/CreateRoom.vue index 662a00b..1722f82 100644 --- a/src/components/CreateRoom.vue +++ b/src/components/CreateRoom.vue @@ -1,112 +1,71 @@
- {{ $t("getlink.continue") }} + {{ $t("getlink.continue") }} {{ $t("getlink.different_link") }}
@@ -243,5 +248,6 @@ export default { \ No newline at end of file diff --git a/src/components/Home.vue b/src/components/Home.vue index 4a98061..adbee17 100644 --- a/src/components/Home.vue +++ b/src/components/Home.vue @@ -1,6 +1,9 @@