From 4122882b7ee5ffa73e6793c9538d37bba2b9570e Mon Sep 17 00:00:00 2001 From: benya Date: Sun, 8 Mar 2026 20:32:29 +0300 Subject: [PATCH] feat(privacy): support nobody option for group invites --- app/users/schemas.py | 2 +- docs/api-reference.md | 1 + docs/core-checklist-status.md | 2 +- web/src/api/users.ts | 2 +- web/src/chat/types.ts | 2 +- web/src/components/SettingsPanel.tsx | 5 +++-- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/users/schemas.py b/app/users/schemas.py index 02f948d..1ac8782 100644 --- a/app/users/schemas.py +++ b/app/users/schemas.py @@ -5,7 +5,7 @@ from typing import Literal PrivacyLevel = Literal["everyone", "contacts", "nobody"] -GroupInvitePrivacyLevel = Literal["everyone", "contacts"] +GroupInvitePrivacyLevel = Literal["everyone", "contacts", "nobody"] PrivateMessagesPrivacyLevel = Literal["everyone", "contacts", "nobody"] diff --git a/docs/api-reference.md b/docs/api-reference.md index c1a0095..960e54e 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -211,6 +211,7 @@ Server behavior: when a user disconnects, active typing/recording indicators are All fields are optional. `privacy_private_messages`: `everyone | contacts | nobody`. +`privacy_group_invites`: `everyone | contacts | nobody`. ## 3.3 Chats diff --git a/docs/core-checklist-status.md b/docs/core-checklist-status.md index 60f58fe..ade6468 100644 --- a/docs/core-checklist-status.md +++ b/docs/core-checklist-status.md @@ -37,7 +37,7 @@ Legend: 28. Notifications - `PARTIAL` (browser notifications + mute/settings; no mobile push infra) 29. Archive - `DONE` 30. Blacklist - `DONE` -31. Privacy - `PARTIAL` (avatar/last-seen/group-invites + PM policy `everyone|contacts|nobody`; integration tests cover PM policy matrix (`everyone/contacts/nobody`), group-invite policy matrix (`everyone/contacts/nobody`), and private chat counterpart visibility for `nobody/contacts`, remaining UX/matrix hardening) +31. Privacy - `PARTIAL` (avatar/last-seen/group-invites + PM policy `everyone|contacts|nobody`; group-invite `nobody` is available in API and web settings; integration tests cover PM policy matrix (`everyone/contacts/nobody`), group-invite policy matrix (`everyone/contacts/nobody`), and private chat counterpart visibility for `nobody/contacts`, remaining UX/matrix hardening) 32. Security - `PARTIAL` (sessions + revoke + 2FA base + access-session visibility; integration tests cover single-session revoke and revoke-all invalidation/force-disconnect; 2FA setup now blocked after enable to prevent secret re-issuance; one-time recovery codes added; UX polish ongoing) 33. Realtime Events - `DONE` (connect/disconnect/send/receive/typing/read/delivered/online/offline + chat/message updates + chat_deleted) 34. Sync - `PARTIAL` (cross-device via backend state + realtime; reconciliation improved for loaded chats/messages, chat-info panel hot-refreshes on `chat_updated`, delete/leave updates realtime subscriptions, full-chat delete emits `chat_deleted`) diff --git a/web/src/api/users.ts b/web/src/api/users.ts index 204a43b..f88b198 100644 --- a/web/src/api/users.ts +++ b/web/src/api/users.ts @@ -17,7 +17,7 @@ interface UserProfileUpdatePayload { privacy_private_messages?: "everyone" | "contacts" | "nobody"; privacy_last_seen?: "everyone" | "contacts" | "nobody"; privacy_avatar?: "everyone" | "contacts" | "nobody"; - privacy_group_invites?: "everyone" | "contacts"; + privacy_group_invites?: "everyone" | "contacts" | "nobody"; } export async function updateMyProfile(payload: UserProfileUpdatePayload): Promise { diff --git a/web/src/chat/types.ts b/web/src/chat/types.ts index f467b2c..0a87811 100644 --- a/web/src/chat/types.ts +++ b/web/src/chat/types.ts @@ -86,7 +86,7 @@ export interface AuthUser { privacy_private_messages?: "everyone" | "contacts" | "nobody"; privacy_last_seen?: "everyone" | "contacts" | "nobody"; privacy_avatar?: "everyone" | "contacts" | "nobody"; - privacy_group_invites?: "everyone" | "contacts"; + privacy_group_invites?: "everyone" | "contacts" | "nobody"; created_at: string; updated_at: string; } diff --git a/web/src/components/SettingsPanel.tsx b/web/src/components/SettingsPanel.tsx index 1985b6e..f48277d 100644 --- a/web/src/components/SettingsPanel.tsx +++ b/web/src/components/SettingsPanel.tsx @@ -47,7 +47,7 @@ export function SettingsPanel({ open, onClose }: Props) { const [recoveryCodes, setRecoveryCodes] = useState([]); const [privacyLastSeen, setPrivacyLastSeen] = useState<"everyone" | "contacts" | "nobody">("everyone"); const [privacyAvatar, setPrivacyAvatar] = useState<"everyone" | "contacts" | "nobody">("everyone"); - const [privacyGroupInvites, setPrivacyGroupInvites] = useState<"everyone" | "contacts">("everyone"); + const [privacyGroupInvites, setPrivacyGroupInvites] = useState<"everyone" | "contacts" | "nobody">("everyone"); const [notificationItems, setNotificationItems] = useState([]); const [notificationItemsLoading, setNotificationItemsLoading] = useState(false); const [profileDraft, setProfileDraft] = useState({ @@ -501,10 +501,11 @@ export function SettingsPanel({ open, onClose }: Props) {