basic notify_push usage with session handling (rebased)

Signed-off-by: chandi Langecker <git@chandi.it>
This commit is contained in:
chandi Langecker
2022-08-04 14:42:27 +02:00
parent 0c69404ac9
commit fcfbcc63b4
21 changed files with 805 additions and 1 deletions

View File

@@ -40,6 +40,8 @@
</p>
</div>
<div class="board-actions">
<SessionList v-if="isNotifyPushEnabled && board && board.activeSessions"
:sessions="board.activeSessions" />
<div v-if="searchQuery || true" class="deck-search">
<input type="search"
class="icon-search"
@@ -224,6 +226,8 @@ import FilterIcon from 'vue-material-design-icons/Filter.vue'
import FilterOffIcon from 'vue-material-design-icons/FilterOff.vue'
import ArrowCollapseVerticalIcon from 'vue-material-design-icons/ArrowCollapseVertical.vue'
import ArrowExpandVerticalIcon from 'vue-material-design-icons/ArrowExpandVertical.vue'
import SessionList from './SessionList'
import { isNotifyPushEnabled } from '../listeners'
export default {
name: 'Controls',
@@ -239,6 +243,7 @@ export default {
FilterOffIcon,
ArrowCollapseVerticalIcon,
ArrowExpandVerticalIcon,
SessionList,
},
mixins: [labelStyle],
props: {
@@ -286,6 +291,9 @@ export default {
labelsSorted() {
return [...this.board.labels].sort((a, b) => (a.title < b.title) ? -1 : 1)
},
isNotifyPushEnabled() {
return isNotifyPushEnabled()
},
},
watch: {
board(current, previous) {

View File

@@ -0,0 +1,115 @@
<!--
* @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>
* @copyright Copyright (c) 2022, chandi Langecker (git@chandi.it)
*
* @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/>.
*
* -->
<template>
<div class="session-list">
<button slot="trigger"
v-tooltip.bottom="t('text', 'Active people')"
class="avatar-list">
<div v-for="session in sessionsVisible"
:key="session"
class="avatar-wrapper"
:style="sessionAvatarStyle">
<Avatar :user="session"
:disable-menu="true"
:show-user-status="false"
:disable-tooltip="true"
:size="size" />
</div>
</button>
</div>
</template>
<script>
import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip'
import Avatar from '@nextcloud/vue/dist/Components/Avatar'
export default {
name: 'SessionList',
components: {
Avatar,
},
directives: {
tooltip: Tooltip,
},
props: {
sessions: {
type: Array,
default: () => { return [] },
},
size: {
type: Number,
default: () => 32,
},
},
computed: {
sessionsVisible() {
if (!this.sessions) return []
return this.sessions.slice(0, 5)
},
sessionAvatarStyle() {
return {
'--size': this.size + 'px',
'--font-size': this.size / 2 + 'px',
}
},
},
}
</script>
<style scoped lang="scss">
.session-list {
padding-left: 0.5em;
padding-right: 0.5em;
}
.avatar-list {
border: none;
background-color: var(--color-main-background);
padding: 0;
margin: 0;
padding-left: 6px;
display: inline-flex;
flex-direction: row-reverse;
&:focus {
background-color: #eee;
}
}
.avatar-wrapper {
background-color: #b9b9b9;
border-radius: 50%;
border-width: 2px;
border-style: solid;
width: var(--size);
height: var(--size);
text-align: center;
color: #ffffff;
line-height: var(--size);
font-size: var(--font-size);
font-weight: normal;
z-index: 1;
overflow: hidden;
box-sizing: content-box !important;
margin-left: -8px;
}
</style>

View File

@@ -81,6 +81,8 @@ import Stack from './Stack.vue'
import { NcEmptyContent } from '@nextcloud/vue'
import GlobalSearchResults from '../search/GlobalSearchResults.vue'
import { showError } from '../../helpers/errors.js'
import { sessionApi } from '../../services/SessionApi'
import { isNotifyPushEnabled } from '../../listeners'
export default {
name: 'Board',
@@ -128,13 +130,51 @@ export default {
},
},
watch: {
id: 'fetchData',
id(newValue, oldValue) {
if (oldValue) {
// close old session
sessionApi.closeSession(oldValue, this.token)
this.token = null
}
// create new session
this.ensureSession(newValue)
this.fetchData()
},
showArchived() {
this.fetchData()
},
},
created() {
if (isNotifyPushEnabled()) {
// create a session
this.ensureSession()
}
this.fetchData()
if (isNotifyPushEnabled()) {
// regularly let the server know that we are still here
this.sessionInterval = setInterval(() => {
this.ensureSession()
}, 25 * 1000)
// we don't get events pushed for sessions that have expired,
// so we poll the list of sessions every minute when there
// are other sessions active
this.refreshInterval = setInterval(() => {
if (this.board?.activeSessions?.length) {
this.refreshData()
}
}, 60 * 1000)
}
},
beforeDestroy() {
if (isNotifyPushEnabled()) {
sessionApi.closeSession(this.id, this.token)
clearInterval(this.sessionInterval)
clearInterval(this.refreshInterval)
}
},
methods: {
async fetchData() {
@@ -149,6 +189,32 @@ export default {
this.loading = false
},
async ensureSession(boardId = this.id) {
if (this.token) {
try {
await sessionApi.syncSession(boardId, this.token)
} catch (err) {
// session probably expired, let's try again
// with a fresh session
this.token = null
setTimeout(() => {
this.ensureSession()
}, 100)
}
} else {
try {
const res = await sessionApi.createSession(boardId)
this.token = res.token
} catch (err) {
showError(err)
}
}
},
async refreshData() {
await this.$store.dispatch('refreshBoard', this.id)
},
onDropStack({ removedIndex, addedIndex }) {
this.$store.dispatch('orderStack', { stack: this.stacksByBoard[removedIndex], removedIndex, addedIndex })
},