mobile: task particles — view, edit, and compose (parity phase 3)
Bring the task particle to parity with desktop's richer model: - Replace the thin `quest` schema with desktop's `task` model (ChecklistItem, TaskProperties: title/notes/checklist/assigned_to/done) in the discriminated union, the Firestore converter, and consumers (StreamCard, FallbackParticleView). - New TaskParticleView renders an editable card (round done checkbox, title, notes, checklist with add/toggle/edit/remove, assignee chips) persisting each edit to Firestore; an 8s dwell auto-advances and field focus suspends playback. Wired into StreamView's render switch. - Compose: a task button in the ComposeDock opens a TaskComposeSheet (createTaskParticle helper). Gated off in the new-stream flow, where a stream's first particle must be text or media. Note: particles are written client-side to Firestore, matching desktop; Orion's REST validator still only accepts `quest`, which is a pre-existing inconsistency to reconcile backend-side separately. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01LqGPzXQ1AA9CqYHgCgmbtV
This commit is contained in:
@@ -97,7 +97,7 @@ const particleConverter: FirestoreDataConverter<Particle> = {
|
||||
case 'media':
|
||||
case 'file':
|
||||
case 'text':
|
||||
case 'quest':
|
||||
case 'task':
|
||||
case 'paper': {
|
||||
// Firestore stores timestamps as `Timestamp`; zod expects `Date`. Text
|
||||
// particles carry `properties.edited_at`, so coerce it if present.
|
||||
|
||||
@@ -103,6 +103,32 @@ export async function createTextParticle({
|
||||
return createParticle(collectionPath, 'text', { content }, createdByHumanId);
|
||||
}
|
||||
|
||||
interface CreateTaskParticleParams {
|
||||
targetPath: ParticlePath;
|
||||
title: string;
|
||||
notes?: string;
|
||||
createdByHumanId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a `task` particle. Mirrors createTextParticle — the checklist and
|
||||
* assignee are left empty and edited inline in the task card afterwards.
|
||||
*/
|
||||
export async function createTaskParticle({
|
||||
targetPath,
|
||||
title,
|
||||
notes,
|
||||
createdByHumanId,
|
||||
}: CreateTaskParticleParams): Promise<string> {
|
||||
const collectionPath = toFirestoreChildrenPath(targetPath);
|
||||
return createParticle(
|
||||
collectionPath,
|
||||
'task',
|
||||
{ title, done: false, ...(notes ? { notes } : {}) },
|
||||
createdByHumanId,
|
||||
);
|
||||
}
|
||||
|
||||
function extensionFromMime(mime: string): string {
|
||||
if (mime === 'video/mp4') return '.mp4';
|
||||
if (mime === 'video/quicktime') return '.mov';
|
||||
|
||||
Reference in New Issue
Block a user