diff --git a/src/cyd-api-client.ts b/src/cyd-api-client.ts index 0bcc947c..c00f6531 100644 --- a/src/cyd-api-client.ts +++ b/src/cyd-api-client.ts @@ -567,4 +567,22 @@ export default class CydAPIClient { return this.returnError("Failed to subscribe to newsletter. Maybe the server is down?") } } + + // User activity + + async postUserActivity(): Promise { + if (!await this.validateAPIToken()) { + return this.returnError("Failed to get a new API token.") + } + + try { + const response = await this.fetchAuthenticated("POST", `${this.apiURL}/user/activity`, null); + if (response.status != 200) { + return this.returnError("Failed to update user activity.", response.status) + } + return true; + } catch { + return this.returnError("Failed to update user activity. Maybe the server is down?") + } + } } diff --git a/src/renderer/src/App.vue b/src/renderer/src/App.vue index 095a6327..98066978 100644 --- a/src/renderer/src/App.vue +++ b/src/renderer/src/App.vue @@ -84,6 +84,35 @@ emitter?.on('show-advanced-settings', () => { showAdvancedSettingsModal.value = true; }); +// Track whether a user is actively using Cyd +const updateUserActivity = async () => { + if (isSignedIn.value) { + try { + await apiClient.value.postUserActivity(); + } catch (error) { + console.error("Failed to update user activity:", error); + } + } +}; + +// Update user activity periodically (every 6 hours) +let userActivityInterval: ReturnType | null = null; + +const startUserActivityInterval = () => { + if (userActivityInterval) { + clearInterval(userActivityInterval); + } + // Update every 6 hours (6 * 60 * 60 * 1000 = 21600000 ms) + userActivityInterval = setInterval(updateUserActivity, 21600000); +}; + +// Stop the interval when signing out +emitter?.on('signed-out', () => { + if (userActivityInterval) { + clearInterval(userActivityInterval); + userActivityInterval = null; + } +}); onMounted(async () => { await window.electron.trackEvent(PlausibleEvents.APP_OPENED, navigator.userAgent); @@ -98,6 +127,12 @@ onMounted(async () => { isSignedIn.value = true; } + // If logged in, update the server with user activity (gets truncated to day) + if (isSignedIn.value) { + await updateUserActivity(); + startUserActivityInterval(); + } + isReady.value = true; // Change the app title diff --git a/src/renderer/src/modals/SignInModal.vue b/src/renderer/src/modals/SignInModal.vue index ff570d18..2c94db7a 100644 --- a/src/renderer/src/modals/SignInModal.vue +++ b/src/renderer/src/modals/SignInModal.vue @@ -157,6 +157,9 @@ async function registerDevice() { signInState.value = 'token'; hide(); + // Update user activity immediately after successful sign in + await apiClient.value.postUserActivity(); + // Emit the signed-in event emitter?.emit('signed-in'); }