Merge pull request #2360 from nextcloud/bugfix/vuejs/stop-long-polling-requests-when-changing-conversations

Stop long polling requests when changing conversations.
This commit is contained in:
Joas Schilling 2019-10-31 10:14:49 +01:00 коммит произвёл GitHub
Родитель 76c6e7b602 aa0648b2c7
Коммит 5c2c7b1738
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 112 добавлений и 57 удалений

20
package-lock.json сгенерированный
Просмотреть файл

@ -954,30 +954,23 @@
}
},
"@nextcloud/axios": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/@nextcloud/axios/-/axios-0.4.2.tgz",
"integrity": "sha512-HrViGxCX0qTE5uCQyMWD0BM5x0cj9VGLXN708zWsehgkTUnwt7qEnyrbuaGSOkdqlbQbf36+bRMxQI23a2mlIg==",
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@nextcloud/axios/-/axios-0.5.0.tgz",
"integrity": "sha512-cnf/bgyOpiUty50VMfmtWYNvq0n2G4YJpNldUcY/LJrB5ENyuiv7vJbOl9R40pb6NztWkmDyTK8Ycl1VlzcYrA==",
"requires": {
"@babel/cli": "^7.6.2",
"@babel/core": "^7.6.2",
"@babel/preset-env": "^7.6.2",
"@babel/preset-typescript": "^7.6.0",
"@nextcloud/auth": "^0.3.1",
"@nextcloud/event-bus": "^0.2.0",
"axios": "^0.19.0",
"browserslist-config-nextcloud": "0.0.1",
"core-js": "^3.2.1"
},
"dependencies": {
"browserslist-config-nextcloud": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/browserslist-config-nextcloud/-/browserslist-config-nextcloud-0.0.1.tgz",
"integrity": "sha512-BUpPPPfE42jL2puSqfnsoOMoz6g+jqznoaoZmig4Kx1ULApBmM6iH+/7V1yblQz2PsOp39HET1byAB3h3h+kew=="
},
"core-js": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.3.4.tgz",
"integrity": "sha512-BtibooaAmSOptGLRccsuX/dqgPtXwNgqcvYA6kOTTMzonRxZ+pJS4e+6mvVutESfXMeTnK8m3M+aBu3bkJbR+w=="
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.3.5.tgz",
"integrity": "sha512-0J3K+Par/ZydhKg8pEiTcK/9d65/nqJOzY62uMkjeBmt05fDOt/khUVjDdh8TpeIuGQDy1yLDDCjiWN/8pFIuw=="
}
}
},
@ -6568,7 +6561,6 @@
"resolved": "https://registry.npmjs.org/nextcloud-vue-collections/-/nextcloud-vue-collections-0.6.0.tgz",
"integrity": "sha512-P91zLvjFtYikfS2Nz5FNgGpZAzdDACzlHhaG8yjPTFxZI9nv0a8tzHTMYdDzUc3cgxriJ5N4ENBtrWKWWnrR5g==",
"requires": {
"@nextcloud/axios": "^0.4.2",
"@nextcloud/router": "^0.1.0",
"lodash": "^4.17.11",
"nextcloud-vue": "^0.12.1",

Просмотреть файл

@ -16,10 +16,10 @@
},
"dependencies": {
"@nextcloud/auth": "^0.3.1",
"@nextcloud/axios": "^0.5.0",
"crypto-js": "^3.1.9-1",
"debounce": "^1.2.0",
"nextcloud-auth": "0.0.3",
"nextcloud-axios": "^0.2.1",
"nextcloud-initial-state": "0.0.3",
"nextcloud-router": "0.0.9",
"nextcloud-vue": "^0.12.7",

Просмотреть файл

@ -116,9 +116,16 @@ export default {
* This runs whenever the new route is a conversation.
*/
if (to.name === 'conversation') {
// Page title
const NEXT_CONVERSATION_NAME = this.getConversationName(to.params.token)
this.setPageTitle(NEXT_CONVERSATION_NAME)
}
/**
* Fires a global event that tells the whole app that the route has changed. The event
* carries the from and to objects as payload
*/
EventBus.$emit('routeChange', { from, to })
next()
})
},

Просмотреть файл

@ -30,7 +30,7 @@
:display-name="item.displayName" />
<div v-if="showFavorite"
class="favorite-mark">
<span class="icon icon-favorite"></span>
<span class="icon icon-favorite" />
<span class="hidden-visually">{{ t('spreed', 'Favorite') }}</span>
</div>
</div>

Просмотреть файл

@ -47,7 +47,8 @@ the Vue virtual scroll list component, whose docs you can find [here.](https://g
<script>
import virtualList from 'vue-virtual-scroll-list'
import MessagesGroup from './MessagesGroup/MessagesGroup'
import { fetchMessages, lookForNewMessges } from '../../services/messagesService'
import { fetchMessages, cancelableLookForNewMessages } from '../../services/messagesService'
import { EventBus } from '../../services/EventBus'
export default {
name: 'MessagesList',
@ -73,6 +74,12 @@ export default {
* bottom.
*/
isInitiated: false,
/**
* Stores the cancel function returned by `cancelableLookForNewMessages`,
* which allows to cancel the previous long polling request for new
* messages before making another one.
*/
cancelRequest: null,
}
},
@ -115,19 +122,20 @@ export default {
},
},
watch: {
token: function() {
this.onTokenChange()
},
},
/**
* Fetches the messages when the MessageList is mounted for the
* first time. The router mounts this component only if the token
* is passed in so there's no need to check the token prop.
* Fetches the messages when the MessageList created. The router mounts this
* component only if the token is passed in so there's no need to check the
* token prop.
*/
beforeMount() {
this.onTokenChange()
created() {
this.onRouteChange()
/**
* Add a listener for routeChange event emitted by the App.vue component.
* Call the onRouteChange method function whenever the route changes.
*/
EventBus.$on('routeChange', () => {
this.onRouteChange()
})
},
beforeUpdate() {
@ -165,27 +173,37 @@ export default {
},
/**
* Fetches the messages of a conversation given the
* conversation token.
* Fetches the messages of a conversation given the conversation token. Triggers
* a long-polling request for new messages.
*/
async onTokenChange() {
async onRouteChange() {
this.isInitiated = false
const messages = await fetchMessages(this.token)
// Process each messages and adds it to the store
messages.data.ocs.data.forEach(message => {
this.$store.dispatch('processMessage', message)
})
// After loading the old messages to the store, we start looking for new mwssages.
this.getNewMessages()
},
/**
* Creates a long polling request for a new message.
*/
async getNewMessages() {
const lastKnownMessageId = this.messagesList[this.messagesList.length - 1].id
const messages = await lookForNewMessges(this.token, lastKnownMessageId)
// If there are no new messages, the variable messages will be undefined.
/**
* If there's already one pending long polling request from a previous call
* of this method, we call the `cancelRequest` function to clear it and reset
* the cancelRequest to null in the component's data.
*/
if (typeof this.cancelRequest === 'function') {
this.cancelRequest('canceled')
this.cancelRequest = null
}
// Get a new request function and cancel function pair
const { lookForNewMessages, cancel } = cancelableLookForNewMessages()
// store the cancel function in the data
this.cancelRequest = cancel
const lastKnownMessageId = this.getLastKnownMessageId()
const messages = await lookForNewMessages(this.token, lastKnownMessageId)
if (messages !== undefined) {
// Process each messages and adds it to the store
messages.data.ocs.data.forEach(message => {
@ -194,12 +212,11 @@ export default {
this.scrollToBottom()
}
/**
* This method recursively call itself after a response, so we're always
* looking for new messages.
* If there are no new messages, the variable messages will be undefined, so the
* previous code block will be skipped and this method recursively calls itself.
*/
this.getNewMessages()
},
/**
* Dispatches the deleteMessages action.
* @param {object} event The deleteMessage event emitted by the Message component.
@ -216,6 +233,17 @@ export default {
})
},
/**
* gets the last known message id.
* @returns {string} The last known message id.
*/
getLastKnownMessageId() {
if (this.messagesList[this.messagesList.length - 1]) {
return this.messagesList[this.messagesList.length - 1].id
}
return '0'
},
},
}
</script>

Просмотреть файл

@ -27,7 +27,7 @@
<ConversationIcon
slot="icon"
:item="item"
:hideFavorite="false" />
:hide-favorite="false" />
<template slot="subtitle">
{{ simpleLastChatMessage }}
</template>

Просмотреть файл

@ -20,7 +20,7 @@
*
*/
import axios from 'nextcloud-axios'
import axios from '@nextcloud/axios'
import { generateOcsUrl } from 'nextcloud-router'
import { CONVERSATION } from '../constants'

Просмотреть файл

@ -20,9 +20,11 @@
*
*/
import axios from 'nextcloud-axios'
import axios from '@nextcloud/axios'
import { generateOcsUrl } from 'nextcloud-router'
const CANCEL_TOKEN = axios.CancelToken
/**
* Fetches messages that belong to a particular conversation
* specified with its token.
@ -39,18 +41,44 @@ const fetchMessages = async function(token) {
}
/**
* Fetches newly created messages that belong to a particular conversation
* specified with its token.
*
* @param {string} token The conversation token;
* @param {int} lastKnownMessageId The id of the last message in the store.
* Creates a cancelable axios 'request object'. We need this in order to cancel
* long polling requests for new messages of previous conversatios when
* switching to a new one.
* @returns {object} An object that contains 2 functions:
* - `lookForNewMessages` : the api call async function with the injected cancel
* token;
* - `cancel` : the function that allows to cancel that particular a call;
*/
const lookForNewMessges = async function(token, lastKnownMessageId) {
try {
const response = await axios.get(generateOcsUrl('apps/spreed/api/v1/chat', 2) + token + '?lookIntoFuture=1' + '&includeLastKnown=0' + `&lastKnownMessageId=${lastKnownMessageId}`)
return response
} catch (error) {
console.debug('Error while looking for new message: ', error)
const cancelableLookForNewMessages = function() {
/**
* cancelToken= the token that gets injected into the axios method and links it
* to the cancel function;
* cancel= function that allows to delete the api call;
*/
const { token: cancelToken, cancel } = CANCEL_TOKEN.source()
/**
* Fetches newly created messages that belong to a particular conversation
* specified with its token.
*
* @param {string} token The conversation token;
* @param {int} lastKnownMessageId The id of the last message in the store.
*/
const lookForNewMessages = async(token, lastKnownMessageId) => {
try {
const response = await axios.get(generateOcsUrl('apps/spreed/api/v1/chat', 2) + token + '?lookIntoFuture=1' + '&includeLastKnown=0' + `&lastKnownMessageId=${lastKnownMessageId}`, { cancelToken })
return response
} catch (exception) {
if (axios.isCancel(exception)) {
console.debug(exception.message)
return exception.message
} else {
console.debug('Error while looking for new message: ', exception)
}
}
}
return {
lookForNewMessages,
cancel,
}
}
@ -73,6 +101,6 @@ const postNewMessage = async function({ token, message, parent }) {
export {
fetchMessages,
lookForNewMessges,
cancelableLookForNewMessages,
postNewMessage,
}

Просмотреть файл

@ -20,7 +20,7 @@
*
*/
import axios from 'nextcloud-axios'
import axios from '@nextcloud/axios'
import { generateOcsUrl } from 'nextcloud-router'
/**

Просмотреть файл

@ -57,7 +57,7 @@
</template>
<script>
import axios from 'nextcloud-axios'
import axios from '@nextcloud/axios'
import debounce from 'debounce'
import { Multiselect } from 'nextcloud-vue'
import { generateOcsUrl } from 'nextcloud-router/dist/index'

Просмотреть файл

@ -105,7 +105,7 @@
/* global OC */
import { Modal } from 'nextcloud-vue/dist/Components/Modal'
import { Avatar } from 'nextcloud-vue/dist/Components/Avatar'
import axios from 'nextcloud-axios'
import axios from '@nextcloud/axios'
export default {
name: 'RoomSelector',