Skip to content

Commit b211ca1

Browse files
authored
Merge pull request #3240 from GetStream/develop
Next Release
2 parents cedf07b + 8c98b12 commit b211ca1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+5655
-3281
lines changed

AGENTS.md

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
# Guidance for AI coding agents
2+
3+
File purpose: operational rules for automated or assisted code changes. Human-facing conceptual docs belong in `README.md` or the docs site.
4+
5+
## Repository purpose
6+
7+
Stream Chat SDKs for:
8+
9+
- React Native CLI
10+
- Expo
11+
12+
Goals: API stability, backward compatibility, predictable releases, strong test coverage, accessibility, and performance discipline.
13+
14+
## Tech & toolchain
15+
16+
- Languages: TypeScript, React (web + native)
17+
- Runtime: Node (use `nvm use` with `.nvmrc`)
18+
- Package manager: Yarn (V1)
19+
- Testing: Jest (unit)
20+
- Lint/Format: ESLint + Prettier
21+
- Build: Package-local build scripts (composed via root)
22+
- Release: Conventional Commits -> automated versioning/publishing
23+
- Platforms:
24+
- React Native: iOS and Android
25+
26+
## Environment setup
27+
28+
1. `nvm use`
29+
2. `yarn install`
30+
3. (Optional) Verify: `node -v` matches `.nvmrc`
31+
4. `cd package`
32+
5. `yarn install-all && cd ..`
33+
6. Run tests: `yarn test:unit`
34+
35+
## Project layout (high-level)
36+
37+
- `package/`
38+
- `native-package/` (`react-native` CLI specific bundle)
39+
- `expo-package/` (`Expo` specific bundle)
40+
- `.` (core UI SDK, shared between both bundles)
41+
- `examples/`
42+
- `ExpoMessaging/`
43+
- `SampleApp/`
44+
- `TypeScriptMessaging/`
45+
- Config roots: linting, tsconfig, playwright, babel
46+
- Do not edit generated output (`lib/`, build artifacts)
47+
48+
## Core commands (Runbook)
49+
50+
| Action | Command |
51+
|-------------------------|------------------------------|
52+
| Install deps | `yarn install` |
53+
| Full build | `yarn install-all` |
54+
| Watch (if available) | `yarn start` (add if absent) |
55+
| Lint | `yarn lint` |
56+
| Fix lint (if separate) | `yarn lint --fix` |
57+
| Unit tests (CI profile) | `yarn test:unit` |
58+
59+
## API design principles
60+
61+
- Semantic versioning
62+
- Use `@deprecated` JSDoc with replacement guidance
63+
- Provide migration docs for breaking changes
64+
- Avoid breaking changes; prefer additive evolution
65+
- Public surfaces: explicit TypeScript types/interfaces
66+
- Consistent naming: `camelCase` for functions/properties, `PascalCase` for components/types
67+
68+
### Deprecation lifecycle
69+
70+
1. Mark with `@deprecated` + rationale + alternative.
71+
2. Maintain for at least one minor release unless security-critical.
72+
3. Add to migration documentation.
73+
4. Remove only in next major.
74+
75+
## Performance guidelines
76+
77+
- Minimize re-renders (memoization, stable refs)
78+
- Use `React.memo` / `useCallback` / `useMemo` when profiling justifies
79+
- Clean up side effects (`AbortController` for network calls, unsubscribe listeners when unmounting)
80+
- Monitor bundle size; justify increases > 2% per package
81+
- Prefer lazy loading for optional heavy modules
82+
- Avoid unnecessary large dependency additions
83+
84+
## Error & logging policy
85+
86+
- Public API: throw descriptive errors or return typed error results (consistent with existing patterns)
87+
- No console noise in production builds
88+
- Internal debug logging gated behind env flag (if present)
89+
- Never leak credentials/user data in errors
90+
91+
## Concurrency & async
92+
93+
- Cancel stale async operations (media, network) when components unmount
94+
- Use `AbortController` for fetch-like APIs
95+
- Avoid race conditions: check instance IDs / timestamps before state updates
96+
97+
## Testing strategy
98+
99+
- Unit: pure functions, small components
100+
- React Native: target minimal smoke + platform logic (avoid flakiness)
101+
- Mocks/fakes: prefer shared test helpers
102+
- Coverage target: maintain or improve existing percentage (fail PR if global coverage drops)
103+
- File naming: `*.test.ts` / `*.spec.ts(x)`
104+
- Add tests for: new public API, bug fixes (regression test), performance-sensitive utilities
105+
106+
## CI expectations
107+
108+
- Mandatory: build, lint, type check, unit/integration tests, (optionally) E2E smoke
109+
- Node versions: those listed in matrix (see workflow YAML files under `.github/workflows/`)
110+
- Failing or flaky tests: fix or quarantine with justification PR comment (temporary)
111+
- Zero new warnings
112+
113+
## Release workflow (high-level)
114+
115+
1. Conventional Commit messages on PR merge
116+
2. Release automation aggregates commits
117+
3. Version bump + changelog + tag
118+
4. Publish to registry
119+
5. Deprecations noted in CHANGELOG
120+
6. Ensure docs updated prior to publishing breaking changes
121+
122+
## Dependency policy
123+
124+
- Avoid adding large deps without justification (size, maintenance)
125+
- Prefer existing utility packages
126+
- Run `yarn audit` (or equivalent) if adding security-impacting deps
127+
- Keep upgrades separate from feature changes when possible
128+
129+
## Samples & docs
130+
131+
- New public feature: update at least one sample app
132+
- Breaking changes: provide migration snippet
133+
- Keep code snippets compilable
134+
- Use placeholder keys (`YOUR_STREAM_KEY`)
135+
136+
## React Native specifics
137+
138+
- Clear Metro cache if module resolution has issues: `yarn react-native start --reset-cache` (for RN CLI) or `yarn expo start --dev-client -c` (for `Expo`)
139+
- Test on iOS + Android for native module or platform-specific UI changes
140+
- Avoid unguarded web-only APIs in shared code
141+
- If the apps in `/examples` are failing to build or install, run:
142+
- `watchman watch-del-all && rm -rf ~/Library/Developer/Xcode/DerivedData/*`
143+
- `(cd ios && bundle exec pod install)` (for RN CLI based sample apps)
144+
- `npx expo prebuild` (if changes have been done in `app.json` of `ExpoMessaging`)
145+
- `rm -rf ios && rm -rf android` (if new native modules have been installed in `ExpoMessaging`)
146+
147+
## Linting & formatting
148+
149+
- Run `yarn lint` before commit
150+
- Narrowly scope `eslint-disable` with inline comments and rationale
151+
- No broad rule disabling
152+
153+
## Commit / PR conventions
154+
155+
- Small, focused PRs
156+
- Include tests for changes
157+
- Screenshot or video for UI changes (before/after)
158+
- Label breaking changes clearly in description
159+
- Document public API changes
160+
161+
## Security
162+
163+
- No credentials or real user data
164+
- Use placeholders in examples
165+
- Scripts must error on missing critical env vars
166+
- Avoid introducing unmaintained dependencies
167+
168+
## Prohibited edits
169+
170+
- Do not edit build artifacts
171+
- `package/lib`
172+
- `ios` and `android` directories in `ExpoMessaging`
173+
- `ios/build` and `android/build` in the other sample apps
174+
- `node_modules` everywhere
175+
- Do not bypass lint/type errors with force merges
176+
177+
## Quick agent checklist (per commit)
178+
179+
- Build succeeds
180+
- Lint clean
181+
- Type check clean
182+
- Tests (unit/integration) green
183+
- Coverage not reduced
184+
- Public API docs updated if changed
185+
- Samples updated if feature surfaced
186+
- No new warnings
187+
- No generated files modified
188+
189+
---
190+
191+
Refine this file iteratively for agent clarity; keep human-facing explanations in docs site / `README.md`.

