From 4d1467541b3e45d8da187739f8747e41d934a4ea Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Tue, 2 Apr 2024 14:14:36 +0200 Subject: [PATCH 1/7] fix(offline-support): foreign constraint failure --- examples/SampleApp/App.tsx | 9 ++ .../ios/SampleApp.xcodeproj/project.pbxproj | 94 ++++++++++--------- examples/SampleApp/metro.config.js | 11 +-- examples/SampleApp/yarn.lock | 25 +---- examples/TypeScriptMessaging/metro.config.js | 10 +- .../Chat/hooks/handleEventToSyncDB.ts | 50 +++++++++- .../components/Chat/hooks/useSyncDatabase.ts | 2 +- .../apis/upsertChannelDataFromChannel.ts | 25 +++++ package/src/store/apis/upsertMessages.ts | 72 +++++++++++--- .../src/store/mappers/mapChannelToStorable.ts | 68 ++++++++++++++ package/src/utils/DBSyncManager.ts | 10 +- 11 files changed, 280 insertions(+), 96 deletions(-) create mode 100644 package/src/store/apis/upsertChannelDataFromChannel.ts create mode 100644 package/src/store/mappers/mapChannelToStorable.ts diff --git a/examples/SampleApp/App.tsx b/examples/SampleApp/App.tsx index 8781a891a1..89615f19e2 100644 --- a/examples/SampleApp/App.tsx +++ b/examples/SampleApp/App.tsx @@ -80,6 +80,10 @@ const App = () => { useEffect(() => { const unsubscribeOnNotificationOpen = messaging().onNotificationOpenedApp((remoteMessage) => { + console.log('background', { + dataFromRemoteMessage: remoteMessage.data, + remoteMessage, + }); // Notification caused app to open from background state on iOS const channelId = remoteMessage.data?.channel_id as string; if (channelId) { @@ -89,6 +93,11 @@ const App = () => { // handle notification clicks on foreground const unsubscribeForegroundEvent = notifee.onForegroundEvent(({ detail, type }) => { if (type === EventType.PRESS) { + console.log('foreground', { + dataFromRemoteMessage: detail.notification?.data, + dataStream: detail.notification?.data, + remoteMessage: detail.notification, + }); // user has pressed the foreground notification const channelId = detail.notification?.data?.channel_id as string; if (channelId) { diff --git a/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj b/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj index 83d4bc4364..34ca1bdd31 100644 --- a/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj +++ b/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj @@ -606,24 +606,30 @@ "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios", "${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers", "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", - " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", ); IPHONEOS_DEPLOYMENT_TARGET = 13.4; LD_RUNPATH_SEARCH_PATHS = ( @@ -645,10 +651,7 @@ "-DFOLLY_USE_LIBCPP=1", "-DFOLLY_CFG_NO_COROUTINES=1", ); - OTHER_LDFLAGS = ( - "$(inherited)", - " ", - ); + OTHER_LDFLAGS = "$(inherited) "; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; @@ -708,24 +711,30 @@ "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios", "${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers", "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", - " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", ); IPHONEOS_DEPLOYMENT_TARGET = 13.4; LD_RUNPATH_SEARCH_PATHS = ( @@ -746,10 +755,7 @@ "-DFOLLY_USE_LIBCPP=1", "-DFOLLY_CFG_NO_COROUTINES=1", ); - OTHER_LDFLAGS = ( - "$(inherited)", - " ", - ); + OTHER_LDFLAGS = "$(inherited) "; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; diff --git a/examples/SampleApp/metro.config.js b/examples/SampleApp/metro.config.js index 784f43cd43..e607aacbf2 100644 --- a/examples/SampleApp/metro.config.js +++ b/examples/SampleApp/metro.config.js @@ -11,19 +11,17 @@ const config = getDefaultConfig(__dirname); const PATH = require('path'); const packageDirPath = PATH.resolve(__dirname, '../../package'); -const nativePackageDirPath = PATH.resolve(__dirname, '../../package/native-package'); const symlinked = { - 'stream-chat-react-native': nativePackageDirPath, + 'stream-chat-react-native': PATH.resolve(packageDirPath, 'native-package'), 'stream-chat-react-native-core': packageDirPath, }; // find what all modules need to be unique for the app (mainly react and react-native) -// note: we filter the symlinked modules as they are already unique -// and as they dont follow the workspace pattern the auto-generated path to the module is incorrect const dependencyPackageNames = Object.keys(require('./package.json').dependencies); const uniqueModules = dependencyPackageNames.map((packageName) => { + // if the module is symlinked, use the symlinked path that we know if (symlinked[packageName]) { const modulePath = symlinked[packageName]; const escapedPackageName = PATH.normalize(packageName).replace(/\\/g, '\\\\'); @@ -61,9 +59,10 @@ const extraNodeModules = uniqueModules.reduce((acc, item) => { config.resolver.blockList = exclusionList(blockList); config.resolver.extraNodeModules = extraNodeModules; -config.resolver.nodeModulesPaths = [PATH.resolve(__dirname, 'node_modules')]; +extraNodeModules['stream-chat'] = '/Users/santhoshvaiyapuri/Projects/stream-chat-js'; // add the package dir for metro to access the package folder -config.watchFolders = [packageDirPath]; +config.watchFolders = [packageDirPath, '/Users/santhoshvaiyapuri/Projects/stream-chat-js']; +config.resolver.nodeModulesPaths = [PATH.resolve(__dirname, 'node_modules')]; module.exports = config; diff --git a/examples/SampleApp/yarn.lock b/examples/SampleApp/yarn.lock index 2b4c4fa121..6ed6b69056 100644 --- a/examples/SampleApp/yarn.lock +++ b/examples/SampleApp/yarn.lock @@ -6897,10 +6897,10 @@ statuses@~1.5.0: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== -stream-chat-react-native-core@5.24.0: - version "5.24.0" - resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-5.24.0.tgz#425a06d2ad70efa322b8b4a6293de820fee9bb7d" - integrity sha512-HAGMjx69vfPLW8qOVKlcmQGTHDE3NBNi5H2mrf72J/mkSPtmFjMfC3/ZWY9RPwjdUyBMkUCv85ZN0n92sNHn6w== +stream-chat-react-native-core@5.26.0: + version "5.26.0" + resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-5.26.0.tgz#b081bde25585a9f571ac7bb2dc8c910e4ddc99c5" + integrity sha512-Sh5MufLMn6JlEDiML+xDwa5EaCkegHMhKTXdBxfbtbbxaBl1WDOhpwgR6ROIyvGt8nuJ4NQOzRseiNCyXpibXA== dependencies: "@babel/runtime" "^7.12.5" "@gorhom/bottom-sheet" "4.4.8" @@ -6915,7 +6915,7 @@ stream-chat-react-native-core@5.24.0: path "0.12.7" react-native-markdown-package "1.8.2" react-native-url-polyfill "^1.3.0" - stream-chat "8.15.0" + stream-chat "8.17.0" "stream-chat-react-native-core@link:../../package": version "0.0.0" @@ -6925,21 +6925,6 @@ stream-chat-react-native-core@5.24.0: version "0.0.0" uid "" -stream-chat@8.15.0: - version "8.15.0" - resolved "https://registry.yarnpkg.com/stream-chat/-/stream-chat-8.15.0.tgz#37d84768b24e32fcea85b29165e05cb2d5cae517" - integrity sha512-6qrV0pL5dBkqPslpoSkBLjrgiMtFaOvUChue7j3VafhogXGLpu4j6ACPWq3Lrj9XJGfJkOwfT7LyqEhwfmajgQ== - dependencies: - "@babel/runtime" "^7.16.3" - "@types/jsonwebtoken" "~9.0.0" - "@types/ws" "^7.4.0" - axios "^1.6.0" - base64-js "^1.5.1" - form-data "^4.0.0" - isomorphic-ws "^4.0.1" - jsonwebtoken "~9.0.0" - ws "^7.4.4" - stream-chat@8.17.0: version "8.17.0" resolved "https://registry.yarnpkg.com/stream-chat/-/stream-chat-8.17.0.tgz#01c4aacbcdb5dd734b088e70f40cd42a0bcd0fb7" diff --git a/examples/TypeScriptMessaging/metro.config.js b/examples/TypeScriptMessaging/metro.config.js index 784f43cd43..61902517cb 100644 --- a/examples/TypeScriptMessaging/metro.config.js +++ b/examples/TypeScriptMessaging/metro.config.js @@ -11,19 +11,17 @@ const config = getDefaultConfig(__dirname); const PATH = require('path'); const packageDirPath = PATH.resolve(__dirname, '../../package'); -const nativePackageDirPath = PATH.resolve(__dirname, '../../package/native-package'); const symlinked = { - 'stream-chat-react-native': nativePackageDirPath, + 'stream-chat-react-native': PATH.resolve(packageDirPath, 'native-package'), 'stream-chat-react-native-core': packageDirPath, }; // find what all modules need to be unique for the app (mainly react and react-native) -// note: we filter the symlinked modules as they are already unique -// and as they dont follow the workspace pattern the auto-generated path to the module is incorrect const dependencyPackageNames = Object.keys(require('./package.json').dependencies); const uniqueModules = dependencyPackageNames.map((packageName) => { + // if the module is symlinked, use the symlinked path that we know if (symlinked[packageName]) { const modulePath = symlinked[packageName]; const escapedPackageName = PATH.normalize(packageName).replace(/\\/g, '\\\\'); @@ -61,9 +59,9 @@ const extraNodeModules = uniqueModules.reduce((acc, item) => { config.resolver.blockList = exclusionList(blockList); config.resolver.extraNodeModules = extraNodeModules; -config.resolver.nodeModulesPaths = [PATH.resolve(__dirname, 'node_modules')]; +extraNodeModules['stream-chat'] = '/Users/santhoshvaiyapuri/Projects/stream-chat-js'; // add the package dir for metro to access the package folder -config.watchFolders = [packageDirPath]; +config.watchFolders = [packageDirPath, '/Users/santhoshvaiyapuri/Projects/stream-chat-js']; module.exports = config; diff --git a/package/src/components/Chat/hooks/handleEventToSyncDB.ts b/package/src/components/Chat/hooks/handleEventToSyncDB.ts index 8214bfce10..13e3ca964c 100644 --- a/package/src/components/Chat/hooks/handleEventToSyncDB.ts +++ b/package/src/components/Chat/hooks/handleEventToSyncDB.ts @@ -1,16 +1,25 @@ -import type { Event } from 'stream-chat'; +import { PreparedQueries } from 'src/store/types'; +import { DefaultStreamChatGenerics } from 'src/types/types'; +import type { Event, StreamChat } from 'stream-chat'; import { deleteChannel } from '../../../store/apis/deleteChannel'; import { deleteMember } from '../../../store/apis/deleteMember'; import { deleteMessagesForChannel } from '../../../store/apis/deleteMessagesForChannel'; import { updateMessage } from '../../../store/apis/updateMessage'; import { upsertChannelData } from '../../../store/apis/upsertChannelData'; +import { upsertChannelDataFromChannel } from '../../../store/apis/upsertChannelDataFromChannel'; import { upsertChannels } from '../../../store/apis/upsertChannels'; import { upsertMembers } from '../../../store/apis/upsertMembers'; import { upsertMessages } from '../../../store/apis/upsertMessages'; import { upsertReads } from '../../../store/apis/upsertReads'; -export const handleEventToSyncDB = (event: Event, flush?: boolean) => { +export const handleEventToSyncDB = < + StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, +>( + event: Event, + client: StreamChat, + flush?: boolean, +) => { const { type } = event; if (type === 'message.read') { @@ -31,10 +40,45 @@ export const handleEventToSyncDB = (event: Event, flush?: boolean) => { if (type === 'message.new') { if (event.message && (!event.message.parent_id || event.message.show_in_channel)) { - return upsertMessages({ + const channel = + event.channel_type && event.channel_id + ? client.channel(event.channel_type, event.channel_id) + : undefined; + const skippedMessageQueries: PreparedQueries[] = []; + const queries = upsertMessages({ flush, messages: [event.message], + onNonExistentChannel: (map) => { + map.forEach((skippedQueries) => { + skippedMessageQueries.push(...skippedQueries); + }); + }, }); + // if a channel was hidden and a new message was received it would be missing from the database + if (skippedMessageQueries.length) { + if (channel && channel.initialized && !channel.disconnected) { + const channelQuery = upsertChannelDataFromChannel({ + channel, + flush, + }); + if (channelQuery) { + return [...channelQuery, ...queries, ...skippedMessageQueries]; + } else { + console.warn( + 'Couldnt create channel queries on "message.new" for an initialized channel that is not in DB, skipping message', + { event }, + ); + return []; + } + } else { + console.warn( + 'Received "message.new" event for a non initialized channel that is not in DB, skipping message', + { event }, + ); + return []; + } + } + return queries; } } diff --git a/package/src/components/Chat/hooks/useSyncDatabase.ts b/package/src/components/Chat/hooks/useSyncDatabase.ts index 536319850d..9b69dbefa3 100644 --- a/package/src/components/Chat/hooks/useSyncDatabase.ts +++ b/package/src/components/Chat/hooks/useSyncDatabase.ts @@ -22,7 +22,7 @@ export const useSyncDatabase = < let listener: ReturnType | undefined; if (enableOfflineSupport && initialisedDatabase) { - listener = client?.on(handleEventToSyncDB); + listener = client?.on((event) => handleEventToSyncDB(event, client)); } return () => { diff --git a/package/src/store/apis/upsertChannelDataFromChannel.ts b/package/src/store/apis/upsertChannelDataFromChannel.ts new file mode 100644 index 0000000000..b8577e18e4 --- /dev/null +++ b/package/src/store/apis/upsertChannelDataFromChannel.ts @@ -0,0 +1,25 @@ +import { DefaultStreamChatGenerics } from 'src/types/types'; +import type { Channel } from 'stream-chat'; + +import { mapChannelToStorable } from '../mappers/mapChannelToStorable'; +import { QuickSqliteClient } from '../QuickSqliteClient'; +import { createUpsertQuery } from '../sqlite-utils/createUpsertQuery'; + +export const upsertChannelDataFromChannel = < + StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, +>({ + channel, + flush = true, +}: { + channel: Channel; + flush?: boolean; +}) => { + const storableChannel = mapChannelToStorable(channel); + if (!storableChannel) return; + const query = createUpsertQuery('channels', storableChannel); + if (flush) { + QuickSqliteClient.executeSqlBatch([query]); + } + + return [query]; +}; diff --git a/package/src/store/apis/upsertMessages.ts b/package/src/store/apis/upsertMessages.ts index 0077131067..482fe940f6 100644 --- a/package/src/store/apis/upsertMessages.ts +++ b/package/src/store/apis/upsertMessages.ts @@ -4,39 +4,79 @@ import { mapMessageToStorable } from '../mappers/mapMessageToStorable'; import { mapReactionToStorable } from '../mappers/mapReactionToStorable'; import { mapUserToStorable } from '../mappers/mapUserToStorable'; import { QuickSqliteClient } from '../QuickSqliteClient'; +import { createSelectQuery } from '../sqlite-utils/createSelectQuery'; import { createUpsertQuery } from '../sqlite-utils/createUpsertQuery'; +import type { PreparedQueries } from '../types'; export const upsertMessages = ({ flush = true, messages, + onNonExistentChannel, }: { messages: MessageResponse[]; flush?: boolean; + /** + * Callback to be called when a message is received for a non-existent channel. + * This can happen when a channel is hidden and a new message comes in. + * In this case, the channel should be re-added to the database and then the message should be added. + * **Note**: This callback is to be called with a map of channel cid to the queries that should be executed for that channel + */ + onNonExistentChannel?: (map: Map) => void; }) => { const storableMessages: Array> = []; const storableUsers: Array> = []; const storableReactions: Array> = []; - messages?.forEach((message: MessageResponse) => { - storableMessages.push(mapMessageToStorable(message)); + const usersToUpsert: PreparedQueries[] = []; + const messagesToUpsert: PreparedQueries[] = []; + const reactionsToUpsert: PreparedQueries[] = []; + + const nonExistentChannelQueriesMap = new Map(); + + messages.forEach((message) => { + let storableMessage = mapMessageToStorable(message); + storableMessages.push(storableMessage); + const messageQuery = createUpsertQuery('messages', storableMessage); + const userQueries: PreparedQueries[] = []; if (message.user) { - storableUsers.push(mapUserToStorable(message.user)); + const userStorable = mapUserToStorable(message.user); + storableUsers.push(userStorable); + userQueries.push(createUpsertQuery('users', userStorable)); } + const reactionsQueries: PreparedQueries[] = []; [...(message.latest_reactions || []), ...(message.own_reactions || [])].forEach((r) => { if (r.user) { - storableUsers.push(mapUserToStorable(r.user)); + const reactionUserStorable = mapUserToStorable(r.user); + storableUsers.push(reactionUserStorable); + userQueries.push(createUpsertQuery('users', reactionUserStorable)); } - storableReactions.push(mapReactionToStorable(r)); + const reactionStorable = mapReactionToStorable(r); + storableReactions.push(reactionStorable); + reactionsQueries.push(createUpsertQuery('reactions', reactionStorable)); }); - }); - const finalQueries = [ - ...storableMessages.map((storableMessage) => createUpsertQuery('messages', storableMessage)), - ...storableUsers.map((storableUser) => createUpsertQuery('users', storableUser)), - ...storableReactions.map((storableReaction) => - createUpsertQuery('reactions', storableReaction), - ), - ]; + if (message.cid) { + const channels = QuickSqliteClient.executeSql.apply( + null, + createSelectQuery('channels', ['cid'], { + cid: message.cid, + }), + ); + + if (channels.length === 0) { + // channel was not present and a new message came so we must re-add the channel first + const skipped = [messageQuery, ...userQueries, ...reactionsQueries]; + storableMessage = Object.assign({ skipped: true }, storableMessage); + const previousQueries = nonExistentChannelQueriesMap.get(message.cid) ?? []; + nonExistentChannelQueriesMap.set(message.cid, previousQueries.concat(skipped)); + return; + } + } + + messagesToUpsert.push(messageQuery); + usersToUpsert.push(...userQueries); + reactionsToUpsert.push(...reactionsQueries); + }); QuickSqliteClient.logger?.('info', 'upsertMessages', { flush, @@ -45,6 +85,12 @@ export const upsertMessages = ({ users: storableUsers, }); + // note: the order is important here.. messages, users should be inserted first and then reactions. + // As reactions have foreign key constraints to messages and users. + const finalQueries = [...messagesToUpsert, ...usersToUpsert, ...reactionsToUpsert]; + + onNonExistentChannel?.(nonExistentChannelQueriesMap); + if (flush) { QuickSqliteClient.executeSqlBatch(finalQueries); } diff --git a/package/src/store/mappers/mapChannelToStorable.ts b/package/src/store/mappers/mapChannelToStorable.ts new file mode 100644 index 0000000000..1eaf33efdf --- /dev/null +++ b/package/src/store/mappers/mapChannelToStorable.ts @@ -0,0 +1,68 @@ +import type { Channel, ChannelResponse } from 'stream-chat'; + +import { mapDateTimeToStorable } from './mapDateTimeToStorable'; + +import type { DefaultStreamChatGenerics } from '../../types/types'; + +import type { TableRow } from '../types'; + +export const mapChannelToStorable = < + StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, +>( + channel: Channel, +): TableRow<'channels'> | undefined => { + if (!channel.data) return; + const { + auto_translation_enabled, + auto_translation_language, + cid, + config, + cooldown, + created_at, + deleted_at, + disabled, + frozen, + hidden, + id, + invites, + last_message_at, + member_count, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + members, + muted, + own_capabilities, + team, + truncated_at, + truncated_by, + truncated_by_id, + type, + updated_at, + ...extraData + } = channel.data as unknown as ChannelResponse; + + return { + autoTranslationEnabled: auto_translation_enabled, + autoTranslationLanguage: auto_translation_language, + cid, + config: config && JSON.stringify(config), + cooldown, + createdAt: mapDateTimeToStorable(created_at), + deletedAt: mapDateTimeToStorable(deleted_at), + disabled, + extraData: JSON.stringify(extraData), + frozen, + hidden, + id, + invites: invites && JSON.stringify(invites), + lastMessageAt: mapDateTimeToStorable(last_message_at), + memberCount: member_count, + muted, + ownCapabilities: own_capabilities && JSON.stringify(own_capabilities), + team, + truncatedAt: truncated_at, + truncatedBy: truncated_by && JSON.stringify(truncated_by), + truncatedById: truncated_by_id, + type, + updatedAt: updated_at, + }; +}; diff --git a/package/src/utils/DBSyncManager.ts b/package/src/utils/DBSyncManager.ts index 5fbf6c7bdf..e5638208b9 100644 --- a/package/src/utils/DBSyncManager.ts +++ b/package/src/utils/DBSyncManager.ts @@ -90,7 +90,11 @@ export class DBSyncManager { }; }; - static sync = async () => { + static sync = async < + StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, + >( + client: StreamChat, + ) => { if (!this.client?.user) return; const cids = getAllChannelIds(); // If there are no channels, then there is no need to sync. @@ -113,7 +117,7 @@ export class DBSyncManager { try { const result = await this.client.sync(cids, lastSyncedAtDate.toISOString()); const queries = result.events.reduce((queries, event) => { - queries = queries.concat(handleEventToSyncDB(event, false)); + queries = queries.concat(handleEventToSyncDB(event, client, false)); return queries; }, []); @@ -137,7 +141,7 @@ export class DBSyncManager { if (!this.client) return; await this.executePendingTasks(this.client); - await this.sync(); + await this.sync(this.client); }; static queueTask = async < From be51c021c42efde5620d22c8e190205231f34629 Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Tue, 2 Apr 2024 14:21:09 +0200 Subject: [PATCH 2/7] revert unnecessary changes --- examples/SampleApp/App.tsx | 9 -- .../ios/SampleApp.xcodeproj/project.pbxproj | 94 +++++++++---------- examples/SampleApp/metro.config.js | 11 ++- examples/SampleApp/yarn.lock | 25 ++++- examples/TypeScriptMessaging/metro.config.js | 10 +- 5 files changed, 76 insertions(+), 73 deletions(-) diff --git a/examples/SampleApp/App.tsx b/examples/SampleApp/App.tsx index 89615f19e2..8781a891a1 100644 --- a/examples/SampleApp/App.tsx +++ b/examples/SampleApp/App.tsx @@ -80,10 +80,6 @@ const App = () => { useEffect(() => { const unsubscribeOnNotificationOpen = messaging().onNotificationOpenedApp((remoteMessage) => { - console.log('background', { - dataFromRemoteMessage: remoteMessage.data, - remoteMessage, - }); // Notification caused app to open from background state on iOS const channelId = remoteMessage.data?.channel_id as string; if (channelId) { @@ -93,11 +89,6 @@ const App = () => { // handle notification clicks on foreground const unsubscribeForegroundEvent = notifee.onForegroundEvent(({ detail, type }) => { if (type === EventType.PRESS) { - console.log('foreground', { - dataFromRemoteMessage: detail.notification?.data, - dataStream: detail.notification?.data, - remoteMessage: detail.notification, - }); // user has pressed the foreground notification const channelId = detail.notification?.data?.channel_id as string; if (channelId) { diff --git a/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj b/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj index 34ca1bdd31..83d4bc4364 100644 --- a/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj +++ b/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj @@ -606,30 +606,24 @@ "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios", "${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers", "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", ); IPHONEOS_DEPLOYMENT_TARGET = 13.4; LD_RUNPATH_SEARCH_PATHS = ( @@ -651,7 +645,10 @@ "-DFOLLY_USE_LIBCPP=1", "-DFOLLY_CFG_NO_COROUTINES=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; @@ -711,30 +708,24 @@ "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios", "${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers", "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", - "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", ); IPHONEOS_DEPLOYMENT_TARGET = 13.4; LD_RUNPATH_SEARCH_PATHS = ( @@ -755,7 +746,10 @@ "-DFOLLY_USE_LIBCPP=1", "-DFOLLY_CFG_NO_COROUTINES=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; diff --git a/examples/SampleApp/metro.config.js b/examples/SampleApp/metro.config.js index e607aacbf2..784f43cd43 100644 --- a/examples/SampleApp/metro.config.js +++ b/examples/SampleApp/metro.config.js @@ -11,17 +11,19 @@ const config = getDefaultConfig(__dirname); const PATH = require('path'); const packageDirPath = PATH.resolve(__dirname, '../../package'); +const nativePackageDirPath = PATH.resolve(__dirname, '../../package/native-package'); const symlinked = { - 'stream-chat-react-native': PATH.resolve(packageDirPath, 'native-package'), + 'stream-chat-react-native': nativePackageDirPath, 'stream-chat-react-native-core': packageDirPath, }; // find what all modules need to be unique for the app (mainly react and react-native) +// note: we filter the symlinked modules as they are already unique +// and as they dont follow the workspace pattern the auto-generated path to the module is incorrect const dependencyPackageNames = Object.keys(require('./package.json').dependencies); const uniqueModules = dependencyPackageNames.map((packageName) => { - // if the module is symlinked, use the symlinked path that we know if (symlinked[packageName]) { const modulePath = symlinked[packageName]; const escapedPackageName = PATH.normalize(packageName).replace(/\\/g, '\\\\'); @@ -59,10 +61,9 @@ const extraNodeModules = uniqueModules.reduce((acc, item) => { config.resolver.blockList = exclusionList(blockList); config.resolver.extraNodeModules = extraNodeModules; -extraNodeModules['stream-chat'] = '/Users/santhoshvaiyapuri/Projects/stream-chat-js'; +config.resolver.nodeModulesPaths = [PATH.resolve(__dirname, 'node_modules')]; // add the package dir for metro to access the package folder -config.watchFolders = [packageDirPath, '/Users/santhoshvaiyapuri/Projects/stream-chat-js']; -config.resolver.nodeModulesPaths = [PATH.resolve(__dirname, 'node_modules')]; +config.watchFolders = [packageDirPath]; module.exports = config; diff --git a/examples/SampleApp/yarn.lock b/examples/SampleApp/yarn.lock index 6ed6b69056..2b4c4fa121 100644 --- a/examples/SampleApp/yarn.lock +++ b/examples/SampleApp/yarn.lock @@ -6897,10 +6897,10 @@ statuses@~1.5.0: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== -stream-chat-react-native-core@5.26.0: - version "5.26.0" - resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-5.26.0.tgz#b081bde25585a9f571ac7bb2dc8c910e4ddc99c5" - integrity sha512-Sh5MufLMn6JlEDiML+xDwa5EaCkegHMhKTXdBxfbtbbxaBl1WDOhpwgR6ROIyvGt8nuJ4NQOzRseiNCyXpibXA== +stream-chat-react-native-core@5.24.0: + version "5.24.0" + resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-5.24.0.tgz#425a06d2ad70efa322b8b4a6293de820fee9bb7d" + integrity sha512-HAGMjx69vfPLW8qOVKlcmQGTHDE3NBNi5H2mrf72J/mkSPtmFjMfC3/ZWY9RPwjdUyBMkUCv85ZN0n92sNHn6w== dependencies: "@babel/runtime" "^7.12.5" "@gorhom/bottom-sheet" "4.4.8" @@ -6915,7 +6915,7 @@ stream-chat-react-native-core@5.26.0: path "0.12.7" react-native-markdown-package "1.8.2" react-native-url-polyfill "^1.3.0" - stream-chat "8.17.0" + stream-chat "8.15.0" "stream-chat-react-native-core@link:../../package": version "0.0.0" @@ -6925,6 +6925,21 @@ stream-chat-react-native-core@5.26.0: version "0.0.0" uid "" +stream-chat@8.15.0: + version "8.15.0" + resolved "https://registry.yarnpkg.com/stream-chat/-/stream-chat-8.15.0.tgz#37d84768b24e32fcea85b29165e05cb2d5cae517" + integrity sha512-6qrV0pL5dBkqPslpoSkBLjrgiMtFaOvUChue7j3VafhogXGLpu4j6ACPWq3Lrj9XJGfJkOwfT7LyqEhwfmajgQ== + dependencies: + "@babel/runtime" "^7.16.3" + "@types/jsonwebtoken" "~9.0.0" + "@types/ws" "^7.4.0" + axios "^1.6.0" + base64-js "^1.5.1" + form-data "^4.0.0" + isomorphic-ws "^4.0.1" + jsonwebtoken "~9.0.0" + ws "^7.4.4" + stream-chat@8.17.0: version "8.17.0" resolved "https://registry.yarnpkg.com/stream-chat/-/stream-chat-8.17.0.tgz#01c4aacbcdb5dd734b088e70f40cd42a0bcd0fb7" diff --git a/examples/TypeScriptMessaging/metro.config.js b/examples/TypeScriptMessaging/metro.config.js index 61902517cb..784f43cd43 100644 --- a/examples/TypeScriptMessaging/metro.config.js +++ b/examples/TypeScriptMessaging/metro.config.js @@ -11,17 +11,19 @@ const config = getDefaultConfig(__dirname); const PATH = require('path'); const packageDirPath = PATH.resolve(__dirname, '../../package'); +const nativePackageDirPath = PATH.resolve(__dirname, '../../package/native-package'); const symlinked = { - 'stream-chat-react-native': PATH.resolve(packageDirPath, 'native-package'), + 'stream-chat-react-native': nativePackageDirPath, 'stream-chat-react-native-core': packageDirPath, }; // find what all modules need to be unique for the app (mainly react and react-native) +// note: we filter the symlinked modules as they are already unique +// and as they dont follow the workspace pattern the auto-generated path to the module is incorrect const dependencyPackageNames = Object.keys(require('./package.json').dependencies); const uniqueModules = dependencyPackageNames.map((packageName) => { - // if the module is symlinked, use the symlinked path that we know if (symlinked[packageName]) { const modulePath = symlinked[packageName]; const escapedPackageName = PATH.normalize(packageName).replace(/\\/g, '\\\\'); @@ -59,9 +61,9 @@ const extraNodeModules = uniqueModules.reduce((acc, item) => { config.resolver.blockList = exclusionList(blockList); config.resolver.extraNodeModules = extraNodeModules; -extraNodeModules['stream-chat'] = '/Users/santhoshvaiyapuri/Projects/stream-chat-js'; +config.resolver.nodeModulesPaths = [PATH.resolve(__dirname, 'node_modules')]; // add the package dir for metro to access the package folder -config.watchFolders = [packageDirPath, '/Users/santhoshvaiyapuri/Projects/stream-chat-js']; +config.watchFolders = [packageDirPath]; module.exports = config; From 4f64eea09ed6cdb64260b5845dc71c8637f0b345 Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Tue, 2 Apr 2024 14:26:46 +0200 Subject: [PATCH 3/7] staore message map later --- package/src/store/apis/upsertMessages.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package/src/store/apis/upsertMessages.ts b/package/src/store/apis/upsertMessages.ts index 482fe940f6..79a3b1347f 100644 --- a/package/src/store/apis/upsertMessages.ts +++ b/package/src/store/apis/upsertMessages.ts @@ -35,7 +35,6 @@ export const upsertMessages = ({ messages.forEach((message) => { let storableMessage = mapMessageToStorable(message); - storableMessages.push(storableMessage); const messageQuery = createUpsertQuery('messages', storableMessage); const userQueries: PreparedQueries[] = []; if (message.user) { @@ -67,12 +66,14 @@ export const upsertMessages = ({ // channel was not present and a new message came so we must re-add the channel first const skipped = [messageQuery, ...userQueries, ...reactionsQueries]; storableMessage = Object.assign({ skipped: true }, storableMessage); + storableMessages.push(storableMessage); const previousQueries = nonExistentChannelQueriesMap.get(message.cid) ?? []; nonExistentChannelQueriesMap.set(message.cid, previousQueries.concat(skipped)); return; } } + storableMessages.push(storableMessage); messagesToUpsert.push(messageQuery); usersToUpsert.push(...userQueries); reactionsToUpsert.push(...reactionsQueries); From a40650fe43ebe9aa2e14dbdc5942fc37745d471a Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Tue, 2 Apr 2024 16:07:39 +0200 Subject: [PATCH 4/7] safeguard all events --- .../Chat/hooks/handleEventToSyncDB.ts | 163 ++++++++++-------- package/src/store/apis/upsertMessages.ts | 73 ++------ 2 files changed, 107 insertions(+), 129 deletions(-) diff --git a/package/src/components/Chat/hooks/handleEventToSyncDB.ts b/package/src/components/Chat/hooks/handleEventToSyncDB.ts index 13e3ca964c..ba52f4449b 100644 --- a/package/src/components/Chat/hooks/handleEventToSyncDB.ts +++ b/package/src/components/Chat/hooks/handleEventToSyncDB.ts @@ -1,4 +1,3 @@ -import { PreparedQueries } from 'src/store/types'; import { DefaultStreamChatGenerics } from 'src/types/types'; import type { Event, StreamChat } from 'stream-chat'; @@ -12,6 +11,9 @@ import { upsertChannels } from '../../../store/apis/upsertChannels'; import { upsertMembers } from '../../../store/apis/upsertMembers'; import { upsertMessages } from '../../../store/apis/upsertMessages'; import { upsertReads } from '../../../store/apis/upsertReads'; +import { QuickSqliteClient } from '../../../store/QuickSqliteClient'; +import { createSelectQuery } from '../../../store/sqlite-utils/createSelectQuery'; +import { PreparedQueries } from '../../../store/types'; export const handleEventToSyncDB = < StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, @@ -22,63 +24,76 @@ export const handleEventToSyncDB = < ) => { const { type } = event; - if (type === 'message.read') { - if (event.user?.id && event.cid) { - return upsertReads({ - cid: event.cid, - flush, - reads: [ - { - last_read: event.received_at as string, - unread_messages: 0, - user: event.user, - }, - ], - }); - } - } - - if (type === 'message.new') { - if (event.message && (!event.message.parent_id || event.message.show_in_channel)) { + // This function is used to guard the queries that require channel to be present in the db first + // If channel is not present in the db, we first fetch the channel data from the channel object + // and then add the queries with a channel create query first + const queriesWithChannelGuard = (queries: PreparedQueries[]) => { + const cid = event.cid || event.channel?.cid; + if (!cid) return queries; + const channels = QuickSqliteClient.executeSql.apply( + null, + createSelectQuery('channels', ['cid'], { + cid, + }), + ); + // a channel is not present in the db, we first fetch the channel data from the channel object. + // this can happen for example when a message.new event is received for a channel that is not in the db due to a channel being hidden. + if (channels.length === 0) { const channel = event.channel_type && event.channel_id ? client.channel(event.channel_type, event.channel_id) : undefined; - const skippedMessageQueries: PreparedQueries[] = []; - const queries = upsertMessages({ - flush, - messages: [event.message], - onNonExistentChannel: (map) => { - map.forEach((skippedQueries) => { - skippedMessageQueries.push(...skippedQueries); - }); - }, - }); - // if a channel was hidden and a new message was received it would be missing from the database - if (skippedMessageQueries.length) { - if (channel && channel.initialized && !channel.disconnected) { - const channelQuery = upsertChannelDataFromChannel({ - channel, - flush, - }); - if (channelQuery) { - return [...channelQuery, ...queries, ...skippedMessageQueries]; - } else { - console.warn( - 'Couldnt create channel queries on "message.new" for an initialized channel that is not in DB, skipping message', - { event }, - ); - return []; - } + if (channel && channel.initialized && !channel.disconnected) { + const channelQuery = upsertChannelDataFromChannel({ + channel, + flush, + }); + if (channelQuery) { + return [...channelQuery, ...queries]; } else { console.warn( - 'Received "message.new" event for a non initialized channel that is not in DB, skipping message', + 'Couldnt create channel queries on event for an initialized channel that is not in DB, skipping event', { event }, ); return []; } + } else { + console.warn( + 'Received "message.new" event for a non initialized channel that is not in DB, skipping event', + { event }, + ); + return []; } - return queries; + } + return queries; + }; + + if (type === 'message.read') { + if (event.user?.id && event.cid) { + return queriesWithChannelGuard( + upsertReads({ + cid: event.cid, + flush, + reads: [ + { + last_read: event.received_at as string, + unread_messages: 0, + user: event.user, + }, + ], + }), + ); + } + } + + if (type === 'message.new') { + if (event.message && (!event.message.parent_id || event.message.show_in_channel)) { + return queriesWithChannelGuard( + upsertMessages({ + flush, + messages: [event.message], + }), + ); } } @@ -86,10 +101,12 @@ export const handleEventToSyncDB = < if (event.message && !event.message.parent_id) { // Update only if it exists, otherwise event could be related // to a message which is not in database. - return updateMessage({ - flush, - message: event.message, - }); + return queriesWithChannelGuard( + updateMessage({ + flush, + message: event.message, + }), + ); } } @@ -97,10 +114,12 @@ export const handleEventToSyncDB = < if (event.message && event.reaction) { // We update the entire message to make sure we also update // reaction_counts. - return updateMessage({ - flush, - message: event.message, - }); + return queriesWithChannelGuard( + updateMessage({ + flush, + message: event.message, + }), + ); } } @@ -109,10 +128,12 @@ export const handleEventToSyncDB = < // Here we are relying on the fact message.latest_reactions always includes // the new reaction. So we first delete all the existing reactions and populate // the reactions table with message.latest_reactions - return updateMessage({ - flush, - message: event.message, - }); + return queriesWithChannelGuard( + updateMessage({ + flush, + message: event.message, + }), + ); } } @@ -164,21 +185,25 @@ export const handleEventToSyncDB = < if (type === 'member.added' || type === 'member.updated') { if (event.member && event.cid) { - return upsertMembers({ - cid: event.cid, - flush, - members: [event.member], - }); + return queriesWithChannelGuard( + upsertMembers({ + cid: event.cid, + flush, + members: [event.member], + }), + ); } } if (type === 'member.removed') { if (event.member && event.cid) { - return deleteMember({ - cid: event.cid, - flush, - member: event.member, - }); + return queriesWithChannelGuard( + deleteMember({ + cid: event.cid, + flush, + member: event.member, + }), + ); } } diff --git a/package/src/store/apis/upsertMessages.ts b/package/src/store/apis/upsertMessages.ts index 79a3b1347f..0077131067 100644 --- a/package/src/store/apis/upsertMessages.ts +++ b/package/src/store/apis/upsertMessages.ts @@ -4,81 +4,40 @@ import { mapMessageToStorable } from '../mappers/mapMessageToStorable'; import { mapReactionToStorable } from '../mappers/mapReactionToStorable'; import { mapUserToStorable } from '../mappers/mapUserToStorable'; import { QuickSqliteClient } from '../QuickSqliteClient'; -import { createSelectQuery } from '../sqlite-utils/createSelectQuery'; import { createUpsertQuery } from '../sqlite-utils/createUpsertQuery'; -import type { PreparedQueries } from '../types'; export const upsertMessages = ({ flush = true, messages, - onNonExistentChannel, }: { messages: MessageResponse[]; flush?: boolean; - /** - * Callback to be called when a message is received for a non-existent channel. - * This can happen when a channel is hidden and a new message comes in. - * In this case, the channel should be re-added to the database and then the message should be added. - * **Note**: This callback is to be called with a map of channel cid to the queries that should be executed for that channel - */ - onNonExistentChannel?: (map: Map) => void; }) => { const storableMessages: Array> = []; const storableUsers: Array> = []; const storableReactions: Array> = []; - const usersToUpsert: PreparedQueries[] = []; - const messagesToUpsert: PreparedQueries[] = []; - const reactionsToUpsert: PreparedQueries[] = []; - - const nonExistentChannelQueriesMap = new Map(); - - messages.forEach((message) => { - let storableMessage = mapMessageToStorable(message); - const messageQuery = createUpsertQuery('messages', storableMessage); - const userQueries: PreparedQueries[] = []; + messages?.forEach((message: MessageResponse) => { + storableMessages.push(mapMessageToStorable(message)); if (message.user) { - const userStorable = mapUserToStorable(message.user); - storableUsers.push(userStorable); - userQueries.push(createUpsertQuery('users', userStorable)); + storableUsers.push(mapUserToStorable(message.user)); } - const reactionsQueries: PreparedQueries[] = []; [...(message.latest_reactions || []), ...(message.own_reactions || [])].forEach((r) => { if (r.user) { - const reactionUserStorable = mapUserToStorable(r.user); - storableUsers.push(reactionUserStorable); - userQueries.push(createUpsertQuery('users', reactionUserStorable)); + storableUsers.push(mapUserToStorable(r.user)); } - const reactionStorable = mapReactionToStorable(r); - storableReactions.push(reactionStorable); - reactionsQueries.push(createUpsertQuery('reactions', reactionStorable)); + storableReactions.push(mapReactionToStorable(r)); }); - - if (message.cid) { - const channels = QuickSqliteClient.executeSql.apply( - null, - createSelectQuery('channels', ['cid'], { - cid: message.cid, - }), - ); - - if (channels.length === 0) { - // channel was not present and a new message came so we must re-add the channel first - const skipped = [messageQuery, ...userQueries, ...reactionsQueries]; - storableMessage = Object.assign({ skipped: true }, storableMessage); - storableMessages.push(storableMessage); - const previousQueries = nonExistentChannelQueriesMap.get(message.cid) ?? []; - nonExistentChannelQueriesMap.set(message.cid, previousQueries.concat(skipped)); - return; - } - } - - storableMessages.push(storableMessage); - messagesToUpsert.push(messageQuery); - usersToUpsert.push(...userQueries); - reactionsToUpsert.push(...reactionsQueries); }); + const finalQueries = [ + ...storableMessages.map((storableMessage) => createUpsertQuery('messages', storableMessage)), + ...storableUsers.map((storableUser) => createUpsertQuery('users', storableUser)), + ...storableReactions.map((storableReaction) => + createUpsertQuery('reactions', storableReaction), + ), + ]; + QuickSqliteClient.logger?.('info', 'upsertMessages', { flush, messages: storableMessages, @@ -86,12 +45,6 @@ export const upsertMessages = ({ users: storableUsers, }); - // note: the order is important here.. messages, users should be inserted first and then reactions. - // As reactions have foreign key constraints to messages and users. - const finalQueries = [...messagesToUpsert, ...usersToUpsert, ...reactionsToUpsert]; - - onNonExistentChannel?.(nonExistentChannelQueriesMap); - if (flush) { QuickSqliteClient.executeSqlBatch(finalQueries); } From d1ff3a203801d591d7a09021945986b241229aad Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Tue, 2 Apr 2024 16:43:55 +0200 Subject: [PATCH 5/7] better logging --- package/src/components/Chat/hooks/handleEventToSyncDB.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/src/components/Chat/hooks/handleEventToSyncDB.ts b/package/src/components/Chat/hooks/handleEventToSyncDB.ts index ba52f4449b..ca3dda237a 100644 --- a/package/src/components/Chat/hooks/handleEventToSyncDB.ts +++ b/package/src/components/Chat/hooks/handleEventToSyncDB.ts @@ -52,14 +52,14 @@ export const handleEventToSyncDB = < return [...channelQuery, ...queries]; } else { console.warn( - 'Couldnt create channel queries on event for an initialized channel that is not in DB, skipping event', + `Couldnt create channel queries on ${type} event for an initialized channel that is not in DB, skipping event`, { event }, ); return []; } } else { console.warn( - 'Received "message.new" event for a non initialized channel that is not in DB, skipping event', + `Received ${type} event for a non initialized channel that is not in DB, skipping event`, { event }, ); return []; From 2be4222ecdd78dd3f2c53df119bbe8a0e3d9df4f Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Tue, 2 Apr 2024 18:01:07 +0200 Subject: [PATCH 6/7] override flush --- .../Chat/hooks/handleEventToSyncDB.ts | 82 +++++++++++-------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/package/src/components/Chat/hooks/handleEventToSyncDB.ts b/package/src/components/Chat/hooks/handleEventToSyncDB.ts index ca3dda237a..a285a81877 100644 --- a/package/src/components/Chat/hooks/handleEventToSyncDB.ts +++ b/package/src/components/Chat/hooks/handleEventToSyncDB.ts @@ -27,9 +27,11 @@ export const handleEventToSyncDB = < // This function is used to guard the queries that require channel to be present in the db first // If channel is not present in the db, we first fetch the channel data from the channel object // and then add the queries with a channel create query first - const queriesWithChannelGuard = (queries: PreparedQueries[]) => { + const queriesWithChannelGuard = ( + createQueries: (flushOverride?: boolean) => PreparedQueries[], + ) => { const cid = event.cid || event.channel?.cid; - if (!cid) return queries; + if (!cid) return createQueries(flush); const channels = QuickSqliteClient.executeSql.apply( null, createSelectQuery('channels', ['cid'], { @@ -49,7 +51,7 @@ export const handleEventToSyncDB = < flush, }); if (channelQuery) { - return [...channelQuery, ...queries]; + return [...channelQuery, ...createQueries(false)]; } else { console.warn( `Couldnt create channel queries on ${type} event for an initialized channel that is not in DB, skipping event`, @@ -65,20 +67,22 @@ export const handleEventToSyncDB = < return []; } } - return queries; + return createQueries(flush); }; if (type === 'message.read') { - if (event.user?.id && event.cid) { - return queriesWithChannelGuard( + const cid = event.cid; + const user = event.user; + if (user?.id && cid) { + return queriesWithChannelGuard((flushOverride) => upsertReads({ - cid: event.cid, - flush, + cid, + flush: flushOverride, reads: [ { last_read: event.received_at as string, unread_messages: 0, - user: event.user, + user, }, ], }), @@ -87,51 +91,55 @@ export const handleEventToSyncDB = < } if (type === 'message.new') { - if (event.message && (!event.message.parent_id || event.message.show_in_channel)) { - return queriesWithChannelGuard( + const message = event.message; + if (message && (!message.parent_id || message.show_in_channel)) { + return queriesWithChannelGuard((flushOverride) => upsertMessages({ - flush, - messages: [event.message], + flush: flushOverride, + messages: [message], }), ); } } if (type === 'message.updated' || type === 'message.deleted') { - if (event.message && !event.message.parent_id) { + const message = event.message; + if (message && !message.parent_id) { // Update only if it exists, otherwise event could be related // to a message which is not in database. - return queriesWithChannelGuard( + return queriesWithChannelGuard((flushOverride) => updateMessage({ - flush, - message: event.message, + flush: flushOverride, + message, }), ); } } if (type === 'reaction.updated') { - if (event.message && event.reaction) { + const message = event.message; + if (message && event.reaction) { // We update the entire message to make sure we also update // reaction_counts. - return queriesWithChannelGuard( + return queriesWithChannelGuard((flushOverride) => updateMessage({ - flush, - message: event.message, + flush: flushOverride, + message, }), ); } } if (type === 'reaction.new' || type === 'reaction.deleted') { - if (event.message && !event.message.parent_id) { + const message = event.message; + if (message && !message.parent_id) { // Here we are relying on the fact message.latest_reactions always includes // the new reaction. So we first delete all the existing reactions and populate // the reactions table with message.latest_reactions - return queriesWithChannelGuard( + return queriesWithChannelGuard((flushOverride) => updateMessage({ - flush, - message: event.message, + flush: flushOverride, + message, }), ); } @@ -184,24 +192,28 @@ export const handleEventToSyncDB = < } if (type === 'member.added' || type === 'member.updated') { - if (event.member && event.cid) { - return queriesWithChannelGuard( + const member = event.member; + const cid = event.cid; + if (member && cid) { + return queriesWithChannelGuard((flushOverride) => upsertMembers({ - cid: event.cid, - flush, - members: [event.member], + cid, + flush: flushOverride, + members: [member], }), ); } } if (type === 'member.removed') { - if (event.member && event.cid) { - return queriesWithChannelGuard( + const member = event.member; + const cid = event.cid; + if (member && cid) { + return queriesWithChannelGuard((flushOverride) => deleteMember({ - cid: event.cid, - flush, - member: event.member, + cid, + flush: flushOverride, + member, }), ); } From fb6f631570d1d847feb911146fb53d42c5f3ed74 Mon Sep 17 00:00:00 2001 From: Khushal Agarwal Date: Tue, 2 Apr 2024 22:18:03 +0530 Subject: [PATCH 7/7] fix: flush around handleEventToSyncDB --- package/src/components/Chat/hooks/handleEventToSyncDB.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/package/src/components/Chat/hooks/handleEventToSyncDB.ts b/package/src/components/Chat/hooks/handleEventToSyncDB.ts index a285a81877..2fa8420724 100644 --- a/package/src/components/Chat/hooks/handleEventToSyncDB.ts +++ b/package/src/components/Chat/hooks/handleEventToSyncDB.ts @@ -31,6 +31,7 @@ export const handleEventToSyncDB = < createQueries: (flushOverride?: boolean) => PreparedQueries[], ) => { const cid = event.cid || event.channel?.cid; + if (!cid) return createQueries(flush); const channels = QuickSqliteClient.executeSql.apply( null, @@ -51,7 +52,11 @@ export const handleEventToSyncDB = < flush, }); if (channelQuery) { - return [...channelQuery, ...createQueries(false)]; + const newQueries = [...channelQuery, ...createQueries(false)]; + if (flush !== false) { + QuickSqliteClient.executeSqlBatch(newQueries); + } + return newQueries; } else { console.warn( `Couldnt create channel queries on ${type} event for an initialized channel that is not in DB, skipping event`,