first attempt at stream sidebar, tasks, and events

This commit is contained in:
Arjun Patel
2026-06-11 22:38:55 -07:00
parent 9e3a30f74e
commit 1baae790c6
219 changed files with 7463 additions and 809 deletions
+28 -20
View File
@@ -33,6 +33,31 @@ import type {
// --- Converter ---
// Firestore stores timestamps as `Timestamp`; zod expects `Date`. Coerce the
// per-type property fields that carry timestamps.
function coerceLeafPropertyDates(
type: ParticleType,
properties: DocumentData | undefined,
): DocumentData | undefined {
if (!properties) return properties;
if (type === 'text' && properties.edited_at) {
return {
...properties,
edited_at: (properties.edited_at as Timestamp).toDate(),
};
}
if (type === 'event') {
return {
...properties,
start_at: (properties.start_at as Timestamp).toDate(),
end_at: properties.end_at
? (properties.end_at as Timestamp).toDate()
: undefined,
};
}
return properties;
}
const particleConverter: FirestoreDataConverter<Particle> = {
toFirestore(particle: Particle): DocumentData {
const { id: _id, created_at, updated_at, ...rest } = particle;
@@ -79,7 +104,6 @@ const particleConverter: FirestoreDataConverter<Particle> = {
: undefined,
huddle_active_participants:
raw.huddle_active_participants ?? undefined,
status: raw.status ?? undefined,
});
case 'folder':
return ParticleSchema.parse({
@@ -96,17 +120,10 @@ const particleConverter: FirestoreDataConverter<Particle> = {
case 'media':
case 'file':
case 'text':
case 'quest':
case 'task':
case 'event':
case 'paper': {
// Firestore stores timestamps as `Timestamp`; zod expects `Date`. Text
// particles carry `properties.edited_at`, so coerce it if present.
const properties =
type === 'text' && raw.properties?.edited_at
? {
...raw.properties,
edited_at: (raw.properties.edited_at as Timestamp).toDate(),
}
: raw.properties;
const properties = coerceLeafPropertyDates(type, raw.properties);
return ParticleSchema.parse({
id: snap.id,
type: raw.type,
@@ -308,7 +325,6 @@ export async function createStreamParticle(
created_at: new Date(),
created_by_human_id: createdByHumanId,
visible_to: visibleTo,
status: 'open',
});
const ref = await addDoc(typedCollection(collectionPath), particle);
return ref.id;
@@ -382,14 +398,6 @@ export async function updateParticle(
});
}
export async function updateStreamStatus(
docPath: string,
status: 'open' | 'closed',
): Promise<void> {
const particleRef = typedDoc(docPath);
await updateDoc(particleRef, { status, updated_at: serverTimestamp() });
}
/**
* Soft-delete (tombstone) a non-container particle. The Firestore doc stays
* in place so concurrent viewers see the deletion inline rather than being