From ba0f818a1b98746a8d3d0c313d0a1627abfaea1f Mon Sep 17 00:00:00 2001 From: Anton Arnautov Date: Mon, 27 Apr 2026 14:26:12 +0200 Subject: [PATCH 1/2] Initial commit --- .github/workflows/ci.yml | 13 +++++++++++-- examples/vite/src/App.tsx | 14 +++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 851cda89c0..754dfa1fa3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,6 +55,15 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} deploy-vite-example: + strategy: + matrix: + projects: + - name: Vite Example Development + project-id: prj_2Rq8kuNd0BmqKd2NwvkDjX9Htnx5 + stream-api-key: rq7ntdb4zzsq + - name: Vite Example Public + project-id: prj_yBYBe1AJV0tbpTqxCMzcyRfFWXxP + stream-api-key: xzwhhgtazy6h runs-on: ubuntu-latest # skip if build fails needs: @@ -62,8 +71,8 @@ jobs: name: Deploy Vite Example to Vercel env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_PROJECT_ID: prj_2Rq8kuNd0BmqKd2NwvkDjX9Htnx5 - VITE_STREAM_API_KEY: ${{ vars.VITE_STREAM_API_KEY }} + VERCEL_PROJECT_ID: ${{ matrix.projects.project-id }} + VITE_STREAM_API_KEY: ${{ matrix.projects.stream-api-key }} NODE_ENV: production steps: - uses: actions/checkout@v6 diff --git a/examples/vite/src/App.tsx b/examples/vite/src/App.tsx index 501b506a23..e2656058c2 100644 --- a/examples/vite/src/App.tsx +++ b/examples/vite/src/App.tsx @@ -125,12 +125,20 @@ const useUser = () => { localStorage.setItem('user_id', userId); }, [userId]); + const environment = + import.meta.env.VITE_STREAM_API_KEY === 'xzwhhgtazy6h' + ? 'public-shared-chat-redesign' + : 'shared-chat-redesign'; + + const url = new URL('https://pronto.getstream.io/api/auth/create-token'); + + url.searchParams.set('environment', environment); + url.searchParams.set('user_id', userId); + const tokenProvider = useCallback(() => { return token && userId === parseUserIdFromToken(token) ? Promise.resolve(token) - : fetch( - `https://pronto.getstream.io/api/auth/create-token?environment=shared-chat-redesign&user_id=${userId}`, - ) + : fetch(url.toString()) .then((response) => response.json()) .then((data) => data.token as string); }, [userId]); From c75c6b2c19c9e4fc760e55d73f7562f2de0b8660 Mon Sep 17 00:00:00 2001 From: Anton Arnautov Date: Mon, 27 Apr 2026 16:50:58 +0200 Subject: [PATCH 2/2] Join/Leave button + extended filters --- .github/workflows/ci.yml | 4 +-- examples/vite/src/App.tsx | 13 +++++++- examples/vite/src/ChatLayout/Panels.tsx | 42 +++++++++++++++++++++++-- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 754dfa1fa3..cdcebe3882 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,7 +100,7 @@ jobs: - name: Vercel Pull/Build/Deploy (Preview) working-directory: examples/vite if: ${{ github.ref_name != 'master' }} - run: > + run: >- npx vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }} && npx vercel build && npx vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} @@ -108,7 +108,7 @@ jobs: - name: Vercel Pull/Build/Deploy (Production) working-directory: examples/vite if: ${{ github.ref_name == 'master' }} - run: > + run: >- npx vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} && npx vercel build --prod && npx vercel deploy --prod --prebuilt --token=${{ secrets.VERCEL_TOKEN }} diff --git a/examples/vite/src/App.tsx b/examples/vite/src/App.tsx index e2656058c2..b511bf8c05 100644 --- a/examples/vite/src/App.tsx +++ b/examples/vite/src/App.tsx @@ -68,6 +68,8 @@ import { import { ConfigurableMessageActions } from './CustomMessageActions'; import { SidebarToggle } from './Sidebar/SidebarToggle.tsx'; +const PUBLIC_VITE_EXAMPLE_API_KEY = 'xzwhhgtazy6h'; + init({ data }); const parseUserIdFromToken = (token: string) => { @@ -126,7 +128,7 @@ const useUser = () => { }, [userId]); const environment = - import.meta.env.VITE_STREAM_API_KEY === 'xzwhhgtazy6h' + apiKey === PUBLIC_VITE_EXAMPLE_API_KEY ? 'public-shared-chat-redesign' : 'shared-chat-redesign'; @@ -257,6 +259,7 @@ const App = () => { ...(userName && { name: userName }), }, }); + const searchController = useMemo(() => { if (!chatClient) return undefined; @@ -286,6 +289,14 @@ const App = () => { members: { $in: [userId] }, }, { type: 'public' }, + // public example channels + { + cid: { + $in: ['random', 'general', 'music', 'jokes'].map( + (channelId) => `messaging:${channelId}`, + ), + }, + }, ], }), [userId], diff --git a/examples/vite/src/ChatLayout/Panels.tsx b/examples/vite/src/ChatLayout/Panels.tsx index a65b445503..9a1159a2f0 100644 --- a/examples/vite/src/ChatLayout/Panels.tsx +++ b/examples/vite/src/ChatLayout/Panels.tsx @@ -1,6 +1,11 @@ import clsx from 'clsx'; -import type { ChannelFilters, ChannelOptions, ChannelSort } from 'stream-chat'; -import { useEffect, useRef } from 'react'; +import type { + ChannelFilters, + ChannelMemberResponse, + ChannelOptions, + ChannelSort, +} from 'stream-chat'; +import { useCallback, useEffect, useRef } from 'react'; import { AIStateIndicator, Channel, @@ -21,6 +26,8 @@ import { useChatContext, type ChatViewSelectorEntry, useThreadsViewContext, + Button, + useChannelMembersState, } from 'stream-chat-react'; import { useAppSettingsSelector } from '../AppSettings/state'; @@ -97,6 +104,34 @@ const ResponsiveChannelPanels = () => { ); }; +const HeaderStartContent = () => { + const { client } = useChatContext(); + const { channel } = useChannelStateContext(); + const members = useChannelMembersState(channel); + const membership = members[client.userID!] as ChannelMemberResponse | undefined; + + const isMember = typeof membership?.channel_role === 'string'; + const canJoin = channel.data?.own_capabilities?.includes('join-channel'); + + const handleClick = useCallback(() => { + if (isMember) { + channel.removeMembers([client.userID!]).then(() => { + channel.watch(); + }); + } else { + channel.addMembers([client.userID!]); + } + }, [isMember]); + + if (!canJoin) return null; + + return ( + + ); +}; + export const ChannelsPanels = ({ filters, iconOnly, @@ -148,6 +183,7 @@ export const ChannelsPanels = ({ }} > {}} customActiveChannel={initialChannelId} filters={filters} options={options} @@ -157,7 +193,7 @@ export const ChannelsPanels = ({ - +