Conversation
📝 WalkthroughWalkthroughAdds a DM privacy system: UI settings, client hooks, server getters/setters, API route for current user's DM privacy, and enforcement checks in DM creation and posting endpoints, plus localization strings. Changes
Sequence DiagramssequenceDiagram
actor User
participant UI as Settings Component
participant Hook as useUpdateDmPrivacy
participant API as /api/mattermost/me/dm-privacy
participant MM as Mattermost API
User->>UI: Select privacy option
UI->>Hook: trigger mutation (privacy)
Hook->>API: PUT { privacy }
API->>API: validate privacy
API->>MM: GET /users/me
MM-->>API: user with props
API->>MM: PATCH /users/{id} (privacy prop)
MM-->>API: updated user
API-->>Hook: { privacy }
Hook->>Hook: invalidate cache
Hook-->>UI: success
UI->>User: show success toast
sequenceDiagram
actor Sender
participant DirectAPI as POST /api/mattermost/direct
participant MM as Mattermost API
participant QueryClient as Query Cache
actor Recipient
Sender->>DirectAPI: request to create DM with Recipient
DirectAPI->>MM: fetch Recipient (with props)
MM-->>DirectAPI: Recipient (props)
DirectAPI->>DirectAPI: getUserDmPrivacy(Recipient) = "followers"
DirectAPI->>QueryClient: query relationship (does Recipient follow Sender?)
QueryClient-->>DirectAPI: response (no)
DirectAPI-->>Sender: 403 Forbidden (DM not allowed)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧹 Recent nitpick comments
📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used🧬 Code graph analysis (1)apps/web/src/app/(dynamicPages)/profile/[username]/settings/_dm-privacy.tsx (3)
🔇 Additional comments (1)
✏️ Tip: You can disable this entire section by setting Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@apps/web/src/app/`(dynamicPages)/profile/[username]/settings/_dm-privacy.tsx:
- Around line 10-14: The component currently returns early only on isLoading,
which lets a failed fetch leave `privacy` undefined and default the UI to "all";
update the guard after calling useDmPrivacyQuery() to also extract and check
`isError` (e.g., const { data: privacy, isLoading, isError } =
useDmPrivacyQuery()) and return early when isError is true so the UI does not
misrepresent settings; keep using useUpdateDmPrivacy() and its mutate alias
updatePrivacy unchanged but ensure downstream rendering relies on a defined
`privacy` value only after the combined loading/error guard passes.
♻️ Duplicate comments (1)
apps/web/src/app/api/mattermost/channels/[channelId]/posts/route.ts (1)
253-292: Same follower-direction concern as/direct.Please verify the relationship direction for
"followers"privacy here as well (same SDK call and semantics).
🧹 Nitpick comments (2)
apps/web/src/features/chat/mattermost-api.ts (1)
767-767: Consider sharingDmPrivacyLevelacross server/client.To avoid drift between server and client unions, consider moving this type to a shared module used by both sides.
apps/web/src/app/(dynamicPages)/profile/[username]/settings/_dm-privacy.tsx (1)
15-26: Select won’t reflect changes until refetch; consider optimistic/local state.
Because the select is controlled byprivacy, the user’s choice snaps back until the query refreshes. A small local state (or react-query optimistic update) would keep the UI responsive.♻️ Example using local state
+import { useEffect, useState } from "react"; ... - const { data: privacy, isLoading } = useDmPrivacyQuery(); + const { data: privacy, isLoading } = useDmPrivacyQuery(); + const [selectedPrivacy, setSelectedPrivacy] = useState<DmPrivacyLevel | undefined>(privacy); + useEffect(() => setSelectedPrivacy(privacy), [privacy]); ... - const handlePrivacyChange = (e: React.ChangeEvent<HTMLSelectElement>) => { + const handlePrivacyChange = (e: React.ChangeEvent<HTMLSelectElement>) => { const newPrivacy = e.target.value as DmPrivacyLevel; + setSelectedPrivacy(newPrivacy); updatePrivacy(newPrivacy, { onSuccess: () => { success(i18next.t("settings.dm-privacy-updated")); }, onError: (error) => { // Error is already thrown and will be caught by error boundary console.error("Failed to update DM privacy:", error); + setSelectedPrivacy(privacy); } }); }; ... - <FormControl value={privacy || "all"} type="select" onChange={handlePrivacyChange}> + <FormControl value={selectedPrivacy || "all"} type="select" onChange={handlePrivacyChange}>Also applies to: 42-42
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
apps/web/src/app/(dynamicPages)/profile/[username]/settings/_dm-privacy.tsxapps/web/src/app/(dynamicPages)/profile/[username]/settings/_page.tsxapps/web/src/app/api/mattermost/channels/[channelId]/posts/route.tsapps/web/src/app/api/mattermost/direct/route.tsapps/web/src/app/api/mattermost/me/dm-privacy/route.tsapps/web/src/features/chat/mattermost-api.tsapps/web/src/features/i18n/locales/en-US.jsonapps/web/src/server/mattermost.ts
🧰 Additional context used
🧬 Code graph analysis (5)
apps/web/src/app/(dynamicPages)/profile/[username]/settings/_dm-privacy.tsx (3)
apps/web/src/features/chat/mattermost-api.ts (3)
useDmPrivacyQuery(769-783)useUpdateDmPrivacy(785-807)DmPrivacyLevel(767-767)apps/web/src/server/mattermost.ts (1)
DmPrivacyLevel(13-13)apps/web/src/features/shared/feedback/feedback-events.ts (1)
success(47-55)
apps/web/src/features/chat/mattermost-api.ts (1)
apps/web/src/server/mattermost.ts (1)
DmPrivacyLevel(13-13)
apps/web/src/app/(dynamicPages)/profile/[username]/settings/_page.tsx (2)
apps/web/src/app/(dynamicPages)/profile/[username]/settings/_preferences.tsx (1)
Preferences(18-152)apps/web/src/app/(dynamicPages)/profile/[username]/settings/_dm-privacy.tsx (1)
DmPrivacySettings(9-50)
apps/web/src/app/api/mattermost/channels/[channelId]/posts/route.ts (1)
apps/web/src/server/mattermost.ts (4)
mmUserFetch(259-268)MattermostChannel(25-30)getMattermostUserWithProps(203-207)getUserDmPrivacy(358-364)
apps/web/src/app/api/mattermost/direct/route.ts (1)
apps/web/src/server/mattermost.ts (2)
getMattermostUserWithProps(203-207)getUserDmPrivacy(358-364)
🔇 Additional comments (13)
apps/web/src/server/mattermost.ts (3)
11-13: Good addition of shared DM privacy constants/types.Keeps the prop key and allowed values centralized.
358-364: DM privacy normalization looks correct.Defaulting to
"all"when unset/invalid is a sensible fallback.
443-459: Setter implementation looks solid.The props merge and patch flow aligns with existing admin updates.
apps/web/src/app/api/mattermost/direct/route.ts (2)
5-12: No review comment for this change.
39-69: I need the review comment to be provided in order to rewrite it. Please share the review comment (ideally within<review_comment>tags) along with any context about the codebase changes being reviewed.apps/web/src/app/api/mattermost/me/dm-privacy/route.ts (2)
13-28: GET handler looks good.Clean token check, user resolution, and privacy response.
30-54: PUT handler looks good.Validation + update flow is clear and consistent.
apps/web/src/features/chat/mattermost-api.ts (1)
769-806: Hooks look good.Query/mutation flows and cache invalidation are correct.
apps/web/src/app/api/mattermost/channels/[channelId]/posts/route.ts (1)
8-18: No review comment for this change.apps/web/src/app/(dynamicPages)/profile/[username]/settings/_page.tsx (1)
6-14: LGTM: DM privacy card integrated into settings layout.
Nice, clean insertion in the grid without impacting existing flow.apps/web/src/features/i18n/locales/en-US.json (3)
1080-1082: LGTM: Profile DM badge labels added.
Strings are clear and consistent with the privacy options.
1190-1200: LGTM: Settings DM privacy copy is complete and consistent.
Nice, descriptive labels and help text.
2177-2180: LGTM: Chat entry-menu DM restriction messages added.
These align well with the privacy states.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
Summary by CodeRabbit
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.