Skip to content

Announcement channel type implementation#182

Merged
acarlson33 merged 1 commit into
mainfrom
04-12-announcement_channel_type_implementation
May 5, 2026
Merged

Announcement channel type implementation#182
acarlson33 merged 1 commit into
mainfrom
04-12-announcement_channel_type_implementation

Conversation

@acarlson33

@acarlson33 acarlson33 commented Apr 19, 2026

Copy link
Copy Markdown
Owner

This pull request introduces several improvements and new tests for channel and server management, especially around channel creation and permissions. It expands test coverage for API routes, admin UI, and category management, and makes minor enhancements to test utilities and mocks.

Channel and Server Management Tests:

  • Added comprehensive tests for the /api/channels POST endpoint, including authentication, permissions, and channel creation scenarios.
  • Mocked and tested permission logic for channel creation, ensuring correct HTTP status codes and error messages for unauthorized or forbidden actions.

Admin and Category UI Tests:

  • Added tests for the admin Server Management UI to verify correct channel type options and channel creation flows, especially for "announcement" channels.
  • Improved CategorySettingsPanel tests to cover channel creation from setup, updating channel types, and refined button selection logic for more robust testing. [1] [2]

Test Utility and Mock Enhancements:

  • Enhanced test mocks to support new scenarios, including channel and server data structures, and improved mock components for chat surfaces to handle composer state and read-only messages. [1] [2]
  • Updated server permissions tests and mocks to cover channel-level access and permissions. [1] [2] [3]

Database Schema Setup:

  • Updated the Appwrite setup script to add new string attributes (type, topic) and an index for type in the channels collection.

These changes collectively improve test coverage, reliability, and the flexibility of channel management features in the application.

@appwrite

appwrite Bot commented Apr 19, 2026

Copy link
Copy Markdown

Firepit

Project ID: 68b230a0002245833242

Sites (1)
Site Status Logs Preview QR
 firepit
68eed9c6001f50d8f260
Ready Ready View Logs Preview URL QR Code

Tip

Preview deployments create instant URLs for every branch and commit

@coderabbitai

coderabbitai Bot commented Apr 19, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features

    • Added channel types: text, voice, and announcement channels
    • Added channel topics for channel descriptions
    • Announcement channels now display read-only composer for users without send permissions
    • Added visual indicators (badges and icons) to distinguish announcement channels in the UI
  • Bug Fixes

    • Improved channel permission validation for message sending

Walkthrough

The PR introduces channel types (text, voice, announcement) across the application stack. Channels now store a type field with validation, and announcement channels enforce special permissions: only users with manageChannels permission can send messages. The chat UI displays announcement affordances and renders a read-only composer when the current user lacks send access.

Changes

Channel Type Feature

Layer / File(s) Summary
Database Schema
scripts/setup-appwrite.ts, src/lib/types.ts
Appwrite schema adds type and topic attributes to channels; Channel type now includes optional type: "text" | "voice" | "announcement" and topic: string.
Validation & Normalization
src/lib/appwrite-servers.ts, src/lib/server-channel-access.ts, src/app/admin/server-actions.ts
Introduces shared CHANNEL_TYPES allowlist and normalizeChannelType() helpers throughout the stack to validate and coerce channel types, defaulting to "text" for invalid values.
Permission & Access Logic
src/lib/server-channel-access.ts, src/app/api/servers/[serverId]/permissions/route.ts
getChannelAccessForUser() applies announcement-channel-specific send logic: canSend requires both readMessages and manageChannels for announcements, otherwise requires readMessages and sendMessages. Permissions endpoint now returns canRead/canSend fields derived from channel access.
API Routes & Actions
src/app/api/channels/route.ts, src/app/api/channels/[channelId]/route.ts, src/app/admin/server-actions.ts
POST /api/channels validates and normalizes incoming type; PATCH route accepts and validates type/topic updates; createChannelAction and listChannelsAction include type in requests/responses.
Client Hooks & State
src/app/chat/hooks/useChannels.ts
useChannels.create() now accepts optional type parameter (default "text") and passes it through to createChannel().
UI Components
src/app/admin/server-management.tsx, src/app/chat/page.tsx, src/components/category-settings-panel.tsx, src/components/chat-surface-panel.tsx, src/components/emoji-picker.tsx
Channel creation UI includes type selector (text/announcement); announcement channels display icon/badge in sidebar; composer becomes read-only with explanatory message when user lacks send permissions; emoji picker supports disabled state.
Tests
src/__tests__/admin-server-management.test.tsx, src/__tests__/api-routes/channels.test.ts, src/__tests__/api-routes/server-permissions.test.ts, src/__tests__/category-settings-panel.test.tsx, src/__tests__/chat-page.test.tsx
Test suites updated to mock channel type, validate POST/PATCH channel routes, check permission responses include canSend/canRead, and verify announcement-channel read-only composer behavior.

