Skip to content

Commit 11c3307

Browse files
authored
feat: add reminders implementation (#3141)
* feat: add reminders implementation * feat: add reminders implementation * fix: how data is fetched * fix: remove console.log * fix: improve icons and usage * fix: optimize sample app code * fix: make reminder list better * fix: add export for message composer api context * fix: reminders loading when the tab is changed * feat: add useQueryReminders * feat: add useQueryReminders improvements * feat: add useQueryReminders
1 parent ed4d6ae commit 11c3307

34 files changed

+1264
-196
lines changed

examples/SampleApp/App.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import {
1010
OverlayProvider,
1111
setupCommandUIMiddlewares,
1212
SqliteClient,
13+
Streami18n,
1314
ThemeProvider,
1415
useOverlayContext,
16+
enTranslations,
1517
} from 'stream-chat-react-native';
1618
import { getMessaging } from '@react-native-firebase/messaging';
1719
import notifee, { EventType } from '@notifee/react-native';
@@ -201,16 +203,28 @@ const DrawerNavigatorWrapper: React.FC<{
201203
chatClient: StreamChat;
202204
}> = ({ chatClient }) => {
203205
const streamChatTheme = useStreamChatTheme();
206+
const streami18n = new Streami18n();
207+
208+
streami18n.registerTranslation('en', {
209+
...enTranslations,
210+
'Due since {{ dueSince }}': 'Due since {{ dueSince }}',
211+
'Due {{ timeLeft }}': 'Due {{ timeLeft }}',
212+
'duration/Message reminder': '{{ milliseconds | durationFormatter(withSuffix: true) }}',
213+
'duration/Remind Me': '{{ milliseconds | durationFormatter(withSuffix: true) }}',
214+
'timestamp/Remind me': '{{ milliseconds | durationFormatter(withSuffix: true) }}',
215+
'timestamp/ReminderNotification': '{{ timestamp | timestampFormatter(calendar: true) }}',
216+
});
204217

205218
return (
206219
<GestureHandlerRootView style={{ flex: 1 }}>
207-
<OverlayProvider value={{ style: streamChatTheme }}>
220+
<OverlayProvider value={{ style: streamChatTheme }} i18nInstance={streami18n}>
208221
<Chat
209222
client={chatClient}
210223
enableOfflineSupport
211224
// @ts-expect-error - the `ImageComponent` prop is generic, meaning we can expect an error
212225
ImageComponent={FastImage}
213226
isMessageAIGenerated={isMessageAIGenerated}
227+
i18nInstance={streami18n}
214228
>
215229
<AppOverlayProvider>
216230
<UserSearchProvider>

examples/SampleApp/ios/Podfile.lock

Lines changed: 71 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -2425,7 +2425,7 @@ PODS:
24252425
- libwebp (~> 1.0)
24262426
- SDWebImage/Core (~> 5.10)
24272427
- SocketRocket (0.7.1)
2428-
- stream-chat-react-native (7.2.0):
2428+
- stream-chat-react-native (8.0.0):
24292429
- DoubleConversion
24302430
- glog
24312431
- hermes-engine
@@ -2784,7 +2784,7 @@ SPEC CHECKSUMS:
27842784
hermes-engine: 94ed01537bdeccaab1adbf94b040d115d6fa1a7f
27852785
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
27862786
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
2787-
op-sqlite: 6a5255f36253697406618ceba5212d6572012a9d
2787+
op-sqlite: d7e3053a9518cfb8f763f09a899df7403a9d4eb9
27882788
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
27892789
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
27902790
RCT-Folly: 36fe2295e44b10d831836cc0d1daec5f8abcf809
@@ -2794,87 +2794,87 @@ SPEC CHECKSUMS:
27942794
React: 114ee161feb204412580928b743e6716aebac987
27952795
React-callinvoker: d175cf3640a993f6cd960044a7657543157f0ba9
27962796
React-Codegen: 4b8b4817cea7a54b83851d4c1f91f79aa73de30a
2797-
React-Core: e84d47ce3df8dde567f5b9668f6103f8e562d72a
2798-
React-CoreModules: ce8e588dca54cd790e2d424d0e678924e62b41b1
2799-
React-cxxreact: 2c10954abacc35e876adf46e25ebfd74a0106521
2797+
React-Core: cd487c9eeb125c902242bcc76ced12e14adf4ea4
2798+
React-CoreModules: 202df4f342e5c2893d5d1899b2856879a90d4bf1
2799+
React-cxxreact: 72be57cebb9976199e6130ec6f9d511c6ae3b310
28002800
React-debug: d2042a489905b47cb1a987bb9eacc81a68e89587
2801-
React-defaultsnativemodule: caeee1aa6a9bc60c27c3678e96055ada96c37e22
2802-
React-domnativemodule: 0b7d770c5d3da2bc194091bf9fc68416e9b88670
2803-
React-Fabric: b8118ef9817c9ccadd7a878d66ae2faf02e59e8a
2804-
React-FabricComponents: 7da55420b42b55e06b3b9b6a1aad3c7e300885f2
2805-
React-FabricImage: 2515fa043a9e652b97947231c0a79bfa778cdd37
2801+
React-defaultsnativemodule: db406c1b44ed9fd30ef9c86a2d0529c4ba8ed8c9
2802+
React-domnativemodule: f71e27c03fbdeaa5a318576beb99b997267dfa31
2803+
React-Fabric: 84377d3efd7f7fb832eb48674c64a4f1b75bc68f
2804+
React-FabricComponents: e99187e0dcd333cba72d7ac931c06dea66c60849
2805+
React-FabricImage: 0cd0b4b1ad571648c9aa74060075f425bb1a0baf
28062806
React-featureflags: 8d2d86c7d0812ab474cb326b854c987f44633c72
2807-
React-featureflagsnativemodule: 8a11970c8713104bf7327ca8aa314be250e6d73f
2808-
React-graphics: 6581de50c43af3a613ae197df886c86a014ed165
2809-
React-hermes: 2a9fb8df8a1be5e5b065c91d7b0fad072554055e
2810-
React-idlecallbacksnativemodule: 66d6d2e3c85d19067763d6764cd3159a39b89e60
2811-
React-ImageManager: 9ab8bc29c0a743ec51f109925f6d961a57a8293e
2812-
React-jserrorhandler: f51e203fc68469a86d49fc4dbc99254aca68d122
2813-
React-jsi: 606a42a46f9a7299c1757686c6856eca8346754b
2814-
React-jsiexecutor: 762ee9c7962597d2f168afee00a0cac7573e6e48
2815-
React-jsinspector: 8f3969250c71e78f6a1ef9c6109f12cf723bf69c
2816-
React-jsinspectortracing: 1c7188a624cb28de6b13d69492161ad2265327b6
2817-
React-jsitooling: 92450f21d15de625ae6f35cd1540164e7ed61e91
2818-
React-jsitracing: 4185f91ab619c7005a28edcb8b35b5e741460a16
2819-
React-logger: 44e070aefe084568c02b544b9d7d436703be1a47
2820-
React-Mapbuffer: ed1f528261013e46790379cc6727b8005e169a67
2821-
React-microtasksnativemodule: da563fd4cb7ecc23f67b5f15b92dfe94a9791e2d
2822-
react-native-blob-util: 68f288ed8fd64a191a4d83ffa5a1dd375dc786d2
2823-
react-native-cameraroll: 71f93f6a1676f322b7913462d3613e70bd2dc675
2824-
react-native-document-picker: 06ad77eb1650e628def114ff74f9f767f1888edd
2825-
react-native-image-picker: 6ccf4bb98d38facc22db35fc2ee59a574fca4d16
2826-
react-native-netinfo: f0a9899081c185db1de5bb2fdc1c88c202a059ac
2827-
react-native-safe-area-context: f2e97b088ae5f3078f1ad03f51a44bd84f479053
2828-
react-native-video: 1e0df051b1d5c4bfc8532d8c8bd68428de4a4081
2829-
React-NativeModulesApple: c761e39d8bb17a074530cb637ec6fe4460fac3af
2807+
React-featureflagsnativemodule: bd54ca89ed64d31a61a17fa103a991fcfee70821
2808+
React-graphics: bad51b93c457bb7517540bc9d112e8218773dfe1
2809+
React-hermes: a40e47b18c31efe4baa7942357e2327ddab63918
2810+
React-idlecallbacksnativemodule: 75f7e8b32e74c78124297ba73c1b3e6ac08fd39b
2811+
React-ImageManager: 529243f1b6094af8a8dd58aef885f9afb1c73d8c
2812+
React-jserrorhandler: bbd5fd904735fac723afc84d295fb17558956698
2813+
React-jsi: ae02c9d6d68dbed80a9fde8f6d6198035ca154ce
2814+
React-jsiexecutor: 8c266057f23430685a2d928703e77eda80e1742e
2815+
React-jsinspector: 1971f190bf2f1ee72f9e1e0e6f56f2f42024fdb2
2816+
React-jsinspectortracing: 56c50684abc51cbf959a6bbba4ec20fe02f20259
2817+
React-jsitooling: 0a516b47b6b1c427751ae2a5537c07dd5af60470
2818+
React-jsitracing: 6aa49f8c5a07eb1c042a924419ecd5d39c2e2098
2819+
React-logger: 514fac028fee60c84591f951c7c04ba1c5023334
2820+
React-Mapbuffer: 02f95b72f631228befdfb7e71ad8e2e12b6dae0d
2821+
React-microtasksnativemodule: 953b922f9473c100dd153cc51739e0d210bfd617
2822+
react-native-blob-util: e032f2a9d5779aa94934139a60fe5ed6c5071328
2823+
react-native-cameraroll: 23d28040c32ca8b20661e0c41b56ab041779244b
2824+
react-native-document-picker: 1f8a568fcd43ed5ad9e53307d487d1de21c340a4
2825+
react-native-image-picker: f6ece66f251f4a17aab08f5add7be6eb9e7f5356
2826+
react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187
2827+
react-native-safe-area-context: 8cf8caf5fc0f5a5d3581b650579af9c85c16be04
2828+
react-native-video: 6a236aa5c7619d4ba9b07ddf965a0b62b1702da3
2829+
React-NativeModulesApple: 579cac2962d42ffee0d8aae48752f30c4e498bb9
28302830
React-oscompat: f26aa2a4adc84c34212ab12c07988fe19e9cf16a
2831-
React-perflogger: fb196ad3fa3263afc55f773a10c2741517a27f7c
2832-
React-performancetimeline: 0d549cebbe759dcbe8efde0fd9cf8922e5788bb8
2831+
React-perflogger: e15a0d43d1928e1c82f4f0b7fc05f7e9bccfede8
2832+
React-performancetimeline: 117ad5127882ec6a459435d1397d713e35760be7
28332833
React-RCTActionSheet: c89c8b9b7c3ef87cb6a67e20f5eaea271f4b5f67
2834-
React-RCTAnimation: 8cff4eda84c7e70c674c50763c724c660ae7e56c
2835-
React-RCTAppDelegate: 12b784fb29e25a606aaf869d11efb4ae97bb81b3
2836-
React-RCTBlob: 105ead00cc3cb7ed4180481cec7cb68829c0c16b
2837-
React-RCTFabric: 5584605ac217c7e66d69bf5b72260e1e64ed8a9a
2838-
React-RCTFBReactNativeSpec: c3bfd143e072358d0d8b7efc97fb6a09e77f8f46
2839-
React-RCTImage: ef3831114706dbb9ccab839abe804edef1e1faab
2840-
React-RCTLinking: eadceef820a11dd2bc7b4b569406eacc637c7f82
2841-
React-RCTNetwork: 9e1323f0cdfaf0f561d8a6667363cc8deadf41e8
2842-
React-RCTRuntime: b531dd9beffda4bf014edcbb416eda1411392ac5
2843-
React-RCTSettings: 482bb7da0e94823cd1a36edd408c85abb2d2e42a
2844-
React-RCTText: d103fac423b92be0ed295767ea4c2ecc1f4389fd
2845-
React-RCTVibration: 816504f335105f0682467823400436e18ec98b7b
2834+
React-RCTAnimation: e00af558ccb5fedd380ae32329be4c38e92e9b90
2835+
React-RCTAppDelegate: 10d98d4867643322fa4fcd04548359ac88c74656
2836+
React-RCTBlob: ef645bccf9c33d3b4391794983744da897474dfb
2837+
React-RCTFabric: bb4d7c817e3228d6e55052a420e6ec2892a6a702
2838+
React-RCTFBReactNativeSpec: e0942c2c7efa10303c63e287c1c1788aeb6d99ef
2839+
React-RCTImage: 0e3669a0bda8995874736d0f8f12c21d522df3c4
2840+
React-RCTLinking: bd81ec3d1b6686a7c58bc8ed8b7a1f05ff2b3f8b
2841+
React-RCTNetwork: 20b8044841a043b80e7027e1bc4049ffa552d1fa
2842+
React-RCTRuntime: 13b115119851e0bb55cc7e3f347d763b7c6d9e40
2843+
React-RCTSettings: fa1d3e6c302e9980b5670315e2ccc998255ce32a
2844+
React-RCTText: 71f01a9261c015b76702e9d7a4153c9ca45f2341
2845+
React-RCTVibration: 0e05fa4647ec1391c409fcc1cbd7cdb4894d80ef
28462846
React-rendererconsistency: fd094e5d05a8969e556334496610e66886ab10cb
2847-
React-renderercss: 7d0c1b296a1712ee17a776eb60589e95d4d6ee2a
2848-
React-rendererdebug: 6ede134de6beebd53b4917d666142c362b7b6632
2847+
React-renderercss: f40da71385a7950b778c185e77a3979853f9827c
2848+
React-rendererdebug: c189ec79cb8a585c973c404f3341c4a8627e3a98
28492849
React-rncore: 9f8b4d93dba987f5209346404ef740c6b6105d73
2850-
React-RuntimeApple: 53437a180d53b77b14817f41efbdc547a071886d
2851-
React-RuntimeCore: ab6eab30b91c75b5f07c980dae9cf7951600a33f
2850+
React-RuntimeApple: 4f2fd7353b517ec268f7fb076976354c1de5d086
2851+
React-RuntimeCore: 134e3e2982f4afceb751c0b3fdf6c5f3e1768def
28522852
React-runtimeexecutor: 4e7bc0119ff38f80df43d109ef9508497cac1eee
2853-
React-RuntimeHermes: 16fa50d6afea4bfc8bcc8905b7146b28f838fd80
2854-
React-runtimescheduler: e44b4ba76d439272d58bd95c0e0750e428b2a6fc
2853+
React-RuntimeHermes: 0608c43ea0a6e746e964678b343dc439c3e198f8
2854+
React-runtimescheduler: b9d753b1d34c274261ed14fa35151b38232a7f13
28552855
React-timing: 220bf771493c44a15fdbdedad2cf0a2e3c41edcd
2856-
React-utils: d6819ad2da189ca066301cf4e41aaad51cef4d16
2857-
ReactAppDependencyProvider: 552391af67c115d8ee20f9359711e7821145cd96
2858-
ReactCodegen: 5a3b071aec923e67260550b3f65329f844d691fa
2859-
ReactCommon: 1dce2374d9dc2cdf634244f1584513b606512750
2860-
RNAudioRecorderPlayer: 8a1c6ee5080aa83c3f2ccc75d1a43b2ce82b366d
2861-
RNCAsyncStorage: faf86846c58994b6b8aab074a4914eb1796c2a9f
2862-
RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8
2863-
RNFBApp: df5caad9f64b6bc87f8a0b110e6bc411fb00a12b
2864-
RNFBMessaging: 6586f18ab3411aeb3349088c19fe54283d39e529
2865-
RNGestureHandler: 2fa49aef8b58d35bcc61abe06ffecc4bcc5268a4
2866-
RNNotifee: 4a6ee5c7deaf00e005050052d73ee6315dff7ec9
2867-
RNReactNativeHapticFeedback: 85c0a6ff490d52f5e8073040296fefe5945ebbfa
2868-
RNReanimated: 2b314b18e28adf1b300f606eb0f205a1a6a643e1
2869-
RNScreens: d9d5d8a2a484bb4446968bfa00db991f1117db44
2870-
RNShare: 082dae96ef6cabef31d3e6b016d388e71ff0e0d7
2871-
RNSVG: 1fa61293cb54a97d8ee3b182d2fb60443edcdf69
2856+
React-utils: a439ca94e40a809a832ad8329698df0146eaccc8
2857+
ReactAppDependencyProvider: e6d0c3c3cc9862a3ccef0c252835cd7ccb96313d
2858+
ReactCodegen: 338cfa0d67a821a9caec4888f6f83458dcc69771
2859+
ReactCommon: 4dbe3f8218c0b6589e982081f503725057457d2b
2860+
RNAudioRecorderPlayer: 5d5aac7a0e0f159861736ef2b433770342da7197
2861+
RNCAsyncStorage: c1dca434eda758348a549efb46822b4091d76804
2862+
RNFastImage: 462a183c4b0b6b26fdfd639e1ed6ba37536c3b87
2863+
RNFBApp: db9c2e6d36fe579ab19b82c0a4a417ff7569db7e
2864+
RNFBMessaging: de62448d205095171915d622ed5fb45c2be5e075
2865+
RNGestureHandler: 9aefee4dffdfa4a32d401a771b8a513ecb507d08
2866+
RNNotifee: 5e3b271e8ea7456a36eec994085543c9adca9168
2867+
RNReactNativeHapticFeedback: 1597ab4e15cabb38f0f0c62faa17a5e295430170
2868+
RNReanimated: 0df48dab58b5ee37fd1d1341caba79f59a1187ec
2869+
RNScreens: 90b905d545a5ebbe976985702b8a39e3475727b2
2870+
RNShare: 6300b941668273d502ecee9122cade0d5ea966bd
2871+
RNSVG: 45e3c3210465e75ab6374c9f746179e75d76ce48
28722872
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
28732873
SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d
28742874
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
2875-
stream-chat-react-native: 2de2866392bfecadf005ec5f0b1f882b613b89ba
2875+
stream-chat-react-native: ab733c7105d4e46deccbf6e1c0267b8c230a25ca
28762876
Yoga: b2eaabf17044cd4273a661b14eb83f9fd2c90491
28772877

28782878
PODFILE CHECKSUM: 4f662370295f8f9cee909f1a4c59a614999a209d
28792879

2880-
COCOAPODS: 1.14.3
2880+
COCOAPODS: 1.16.2

examples/SampleApp/src/components/BottomTabs.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { MentionsTab } from '../icons/MentionsTab';
1212
import type { BottomTabBarProps } from '@react-navigation/bottom-tabs';
1313
import type { Route } from '@react-navigation/native';
1414
import { DraftsTab } from '../icons/DraftsTab';
15+
import { RemindersTab } from '../icons/ReminderTab';
1516

1617
const styles = StyleSheet.create({
1718
notification: {
@@ -64,6 +65,13 @@ const getTab = (key: string) => {
6465
iconActive: <MentionsTab active />,
6566
title: 'Mentions',
6667
};
68+
case 'RemindersScreen':
69+
return {
70+
icon: <RemindersTab />,
71+
iconActive: <RemindersTab active />,
72+
title: 'Reminders',
73+
};
74+
// Add more cases for other tabs as needed
6775
default:
6876
return null;
6977
}

examples/SampleApp/src/components/DraftsList.tsx

Lines changed: 3 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { FlatList, Pressable, StyleSheet, Text, View } from 'react-native';
22
import { DraftsIcon } from '../icons/DraftIcon';
33
import {
4-
FileTypes,
54
MessagePreview,
6-
TranslationContextValue,
75
useChatContext,
86
useStateStore,
97
useTheme,
@@ -14,6 +12,7 @@ import { useCallback, useEffect, useMemo } from 'react';
1412
import dayjs from 'dayjs';
1513
import { useIsFocused, useNavigation } from '@react-navigation/native';
1614
import { ChannelResponse, DraftMessage, DraftResponse, MessageResponseBase } from 'stream-chat';
15+
import { getPreviewFromMessage } from '../utils/getPreviewOfMessage';
1716

1817
export type DraftItemProps = {
1918
type?: 'channel' | 'thread';
@@ -24,67 +23,6 @@ export type DraftItemProps = {
2423
thread?: MessageResponseBase;
2524
};
2625

27-
export const attachmentTypeIconMap = {
28-
audio: '🔈',
29-
file: '📄',
30-
image: '📷',
31-
video: '🎥',
32-
voiceRecording: '🎙️',
33-
} as const;
34-
35-
const getPreviewFromMessage = ({
36-
t,
37-
draftMessage,
38-
}: {
39-
t: TranslationContextValue['t'];
40-
draftMessage: DraftMessage;
41-
}) => {
42-
if (draftMessage.attachments?.length) {
43-
const attachment = draftMessage?.attachments?.at(0);
44-
45-
const attachmentIcon = attachment
46-
? `${
47-
attachmentTypeIconMap[
48-
(attachment.type as keyof typeof attachmentTypeIconMap) ?? 'file'
49-
] ?? attachmentTypeIconMap.file
50-
} `
51-
: '';
52-
53-
if (attachment?.type === FileTypes.VoiceRecording) {
54-
return [
55-
{ bold: false, text: attachmentIcon },
56-
{
57-
bold: false,
58-
text: t('Voice message'),
59-
},
60-
];
61-
}
62-
return [
63-
{ bold: false, text: attachmentIcon },
64-
{
65-
bold: false,
66-
text:
67-
attachment?.type === FileTypes.Image
68-
? attachment?.fallback
69-
? attachment?.fallback
70-
: 'N/A'
71-
: attachment?.title
72-
? attachment?.title
73-
: 'N/A',
74-
},
75-
];
76-
}
77-
78-
if (draftMessage.text) {
79-
return [
80-
{
81-
bold: false,
82-
text: draftMessage.text,
83-
},
84-
];
85-
}
86-
};
87-
8826
export const DraftItem = ({ type, channel, date, message, thread }: DraftItemProps) => {
8927
const {
9028
theme: {
@@ -113,7 +51,7 @@ export const DraftItem = ({ type, channel, date, message, thread }: DraftItemPro
11351
};
11452

11553
const previews = useMemo(() => {
116-
return getPreviewFromMessage({ draftMessage: message, t });
54+
return getPreviewFromMessage({ message, t });
11755
}, [message, t]);
11856

11957
return (
@@ -187,7 +125,7 @@ export const DraftsList = () => {
187125
}, [draftsManager]);
188126

189127
const onEndReached = useCallback(() => {
190-
draftsManager.loadNextPage();
128+
draftsManager.loadNextPage();
191129
}, [draftsManager]);
192130

193131
return (

0 commit comments

Comments
 (0)