diff --git a/src/assets/css/_variables.scss b/src/assets/css/_variables.scss index fad1a0c..d0f2e67 100644 --- a/src/assets/css/_variables.scss +++ b/src/assets/css/_variables.scss @@ -15,5 +15,5 @@ $chat-button-height: 50px; $voice-recorder-color: #6f6f6f; $voice-recording-color: red; $voice-recorded-color: #3ae17d; - -$poll-hilite-color: $very-very-purple; \ No newline at end of file +$poll-hilite-color: #6360f0; +$poll-hilite-color-bg: #d6d5fc; \ No newline at end of file diff --git a/src/assets/css/chat.scss b/src/assets/css/chat.scss index 7d8282e..4034b3f 100644 --- a/src/assets/css/chat.scss +++ b/src/assets/css/chat.scss @@ -611,6 +611,10 @@ $admin-fg: white; box-shadow: 4px 4px 8px rgba(0,0,0,0.15); } +.send-options { + z-index: 11; // Above mic button +} + .message-operations-picker { background-color: white; text-align: center; diff --git a/src/assets/css/components/_poll.scss b/src/assets/css/components/_poll.scss index f809e9b..948bb65 100644 --- a/src/assets/css/components/_poll.scss +++ b/src/assets/css/components/_poll.scss @@ -2,19 +2,57 @@ width: 70%; } +.poll-bubble { + color: black; + padding: $chat-standard-padding-s !important; + font-family: "Inter", sans-serif; + font-size: 16 * $chat-text-size; + line-height: 16 * $chat-text-size; +} + +.from-admin .poll-bubble { + color: rgba(white, 0.9); +} + +.poll-icon { + path { + fill: currentColor; + } +} + +.poll-check-icon { + width: 14.18px; + height: 12px; +} + +.poll-question { + font-weight: 700; + margin-top: $chat-standard-padding-xs; + margin-bottom: $chat-standard-padding-s; +} + .poll-answer { - border: 1px solid #666; - border-radius: 5px; - padding: 10px; - margin: 10px; + border: 1px solid currentColor; + border-radius: 4px; + padding: 15px 14px; + margin: 0px; + &.winner { + font-weight: 700; + } &.selected { border: 1px solid $poll-hilite-color; + background-color: $poll-hilite-color-bg; + color: #1d1d1d; + font-weight: 700; + } + &.result { + border: none; + padding: 15px 0px; } .poll-answer-title { - color: #444; } .poll-answer-num-votes { - font-size: 0.7rem; + font-size: 0.75rem; } justify-content: space-between; position: relative; @@ -23,32 +61,49 @@ .poll-percent-indicator { position: absolute; bottom: 2px; - left: 2px; - right: 2px; - height: 4px; + left: 0px; + right: 0px; + height: 8px; + margin-top: 4px; .bar { - background-color: $poll-hilite-color; + background-color: #7e7cf8; position: absolute; bottom: 0px; left: 0px; top: 0px; - border-radius: 3px; + border-radius: 4px; } } .poll-status { - margin: 10px; justify-content: space-between; + font-size: 13px; + line-height: 117%; + margin: 0px; .poll-status-title { - font-size: 0.7rem; } .poll-status-close { - font-size: 0.7rem; color: $poll-hilite-color; } } +.poll-submit { + .v-btn { + font-family: "Inter", sans-serif; + font-weight: 700; + font-size: 11 * $chat-text-size; + color: white; + text-transform: uppercase; + background-color: $poll-hilite-color !important; + border: 1px solid black; + border-radius: 21px !important; + height: 42px !important; + margin-top: $chat-standard-padding-xs; + margin-bottom: $chat-standard-padding-xs; + } +} + // Creation dialog // .poll-create-dialog-content { diff --git a/src/assets/icons/ic_check.svg b/src/assets/icons/ic_check.svg new file mode 100644 index 0000000..229191f --- /dev/null +++ b/src/assets/icons/ic_check.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/ic_poll.svg b/src/assets/icons/ic_poll.svg new file mode 100644 index 0000000..4e711d5 --- /dev/null +++ b/src/assets/icons/ic_poll.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/translations/en.json b/src/assets/translations/en.json index 50ef128..b2ced95 100644 --- a/src/assets/translations/en.json +++ b/src/assets/translations/en.json @@ -254,6 +254,8 @@ "poll_status_disclosed": "Results will be shown when poll is closed.", "poll_status_open": "Poll is open", "poll_status_open_not_voted": "Poll is open - vote to see the results", - "close_poll": "Close poll" + "close_poll": "Close poll", + "poll_submit": "Submit", + "num_answered": "{count} have answered" } } diff --git a/src/components/Chat.vue b/src/components/Chat.vue index d4fbad3..af7f2f4 100644 --- a/src/components/Chat.vue +++ b/src/components/Chat.vue @@ -1,12 +1,9 @@ @@ -532,20 +443,18 @@ function ScrollPosition(node) { this.readyFor = "up"; } -ScrollPosition.prototype.restore = function () { +ScrollPosition.prototype.restore = function() { if (this.readyFor === "up") { - this.node.scrollTop = - this.node.scrollHeight - this.previousScrollHeightMinusTop; + this.node.scrollTop = this.node.scrollHeight - this.previousScrollHeightMinusTop; } else { this.node.scrollTop = this.previousScrollTop; } }; -ScrollPosition.prototype.prepareFor = function (direction) { +ScrollPosition.prototype.prepareFor = function(direction) { this.readyFor = direction || "up"; if (this.readyFor === "up") { - this.previousScrollHeightMinusTop = - this.node.scrollHeight - this.node.scrollTop; + this.previousScrollHeightMinusTop = this.node.scrollHeight - this.node.scrollTop; } else { this.previousScrollTop = this.node.scrollTop; } @@ -592,7 +501,7 @@ export default { StickerPickerBottomSheet, BottomSheet, AvatarOperations, - CreatePollDialog + CreatePollDialog, }, data() { @@ -662,12 +571,12 @@ export default { filters: { latestReply(contents) { - const contentArr = contents.split('\n').reverse(); - if (contentArr[0] === '') { + const contentArr = contents.split("\n").reverse(); + if (contentArr[0] === "") { contentArr.shift(); } - return contentArr[0].replace(/^> (<.*> )?/g, ''); - } + return contentArr[0].replace(/^> (<.*> )?/g, ""); + }, }, mounted() { @@ -714,10 +623,7 @@ export default { // If we have sent a RR, use that as read marker (so we don't have to wait for server round trip) return this.lastRR.getId(); } - return ( - this.fullyReadMarker || - this.room.getEventReadUpTo(this.$matrix.currentUserId, false) - ); + return this.fullyReadMarker || this.room.getEventReadUpTo(this.$matrix.currentUserId, false); }, fullyReadMarker() { const readEvent = this.room.getAccountData("m.fully_read"); @@ -727,11 +633,7 @@ export default { return null; }, attachButtonDisabled() { - return ( - this.editedEvent != null || - this.replyToEvent != null || - this.currentInput.length > 0 - ); + return this.editedEvent != null || this.replyToEvent != null || this.currentInput.length > 0; }, sendButtonDisabled() { return this.currentInput.length == 0; @@ -760,8 +662,7 @@ export default { if (ref && ref[0]) { if (this.showAvatarMenuAnchor) { var rectAnchor = this.showAvatarMenuAnchor.getBoundingClientRect(); - var rectChat = - this.$refs.avatarOperationsStrut.getBoundingClientRect(); + var rectChat = this.$refs.avatarOperationsStrut.getBoundingClientRect(); top = rectAnchor.top - rectChat.top; left = rectAnchor.left - rectChat.left; // if (left + 250 > rectChat.right) { @@ -783,9 +684,10 @@ export default { canCreatePoll() { // We say that if you can redact events, you are allowed to create polls. const me = this.room && this.room.getMember(this.$matrix.currentUserId); - let isAdmin = me && this.room.currentState && this.room.currentState.hasSufficientPowerLevelFor("redact", me.powerLevel); + let isAdmin = + me && this.room.currentState && this.room.currentState.hasSufficientPowerLevelFor("redact", me.powerLevel); return isAdmin; - } + }, }, watch: { @@ -795,9 +697,7 @@ export default { if (value && value == oldValue) { return; // No change. } - console.log( - "Chat: Current room changed to " + (value ? value : "null") - ); + console.log("Chat: Current room changed to " + (value ? value : "null")); // Clear old events this.$matrix.off("Room.timeline", this.onEvent); @@ -839,18 +739,14 @@ export default { this.$nextTick(() => { // Calculate where to show the context menu. // - const ref = - this.selectedEvent && this.$refs[this.selectedEvent.getId()]; + const ref = this.selectedEvent && this.$refs[this.selectedEvent.getId()]; var top = 0; var left = 0; if (ref && ref[0]) { if (this.showContextMenuAnchor) { - var rectAnchor = - this.showContextMenuAnchor.getBoundingClientRect(); - var rectChat = - this.$refs.messageOperationsStrut.getBoundingClientRect(); - var rectOps = - this.$refs.messageOperations.$el.getBoundingClientRect(); + var rectAnchor = this.showContextMenuAnchor.getBoundingClientRect(); + var rectChat = this.$refs.messageOperationsStrut.getBoundingClientRect(); + var rectOps = this.$refs.messageOperations.$el.getBoundingClientRect(); top = rectAnchor.top - rectChat.top - 50; left = rectAnchor.left - rectChat.left - 50; if (left + rectOps.width >= rectChat.right) { @@ -868,16 +764,10 @@ export default { onRoomJoined(initialEventId) { // Was this room just created (by you)? Show a small info header in // that case! - const createEvent = this.room.currentState.getStateEvents( - "m.room.create", - "" - ); + const createEvent = this.room.currentState.getStateEvents("m.room.create", ""); if (createEvent) { const creatorId = createEvent.getContent().creator; - if ( - creatorId == this.$matrix.currentUserId && - createEvent.getLocalAge() < 5 * 60000 /* 5 minutes */ - ) { + if (creatorId == this.$matrix.currentUserId && createEvent.getLocalAge() < 5 * 60000 /* 5 minutes */) { this.showCreatedRoomWelcomeHeader = true; } } @@ -890,11 +780,7 @@ export default { //initialEventId = null; this.timelineSet = this.room.getUnfilteredTimelineSet(); - this.timelineWindow = new TimelineWindow( - this.$matrix.matrixClient, - this.timelineSet, - {} - ); + this.timelineWindow = new TimelineWindow(this.$matrix.matrixClient, this.timelineSet, {}); const self = this; this.timelineWindow .load(initialEventId, 20) @@ -904,21 +790,18 @@ export default { const getMoreIfNeeded = function _getMoreIfNeeded() { const container = self.$refs.chatContainer; if ( - container.scrollHeight <= - (1 + 2 * WINDOW_BUFFER_SIZE) * container.clientHeight && + container.scrollHeight <= (1 + 2 * WINDOW_BUFFER_SIZE) * container.clientHeight && self.timelineWindow && self.timelineWindow.canPaginate(EventTimeline.BACKWARDS) ) { - return self.timelineWindow - .paginate(EventTimeline.BACKWARDS, 10, true, 5) - .then((success) => { - self.events = self.timelineWindow.getEvents(); - if (success) { - return _getMoreIfNeeded.call(self); - } else { - return Promise.reject("Failed to paginate"); - } - }); + return self.timelineWindow.paginate(EventTimeline.BACKWARDS, 10, true, 5).then((success) => { + self.events = self.timelineWindow.getEvents(); + if (success) { + return _getMoreIfNeeded.call(self); + } else { + return Promise.reject("Failed to paginate"); + } + }); } else { return Promise.resolve("Done"); } @@ -967,18 +850,11 @@ export default { }, scrollToEndOfTimeline() { - if ( - this.timelineWindow && - this.timelineWindow.canPaginate(EventTimeline.FORWARDS) - ) { + if (this.timelineWindow && this.timelineWindow.canPaginate(EventTimeline.FORWARDS)) { this.loading = true; // Instead of paging though ALL history, just reload a timeline at the live marker... var timelineSet = this.room.getUnfilteredTimelineSet(); - var timelineWindow = new TimelineWindow( - this.$matrix.matrixClient, - timelineSet, - {} - ); + var timelineWindow = new TimelineWindow(this.$matrix.matrixClient, timelineSet, {}); const self = this; timelineWindow .load(null, 20) @@ -1062,10 +938,7 @@ export default { switch (event.getType()) { case "m.room.member": if (event.getContent().membership == "join") { - if ( - event.getPrevContent() && - event.getPrevContent().membership == "join" - ) { + if (event.getPrevContent() && event.getPrevContent().membership == "join") { // We we already joined, so this must be a display name and/or avatar update! return ContactChanged; } else { @@ -1163,14 +1036,8 @@ export default { case "im.keanu.room_deletion_notice": { // Custom event for notice 30 seconds before a room is deleted/purged. - const deletionNotices = this.room.currentState.getStateEvents( - "im.keanu.room_deletion_notice" - ); - if ( - deletionNotices && - deletionNotices.length > 0 && - deletionNotices[deletionNotices.length - 1] == event - ) { + const deletionNotices = this.room.currentState.getStateEvents("im.keanu.room_deletion_notice"); + if (deletionNotices && deletionNotices.length > 0 && deletionNotices[deletionNotices.length - 1] == event) { // This is the latest/last one. Look at the status flag. Show nothing if it is "cancel". if (event.getContent().status != "cancel") { return RoomDeletionNotice; @@ -1198,19 +1065,12 @@ export default { if (container.scrollTop <= bufferHeight) { // Scrolled to top this.handleScrolledToTop(); - } else if ( - container.scrollHeight - - container.scrollTop.toFixed(0) - - container.clientHeight <= - bufferHeight - ) { + } else if (container.scrollHeight - container.scrollTop.toFixed(0) - container.clientHeight <= bufferHeight) { this.handleScrolledToBottom(false); } this.showScrollToEnd = - container.scrollHeight - container.scrollTop.toFixed(0) > - container.clientHeight || - (this.timelineWindow && - this.timelineWindow.canPaginate(EventTimeline.FORWARDS)); + container.scrollHeight - container.scrollTop.toFixed(0) > container.clientHeight || + (this.timelineWindow && this.timelineWindow.canPaginate(EventTimeline.FORWARDS)); this.restartRRTimer(); }, @@ -1229,10 +1089,7 @@ export default { // If we are at bottom, scroll to see new events... const container = this.$refs.chatContainer; var scrollToSeeNew = event.getSender() == this.$matrix.currentUserId; // When we sent, scroll - if ( - container.scrollHeight - container.scrollTop.toFixed(0) == - container.clientHeight - ) { + if (container.scrollHeight - container.scrollTop.toFixed(0) == container.clientHeight) { scrollToSeeNew = true; } if (this.initialLoadDone && event.forwardLooking && !event.isRelation()) { @@ -1270,13 +1127,7 @@ export default { sendMessage(text) { if (text && text.length > 0) { util - .sendTextMessage( - this.$matrix.matrixClient, - this.roomId, - text, - this.editedEvent, - this.replyToEvent - ) + .sendTextMessage(this.$matrix.matrixClient, this.roomId, text, this.editedEvent, this.replyToEvent) .then(() => { console.log("Sent message"); }) @@ -1308,9 +1159,7 @@ export default { dimensions: null, }; try { - this.currentImageInput.dimensions = sizeOf( - dataUriToBuffer(e.target.result) - ); + this.currentImageInput.dimensions = sizeOf(dataUriToBuffer(e.target.result)); // Need to resize? const w = this.currentImageInput.dimensions.width; @@ -1318,9 +1167,7 @@ export default { if (w > 640 || h > 640) { var aspect = w / h; var newWidth = parseInt((w > h ? 640 : 640 * aspect).toFixed()); - var newHeight = parseInt( - (w > h ? 640 / aspect : 640).toFixed() - ); + var newHeight = parseInt((w > h ? 640 / aspect : 640).toFixed()); var imageResize = new ImageResize({ format: "png", width: newWidth, @@ -1366,10 +1213,10 @@ export default { onUploadProgress(p) { if (p.total) { - this.currentSendProgress = this.$t( - "message.upload_progress_with_total", - { count: p.loaded || 0, total: p.total } - ); + this.currentSendProgress = this.$t("message.upload_progress_with_total", { + count: p.loaded || 0, + total: p.total, + }); } else { this.currentSendProgress = this.$t("message.upload_progress", { count: p.loaded || 0, @@ -1381,11 +1228,7 @@ export default { this.$refs.attachment.value = null; if (this.currentImageInputPath) { var inputFile = this.currentImageInputPath; - if ( - this.currentImageInput && - this.currentImageInput.scaled && - this.currentImageInput.useScaled - ) { + if (this.currentImageInput && this.currentImageInput.scaled && this.currentImageInput.useScaled) { // Send scaled version of image instead! inputFile = this.currentImageInput.scaled; } @@ -1497,7 +1340,7 @@ export default { }, smoothScrollToEnd() { - this.$nextTick(function () { + this.$nextTick(function() { const container = this.$refs.chatContainer; if (container.children.length > 0) { const lastChild = container.children[container.children.length - 1]; @@ -1532,19 +1375,19 @@ export default { setReplyToImage(event) { util - .getThumbnail(this.$matrix.matrixClient, event) - .then((url) => { - this.replyToImg = url; - }) - .catch((err) => { - console.log("Failed to fetch thumbnail: ", err); - }); + .getThumbnail(this.$matrix.matrixClient, event) + .then((url) => { + this.replyToImg = url; + }) + .catch((err) => { + console.log("Failed to fetch thumbnail: ", err); + }); }, addReply(event) { this.replyToEvent = event; this.$refs.messageInput.focus(); - this.replyToContentType= event.getContent().msgtype; + this.replyToContentType = event.getContent().msgtype; this.setReplyToImage(event); }, @@ -1576,10 +1419,10 @@ export default { document.body.appendChild(link); link.click(); - setTimeout(function(){ + setTimeout(function() { document.body.removeChild(link); URL.revokeObjectURL(url); - }, 200) + }, 200); }) .catch((err) => { console.log("Failed to fetch attachment: ", err); @@ -1604,12 +1447,7 @@ export default { sendQuickReaction(e) { util - .sendQuickReaction( - this.$matrix.matrixClient, - this.roomId, - e.reaction, - e.event - ) + .sendQuickReaction(this.$matrix.matrixClient, this.roomId, e.reaction, e.event) .then(() => { console.log("Quick reaction message"); }) @@ -1651,9 +1489,7 @@ export default { { name: "Chat", params: { - roomId: util.sanitizeRoomId( - room.getCanonicalAlias() || room.roomId - ), + roomId: util.sanitizeRoomId(room.getCanonicalAlias() || room.roomId), }, }, -1 @@ -1729,10 +1565,7 @@ export default { this.$matrix.matrixClient .sendReadReceipt(event) .then(() => { - this.$matrix.matrixClient.setRoomReadMarkers( - this.room.roomId, - event.getId() - ); + this.$matrix.matrixClient.setRoomReadMarkers(this.room.roomId, event.getId()); }) .then(() => { console.log("RR sent for event: " + event.getId()); @@ -1830,11 +1663,7 @@ export default { onHeaderClick() { const joinedRooms = this.$matrix.joinedRooms; - if ( - joinedRooms && - joinedRooms.length == 1 && - joinedRooms[0].roomId == this.room.roomId - ) { + if (joinedRooms && joinedRooms.length == 1 && joinedRooms[0].roomId == this.room.roomId) { // Only joined to this room, go directly to room details! this.$navigation.push({ name: "RoomInfo" }); return; @@ -1842,7 +1671,7 @@ export default { this.$refs.roomInfoSheet.open(); }, onInvitationsClick() { - this.$navigation.push({ name: "Home" }, -1); + this.$navigation.push({ name: "Home" }, -1); }, }, }; @@ -1850,4 +1679,4 @@ export default { \ No newline at end of file + diff --git a/src/components/ChatHeader.vue b/src/components/ChatHeader.vue index 6bb07e3..79cfb91 100644 --- a/src/components/ChatHeader.vue +++ b/src/components/ChatHeader.vue @@ -7,7 +7,7 @@ @click.stop="onHeaderClicked" > - + diff --git a/src/components/RoomList.vue b/src/components/RoomList.vue index b78de3a..5010a7c 100644 --- a/src/components/RoomList.vue +++ b/src/components/RoomList.vue @@ -18,7 +18,10 @@ :value="room.roomId" > - + + {{ + room.name.substring(0, 1).toUpperCase() + }} {{ room.name }} @@ -50,7 +53,10 @@ :value="room.roomId" > - + + {{ + room.name.substring(0, 1).toUpperCase() + }}
{{ notificationCount(room) }} diff --git a/src/components/messages/MessageIncomingPoll.vue b/src/components/messages/MessageIncomingPoll.vue index 8b43d4c..2b956a3 100644 --- a/src/components/messages/MessageIncomingPoll.vue +++ b/src/components/messages/MessageIncomingPoll.vue @@ -1,27 +1,60 @@