Adds the client mirror of section_enabled():
* SectionKey / FeatureFlags types + SECTION_KEYS constant in
@colectivo/types so the SQL and TS sides share the same vocabulary.
* apps/web/src/lib/stores/features.ts:
- currentUserFeatures writable, loaded by root layout
- enabledSections derived map { section → boolean }
- enabledSectionList ordered SectionKey[] visible to UI;
forces 'lists' if every layer says OFF
- setUserFeature per-user toggle (optimistic + rollback)
- setCollectiveFeature admin toggle (rolls back on RLS deny)
- realtime subscriptions one channel for the user's row, one
for the active collective; rebuilt
whenever those targets change
Wired into the root layout: loadCurrentUserFeatures runs from inside
the existing setTimeout-deferred onAuthStateChange callback (the
documented gotcha), and currentCollective.subscribe drives
subscribeCollectiveFeatures so collective switches re-target the
realtime filter without an extra auth event.
loadUserCollectives + every other place that fabricates a Collective
object now carries feature_flags (defaulting to {} for freshly created
collectives) so the Collective interface stays sound.
Integration spec (6 tests, SV-01..SV-06) covers default-ON, user-only,
collective-beats-user, RLS writes (user_update_own + collectives_update),
and per-collective isolation for a user in two collectives.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
25 lines
664 B
TypeScript
25 lines
664 B
TypeScript
import { writable } from 'svelte/store';
|
|
import type { FeatureFlags } from '@colectivo/types';
|
|
|
|
export interface Collective {
|
|
id: string;
|
|
name: string;
|
|
emoji: string;
|
|
created_at: string;
|
|
feature_flags: FeatureFlags;
|
|
}
|
|
|
|
export interface CollectiveMember {
|
|
user_id: string;
|
|
collective_id: string;
|
|
role: 'admin' | 'member' | 'guest';
|
|
display_name: string;
|
|
avatar_type: 'initials' | 'emoji' | 'upload';
|
|
avatar_emoji: string | null;
|
|
avatar_url: string | null;
|
|
}
|
|
|
|
export const currentCollective = writable<Collective | null>(null);
|
|
export const collectiveMembers = writable<CollectiveMember[]>([]);
|
|
export const userCollectives = writable<Collective[]>([]);
|