Skip to content

Commit 69cad81

Browse files
committed
refactor(user-status): improve useHeartbeat
- Add notes - Simplify - Wait for heartbeat request before waiting for the next heartbeat - Also restart on going to away Signed-off-by: Grigorii K. Shartsev <me@shgk.me>
1 parent a580d19 commit 69cad81

File tree

2 files changed

+46
-26
lines changed

2 files changed

+46
-26
lines changed

src/talk/renderer/TitleBar/TitleBar.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import { appData } from '../../../app/AppData.js'
1111
import { BUILD_CONFIG } from '../../../shared/build.config.ts'
1212
import { useDevMode } from '../../../shared/useDevMode.ts'
1313
import { useAppConfigStore } from '../Settings/appConfig.store.ts'
14-
import { useUserStatusHeartbeat } from '../UserStatus/useHeartbeat.ts'
14+
import { useHeartbeat } from '../UserStatus/useHeartbeat.ts'
1515
import { useUserStatusStore } from '../UserStatus/userStatus.store.ts'
1616
1717
useUserStatusStore()
18-
useUserStatusHeartbeat()
18+
useHeartbeat()
1919
useAppConfigStore()
2020
2121
const channel = __CHANNEL__
Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,71 @@
1-
/**
1+
/*
22
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
55

6+
import { createSharedComposable } from '@vueuse/core'
67
import { onBeforeUnmount, watch } from 'vue'
78
import { useAppIdle } from './composables/useAppIdle.ts'
89
import { useUserStatusStore } from './userStatus.store.ts'
910

10-
/** How often to update the user status */
11-
const USER_STATUS_UPDATE_INTERVAL = 5 * 60 * 1000 // 5 minutes
11+
// General notes:
12+
// - Server has INVALIDATE_STATUS_THRESHOLD with 15 minutes, preventing immediate status update on heartbeat request
13+
// See: https://github.com/nextcloud/server/blob/v31.0.5/apps/user_status/lib/Service/StatusService.php
14+
// - However, "away" status has higher priority than "online"
15+
// See: https://github.com/nextcloud/server/blob/v31.0.5/apps/user_status/lib/Service/StatusService.php#L41-L48
16+
// - Thus:
17+
// - Changing "Away -> Online" has a 15 minutes threshold
18+
// - Changing "Online -> Away" is immediate
19+
// - See: https://github.com/nextcloud/server/blob/master/apps/user_status/lib/Listener/UserLiveStatusListener.php#L75-L87
20+
// - This might change in future to have symmetric behavior on heartbeat
21+
22+
/** How often to send the heartbeat. Must be less than 15 min. */
23+
const HEARTBEAT_INTERVAL = 5 * 60 * 1000 // 5 minutes
1224

13-
/** How long user is considered active */
14-
const USER_STATUS_ACTIVE_TIMEOUT = 2 * 60 * 1000 // 2 minutes
25+
/** How long user is considered active before going away */
26+
const AWAY_THRESHOLD = 2 * 60 * 1000 // 2 minutes
1527

1628
/**
17-
* Composable to update the user status in the background
29+
* Background heartbeat with user status update
1830
*/
19-
export function useUserStatusHeartbeat() {
31+
export const useHeartbeat = createSharedComposable(() => {
2032
const userStatusStore = useUserStatusStore()
21-
const isIdle = useAppIdle(USER_STATUS_ACTIVE_TIMEOUT)
33+
const isAway = useAppIdle(AWAY_THRESHOLD)
34+
35+
let heartbeatTimeout: number | undefined
2236

2337
/**
2438
* Send a heartbeat
2539
*/
26-
function heartbeat() {
27-
userStatusStore.updateUserStatusWithHeartbeat(isIdle.value)
40+
async function heartbeat() {
41+
try {
42+
await userStatusStore.updateUserStatusWithHeartbeat(isAway.value)
43+
} catch (error) {
44+
console.error('Error on heartbeat:', error)
45+
}
2846
}
2947

30-
let heartbeatInterval: number | undefined
31-
const restartHeartbeat = () => {
32-
if (heartbeatInterval) {
33-
clearInterval(heartbeatInterval)
48+
/**
49+
* Start heartbeat interval
50+
*/
51+
async function restartHeartbeat() {
52+
if (heartbeatTimeout) {
53+
clearTimeout(heartbeatTimeout)
3454
}
35-
heartbeat()
55+
await heartbeat()
3656
// TODO: fix when main and renderer process have separate tsconfig
37-
heartbeatInterval = setInterval(heartbeat, USER_STATUS_UPDATE_INTERVAL) as unknown as number
57+
heartbeatTimeout = setTimeout(heartbeat, HEARTBEAT_INTERVAL) as unknown as number
3858
}
3959

40-
watch(isIdle, () => {
41-
if (!isIdle.value) {
42-
restartHeartbeat()
43-
}
60+
// Restart heartbeat to immediately notify server on state change
61+
watch(isAway, () => {
62+
// Note: both app and system level activity state changes to inactive with a threshold.
63+
// Only lock/unlock state can be changed many times in a short period, but it is unlikely
64+
// Thus it unlikely overloads with heartbeat, no need to debounce
65+
restartHeartbeat()
4466
}, { immediate: true })
4567

4668
onBeforeUnmount(() => {
47-
clearInterval(heartbeatInterval)
69+
clearTimeout(heartbeatTimeout)
4870
})
49-
50-
return { isIdle }
51-
}
71+
})

0 commit comments

Comments
 (0)