Sequence Diagram

sequenceDiagram
    participant User
    participant ChatUI as Chat Page
    participant PermAPI as Permissions API
    participant CompUI as Composer UI
    participant Toast

    User->>ChatUI: Select announcement channel
    ChatUI->>PermAPI: GET /api/servers/:id/permissions<br/>(channelId=announce-123)
    PermAPI-->>ChatUI: { canSend: false, canRead: true, ... }
    ChatUI->>ChatUI: Set canSendMessages = false<br/>selectedChannelData.type = "announcement"
    ChatUI->>CompUI: composer = {<br/>  disabled: true,<br/>  readOnly: true,<br/>  readOnlyMessage: "Only moderators..."<br/>}
    CompUI-->>User: Render read-only status box<br/>Disable emoji picker
    User->>ChatUI: Attempt to type (blocked)
    ChatUI-->>User: Composer input disabled,<br/>Announcement icon visible
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • PR #167: Modifies src/lib/server-channel-access.ts::getChannelAccessForUser() to add category-based role requirements and channel-access checks; directly overlaps with this PR's permission logic changes.
  • PR #157: Updates src/lib/server-channel-access.ts helpers and permission derivation logic; shares the same core permission module changes as this PR.
  • PR #177: Refactors src/lib/server-channel-access.ts argument handling and category-access permission computation; impacts the same permission derivation flow this PR extends.

Poem

🐰 Channels now sport types, oh what a sight!
Announcements stand bold, but send's not your right
Unless you manage, that magical key,
Then read-only whispers keep composers at peace. ✨📢

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.21% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Announcement channel type implementation' directly and specifically reflects the main feature added across the changeset: support for announcement channels with associated type handling and tests.
Description check ✅ Passed The description comprehensively relates to the changeset by detailing test additions for API routes, admin UI, category management, mock enhancements, and database schema updates—all present in the pull request.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 04-12-announcement_channel_type_implementation
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch 04-12-announcement_channel_type_implementation

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.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

acarlson33 commented Apr 19, 2026

Copy link
Copy Markdown
Owner Author

@acarlson33

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 2, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@acarlson33

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 2, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/__tests__/api-routes/server-permissions.test.ts (1)

151-173: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing assertions for the new canRead/canSend fields.

mockGetChannelAccessForUser is set up to return canRead: true, canSend: false, but the expect at line 167 doesn't verify those values make it into the response. This is the core behavior added by this PR.

✅ Suggested assertion additions
        await expect(response.json()).resolves.toMatchObject({
            readMessages: true,
            manageMessages: true,
            sendMessages: false,
+           canRead: true,
+           canSend: false,
        });

The same gap exists in the "base server permissions" test (lines 96–101) — consider adding canRead: true, canSend: true there as well.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/api-routes/server-permissions.test.ts` around lines 151 - 173,
Add assertions to verify the new canRead/canSend fields are propagated into the
API response: update the test using mockGetChannelAccessForUser (the case
returning canRead: true, canSend: false) to assert the GET handler's response
JSON includes canRead: true and canSend: false, and likewise update the "base
server permissions" test (where mockGetChannelAccessForUser should return
canRead: true, canSend: true) to assert those fields are present and true;
locate the tests referencing mockGetChannelAccessForUser and the GET(request, {
params: ... }) call to add these expectations.
src/app/api/channels/route.ts (2)

423-424: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Compute nextCursor from the raw page, not the permission-filtered result.

If some channels in the fetched page are filtered out by readMessages, channels.length can drop below limit even when more documents exist after this page. Clients will stop paginating early and miss accessible channels farther down the dataset.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/api/channels/route.ts` around lines 423 - 424, The pagination cursor
is being computed from the permission-filtered array (channels) which can be
shorter than the fetched page after readMessages filtering; change the code to
compute nextCursor from the raw fetched page (e.g., rawChannels or fetchedPage)
instead: keep using readMessages-filtered channels for the response, but derive
nextCursor using the raw page's length and its last item (e.g., lastRaw.$id)
compared against limit so clients continue paginating when more documents exist
beyond the current fetched page.

