diff --git a/public/locales/en/common.json b/public/locales/en/common.json index b3f0abbc..75e23d57 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -185,11 +185,22 @@ "description": "Messages are not encrypted and can be read by relays. For maximum privacy host your own relay.", "placeholder": "Choose visibility" }, - "access": { - "label": "Access", - "anyone": "Anyone", - "invite-only": "Invite only", - "placeholder": "Choose policy" + "access-settings": "Access Settings", + "private": { + "label": "Private", + "description": "Only members can read group messages" + }, + "restricted": { + "label": "Restricted", + "description": "Only members can send messages" + }, + "hidden": { + "label": "Hidden", + "description": "Group info is hidden from non-members" + }, + "closed": { + "label": "Closed", + "description": "Join requests are ignored (invite-only)" }, "submit": { "trigger": "Create", @@ -281,27 +292,21 @@ "metadata": { "community": "This is a profile based community", "group": "Group", - "private": "Private", - "closed": "Closed", - "visibility": { - "private": { - "trigger": "Members only", - "content": "Only members can read content" - }, - "public": { - "trigger": "Public", - "content": "Can be read by anyone" - } + "private": { + "trigger": "Private", + "content": "Only members can read messages" }, - "access": { - "closed": { - "trigger": "Closed", - "content": "Requires approval or an invitation to join" - }, - "open": { - "trigger": "Open", - "content": "Anyone can join" - } + "restricted": { + "trigger": "Restricted", + "content": "Only members can send messages" + }, + "hidden": { + "trigger": "Hidden", + "content": "Group info is hidden from non-members" + }, + "closed": { + "trigger": "Closed", + "content": "Join requests are ignored (invite-only)" }, "join-the-conversation": "Join the conversation", "map_picker": { @@ -347,16 +352,14 @@ }, "privacy": { "tab": "Privacy", - "visibility": "Visibility", - "access": "Access Control", - "public": "Public", - "public_description": "Anyone can see the group and its content", "private": "Private", - "private_description": "Only members can see the group content", - "open": "Open", - "open_description": "Anyone can join the group", + "private_description": "Only members can read group messages", + "restricted": "Restricted", + "restricted_description": "Only members can send messages to the group", + "hidden": "Hidden", + "hidden_description": "Relay hides group metadata from non-members", "closed": "Closed", - "closed_description": "Members must be approved to join", + "closed_description": "Join requests are ignored (invite-only)", "save": "Save Privacy Settings", "saving": "Saving...", "success": "Group privacy settings updated successfully", diff --git a/src/components/nostr/groups/chat.tsx b/src/components/nostr/groups/chat.tsx index d5130e6b..ae521c7b 100644 --- a/src/components/nostr/groups/chat.tsx +++ b/src/components/nostr/groups/chat.tsx @@ -33,7 +33,11 @@ import { formatShortNumber } from "@/lib/number"; import { Badge } from "@/components/ui/badge"; import { Name } from "@/components/nostr/name"; import Amount from "@/components/amount"; -import { useCommunity, useFetchGroupParticipants } from "@/lib/nostr/groups"; +import { + useCommunity, + useFetchGroupParticipants, + useGroup, +} from "@/lib/nostr/groups"; import { Zap } from "@/components/nostr/zap"; import { validateZap } from "@/lib/nip-57"; import { ChatInput } from "@/components/nostr/chat/input"; @@ -812,6 +816,7 @@ export const GroupChat = forwardRef( ({ group }: { group: Group }, ref: ForwardedRef) => { // todo: load older messages when scrolling up const { data: participants } = useFetchGroupParticipants(group); + const { data: metadata } = useGroup(group); const members = participants?.members || []; const admins = participants?.admins || []; const { data: relayInfo } = useRelayInfo(group.relay); @@ -964,7 +969,9 @@ export const GroupChat = forwardRef( ...(isRelayGroup ? [["-"]] : []), ]} showJoinRequest={ - !canIPoast && relayInfo?.supported_nips?.includes(29) + !canIPoast && + relayInfo?.supported_nips?.includes(29) && + !metadata?.isClosed } > {replyingTo ? ( diff --git a/src/components/nostr/groups/create-on-relay.tsx b/src/components/nostr/groups/create-on-relay.tsx index 8d00ef3c..8d3ffd12 100644 --- a/src/components/nostr/groups/create-on-relay.tsx +++ b/src/components/nostr/groups/create-on-relay.tsx @@ -5,7 +5,7 @@ import { z } from "zod"; import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; -import { Plus } from "lucide-react"; +import { Plus, BookLock, PenOff, EyeOff, ShieldOff } from "lucide-react"; import { NostrEvent } from "nostr-tools"; import { groupsContentAtom } from "@/app/store"; import { Button } from "@/components/ui/button"; @@ -29,13 +29,7 @@ import { import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { UploadImage } from "@/components/upload-image"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; +import { Switch } from "@/components/ui/switch"; import { useRelays, useRelaySet } from "@/lib/nostr"; import { useCreateGroup, useEditGroup } from "@/lib/nostr/groups"; import { getRelayHost, isRelayURL } from "@/lib/relay"; @@ -51,8 +45,10 @@ const formSchema = z.object({ name: z.string().min(1).max(140), picture: z.string().url().optional(), about: z.string().min(0).max(500).optional(), - visibility: z.enum(["public", "private"]).default("public"), - access: z.enum(["open", "closed"]).default("open"), + isPrivate: z.boolean().default(false), + isRestricted: z.boolean().default(false), + isHidden: z.boolean().default(false), + isClosed: z.boolean().default(false), }); /** @@ -85,8 +81,10 @@ export function CreateGroupOnRelay({ const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { - visibility: "public", - access: "open", + isPrivate: false, + isRestricted: false, + isHidden: false, + isClosed: false, }, }); @@ -135,8 +133,10 @@ export function CreateGroupOnRelay({ name: "", picture: "", about: "", - visibility: "public", - access: "open", + isPrivate: false, + isRestricted: false, + isHidden: false, + isClosed: false, }); } @@ -235,81 +235,116 @@ export function CreateGroupOnRelay({ {t("group.create.form.relay.description")} - ( - -
- - {t("group.create.form.visibility.label")} - + {/* Group Access Settings */} +
+

+ {t("group.create.form.access-settings")} +

+ ( + +
+ +
+ + {t("group.create.form.private.label")} + + + {t("group.create.form.private.description")} + +
+
- + /> -
- - {t("group.create.form.visibility.description")} - - - - )} - /> - ( - -
- {t("group.create.form.access.label")} + + )} + /> + ( + +
+ +
+ + {t("group.create.form.restricted.label")} + + + {t("group.create.form.restricted.description")} + +
+
- + /> -
- -
- )} - /> + + )} + /> + ( + +
+ +
+ + {t("group.create.form.hidden.label")} + + + {t("group.create.form.hidden.description")} + +
+
+ + + +
+ )} + /> + ( + +
+ +
+ + {t("group.create.form.closed.label")} + + + {t("group.create.form.closed.description")} + +
+
+ + + +
+ )} + /> +