Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/spreed.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoas Schilling <213943+nickvergessen@users.noreply.github.com>2020-01-09 10:00:35 +0300
committerGitHub <noreply@github.com>2020-01-09 10:00:35 +0300
commit7be3922f21af8eebdbee9a51f9e6c7f395a6e7f5 (patch)
tree34e309cf8c08ff1b9fdaaea9972d9be90a2ba2a2 /src
parenta1ade6a43b479da389452af9d5b811937e3b80eb (diff)
parent9168c903d05f8b61fdbedbd77c79346b8ad09c2e (diff)
Merge pull request #2692 from nextcloud/bugfix/2681-2595/scrolling
Fix scrolling issues
Diffstat (limited to 'src')
-rw-r--r--src/App.vue6
-rw-r--r--src/components/MessagesList/MessagesList.vue44
-rw-r--r--src/main.js2
-rw-r--r--src/store/index.js2
-rw-r--r--src/store/windowVisibilityStore.js58
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 }