diff --git a/docusaurus/docs/reactnative/basics/hello_stream_chat.mdx b/docusaurus/docs/reactnative/basics/hello_stream_chat.mdx index 0a19454871..59b53c2601 100644 --- a/docusaurus/docs/reactnative/basics/hello_stream_chat.mdx +++ b/docusaurus/docs/reactnative/basics/hello_stream_chat.mdx @@ -27,8 +27,7 @@ Before starting make sure you have installed `stream-chat-react-native` as direc Stream Chat for React Native uses [`stream-chat`](https://github.com/GetStream/stream-chat-js), Stream's JavaScript client, to interact with Stream's chat services. `stream-chat` is a dependency of Stream Chat for React Native so it is can be used once `stream-chat-react-native` is installed. -To start you must get an instance of Stream Chat, for this you will need an API key. -To create one you can sign up for a [free 30-day trial](https://getstream.io/chat/trial/) of Stream Chat, no CC is required. +To start, you can sign up for a [Free subscription](https://getstream.io/chat/pricing/) and then get your API key. ```ts import { StreamChat } from 'stream-chat'; diff --git a/docusaurus/docs/reactnative/basics/internationalization.mdx b/docusaurus/docs/reactnative/basics/internationalization.mdx index a0b8345ff5..0f3d41b192 100644 --- a/docusaurus/docs/reactnative/basics/internationalization.mdx +++ b/docusaurus/docs/reactnative/basics/internationalization.mdx @@ -106,8 +106,8 @@ streami18n.registerTranslation('nl', { [`react-native-localize`](https://github.com/zoontek/react-native-localize#-react-native-localize) package provides a toolbox for React Native app localization. You can use this package to access user preferred locale, and use it to set language for chat components: ```tsx -import *as RNLocalizefrom 'react-native-localize'; -const streami18n =new Streami18n(); +import * as RNLocalize from 'react-native-localize'; +const streami18n = new Streami18n(); const userPreferredLocales = RNLocalize.getLocales(); @@ -176,14 +176,14 @@ const i18n =new Streami18n({ Or by providing your own [Day.js](https://day.js.org/docs/en/installation/installation) object: ```tsx -import Dayjsfrom 'dayjs'; +import Dayjs from 'dayjs'; import 'dayjs/locale/nl'; import 'dayjs/locale/it'; // or if you want to include all locales import 'dayjs/min/locales'; -const i18n =new Streami18n({ +const i18n = new Streami18n({ language: 'nl', DateTimeParser: Dayjs, }); @@ -195,6 +195,28 @@ If you would like to stick with English language for date-times in Stream compon If your application has a user-base that speaks more than one language, Stream's Chat Client provides the option to automatically translate messages. For more information on using automatic machine translation for messages, see the [Chat Client Guide on Translation](https://getstream.io/chat/docs/react-native/translation/?language=javascript). +### Timezone location + +To display date and time in different than machine's local timezone, you can provide the timezone parameter to the `Streami18n` constructor. The timezone value has to be a valid [timezone identifier string](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). If no timezone parameter is provided, then the machine's local timezone is applied. + +:::note +On our React Native SDK, the timezone is only supported through `moment-timezone` and not through the default `Dayjs`. This is because of the [following issue](https://github.com/iamkun/dayjs/issues/1377). + +So, to ensure this please pass the `moment-timezone` object to the `DateTimeParser` key of the `Streami18n` constructor. +::: + +```tsx +import { Streami18n } from 'stream-chat-react'; +import momentTimezone from 'moment-timezone'; + +const streami18n = new Streami18n({ + DateTimeParser: momentTimezone, + timezone: 'Europe/Budapest', +}); +``` + +Moment Timezone will automatically load and extend the moment module, then return the modified instance. This will also prevent multiple versions of moment being installed in a project. + ## Options `options` are the first optional parameter passed to `Streami18n`, it is an object with all keys being optional. diff --git a/docusaurus/docs/reactnative/ui-components/overview.mdx b/docusaurus/docs/reactnative/ui-components/overview.mdx index 9406272883..29886c4d33 100644 --- a/docusaurus/docs/reactnative/ui-components/overview.mdx +++ b/docusaurus/docs/reactnative/ui-components/overview.mdx @@ -30,7 +30,7 @@ Stream Chat for React Native uses [`stream-chat`](https://github.com/GetStream/s You don't have to explicitly install `stream-chat` in your project. ::: -To start you must get an instance of Stream Chat, for this you will need an API key. To create one you can sign up for a [free 30-day trial](https://getstream.io/chat/trial/) of Stream Chat. +To start, you can sign up for a [Free subscription](https://getstream.io/chat/pricing/) and then get your API key. ```ts import { StreamChat } from 'stream-chat'; diff --git a/examples/SampleApp/ios/Podfile.lock b/examples/SampleApp/ios/Podfile.lock index 21809564f8..4beb2a4589 100644 --- a/examples/SampleApp/ios/Podfile.lock +++ b/examples/SampleApp/ios/Podfile.lock @@ -1040,14 +1040,13 @@ PODS: - React-Core - react-native-safe-area-context (4.9.0): - React-Core - - react-native-video (6.0.0-beta.8): + - react-native-video (6.4.2): - glog - RCT-Folly (= 2022.05.16.00) - React-Core - - react-native-video/Video (= 6.0.0-beta.8) - - react-native-video/Video (6.0.0-beta.8): + - react-native-video/Video (= 6.4.2) + - react-native-video/Video (6.4.2): - glog - - PromisesSwift - RCT-Folly (= 2022.05.16.00) - React-Core - React-nativeconfig (0.73.5) @@ -1580,7 +1579,7 @@ SPEC CHECKSUMS: react-native-netinfo: 299dad906cdbf3b67bcc6f693c807f98bdd127cc react-native-quick-sqlite: 2b225dadc63b670f027111e58f6f169773f6d755 react-native-safe-area-context: b97eb6f9e3b7f437806c2ce5983f479f8eb5de4b - react-native-video: 2e38d3e0f7382427539b6b436812553480fcf70a + react-native-video: 244d6e4f8f426df9d5cc20915a56194399e9e067 React-nativeconfig: 78e2b14c0ee479449845b67bbe103343257cbcc0 React-NativeModulesApple: 4fce13d416890352a7a9621427459fe738c4e79a React-perflogger: 0dd9f1725d55f8264b81efadd373fe1d9cca7dc2 diff --git a/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj b/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj index 4cec7d6c34..46c2170b2c 100644 --- a/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj +++ b/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj @@ -630,6 +630,9 @@ " ${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 = ( @@ -738,6 +741,9 @@ " ${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 = ( diff --git a/examples/SampleApp/package.json b/examples/SampleApp/package.json index 6092a634fc..b008442742 100644 --- a/examples/SampleApp/package.json +++ b/examples/SampleApp/package.json @@ -51,7 +51,7 @@ "react-native-screens": "^3.29.0", "react-native-share": "10.0.2", "react-native-svg": "^14.1.0", - "react-native-video": "6.0.0-beta.8", + "react-native-video": "^6.4.2", "stream-chat-react-native": "link:../../package/native-package", "stream-chat-react-native-core": "link:../../package" }, diff --git a/examples/SampleApp/yarn.lock b/examples/SampleApp/yarn.lock index fe74be2c58..11d050a942 100644 --- a/examples/SampleApp/yarn.lock +++ b/examples/SampleApp/yarn.lock @@ -1120,13 +1120,20 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.0", "@babel/runtime@^7.16.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.8.4": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.16.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.8.4": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7" integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw== dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.17.2": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.8.tgz#5d958c3827b13cc6d05e038c07fb2e5e3420d82e" + integrity sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.0.0", "@babel/template@^7.22.15", "@babel/template@^7.23.9", "@babel/template@^7.3.3": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.23.9.tgz#f881d0487cba2828d3259dcb9ef5005a9731011a" @@ -4198,12 +4205,12 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -i18next@20.2.4: - version "20.2.4" - resolved "https://registry.yarnpkg.com/i18next/-/i18next-20.2.4.tgz#972220f19dfef0075a70890d3e8b1f7cf64c5bd6" - integrity sha512-goE1LCA/IZOGG26PkkqoOl2KWR7YP606SvokVQZ29J6QwE02KycrzNetoMUJeqYrTxs4rmiiZgZp+q8qofQL6Q== +i18next@^21.6.14: + version "21.10.0" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.10.0.tgz#85429af55fdca4858345d0e16b584ec29520197d" + integrity sha512-YeuIBmFsGjUfO3qBmMOc0rQaun4mIpGKET5WDwvu8lU7gvwpcariZLNtL0Fzj+zazcHUrlXHiptcFhBMFaxzfg== dependencies: - "@babel/runtime" "^7.12.0" + "@babel/runtime" "^7.17.2" ieee754@^1.1.13: version "1.2.1" @@ -4278,6 +4285,11 @@ internal-slot@^1.0.5, internal-slot@^1.0.7: hasown "^2.0.0" side-channel "^1.0.4" +intl-pluralrules@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/intl-pluralrules/-/intl-pluralrules-2.0.1.tgz#de16c3df1e09437635829725e88ea70c9ad79569" + integrity sha512-astxTLzIdXPeN0K9Rumi6LfMpm3rvNO0iJE+h/k8Kr/is+wPbRe4ikyDjlLr6VTh/mEfNv8RjN+gu3KwDiuhqg== + invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -6265,10 +6277,10 @@ react-native-url-polyfill@^1.3.0: dependencies: whatwg-url-without-unicode "8.0.0-3" -react-native-video@6.0.0-beta.8: - version "6.0.0-beta.8" - resolved "https://registry.yarnpkg.com/react-native-video/-/react-native-video-6.0.0-beta.8.tgz#76597ea61d3791beb14731e9e469ddb86e88adf9" - integrity sha512-pWAJKhP6yt7sIe/u4vi292sWaHNgqSwZbKzauRKBHHEJr2pY/EG7Lis5hDI+rOJlQ0NmsPEurbUPH5TGQsFJFw== +react-native-video@^6.4.2: + version "6.4.2" + resolved "https://registry.yarnpkg.com/react-native-video/-/react-native-video-6.4.2.tgz#f1d8e7594d36aa513ba21aa86bcd6e11c2716915" + integrity sha512-8/8AVnamWJU80ZTWFFrLtpzfJeonSM8C8C8YycWAnjzro+FpJqpnQFFheVU2l2BSrnRTGA3eT2p4euXc+SByMw== react-native@*, react-native@^0.73.0: version "0.73.5" @@ -6825,15 +6837,16 @@ 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.31.1: - version "5.31.1" - resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-5.31.1.tgz#4367276c8316d325e6b062e4efb95d464d4e2eef" - integrity sha512-EKHMQ0bWRoyubTxk1IKEbkIzcIY5hxHDpyGU7ObGXLLnhoVBEDranZBQ7dNDdbuM88uKh9mGK5WH4NQSTioKxQ== +stream-chat-react-native-core@5.33.1: + version "5.33.1" + resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-5.33.1.tgz#d9e7847469d3ffb6e7fd35fbb7b720f2e25d172e" + integrity sha512-TCDmChJe07cYyL3sErc6qycRFMA+HbflCKRGrFvVvpU0RdWJljaqiOo3avFSauciSnQxx9WxzTkMism8YsFHcQ== dependencies: "@gorhom/bottom-sheet" "4.4.8" dayjs "1.10.5" emoji-regex "^10.3.0" - i18next "20.2.4" + i18next "^21.6.14" + intl-pluralrules "^2.0.1" linkifyjs "^4.1.1" lodash-es "4.17.21" mime-types "^2.1.34" diff --git a/examples/TypeScriptMessaging/ios/Podfile.lock b/examples/TypeScriptMessaging/ios/Podfile.lock index b4dbb50a44..a425bc4bab 100644 --- a/examples/TypeScriptMessaging/ios/Podfile.lock +++ b/examples/TypeScriptMessaging/ios/Podfile.lock @@ -73,9 +73,6 @@ PODS: - hermes-engine/Pre-built (0.73.6) - libevent (2.1.12) - OpenSSL-Universal (1.1.1100) - - PromisesObjC (2.4.0) - - PromisesSwift (2.4.0): - - PromisesObjC (= 2.4.0) - RCT-Folly (2022.05.16.00): - boost - DoubleConversion @@ -963,14 +960,13 @@ PODS: - React-Core - react-native-safe-area-context (4.9.0): - React-Core - - react-native-video (6.0.0-beta.8): + - react-native-video (6.4.2): - glog - RCT-Folly (= 2022.05.16.00) - React-Core - - react-native-video/Video (= 6.0.0-beta.8) - - react-native-video/Video (6.0.0-beta.8): + - react-native-video/Video (= 6.4.2) + - react-native-video/Video (6.4.2): - glog - - PromisesSwift - RCT-Folly (= 2022.05.16.00) - React-Core - React-nativeconfig (0.73.6) @@ -1288,8 +1284,6 @@ SPEC REPOS: - fmt - libevent - OpenSSL-Universal - - PromisesObjC - - PromisesSwift - SocketRocket - TOCropViewController @@ -1449,8 +1443,6 @@ SPEC CHECKSUMS: hermes-engine: 9cecf9953a681df7556b8cc9c74905de8f3293c0 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c - PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 - PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 RCT-Folly: 7169b2b1c44399c76a47b5deaaba715eeeb476c0 RCTRequired: ca1d7414aba0b27efcfa2ccd37637edb1ab77d96 RCTTypeSafety: 678e344fb976ff98343ca61dc62e151f3a042292 @@ -1479,7 +1471,7 @@ SPEC CHECKSUMS: react-native-netinfo: 299dad906cdbf3b67bcc6f693c807f98bdd127cc react-native-quick-sqlite: 2b225dadc63b670f027111e58f6f169773f6d755 react-native-safe-area-context: b97eb6f9e3b7f437806c2ce5983f479f8eb5de4b - react-native-video: d440605e68cf173e70f0b25112455e3d86890663 + react-native-video: da6a37174e0cf74ba3decdbe671a0bf8e309538c React-nativeconfig: b4d4e9901d4cabb57be63053fd2aa6086eb3c85f React-NativeModulesApple: cd26e56d56350e123da0c1e3e4c76cb58a05e1ee React-perflogger: 5f49905de275bac07ac7ea7f575a70611fa988f2 @@ -1517,4 +1509,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 90406e1e85c82b37484f5d746afa45c0637bb4b3 -COCOAPODS: 1.15.2 +COCOAPODS: 1.14.3 diff --git a/examples/TypeScriptMessaging/package.json b/examples/TypeScriptMessaging/package.json index 20646e82c9..144f1b3f1a 100644 --- a/examples/TypeScriptMessaging/package.json +++ b/examples/TypeScriptMessaging/package.json @@ -20,8 +20,8 @@ "@react-navigation/stack": "^6.2.0", "@stream-io/flat-list-mvcp": "0.10.3", "react": "18.2.0", - "react-native-audio-recorder-player": "3.6.6", "react-native": "^0.73.6", + "react-native-audio-recorder-player": "3.6.6", "react-native-document-picker": "^9.0.1", "react-native-fs": "^2.18.0", "react-native-gesture-handler": "^2.14.0", @@ -34,7 +34,7 @@ "react-native-screens": "^3.28.0", "react-native-share": "^8.2.2", "react-native-svg": "^14.0.0", - "react-native-video": "6.0.0-beta.8", + "react-native-video": "^6.4.2", "stream-chat-react-native": "link:../../package/native-package", "stream-chat-react-native-core": "link:../../package" }, diff --git a/examples/TypeScriptMessaging/yarn.lock b/examples/TypeScriptMessaging/yarn.lock index 60b8845c38..2ee4fc7e48 100644 --- a/examples/TypeScriptMessaging/yarn.lock +++ b/examples/TypeScriptMessaging/yarn.lock @@ -1404,13 +1404,20 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.0", "@babel/runtime@^7.16.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.8.4": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.16.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.8.4": version "7.22.3" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.3.tgz#0a7fce51d43adbf0f7b517a71f4c3aaca92ebcbb" integrity sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ== dependencies: regenerator-runtime "^0.13.11" +"@babel/runtime@^7.17.2": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.8.tgz#5d958c3827b13cc6d05e038c07fb2e5e3420d82e" + integrity sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.21.9", "@babel/template@^7.3.3": version "7.21.9" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.21.9.tgz#bf8dad2859130ae46088a99c1f265394877446fb" @@ -4465,12 +4472,12 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -i18next@20.2.4: - version "20.2.4" - resolved "https://registry.yarnpkg.com/i18next/-/i18next-20.2.4.tgz#972220f19dfef0075a70890d3e8b1f7cf64c5bd6" - integrity sha512-goE1LCA/IZOGG26PkkqoOl2KWR7YP606SvokVQZ29J6QwE02KycrzNetoMUJeqYrTxs4rmiiZgZp+q8qofQL6Q== +i18next@^21.6.14: + version "21.10.0" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.10.0.tgz#85429af55fdca4858345d0e16b584ec29520197d" + integrity sha512-YeuIBmFsGjUfO3qBmMOc0rQaun4mIpGKET5WDwvu8lU7gvwpcariZLNtL0Fzj+zazcHUrlXHiptcFhBMFaxzfg== dependencies: - "@babel/runtime" "^7.12.0" + "@babel/runtime" "^7.17.2" ieee754@^1.1.13: version "1.2.1" @@ -4545,6 +4552,11 @@ internal-slot@^1.0.3, internal-slot@^1.0.5: has "^1.0.3" side-channel "^1.0.4" +intl-pluralrules@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/intl-pluralrules/-/intl-pluralrules-2.0.1.tgz#de16c3df1e09437635829725e88ea70c9ad79569" + integrity sha512-astxTLzIdXPeN0K9Rumi6LfMpm3rvNO0iJE+h/k8Kr/is+wPbRe4ikyDjlLr6VTh/mEfNv8RjN+gu3KwDiuhqg== + invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -6387,10 +6399,10 @@ react-native-url-polyfill@^1.3.0: dependencies: whatwg-url-without-unicode "8.0.0-3" -react-native-video@6.0.0-beta.8: - version "6.0.0-beta.8" - resolved "https://registry.yarnpkg.com/react-native-video/-/react-native-video-6.0.0-beta.8.tgz#76597ea61d3791beb14731e9e469ddb86e88adf9" - integrity sha512-pWAJKhP6yt7sIe/u4vi292sWaHNgqSwZbKzauRKBHHEJr2pY/EG7Lis5hDI+rOJlQ0NmsPEurbUPH5TGQsFJFw== +react-native-video@^6.4.2: + version "6.4.2" + resolved "https://registry.yarnpkg.com/react-native-video/-/react-native-video-6.4.2.tgz#f1d8e7594d36aa513ba21aa86bcd6e11c2716915" + integrity sha512-8/8AVnamWJU80ZTWFFrLtpzfJeonSM8C8C8YycWAnjzro+FpJqpnQFFheVU2l2BSrnRTGA3eT2p4euXc+SByMw== react-native@*, react-native@^0.73.6: version "0.73.6" @@ -6518,6 +6530,11 @@ regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.2: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + regenerator-transform@^0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" @@ -6882,15 +6899,16 @@ 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.31.1: - version "5.31.1" - resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-5.31.1.tgz#4367276c8316d325e6b062e4efb95d464d4e2eef" - integrity sha512-EKHMQ0bWRoyubTxk1IKEbkIzcIY5hxHDpyGU7ObGXLLnhoVBEDranZBQ7dNDdbuM88uKh9mGK5WH4NQSTioKxQ== +stream-chat-react-native-core@5.33.1: + version "5.33.1" + resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-5.33.1.tgz#d9e7847469d3ffb6e7fd35fbb7b720f2e25d172e" + integrity sha512-TCDmChJe07cYyL3sErc6qycRFMA+HbflCKRGrFvVvpU0RdWJljaqiOo3avFSauciSnQxx9WxzTkMism8YsFHcQ== dependencies: "@gorhom/bottom-sheet" "4.4.8" dayjs "1.10.5" emoji-regex "^10.3.0" - i18next "20.2.4" + i18next "^21.6.14" + intl-pluralrules "^2.0.1" linkifyjs "^4.1.1" lodash-es "4.17.21" mime-types "^2.1.34" diff --git a/package/jest-global-setup.js b/package/jest-global-setup.js new file mode 100644 index 0000000000..1895a06077 --- /dev/null +++ b/package/jest-global-setup.js @@ -0,0 +1,4 @@ +/* eslint-disable require-await */ +module.exports = async () => { + process.env.TZ = 'UTC'; +}; diff --git a/package/jest.config.js b/package/jest.config.js index cfe0a33857..a016e3d429 100644 --- a/package/jest.config.js +++ b/package/jest.config.js @@ -1,6 +1,7 @@ /* global require */ // eslint-disable-next-line no-undef module.exports = { + globalSetup: './jest-global-setup.js', moduleNameMapper: { 'mock-builders(.*)$': '/src/mock-builders$1', }, diff --git a/package/native-package/package.json b/package/native-package/package.json index eec0af9c9d..22f17ea81f 100644 --- a/package/native-package/package.json +++ b/package/native-package/package.json @@ -26,7 +26,7 @@ "react-native-image-resizer": ">=1.4.2", "react-native-share": ">=4.1.0", "react-native-audio-recorder-player": ">=3.6.4", - "react-native-video": ">=5.2.1" + "react-native-video": ">=6.4.2" }, "peerDependenciesMeta": { "@react-native-clipboard/clipboard": { diff --git a/package/native-package/yarn.lock b/package/native-package/yarn.lock index d5aa76941a..7dd2c23696 100644 --- a/package/native-package/yarn.lock +++ b/package/native-package/yarn.lock @@ -4254,10 +4254,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.33.0: - version "5.33.0" - resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-5.33.0.tgz#14f04de90cbc8db011bab8db3fa84abe2dc2eaec" - integrity sha512-V9OJA9MrHzaCw5q16ZRbEktA1HamITbXPOkVZOjpDbb0OBcmedmOnD9C2NFIprc770lhllS/1MKBDr0GdQ9NXQ== +stream-chat-react-native-core@5.33.1: + version "5.33.1" + resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-5.33.1.tgz#d9e7847469d3ffb6e7fd35fbb7b720f2e25d172e" + integrity sha512-TCDmChJe07cYyL3sErc6qycRFMA+HbflCKRGrFvVvpU0RdWJljaqiOo3avFSauciSnQxx9WxzTkMism8YsFHcQ== dependencies: "@gorhom/bottom-sheet" "4.4.8" dayjs "1.10.5" diff --git a/package/package.json b/package/package.json index 243fcba226..fa91ac82a0 100644 --- a/package/package.json +++ b/package/package.json @@ -135,7 +135,7 @@ "eslint-plugin-typescript-sort-keys": "3.2.0", "i18next-parser": "^9.0.0", "jest": "29.6.3", - "moment": "2.29.2", + "moment-timezone": "^0.5.45", "prettier": "2.8.8", "react": "18.2.0", "react-docgen-typescript": "1.22.0", diff --git a/package/src/components/Attachment/Card.tsx b/package/src/components/Attachment/Card.tsx index d8d61ae48f..3ac60f76fa 100644 --- a/package/src/components/Attachment/Card.tsx +++ b/package/src/components/Attachment/Card.tsx @@ -137,7 +137,7 @@ const CardWithContext = < const { theme: { - colors: { accent_blue, black, blue_alice, transparent, white }, + colors: { accent_blue, black, blue_alice, static_black, static_white, transparent }, messageSimple: { card: { authorName, @@ -210,8 +210,10 @@ const CardWithContext = < style={[styles.cardCover, cover, stylesProp.cardCover]} > {isVideoCard ? ( - - + + ) : null} diff --git a/package/src/components/Message/MessageSimple/MessageSimple.tsx b/package/src/components/Message/MessageSimple/MessageSimple.tsx index e20437801c..da8d630512 100644 --- a/package/src/components/Message/MessageSimple/MessageSimple.tsx +++ b/package/src/components/Message/MessageSimple/MessageSimple.tsx @@ -84,7 +84,7 @@ const MessageSimpleWithContext = < const messageGroupedSingleOrBottom = groupStyles.includes('single') || groupStyles.includes('bottom'); - const showReactions = hasReactions && ReactionList; + const showReactions = message.type !== 'deleted' && hasReactions && ReactionList; const lastMessageInMessageListStyles = [styles.lastMessageContainer, lastMessageContainer]; const messageGroupedSingleOrBottomStyles = [ diff --git a/package/src/components/Message/hooks/useMessageActions.tsx b/package/src/components/Message/hooks/useMessageActions.tsx index e68c4cd67a..6f9774e145 100644 --- a/package/src/components/Message/hooks/useMessageActions.tsx +++ b/package/src/components/Message/hooks/useMessageActions.tsx @@ -18,7 +18,7 @@ import { MessageFlag, Mute, Pin, - SendUp, + Resend, ThreadReply, Unpin, UserDelete, @@ -95,7 +95,7 @@ export const useMessageActions = < }) => { const { theme: { - colors: { accent_blue, accent_red, grey }, + colors: { accent_red, grey }, }, } = useTheme(); const { @@ -179,7 +179,7 @@ export const useMessageActions = < handleDeleteMessage(); }, actionType: 'deleteMessage', - icon: , + icon: , title: t('Delete Message'), titleStyle: { color: accent_red }, }; @@ -206,7 +206,7 @@ export const useMessageActions = < handleTogglePinMessage(); }, actionType: 'pinMessage', - icon: , + icon: , title: t('Pin to Conversation'), }; @@ -289,7 +289,7 @@ export const useMessageActions = < await handleResendMessage(); }, actionType: 'retry', - icon: , + icon: , title: t('Resend'), }; diff --git a/package/src/contexts/themeContext/utils/theme.ts b/package/src/contexts/themeContext/utils/theme.ts index f84dddd052..87c203a750 100644 --- a/package/src/contexts/themeContext/utils/theme.ts +++ b/package/src/contexts/themeContext/utils/theme.ts @@ -1008,8 +1008,8 @@ export const defaultTheme: Theme = { videoContainer: {}, }, playIcon: { - height: 24, - width: 24, + height: 32, + width: 32, }, }, container: {}, diff --git a/package/src/contexts/translationContext/TranslationContext.tsx b/package/src/contexts/translationContext/TranslationContext.tsx index b3a295dd0a..98da3624b3 100644 --- a/package/src/contexts/translationContext/TranslationContext.tsx +++ b/package/src/contexts/translationContext/TranslationContext.tsx @@ -3,7 +3,7 @@ import React, { useContext } from 'react'; import Dayjs from 'dayjs'; import type { TFunction } from 'i18next'; -import type { Moment } from 'moment'; +import type { Moment } from 'moment-timezone'; import type { TranslationLanguages } from 'stream-chat'; diff --git a/package/src/icons/Pin.tsx b/package/src/icons/Pin.tsx index a1d97ba645..2ad806cef4 100644 --- a/package/src/icons/Pin.tsx +++ b/package/src/icons/Pin.tsx @@ -2,11 +2,15 @@ import React from 'react'; import { IconProps, RootPath, RootSvg } from './utils/base'; -export const Pin = (props: IconProps) => ( +type Props = IconProps & { + size: number; +}; + +export const Pin = (props: Props) => ( ( + + + +); diff --git a/package/src/icons/index.ts b/package/src/icons/index.ts index d71893bf70..4da601145f 100644 --- a/package/src/icons/index.ts +++ b/package/src/icons/index.ts @@ -61,6 +61,7 @@ export * from './PPT'; export * from './PPTX'; export * from './RAR'; export * from './Refresh'; +export * from './Resend'; export * from './RTF'; export * from './Search'; export * from './SendCheck'; diff --git a/package/src/utils/__tests__/Streami18n.test.js b/package/src/utils/__tests__/Streami18n.test.js index f84f40599d..c1ab95e74c 100644 --- a/package/src/utils/__tests__/Streami18n.test.js +++ b/package/src/utils/__tests__/Streami18n.test.js @@ -1,6 +1,7 @@ import { default as Dayjs } from 'dayjs'; import 'dayjs/locale/nl'; import localeData from 'dayjs/plugin/localeData'; +import moment from 'moment-timezone'; import frTranslations from '../../i18n/fr.json'; import nlTranslations from '../../i18n/nl.json'; @@ -18,6 +19,12 @@ const customDayjsLocaleConfig = { weekdaysShort: 'sun_mán_týs_mik_hós_frí_ley'.split('_'), }; +describe('Jest Timezone', () => { + it('global config should set the timezone to UTC', () => { + expect(new Date().getTimezoneOffset()).toBe(0); + }); +}); + describe('Streami18n instance - default', () => { const streami18nOptions = { logger: () => {} }; const streami18n = new Streami18n(streami18nOptions); @@ -184,24 +191,53 @@ describe('setLanguage - switch to french', () => { }); }); -describe('formatters property', () => { - it('contains the default timestampFormatter', () => { - expect(new Streami18n().formatters.timestampFormatter).toBeDefined(); - }); - it('allows to override the default timestampFormatter', async () => { - const i18n = new Streami18n({ - formatters: { timestampFormatter: () => () => 'custom' }, - translationsForLanguage: { abc: '{{ value | timestampFormatter }}' }, +describe('Streami18n timezone', () => { + describe.each([['moment', moment]])('%s', (moduleName, module) => { + it('is by default the local timezone', () => { + const streamI18n = new Streami18n({ DateTimeParser: module }); + const date = new Date(); + expect(streamI18n.tDateTimeParser(date).format('H')).toBe(date.getHours().toString()); }); - await i18n.init(); - expect(i18n.t('abc')).toBe('custom'); - }); - it('allows to add new custom formatter', async () => { - const i18n = new Streami18n({ - formatters: { customFormatter: () => () => 'custom' }, - translationsForLanguage: { abc: '{{ value | customFormatter }}' }, + + it('can be set to different timezone on init', () => { + const streamI18n = new Streami18n({ DateTimeParser: module, timezone: 'Europe/Prague' }); + const date = new Date(); + expect(streamI18n.tDateTimeParser(date).format('H')).not.toBe(date.getHours().toString()); + expect(streamI18n.tDateTimeParser(date).format('H')).not.toBe( + (date.getUTCHours() - 2).toString(), + ); + }); + + it('is ignored if datetime parser does not support timezones', () => { + const tz = module.tz; + delete module.tz; + + const streamI18n = new Streami18n({ DateTimeParser: module, timezone: 'Europe/Prague' }); + const date = new Date(); + expect(streamI18n.tDateTimeParser(date).format('H')).toBe(date.getHours().toString()); + + module.tz = tz; + }); + describe('formatters property', () => { + it('contains the default timestampFormatter', () => { + expect(new Streami18n().formatters.timestampFormatter).toBeDefined(); + }); + it('allows to override the default timestampFormatter', async () => { + const i18n = new Streami18n({ + formatters: { timestampFormatter: () => () => 'custom' }, + translationsForLanguage: { abc: '{{ value | timestampFormatter }}' }, + }); + await i18n.init(); + expect(i18n.t('abc')).toBe('custom'); + }); + it('allows to add new custom formatter', async () => { + const i18n = new Streami18n({ + formatters: { customFormatter: () => () => 'custom' }, + translationsForLanguage: { abc: '{{ value | customFormatter }}' }, + }); + await i18n.init(); + expect(i18n.t('abc')).toBe('custom'); + }); }); - await i18n.init(); - expect(i18n.t('abc')).toBe('custom'); }); }); diff --git a/package/src/utils/i18n/Streami18n.ts b/package/src/utils/i18n/Streami18n.ts index 5b162ba70e..3980dc0a7e 100644 --- a/package/src/utils/i18n/Streami18n.ts +++ b/package/src/utils/i18n/Streami18n.ts @@ -4,9 +4,10 @@ import localeData from 'dayjs/plugin/localeData'; import LocalizedFormat from 'dayjs/plugin/localizedFormat'; import relativeTime from 'dayjs/plugin/relativeTime'; import updateLocale from 'dayjs/plugin/updateLocale'; +import utc from 'dayjs/plugin/utc'; import i18n, { FallbackLng, TFunction } from 'i18next'; -import type moment from 'moment'; +import type momentTimezone from 'moment-timezone'; import { calendarFormats } from './calendarFormats'; import { @@ -54,6 +55,7 @@ const defaultNS = 'translation'; const defaultLng = 'en'; Dayjs.extend(updateLocale); +Dayjs.extend(utc); Dayjs.updateLocale('en', { calendar: calendarFormats.en, @@ -147,18 +149,28 @@ const en_locale = { weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], }; +type DateTimeParserModule = typeof Dayjs | typeof momentTimezone; + // Type guards to check DayJs -const isDayJs = (dateTimeParser: typeof Dayjs | typeof moment): dateTimeParser is typeof Dayjs => +const isDayJs = (dateTimeParser: DateTimeParserModule): dateTimeParser is typeof Dayjs => (dateTimeParser as typeof Dayjs).extend !== undefined; +type TimezoneParser = { + tz: momentTimezone.MomentTimezone | Dayjs.Dayjs; +}; + +const supportsTz = (dateTimeParser: unknown): dateTimeParser is TimezoneParser => + (dateTimeParser as TimezoneParser).tz !== undefined; + type Streami18nOptions = { - DateTimeParser?: typeof Dayjs | typeof moment; + DateTimeParser?: DateTimeParserModule; dayjsLocaleConfigForLanguage?: Partial; debug?: boolean; disableDateTimeTranslations?: boolean; formatters?: Partial & CustomFormatters; language?: string; logger?: (msg?: string) => void; + timezone?: string; translationsForLanguage?: Partial; }; @@ -385,10 +397,14 @@ export class Streami18n { */ logger: (msg?: string) => void; currentLanguage: string; - DateTimeParser: typeof Dayjs | typeof moment; + DateTimeParser: DateTimeParserModule; formatters: PredefinedFormatters & CustomFormatters = predefinedFormatters; isCustomDateTimeParser: boolean; i18nextConfig: I18NextConfig; + /** + * A valid TZ identifier string (https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) + */ + timezone?: string; /** * Constructor accepts following options: @@ -427,6 +443,7 @@ export class Streami18n { this.currentLanguage = finalOptions.language; this.DateTimeParser = finalOptions.DateTimeParser; + this.timezone = finalOptions.timezone; this.formatters = { ...predefinedFormatters, ...options?.formatters }; try { @@ -504,19 +521,19 @@ export class Streami18n { } this.tDateTimeParser = (timestamp) => { - if (finalOptions.disableDateTimeTranslations || !this.localeExists(this.currentLanguage)) { - /** - * TS needs to know which is being called to accept the chain call - */ - if (isDayJs(this.DateTimeParser)) { - return this.DateTimeParser(timestamp).locale(defaultLng); - } - return this.DateTimeParser(timestamp).locale(defaultLng); + const language = + finalOptions.disableDateTimeTranslations || !this.localeExists(this.currentLanguage) + ? defaultLng + : this.currentLanguage; + + // If the DateTimeParser is not a Dayjs instance, we assume it is a Moment instance. + if (!isDayJs(this.DateTimeParser)) { + return supportsTz(this.DateTimeParser) && this.timezone + ? this.DateTimeParser(timestamp).tz(this.timezone).locale(language) + : this.DateTimeParser(timestamp).locale(language); } - if (isDayJs(this.DateTimeParser)) { - return this.DateTimeParser(timestamp).locale(this.currentLanguage); - } - return this.DateTimeParser(timestamp).locale(this.currentLanguage); + + return this.DateTimeParser(timestamp).locale(language); }; } diff --git a/package/yarn.lock b/package/yarn.lock index 6ccecd67fa..063949b1a6 100644 --- a/package/yarn.lock +++ b/package/yarn.lock @@ -8765,10 +8765,17 @@ mktemp@~0.4.0: resolved "https://registry.yarnpkg.com/mktemp/-/mktemp-0.4.0.tgz#6d0515611c8a8c84e484aa2000129b98e981ff0b" integrity sha512-IXnMcJ6ZyTuhRmJSjzvHSRhlVPiN9Jwc6e59V0bEJ0ba6OBeX2L0E+mRN1QseeOF4mM+F1Rit6Nh7o+rl2Yn/A== -moment@2.29.2: - version "2.29.2" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.2.tgz#00910c60b20843bcba52d37d58c628b47b1f20e4" - integrity sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg== +moment-timezone@^0.5.45: + version "0.5.45" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.45.tgz#cb685acd56bac10e69d93c536366eb65aa6bcf5c" + integrity sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ== + dependencies: + moment "^2.29.4" + +moment@^2.29.4: + version "2.30.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" + integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== move-concurrently@^1.0.1: version "1.0.1"