Skip to content

Commit 9f5ea26

Browse files
vishalnarkhedemadsroskarvanGalileasanthoshvai
authored
feat: offline support (#1527)
* feat: offline support * refactor: drop un-used function pragmaUserVersion * refactor: reverting changes to ChannelPreview * fix: fixing migration code issue * refactor: drop limits from queries * refactor: handling of message.read events and reads entity * refactor: added members to separate table * refactor: relative explicit query with left join to enrich users * refactor: cleanup and reorganizing * refactor: added index and foreign key constraints * refactor: changes to be compatible with js client * test: mocking sqlite * refactor: added missing fields on channels table * refactor: changes to be in sync with event from js client * refactor: renaming table and cleanup of columns * refactor: moving all quick-sqlite logic to separate class * test: added simple test for offline support * refactor: handle member related events * refactor: added usage of sync api * refactor: cleaning up un-necessary packages * refactor: fixed issue with update message foreign key constraint failure * refactor: cleanup * refactor: cleanup * tests: writing tests for offline feature * refactor: removed dev-menu package and usage * refactor: remove unused initDevMenu import * refactor: remove un-used flipper packages * docs: adding code comments * refactor: removing unnecessary generics * refactor: cleanup of mapper functions * refactor: renaming offlineChannelsActive to staticChannelsActive * refactor: removed metro config changes * buiild: fixed issue with manual release script * refactor: cleanup of handleEventToSyncDB * refactor: fixing lint issue * fix: removed redundant usage of useStreami18n hook * fix: issue with message not displayed in static state * build: updating stream-chat version * fix: issue with appendWhereClause and undefined value * fix: issue with members state initialization * build: do not format on save * fix: issue mapping memberCount to api response * build: upgrading stream-chat version * fix: reposition image error indicator * refactor: use network error handling to useLoadingImage * fix: image loading issue after network is recovered * fix: logic for sync api and lint issues * feat: point cameraroll dependency to the stream fork version * fix: message.new event handling * refactor: remove unnecessary client as hook dep * refactor: code review changes * refactor: moving offline support logic to Chat component * refactor: fix lint and typescript issues * refactor: handle quick-sqlite not being installed in better way * docs: lint fixes Co-authored-by: Mads Røskar <[email protected]> Co-authored-by: stevegalili <[email protected]> Co-authored-by: Santhosh Vaiyapuri <[email protected]>
1 parent 9063859 commit 9f5ea26

File tree

119 files changed

+3665
-429
lines changed

Some content is hidden

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

119 files changed

+3665
-429
lines changed

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
22
"editor.formatOnSave": true
3-
}
3+
}

bin/release.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,15 @@ cd ..
3030
sed -e 's|"version": "[^"]*"|"version": "'"$1"'"|g' -i.bak src/version.json
3131
rm src/version.json.bak
3232

33+
git add package.json
3334
git add {expo,native}-package/package.json
3435
git add src/version.json
3536

37+
git add yarn.lock
3638
git add expo-package/yarn.lock
3739
git add native-package/yarn.lock
3840

39-
npm version "$1" --force
41+
npm version --no-git-tag-version "$1"
4042

4143
npm publish --tag="$tag"
4244

@@ -45,6 +47,4 @@ npm publish --tag="$tag"
4547

4648

4749
cd ../expo-package
48-
npm publish --tag="$tag"
49-
50-
git push --follow-tags
50+
npm publish --tag="$tag"

docusaurus/docs/reactnative/basics/upgrade_helper.mdx

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import TabItem from '@theme/TabItem';
88

99
## Update Camera Roll dependency
1010

11-
Replace the `@react-native-community/cameraroll` with `@stream-io/react-native-cameraroll`. We had to roll out our fork as `@react-native-community/cameraroll` is no longer accepting pull requests for important fixes like Android 11 compatibility.
11+
Replace the `@react-native-community/cameraroll` with `@stream-io/react-native-cameraroll`. We had to roll out our fork as `@react-native-community/cameraroll` is no longer accepting pull requests for important fixes like Android 11 compatibility.
1212

