-
Notifications
You must be signed in to change notification settings - Fork 62
Expand file tree
/
Copy pathuseHeartbeat.ts
More file actions
71 lines (61 loc) · 2.47 KB
/
useHeartbeat.ts
File metadata and controls
71 lines (61 loc) · 2.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/*
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { createSharedComposable } from '@vueuse/core'
import { onBeforeUnmount, watch } from 'vue'
import { useIsAway } from './composables/useIsAway.ts'
import { useUserStatusStore } from './userStatus.store.ts'
// General notes:
// - Server has INVALIDATE_STATUS_THRESHOLD with 15 minutes, preventing immediate status update on heartbeat request
// See: https://github.com/nextcloud/server/blob/v31.0.5/apps/user_status/lib/Service/StatusService.php
// - However, "online" status has higher priority than "away"
// - Thus:
// - Changing "Away -> Online" is immediate
// - Changing "Online -> Away" has a 15 minutes threshold
// - See: https://github.com/nextcloud/server/blob/v31.0.5/apps/user_status/lib/Service/StatusService.php#L41-L48
// and: https://github.com/nextcloud/server/blob/master/apps/user_status/lib/Listener/UserLiveStatusListener.php#L75-L87
// - This might change in future to have symmetric behavior on heartbeat
/** How often to send the heartbeat. Must be less than 15 min. */
const HEARTBEAT_INTERVAL = 5 * 60 * 1000 // 5 minutes
/** How long user is considered active before going away */
const AWAY_THRESHOLD = 2 * 60 * 1000 // 2 minutes
/**
* Background heartbeat with user status update
*/
export const useHeartbeat = createSharedComposable(() => {
const userStatusStore = useUserStatusStore()
const isAway = useIsAway(AWAY_THRESHOLD)
let heartbeatTimeout: number | undefined
/**
* Send a heartbeat
*/
async function heartbeat() {
try {
await userStatusStore.updateUserStatusWithHeartbeat(isAway.value)
} catch (error) {
console.error('Error on heartbeat:', error)
}
}
/**
* Start heartbeat interval
*/
async function restartHeartbeat() {
if (heartbeatTimeout) {
clearTimeout(heartbeatTimeout)
}
await heartbeat()
// TODO: fix when main and renderer process have separate tsconfig
heartbeatTimeout = setTimeout(heartbeat, HEARTBEAT_INTERVAL) as unknown as number
}
// Restart heartbeat to immediately notify server on state change
watch(isAway, () => {
// Note: both app and system level activity state changes to inactive with a threshold.
// Only lock/unlock state can be changed many times in a short period, but it is unlikely
// Thus it unlikely overloads with heartbeat, no need to debounce
restartHeartbeat()
}, { immediate: true })
onBeforeUnmount(() => {
clearTimeout(heartbeatTimeout)
})
})