diff options
Diffstat (limited to 'src/components/chat')
| -rw-r--r-- | src/components/chat/chat.js | 63 | ||||
| -rw-r--r-- | src/components/chat/chat.scss | 32 | ||||
| -rw-r--r-- | src/components/chat/chat.vue | 2 | ||||
| -rw-r--r-- | src/components/chat/chat_layout.js | 100 | ||||
| -rw-r--r-- | src/components/chat/chat_layout_utils.js | 3 |
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 } |
