refactor: use interfaces for function params

This commit is contained in:
talksik
2026-04-07 14:21:34 -07:00
parent 4a9cb7fc58
commit e7384cb45c
4 changed files with 76 additions and 70 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ interface FolderViewProps {
} }
export function FolderView({ path, folderParticle }: FolderViewProps) { export function FolderView({ path, folderParticle }: FolderViewProps) {
const { children, error, isLoading } = useLiveParticleChildren(path); const { children, error, isLoading } = useLiveParticleChildren({ path });
const { networkId } = parseParticlePath(path); const { networkId } = parseParticlePath(path);
return ( return (
+39 -28
View File
@@ -1,4 +1,4 @@
import { useState, useEffect, useMemo } from "react"; import { useState, useEffect } from "react";
import { import {
subscribeToParticle, subscribeToParticle,
subscribeToParticleChildren, subscribeToParticleChildren,
@@ -26,13 +26,12 @@ export function useLiveParticle(path: ParticlePath): UseLiveParticleResult {
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<Error | null>(null); const [error, setError] = useState<Error | null>(null);
const docPath = useMemo(() => toFirestoreDocPath(path), [path]);
useEffect(() => { useEffect(() => {
setIsLoading(true); setIsLoading(true);
setError(null); setError(null);
setParticle(null); setParticle(null);
const docPath = toFirestoreDocPath(path);
const unsubscribe = subscribeToParticle( const unsubscribe = subscribeToParticle(
docPath, docPath,
(data) => { (data) => {
@@ -46,7 +45,7 @@ export function useLiveParticle(path: ParticlePath): UseLiveParticleResult {
); );
return unsubscribe; return unsubscribe;
}, [docPath]); }, [path]);
return { particle, isLoading, error }; return { particle, isLoading, error };
} }
@@ -57,47 +56,59 @@ interface UseLiveParticleChildrenResult {
error: Error | null; error: Error | null;
} }
interface UseLiveParticleChildrenParams {
orderByField?: string;
orderDirection?: "asc" | "desc";
visibilityScopes?: string[];
onAdded?: (child: Particle) => void;
onRemoved?: (child: Particle, updatedChildren: Particle[]) => void;
whereFilter?: QueryFieldFilterConstraint;
}
export function useLiveParticleChildren( export function useLiveParticleChildren(
path: ParticlePath, path: ParticlePath,
orderByField: string = "created_at", {
orderDirection: "asc" | "desc" = "desc", orderByField = "created_at",
visibilityScopes?: string[], orderDirection = "desc",
onAdded?: (child: Particle) => void, visibilityScopes,
onRemoved?: (child: Particle, updatedChildren: Particle[]) => void, onAdded,
whereFilter?: QueryFieldFilterConstraint onRemoved,
whereFilter,
}: UseLiveParticleChildrenParams
): UseLiveParticleChildrenResult { ): UseLiveParticleChildrenResult {
const [children, setChildren] = useState<Particle[]>([]); const [children, setChildren] = useState<Particle[]>([]);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<Error | null>(null); const [error, setError] = useState<Error | null>(null);
const collectionPath = useMemo(() => toFirestoreChildrenPath(path), [path]);
useEffect(() => { useEffect(() => {
setIsLoading(true); setIsLoading(true);
setError(null); setError(null);
setChildren([]); setChildren([]);
const collectionPath = toFirestoreChildrenPath(path);
const unsubscribe = subscribeToParticleChildren( const unsubscribe = subscribeToParticleChildren(
collectionPath, collectionPath,
(data) => { {
setChildren(data); onData: (data) => {
setIsLoading(false); setChildren(data);
}, setIsLoading(false);
(err) => { },
setError(err); onError: (err) => {
setIsLoading(false); setError(err);
}, setIsLoading(false);
visibilityScopes, },
orderByField, visibilityScopes,
orderDirection, orderByField,
onAdded, orderDirection,
onRemoved, onAdded,
whereFilter, onRemoved,
whereFilter,
}
); );
return unsubscribe; return unsubscribe;
// FIX: do we need to listen to more deps? Would that cause side effects that break behavior }, [path]);
}, [collectionPath]);
return { children, isLoading, error }; return { children, isLoading, error };
} }
+6 -31
View File
@@ -1,10 +1,8 @@
import { useEffect, useMemo, useState } from "react"; import { useMemo } from "react";
import { useLiveParticleChildren } from "@/hooks/use-particle"; import { useLiveParticleChildren } from "@/hooks/use-particle";
import { useAuthStore } from "@/stores/auth-store"; import { useAuthStore } from "@/stores/auth-store";
import { parseParticlePath, type ParticlePath } from "@/lib/particle-path"; import { parseParticlePath, type ParticlePath } from "@/lib/particle-path";
import type { Particle, StreamProperties } from "@/api/types"; import type { Particle, StreamProperties } from "@/api/types";
import { where, Timestamp } from "firebase/firestore";
import { useNetwork } from "@/hooks/use-networks";
type StreamParticle = Particle & { type: "stream"; properties: StreamProperties }; type StreamParticle = Particle & { type: "stream"; properties: StreamProperties };
@@ -27,37 +25,14 @@ export function useStreamParticles(path: ParticlePath): UseStreamParticlesResult
const { networkId } = parseParticlePath(path); const { networkId } = parseParticlePath(path);
const user = useAuthStore((s) => s.user); const user = useAuthStore((s) => s.user);
const visibilityScopes = useVisibilityScopes(user?.id, networkId); const visibilityScopes = useVisibilityScopes(user?.id, networkId);
const network = useNetwork(networkId);
const retentionHours = network?.message_retention_hours ?? 24;
const [recencyCutoff, setRecencyCutoff] = useState(() => {
const d = new Date();
d.setHours(d.getHours() - retentionHours);
return Timestamp.fromDate(d);
});
useEffect(() => {
// Recalculate immediately when retention changes
const d = new Date();
d.setHours(d.getHours() - retentionHours);
setRecencyCutoff(Timestamp.fromDate(d));
const interval = setInterval(() => {
const d = new Date();
d.setHours(d.getHours() - retentionHours);
setRecencyCutoff(Timestamp.fromDate(d));
}, 60 * 60 * 1000);
return () => clearInterval(interval);
}, [retentionHours]);
const { children, isLoading } = useLiveParticleChildren( const { children, isLoading } = useLiveParticleChildren(
path, path,
"last_child_created_at", {
"desc", orderByField: "last_child_created_at",
visibilityScopes, orderDirection: "desc",
undefined, visibilityScopes,
undefined, }
where("last_child_created_at", ">=", recencyCutoff),
); );
const streams = useMemo( const streams = useMemo(
+30 -10
View File
@@ -128,10 +128,17 @@ export async function getParticle(docPath: string): Promise<Particle | null> {
return doc.data(); return doc.data();
} }
export interface GetParticleChildrenOptions {
orderByField: string;
orderDirection: "asc" | "desc";
}
export async function getParticleChildren( export async function getParticleChildren(
collectionPath: string, collectionPath: string,
orderByField: string = "created_at", {
orderDirection: "asc" | "desc" = "asc", orderByField = "created_at",
orderDirection = "asc",
}: GetParticleChildrenOptions = { orderByField: "created_at", orderDirection: "asc" },
): Promise<Particle[]> { ): Promise<Particle[]> {
const q = query( const q = query(
typedCollection(collectionPath), typedCollection(collectionPath),
@@ -141,16 +148,29 @@ export async function getParticleChildren(
return snap.docs.map((d) => d.data()); return snap.docs.map((d) => d.data());
} }
export interface SubscribeToParticleChildrenOptions {
onData: (children: Particle[]) => void;
onError: (error: Error) => void;
visibilityScopes?: string[];
orderByField?: string;
orderDirection?: "asc" | "desc";
onAdded?: (child: Particle) => void;
onRemoved?: (child: Particle, updatedChildren: Particle[]) => void;
whereFilter?: QueryFieldFilterConstraint;
}
export function subscribeToParticleChildren( export function subscribeToParticleChildren(
collectionPath: string, collectionPath: string,
onData: (children: Particle[]) => void, {
onError: (error: Error) => void, onData,
visibilityScopes: string[] = [], onError,
orderByField: string = "created_at", visibilityScopes = [],
orderDirection: "asc" | "desc" = "desc", orderByField = "created_at",
onAdded?: (child: Particle) => void, orderDirection = "desc",
onRemoved?: (child: Particle, updatedChildren: Particle[]) => void, onAdded,
whereFilter?: QueryFieldFilterConstraint, onRemoved,
whereFilter,
}: SubscribeToParticleChildrenOptions
): Unsubscribe { ): Unsubscribe {
let q = query(typedCollection(collectionPath), orderBy(orderByField, orderDirection)); let q = query(typedCollection(collectionPath), orderBy(orderByField, orderDirection));
if (visibilityScopes.length > 0) { if (visibilityScopes.length > 0) {