1313
```bash
1414
# Remove existing cameraroll dependency
@@ -32,14 +32,15 @@ Offline Support is a major opt-in feature introduced in v5 of SDK. To enable off
3232

3333
Until v4, you could provide separate `Chat` component for each usage of `Channel` component or `ChannelList` component.
3434
But from v5, it is necessary that you provide only one instance of `Chat` component within your application.
35-
This component needs to be a parent for all the chat related components such as `ChannelList`, `Channel` or `Thread`.
35+
This component needs to be a parent for all the chat related components such as `ChannelList`, `Channel` or `Thread`.
3636

3737
- **Do not wait for `connectUser` call to succeed**
3838

3939
It is important that you call `connectUser` method on chat client, before you render Chat components.
4040
But you don't need to wait for `connectUser` to succeed before rendering Chat components. This is to ensure:
41-
- Chat components have access to current user information, which is important to store/access offline data.
42-
- In case or slow or no network, Chat components will still load the chat data without waiting for connectUser to succeed.
41+
42+
- Chat components have access to current user information, which is important to store/access offline data.
43+
- In case or slow or no network, Chat components will still load the chat data without waiting for connectUser to succeed.
4344

4445
```tsx {7,8,16}
4546
const chatClient = StreamChat.getInstance('API_KEY');
@@ -48,23 +49,23 @@ Offline Support is a major opt-in feature introduced in v5 of SDK. To enable off
4849

4950
useEffect(() => {
5051
const startChat = async () => {
51-
const connectPromise = chatClient.connectUser(user, tokenOrTokenProvider)
52+
const connectPromise = chatClient.connectUser(user, tokenOrTokenProvider);
5253
setIsClientReady(true); // this allows components to render
5354
await connectPromise();
5455
// Any other post-connectUser logic you may have goes here.
55-
}
56+
};
5657

5758
startChat();
58-
}, [])
59+
}, []);
5960

6061
if (!isClientReady) return null; // or some loading indicator;
6162

