feat: allow network admin to control ephemerality setting

Resolves #92
This commit is contained in:
talksik
2026-03-30 13:27:27 -07:00
parent 7f93aa9db3
commit d56541bd13
16 changed files with 323 additions and 60 deletions
+13
View File
@@ -0,0 +1,13 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiClient } from "@/api/client";
export function useSetMessageRetention(networkId: string) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (hours: number) =>
apiClient.setMessageRetentionHours(networkId, hours),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["networks"] });
},
});
}
+11 -4
View File
@@ -4,7 +4,7 @@ import { useAuthStore } from "@/stores/auth-store";
import { parseParticlePath, type ParticlePath } from "@/lib/particle-path";
import type { Particle, StreamProperties } from "@/api/types";
import { where, Timestamp } from "firebase/firestore";
import { RECENCY_WINDOW_HOURS } from "@/lib/constants";
import { useNetwork } from "@/hooks/use-networks";
type StreamParticle = Particle & { type: "stream"; properties: StreamProperties };
@@ -27,21 +27,28 @@ export function useStreamParticles(path: ParticlePath): UseStreamParticlesResult
const { networkId } = parseParticlePath(path);
const user = useAuthStore((s) => s.user);
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() - RECENCY_WINDOW_HOURS);
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() - RECENCY_WINDOW_HOURS);
d.setHours(d.getHours() - retentionHours);
setRecencyCutoff(Timestamp.fromDate(d));
}, 60 * 60 * 1000);
return () => clearInterval(interval);
}, []);
}, [retentionHours]);
const { children, isLoading } = useLiveParticleChildren(
path,
+6 -3
View File
@@ -2,10 +2,10 @@ import { useCallback, useEffect, useEffectEvent, useMemo, useReducer, useRef, us
import { useAuthStore } from "@/stores/auth-store";
import type { Particle } from "@/api/types";
import { useLiveParticleChildren } from "@/hooks/use-particle";
import { toFirestoreDocPath, type ParticlePath } from "@/lib/particle-path";
import { parseParticlePath, toFirestoreDocPath, type ParticlePath } from "@/lib/particle-path";
import { updateStreamPlaybackMarker } from "@/lib/firestore-particles";
import { where, Timestamp } from "firebase/firestore";
import { RECENCY_WINDOW_HOURS } from "@/lib/constants";
import { useNetwork } from "@/hooks/use-networks";
// --- Playback reducer (ID-based) ---
@@ -96,6 +96,9 @@ export function useStreamPlayback(
path: ParticlePath,
): UseStreamPlaybackResult {
const userId = useAuthStore((s) => s.user?.id);
const { networkId } = parseParticlePath(path);
const network = useNetwork(networkId);
const retentionHours = network?.message_retention_hours ?? 24;
const [state, dispatch] = useReducer(playbackReducer, initialState);
// Track the stream ID we've initialized for, to reset when navigating between streams
const initializedForRef = useRef<string | null>(null);
@@ -117,7 +120,7 @@ export function useStreamPlayback(
const [recencyCutoff] = useState(() => {
const d = new Date();
d.setHours(d.getHours() - RECENCY_WINDOW_HOURS);
d.setHours(d.getHours() - retentionHours);
return Timestamp.fromDate(d);
});