diff --git a/src/components/dialogs/UserDialog/UserDialogActivityTab.vue b/src/components/dialogs/UserDialog/UserDialogActivityTab.vue index e896a4ee..9e397a7e 100644 --- a/src/components/dialogs/UserDialog/UserDialogActivityTab.vue +++ b/src/components/dialogs/UserDialog/UserDialogActivityTab.vue @@ -23,7 +23,6 @@ - {{ t('dialog.user.activity.period_180') }} {{ t('dialog.user.activity.period_90') }} {{ t('dialog.user.activity.period_30') }} {{ t('dialog.user.activity.period_7') }} @@ -241,6 +240,7 @@ }); let activeRequestId = 0; + let activeOverlapRequestId = 0; let lastLoadedUserId = ''; const pendingWorldThumbnailFetches = new Set(); @@ -281,6 +281,7 @@ mainHeatmapView.value = { rawBuckets: [], normalizedBuckets: [] }; overlapHeatmapView.value = { rawBuckets: [], normalizedBuckets: [] }; activeRequestId++; + activeOverlapRequestId++; lastLoadedUserId = ''; } @@ -291,6 +292,7 @@ } const requestId = ++activeRequestId; + ++activeOverlapRequestId; if (!silent) { isLoading.value = true; } @@ -386,20 +388,56 @@ await refreshData(); } + async function refreshOverlapOnly() { + const userId = userDialog.value.id; + if (!userId || isSelf.value || !hasAnyData.value) { + return; + } + + const requestId = ++activeOverlapRequestId; + isOverlapLoading.value = true; + + try { + const rangeDays = parseInt(selectedPeriod.value, 10) || 30; + const overlapView = await activityStore.loadOverlapView({ + currentUserId: currentUser.value.id, + targetUserId: userId, + rangeDays, + dayLabels: dayLabels.value, + forceRefresh: false, + excludeHours: { + enabled: excludeHoursEnabled.value, + startHour: parseInt(excludeStartHour.value, 10), + endHour: parseInt(excludeEndHour.value, 10) + } + }); + if (requestId !== activeOverlapRequestId || userDialog.value.id !== userId) { + return; + } + overlapHeatmapView.value = { + rawBuckets: overlapView.rawBuckets, + normalizedBuckets: overlapView.normalizedBuckets + }; + hasOverlapData.value = overlapView.hasOverlapData; + overlapPercent.value = overlapView.overlapPercent; + bestOverlapTime.value = overlapView.bestOverlapTime; + } finally { + if (requestId === activeOverlapRequestId) { + isOverlapLoading.value = false; + } + } + } + async function onExcludeToggle(value) { excludeHoursEnabled.value = value; await configRepository.setBool('VRCX_overlapExcludeEnabled', value); - if (!isSelf.value && hasAnyData.value) { - await refreshData({ silent: true }); - } + await refreshOverlapOnly(); } async function onExcludeRangeChange() { await configRepository.setString('VRCX_overlapExcludeStart', excludeStartHour.value); await configRepository.setString('VRCX_overlapExcludeEnd', excludeEndHour.value); - if (!isSelf.value && hasAnyData.value) { - await refreshData({ silent: true }); - } + await refreshOverlapOnly(); } async function fetchMissingTopWorldThumbnails(worlds) { @@ -510,19 +548,34 @@ ensureActivityChart(); if (!activityChart) return; - activityChart.setOption(buildHeatmapOption({ - data: toHeatmapSeriesData(mainHeatmapView.value.normalizedBuckets, weekStartsOn.value), - rawBuckets: mainHeatmapView.value.rawBuckets, - dayLabels: displayDayLabels.value, - hourLabels, - weekStartsOn: weekStartsOn.value, - isDarkMode: isDarkMode.value, - emptyColor: isDarkMode.value ? 'hsl(220, 15%, 12%)' : 'hsl(210, 30%, 95%)', - scaleColors: isDarkMode.value - ? ['hsl(160, 40%, 24%)', 'hsl(150, 48%, 32%)', 'hsl(142, 55%, 38%)', 'hsl(142, 65%, 46%)', 'hsl(142, 80%, 55%)'] - : ['hsl(160, 40%, 82%)', 'hsl(155, 45%, 68%)', 'hsl(142, 55%, 55%)', 'hsl(142, 65%, 40%)', 'hsl(142, 76%, 30%)'], - unitLabel: t('dialog.user.activity.minutes_online') - }), { notMerge: true }); + activityChart.setOption( + buildHeatmapOption({ + data: toHeatmapSeriesData(mainHeatmapView.value.normalizedBuckets, weekStartsOn.value), + rawBuckets: mainHeatmapView.value.rawBuckets, + dayLabels: displayDayLabels.value, + hourLabels, + weekStartsOn: weekStartsOn.value, + isDarkMode: isDarkMode.value, + emptyColor: isDarkMode.value ? 'hsl(220, 15%, 12%)' : 'hsl(210, 30%, 95%)', + scaleColors: isDarkMode.value + ? [ + 'hsl(160, 40%, 24%)', + 'hsl(150, 48%, 32%)', + 'hsl(142, 55%, 38%)', + 'hsl(142, 65%, 46%)', + 'hsl(142, 80%, 55%)' + ] + : [ + 'hsl(160, 40%, 82%)', + 'hsl(155, 45%, 68%)', + 'hsl(142, 55%, 55%)', + 'hsl(142, 65%, 40%)', + 'hsl(142, 76%, 30%)' + ], + unitLabel: t('dialog.user.activity.minutes_online') + }), + { replaceMerge: ['series'] } + ); } function renderOverlapChart() { @@ -533,19 +586,34 @@ ensureOverlapChart(); if (!overlapChart) return; - overlapChart.setOption(buildHeatmapOption({ - data: toHeatmapSeriesData(overlapHeatmapView.value.normalizedBuckets, weekStartsOn.value), - rawBuckets: overlapHeatmapView.value.rawBuckets, - dayLabels: displayDayLabels.value, - hourLabels, - weekStartsOn: weekStartsOn.value, - isDarkMode: isDarkMode.value, - emptyColor: isDarkMode.value ? 'hsl(220, 15%, 12%)' : 'hsl(210, 30%, 95%)', - scaleColors: isDarkMode.value - ? ['hsl(260, 30%, 26%)', 'hsl(260, 42%, 36%)', 'hsl(260, 50%, 45%)', 'hsl(260, 60%, 54%)', 'hsl(260, 70%, 62%)'] - : ['hsl(260, 35%, 85%)', 'hsl(260, 42%, 70%)', 'hsl(260, 48%, 58%)', 'hsl(260, 55%, 48%)', 'hsl(260, 60%, 38%)'], - unitLabel: t('dialog.user.activity.overlap.minutes_overlap') - }), { notMerge: true }); + overlapChart.setOption( + buildHeatmapOption({ + data: toHeatmapSeriesData(overlapHeatmapView.value.normalizedBuckets, weekStartsOn.value), + rawBuckets: overlapHeatmapView.value.rawBuckets, + dayLabels: displayDayLabels.value, + hourLabels, + weekStartsOn: weekStartsOn.value, + isDarkMode: isDarkMode.value, + emptyColor: isDarkMode.value ? 'hsl(220, 15%, 12%)' : 'hsl(210, 30%, 95%)', + scaleColors: isDarkMode.value + ? [ + 'hsl(260, 30%, 26%)', + 'hsl(260, 42%, 36%)', + 'hsl(260, 50%, 45%)', + 'hsl(260, 60%, 54%)', + 'hsl(260, 70%, 62%)' + ] + : [ + 'hsl(260, 35%, 85%)', + 'hsl(260, 42%, 70%)', + 'hsl(260, 48%, 58%)', + 'hsl(260, 55%, 48%)', + 'hsl(260, 60%, 38%)' + ], + unitLabel: t('dialog.user.activity.overlap.minutes_overlap') + }), + { replaceMerge: ['series'] } + ); } function rebuildCharts() { @@ -557,7 +625,7 @@ } function onChartRightClick() { - toast(t('dialog.user.activity.easter_egg'), { position: 'bottom-center', icon: h(Tractor) }); + toast(t('dialog.user.activity.chart_hint'), { position: 'bottom-center', icon: h(Tractor) }); clearTimeout(easterEggTimer); easterEggTimer = setTimeout(() => { easterEggTimer = null; @@ -566,7 +634,7 @@ function onOverlapChartRightClick() { if (easterEggTimer) { - toast(t('dialog.user.activity.easter_egg_reply'), { position: 'bottom-center', icon: h(Sprout) }); + toast(t('dialog.user.activity.chart_hint_reply'), { position: 'bottom-center', icon: h(Sprout) }); } } @@ -577,40 +645,60 @@ void loadForVisibleTab(); } - watch(() => userDialog.value.id, () => { - resetActivityState(); - rebuildCharts(); - if (userDialog.value.visible && userDialog.value.activeTab === 'Activity') { - void nextTick(() => loadForVisibleTab()); + watch( + () => userDialog.value.id, + () => { + resetActivityState(); + rebuildCharts(); + if (userDialog.value.visible && userDialog.value.activeTab === 'Activity') { + void nextTick(() => loadForVisibleTab()); + } } - }); + ); watch([locale, isDarkMode, weekStartsOn], rebuildCharts); - watch(() => selectedPeriod.value, () => { - if (userDialog.value.visible && userDialog.value.activeTab === 'Activity') { - void onPeriodChange(); + watch( + () => selectedPeriod.value, + () => { + if (userDialog.value.visible && userDialog.value.activeTab === 'Activity') { + void onPeriodChange(); + } } - }); - watch(() => mainHeatmapView.value, () => { - nextTick(() => renderActivityChart()); - }, { deep: true }); - watch(() => overlapHeatmapView.value, () => { - nextTick(() => renderOverlapChart()); - }, { deep: true }); - watch(() => userDialog.value.visible, (visible) => { - if (!visible) return; - nextTick(() => { - activityChart?.resize(); - overlapChart?.resize(); - }); - if (userDialog.value.activeTab === 'Activity') { - void loadForVisibleTab(); + ); + watch( + () => mainHeatmapView.value, + () => { + nextTick(() => renderActivityChart()); + }, + { deep: true } + ); + watch( + () => overlapHeatmapView.value, + () => { + nextTick(() => renderOverlapChart()); + }, + { deep: true } + ); + watch( + () => userDialog.value.visible, + (visible) => { + if (!visible) return; + nextTick(() => { + activityChart?.resize(); + overlapChart?.resize(); + }); + if (userDialog.value.activeTab === 'Activity') { + void loadForVisibleTab(); + } } - }); - watch(() => userDialog.value.activeTab, (activeTab) => { - if (activeTab === 'Activity' && userDialog.value.visible) { - void loadForVisibleTab(); + ); + watch( + () => userDialog.value.activeTab, + (activeTab) => { + if (activeTab === 'Activity' && userDialog.value.visible) { + void loadForVisibleTab(); + } } - }); + ); onMounted(async () => { await initializeSettings(); diff --git a/src/localization/en.json b/src/localization/en.json index 3ca36076..1acfd0ef 100644 --- a/src/localization/en.json +++ b/src/localization/en.json @@ -1435,7 +1435,6 @@ "most_active_day": "Most active day:", "most_active_time": "Peak hours:", "period": "Period:", - "period_180": "Last 180 Days", "period_90": "Last 90 Days", "period_30": "Last 30 Days", "period_7": "Last 7 Days", @@ -1450,8 +1449,8 @@ "sat": "Sat", "sun": "Sun" }, - "easter_egg": "Did you farm your green squares today?", - "easter_egg_reply": "You can't farm this.", + "chart_hint": "Did you farm your green squares today?", + "chart_hint_reply": "You can't farm this.", "overlap": { "header": "Online Overlap", "peak_overlap": "Peak overlap:", diff --git a/src/stores/activity.js b/src/stores/activity.js index b34eb9ec..6541b86f 100644 --- a/src/stores/activity.js +++ b/src/stores/activity.js @@ -134,8 +134,21 @@ export const useActivityStore = defineStore('Activity', () => { return false; } - async function loadActivity(userId, { isSelf = false, rangeDays = 30, normalizeConfig, dayLabels, forceRefresh = false }) { - const snapshot = await ensureSnapshot(userId, { isSelf, rangeDays, forceRefresh }); + async function loadActivity( + userId, + { + isSelf = false, + rangeDays = 30, + normalizeConfig, + dayLabels, + forceRefresh = false + } + ) { + const snapshot = await ensureSnapshot(userId, { + isSelf, + rangeDays, + forceRefresh + }); const cacheKey = String(rangeDays); const currentCursor = snapshot.sync.sourceLastCreatedAt || ''; @@ -175,38 +188,55 @@ export const useActivityStore = defineStore('Activity', () => { builtAt: new Date().toISOString() }; snapshot.activityViews.set(cacheKey, view); - deferWrite(() => database.upsertActivityBucketCacheV2({ - ownerUserId: userId, - rangeDays, - viewKind: database.ACTIVITY_VIEW_KIND.ACTIVITY, - builtFromCursor: currentCursor, - rawBuckets: view.rawBuckets, - normalizedBuckets: view.normalizedBuckets, - summary: { - peakDay: view.peakDay, - peakTime: view.peakTime, - filteredEventCount: view.filteredEventCount - }, - builtAt: view.builtAt - })); + deferWrite(() => + database.upsertActivityBucketCacheV2({ + ownerUserId: userId, + rangeDays, + viewKind: database.ACTIVITY_VIEW_KIND.ACTIVITY, + builtFromCursor: currentCursor, + rawBuckets: view.rawBuckets, + normalizedBuckets: view.normalizedBuckets, + summary: { + peakDay: view.peakDay, + peakTime: view.peakTime, + filteredEventCount: view.filteredEventCount + }, + builtAt: view.builtAt + }) + ); return buildActivityResponse(snapshot, view); } - async function loadOverlap(currentUserId, targetUserId, { - rangeDays = 30, - dayLabels, - normalizeConfig, - excludeHours, - forceRefresh = false - }) { + async function loadOverlap( + currentUserId, + targetUserId, + { + rangeDays = 30, + dayLabels, + normalizeConfig, + excludeHours, + forceRefresh = false + } + ) { const [selfSnapshot, targetSnapshot] = await Promise.all([ - ensureSnapshot(currentUserId, { isSelf: true, rangeDays, forceRefresh }), - ensureSnapshot(targetUserId, { isSelf: false, rangeDays, forceRefresh }) + ensureSnapshot(currentUserId, { + isSelf: true, + rangeDays, + forceRefresh + }), + ensureSnapshot(targetUserId, { + isSelf: false, + rangeDays, + forceRefresh + }) ]); const excludeKey = overlapExcludeKey(excludeHours); const cacheKey = `${targetUserId}:${rangeDays}:${excludeKey}`; - const cursor = pairCursor(selfSnapshot.sync.sourceLastCreatedAt, targetSnapshot.sync.sourceLastCreatedAt); + const cursor = pairCursor( + selfSnapshot.sync.sourceLastCreatedAt, + targetSnapshot.sync.sourceLastCreatedAt + ); let view = targetSnapshot.overlapViews.get(cacheKey); if (view?.builtFromCursor === cursor) { @@ -246,26 +276,35 @@ export const useActivityStore = defineStore('Activity', () => { builtAt: new Date().toISOString() }; targetSnapshot.overlapViews.set(cacheKey, view); - deferWrite(() => database.upsertActivityBucketCacheV2({ - ownerUserId: currentUserId, - targetUserId, - rangeDays, - viewKind: database.ACTIVITY_VIEW_KIND.OVERLAP, - excludeKey, - builtFromCursor: cursor, - rawBuckets: view.rawBuckets, - normalizedBuckets: view.normalizedBuckets, - summary: { - overlapPercent: view.overlapPercent, - bestOverlapTime: view.bestOverlapTime - }, - builtAt: view.builtAt - })); + deferWrite(() => + database.upsertActivityBucketCacheV2({ + ownerUserId: currentUserId, + targetUserId, + rangeDays, + viewKind: database.ACTIVITY_VIEW_KIND.OVERLAP, + excludeKey, + builtFromCursor: cursor, + rawBuckets: view.rawBuckets, + normalizedBuckets: view.normalizedBuckets, + summary: { + overlapPercent: view.overlapPercent, + bestOverlapTime: view.bestOverlapTime + }, + builtAt: view.builtAt + }) + ); return view; } - async function loadTopWorlds(userId, { rangeDays = 30, limit = 5, isSelf = true, forceRefresh = false }) { - const snapshot = await ensureSnapshot(userId, { isSelf, rangeDays, forceRefresh }); + async function loadTopWorlds( + userId, + { rangeDays = 30, limit = 5, isSelf = true, forceRefresh = false } + ) { + const snapshot = await ensureSnapshot(userId, { + isSelf, + rangeDays, + forceRefresh + }); const cacheKey = `${rangeDays}:${limit}`; const currentCursor = snapshot.sync.sourceLastCreatedAt || ''; @@ -275,7 +314,10 @@ export const useActivityStore = defineStore('Activity', () => { } if (!forceRefresh) { - const persisted = await database.getActivityTopWorldsCacheV2(userId, rangeDays); + const persisted = await database.getActivityTopWorldsCacheV2( + userId, + rangeDays + ); if (persisted?.builtFromCursor === currentCursor) { snapshot.topWorldsViews.set(cacheKey, persisted); return persisted.worlds; @@ -292,14 +334,16 @@ export const useActivityStore = defineStore('Activity', () => { }; snapshot.topWorldsViews.set(cacheKey, entry); deferWrite(() => database.replaceActivityTopWorldsCacheV2(entry)); - deferWrite(() => database.upsertActivityRangeCacheV2({ - userId, - rangeDays, - cacheKind: database.ACTIVITY_RANGE_CACHE_KIND.TOP_WORLDS, - isComplete: true, - builtFromCursor: currentCursor, - builtAt: entry.builtAt - })); + deferWrite(() => + database.upsertActivityRangeCacheV2({ + userId, + rangeDays, + cacheKind: database.ACTIVITY_RANGE_CACHE_KIND.TOP_WORLDS, + isComplete: true, + builtFromCursor: currentCursor, + builtAt: entry.builtAt + }) + ); return worlds; } @@ -307,7 +351,13 @@ export const useActivityStore = defineStore('Activity', () => { return loadActivity(userId, { ...options, forceRefresh: true }); } - async function loadActivityView({ userId, isSelf = false, rangeDays = 30, dayLabels, forceRefresh = false }) { + async function loadActivityView({ + userId, + isSelf = false, + rangeDays = 30, + dayLabels, + forceRefresh = false + }) { const response = await loadActivity(userId, { isSelf, rangeDays, @@ -325,7 +375,14 @@ export const useActivityStore = defineStore('Activity', () => { }; } - async function loadOverlapView({ currentUserId, targetUserId, rangeDays = 30, dayLabels, excludeHours, forceRefresh = false }) { + async function loadOverlapView({ + currentUserId, + targetUserId, + rangeDays = 30, + dayLabels, + excludeHours, + forceRefresh = false + }) { const response = await loadOverlap(currentUserId, targetUserId, { rangeDays, dayLabels, @@ -342,7 +399,12 @@ export const useActivityStore = defineStore('Activity', () => { }; } - async function loadTopWorldsView({ userId, rangeDays = 30, limit = 5, forceRefresh = false }) { + async function loadTopWorldsView({ + userId, + rangeDays = 30, + limit = 5, + forceRefresh = false + }) { return loadTopWorlds(userId, { rangeDays, limit, @@ -399,7 +461,10 @@ async function hydrateSnapshot(userId, isSelf) { snapshot.sync = { ...snapshot.sync, ...syncState, - isSelf: typeof syncState.isSelf === 'boolean' ? syncState.isSelf : snapshot.isSelf + isSelf: + typeof syncState.isSelf === 'boolean' + ? syncState.isSelf + : snapshot.isSelf }; } if (sessions.length > 0) { @@ -408,7 +473,10 @@ async function hydrateSnapshot(userId, isSelf) { return snapshot; } -async function ensureSnapshot(userId, { isSelf, rangeDays, forceRefresh = false }) { +async function ensureSnapshot( + userId, + { isSelf, rangeDays, forceRefresh = false } +) { const jobKey = `${userId}:${isSelf}:${rangeDays}:${forceRefresh ? 'force' : 'normal'}`; const existingJob = inFlightJobs.get(jobKey); if (existingJob) { @@ -440,7 +508,10 @@ async function fullRefresh(snapshot, rangeDays) { isSelf: snapshot.isSelf, fromDays: rangeDays }); - const sourceLastCreatedAt = sourceItems.length > 0 ? sourceItems[sourceItems.length - 1].created_at : ''; + const sourceLastCreatedAt = + sourceItems.length > 0 + ? sourceItems[sourceItems.length - 1].created_at + : ''; const result = await workerCall('computeSessionsSnapshot', { sourceType: snapshot.isSelf ? 'self_gamelog' : 'friend_presence', rows: snapshot.isSelf ? sourceItems : undefined, @@ -462,16 +533,20 @@ async function fullRefresh(snapshot, rangeDays) { }; clearDerivedViews(snapshot); - deferWrite(() => database.replaceActivitySessionsV2(snapshot.userId, snapshot.sessions)); + deferWrite(() => + database.replaceActivitySessionsV2(snapshot.userId, snapshot.sessions) + ); deferWrite(() => database.upsertActivitySyncStateV2(snapshot.sync)); - deferWrite(() => database.upsertActivityRangeCacheV2({ - userId: snapshot.userId, - rangeDays, - cacheKind: database.ACTIVITY_RANGE_CACHE_KIND.SESSIONS, - isComplete: true, - builtFromCursor: snapshot.sync.sourceLastCreatedAt, - builtAt: snapshot.sync.updatedAt - })); + deferWrite(() => + database.upsertActivityRangeCacheV2({ + userId: snapshot.userId, + rangeDays, + cacheKind: database.ACTIVITY_RANGE_CACHE_KIND.SESSIONS, + isComplete: true, + builtFromCursor: snapshot.sync.sourceLastCreatedAt, + builtAt: snapshot.sync.updatedAt + }) + ); } async function incrementalRefresh(snapshot) { @@ -496,15 +571,18 @@ async function incrementalRefresh(snapshot) { sourceType: snapshot.isSelf ? 'self_gamelog' : 'friend_presence', rows: snapshot.isSelf ? sourceItems : undefined, events: snapshot.isSelf ? undefined : sourceItems, - initialStart: snapshot.isSelf ? null : snapshot.sync.pendingSessionStartAt, + initialStart: snapshot.isSelf + ? null + : snapshot.sync.pendingSessionStartAt, nowMs: Date.now(), mayHaveOpenTail: snapshot.isSelf, sourceRevision: sourceLastCreatedAt }); - const replaceFromStartAt = snapshot.sessions.length > 0 - ? snapshot.sessions[Math.max(snapshot.sessions.length - 1, 0)].start - : null; + const replaceFromStartAt = + snapshot.sessions.length > 0 + ? snapshot.sessions[Math.max(snapshot.sessions.length - 1, 0)].start + : null; const merged = mergeSessions(snapshot.sessions, result.sessions); snapshot.sessions = merged; snapshot.sync = { @@ -515,14 +593,17 @@ async function incrementalRefresh(snapshot) { }; clearDerivedViews(snapshot); - const tailSessions = replaceFromStartAt === null - ? merged - : merged.filter((session) => session.start >= replaceFromStartAt); - deferWrite(() => database.appendActivitySessionsV2({ - userId: snapshot.userId, - sessions: tailSessions, - replaceFromStartAt - })); + const tailSessions = + replaceFromStartAt === null + ? merged + : merged.filter((session) => session.start >= replaceFromStartAt); + deferWrite(() => + database.appendActivitySessionsV2({ + userId: snapshot.userId, + sessions: tailSessions, + replaceFromStartAt + }) + ); deferWrite(() => database.upsertActivitySyncStateV2(snapshot.sync)); } @@ -550,58 +631,120 @@ async function expandRange(snapshot, rangeDays) { if (result.sessions.length > 0) { snapshot.sessions = mergeSessions(result.sessions, snapshot.sessions); - deferWrite(() => database.replaceActivitySessionsV2(snapshot.userId, snapshot.sessions)); + deferWrite(() => + database.replaceActivitySessionsV2( + snapshot.userId, + snapshot.sessions + ) + ); } snapshot.sync.cachedRangeDays = rangeDays; snapshot.sync.updatedAt = new Date().toISOString(); clearDerivedViews(snapshot); deferWrite(() => database.upsertActivitySyncStateV2(snapshot.sync)); - deferWrite(() => database.upsertActivityRangeCacheV2({ - userId: snapshot.userId, - rangeDays, - cacheKind: database.ACTIVITY_RANGE_CACHE_KIND.SESSIONS, - isComplete: true, - builtFromCursor: snapshot.sync.sourceLastCreatedAt, - builtAt: snapshot.sync.updatedAt - })); + deferWrite(() => + database.upsertActivityRangeCacheV2({ + userId: snapshot.userId, + rangeDays, + cacheKind: database.ACTIVITY_RANGE_CACHE_KIND.SESSIONS, + isComplete: true, + builtFromCursor: snapshot.sync.sourceLastCreatedAt, + builtAt: snapshot.sync.updatedAt + }) + ); } function pickActivityNormalizeConfig(isSelf, rangeDays) { const role = isSelf ? 'self' : 'friend'; - return { - self: { - 7: { floorPercentile: 10, capPercentile: 80, rankWeight: 0.15, targetCoverage: 0.12, targetVolume: 40 }, - 30: { floorPercentile: 15, capPercentile: 85, rankWeight: 0.20, targetCoverage: 0.25, targetVolume: 60 }, - 90: { floorPercentile: 15, capPercentile: 85, rankWeight: 0.20, targetCoverage: 0.30, targetVolume: 50 }, - 180: { floorPercentile: 20, capPercentile: 85, rankWeight: 0.20, targetCoverage: 0.35, targetVolume: 40 } - }, - friend: { - 7: { floorPercentile: 10, capPercentile: 80, rankWeight: 0.15, targetCoverage: 0.12, targetVolume: 40 }, - 30: { floorPercentile: 15, capPercentile: 85, rankWeight: 0.20, targetCoverage: 0.25, targetVolume: 60 }, - 90: { floorPercentile: 15, capPercentile: 85, rankWeight: 0.20, targetCoverage: 0.30, targetVolume: 50 }, - 180: { floorPercentile: 20, capPercentile: 85, rankWeight: 0.20, targetCoverage: 0.35, targetVolume: 40 } + return ( + { + self: { + 7: { + floorPercentile: 10, + capPercentile: 80, + rankWeight: 0.15, + targetCoverage: 0.12, + targetVolume: 40 + }, + 30: { + floorPercentile: 15, + capPercentile: 85, + rankWeight: 0.2, + targetCoverage: 0.25, + targetVolume: 60 + }, + 90: { + floorPercentile: 15, + capPercentile: 85, + rankWeight: 0.2, + targetCoverage: 0.3, + targetVolume: 50 + } + }, + friend: { + 7: { + floorPercentile: 10, + capPercentile: 80, + rankWeight: 0.15, + targetCoverage: 0.12, + targetVolume: 40 + }, + 30: { + floorPercentile: 15, + capPercentile: 85, + rankWeight: 0.2, + targetCoverage: 0.25, + targetVolume: 60 + }, + 90: { + floorPercentile: 15, + capPercentile: 85, + rankWeight: 0.2, + targetCoverage: 0.3, + targetVolume: 50 + } + } + }[role][rangeDays] || { + floorPercentile: 15, + capPercentile: 85, + rankWeight: 0.2, + targetCoverage: 0.25, + targetVolume: 60 } - }[role][rangeDays] || { - floorPercentile: 15, - capPercentile: 85, - rankWeight: 0.20, - targetCoverage: 0.25, - targetVolume: 60 - }; + ); } function pickOverlapNormalizeConfig(rangeDays) { - return { - 7: { floorPercentile: 10, capPercentile: 80, rankWeight: 0.15, targetCoverage: 0.08, targetVolume: 15 }, - 30: { floorPercentile: 15, capPercentile: 85, rankWeight: 0.20, targetCoverage: 0.15, targetVolume: 25 }, - 90: { floorPercentile: 15, capPercentile: 85, rankWeight: 0.20, targetCoverage: 0.18, targetVolume: 20 }, - 180: { floorPercentile: 20, capPercentile: 85, rankWeight: 0.20, targetCoverage: 0.20, targetVolume: 15 } - }[rangeDays] || { - floorPercentile: 15, - capPercentile: 85, - rankWeight: 0.20, - targetCoverage: 0.15, - targetVolume: 25 - }; + return ( + { + 7: { + floorPercentile: 10, + capPercentile: 80, + rankWeight: 0.15, + targetCoverage: 0.08, + targetVolume: 15 + }, + 30: { + floorPercentile: 15, + capPercentile: 85, + rankWeight: 0.2, + targetCoverage: 0.15, + targetVolume: 25 + }, + 90: { + floorPercentile: 15, + capPercentile: 85, + rankWeight: 0.2, + targetCoverage: 0.18, + targetVolume: 20 + } + }[rangeDays] || { + floorPercentile: 15, + capPercentile: 85, + rankWeight: 0.2, + targetCoverage: 0.15, + targetVolume: 25 + } + ); }