6263
return (
6364
<Chat client={chatClient} enableOfflineSupport>
6465
...
6566
</Chat>
66-
)
67-
}
67+
);
68+
};
6869
```
6970

7071
- **Add `enableOfflineSupport` prop on Chat component**
@@ -74,5 +75,5 @@ Offline Support is a major opt-in feature introduced in v5 of SDK. To enable off
7475

7576
<Chat client={chatClient} enableOfflineSupport>
7677
...
77-
</Chat>
78-
```
78+
</Chat>;
79+
```

examples/ExpoMessaging/yarn.lock

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6904,6 +6904,21 @@ [email protected]:
69046904
jsonwebtoken "^8.5.1"
69056905
ws "^7.4.4"
69066906

6907+
6908+
version "7.0.0-offline-support.3"
6909+
resolved "https://registry.yarnpkg.com/stream-chat/-/stream-chat-7.0.0-offline-support.3.tgz#de18a7401f6007b89f5aaf6015ee933b45e218de"
6910+
integrity sha512-uYeDt1MqCDsZ9AIdBw5c7hRM0dshxLUkaYy3Co6gdAqh6AXCEVVDSzjxXT2w1wpKZpxpwZoy4YyQJTYKrNcJNA==
6911+
dependencies:
6912+
"@babel/runtime" "^7.16.3"
6913+
"@types/jsonwebtoken" "^8.5.6"
6914+
"@types/ws" "^7.4.0"
6915+
axios "^0.22.0"
6916+
base64-js "^1.5.1"
6917+
form-data "^4.0.0"
6918+
isomorphic-ws "^4.0.1"
6919+
jsonwebtoken "^8.5.1"
6920+
ws "^7.4.4"
6921+
69076922
strict-uri-encode@^2.0.0:
69086923
version "2.0.0"
69096924
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"

examples/SampleApp/App.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ import { UserSelectorScreen } from './src/screens/UserSelectorScreen';
3131

3232
import type { StreamChat } from 'stream-chat';
3333

34+
LogBox.ignoreLogs(["Seems like you're using an old API"]);
35+
LogBox.ignoreLogs(['Each child in a list should have a unique']);
36+
3437
import type {
3538
StackNavigatorParamList,
3639
StreamChatGenerics,
@@ -128,7 +131,7 @@ const App = () => {
128131
}}
129132
>
130133
<AppContext.Provider value={{ chatClient, loginUser, logout, switchUser }}>
131-
{isConnecting ? (
134+
{isConnecting && !chatClient ? (
132135
<LoadingScreen />
133136
) : chatClient ? (
134137
<DrawerNavigatorWrapper chatClient={chatClient} />
@@ -165,7 +168,7 @@ const DrawerNavigatorWrapper: React.FC<{
165168
return (
166169
<GestureHandlerRootView style={{ flex: 1 }}>
167170
<OverlayProvider<StreamChatGenerics> bottomInset={bottom} value={{ style: streamChatTheme }}>
168-
<Chat<StreamChatGenerics> client={chatClient}>
171+
<Chat<StreamChatGenerics> client={chatClient} enableOfflineSupport>
169172
<AppOverlayProvider>
170173
<UserSearchProvider>
171174
<DrawerNavigator />

examples/SampleApp/ios/Podfile.lock

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ PODS:
5454
- FirebaseCoreDiagnostics (~> 8.0)
5555
- GoogleUtilities/Environment (~> 7.7)
5656
- GoogleUtilities/Logger (~> 7.7)
57-
- FirebaseCoreDiagnostics (8.14.0):
57+
- FirebaseCoreDiagnostics (8.15.0):
5858
- GoogleDataTransport (~> 9.1)
5959
- GoogleUtilities/Environment (~> 7.7)
6060
- GoogleUtilities/Logger (~> 7.7)
@@ -66,7 +66,7 @@ PODS:
6666
- GoogleUtilities/Environment (~> 7.7)
6767
- nanopb (~> 2.30908.0)
6868
- PromisesObjC (< 3.0, >= 1.2)
69-
- FirebaseInstallations (8.14.0):
69+
- FirebaseInstallations (8.15.0):
7070
- FirebaseCore (~> 8.0)
7171
- GoogleUtilities/Environment (~> 7.7)
7272
- GoogleUtilities/UserDefaults (~> 7.7)
@@ -162,9 +162,9 @@ PODS:
162162
- GoogleUtilities/Network (~> 7.7)
163163
- "GoogleUtilities/NSData+zlib (~> 7.7)"
164164
- nanopb (~> 2.30908.0)
165-
- GoogleDataTransport (9.1.2):
166-
- GoogleUtilities/Environment (~> 7.2)
167-
- nanopb (~> 2.30908.0)
165+
- GoogleDataTransport (9.1.4):
166+
- GoogleUtilities/Environment (~> 7.7)
167+
- nanopb (< 2.30910.0, >= 2.30908.0)
168168
- PromisesObjC (< 3.0, >= 1.2)
169169
- GoogleUtilities/AppDelegateSwizzler (7.7.0):
170170
- GoogleUtilities/Environment
@@ -193,7 +193,7 @@ PODS:
193193
- nanopb/decode (2.30908.0)
194194
- nanopb/encode (2.30908.0)
195195
- OpenSSL-Universal (1.1.180)
196-
- PromisesObjC (2.0.0)
196+
- PromisesObjC (2.1.0)
197197
- RCT-Folly (2021.06.28.00-v2):
198198
- boost
199199
- DoubleConversion
@@ -409,14 +409,18 @@ PODS:
409409
- React-jsinspector (0.67.3)
410410
- React-logger (0.67.3):
411411
- glog
412-
- react-native-cameraroll (4.0.4):
412+
- react-native-cameraroll (4.2.0):
413413
- React-Core
414414
- react-native-document-picker (5.0.4):
415415
- React-Core
416416
- react-native-image-resizer (1.4.5):
417417
- React-Core
418418
- react-native-netinfo (6.0.0):
419419
- React-Core
420+
- react-native-quick-sqlite (3.1.8):
421+
- React
422+
- React-callinvoker
423+
- React-Core
420424
- react-native-safe-area-context (3.2.0):
421425
- React-Core
422426
- react-native-video (5.2.0):
@@ -606,10 +610,11 @@ DEPENDENCIES:
606610
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
607611
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
608612
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
609-
- "react-native-cameraroll (from `../node_modules/@react-native-community/cameraroll`)"
613+
- "react-native-cameraroll (from `../node_modules/@stream-io/react-native-cameraroll`)"
610614
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
611615
- react-native-image-resizer (from `../node_modules/react-native-image-resizer`)
612616
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
617+
- react-native-quick-sqlite (from `../node_modules/react-native-quick-sqlite`)
613618
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
614619
- react-native-video (from `../node_modules/react-native-video`)
615620
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
@@ -709,13 +714,15 @@ EXTERNAL SOURCES:
709714
React-logger:
710715
:path: "../node_modules/react-native/ReactCommon/logger"
711716
react-native-cameraroll:
712-
:path: "../node_modules/@react-native-community/cameraroll"
717+
:path: "../node_modules/@stream-io/react-native-cameraroll"
713718
react-native-document-picker:
714719
:path: "../node_modules/react-native-document-picker"
715720
react-native-image-resizer:
716721
:path: "../node_modules/react-native-image-resizer"
717722
react-native-netinfo:
718723
:path: "../node_modules/@react-native-community/netinfo"
724+
react-native-quick-sqlite:
725+
:path: "../node_modules/react-native-quick-sqlite"
719726
react-native-safe-area-context:
720727
:path: "../node_modules/react-native-safe-area-context"
721728
react-native-video:
@@ -783,9 +790,9 @@ SPEC CHECKSUMS:
783790
FirebaseAnalytics: 2fc3876e2eb347673ad2f35e249ae7b15d6c88f5
784791
FirebaseAppDistribution: 54c3de22099cd46d93b6a24b1e70474530bb3188
785792
FirebaseCore: b84a44ee7ba999e0f9f76d198a9c7f60a797b848
786-
FirebaseCoreDiagnostics: fd0c8490f34287229c1d6c103d3a55f81ec85712
793+
FirebaseCoreDiagnostics: 92e07a649aeb66352b319d43bdd2ee3942af84cb
787794
FirebaseCrashlytics: 079ef36f5c4b7161188928faec40fa276ebefd84
788-
FirebaseInstallations: 7d1d967a307c12f1aadd76844fc321cef699b1ce
795+
FirebaseInstallations: 40bd9054049b2eae9a2c38ef1c3dd213df3605cd
789796
FirebaseMessaging: 5ebc42d281567658a2cb72b9ef3506e4a1a1a6e4
790797
Flipper: 30e8eeeed6abdc98edaf32af0cda2f198be4b733
791798
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
@@ -799,13 +806,13 @@ SPEC CHECKSUMS:
799806
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
800807
glog: 85ecdd10ee8d8ec362ef519a6a45ff9aa27b2e85
801808
GoogleAppMeasurement: 71156240babd3cc6ced03e0d54816f01a880c730
802-
GoogleDataTransport: 629c20a4d363167143f30ea78320d5a7eb8bd940
809+
GoogleDataTransport: 5fffe35792f8b96ec8d6775f5eccd83c998d5a3b
803810
GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1
804811
hermes-engine: bf7577d12ac6ccf53ab8b5af3c6ccf0dd8458c5c
805812
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
806813
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
807814
OpenSSL-Universal: 1aa4f6a6ee7256b83db99ec1ccdaa80d10f9af9b
808-
PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58
815+
PromisesObjC: 99b6f43f9e1044bd87a95a60beff28c2c44ddb72
809816
RCT-Folly: 803a9cfd78114b2ec0f140cfa6fa2a6bafb2d685
810817
RCTRequired: 3c77b683474faf23920fbefc71c4e13af21470c0
811818
RCTTypeSafety: 720b1841260dac692444c2822b27403178da8b28
@@ -819,10 +826,11 @@ SPEC CHECKSUMS:
819826
React-jsiexecutor: 15ea57ead631a11fad57634ff69f78e797113a39
820827
React-jsinspector: 1e1e03345cf6d47779e2061d679d0a87d9ae73d8
821828
React-logger: 1e10789cb84f99288479ba5f20822ce43ced6ffe
822-
react-native-cameraroll: 88f4e62d9ecd0e1f253abe4f685474f2ea14bfa2
829+
react-native-cameraroll: 71a4a68ebcff2fcb7305f4b408f9a972658bcdc7
823830
react-native-document-picker: 1a7518132d4a06b67f459be9bb1464a567d2b3b4
824831
react-native-image-resizer: d9fb629a867335bdc13230ac2a58702bb8c8828f
825832
react-native-netinfo: e849fc21ca2f4128a5726c801a82fc6f4a6db50d
833+
react-native-quick-sqlite: e31f937c9385597bfb402ea0e7677e0093bc17d0
826834
react-native-safe-area-context: f0906bf8bc9835ac9a9d3f97e8bde2a997d8da79
827835
react-native-video: a4c2635d0802f983594b7057e1bce8f442f0ad28
828836
React-perflogger: 93d3f142d6d9a46e635f09ba0518027215a41098

examples/SampleApp/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
"dependencies": {
2727
"@notifee/react-native": "^5.0.3",
2828
"@react-native-async-storage/async-storage": "1.15.17",
29-
"@react-native-community/cameraroll": "4.0.4",
3029
"@react-native-community/masked-view": "0.1.11",
3130
"@react-native-community/netinfo": "6.0.0",
3231
"@react-native-firebase/app": "^14.7.0",
@@ -36,6 +35,7 @@
3635
"@react-navigation/native": "5.9.4",
3736
"@react-navigation/stack": "5.14.5",
3837
"@stream-io/flat-list-mvcp": "0.10.2",
38+
"@stream-io/react-native-cameraroll": "^4.2.0",
3939
"patch-package": "6.4.7",
4040
"react": "17.0.2",
4141
"react-native": "0.67.3",
@@ -46,6 +46,7 @@
4646
"react-native-image-crop-picker": "0.36.2",
4747
"react-native-image-resizer": "1.4.5",
4848
"react-native-markdown-package": "1.8.1",
49+
"react-native-quick-sqlite": "^3.1.5",
4950
"react-native-reanimated": "2.7.0",
5051
"react-native-safe-area-context": "3.2.0",
5152
"react-native-screens": "3.2.0",

examples/SampleApp/src/hooks/useChatClient.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react';
22
import { StreamChat } from 'stream-chat';
33
import messaging from '@react-native-firebase/messaging';
44
import notifee from '@notifee/react-native';
5-
5+
import { QuickSqliteClient } from 'stream-chat-react-native';
66
import { USER_TOKENS, USERS } from '../ChatUsers';
77
import AsyncStore from '../utils/AsyncStore';
88

@@ -74,15 +74,17 @@ export const useChatClient = () => {
7474
unsubscribePushListenersRef.current?.();
7575
const client = StreamChat.getInstance<StreamChatGenerics>(config.apiKey, {
7676
timeout: 6000,
77+
// logger: (type, msg) => console.log(type, msg)
7778
});
7879

7980
const user = {
8081
id: config.userId,
8182
image: config.userImage,
8283
name: config.userName,
8384
};
84-
85-
await client.connectUser(user, config.userToken);
85+
const promise = client.connectUser(user, config.userToken);
86+
setChatClient(client);
87+
await promise;
8688
await AsyncStore.setItem('@stream-rn-sampleapp-login-config', config);
8789

8890
const permissionAuthStatus = await messaging().hasPermission();
@@ -162,6 +164,7 @@ export const useChatClient = () => {
162164
};
163165

164166
const logout = async () => {
167+
QuickSqliteClient.resetDB();
165168
setChatClient(null);
166169
chatClient?.disconnectUser();
167170
await AsyncStore.removeItem('@stream-rn-sampleapp-login-config');

examples/SampleApp/src/screens/ChannelListScreen.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ const options = {
5757
state: true,
5858
watch: true,
5959
};
60-
6160
export const ChannelListScreen: React.FC = () => {
6261
const { chatClient } = useAppContext();
6362
const navigation = useNavigation();
@@ -81,7 +80,7 @@ export const ChannelListScreen: React.FC = () => {
8180
() => ({
8281
...baseFilters,
8382
members: {
84-
$in: [chatClientUserId || ''],
83+
$in: [chatClientUserId],
8584
},
8685
}),
8786
[chatClientUserId],
@@ -98,12 +97,14 @@ export const ChannelListScreen: React.FC = () => {
9897
</View>
9998
);
10099

101-
if (!chatClient) return null;
102-
103100
const setScrollRef = (ref: React.RefObject<FlatList<Channel<StreamChatGenerics>> | null>) => {
104101
scrollRef.current = ref;
105102
};
106103

104+
if (!chatClient) {
105+
return null;
106+
}
107+
107108
return (
108109
<View
109110
style={[

examples/SampleApp/src/screens/ChannelScreen.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,15 @@ export const ChannelScreen: React.FC<ChannelScreenProps> = ({
121121
if (!chatClient || !channelId) return;
122122

123123
const newChannel = chatClient?.channel('messaging', channelId);
124+
setChannel(newChannel);
124125

125126
if (!newChannel?.initialized) {
126127
await newChannel?.watch();
127128
}
128-
setChannel(newChannel);
129129
};
130130

131131
initChannel();
132-
}, [channelId]);
132+
}, [channelId, chatClient]);
133133

134134
useFocusEffect(() => {
135135
setSelectedThread(undefined);

0 commit comments

Comments
 (0)