mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
Media sends (photo/video/document/voice) are now opt-in per account via
the existing profiles.beta_features mechanism (migration 011 — same
pattern used to dogfood Flows before GA). No migration needed.
- New shared `hasBetaFeature` + `CHAT_MEDIA_BETA` ('chat_media') key, and
a `useBetaFeature` client hook mirroring useCan (false while loading).
- Composer: the attach menu, hidden file inputs, and voice recorder only
render when the flag is on; the hint indent reverts so non-beta users
see the unchanged text + template composer.
- Server: the send route rejects media kinds (403) and the transcode
route rejects (403) unless the caller's profile is opted in — so the
endpoints can't be driven ahead of the rollout. Text + templates stay
available to everyone.
Enable for one account via Supabase Studio:
UPDATE profiles SET beta_features = array_append(beta_features, 'chat_media')
WHERE email = 'someone@example.com';
Tests: hasBetaFeature null-safety + membership (419 total). typecheck/lint/build green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
32 lines
1.1 KiB
TypeScript
32 lines
1.1 KiB
TypeScript
/**
|
|
* Beta feature flags, stored per-profile in `profiles.beta_features`
|
|
* (a `TEXT[]`, migration 011). A feature is "on" for an account when its
|
|
* key is present in that array; the owner opts in by editing the column
|
|
* in Supabase Studio:
|
|
*
|
|
* UPDATE profiles
|
|
* SET beta_features = array_append(beta_features, 'chat_media')
|
|
* WHERE email = 'someone@example.com';
|
|
*
|
|
* This module is the single source of truth for the keys + the membership
|
|
* check so the client hook (`useBetaFeature`) and the server routes gate
|
|
* on exactly the same logic.
|
|
*/
|
|
|
|
/** Agent-initiated media sends in the inbox composer (issue #213). */
|
|
export const CHAT_MEDIA_BETA = "chat_media";
|
|
|
|
export type BetaFeature = typeof CHAT_MEDIA_BETA;
|
|
|
|
/**
|
|
* Whether an opted-in feature list contains `feature`. Pure + null-safe so
|
|
* both the client (reading `profile.beta_features`) and the server
|
|
* (reading the row from Supabase) share one implementation.
|
|
*/
|
|
export function hasBetaFeature(
|
|
betaFeatures: readonly string[] | null | undefined,
|
|
feature: BetaFeature,
|
|
): boolean {
|
|
return betaFeatures?.includes(feature) ?? false;
|
|
}
|