diff options
author | Joas Schilling <213943+nickvergessen@users.noreply.github.com> | 2020-01-09 10:00:35 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-09 10:00:35 +0300 |
commit | 7be3922f21af8eebdbee9a51f9e6c7f395a6e7f5 (patch) | |
tree | 34e309cf8c08ff1b9fdaaea9972d9be90a2ba2a2 /src | |
parent | a1ade6a43b479da389452af9d5b811937e3b80eb (diff) | |
parent | 9168c903d05f8b61fdbedbd77c79346b8ad09c2e (diff) |
Merge pull request #2692 from nextcloud/bugfix/2681-2595/scrolling
Fix scrolling issues
Diffstat (limited to 'src')
-rw-r--r-- | src/App.vue | 6 | ||||
-rw-r--r-- | src/components/MessagesList/MessagesList.vue | 44 | ||||
-rw-r--r-- | src/main.js | 2 | ||||
-rw-r--r-- | src/store/index.js | 2 | ||||
-rw-r--r-- | src/store/windowVisibilityStore.js | 58 |
5 files changed, 102 insertions, 10 deletions
diff --git a/src/App.vue b/src/App.vue index 1762f272f..e3b155a28 100644 --- a/src/App.vue +++ b/src/App.vue @@ -59,11 +59,13 @@ export default { savedLastMessageMap: {}, defaultPageTitle: false, loading: false, - windowIsVisible: true, } }, computed: { + windowIsVisible() { + return this.$store.getters.windowIsVisible + }, conversations() { return this.$store.getters.conversations }, @@ -240,7 +242,7 @@ export default { }, changeWindowVisibility() { - this.windowIsVisible = !document.hidden + this.$store.dispatch('setWindowVisibility', !document.hidden) if (this.windowIsVisible) { // Remove the potential "*" marker for unread chat messages this.setPageTitle(this.getConversationName(this.token), false) diff --git a/src/components/MessagesList/MessagesList.vue b/src/components/MessagesList/MessagesList.vue index 41705c260..47908496d 100644 --- a/src/components/MessagesList/MessagesList.vue +++ b/src/components/MessagesList/MessagesList.vue @@ -22,17 +22,14 @@ This component is a wrapper for the list of messages. It's main purpose it to get the messagesList array and loop through the list to generate the messages. -In order not to render each and every messages that is in the store, we use -the Vue virtual scroll list component, whose docs you can find [here.](https://github.com/tangbc/vue-virtual-scroll-list) </docs> <template> <!-- size and remain refer to the amount and initial height of the items that are outside of the viewport --> - <virtual-list :size="40" - :remain="8" - :variable="true" + <div + v-scroll="handleScroll" class="scroller"> <MessagesGroup v-for="item of messagesGroupedByAuthor" @@ -41,12 +38,11 @@ the Vue virtual scroll list component, whose docs you can find [here.](https://g v-bind="item" :messages="item" @deleteMessage="handleDeleteMessage" /> - </virtual-list> + </div> </template> <script> import moment from '@nextcloud/moment' -import virtualList from 'vue-virtual-scroll-list' import MessagesGroup from './MessagesGroup/MessagesGroup' import { fetchMessages, lookForNewMessages } from '../../services/messagesService' import { EventBus } from '../../services/EventBus' @@ -57,7 +53,6 @@ export default { name: 'MessagesList', components: { MessagesGroup, - virtualList, }, props: { @@ -89,6 +84,8 @@ export default { * when quickly switching to a new conversation. */ cancelFetchMessages: () => {}, + + isScrolledToBottom: true, } }, @@ -134,6 +131,17 @@ export default { } return groups }, + /** + * In order for the state of the component to be sticky, the browser window must be + * active and the div .scroller must be scrolled to the bottom. + * When isSticky is true, as new messages are appended to the list, the div .scroller + * automatically scrolls down to the last message, if it's false, new messages are + * appended but the scrolling position is not altered. + * @returns {boolean} + */ + isSticky() { + return this.isScrolledToBottom && this.$store.getters.windowIsVisible() + }, }, /** @@ -318,6 +326,11 @@ export default { messages.data.ocs.data.forEach(message => { this.$store.dispatch('processMessage', message) }) + // Scroll to the last message if sticky + if (this.isSticky) { + this.scrollToBottom() + } + // Recursive call this.getNewMessages() } catch (exception) { if (exception.response) { @@ -341,6 +354,20 @@ export default { this.$store.dispatch('deleteMessage', event.message) }, /** + * When the div is scrolled, this method checks if it's been scrolled to the + * bottom. + */ + handleScroll() { + const scrollOffset = document.querySelector('.scroller').scrollHeight - document.querySelector('.scroller').scrollTop + const elementHeight = document.querySelector('.scroller').clientHeight + const tolerance = 3 + if (scrollOffset < elementHeight + tolerance && scrollOffset > elementHeight - tolerance) { + this.isScrolledToBottom = true + } else { + this.isScrolledToBottom = false + } + }, + /** * Scrolls to the bottom of the list. */ scrollToBottom() { @@ -372,5 +399,6 @@ export default { <style lang="scss" scoped> .scroller { flex: 1 0; + overflow-y: auto; } </style> diff --git a/src/main.js b/src/main.js index cfaca35f9..7230aebf5 100644 --- a/src/main.js +++ b/src/main.js @@ -42,6 +42,7 @@ import contenteditableDirective from 'vue-contenteditable-directive' import VueClipboard from 'vue-clipboard2' import { translate, translatePlural } from '@nextcloud/l10n' import VueObserveVisibility from 'vue-observe-visibility' +import vuescroll from 'vue-scroll' // CSP config for webpack dynamic chunk loading // eslint-disable-next-line @@ -64,6 +65,7 @@ Vue.use(Vuex) Vue.use(VueRouter) Vue.use(VueClipboard) Vue.use(VueObserveVisibility) +Vue.use(vuescroll, { debounce: 600 }) export default new Vue({ el: '#content', diff --git a/src/store/index.js b/src/store/index.js index 53ab37cd3..dc5121559 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -29,6 +29,7 @@ import participantsStore from './participantsStore' import quoteReplyStore from './quoteReplyStore' import sidebarStore from './sidebarStore' import tokenStore from './tokenStore' +import windowVisibilityStore from './windowVisibilityStore' Vue.use(Vuex) @@ -43,6 +44,7 @@ export default new Store({ quoteReplyStore, sidebarStore, tokenStore, + windowVisibilityStore, }, mutations, diff --git a/src/store/windowVisibilityStore.js b/src/store/windowVisibilityStore.js new file mode 100644 index 000000000..ebbb41911 --- /dev/null +++ b/src/store/windowVisibilityStore.js @@ -0,0 +1,58 @@ +/** + * @copyright Copyright (c) 2019 Marco Ambrosini <marcoambrosini@pm.me> + * + * @author Marco Ambrosini <marcoambrosini@pm.me> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +const state = { + visible: true, +} + +const getters = { + windowIsVisible: (state) => () => { + return state.visible + }, +} + +const mutations = { + /** + * Sets the current visibility state + * + * @param {object} state current store state; + * @param {boolean} value the value; + */ + setVisibility(state, value) { + state.visible = value + }, +} + +const actions = { + /** + * Sets the current visibility state + * + * @param {object} context the context object; + * @param {boolean} value the value; + */ + setWindowVisibility(context, value) { + context.commit('setVisibility', value) + }, + +} + +export default { state, mutations, getters, actions } |