fix: make activity view passive
This commit is contained in:
parent
8010d6c601
commit
3b7258ed3e
|
|
@ -115,76 +115,10 @@
|
|||
};
|
||||
}
|
||||
|
||||
function activityId() {
|
||||
return 'activity-' + Date.now() + '-' + Math.random().toString(36).slice(2);
|
||||
}
|
||||
|
||||
function eventPayload(event) {
|
||||
return event && event.payload && typeof event.payload === 'object' ? event.payload : {};
|
||||
}
|
||||
|
||||
function firstText(values) {
|
||||
for (var i = 0; i < values.length; i += 1) {
|
||||
var value = text(values[i]).trim();
|
||||
if (value) return value;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function titleFromEvent(event, payload) {
|
||||
return firstText([
|
||||
payload.title,
|
||||
payload.name,
|
||||
payload.path,
|
||||
payload.url,
|
||||
payload.captureId,
|
||||
event.name,
|
||||
'Activity event'
|
||||
]);
|
||||
}
|
||||
|
||||
function summaryFromEvent(event, payload) {
|
||||
if (payload.text) return text(payload.text).trim();
|
||||
return firstText([
|
||||
payload.summary,
|
||||
payload.description,
|
||||
payload.path,
|
||||
payload.url,
|
||||
payload.domain,
|
||||
event.name
|
||||
]);
|
||||
}
|
||||
|
||||
function eventToActivity(event, scope) {
|
||||
var payload = eventPayload(event);
|
||||
var workspaceRoot = workspaceFromPayload(payload) || (scope && scope.workspaceRoot) || '';
|
||||
return {
|
||||
activityId: activityId(),
|
||||
type: text(event && event.name).trim() || 'activity.event',
|
||||
title: titleFromEvent(event || {}, payload),
|
||||
summary: summaryFromEvent(event || {}, payload),
|
||||
occurredAt: text(payload.occurredAt || payload.capturedAt || (event && event.timestamp) || new Date().toISOString()),
|
||||
receivedAt: new Date().toISOString(),
|
||||
sourcePluginId: text((event && event.pluginId) || payload.pluginId || payload.sourcePluginId),
|
||||
workspaceRootPath: workspaceRoot,
|
||||
payload: payload
|
||||
};
|
||||
}
|
||||
|
||||
function manualActivity(scope) {
|
||||
return {
|
||||
activityId: activityId(),
|
||||
type: 'activity.manual',
|
||||
title: 'Manual activity',
|
||||
summary: 'Manually recorded activity event',
|
||||
occurredAt: new Date().toISOString(),
|
||||
receivedAt: new Date().toISOString(),
|
||||
sourcePluginId: PLUGIN_ID,
|
||||
workspaceRootPath: (scope && scope.workspaceRoot) || '',
|
||||
payload: {}
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeStoredEvents(value, storageKey) {
|
||||
if (!Array.isArray(value)) return [];
|
||||
return value.filter(function (item) {
|
||||
|
|
@ -268,14 +202,6 @@
|
|||
var titleEl = el('span', { className: 'activity-title', textContent: scope.mode === 'global' ? 'Activity' : 'Activity · ' + scope.label });
|
||||
var countEl = el('span', { className: 'activity-count' });
|
||||
var statusEl = el('span', { className: 'activity-status' });
|
||||
var manualBtn = el('button', {
|
||||
className: 'activity-btn',
|
||||
'data-activity-action': 'manual',
|
||||
textContent: 'Record',
|
||||
onClick: function () {
|
||||
addActivity(manualActivity(scope));
|
||||
}
|
||||
});
|
||||
var clearBtn = el('button', {
|
||||
className: 'activity-btn danger',
|
||||
'data-activity-action': 'clear',
|
||||
|
|
@ -293,7 +219,6 @@
|
|||
toolbar.appendChild(countEl);
|
||||
toolbar.appendChild(el('span', { className: 'activity-spacer' }));
|
||||
toolbar.appendChild(statusEl);
|
||||
toolbar.appendChild(manualBtn);
|
||||
toolbar.appendChild(clearBtn);
|
||||
|
||||
var listEl = el('div', { className: 'activity-list' });
|
||||
|
|
@ -331,14 +256,6 @@
|
|||
});
|
||||
}
|
||||
|
||||
function addActivity(activity) {
|
||||
activity._storageKey = scope.key;
|
||||
events = sortEvents([activity].concat(events));
|
||||
statusText = 'Activity recorded';
|
||||
statusClass = '';
|
||||
return persist().then(render);
|
||||
}
|
||||
|
||||
function renderList() {
|
||||
listEl.innerHTML = '';
|
||||
if (events.length === 0) {
|
||||
|
|
@ -406,7 +323,7 @@
|
|||
return api.events.subscribe(eventName, function (event) {
|
||||
var eventWorkspace = workspaceFromPayload(eventPayload(event));
|
||||
if (scope.mode === 'workspace' && eventWorkspace && eventWorkspace !== scope.workspaceRoot) return Promise.resolve();
|
||||
return addActivity(eventToActivity(event, scope));
|
||||
return loadStored().then(render);
|
||||
}).then(function (unsubscribe) {
|
||||
if (typeof unsubscribe === 'function') unsubscribers.push(unsubscribe);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -157,11 +157,31 @@ async function mountWithApi(api, props = { workspaceNode: { name: 'Project' }, w
|
|||
(async () => {
|
||||
const api = makeApi();
|
||||
const { component, container } = await mountWithApi(api);
|
||||
const projectKey = 'events:workspace:Project';
|
||||
const clientKey = 'events:workspace:ClientA';
|
||||
const globalKey = 'events:global';
|
||||
|
||||
for (const name of ['file.opened', 'file.changed', 'note.saved', 'action.started', 'browser.capture.received', 'case.selected', 'browser.capture.selection']) {
|
||||
if (typeof api.handlers[name] !== 'function') throw new Error(`${name} subscription missing`);
|
||||
}
|
||||
|
||||
await api.settings.write(projectKey, [{
|
||||
activityId: 'capture-1',
|
||||
type: 'browser.capture.selection',
|
||||
title: 'Example Article',
|
||||
summary: 'Selected text',
|
||||
occurredAt: '2026-06-27T00:00:00Z',
|
||||
sourcePluginId: 'verstak.browser-inbox',
|
||||
workspaceRootPath: 'Project',
|
||||
payload: {
|
||||
captureId: 'capture-1',
|
||||
kind: 'selection',
|
||||
title: 'Example Article',
|
||||
url: 'https://example.com/article',
|
||||
text: 'Selected text',
|
||||
workspaceRootPath: 'Project',
|
||||
},
|
||||
}]);
|
||||
await api.handlers['browser.capture.selection']({
|
||||
name: 'browser.capture.selection',
|
||||
pluginId: 'verstak.browser-inbox',
|
||||
|
|
@ -176,9 +196,6 @@ async function mountWithApi(api, props = { workspaceNode: { name: 'Project' }, w
|
|||
});
|
||||
await flush();
|
||||
|
||||
const projectKey = 'events:workspace:Project';
|
||||
const clientKey = 'events:workspace:ClientA';
|
||||
const globalKey = 'events:global';
|
||||
const stored = api.storedEvents(projectKey);
|
||||
if (stored.length !== 1) throw new Error(`expected one stored activity event, got ${stored.length}`);
|
||||
if (stored[0].type !== 'browser.capture.selection') throw new Error('stored event type mismatch');
|
||||
|
|
@ -189,6 +206,20 @@ async function mountWithApi(api, props = { workspaceNode: { name: 'Project' }, w
|
|||
|
||||
const clientView = await mountWithApi(api, { workspaceNode: { name: 'ClientA' }, workspaceRootPath: 'ClientA' });
|
||||
if (clientView.container.textContent.includes('Example Article')) throw new Error('Project activity leaked into ClientA workspace view');
|
||||
await api.settings.write(clientKey, [{
|
||||
activityId: 'client-note',
|
||||
type: 'note.saved',
|
||||
title: 'Client note',
|
||||
summary: 'ClientA/Notes/Client.md',
|
||||
occurredAt: '2026-06-27T00:10:00Z',
|
||||
sourcePluginId: 'verstak.notes',
|
||||
workspaceRootPath: 'ClientA',
|
||||
payload: {
|
||||
title: 'Client note',
|
||||
path: 'ClientA/Notes/Client.md',
|
||||
workspaceRootPath: 'ClientA',
|
||||
},
|
||||
}]);
|
||||
await api.handlers['note.saved']({
|
||||
name: 'note.saved',
|
||||
pluginId: 'verstak.notes',
|
||||
|
|
@ -210,11 +241,7 @@ async function mountWithApi(api, props = { workspaceNode: { name: 'Project' }, w
|
|||
component.unmount && component.unmount(globalView.container);
|
||||
|
||||
const manualButton = walk(container, (node) => node.getAttribute && node.getAttribute('data-activity-action') === 'manual');
|
||||
if (!manualButton) throw new Error('manual activity button not found');
|
||||
manualButton.click();
|
||||
await flush();
|
||||
if (api.storedEvents(projectKey).length !== 2) throw new Error('manual activity was not stored');
|
||||
if (!container.textContent.includes('Manual activity')) throw new Error('manual activity was not rendered');
|
||||
if (manualButton) throw new Error('manual activity button should not be rendered');
|
||||
|
||||
const clearButton = walk(container, (node) => node.getAttribute && node.getAttribute('data-activity-action') === 'clear');
|
||||
if (!clearButton) throw new Error('clear activity button not found');
|
||||
|
|
|
|||
Loading…
Reference in New Issue