426-436: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Don't mark this authenticated channel list as public cacheable.

This response varies by session and per-channel permissions, so a shared cache can serve one member's filtered channel list to another user. This needs a private/no-store policy, or a cache key that varies per authenticated user.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/api/channels/route.ts` around lines 426 - 436, The response for the
authenticated channel list is currently marked "public" which allows shared
caches to leak one user's filtered channels to others; update the Cache-Control
header in the compressedResponse call (the block that returns { channels,
nextCursor } and sets headers) to a private or no-store policy for authenticated
responses (e.g., replace "public, s-maxage=60, stale-while-revalidate=300" with
"private, s-maxage=60, stale-while-revalidate=300" or "no-store") so
per-session/per-permission data is not served from a shared cache. Ensure you
change the header in the same compressedResponse call that sets headers for the
channels route.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/__tests__/category-settings-panel.test.tsx`:
- Around line 196-279: Update the "creates channels from channel setup" test in
category-settings-panel.test.tsx to choose the "announcement" channel type in
the create flow (e.g., change the form control that sets type to "announcement")
and then assert that the POST request body includes type: "announcement" by
inspecting the fetch mock call (parse JSON from expect.objectContaining... or by
JSON.parse((global.fetch as vi.Mock).mock.calls[n][1].body)); additionally, when
the update PATCH request is made later in this flow, assert its parsed request
body.type is also "announcement". Target symbols: the test "creates channels
from channel setup", the CategorySettingsPanel render, the create button click,
and the global.fetch mock calls to validate the request bodies for POST and
PATCH.

In `@src/app/admin/server-management.tsx`:
- Around line 63-77: The component currently only calls loadServers when isAdmin
is true, preventing moderators from ever seeing servers or selecting one; update
the server-loading logic and backend action so moderators are supported
end-to-end: change the useEffect condition that calls loadServers() to check
(isAdmin || isModerator) and ensure the backend/API action listServersAction
(the admin-only logic) is extended or a new listServersForModerator variant is
implemented and wired so that when (isModerator && !isAdmin) the UI can call the
moderator-capable listing and populate selectedServerId, enabling
loadChannels(selectedServerId) to work for moderators as well.
- Around line 277-283: The Button elements that trigger actions (e.g., the
"Create Server" button using onClick={handleCreateServer} and controlled by
isCreatingServer and serverName) are missing an explicit type and currently rely
on the default implicit submit behavior; update those Button components to
include type="button" to prevent accidental form submissions (also apply the
same change to the other action Buttons referenced around the later panel, e.g.,
the buttons in the 442-454 region).

In `@src/app/api/channels/route.ts`:
- Around line 68-75: The handler currently calls .trim() on body.serverId,
body.name and body.topic without type checks—if request.json() yields
non-strings the app will throw a 500; update the request processing in
src/app/api/channels/route.ts so you first validate that body is an object and
that body.serverId, body.name and body.topic (when present) are strings,
returning a 400 Bad Request for invalid types, and only then call .trim() on
those string fields (refer to the variables body, serverId, name, topic and the
route's request.json() parsing to locate the code to change).

In `@src/app/api/servers/`[serverId]/permissions/route.ts:
- Around line 121-132: You’re duplicating heavy permission work by calling
getChannelAccessForUser after already computing getServerPermissionsForUser and
effectivePerms; instead, derive canRead/canSend from effectivePerms plus one
lightweight channel fetch: export and use hasAccessToCategory and
normalizeChannelType from src/lib/server-channel-access.ts (or refactor
getChannelAccessForUser to accept the precomputed ServerAccess) to compute
channel-type-specific rules, and ensure canSend is computed taking announcement
channels and manageChannels into account (don’t rely on spread sendMessages
alone) so the handler returns correct canRead/canSend without re-running
getServerPermissionsForUser/getEffectivePermissions.

In `@src/lib/server-channel-access.ts`:
- Around line 16-29: Duplicate CHANNEL_TYPES and normalizeChannelType should be
moved into a single shared module and all local copies removed; create a shared
export for CHANNEL_TYPES and normalizeChannelType, ensure the
normalizeChannelType return type is the precise union "text" | "voice" |
"announcement" (not Channel["type"] or allowing undefined), then replace the
duplicated definitions in each file by importing the shared symbols
(CHANNEL_TYPES and normalizeChannelType) and update call sites to use the
imported helper.

---

Outside diff comments:
In `@src/__tests__/api-routes/server-permissions.test.ts`:
- Around line 151-173: Add assertions to verify the new canRead/canSend fields
are propagated into the API response: update the test using
mockGetChannelAccessForUser (the case returning canRead: true, canSend: false)
to assert the GET handler's response JSON includes canRead: true and canSend:
false, and likewise update the "base server permissions" test (where
mockGetChannelAccessForUser should return canRead: true, canSend: true) to
assert those fields are present and true; locate the tests referencing
mockGetChannelAccessForUser and the GET(request, { params: ... }) call to add
these expectations.

In `@src/app/api/channels/route.ts`:
- Around line 423-424: The pagination cursor is being computed from the
permission-filtered array (channels) which can be shorter than the fetched page
after readMessages filtering; change the code to compute nextCursor from the raw
fetched page (e.g., rawChannels or fetchedPage) instead: keep using
readMessages-filtered channels for the response, but derive nextCursor using the
raw page's length and its last item (e.g., lastRaw.$id) compared against limit
so clients continue paginating when more documents exist beyond the current
fetched page.
- Around line 426-436: The response for the authenticated channel list is
currently marked "public" which allows shared caches to leak one user's filtered
channels to others; update the Cache-Control header in the compressedResponse
call (the block that returns { channels, nextCursor } and sets headers) to a
private or no-store policy for authenticated responses (e.g., replace "public,
s-maxage=60, stale-while-revalidate=300" with "private, s-maxage=60,
stale-while-revalidate=300" or "no-store") so per-session/per-permission data is
not served from a shared cache. Ensure you change the header in the same
compressedResponse call that sets headers for the channels route.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f07b7940-cceb-4b0f-a560-1d39577c7c5d

📥 Commits

Reviewing files that changed from the base of the PR and between bd9103b and 3b41765.

📒 Files selected for processing (19)
  • scripts/setup-appwrite.ts
  • src/__tests__/admin-server-management.test.tsx
  • src/__tests__/api-routes/channels.test.ts
  • src/__tests__/api-routes/server-permissions.test.ts
  • src/__tests__/category-settings-panel.test.tsx
  • src/__tests__/chat-page.test.tsx
  • src/app/admin/server-actions.ts
  • src/app/admin/server-management.tsx
  • src/app/api/channels/[channelId]/route.ts
  • src/app/api/channels/route.ts
  • src/app/api/servers/[serverId]/permissions/route.ts
  • src/app/chat/hooks/useChannels.ts
  • src/app/chat/page.tsx
  • src/components/category-settings-panel.tsx
  • src/components/chat-surface-panel.tsx
  • src/components/emoji-picker.tsx
  • src/lib/appwrite-servers.ts
  • src/lib/server-channel-access.ts
  • src/lib/types.ts

Comment thread src/__tests__/category-settings-panel.test.tsx
Comment thread src/app/admin/server-management.tsx
Comment thread src/app/admin/server-management.tsx
Comment thread src/app/api/channels/route.ts
Comment thread src/app/api/servers/[serverId]/permissions/route.ts
Comment thread src/lib/server-channel-access.ts
@acarlson33

Copy link
Copy Markdown
Owner Author

@coderabbitai resolve

@coderabbitai

coderabbitai Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Comments resolved and changes approved.

acarlson33 commented May 5, 2026

Copy link
Copy Markdown
Owner Author

Merge activity

  • May 5, 11:21 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • May 5, 11:21 PM UTC: @acarlson33 merged this pull request with Graphite.

@acarlson33 acarlson33 merged commit 553ac7c into main May 5, 2026
3 of 4 checks passed
@acarlson33 acarlson33 deleted the 04-12-announcement_channel_type_implementation branch May 5, 2026 23:21
@coderabbitai coderabbitai Bot mentioned this pull request May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant