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

github.com/nextcloud/polls.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordartcafe <github@dartcafe.de>2022-11-13 23:34:48 +0300
committerdartcafe <github@dartcafe.de>2022-11-13 23:34:48 +0300
commitce8ae122eb3a6a2259affb00759c3941b3a9e033 (patch)
tree63b2520821bec5a93e8c29729fa6fb98abb957db
parent0f098ccc6ecb26131ff37a18cc29b9d3d02fdcda (diff)
updates and refactor watchPollsref/axios-requests
Signed-off-by: dartcafe <github@dartcafe.de>
-rw-r--r--src/js/Api/AxiosHelper.js2
-rw-r--r--src/js/Api/polls.js9
-rw-r--r--src/js/Api/public.js3
-rw-r--r--src/js/helpers/AxiosHelper.js32
-rw-r--r--src/js/mixins/watchPolls.js188
-rw-r--r--src/js/views/SideBar.vue22
6 files changed, 102 insertions, 154 deletions
diff --git a/src/js/Api/AxiosHelper.js b/src/js/Api/AxiosHelper.js
index f5ffbbe9..78c974a4 100644
--- a/src/js/Api/AxiosHelper.js
+++ b/src/js/Api/AxiosHelper.js
@@ -47,7 +47,7 @@ const axiosOcsInstance = axios.create(axiosOcsConfig)
/**
* Description
*
- * @param {any} apiObject
+ * @param {any} apiObject apiObject
* @return {any}
*/
const createCancelTokenHandler = (apiObject) => {
diff --git a/src/js/Api/polls.js b/src/js/Api/polls.js
index 92911f4e..3b278070 100644
--- a/src/js/Api/polls.js
+++ b/src/js/Api/polls.js
@@ -50,6 +50,15 @@ const polls = {
})
},
+ watchPoll(pollId = 0, lastUpdated) {
+ return axiosInstance.request({
+ method: 'GET',
+ url: `poll/${pollId}/watch`,
+ params: { offset: lastUpdated },
+ cancelToken: cancelTokenHandlerObject[this.watchPoll.name].handleRequestCancellation().token,
+ })
+ },
+
takeOver(pollId) {
return axiosInstance.request({
method: 'PUT',
diff --git a/src/js/Api/public.js b/src/js/Api/public.js
index 362665fc..e32460d9 100644
--- a/src/js/Api/public.js
+++ b/src/js/Api/public.js
@@ -32,10 +32,11 @@ const publicPoll = {
})
},
- watch(shareToken) {
+ watchPoll(shareToken, lastUpdated) {
return axiosInstance.request({
method: 'GET',
url: `s/${shareToken}/watch`,
+ params: { offset: lastUpdated },
cancelToken: cancelTokenHandlerObject[this.watch.name].handleRequestCancellation().token,
})
},
diff --git a/src/js/helpers/AxiosHelper.js b/src/js/helpers/AxiosHelper.js
deleted file mode 100644
index df5e15af..00000000
--- a/src/js/helpers/AxiosHelper.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * @copyright Copyright (c) 2022 Rene Gieling <github@dartcafe.de>
- *
- * @author Rene Gieling <github@dartcafe.de>
- *
- * @license AGPL-3.0-or-later
- *
- * 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 clientSessionId = Math.random().toString(36).substring(2)
-
-const axiosDefaultConfig = {
- headers: {
- Accept: 'application/json',
- 'Nc-Polls-Client-Id': clientSessionId,
- },
-}
-
-export { axiosDefaultConfig }
diff --git a/src/js/mixins/watchPolls.js b/src/js/mixins/watchPolls.js
index 7534d2df..a6a846ed 100644
--- a/src/js/mixins/watchPolls.js
+++ b/src/js/mixins/watchPolls.js
@@ -21,29 +21,25 @@
*
*/
-import axios from '@nextcloud/axios'
-import { generateUrl } from '@nextcloud/router'
import { getCurrentUser } from '@nextcloud/auth'
import { mapState } from 'vuex'
import { InvalidJSON } from '../Exceptions/Exceptions.js'
-import { axiosDefaultConfig } from '../helpers/AxiosHelper.js'
+import { PollsAPI } from '../Api/polls.js'
+import { PublicAPI } from '../Api/public.js'
-const defaultSleepTimeout = 30
+const SLEEP_TIMEOUT_DEFAULT = 30
+const MAX_TRIES = 5
export const watchPolls = {
data() {
return {
- cancelToken: null,
restart: false,
watching: true,
lastUpdated: Math.round(Date.now() / 1000),
- retryCounter: 0,
- maxTries: 5,
endPoint: '',
isLoggedin: !!getCurrentUser(),
isAdmin: !!getCurrentUser()?.isAdmin,
- sleepTimeout: defaultSleepTimeout, // seconds
- gotValidResponse: true,
+ sleepTimeout: SLEEP_TIMEOUT_DEFAULT, // seconds
}
},
@@ -51,170 +47,132 @@ export const watchPolls = {
...mapState({
updateType: (state) => state.appSettings.updateType,
}),
- },
- methods: {
- async watchPolls() {
- if (this.cancelToken) {
- this.cancelWatch() // there is already a cancelToken, cancel the previous session
+ pollingDisabled() {
+ if (this.updateType !== 'noPolling') {
+ return false
}
- this.cancelToken = axios.CancelToken.source() // get a new cancel token
+ console.debug('[polls]', 'Polling for updates is disabled. Cancel watch.')
+ return true
+ },
+ },
- while (this.retryCounter < this.maxTries) {
- this.sleepTimeout = defaultSleepTimeout // reset sleep timer to default
- this.gotValidResponse = false
+ methods: {
+ async watchPolls() {
+ let retryCounter = 0
+ const sleepTimeout = SLEEP_TIMEOUT_DEFAULT
- if (this.updateType === 'noPolling') {
- // leave if polling is disabled
- console.debug('[polls]', 'Polling for updates is disabled. Cancel watch.')
- this.cancelWatch()
- return
- }
+ while (retryCounter < MAX_TRIES) {
+ if (this.pollingDisabled) return
- // loop while tab is hidden and avoid further requests
+ // avoid requests when app is in background and pause
while (document.hidden) {
- console.debug('[polls]', 'app is in background')
- await new Promise((resolve) => setTimeout(resolve, 2000))
+ console.debug('[polls]', 'App in background, pause watching')
+ await new Promise((resolve) => setTimeout(resolve, 5000))
}
try {
- console.debug('[polls]', 'Watch for updates')
- await this.handleResponse(await this.fetchUpdates())
+ const response = await this.fetchUpdates()
- } catch (e) {
- if (axios.isCancel(e)) {
- this.handleCanceledRequest()
+ if (response.headers['content-type'].includes('application/json')) {
+ retryCounter = 0
+ this.loadStores(response.data.updates)
} else {
- this.handleConnectionError(e)
+ throw new InvalidJSON(`No JSON response recieved, got "${response.headers['content-type']}"`)
}
+
+ } catch (e) {
+ this.sleepTimeout = e?.response.headers['retry-after'] ?? SLEEP_TIMEOUT_DEFAULT
+ retryCounter = await this.handleConnectionException(e, retryCounter, sleepTimeout)
}
- // sleep if request was invalid or polling is set to something else than "longPolling"
- if (this.updateType !== 'longPolling' || !this.gotValidResponse) {
- await this.sleep()
- console.debug('[polls]', 'continue after sleep')
+ // sleep if request was invalid or polling is set to "peeriodicPolling"
+ if (this.updateType === 'periodicPolling' || retryCounter) {
+ await this.sleep(sleepTimeout)
+ console.debug('[polls]', 'Continue after sleep')
}
}
- // invalidate the cancel token before leaving
- this.cancelToken = null
-
- if (this.retryCounter) {
- console.debug('[polls]', `Cancel watch after ${this.retryCounter} failed requests`)
+ if (retryCounter) {
+ console.debug('[polls]', `Cancel watch after ${retryCounter} failed requests`)
}
},
async fetchUpdates() {
- if (this.$route.name === 'publicVote') {
- this.endPoint = `apps/polls/s/${this.$route.params.token}/watch`
- } else {
- this.endPoint = `apps/polls/poll/${this.$route.params.id ?? 0}/watch`
- }
-
+ console.debug('[polls]', `Watching for updates (${this.updateType})`)
await this.$store.dispatch('appSettings/get')
- const response = await axios.get(generateUrl(this.endPoint), {
- ...axiosDefaultConfig,
- params: { offset: this.lastUpdated },
- cancelToken: this.cancelToken.token,
- })
-
- return response
+ if (this.$route.name === 'publicVote') {
+ return await PublicAPI.watchPoll(this.$route.params.token, this.lastUpdated)
+ }
+ return await PollsAPI.watchPoll(this.$route.params.id, this.lastUpdated)
},
- cancelWatch() {
- this.cancelToken.cancel()
+ sleep(retryCounter, sleepTimeout) {
+ const reason = retryCounter ? `Connection error, Attempt: ${retryCounter}/${MAX_TRIES})` : this.updateType
+ console.debug('[polls]', `Sleep for ${sleepTimeout} seconds (reason: ${reason})`)
+ return new Promise((resolve) => setTimeout(resolve, sleepTimeout * 1000))
},
- sleep() {
- let reason = `Connection error, Attempt: ${this.retryCounter}/${this.maxTries})`
-
- if (this.gotValidResponse) {
- reason = this.updateType
- }
-
- console.debug('[polls]', `Sleep for ${this.sleepTimeout} seconds (reason: ${reason})`)
- return new Promise((resolve) => setTimeout(resolve, this.sleepTimeout * 1000))
- },
+ async handleConnectionException(e, retryCounter, sleepTimeout) {
+ retryCounter += 1
- handleResponse(response) {
- if (response.headers['content-type'].includes('application/json')) {
- this.gotValidResponse = true
- console.debug('[polls]', `Update detected (${this.updateType})`, response.data.updates)
- this.loadTables(response.data.updates)
- this.retryCounter = 0 // reset retryCounter after we got a valid response
- return
+ if (e?.code === 'ERR_CANCELED') {
+ return 0
}
- this.gotValidResponse = false
- throw new InvalidJSON(`No JSON response recieved, got "${response.headers['content-type']}"`)
- },
-
- handleCanceledRequest() {
- console.debug('[polls]', 'Fetch canceled')
- this.cancelToken = axios.CancelToken.source()
- },
-
- async handleConnectionError(e) {
if (e.response?.status === 304) {
- console.debug('[polls]', `No updates (using ${this.updateType})`)
- this.gotValidResponse = true
- this.retryCounter = 0 // reset retryCounter, after we get a 304
- return
+ console.debug('[polls]', 'No updates')
+ return 0
}
- this.retryCounter += 1
-
if (e?.response?.status === 503) {
// Server possibly in maintenance mode
- this.sleepTimeout = e.response.headers['retry-after'] ?? this.sleepTimeout
- console.debug('[polls]', `Service not avaiable - retry after ${this.sleepTimeout} seconds`)
- return
+ console.debug('[polls]', `Service not avaiable - retry after ${sleepTimeout} seconds`)
+ return retryCounter
}
+
if (e.response) {
console.error('[polls]', e)
- return
+ return retryCounter
}
- console.debug('[polls]', e.message ?? `No response - request aborted - failed request ${this.retryCounter}/${this.maxTries}`)
+ console.debug('[polls]', e.message ?? `No response - request aborted - failed request ${retryCounter}/${MAX_TRIES}`)
},
- async loadTables(tables) {
+ async loadStores(stores) {
+ console.debug('[polls]', 'Updates detected', stores)
+
let dispatches = ['activity/list']
- console.debug('[polls]', 'fetching updates', tables)
- tables.forEach((item) => {
+
+ stores.forEach((item) => {
this.lastUpdated = Math.max(item.updated, this.lastUpdated)
+
if (item.table === 'polls') {
- if (this.isAdmin) {
- console.debug('[polls]', 'update admin view', item.table)
- // If user is an admin, also load admin list
- dispatches = [...dispatches, 'pollsAdmin/list']
- }
- if (item.pollId === parseInt(this.$route.params.id ?? this.$store.state.share.pollId)) {
- // if current poll is affected, load current poll configuration
- console.debug('[polls]', 'current poll', item.table)
+ // If user is an admin, also load admin list
+ if (this.isAdmin) dispatches = [...dispatches, 'pollsAdmin/list']
+
+ // if user is an authorized user load polls list and combo
+ if (this.isLoggedin) dispatches = [...dispatches, `${item.table}/list`, 'combo/cleanUp']
+
+ // if current poll is affected, load current poll configuration
+ if (item.pollId === this.$store.state.poll.id) {
dispatches = [...dispatches, 'poll/get']
}
- if (this.isLoggedin) {
- // if user is an authorized user load polls list
- console.debug('[polls]', 'update list', item.table)
- dispatches = [...dispatches, `${item.table}/list`]
- }
} else if (!this.isLoggedin && (item.table === 'shares')) {
// if current user is guest and table is shares only reload current share
dispatches = [...dispatches, 'share/get']
} else {
- // otherwise load table
+ // otherwise just load particulair store
dispatches = [...dispatches, `${item.table}/list`]
}
})
- dispatches = [...new Set(dispatches)] // remove duplicates
- await Promise.all(dispatches.map((dispatches) => this.$store.dispatch(dispatches)))
- await this.$store.dispatch('combo/cleanUp')
+ dispatches = [...new Set(dispatches)] // remove duplicates and add combo
+ return Promise.all(dispatches.map((dispatches) => this.$store.dispatch(dispatches)))
},
},
diff --git a/src/js/views/SideBar.vue b/src/js/views/SideBar.vue
index 73a88f1d..2696c372 100644
--- a/src/js/views/SideBar.vue
+++ b/src/js/views/SideBar.vue
@@ -25,7 +25,7 @@
:title="t('polls', 'Details')"
@close="closeSideBar()">
<NcAppSidebarTab v-if="acl.allowEdit"
- :id="'configuration'"
+ id="configuration"
:order="1"
:name="t('polls', 'Configuration')">
<template #icon>
@@ -35,7 +35,7 @@
</NcAppSidebarTab>
<NcAppSidebarTab v-if="acl.allowEdit"
- :id="'options'"
+ id="options"
:order="2"
:name="t('polls', 'Options')">
<template #icon>
@@ -45,7 +45,7 @@
</NcAppSidebarTab>
<NcAppSidebarTab v-if="acl.allowEdit"
- :id="'sharing'"
+ id="sharing"
:order="3"
:name="t('polls', 'Sharing')">
<template #icon>
@@ -55,7 +55,7 @@
</NcAppSidebarTab>
<NcAppSidebarTab v-if="acl.allowComment"
- :id="'comments'"
+ id="comments"
:order="5"
:name="t('polls', 'Comments')">
<template #icon>
@@ -65,7 +65,7 @@
</NcAppSidebarTab>
<NcAppSidebarTab v-if="acl.allowEdit && useActivity"
- :id="'activity'"
+ id="activity"
:order="6"
:name="t('polls', 'Activity')">
<template #icon>
@@ -85,11 +85,23 @@ import SidebarOptionsIcon from 'vue-material-design-icons/FormatListChecks.vue'
import SidebarShareIcon from 'vue-material-design-icons/ShareVariant.vue'
import SidebarCommentsIcon from 'vue-material-design-icons/CommentProcessing.vue'
import SidebarActivityIcon from 'vue-material-design-icons/LightningBolt.vue'
+// test static loading
+// import SideBarTabConfiguration from '../components/SideBar/SideBarTabConfiguration.vue'
+// import SideBarTabComments from '../components/SideBar/SideBarTabComments.vue'
+// import SideBarTabOptions from '../components/SideBar/SideBarTabOptions.vue'
+// import SideBarTabShare from '../components/SideBar/SideBarTabShare.vue'
+// import SideBarTabActivity from '../components/SideBar/SideBarTabActivity.vue'
export default {
name: 'SideBar',
components: {
+ // test static loading
+ // SideBarTabConfiguration,
+ // SideBarTabComments,
+ // SideBarTabOptions,
+ // SideBarTabShare,
+ // SideBarTabActivity,
SideBarTabConfiguration: () => import('../components/SideBar/SideBarTabConfiguration.vue'),
SideBarTabComments: () => import('../components/SideBar/SideBarTabComments.vue'),
SideBarTabOptions: () => import('../components/SideBar/SideBarTabOptions.vue'),