examples/ExpoMessaging/app.json

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,26 @@
1919
"ios": {
2020
"supportsTablet": true,
2121
"usesIcloudStorage": true,
22-
"bundleIdentifier": "io.stream.expomessagingapp",
23-
"appleTeamId": "EHV7XZLAHA"
22+
"bundleIdentifier": "io.getstream.iOS.expomessagingapp",
23+
"appleTeamId": "EHV7XZLAHA",
24+
"googleServicesFile": "./firebase/GoogleService-Info.plist"
2425
},
2526
"android": {
2627
"config": {
2728
"googleMaps": {
2829
"apiKey": "AIzaSyDVh35biMyXbOjt74CQyO1dlqSMlrdHOOA"
2930
}
3031
},
31-
"package": "io.stream.expomessagingapp",
32+
"package": "io.getstream.android.expomessagingapp",
3233
"adaptiveIcon": {
3334
"foregroundImage": "./assets/adaptive-icon.png",
3435
"backgroundColor": "#ffffff"
3536
},
36-
"permissions": ["android.permission.RECORD_AUDIO", "android.permission.MODIFY_AUDIO_SETTINGS"]
37+
"permissions": [
38+
"android.permission.RECORD_AUDIO",
39+
"android.permission.MODIFY_AUDIO_SETTINGS"
40+
],
41+
"googleServicesFile": "./firebase/google-services.json"
3742
},
3843
"web": {
3944
"favicon": "./assets/favicon.png",
@@ -69,8 +74,22 @@
6974
"microphonePermission": "$(PRODUCT_NAME) would like to use your microphone for voice recording."
7075
}
7176
],
72-
73-
"./plugins/keyboardInsetMainActivityListener.js"
77+
"@react-native-firebase/app",
78+
"@react-native-firebase/messaging",
79+
[
80+
"expo-build-properties",
81+
{
82+
"android": {
83+
"extraMavenRepos": ["$rootDir/../../../node_modules/@notifee/react-native/android/libs"]
84+
},
85+
"ios": {
86+
"useFrameworks": "static",
87+
"forceStaticLinking": ["RNFBApp", "RNFBMessaging"]
88+
}
89+
}
90+
],
91+
"./plugins/keyboardInsetMainActivityListener.js",
92+
"./plugins/opSqliteSwiftPlugin.js"
7493
]
7594
}
7695
}

