refactor activity tab

This commit is contained in:
pa
2026-03-21 21:35:28 +09:00
parent 046730215e
commit e5ea66e5d5
16 changed files with 2154 additions and 1660 deletions

View File

@@ -0,0 +1,107 @@
import {
buildSessionsFromEvents,
buildSessionsFromGamelog,
buildHeatmapBuckets,
buildOverlapBuckets,
computeActivityView,
computeOverlapView,
normalizeBuckets
} from '../shared/utils/activityEngine.js';
self.addEventListener('message', (event) => {
const { type, seq, payload } = event.data;
try {
let result;
switch (type) {
case 'computeSessionsSnapshot':
result = computeSessionsSnapshot(payload);
break;
case 'computeActivityView':
result = computeActivityView(payload);
break;
case 'computeOverlapView':
result = computeOverlapView(payload);
break;
case 'buildSessionsFromGamelog':
result = {
sessions: buildSessionsFromGamelog(
payload.rows || [],
payload.mergeGapMs,
payload.nowMs
)
};
break;
case 'buildSessionsFromEvents':
result = buildSessionsFromEvents(payload.events || [], payload.initialStart ?? null);
break;
case 'buildHeatmapBuckets':
result = {
buckets: buildHeatmapBuckets(
payload.sessions || [],
payload.windowStartMs,
payload.nowMs,
payload.maxSessionMs
)
};
break;
case 'buildOverlapBuckets':
result = {
buckets: buildOverlapBuckets(
payload.selfSessions || [],
payload.friendSessions || [],
payload.windowStartMs,
payload.nowMs,
payload.maxSessionMs
)
};
break;
case 'normalizeHeatmapBuckets':
result = {
normalized: normalizeBuckets(
payload.buckets || [],
payload.thresholdMinutes,
payload.capPercentile,
payload.mode
)
};
break;
default:
throw new Error(`Unknown activity worker task: ${type}`);
}
self.postMessage({ type: 'result', seq, payload: result });
} catch (error) {
self.postMessage({
type: 'error',
seq,
payload: { message: error instanceof Error ? error.message : String(error) }
});
}
});
function computeSessionsSnapshot(payload) {
const sourceRevision = payload.sourceRevision || '';
if (payload.sourceType === 'self_gamelog') {
const sessions = buildSessionsFromGamelog(payload.rows, payload.mergeGapMs, payload.nowMs)
.map((session, index, list) => ({
...session,
isOpenTail: index === list.length - 1 && payload.mayHaveOpenTail === true,
sourceRevision
}));
return {
sessions,
pendingSessionStartAt: null
};
}
const result = buildSessionsFromEvents(payload.events, payload.initialStart);
return {
pendingSessionStartAt: result.pendingSessionStartAt,
sessions: result.sessions.map((session) => ({
...session,
isOpenTail: false,
sourceRevision
}))
};
}

View File

@@ -0,0 +1,34 @@
import ActivityWorker from './activityWorker.js?worker&inline';
let worker = null;
let workerSeq = 0;
const pendingWorkerCallbacks = new Map();
function getWorker() {
if (!worker) {
worker = new ActivityWorker();
worker.onmessage = (event) => {
const { type, seq, payload } = event.data;
const callback = pendingWorkerCallbacks.get(seq);
if (!callback) {
return;
}
pendingWorkerCallbacks.delete(seq);
if (type === 'error') {
callback.reject(new Error(payload.message));
return;
}
callback.resolve(payload);
};
}
return worker;
}
export function runActivityWorkerTask(type, payload) {
return new Promise((resolve, reject) => {
const seq = ++workerSeq;
pendingWorkerCallbacks.set(seq, { resolve, reject });
getWorker().postMessage({ type, seq, payload });
});
}