Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: replace StreamChatGenerics with module augmentation #2634

Merged
merged 14 commits into from
Mar 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export default tseslint.config(
{ ignoreRestSiblings: false, caughtErrors: 'none' },
],
'@typescript-eslint/no-unsafe-function-type': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/no-wrapper-object-types': 'error',
'@typescript-eslint/no-non-null-assertion': 'error',
'no-empty-function': 'off',
Expand Down
20 changes: 1 addition & 19 deletions examples/capacitor/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,7 @@ const filters: ChannelFilters = { type: 'messaging', members: { $in: [userId] }
const options: ChannelOptions = { state: true, presence: true, limit: 10 };
const sort: ChannelSort = { last_message_at: -1, updated_at: -1 };

type LocalAttachmentType = Record<string, unknown>;
Copy link
Contributor

Choose a reason for hiding this comment

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

I've yet to see a more beautiful sight than this :D

type LocalChannelType = Record<string, unknown>;
type LocalCommandType = string;
type LocalEventType = Record<string, unknown>;
type LocalMessageType = Record<string, unknown>;
type LocalReactionType = Record<string, unknown>;
type LocalUserType = Record<string, unknown>;

type StreamChatGenerics = {
attachmentType: LocalAttachmentType;
channelType: LocalChannelType;
commandType: LocalCommandType;
eventType: LocalEventType;
messageType: LocalMessageType;
reactionType: LocalReactionType;
userType: LocalUserType;
};

const chatClient = StreamChat.getInstance<StreamChatGenerics>(apiKey);
const chatClient = StreamChat.getInstance(apiKey);

if (process.env.REACT_APP_CHAT_SERVER_ENDPOINT) {
chatClient.setBaseURL(process.env.REACT_APP_CHAT_SERVER_ENDPOINT);
Expand Down
26 changes: 1 addition & 25 deletions examples/typescript/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,7 @@ const filters: ChannelFilters = { type: 'messaging', members: { $in: [userId] }
const options: ChannelOptions = { state: true, presence: true, limit: 10 };
const sort: ChannelSort = { last_message_at: -1, updated_at: -1 };

type LocalAttachmentType = Record<string, unknown>;
type LocalChannelType = Record<string, unknown>;
type LocalCommandType = string;
type LocalEventType = Record<string, unknown>;
type LocalMemberType = Record<string, unknown>;
type LocalMessageType = Record<string, unknown>;
type LocalPollOptionType = Record<string, unknown>;
type LocalPollType = Record<string, unknown>;
type LocalReactionType = Record<string, unknown>;
type LocalUserType = Record<string, unknown>;

type StreamChatGenerics = {
attachmentType: LocalAttachmentType;
channelType: LocalChannelType;
commandType: LocalCommandType;
eventType: LocalEventType;
memberType: LocalMemberType;
messageType: LocalMessageType;
pollOptionType: LocalPollOptionType;
pollType: LocalPollType;
reactionType: LocalReactionType;
userType: LocalUserType;
};

const chatClient = StreamChat.getInstance<StreamChatGenerics>(apiKey);
const chatClient = StreamChat.getInstance(apiKey);

if (process.env.REACT_APP_CHAT_SERVER_ENDPOINT) {
chatClient.setBaseURL(process.env.REACT_APP_CHAT_SERVER_ENDPOINT);
Expand Down
29 changes: 2 additions & 27 deletions examples/vite/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,35 +40,10 @@ const filters: ChannelFilters = {
const options: ChannelOptions = { limit: 5, presence: true, state: true };
const sort: ChannelSort = { pinned_at: 1, last_message_at: -1, updated_at: -1 };

type LocalAttachmentType = Record<string, unknown>;
type LocalChannelType = Record<string, unknown>;
type LocalCommandType = string;
type LocalEventType = Record<string, unknown>;
type LocalMemberType = Record<string, unknown>;
type LocalMessageType = Record<string, unknown>;
type LocalPollOptionType = Record<string, unknown>;
type LocalPollType = Record<string, unknown>;
type LocalReactionType = Record<string, unknown>;
type LocalUserType = Record<string, unknown>;

type StreamChatGenerics = {
attachmentType: LocalAttachmentType;
channelType: LocalChannelType;
commandType: LocalCommandType;
eventType: LocalEventType;
memberType: LocalMemberType;
messageType: LocalMessageType;
pollOptionType: LocalPollOptionType;
pollType: LocalPollType;
reactionType: LocalReactionType;
userType: LocalUserType;
};

const isMessageAIGenerated = (message: StreamMessage<StreamChatGenerics>) =>
!!message?.ai_generated;
const isMessageAIGenerated = (message: StreamMessage) => !!message?.ai_generated;
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, perhaps the typescript compiler is malfunctioning here for some reason - this should be complaining here unless we extend the CustomMessageData interface to include the field; curious as to why it's not ?


const App = () => {
const chatClient = useCreateChatClient<StreamChatGenerics>({
const chatClient = useCreateChatClient({
apiKey,
tokenOrProvider: userToken,
userData: { id: userId },
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@
"react": "^19.0.0",
"react-dom": "^19.0.0",
"semantic-release": "^24.2.3",
"stream-chat": "^8.55.0",
"stream-chat": "^9.0.0-rc.8",
"ts-jest": "^29.2.5",
"typescript": "^5.4.5",
"typescript-eslint": "^8.17.0"
Expand All @@ -260,7 +260,7 @@
"prepare": "husky install",
"preversion": "yarn install",
"test": "jest",
"types": "tsc --noEmit --skipLibCheck false",
"types": "tsc --noEmit",
"validate-translations": "node scripts/validate-translations.js",
"validate-cjs": "concurrently 'node scripts/validate-cjs-node-bundle.cjs' 'node scripts/validate-cjs-browser-bundle.cjs'",
"semantic-release": "semantic-release",
Expand Down
3 changes: 0 additions & 3 deletions src/@types/image-files.d.ts

This file was deleted.

9 changes: 9 additions & 0 deletions src/@types/stream-chat-common-custom-data.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'stream-chat';

declare module 'stream-chat' {
interface CustomChannelData {
image?: string;
name?: string;
subtitle?: string;
}
}
98 changes: 0 additions & 98 deletions src/@types/vfile-message.d.ts

This file was deleted.

19 changes: 6 additions & 13 deletions src/components/AIStateIndicator/AIStateIndicator.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
import React from 'react';

import { Channel } from 'stream-chat';
import type { Channel } from 'stream-chat';

import { AIStates, useAIState } from './hooks/useAIState';

import { useChannelStateContext, useTranslationContext } from '../../context';
import type { DefaultStreamChatGenerics } from '../../types/types';

export type AIStateIndicatorProps<
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
> = {
channel?: Channel<StreamChatGenerics>;
export type AIStateIndicatorProps = {
channel?: Channel;
};

export const AIStateIndicator = <
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
>({
export const AIStateIndicator = ({
channel: channelFromProps,
}: AIStateIndicatorProps<StreamChatGenerics>) => {
}: AIStateIndicatorProps) => {
const { t } = useTranslationContext();
const { channel: channelFromContext } =
useChannelStateContext<StreamChatGenerics>('AIStateIndicator');
const { channel: channelFromContext } = useChannelStateContext('AIStateIndicator');

Check warning on line 16 in src/components/AIStateIndicator/AIStateIndicator.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/AIStateIndicator/AIStateIndicator.tsx#L16

Added line #L16 was not covered by tests
const channel = channelFromProps || channelFromContext;
const { aiState } = useAIState(channel);
const allowedStates = {
Expand Down
28 changes: 9 additions & 19 deletions src/components/AIStateIndicator/hooks/useAIState.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { useEffect, useState } from 'react';

import { AIState, Channel, Event } from 'stream-chat';

import type { DefaultStreamChatGenerics } from '../../../types/types';
import type { AIState, Channel, Event } from 'stream-chat';

export const AIStates = {
Error: 'AI_STATE_ERROR',
Expand All @@ -17,28 +14,21 @@
* @param {Channel} channel - The channel for which we want to know the AI state.
* @returns {{ aiState: AIState }} The current AI state for the given channel.
*/
export const useAIState = <
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
>(
channel?: Channel<StreamChatGenerics>,
): { aiState: AIState } => {
export const useAIState = (channel?: Channel): { aiState: AIState } => {
const [aiState, setAiState] = useState<AIState>(AIStates.Idle);

useEffect(() => {
if (!channel) {
return;
}

const indicatorChangedListener = channel.on(
'ai_indicator.update',
(event: Event<StreamChatGenerics>) => {
const { cid } = event;
const state = event.ai_state as AIState;
if (channel.cid === cid) {
setAiState(state);
}
},
);
const indicatorChangedListener = channel.on('ai_indicator.update', (event: Event) => {
const { cid } = event;
const state = event.ai_state as AIState;

Check warning on line 27 in src/components/AIStateIndicator/hooks/useAIState.ts

View check run for this annotation

Codecov / codecov/patch

src/components/AIStateIndicator/hooks/useAIState.ts#L26-L27

Added lines #L26 - L27 were not covered by tests
if (channel.cid === cid) {
setAiState(state);

Check warning on line 29 in src/components/AIStateIndicator/hooks/useAIState.ts

View check run for this annotation

Codecov / codecov/patch

src/components/AIStateIndicator/hooks/useAIState.ts#L29

Added line #L29 was not covered by tests
}
});

const indicatorClearedListener = channel.on('ai_indicator.clear', (event) => {
const { cid } = event;
Expand Down
Loading