examples/ExpoMessaging/app/_layout.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,16 @@ import { StyleSheet } from 'react-native';
77
import { SafeAreaProvider } from 'react-native-safe-area-context';
88
import { LiveLocationManagerProvider } from 'stream-chat-expo';
99
import { watchLocation } from '../utils/watchLocation';
10+
import { UserProvider, useUserContext } from '@/context/UserContext';
11+
import UserLogin from '@/components/UserLogin';
12+
13+
function Layout() {
14+
const { user } = useUserContext();
15+
16+
if (!user) {
17+
return <UserLogin />;
18+
}
1019

11-
export default function Layout() {
1220
return (
1321
<SafeAreaProvider>
1422
<GestureHandlerRootView style={styles.container}>
@@ -24,6 +32,14 @@ export default function Layout() {
2432
);
2533
}
2634

35+
export default function App() {
36+
return (
37+
<UserProvider>
38+
<Layout />
39+
</UserProvider>
40+
);
41+
}
42+
2743
const styles = StyleSheet.create({
2844
container: {
2945
flex: 1,
Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,42 @@
1-
import React, { useContext } from 'react';
2-
import { SafeAreaView, View } from 'react-native';
3-
import { Channel, MessageInput, MessageList } from 'stream-chat-expo';
4-
import { Stack, useRouter } from 'expo-router';
1+
import React, { useContext, useEffect, useState } from 'react';
2+
import type { Channel as StreamChatChannel } from 'stream-chat';
3+
import { Channel, MessageInput, MessageFlashList, useChatContext } from 'stream-chat-expo';
4+
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
55
import { AuthProgressLoader } from '../../../components/AuthProgressLoader';
66
import { AppContext } from '../../../context/AppContext';
77
import { useHeaderHeight } from '@react-navigation/elements';
88
import InputButtons from '../../../components/InputButtons';
99
import { MessageLocation } from '../../../components/LocationSharing/MessageLocation';
10+
import { SafeAreaView } from 'react-native-safe-area-context';
11+
import { StyleSheet } from 'react-native';
1012

1113
export default function ChannelScreen() {
14+
const { client } = useChatContext();
1215
const router = useRouter();
13-
const { setThread, channel, thread } = useContext(AppContext);
16+
const params = useLocalSearchParams();
17+
const navigateThroughPushNotification = params.push_notification as string;
18+
const channelId = params.cid as string;
19+
const [channelFromParams, setChannelFromParams] = useState<StreamChatChannel | undefined>(
20+
undefined,
21+
);
22+
23+
// Effect to fetch channel from params if channel is navigated through push notification
24+
useEffect(() => {
25+
const initChannel = async () => {
26+
if (navigateThroughPushNotification) {
27+
const channel = client?.channel('messaging', channelId);
28+
await channel?.watch();
29+
setChannelFromParams(channel);
30+
}
31+
};
32+
initChannel();
33+
}, [navigateThroughPushNotification, channelId, client]);
34+
35+
const { setThread, channel: channelContext, thread } = useContext(AppContext);
1436
const headerHeight = useHeaderHeight();
1537

38+
const channel = channelFromParams || channelContext;
39+
1640
if (!channel) {
1741
return <AuthProgressLoader />;
1842
}
@@ -32,29 +56,36 @@ export default function ChannelScreen() {
3256
defaultHandler?.();
3357
};
3458

59+
if (!channel) {
60+
return null;
61+
}
62+
3563
return (
36-
<SafeAreaView>
64+
<Channel
65+
audioRecordingEnabled={true}
66+
channel={channel}
67+
onPressMessage={onPressMessage}
68+
keyboardVerticalOffset={headerHeight}
69+
MessageLocation={MessageLocation}
70+
thread={thread}
71+
>
3772
<Stack.Screen options={{ title: 'Channel Screen' }} />
38-
{channel && (
39-
<Channel
40-
audioRecordingEnabled={true}
41-
channel={channel}
42-
onPressMessage={onPressMessage}
43-
keyboardVerticalOffset={headerHeight}
44-
MessageLocation={MessageLocation}
45-
thread={thread}
46-
>
47-
<View style={{ flex: 1 }}>
48-
<MessageList
49-
onThreadSelect={(thread) => {
50-
setThread(thread);
51-
router.push(`/channel/${channel.cid}/thread/${thread.cid}`);
52-
}}
53-
/>
54-
<MessageInput InputButtons={InputButtons} />
55-
</View>
56-
</Channel>
57-
)}
58-
</SafeAreaView>
73+
74+
<SafeAreaView edges={['bottom']} style={styles.container}>
75+
<MessageFlashList
76+
onThreadSelect={(thread) => {
77+
setThread(thread);
78+
router.push(`/channel/${channel.cid}/thread/${thread.cid}`);
79+
}}
80+
/>
81+
<MessageInput InputButtons={InputButtons} />
82+
</SafeAreaView>
83+
</Channel>
5984
);
6085
}
86+
87+
const styles = StyleSheet.create({
88+
container: {
89+
flex: 1,
90+
},
91+
});

0 commit comments

Comments
 (0)