diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 851cda89c0..cdcebe3882 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
@@ -91,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 }}
@@ -99,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 501b506a23..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) => {
@@ -125,12 +127,20 @@ const useUser = () => {
localStorage.setItem('user_id', userId);
}, [userId]);
+ const environment =
+ apiKey === PUBLIC_VITE_EXAMPLE_API_KEY
+ ? '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]);
@@ -249,6 +259,7 @@ const App = () => {
...(userName && { name: userName }),
},
});
+
const searchController = useMemo(() => {
if (!chatClient) return undefined;
@@ -278,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 = ({
-
+