aboutsummaryrefslogtreecommitdiff
path: root/src/components/chat
diff options
context:
space:
mode:
authoreugenijm <eugenijm@protonmail.com>2020-06-21 17:13:29 +0300
committereugenijm <eugenijm@protonmail.com>2020-07-08 15:21:31 +0300
commitf05f832bff58034d78de9478ae2dbb06284dea75 (patch)
treee41f122ada5957618ea96e2b3206977ba8438fd8 /src/components/chat
parentaa2cf51c05ebdf411d74af5debbbc8fa4d3cf457 (diff)
Address feedback
Use more specific css rules for the emoji dimensions in the chat list status preview. Use more round em value for chat list item height. Add global html overflow and height for smoother chat navigation in the desktop Safari. Use offsetHeight instad of a computed style when setting the window height on resize. Remove margin-bottom from the last message to avoid occasional layout shift in the desktop Safari Use break-word to prevent chat message text overflow Resize and scroll the textarea when inserting a new line on ctrl+enter Remove fade transition on route change Ensure proper border radius at the bottom of the chat, remove unused border-radius Prevent the chat header "jumping" on the avatar load.
Diffstat (limited to 'src/components/chat')
-rw-r--r--src/components/chat/chat.js63
-rw-r--r--src/components/chat/chat.scss32
-rw-r--r--src/components/chat/chat.vue2
-rw-r--r--src/components/chat/chat_layout.js100
-rw-r--r--src/components/chat/chat_layout_utils.js3
5 files changed, 65 insertions, 135 deletions
diff --git a/src/components/chat/chat.js b/src/components/chat/chat.js
index 6e23c20c..9c4e5b05 100644
--- a/src/components/chat/chat.js
+++ b/src/components/chat/chat.js
@@ -2,29 +2,26 @@ import _ from 'lodash'
import { WSConnectionStatus } from '../../services/api/api.service.js'
import { mapGetters, mapState } from 'vuex'
import ChatMessage from '../chat_message/chat_message.vue'
-import ChatAvatar from '../chat_avatar/chat_avatar.vue'
import PostStatusForm from '../post_status_form/post_status_form.vue'
import ChatTitle from '../chat_title/chat_title.vue'
import chatService from '../../services/chat_service/chat_service.js'
-import ChatLayout from './chat_layout.js'
import { getScrollPosition, getNewTopPosition, isBottomedOut, scrollableContainerHeight } from './chat_layout_utils.js'
const BOTTOMED_OUT_OFFSET = 10
const JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET = 150
+const SAFE_RESIZE_TIME_OFFSET = 100
const Chat = {
components: {
ChatMessage,
ChatTitle,
- ChatAvatar,
PostStatusForm
},
- mixins: [ChatLayout],
data () {
return {
jumpToBottomButtonVisible: false,
hoveredMessageChainId: undefined,
- scrollPositionBeforeResize: {},
+ lastScrollPosition: {},
scrollableContainerHeight: '100%',
errorLoadingChat: false
}
@@ -119,6 +116,7 @@ const Chat = {
},
onFilesDropped () {
this.$nextTick(() => {
+ this.handleResize()
this.updateScrollableContainerHeight()
})
},
@@ -129,13 +127,30 @@ const Chat = {
}
})
},
- handleLayoutChange () {
- this.updateScrollableContainerHeight()
- if (this.mobileLayout) {
- this.setMobileChatLayout()
- } else {
- this.unsetMobileChatLayout()
+ setChatLayout () {
+ // This is a hacky way to adjust the global layout to the mobile chat (without modifying the rest of the app).
+ // This layout prevents empty spaces from being visible at the bottom
+ // of the chat on iOS Safari (`safe-area-inset`) when
+ // - the on-screen keyboard appears and the user starts typing
+ // - the user selects the text inside the input area
+ // - the user selects and deletes the text that is multiple lines long
+ // TODO: unify the chat layout with the global layout.
+ let html = document.querySelector('html')
+ if (html) {
+ html.classList.add('chat-layout')
}
+
+ this.$nextTick(() => {
+ this.updateScrollableContainerHeight()
+ })
+ },
+ unsetChatLayout () {
+ let html = document.querySelector('html')
+ if (html) {
+ html.classList.remove('chat-layout')
+ }
+ },
+ handleLayoutChange () {
this.$nextTick(() => {
this.updateScrollableContainerHeight()
this.scrollDown()
@@ -149,15 +164,24 @@ const Chat = {
this.scrollableContainerHeight = scrollableContainerHeight(inner, header, footer) + 'px'
},
// Preserves the scroll position when OSK appears or the posting form changes its height.
- handleResize (opts) {
+ handleResize (opts = {}) {
+ const { expand = false, delayed = false } = opts
+
+ if (delayed) {
+ setTimeout(() => {
+ this.handleResize({ ...opts, delayed: false })
+ }, SAFE_RESIZE_TIME_OFFSET)
+ return
+ }
+
this.$nextTick(() => {
this.updateScrollableContainerHeight()
- const { offsetHeight = undefined } = this.scrollPositionBeforeResize
- this.scrollPositionBeforeResize = getScrollPosition(this.$refs.scrollable)
+ const { offsetHeight = undefined } = this.lastScrollPosition
+ this.lastScrollPosition = getScrollPosition(this.$refs.scrollable)
- const diff = this.scrollPositionBeforeResize.offsetHeight - offsetHeight
- if (diff < 0 || (!this.bottomedOut() && opts && opts.expand)) {
+ const diff = this.lastScrollPosition.offsetHeight - offsetHeight
+ if (diff < 0 || (!this.bottomedOut() && expand)) {
this.$nextTick(() => {
this.updateScrollableContainerHeight()
this.$refs.scrollable.scrollTo({
@@ -281,7 +305,12 @@ const Chat = {
.then(data => {
this.$store.dispatch('addChatMessages', { chatId: this.currentChat.id, messages: [data] }).then(() => {
this.$nextTick(() => {
- this.updateScrollableContainerHeight()
+ this.handleResize()
+ // When the posting form size changes because of a media attachment, we need an extra resize
+ // to account for the potential delay in the DOM update.
+ setTimeout(() => {
+ this.updateScrollableContainerHeight()
+ }, SAFE_RESIZE_TIME_OFFSET)
this.scrollDown({ forceRead: true })
})
})
diff --git a/src/components/chat/chat.scss b/src/components/chat/chat.scss
index 13c52ea3..6ae7ebc9 100644
--- a/src/components/chat/chat.scss
+++ b/src/components/chat/chat.scss
@@ -3,14 +3,17 @@
height: calc(100vh - 60px);
width: 100%;
+ .chat-title {
+ // prevents chat header jumping on when the user avatar loads
+ height: 28px;
+ }
+
.chat-view-inner {
height: auto;
width: 100%;
overflow: visible;
display: flex;
- margin-top: 0.5em;
- margin-left: 0.5em;
- margin-right: 0.5em;
+ margin: 0.5em 0.5em 0 0.5em;
}
.chat-view-body {
@@ -19,23 +22,18 @@
flex-direction: column;
width: 100%;
overflow: visible;
- border-radius: none;
min-height: 100%;
- margin-left: 0;
- margin-right: 0;
- margin-bottom: 0em;
- margin-top: 0em;
+ margin: 0 0 0 0;
border-radius: 10px 10px 0 0;
border-radius: var(--panelRadius, 10px) var(--panelRadius, 10px) 0 0 ;
&::after {
- border-radius: none;
- box-shadow: none;
+ border-radius: 0;
}
}
.scrollable-message-list {
- padding: 0 10px;
+ padding: 0 0.8em;
height: 100%;
overflow-y: scroll;
overflow-x: hidden;
@@ -45,7 +43,7 @@
.footer {
position: sticky;
- bottom: 0px;
+ bottom: 0;
}
.chat-view-heading {
@@ -54,15 +52,19 @@
top: 50px;
display: flex;
z-index: 2;
- border-radius: none;
position: sticky;
display: flex;
overflow: hidden;
}
.go-back-button {
- margin-right: 1.2em;
cursor: pointer;
+ margin-right: 1.4em;
+
+ i {
+ display: flex;
+ align-items: center;
+ }
}
.jump-to-bottom-button {
@@ -135,7 +137,7 @@
overflow: hidden;
height: 100%;
margin: 0;
- border-radius: 0 !important;
+ border-radius: 0;
}
.chat-view-heading {
diff --git a/src/components/chat/chat.vue b/src/components/chat/chat.vue
index d8c91dbe..62b72e14 100644
--- a/src/components/chat/chat.vue
+++ b/src/components/chat/chat.vue
@@ -75,7 +75,7 @@
:disable-polls="true"
:disable-sensitivity-checkbox="true"
:disable-submit="errorLoadingChat || !currentChat"
- :request="sendMessage"
+ :post-handler="sendMessage"
:submit-on-enter="!mobileLayout"
:preserve-focus="!mobileLayout"
:auto-focus="!mobileLayout"
diff --git a/src/components/chat/chat_layout.js b/src/components/chat/chat_layout.js
deleted file mode 100644
index 07ae3abf..00000000
--- a/src/components/chat/chat_layout.js
+++ /dev/null
@@ -1,100 +0,0 @@
-const ChatLayout = {
- methods: {
- setChatLayout () {
- if (this.mobileLayout) {
- this.setMobileChatLayout()
- }
- },
- unsetChatLayout () {
- this.unsetMobileChatLayout()
- },
- setMobileChatLayout () {
- // This is a hacky way to adjust the global layout to the mobile chat (without modifying the rest of the app).
- // This layout prevents empty spaces from being visible at the bottom
- // of the chat on iOS Safari (`safe-area-inset`) when
- // - the on-screen keyboard appears and the user starts typing
- // - the user selects the text inside the input area
- // - the user selects and deletes the text that is multiple lines long
- // TODO: unify the chat layout with the global layout.
-
- let html = document.querySelector('html')
- if (html) {
- html.style.overflow = 'hidden'
- html.style.height = '100%'
- }
-
- let body = document.querySelector('body')
- if (body) {
- body.style.height = '100%'
- }
-
- let app = document.getElementById('app')
- if (app) {
- app.style.height = '100%'
- app.style.overflow = 'hidden'
- app.style.minHeight = 'auto'
- }
-
- let appBgWrapper = window.document.getElementById('app_bg_wrapper')
- if (appBgWrapper) {
- appBgWrapper.style.overflow = 'hidden'
- }
-
- let main = document.getElementsByClassName('main')[0]
- if (main) {
- main.style.overflow = 'hidden'
- main.style.height = '100%'
- }
-
- let content = document.getElementById('content')
- if (content) {
- content.style.paddingTop = '0'
- content.style.height = '100%'
- content.style.overflow = 'visible'
- }
-
- this.$nextTick(() => {
- this.updateScrollableContainerHeight()
- })
- },
- unsetMobileChatLayout () {
- let html = document.querySelector('html')
- if (html) {
- html.style.overflow = 'visible'
- html.style.height = 'unset'
- }
-
- let body = document.querySelector('body')
- if (body) {
- body.style.height = 'unset'
- }
-
- let app = document.getElementById('app')
- if (app) {
- app.style.height = '100%'
- app.style.overflow = 'visible'
- app.style.minHeight = '100vh'
- }
-
- let appBgWrapper = document.getElementById('app_bg_wrapper')
- if (appBgWrapper) {
- appBgWrapper.style.overflow = 'visible'
- }
-
- let main = document.getElementsByClassName('main')[0]
- if (main) {
- main.style.overflow = 'visible'
- main.style.height = 'unset'
- }
-
- let content = document.getElementById('content')
- if (content) {
- content.style.paddingTop = '60px'
- content.style.height = 'unset'
- content.style.overflow = 'unset'
- }
- }
- }
-}
-
-export default ChatLayout
diff --git a/src/components/chat/chat_layout_utils.js b/src/components/chat/chat_layout_utils.js
index f07ba2a1..609dc0c9 100644
--- a/src/components/chat/chat_layout_utils.js
+++ b/src/components/chat/chat_layout_utils.js
@@ -22,6 +22,5 @@ export const isBottomedOut = (el, offset = 0) => {
// Height of the scrollable container. The dynamic height is needed to ensure the mobile browser panel doesn't overlap or hide the posting form.
export const scrollableContainerHeight = (inner, header, footer) => {
- const height = parseFloat(getComputedStyle(inner, null).height.replace('px', ''))
- return height - header.clientHeight - footer.clientHeight
+ return inner.offsetHeight - header.clientHeight - footer.clientHeight
}