diff --git a/.github/actions/install-and-build-sdk/action.yml b/.github/actions/install-and-build-sdk/action.yml index 5ba82e4bc2..f79f913aa5 100644 --- a/.github/actions/install-and-build-sdk/action.yml +++ b/.github/actions/install-and-build-sdk/action.yml @@ -16,11 +16,6 @@ runs: cd package/native-package/ yarn shell: bash - - name: Install & Build the Expo Package - run: | - cd package/expo-package/ - yarn - shell: bash - name: Install & Build the Sample App working-directory: examples/SampleApp run: yarn diff --git a/.github/workflows/check-pr.yml b/.github/workflows/check-pr.yml index 4e5656143d..98113381d5 100644 --- a/.github/workflows/check-pr.yml +++ b/.github/workflows/check-pr.yml @@ -10,7 +10,7 @@ on: jobs: check_pr: - runs-on: ubuntu-latest + runs-on: public strategy: matrix: node-version: [24.x] diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7e6f1a7720..d782fa42e0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,16 +4,16 @@ on: push: branches: - main - - develop +# - develop permissions: - id-token: write # for OIDC / npm provenance if you use it - actions: write # if you dispatch other workflows - contents: write # commits / tags / merge-back + id-token: write # for OIDC / npm provenance if you use it + actions: write # if you dispatch other workflows + contents: write # commits / tags / merge-back jobs: publish: - runs-on: ubuntu-latest + runs-on: public strategy: matrix: node-version: [24.x] @@ -21,7 +21,7 @@ jobs: steps: - uses: actions/checkout@v2 with: - fetch-depth: "0" + fetch-depth: '0' - name: Fetch tags run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* @@ -30,7 +30,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - registry-url: "https://registry.npmjs.org" + registry-url: 'https://registry.npmjs.org' - name: Prepare git run: | diff --git a/.github/workflows/sample-distribution.yml b/.github/workflows/sample-distribution.yml index 981c8bb2c6..5280534365 100644 --- a/.github/workflows/sample-distribution.yml +++ b/.github/workflows/sample-distribution.yml @@ -17,7 +17,7 @@ jobs: runs-on: [macos-15] strategy: matrix: - node-version: [ 24.x ] + node-version: [24.x] steps: - name: Connect Bot uses: webfactory/ssh-agent@v0.7.0 @@ -30,7 +30,7 @@ jobs: - uses: actions/checkout@v3 - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '16.4.0' # Update as needed + xcode-version: '26.2' # Update as needed - uses: ./.github/actions/ruby-cache - name: Install && Build - SDK and Sample App uses: ./.github/actions/install-and-build-sdk @@ -51,18 +51,18 @@ jobs: bundle exec pod install - name: Build and release Testflight QA working-directory: examples/SampleApp - run: bundle exec fastlane deploy_to_testflight_qa deploy:${{ github.ref == 'refs/heads/develop' }}; + run: bundle exec fastlane ios deploy_to_testflight_qa deploy:${{ github.ref == 'refs/heads/develop' }}; env: MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }} APPSTORE_API_KEY: ${{ secrets.APPSTORE_API_KEY }} - build_and_deploy_android_s3: + build_and_deploy_android_firebase: name: Build SampleApp Android and Deploy-${{ github.ref == 'refs/heads/develop' }} - runs-on: ubuntu-latest + runs-on: public strategy: matrix: - node-version: [ 24.x ] + node-version: [24.x] steps: - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4 @@ -74,26 +74,15 @@ jobs: distribution: 'zulu' java-version: '17' check-latest: true + - name: Setup Android SDK + uses: amyu/setup-android@v5 + + - uses: ./.github/actions/ruby-cache - name: Install && Build - SDK and Sample App uses: ./.github/actions/install-and-build-sdk - - name: Build + - name: Build and deploy Android Firebase working-directory: examples/SampleApp - run: | - mkdir android/app/src/main/assets - mkdir tmp - yarn react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest tmp - cd android - rm -rf $HOME/.gradle/caches/ && ./gradlew assembleRelease - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 - if: ${{ github.ref == 'refs/heads/develop' }} - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: us-east-1 - - name: Upload APK - if: ${{ github.ref == 'refs/heads/develop' }} - # https://getstream.io/downloads/rn-sample-app.apk - run: | - cp examples/SampleApp/android/app/build/outputs/apk/release/app-release.apk rn-sample-app.apk - aws s3 cp rn-sample-app.apk s3://${{ secrets.AWS_S3_BUCKET }} --sse AES256 + run: bundle exec fastlane android firebase_build_and_upload deploy:${{ github.ref == 'refs/heads/develop' }}; + env: + ANDROID_FIREBASE_APP_ID: ${{ secrets.ANDROID_FIREBASE_APP_ID }} + FIREBASE_CREDENTIALS_JSON: ${{ secrets.FIREBASE_CREDENTIALS_JSON }} diff --git a/README.md b/README.md index a93c62eca5..973ffc78bb 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ [![NPM](https://img.shields.io/npm/v/stream-chat-react-native.svg)](https://www.npmjs.com/package/stream-chat-react-native) [![Build Status](https://github.com/GetStream/stream-chat-react-native/actions/workflows/release.yml/badge.svg)](https://github.com/GetStream/stream-chat-react-native/actions) [![Component Reference](https://img.shields.io/badge/docs-component%20reference-blue.svg)](https://getstream.io/chat/docs/sdk/reactnative) -![JS Bundle Size](https://img.shields.io/badge/js_bundle_size-302%20KB-blue) +![JS Bundle Size](https://img.shields.io/badge/js_bundle_size-304%20KB-blue) diff --git a/examples/SampleApp/.gitignore b/examples/SampleApp/.gitignore index 1f3e68a22e..7388cefebf 100644 --- a/examples/SampleApp/.gitignore +++ b/examples/SampleApp/.gitignore @@ -73,3 +73,7 @@ yarn-error.log !.yarn/releases !.yarn/sdks !.yarn/versions + +# Credentials +credentials/ +app-build/ diff --git a/examples/SampleApp/App.tsx b/examples/SampleApp/App.tsx index 771d8db352..60f518b389 100644 --- a/examples/SampleApp/App.tsx +++ b/examples/SampleApp/App.tsx @@ -60,6 +60,7 @@ import { Toast } from './src/components/ToastComponent/Toast'; import { useClientNotificationsToastHandler } from './src/hooks/useClientNotificationsToastHandler'; import AsyncStore from './src/utils/AsyncStore.ts'; import { + MessageInputFloatingConfigItem, MessageListImplementationConfigItem, MessageListModeConfigItem, MessageListPruningConfigItem, @@ -94,6 +95,7 @@ notifee.onBackgroundEvent(async ({ detail, type }) => { const Drawer = createDrawerNavigator(); const Stack = createNativeStackNavigator(); const UserSelectorStack = createNativeStackNavigator(); + const App = () => { const { chatClient, isConnecting, loginUser, logout, switchUser } = useChatClient(); const [messageListImplementation, setMessageListImplementation] = useState< @@ -105,8 +107,12 @@ const App = () => { const [messageListPruning, setMessageListPruning] = useState< MessageListPruningConfigItem['value'] | undefined >(undefined); + const [messageInputFloating, setMessageInputFloating] = useState< + MessageInputFloatingConfigItem['value'] | undefined + >(undefined); const colorScheme = useColorScheme(); const streamChatTheme = useStreamChatTheme(); + const streami18n = new Streami18n(); useEffect(() => { const messaging = getMessaging(); @@ -159,6 +165,10 @@ const App = () => { '@stream-rn-sampleapp-messagelist-pruning', { value: undefined }, ); + const messageInputFloatingStoredValue = await AsyncStore.getItem( + '@stream-rn-sampleapp-messageinput-floating', + { value: false }, + ); setMessageListImplementation( messageListImplementationStoredValue?.id as MessageListImplementationConfigItem['id'], ); @@ -166,6 +176,9 @@ const App = () => { setMessageListPruning( messageListPruningStoredValue?.value as MessageListPruningConfigItem['value'], ); + setMessageInputFloating( + messageInputFloatingStoredValue?.value as MessageInputFloatingConfigItem['value'], + ); }; getMessageListConfig(); return () => { @@ -183,6 +196,9 @@ const App = () => { drafts: { enabled: true, }, + linkPreviews: { + enabled: true, + } }); setupCommandUIMiddlewares(composer); @@ -209,39 +225,44 @@ const App = () => { backgroundColor: streamChatTheme.colors?.white_snow || '#FCFCFC', }} > - - - - {isConnecting && !chatClient ? ( - - ) : chatClient ? ( - - ) : ( - - )} - - - + + + + + + {isConnecting && !chatClient ? ( + + ) : chatClient ? ( + + ) : ( + + )} + + + + + ); }; @@ -265,32 +286,26 @@ const isMessageAIGenerated = (message: LocalMessage) => !!message.ai_generated; const DrawerNavigatorWrapper: React.FC<{ chatClient: StreamChat; -}> = ({ chatClient }) => { - const streamChatTheme = useStreamChatTheme(); - const streami18n = new Streami18n(); - + i18nInstance: Streami18n; +}> = ({ chatClient, i18nInstance }) => { return ( - - - - - - - - - - - - - - + + + + + + + + + + ); }; diff --git a/examples/SampleApp/Gemfile.lock b/examples/SampleApp/Gemfile.lock index 83479ab205..e16cd2e14a 100644 --- a/examples/SampleApp/Gemfile.lock +++ b/examples/SampleApp/Gemfile.lock @@ -1,48 +1,56 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.7) + CFPropertyList (3.0.8) + abbrev (0.1.2) + activesupport (7.2.3) base64 - nkf - rexml - activesupport (7.0.8.4) - concurrent-ruby (~> 1.0, >= 1.0.2) + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) - tzinfo (~> 2.0) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + addressable (2.8.8) + public_suffix (>= 2.0.2, < 8.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) artifactory (3.0.17) - ast (2.4.2) + ast (2.4.3) atomos (0.1.3) - aws-eventstream (1.3.0) - aws-partitions (1.966.0) - aws-sdk-core (3.201.5) + aws-eventstream (1.4.0) + aws-partitions (1.1209.0) + aws-sdk-core (3.241.4) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) + aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) + base64 + bigdecimal jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) + logger + aws-sdk-kms (1.121.0) + aws-sdk-core (~> 3, >= 3.241.4) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.158.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-s3 (1.212.0) + aws-sdk-core (~> 3, >= 3.241.4) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.9.1) + aws-sigv4 (1.12.1) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) base64 (0.2.0) - benchmark (0.3.0) - bigdecimal (3.1.5) + benchmark (0.5.0) + bigdecimal (4.0.1) claide (1.1.0) - cocoapods (1.14.3) + cocoapods (1.15.2) addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.14.3) + cocoapods-core (= 1.15.2) cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 2.1, < 3.0) cocoapods-plugins (>= 1.0.0, < 2.0) @@ -57,7 +65,7 @@ GEM nap (~> 1.0) ruby-macho (>= 2.3.0, < 3.0) xcodeproj (>= 1.23.0, < 2.0) - cocoapods-core (1.14.3) + cocoapods-core (1.15.2) activesupport (>= 5.0, < 8) addressable (~> 2.8) algoliasearch (~> 1.0) @@ -80,18 +88,22 @@ GEM colored2 (3.1.2) commander (4.6.0) highline (~> 2.0.0) - concurrent-ruby (1.2.3) + concurrent-ruby (1.3.3) + connection_pool (3.0.2) + csv (3.3.5) declarative (0.0.20) - digest-crc (0.6.5) + digest-crc (0.7.0) rake (>= 12.0.0, < 14.0.0) domain_name (0.6.20240107) dotenv (2.8.1) + drb (2.2.3) emoji_regex (3.2.3) escape (0.0.4) - ethon (0.16.0) + ethon (0.18.0) ffi (>= 1.15.0) - excon (0.111.0) - faraday (1.10.3) + logger + excon (0.112.0) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -103,32 +115,36 @@ GEM faraday-rack (~> 1.0) faraday-retry (~> 1.0) ruby2_keywords (>= 0.0.4) - faraday-cookie_jar (0.0.7) + faraday-cookie_jar (0.0.8) faraday (>= 0.8.0) - http-cookie (~> 1.0.0) + http-cookie (>= 1.0.0) faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) + faraday-em_synchrony (1.0.1) faraday-excon (1.1.0) faraday-httpclient (1.0.1) - faraday-multipart (1.0.4) - multipart-post (~> 2) + faraday-multipart (1.2.0) + multipart-post (~> 2.0) faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - faraday_middleware (1.2.0) + faraday_middleware (1.2.1) faraday (~> 1.0) - fastimage (2.3.1) - fastlane (2.222.0) + fastimage (2.4.0) + fastlane (2.231.1) CFPropertyList (>= 2.3, < 4.0.0) + abbrev (~> 0.1.2) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) aws-sdk-s3 (~> 1.0) babosa (>= 1.0.3, < 2.0.0) - bundler (>= 1.12.0, < 3.0.0) + base64 (~> 0.2.0) + benchmark (>= 0.1.0) + bundler (>= 1.17.3, < 5.0.0) colored (~> 1.2) commander (~> 4.6) + csv (~> 3.3) dotenv (>= 2.1.1, < 3.0.0) emoji_regex (>= 0.1, < 4.0) excon (>= 0.71.0, < 1.0.0) @@ -136,6 +152,7 @@ GEM faraday-cookie_jar (~> 0.0.6) faraday_middleware (~> 1.0) fastimage (>= 2.1.0, < 3.0.0) + fastlane-sirp (>= 1.0.0) gh_inspector (>= 1.1.2, < 2.0.0) google-apis-androidpublisher_v3 (~> 0.3) google-apis-playcustomapp_v1 (~> 0.1) @@ -145,10 +162,14 @@ GEM http-cookie (~> 1.0.5) json (< 3.0.0) jwt (>= 2.1.0, < 3) + logger (>= 1.6, < 2.0) mini_magick (>= 4.9.4, < 5.0.0) multipart-post (>= 2.0.0, < 3.0.0) + mutex_m (~> 0.3.0) naturally (~> 2.2) + nkf (~> 0.2.0) optparse (>= 0.1.1, < 1.0.0) + ostruct (>= 0.1.0) plist (>= 3.1.0, < 4.0.0) rubyzip (>= 2.0.0, < 3.0.0) security (= 0.1.5) @@ -159,15 +180,17 @@ GEM tty-spinner (>= 0.8.0, < 1.0.0) word_wrap (~> 1.0.0) xcodeproj (>= 1.13.0, < 2.0.0) - xcpretty (~> 0.3.0) + xcpretty (~> 0.4.1) xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) - fastlane-plugin-firebase_app_distribution (0.9.1) + fastlane-plugin-firebase_app_distribution (0.10.1) google-apis-firebaseappdistribution_v1 (~> 0.3.0) google-apis-firebaseappdistribution_v1alpha (~> 0.2.0) fastlane-plugin-load_json (0.0.1) fastlane-plugin-stream_actions (0.3.73) xctest_list (= 1.2.1) - ffi (1.17.0) + fastlane-sirp (1.0.0) + sysrandom (~> 1.0) + ffi (1.17.3) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) @@ -191,12 +214,12 @@ GEM google-apis-core (>= 0.11.0, < 2.a) google-apis-storage_v1 (0.31.0) google-apis-core (>= 0.11.0, < 2.a) - google-cloud-core (1.7.1) + google-cloud-core (1.8.0) google-cloud-env (>= 1.0, < 3.a) google-cloud-errors (~> 1.0) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) - google-cloud-errors (1.4.0) + google-cloud-errors (1.5.0) google-cloud-storage (1.47.0) addressable (~> 2.8) digest-crc (~> 0.4) @@ -212,81 +235,88 @@ GEM os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) highline (2.0.3) - http-cookie (1.0.7) + http-cookie (1.0.8) domain_name (~> 0.5) - httpclient (2.8.3) - i18n (1.14.5) + httpclient (2.9.0) + mutex_m + i18n (1.14.8) concurrent-ruby (~> 1.0) jmespath (1.6.2) - json (2.7.2) - jwt (2.8.2) + json (2.18.0) + jwt (2.10.2) base64 - language_server-protocol (3.17.0.3) - logger (1.6.0) + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) + logger (1.7.0) mini_magick (4.13.2) mini_mime (1.1.5) - minitest (5.25.1) + minitest (6.0.1) + prism (~> 1.5) molinillo (0.8.0) - multi_json (1.15.0) + multi_json (1.19.1) multipart-post (2.4.1) - mutex_m (0.2.0) + mutex_m (0.3.0) nanaimo (0.3.0) nap (1.1.0) - naturally (2.2.1) + naturally (2.3.0) netrc (0.11.0) nkf (0.2.0) - optparse (0.5.0) + optparse (0.8.1) os (1.1.4) - parallel (1.26.3) - parser (3.3.4.2) + ostruct (0.6.3) + parallel (1.27.0) + parser (3.3.10.1) ast (~> 2.4.1) racc - plist (3.7.1) + plist (3.7.2) + prism (1.8.0) public_suffix (4.0.7) racc (1.8.1) rainbow (3.1.1) - rake (13.2.1) - regexp_parser (2.9.2) + rake (13.3.1) + regexp_parser (2.11.3) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.3.5) - strscan - rouge (2.0.7) - rubocop (1.65.1) + rexml (3.4.4) + rouge (3.28.0) + rubocop (1.84.0) json (~> 2.3) - language_server-protocol (>= 3.17.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 2.4, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.31.1, < 2.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.49.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.32.1) - parser (>= 3.3.1.0) - rubocop-performance (1.21.1) - rubocop (>= 1.48.1, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.49.0) + parser (>= 3.3.7.2) + prism (~> 1.7) + rubocop-performance (1.26.1) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.47.1, < 2.0) rubocop-require_tools (0.1.2) rubocop (>= 0.49.1) ruby-macho (2.5.1) ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) - rubyzip (2.3.2) + rubyzip (2.4.1) + securerandom (0.4.1) security (0.1.5) - signet (0.19.0) + signet (0.21.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) - jwt (>= 1.5, < 3.0) + jwt (>= 1.5, < 4.0) multi_json (~> 1.10) simctl (1.6.10) CFPropertyList naturally - strscan (3.1.0) + sysrandom (1.0.5) terminal-notifier (2.0.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) @@ -300,17 +330,17 @@ GEM tzinfo (2.0.6) concurrent-ruby (~> 1.0) uber (0.1.0) - unicode-display_width (2.5.0) + unicode-display_width (2.6.0) word_wrap (1.0.0) - xcodeproj (1.25.0) + xcodeproj (1.25.1) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) - rexml (>= 3.3.2, < 4.0) - xcpretty (0.3.0) - rouge (~> 2.0.7) + rexml (>= 3.3.6, < 4.0) + xcpretty (0.4.1) + rouge (~> 3.28.0) xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) xctest_list (1.2.1) diff --git a/examples/SampleApp/android/app/build.gradle b/examples/SampleApp/android/app/build.gradle index e1c59595a5..39b1680d7a 100644 --- a/examples/SampleApp/android/app/build.gradle +++ b/examples/SampleApp/android/app/build.gradle @@ -79,15 +79,15 @@ android { buildToolsVersion rootProject.ext.buildToolsVersion compileSdk rootProject.ext.compileSdkVersion - namespace "com.sampleapp" + namespace "io.getstream.reactnative.sampleapp" defaultConfig { - applicationId "com.sampleapp" + applicationId "io.getstream.reactnative.sampleapp" minSdkVersion rootProject.ext.minSdkVersion multiDexEnabled true targetSdkVersion rootProject.ext.targetSdkVersion vectorDrawables.useSupportLibrary = true - versionCode 22 + versionCode 1 versionName "0.0.22" } diff --git a/examples/SampleApp/android/app/google-services.json b/examples/SampleApp/android/app/google-services.json index 1d5c701c81..ec09246afb 100644 --- a/examples/SampleApp/android/app/google-services.json +++ b/examples/SampleApp/android/app/google-services.json @@ -62,7 +62,7 @@ "client_info": { "mobilesdk_app_id": "1:674907137625:android:5effa1cd0fef9003d7f348", "android_client_info": { - "package_name": "com.sampleapp" + "package_name": "io.getstream.reactnative.sampleapp" } }, "oauth_client": [ @@ -98,7 +98,7 @@ "client_info": { "mobilesdk_app_id": "1:674907137625:android:07c76802bbfd5654d7f348", "android_client_info": { - "package_name": "com.sampleapp.rnpushtest" + "package_name": "io.getstream.reactnative.sampleapp.rnpushtest" } }, "oauth_client": [ diff --git a/examples/SampleApp/android/app/src/androidTest/java/com/sampleapp/DetoxTest.java b/examples/SampleApp/android/app/src/androidTest/java/com/sampleapp/DetoxTest.java index df226139a6..9557ec76a9 100644 --- a/examples/SampleApp/android/app/src/androidTest/java/com/sampleapp/DetoxTest.java +++ b/examples/SampleApp/android/app/src/androidTest/java/com/sampleapp/DetoxTest.java @@ -1,4 +1,4 @@ -package com.sampleapp; +package io.getstream.reactnative.sampleapp; import com.wix.detox.Detox; import com.wix.detox.config.DetoxConfig; @@ -24,7 +24,7 @@ public void runDetoxTests() { DetoxConfig detoxConfig = new DetoxConfig(); detoxConfig.idlePolicyConfig.masterTimeoutSec = 90; detoxConfig.idlePolicyConfig.idleResourceTimeoutSec = 60; - detoxConfig.rnContextLoadTimeoutSec = (com.sampleapp.BuildConfig.DEBUG ? 180 : 60); + detoxConfig.rnContextLoadTimeoutSec = (io.getstream.reactnative.sampleapp.BuildConfig.DEBUG ? 180 : 60); Detox.runTests(mActivityRule, detoxConfig); } diff --git a/examples/SampleApp/android/app/src/main/java/com/sampleapp/MainActivity.kt b/examples/SampleApp/android/app/src/main/java/com/sampleapp/MainActivity.kt index f3ca98b78b..c811c58912 100644 --- a/examples/SampleApp/android/app/src/main/java/com/sampleapp/MainActivity.kt +++ b/examples/SampleApp/android/app/src/main/java/com/sampleapp/MainActivity.kt @@ -1,52 +1,49 @@ -package com.sampleapp +package io.getstream.reactnative.sampleapp -import com.facebook.react.ReactActivity -import com.facebook.react.ReactActivityDelegate -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled -import com.facebook.react.defaults.DefaultReactActivityDelegate - -import android.os.Bundle import android.os.Build +import android.os.Bundle import android.view.View +import androidx.core.graphics.Insets import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding +import com.facebook.react.ReactActivity +import com.facebook.react.ReactActivityDelegate +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled +import com.facebook.react.defaults.DefaultReactActivityDelegate class MainActivity : ReactActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(null) - - if (Build.VERSION.SDK_INT >= 35) { - val rootView = findViewById(android.R.id.content) - - - ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, insets -> - val bars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() - or WindowInsetsCompat.Type.displayCutout() - or WindowInsetsCompat.Type.ime() // adding the ime's height - ) - rootView.updatePadding( - left = bars.left, - top = bars.top, - right = bars.right, - bottom = bars.bottom - ) - WindowInsetsCompat.CONSUMED - } - } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(null) + + if (Build.VERSION.SDK_INT >= 35) { + val rootView = findViewById(android.R.id.content) + + val initial = Insets.of( + rootView.paddingLeft, + rootView.paddingTop, + rootView.paddingRight, + rootView.paddingBottom + ) + + ViewCompat.setOnApplyWindowInsetsListener(rootView) { v, insets -> + val ime = insets.getInsets(WindowInsetsCompat.Type.ime()) + + v.updatePadding( + left = initial.left, + top = initial.top, + right = initial.right, + bottom = initial.bottom + ime.bottom + ) + + insets } - /** - * Returns the name of the main component registered from JavaScript. This is used to schedule - * rendering of the component. - */ - override fun getMainComponentName(): String = "SampleApp" - - /** - * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] - * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] - */ - override fun createReactActivityDelegate(): ReactActivityDelegate = - DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) + } + } + + override fun getMainComponentName(): String = "SampleApp" + + override fun createReactActivityDelegate(): ReactActivityDelegate = + DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) } diff --git a/examples/SampleApp/android/app/src/main/java/com/sampleapp/MainApplication.kt b/examples/SampleApp/android/app/src/main/java/com/sampleapp/MainApplication.kt index 69b92b8443..84c3f9a65e 100644 --- a/examples/SampleApp/android/app/src/main/java/com/sampleapp/MainApplication.kt +++ b/examples/SampleApp/android/app/src/main/java/com/sampleapp/MainApplication.kt @@ -1,4 +1,4 @@ -package com.sampleapp +package io.getstream.reactnative.sampleapp import android.app.Application import com.facebook.react.PackageList diff --git a/examples/SampleApp/fastlane/Fastfile b/examples/SampleApp/fastlane/Fastfile index bf10e28713..0efb03543f 100644 --- a/examples/SampleApp/fastlane/Fastfile +++ b/examples/SampleApp/fastlane/Fastfile @@ -1,13 +1,23 @@ -default_platform(:ios) skip_docs +# Common Configuration github_repo = ENV['GITHUB_REPOSITORY'] || 'GetStream/stream-chat-react-native' -bundle_id = 'io.getstream.reactnative.SampleApp' -xcode_project = 'ios/SampleApp.xcodeproj' -xcode_workspace = 'ios/SampleApp.xcworkspace' root_path = File.absolute_path('../../../') sdk_size_ext = 'KB' @force_check = false +build_output_directory = "./app-build" + +# iOS Platform Configuration +bundle_id = 'io.getstream.reactnative.SampleApp' +xcode_project = 'ios/SampleApp.xcodeproj' +xcode_workspace = 'ios/SampleApp.xcworkspace' +output_ipa_name = "reactnativesampleapp.ipa" + +# Android Platform Configuration +package_name = 'io.getstream.reactnative.sampleapp' +output_apk_name = "reactnativesampleapp.apk" +apk_path = "#{build_output_directory}/#{output_apk_name}" + before_all do if is_ci @@ -20,82 +30,140 @@ end ###### iOS lanes ###### ####################### -lane :deploy_to_testflight_qa do |options| - match_me +platform :ios do + private_lane :latest_appstore_version_code do |options| + livestates = [true, false] + version_codes = [] + livestates.each do |livestate| + vc = app_store_build_number( + live: livestate, + app_identifier: bundle_id + ) + rescue StandardError + puts("No app store build found for liveState: #{livestate} bundle_id: #{bundle_id}") + else + version_codes.append(vc) + end + version_codes.max + end - settings_to_override = { - BUNDLE_IDENTIFIER: bundle_id, - PROVISIONING_PROFILE_SPECIFIER: "match AppStore #{bundle_id}" - } + lane :deploy_to_testflight_qa do |options| + match_me - increment_version_number( - version_number: load_json(json_path: './package.json')['version'], - xcodeproj: xcode_project - ) + deploy = options.fetch(:deploy, false) - current_build_number = app_store_build_number( - api_key: appstore_api_key, - live: false, - app_identifier: bundle_id - ) + UI.message("Deploying to Testflight: #{deploy}") - increment_build_number( - build_number: current_build_number + 1, - xcodeproj: xcode_project - ) + if deploy + increment_version_number( + version_number: load_json(json_path: './package.json')['version'], + xcodeproj: xcode_project + ) - gym( - workspace: xcode_workspace, - scheme: 'SampleApp', - export_method: 'app-store', - export_options: './fastlane/testflight_gym_export_options.plist', - silent: true, - clean: true, - xcargs: settings_to_override, - include_symbols: true, - output_directory: './dist' - ) + current_build_number = latest_appstore_version_code + + puts("Current build number: #{current_build_number}") + + increment_build_number( + build_number: current_build_number + 1, + xcodeproj: xcode_project + ) + end + + settings_to_override = { + BUNDLE_IDENTIFIER: bundle_id, + PROVISIONING_PROFILE_SPECIFIER: "match AppStore #{bundle_id}" + } + + gym( + workspace: xcode_workspace, + scheme: 'SampleApp', + export_method: 'app-store', + export_options: './fastlane/testflight_gym_export_options.plist', + silent: true, + clean: true, + xcargs: settings_to_override, + include_symbols: true, + output_directory: build_output_directory, + output_name: File.basename(output_ipa_name, '.ipa') + ) - if options[:deploy] - begin + if deploy upload_to_testflight( + api_key: appstore_api_key, groups: ['Testers'], changelog: 'Lots of amazing new features to test out!', - reject_build_waiting_for_review: false + reject_build_waiting_for_review: true, + ipa: "#{build_output_directory}/#{output_ipa_name}", + skip_waiting_for_build_processing: true ) - rescue StandardError => e - if e.message.include?('Another build is in review') - UI.important('Another build is already in beta review. Skipping beta review submission') - else - UI.user_error!(e) - end + else + UI.message("Skipping Testflight upload! (deploy: #{deploy})") end end -end -private_lane :appstore_api_key do - @appstore_api_key ||= app_store_connect_api_key( - key_id: 'MT3PRT8TB7', - issuer_id: '69a6de96-0738-47e3-e053-5b8c7c11a4d1', - key_content: ENV.fetch('APPSTORE_API_KEY', nil), - in_house: false - ) -end + private_lane :appstore_api_key do + @appstore_api_key ||= app_store_connect_api_key( + key_id: 'MT3PRT8TB7', + issuer_id: '69a6de96-0738-47e3-e053-5b8c7c11a4d1', + key_content: ENV.fetch('APPSTORE_API_KEY', nil), + in_house: false + ) + end -desc "If `readonly: true` (by default), installs all Certs and Profiles necessary for development and ad-hoc.\nIf `readonly: false`, recreates all Profiles necessary for development and ad-hoc, updates them locally and remotely." -lane :match_me do |options| - custom_match( - api_key: appstore_api_key, - app_identifier: [bundle_id], - readonly: options[:readonly], - register_device: options[:register_device] - ) + desc "If `readonly: true` (by default), installs all Certs and Profiles necessary for development and ad-hoc.\nIf `readonly: false`, recreates all Profiles necessary for development and ad-hoc, updates them locally and remotely." + lane :match_me do |options| + custom_match( + api_key: appstore_api_key, + app_identifier: [bundle_id], + readonly: options[:readonly], + register_device: options[:register_device] + ) + end end ########################### ###### Android lanes ###### ########################### +platform :android do + lane :firebase_build_and_upload do |options| + deploy = options.fetch(:deploy, false) + + UI.message("Deploying to Firebase: #{deploy}") + + # Clean + gradle( + task: "clean", + project_dir: "./android" + ) + + # Build the AAB + gradle( + task: "assemble", + build_type: "Release", + project_dir: "./android" + ) + + Dir.chdir('..') do + sh("mkdir -p #{build_output_directory} && mv -f #{lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]} #{apk_path}") + end + + if deploy + # Upload to Firebase App Distribution + firebase_app_distribution( + app: ENV.fetch('ANDROID_FIREBASE_APP_ID', nil), + service_credentials_json_data: ENV.fetch('FIREBASE_CREDENTIALS_JSON', nil), + android_artifact_path: apk_path, + android_artifact_type: "APK", + groups: "stream-testers" + ) + else + UI.message("Skipping Firebase upload! (deploy: #{deploy})") + end + end +end + ########################## ###### Common lanes ###### ########################## diff --git a/examples/SampleApp/ios/Podfile.lock b/examples/SampleApp/ios/Podfile.lock index 171a4084a1..61fc07eb9c 100644 --- a/examples/SampleApp/ios/Podfile.lock +++ b/examples/SampleApp/ios/Podfile.lock @@ -2734,7 +2734,7 @@ PODS: - FirebaseCoreExtension - React-Core - RNFBApp - - RNGestureHandler (2.26.0): + - RNGestureHandler (2.30.0): - boost - DoubleConversion - fast_float @@ -2798,7 +2798,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - RNReanimated (4.0.1): + - RNReanimated (4.2.1): - boost - DoubleConversion - fast_float @@ -2825,11 +2825,11 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNReanimated/reanimated (= 4.0.1) + - RNReanimated/reanimated (= 4.2.1) - RNWorklets - SocketRocket - Yoga - - RNReanimated/reanimated (4.0.1): + - RNReanimated/reanimated (4.2.1): - boost - DoubleConversion - fast_float @@ -2856,11 +2856,11 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNReanimated/reanimated/apple (= 4.0.1) + - RNReanimated/reanimated/apple (= 4.2.1) - RNWorklets - SocketRocket - Yoga - - RNReanimated/reanimated/apple (4.0.1): + - RNReanimated/reanimated/apple (4.2.1): - boost - DoubleConversion - fast_float @@ -3039,7 +3039,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - RNWorklets (0.4.1): + - RNWorklets (0.7.2): - boost - DoubleConversion - fast_float @@ -3066,10 +3066,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNWorklets/worklets (= 0.4.1) + - RNWorklets/worklets (= 0.7.2) - SocketRocket - Yoga - - RNWorklets/worklets (0.4.1): + - RNWorklets/worklets (0.7.2): - boost - DoubleConversion - fast_float @@ -3096,10 +3096,10 @@ PODS: - ReactCodegen - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - - RNWorklets/worklets/apple (= 0.4.1) + - RNWorklets/worklets/apple (= 0.7.2) - SocketRocket - Yoga - - RNWorklets/worklets/apple (0.4.1): + - RNWorklets/worklets/apple (0.7.2): - boost - DoubleConversion - fast_float @@ -3165,6 +3165,65 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga + - Teleport (0.5.4): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Teleport/common (= 0.5.4) + - Yoga + - Teleport/common (0.5.4): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga - Yoga (0.0.0) DEPENDENCIES: @@ -3269,6 +3328,7 @@ DEPENDENCIES: - RNWorklets (from `../node_modules/react-native-worklets`) - SocketRocket (~> 0.7.1) - stream-chat-react-native (from `../node_modules/stream-chat-react-native`) + - Teleport (from `../node_modules/react-native-teleport`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) SPEC REPOS: @@ -3490,6 +3550,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-worklets" stream-chat-react-native: :path: "../node_modules/stream-chat-react-native" + Teleport: + :path: "../node_modules/react-native-teleport" Yoga: :path: "../node_modules/react-native/ReactCommon/yoga" @@ -3517,9 +3579,9 @@ SPEC CHECKSUMS: hermes-engine: bbc1152da7d2d40f9e59c28acc6576fcf5d28e2a libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 - NitroModules: 8849240f6ee6d3b295514112437e3b09e855cb67 - NitroSound: fe46960c89410e62e05e9a709d8bf28a8202d1b3 - op-sqlite: b9a4028bef60145d7b98fbbc4341c83420cdcfd5 + NitroModules: 01ae20fc1e8fc9a3b088ab8ed06ab92527a04f0d + NitroSound: 347b6a21f2e7d5601c92ef81cec7836f8f8be44c + op-sqlite: a7e46cfdaebeef219fd0e939332967af9fe6d406 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 @@ -3529,91 +3591,92 @@ SPEC CHECKSUMS: React: e7a4655b09d0e17e54be188cc34c2f3e2087318a React-callinvoker: 62192daaa2f30c3321fc531e4f776f7b09cf892b React-Codegen: 4b8b4817cea7a54b83851d4c1f91f79aa73de30a - React-Core: c400b068fdb6172177f3b3fae00c10d1077244d7 - React-CoreModules: 8e911a5a504b45824374eec240a78de7a6db8ca2 - React-cxxreact: 06a91f55ac5f842219d6ca47e0f77187a5b5f4ac + React-Core: b23cdaaa9d76389d958c06af3c57aa6ad611c542 + React-CoreModules: 8e0f562e5695991e455abbebe1e968af71d52553 + React-cxxreact: 6ccbe0cc2c652b29409b14b23cfb3cd74e084691 React-debug: ab52e07f7148480ea61c5e9b68408d749c6e2b8f - React-defaultsnativemodule: fab7bf1b5ce1f3ed252aa4e949ec48a8df67d624 - React-domnativemodule: 735fa5238cceebceeecc18f9f4321016461178cf - React-Fabric: c75719fc8818049c3cf9071f0619af988b020c9f - React-FabricComponents: 74a381cc0dfaf2ec3ee29f39ef8533a7fd864b83 - React-FabricImage: 9a3ff143b1ac42e077c0b33ec790f3674ace5783 - React-featureflags: e1eca0727563a61c919131d57bbd0019c3bdb0f0 - React-featureflagsnativemodule: 692211fd48283b2ddee3817767021010e2f1788e - React-graphics: 759b02bde621f12426c1dc1ae2498aaa6b441cd7 - React-hermes: b6e33fcd21aa7523dc76e62acd7a547e68c28a5b - React-idlecallbacksnativemodule: 731552efc0815fc9d65a6931da55e722b1b3a397 - React-ImageManager: 2c510a480f2c358f56a82df823c66d5700949c96 - React-jserrorhandler: 411e18cbdcbdf546f8f8707091faeb00703527c1 - React-jsi: 3fde19aaf675c0607a0824c4d6002a4943820fd9 - React-jsiexecutor: 4f898228240cf261a02568e985dfa7e1d7ad1dfb - React-jsinspector: 2f0751e6a4fb840f4ed325384d0795a9e9afaf39 - React-jsinspectorcdp: 71c48944d97f5f20e8e144e419ddf04ffa931e93 - React-jsinspectornetwork: 102f347669b278644cc9bb4ebf2f90422bd5ccef - React-jsinspectortracing: 0f6f2ec7f3faa9dc73d591b24b460141612515eb - React-jsitooling: b557f8e12efdaf16997e43b0d07dbd8a3fce3a5b - React-jsitracing: f9a77561d99c0cd053a8230bab4829b100903949 - React-logger: ea80169d826e0cd112fa4d68f58b2b3b968f1ecb - React-Mapbuffer: 230c34b1cabd1c4815726c711b9df221c3d3fbfb - React-microtasksnativemodule: 29d62f132e4aba34ebb7f2b936dde754eb08971b - react-native-blob-util: cbd6b292d0f558f09dce85e6afe68074cd031f3e - react-native-cameraroll: 00057cc0ec595fdbdf282ecfb931d484b240565f - react-native-document-picker: c5fa18e9fc47b34cfbab3b0a4447d0df918a5621 - react-native-geolocation: eb39c815c9b58ddc3efb552cafdd4b035e4cf682 - react-native-image-picker: e479ec8884df9d99a62c1f53f2307055ad43ea85 - react-native-maps: ee1e65647460c3d41e778071be5eda10e3da6225 - react-native-netinfo: f0a9899081c185db1de5bb2fdc1c88c202a059ac - react-native-safe-area-context: 7fd4c2c8023da8e18eaa3424cb49d52f626debee - react-native-video: 71973843c2c9ac154c54f95a5a408fd8b041790e - React-NativeModulesApple: d061f458c3febdf0ac99b1b0faf23b7305974b25 + React-defaultsnativemodule: 291d2b0a93c399056121f4f0acc7f46d155a38ec + React-domnativemodule: c4968302e857bd422df8eec50a3cd4d078bd4ac0 + React-Fabric: 7e3ba48433b87a416052c4077d5965aff64cb8c9 + React-FabricComponents: 21de255cf52232644d12d3288cced1f0c519b25d + React-FabricImage: 15a0961a0ab34178f1c803aa0a7d28f21322ffc3 + React-featureflags: 4e5dad365d57e3c3656447dfdad790f75878d9f4 + React-featureflagsnativemodule: 5eac59389131c2b87d165dac4094b5e86067fabb + React-graphics: 2f9b3db89f156afd793da99f23782f400f58c1ee + React-hermes: cc8c77acee1406c258622cd8abbee9049f6b5761 + React-idlecallbacksnativemodule: 1d7e1f73b624926d16db956e87c4885ef485664a + React-ImageManager: 8b6066f6638fba7d4a94fbd0b39b477ea8aced58 + React-jserrorhandler: e5a4626d65b0eda9a11c43a9f14d0423d8a7080d + React-jsi: ea5c640ea63c127080f158dac7f4f393d13d415c + React-jsiexecutor: cf7920f82e46fe9a484c15c9f31e67d7179aa826 + React-jsinspector: 094e3cb99952a0024fa977fa04706e68747cba18 + React-jsinspectorcdp: dca545979146e3ecbdc999c0789dab55beecc140 + React-jsinspectornetwork: 0a105fe74b0b1a93f70409d955c8a0556dc17c59 + React-jsinspectortracing: 76088dd78a2de3ea637a860cdf39a6d9c2637d6b + React-jsitooling: a2e1e87382aae2294bc94a6e9682b9bc83da1d36 + React-jsitracing: 45827be59e673f4c54175c150891301138846906 + React-logger: 7cfc7b1ae1f8e5fe5097f9c746137cc3a8fad4ce + React-Mapbuffer: 8f620d1794c6b59a8c3862c3ae820a2e9e6c9bb0 + React-microtasksnativemodule: dcf5321c9a41659a6718df8a5f202af1577c6825 + react-native-blob-util: a511afccff6511544ebf56928e6afdf837b037a7 + react-native-cameraroll: 8c3ba9b6f511cf645778de19d5039b61d922fdfb + react-native-document-picker: b37cf6660ad9087b782faa78a1e67687fac15bfd + react-native-geolocation: b7f68b8c04e36ee669c630dbc48dd42cf93a0a41 + react-native-image-picker: 9bceb747cd6cde22a3416ffdc819d11b5b11f156 + react-native-maps: 9febd31278b35cd21e4fad2cf6fa708993be5dab + react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187 + react-native-safe-area-context: 32293dc61d1b92ccf892499ab6f8acfd609f9aef + react-native-video: 4da16bfca01a02aa2095e40683d74f2d6563207c + React-NativeModulesApple: 342e280bb9fc2aa5f61f6c257b309a86b995e12d React-oscompat: 56d6de59f9ae95cd006a1c40be2cde83bc06a4e1 - React-perflogger: 0633844e495d8b34798c9bf0cb32ce315f1d5c9f - React-performancetimeline: 53bdf62ff49a9b0c4bd4d66329fdcf28d77c1c9d + React-perflogger: 4008bd05a8b6c157b06608c0ea0b8bd5d9c5e6c9 + React-performancetimeline: 3ac316a346fe3d48801a746b754dd8f5b5146838 React-RCTActionSheet: 49138012280ec3bbb35193d8d09adb8bc61c982e - React-RCTAnimation: c7ed4a9d5a4e43c9b10f68bb43cd238c4a2e7e89 - React-RCTAppDelegate: ea2ab6f4aef1489f72025b7128d8ab645b40eafb - React-RCTBlob: c052799460b245e1fffe3d1dddea36fa88e998a0 - React-RCTFabric: fad6230640c42fb8cda29b1d0759f7a1fb8cc677 - React-RCTFBReactNativeSpec: ffb22c3ee3d359ae9245ca94af203845da9371ec - React-RCTImage: 59fc2571f4f109a77139924f5babee8f9cd639c9 - React-RCTLinking: a045cb58c08188dce6c6f4621de105114b1b16ce - React-RCTNetwork: fc7115a2f5e15ae0aa05e9a9be726817feefb482 - React-RCTRuntime: c69b86dc60dcc7297318097fc60bd8e40b050f74 - React-RCTSettings: 30d7dd7eae66290467a1e72bf42d927fa78c3884 - React-RCTText: 755d59284e66c7d33bb4f0ccc428fe69110c3e74 - React-RCTVibration: ffe019e588815df226f6f8ccdc65979f8b2bc440 + React-RCTAnimation: ebfe7c62016d4c17b56b2cab3a221908ae46288d + React-RCTAppDelegate: 0108657ba9a19f6a1cd62dcd19c2c0485b3fc251 + React-RCTBlob: 6cc309d1623f3c2679125a04a7425685b7219e6b + React-RCTFabric: 04d1cf11ee3747a699260492e319e92649d7ac88 + React-RCTFBReactNativeSpec: ff3e37e2456afc04211334e86d07bf20488df0ae + React-RCTImage: bb98a59aeed953a48be3f917b9b745b213b340ab + React-RCTLinking: d6e9795d4d75d154c1dd821fd0746cc3e05d6670 + React-RCTNetwork: 5c8a7a2dd26728323189362f149e788548ac72bc + React-RCTRuntime: 52b28e281aba881e1f94ee8b16611823b730d1c5 + React-RCTSettings: b6a02d545ce10dd936b39914b32674db6e865307 + React-RCTText: c7d9232da0e9b5082a99a617483d9164a9cd46e9 + React-RCTVibration: fe636c985c1bf25e4a5b5b4d9315a3b882468a72 React-rendererconsistency: aba18fa58a4d037004f6bed6bb201eb368016c56 - React-renderercss: c7c140782f5f21103b638abfde7b3f11d6a5fd7e - React-rendererdebug: 111519052db9610f1b93baf7350c800621df3d0c + React-renderercss: b490bd53486a6bae1e9809619735d1f2b2cabd7f + React-rendererdebug: 8db25b276b64d5a1dbf05677de0c4ff1039d5184 React-rncore: 22f344c7f9109b68c3872194b0b5081ca1aee655 - React-RuntimeApple: 30d20d804a216eb297ccc9ce1dc9e931738c03b1 - React-RuntimeCore: 6e1facd50e0b7aed1bc36b090015f33133958bb6 + React-RuntimeApple: 19bdabedda0eeb70acb71e68bfc42d61bbcbacd9 + React-RuntimeCore: 11bf03bdbd6e72857481c46d0f4eb9c19b14c754 React-runtimeexecutor: b35de9cb7f5d19c66ea9b067235f95b947697ba5 - React-RuntimeHermes: 222268a5931a23f095565c4d60e2673c04e2178a - React-runtimescheduler: aea93219348ba3069fe6c7685a84fe17d3a4b4ee + React-RuntimeHermes: d8f736d0a2d38233c7ec7bd36040eb9b0a3ccd8c + React-runtimescheduler: 0c95966d030c8ebbebddaab49630cda2889ca073 React-timing: 42e8212c479d1e956d3024be0a07f205a2e34d9d - React-utils: 0ebf25dd4eb1b5497933f4d8923b862d0fe9566f - ReactAppDependencyProvider: 6c9197c1f6643633012ab646d2bfedd1b0d25989 - ReactCodegen: f8ae44cfcb65af88f409f4b094b879591232f12c - ReactCommon: a237545f08f598b8b37dc3a9163c1c4b85817b0b - RNCAsyncStorage: afe7c3711dc256e492aa3a50dcac43eecebd0ae5 - RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8 - RNFBApp: df5caad9f64b6bc87f8a0b110e6bc411fb00a12b - RNFBMessaging: 6586f18ab3411aeb3349088c19fe54283d39e529 - RNGestureHandler: 4d36eb583264375d9f7ece09a2efd918ebc85605 - RNNotifee: 4a6ee5c7deaf00e005050052d73ee6315dff7ec9 - RNReactNativeHapticFeedback: 8bd4a2ba7c3daeb5d2acfceb8b61743f203076d0 - RNReanimated: 408767d090bcbfe3877cfbcc9dc9d29f5e878203 - RNScreens: 5ca475eb30f4362c5808f3ff4a1c7b786bcd878e - RNShare: 083f05646b9534305999cf20452bd87ca0e8b0b0 - RNSVG: fd433fe5da0f7fee8c78f5865f29ab37321dbd7f - RNWorklets: 7d34d4c80edec50bb1eec6bd034e7686db26da8e + React-utils: a185f723baa0c525c361e6c281a846d919623dbe + ReactAppDependencyProvider: 8df342c127fd0c1e30e8b9f71ff814c22414a7c0 + ReactCodegen: 4928682e20747464165effacc170019a18da953c + ReactCommon: ec1cdf708729338070f8c4ad746768a782fd9eb1 + RNCAsyncStorage: f30b3a83064e28b0fc46f1fbd3834589ed64c7b9 + RNFastImage: 462a183c4b0b6b26fdfd639e1ed6ba37536c3b87 + RNFBApp: db9c2e6d36fe579ab19b82c0a4a417ff7569db7e + RNFBMessaging: de62448d205095171915d622ed5fb45c2be5e075 + RNGestureHandler: 0c0d36c0f3c17fc755543fad1c182e1cd541f898 + RNNotifee: 5e3b271e8ea7456a36eec994085543c9adca9168 + RNReactNativeHapticFeedback: d39b9a5b334ce26f49ca6abe9eea8b3938532aee + RNReanimated: e3dd9527a9614e1c9e127018cca9486f2c13b2a9 + RNScreens: 6a2d1ff4d263d29d3d3db9f3c19aad2f99fdd162 + RNShare: 9d801eafd9ae835f51bcae6b5c8de9bf3389075b + RNSVG: bc7ccfe884848ac924d2279d9025d41b5f05cb0c + RNWorklets: 4bd2a43ae826633e5e0a92953fce2eb8265759d4 SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - stream-chat-react-native: 7a042480d22a8a87aaee6186bf2f1013af017d3a + stream-chat-react-native: f42e234640869e0eafcdd354441414ad1818b9fe + Teleport: c089481dd2bb020e3dced39b7f8849b93d1499f6 Yoga: ce248fb32065c9b00451491b06607f1c25b2f1ed PODFILE CHECKSUM: 4f662370295f8f9cee909f1a4c59a614999a209d -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.2 diff --git a/examples/SampleApp/ios/SampleApp-tvOS/Info.plist b/examples/SampleApp/ios/SampleApp-tvOS/Info.plist index a8d20ea4c7..5bbd1e0da7 100644 --- a/examples/SampleApp/ios/SampleApp-tvOS/Info.plist +++ b/examples/SampleApp/ios/SampleApp-tvOS/Info.plist @@ -50,4 +50,4 @@ UIViewControllerBasedStatusBarAppearance - \ No newline at end of file + diff --git a/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj b/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj index fd2714a5da..51996b9be4 100644 --- a/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj +++ b/examples/SampleApp/ios/SampleApp.xcodeproj/project.pbxproj @@ -497,7 +497,7 @@ CODE_SIGN_ENTITLEMENTS = SampleApp/SampleAppDebug.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 149; + CURRENT_PROJECT_VERSION = 926; DEVELOPMENT_TEAM = EHV7XZLAHA; ENABLE_BITCODE = NO; INFOPLIST_FILE = SampleApp/Info.plist; @@ -534,7 +534,7 @@ CODE_SIGN_ENTITLEMENTS = SampleApp/SampleAppRelease.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 149; + CURRENT_PROJECT_VERSION = 926; DEVELOPMENT_TEAM = EHV7XZLAHA; INFOPLIST_FILE = SampleApp/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/examples/SampleApp/ios/SampleApp/Info.plist b/examples/SampleApp/ios/SampleApp/Info.plist index ae8b0f4d7d..b072573cc4 100644 --- a/examples/SampleApp/ios/SampleApp/Info.plist +++ b/examples/SampleApp/ios/SampleApp/Info.plist @@ -59,4 +59,4 @@ UIViewControllerBasedStatusBarAppearance - \ No newline at end of file + diff --git a/examples/SampleApp/package.json b/examples/SampleApp/package.json index 5da8d63f2a..18bc840a1c 100644 --- a/examples/SampleApp/package.json +++ b/examples/SampleApp/package.json @@ -20,7 +20,11 @@ "release-next": "echo \"Skipping next release for SampleApp\"", "test:unit": "echo \"Skipping unit tests for SampleApp\"", "clean": "watchman watch-del-all && yarn cache clean && rm -rf ios/build && pod cache clean --all && rm -rf android/build && cd android && ./gradlew clean && cd -", - "clean-all": "yarn clean && rm -rf node_modules && rm -rf ios/Pods && rm -rf vendor && bundle install && yarn install && cd ios && bundle exec pod install && cd -" + "clean-all": "yarn clean && rm -rf node_modules && rm -rf ios/Pods && rm -rf vendor && bundle install && yarn install && cd ios && bundle exec pod install && cd -", + "fastlane:android-build": "bundle exec fastlane android firebase_build_and_upload deploy:false", + "fastlane:android-deploy": "bundle exec fastlane android firebase_build_and_upload deploy:true", + "fastlane:ios-build": "bundle exec fastlane ios deploy_to_testflight_qa deploy:false", + "fastlane:ios-deploy": "bundle exec fastlane ios deploy_to_testflight_qa deploy:true" }, "dependencies": { "@emoji-mart/data": "^1.2.1", @@ -44,19 +48,20 @@ "react-native": "^0.80.2", "react-native-blob-util": "^0.22.2", "react-native-fast-image": "^8.6.3", - "react-native-gesture-handler": "^2.26.0", + "react-native-gesture-handler": "^2.30.0", "react-native-haptic-feedback": "^2.3.3", "react-native-image-picker": "^8.2.1", "react-native-maps": "1.20.1", "react-native-nitro-modules": "^0.31.3", "react-native-nitro-sound": "^0.2.9", - "react-native-reanimated": "^4.0.1", + "react-native-reanimated": "^4.2.0", "react-native-safe-area-context": "^5.4.1", "react-native-screens": "^4.11.1", "react-native-share": "^12.0.11", "react-native-svg": "^15.12.0", + "react-native-teleport": "^0.5.4", "react-native-video": "^6.16.1", - "react-native-worklets": "^0.4.1", + "react-native-worklets": "^0.7.2", "stream-chat-react-native": "link:../../package/native-package", "stream-chat-react-native-core": "link:../../package" }, diff --git a/examples/SampleApp/src/components/AttachmentPickerContent.tsx b/examples/SampleApp/src/components/AttachmentPickerContent.tsx new file mode 100644 index 0000000000..a7a56e13ef --- /dev/null +++ b/examples/SampleApp/src/components/AttachmentPickerContent.tsx @@ -0,0 +1,53 @@ +import React, { useCallback, useState } from 'react'; +import { + useAttachmentPickerState, + AttachmentPickerContentProps, + AttachmentPickerContent, + AttachmentPickerGenericContent, + useStableCallback, + useTheme, + useTranslationContext, +} from 'stream-chat-react-native'; +import { ShareLocationIcon } from '../icons/ShareLocationIcon.tsx'; +import { LiveLocationCreateModal } from './LocationSharing/CreateLocationModal.tsx'; + +export const CustomAttachmentPickerContent = (props: AttachmentPickerContentProps) => { + const [modalVisible, setModalVisible] = useState(false); + const { selectedPicker } = useAttachmentPickerState(); + const { t } = useTranslationContext(); + const { + theme: { semantics }, + } = useTheme(); + + const Icon = useCallback( + () => , + [semantics.textTertiary], + ); + + const onRequestClose = () => { + setModalVisible(false); + }; + + const onOpenModal = useStableCallback(() => { + setModalVisible(true); + }); + + if (selectedPicker === 'location') { + return ( + <> + + {modalVisible ? ( + + ) : null} + + ); + } + + return ; +}; diff --git a/examples/SampleApp/src/components/AttachmentPickerSelectionBar.tsx b/examples/SampleApp/src/components/AttachmentPickerSelectionBar.tsx index 0b858bcb1c..c663e500c4 100644 --- a/examples/SampleApp/src/components/AttachmentPickerSelectionBar.tsx +++ b/examples/SampleApp/src/components/AttachmentPickerSelectionBar.tsx @@ -1,36 +1,57 @@ -import { useState } from 'react'; -import { Pressable, StyleSheet, View } from 'react-native'; -import { AttachmentPickerSelectionBar, useMessageInputContext } from 'stream-chat-react-native'; +import React, { useState } from 'react'; +import { StyleSheet, View } from 'react-native'; +import { + AttachmentTypePickerButton, + useAttachmentPickerState, + CameraPickerButton, + CommandsPickerButton, + FilePickerButton, + MediaPickerButton, + PollPickerButton, + useAttachmentPickerContext, + useStableCallback, +} from 'stream-chat-react-native'; import { ShareLocationIcon } from '../icons/ShareLocationIcon'; import { LiveLocationCreateModal } from './LocationSharing/CreateLocationModal'; export const CustomAttachmentPickerSelectionBar = () => { const [modalVisible, setModalVisible] = useState(false); - const { closeAttachmentPicker } = useMessageInputContext(); + const { attachmentPickerStore } = useAttachmentPickerContext(); + const { selectedPicker } = useAttachmentPickerState(); const onRequestClose = () => { setModalVisible(false); - closeAttachmentPicker(); }; - const onOpenModal = () => { + const onOpenModal = useStableCallback(() => { + attachmentPickerStore.setSelectedPicker('location'); setModalVisible(true); - }; + }); return ( - - - - - + + + + + + + {modalVisible ? ( + + ) : null} ); }; const styles = StyleSheet.create({ - selectionBar: { flexDirection: 'row', alignItems: 'center' }, - liveLocationButton: { - paddingLeft: 4, + selectionBar: { + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: 16, + paddingBottom: 12, }, }); diff --git a/examples/SampleApp/src/components/ChannelInfoOverlay.tsx b/examples/SampleApp/src/components/ChannelInfoOverlay.tsx index 911353cd9a..228c715b87 100644 --- a/examples/SampleApp/src/components/ChannelInfoOverlay.tsx +++ b/examples/SampleApp/src/components/ChannelInfoOverlay.tsx @@ -15,13 +15,13 @@ import Animated, { withTiming, } from 'react-native-reanimated'; import { - Avatar, CircleClose, Delete, User, UserMinus, useTheme, useViewport, + UserAvatar, } from 'stream-chat-react-native'; import { ChannelMemberResponse } from 'stream-chat'; @@ -34,8 +34,6 @@ import { SafeAreaView } from 'react-native-safe-area-context'; dayjs.extend(relativeTime); -const avatarSize = 64; - const styles = StyleSheet.create({ avatarPresenceIndicator: { right: 5, @@ -83,7 +81,7 @@ const styles = StyleSheet.create({ fontSize: 14, fontWeight: '700', }, - userItemContainer: { marginHorizontal: 8, width: 64 }, + userItemContainer: { marginHorizontal: 8, alignItems: 'center' }, userName: { fontSize: 12, fontWeight: '600', @@ -112,7 +110,8 @@ export const ChannelInfoOverlay = (props: ChannelInfoOverlayProps) => { const { theme: { - colors: { accent_red, black, border, grey, white }, + colors: { accent_red, black, grey, white }, + semantics, }, } = useTheme(); @@ -129,19 +128,19 @@ export const ChannelInfoOverlay = (props: ChannelInfoOverlayProps) => { } showScreen.value = show ? withTiming(1, { - duration: 150, - easing: Easing.in(Easing.ease), - }) + duration: 150, + easing: Easing.in(Easing.ease), + }) : withTiming( - 0, - { - duration: 150, - easing: Easing.out(Easing.ease), - }, - () => { - runOnJS(reset)(); - }, - ); + 0, + { + duration: 150, + easing: Easing.out(Easing.ease), + }, + () => { + runOnJS(reset)(); + }, + ); }; useEffect(() => { @@ -187,12 +186,12 @@ export const ChannelInfoOverlay = (props: ChannelInfoOverlayProps) => { translateY.value = evt.velocityY > 1000 ? withDecay({ - velocity: evt.velocityY, - }) + velocity: evt.velocityY, + }) : withTiming(screenHeight, { - duration: 200, - easing: Easing.out(Easing.ease), - }); + duration: 200, + easing: Easing.out(Easing.ease), + }); } else { translateY.value = withTiming(0); overlayOpacity.value = withTiming(1); @@ -227,31 +226,31 @@ export const ChannelInfoOverlay = (props: ChannelInfoOverlayProps) => { : 0; const channelName = channel ? channel.data?.name || - Object.values(channel.state.members) - .slice(0) - .reduce((returnString, currentMember, index, originalArray) => { - const returnStringLength = returnString.length; - const currentMemberName = - currentMember.user?.name || currentMember.user?.id || 'Unknown User'; - // a rough approximation of when the +Number shows up - if (returnStringLength + (currentMemberName.length + 2) < maxWidth) { - if (returnStringLength) { - returnString += `, ${currentMemberName}`; - } else { - returnString = currentMemberName; - } + Object.values(channel.state.members) + .slice(0) + .reduce((returnString, currentMember, index, originalArray) => { + const returnStringLength = returnString.length; + const currentMemberName = + currentMember.user?.name || currentMember.user?.id || 'Unknown User'; + // a rough approximation of when the +Number shows up + if (returnStringLength + (currentMemberName.length + 2) < maxWidth) { + if (returnStringLength) { + returnString += `, ${currentMemberName}`; } else { - const remainingMembers = originalArray.length - index; - returnString += `, +${remainingMembers}`; - originalArray.splice(1); // exit early + returnString = currentMemberName; } - return returnString; - }, '') + } else { + const remainingMembers = originalArray.length - index; + returnString += `, +${remainingMembers}`; + originalArray.splice(1); // exit early + } + return returnString; + }, '') : ''; const otherMembers = channel ? Object.values(channel.state.members).filter( - (member) => member.user?.id !== clientId, - ) + (member) => member.user?.id !== clientId, + ) : []; const { viewInfo, pinUnpin, archiveUnarchive, leaveGroup, deleteConversation, cancel } = @@ -287,11 +286,10 @@ export const ChannelInfoOverlay = (props: ChannelInfoOverlayProps) => { ? otherMembers[0].user?.online ? 'Online' : `Last Seen ${dayjs(otherMembers[0].user?.last_active).fromNow()}` - : `${Object.keys(channel.state.members).length} Members, ${ - Object.values(channel.state.members).filter( - (member) => !!member.user?.online, - ).length - } Online`} + : `${Object.keys(channel.state.members).length} Members, ${Object.values(channel.state.members).filter( + (member) => !!member.user?.online, + ).length + } Online`} { renderItem={({ item }) => item ? ( - + {item.name || item.id || ''} @@ -332,7 +329,7 @@ export const ChannelInfoOverlay = (props: ChannelInfoOverlayProps) => { style={[ styles.row, { - borderTopColor: border, + borderTopColor: semantics.borderCoreDefault, }, ]} > @@ -347,7 +344,7 @@ export const ChannelInfoOverlay = (props: ChannelInfoOverlayProps) => { style={[ styles.row, { - borderTopColor: border, + borderTopColor: semantics.borderCoreDefault, }, ]} > @@ -364,7 +361,7 @@ export const ChannelInfoOverlay = (props: ChannelInfoOverlayProps) => { style={[ styles.row, { - borderTopColor: border, + borderTopColor: semantics.borderCoreDefault, }, ]} > @@ -379,7 +376,7 @@ export const ChannelInfoOverlay = (props: ChannelInfoOverlayProps) => { {otherMembers.length > 1 && ( - + @@ -392,7 +389,7 @@ export const ChannelInfoOverlay = (props: ChannelInfoOverlayProps) => { style={[ styles.row, { - borderTopColor: border, + borderTopColor: semantics.borderCoreDefault, }, ]} > @@ -409,8 +406,8 @@ export const ChannelInfoOverlay = (props: ChannelInfoOverlayProps) => { style={[ styles.lastRow, { - borderBottomColor: border, - borderTopColor: border, + borderBottomColor: semantics.borderCoreDefault, + borderTopColor: semantics.borderCoreDefault, }, ]} > diff --git a/examples/SampleApp/src/components/ConfirmationBottomSheet.tsx b/examples/SampleApp/src/components/ConfirmationBottomSheet.tsx index 4f33bc9948..3eab535667 100644 --- a/examples/SampleApp/src/components/ConfirmationBottomSheet.tsx +++ b/examples/SampleApp/src/components/ConfirmationBottomSheet.tsx @@ -52,7 +52,8 @@ export const ConfirmationBottomSheet: React.FC = () => { const { theme: { - colors: { accent_red, black, border, grey, white }, + colors: { accent_red, black, grey, white }, + semantics, }, } = useTheme(); const inset = useSafeAreaInsets(); @@ -86,7 +87,7 @@ export const ConfirmationBottomSheet: React.FC = () => { style={[ styles.actionButtonsContainer, { - borderTopColor: border, + borderTopColor: semantics.borderCoreDefault, }, ]} > diff --git a/examples/SampleApp/src/components/MessageInfoBottomSheet.tsx b/examples/SampleApp/src/components/MessageInfoBottomSheet.tsx index a4ad65728b..d3a385a951 100644 --- a/examples/SampleApp/src/components/MessageInfoBottomSheet.tsx +++ b/examples/SampleApp/src/components/MessageInfoBottomSheet.tsx @@ -1,18 +1,18 @@ import React, { useMemo } from 'react'; import { - Avatar, BottomSheetModal, useChatContext, useMessageDeliveredData, useMessageReadData, useTheme, + UserAvatar, } from 'stream-chat-react-native'; import { LocalMessage, UserResponse } from 'stream-chat'; import { FlatList, StyleSheet, Text, View } from 'react-native'; const renderUserItem = ({ item }: { item: UserResponse }) => ( - + {item.name ?? item.id} ); diff --git a/examples/SampleApp/src/components/MessageSearch/MessageSearchList.tsx b/examples/SampleApp/src/components/MessageSearch/MessageSearchList.tsx index b3e91999c3..ad2ec54ded 100644 --- a/examples/SampleApp/src/components/MessageSearch/MessageSearchList.tsx +++ b/examples/SampleApp/src/components/MessageSearch/MessageSearchList.tsx @@ -3,7 +3,7 @@ import { FlatList, StyleSheet, Text, TouchableOpacity, View } from 'react-native import { NavigationProp, useNavigation } from '@react-navigation/native'; import dayjs from 'dayjs'; import calendar from 'dayjs/plugin/calendar'; -import { Avatar, Spinner, useTheme, useViewport } from 'stream-chat-react-native'; +import { Spinner, useTheme, useViewport, UserAvatar } from 'stream-chat-react-native'; import { DEFAULT_PAGINATION_LIMIT } from '../../utils/constants'; import type { MessageResponse } from 'stream-chat'; @@ -27,6 +27,8 @@ const styles = StyleSheet.create({ justifyContent: 'center', }, itemContainer: { + alignItems: 'center', + justifyContent: 'center', borderBottomWidth: 1, flex: 1, flexDirection: 'row', @@ -70,7 +72,8 @@ export const MessageSearchList: React.FC = React.forward } = props; const { theme: { - colors: { black, border, grey, white_snow }, + colors: { black, grey, white_snow }, + semantics, }, } = useTheme(); const { vw } = useViewport(); @@ -92,13 +95,11 @@ export const MessageSearchList: React.FC = React.forward }} > - {`${ - messages.length >= DEFAULT_PAGINATION_LIMIT - ? DEFAULT_PAGINATION_LIMIT - : messages.length - }${messages.length >= DEFAULT_PAGINATION_LIMIT ? '+ ' : ' '} result${ - messages.length === 1 ? '' : 's' - }`} + {`${messages.length >= DEFAULT_PAGINATION_LIMIT + ? DEFAULT_PAGINATION_LIMIT + : messages.length + }${messages.length >= DEFAULT_PAGINATION_LIMIT ? '+ ' : ' '} result${messages.length === 1 ? '' : 's' + }`} )} @@ -128,15 +129,11 @@ export const MessageSearchList: React.FC = React.forward messageId: item.id, }); }} - style={[styles.itemContainer, { borderBottomColor: border }]} + style={[styles.itemContainer, { borderBottomColor: semantics.borderCoreDefault }]} testID='channel-preview-button' > - + {item.user ? : null} + ({ timeLeftMs: state.timeLeftMs, }); -export const MessageReminderHeader = ({ message }: MessageFooterProps) => { +export const MessageReminderHeader = ({ message }: MessageHeaderProps) => { const messageId = message?.id ?? ''; const reminder = useMessageReminder(messageId); const { timeLeftMs } = useStateStore(reminder?.state, reminderStateSelector) ?? {}; const { t } = useTranslationContext(); + const { + theme: { semantics }, + } = useTheme(); + const stopRefreshBoundaryMs = reminder?.timer.stopRefreshBoundaryMs; const stopRefreshTimeStamp = reminder?.remindAt && stopRefreshBoundaryMs @@ -43,8 +50,8 @@ export const MessageReminderHeader = ({ message }: MessageFooterProps) => { if (reminder.remindAt && timeLeftMs !== null) { return ( - + + + + Message Input Floating + + {messageInputFloatingConfigItems.map((item) => ( + + ))} + + + diff --git a/examples/SampleApp/src/components/UnreadCountBadge.tsx b/examples/SampleApp/src/components/UnreadCountBadge.tsx index 71d3a2694a..87c42c8ee2 100644 --- a/examples/SampleApp/src/components/UnreadCountBadge.tsx +++ b/examples/SampleApp/src/components/UnreadCountBadge.tsx @@ -1,33 +1,21 @@ import React, { useEffect, useState } from 'react'; -import { StyleSheet, Text, View } from 'react-native'; -import { useStateStore, useTheme } from 'stream-chat-react-native'; +import { BadgeNotification, useStateStore } from 'stream-chat-react-native'; import { useAppContext } from '../context/AppContext'; import { ThreadManagerState } from 'stream-chat'; -const styles = StyleSheet.create({ - unreadContainer: { - alignItems: 'center', - borderRadius: 8, - justifyContent: 'center', - }, - unreadText: { - color: '#FFFFFF', - fontSize: 11, - fontWeight: '700', - paddingHorizontal: 5, - paddingVertical: 1, - }, -}); - const selector = (nextValue: ThreadManagerState) => - ({ unreadCount: nextValue.unreadThreadCount } as const); + ({ unreadCount: nextValue.unreadThreadCount }) as const; export const ThreadsUnreadCountBadge: React.FC = () => { const { chatClient } = useAppContext(); const { unreadCount } = useStateStore(chatClient?.threads?.state, selector) ?? { unreadCount: 0 }; - return ; + if (unreadCount === 0) { + return null; + } + + return ; }; export const ChannelsUnreadCountBadge: React.FC = () => { @@ -59,26 +47,9 @@ export const ChannelsUnreadCountBadge: React.FC = () => { }; }, [chatClient]); - return ; -}; - -type UnreadCountBadgeProps = { - unreadCount: number | undefined; -}; - -const UnreadCountBadge: React.FC = (props) => { - const { unreadCount } = props; - const { - theme: { - colors: { accent_red }, - }, - } = useTheme(); + if (unreadCount === 0) { + return null; + } - return ( - - {!!unreadCount && ( - {unreadCount > 99 ? '99+' : unreadCount} - )} - - ); + return ; }; diff --git a/examples/SampleApp/src/components/UserInfoOverlay.tsx b/examples/SampleApp/src/components/UserInfoOverlay.tsx index 1d487b8abb..91659ececf 100644 --- a/examples/SampleApp/src/components/UserInfoOverlay.tsx +++ b/examples/SampleApp/src/components/UserInfoOverlay.tsx @@ -15,7 +15,6 @@ import Animated, { withTiming, } from 'react-native-reanimated'; import { - Avatar, CircleClose, MessageIcon, useChatContext, @@ -23,6 +22,7 @@ import { UserMinus, useTheme, useViewport, + UserAvatar, } from 'stream-chat-react-native'; import { useAppOverlayContext } from '../context/AppOverlayContext'; @@ -35,8 +35,6 @@ import { SafeAreaView } from 'react-native-safe-area-context'; dayjs.extend(relativeTime); -const avatarSize = 64; - const styles = StyleSheet.create({ avatarPresenceIndicator: { right: 5, @@ -77,10 +75,7 @@ const styles = StyleSheet.create({ fontWeight: '700', }, userItemContainer: { - marginHorizontal: 8, - paddingBottom: 24, - paddingTop: 16, - width: 64, + paddingVertical: 16, }, userName: { fontSize: 12, @@ -110,7 +105,8 @@ export const UserInfoOverlay = (props: UserInfoOverlayProps) => { const { theme: { - colors: { accent_red, black, border, grey, white }, + colors: { accent_red, black, grey, white }, + semantics, }, } = useTheme(); @@ -127,19 +123,19 @@ export const UserInfoOverlay = (props: UserInfoOverlayProps) => { } showScreen.value = show ? withTiming(1, { - duration: 150, - easing: Easing.in(Easing.ease), - }) + duration: 150, + easing: Easing.in(Easing.ease), + }) : withTiming( - 0, - { - duration: 150, - easing: Easing.out(Easing.ease), - }, - () => { - runOnJS(reset)(); - }, - ); + 0, + { + duration: 150, + easing: Easing.out(Easing.ease), + }, + () => { + runOnJS(reset)(); + }, + ); }; useEffect(() => { @@ -185,12 +181,12 @@ export const UserInfoOverlay = (props: UserInfoOverlayProps) => { translateY.value = evt.velocityY > 1000 ? withDecay({ - velocity: evt.velocityY, - }) + velocity: evt.velocityY, + }) : withTiming(screenHeight, { - duration: 200, - easing: Easing.out(Easing.ease), - }); + duration: 200, + easing: Easing.out(Easing.ease), + }); } else { translateY.value = withTiming(0); overlayOpacity.value = withTiming(1); @@ -221,8 +217,8 @@ export const UserInfoOverlay = (props: UserInfoOverlayProps) => { const self = channel ? Object.values(channel.state.members).find( - (channelMember) => channelMember.user?.id === client.user?.id, - ) + (channelMember) => channelMember.user?.id === client.user?.id, + ) : undefined; const { viewInfo, messageUser, removeFromGroup, cancel } = useUserInfoOverlayActions(); @@ -269,12 +265,11 @@ export const UserInfoOverlay = (props: UserInfoOverlayProps) => { : `Last Seen ${dayjs(member.user?.last_active).fromNow()}`} - @@ -283,7 +278,7 @@ export const UserInfoOverlay = (props: UserInfoOverlayProps) => { style={[ styles.row, { - borderTopColor: border, + borderTopColor: semantics.borderCoreDefault, }, ]} > @@ -298,7 +293,7 @@ export const UserInfoOverlay = (props: UserInfoOverlayProps) => { style={[ styles.row, { - borderTopColor: border, + borderTopColor: semantics.borderCoreDefault, }, ]} > @@ -314,7 +309,7 @@ export const UserInfoOverlay = (props: UserInfoOverlayProps) => { style={[ styles.row, { - borderTopColor: border, + borderTopColor: semantics.borderCoreDefault, }, ]} > @@ -332,8 +327,8 @@ export const UserInfoOverlay = (props: UserInfoOverlayProps) => { style={[ styles.lastRow, { - borderBottomColor: border, - borderTopColor: border, + borderBottomColor: semantics.borderCoreDefault, + borderTopColor: semantics.borderCoreDefault, }, ]} > diff --git a/examples/SampleApp/src/components/UserSearch/UserGridItem.tsx b/examples/SampleApp/src/components/UserSearch/UserGridItem.tsx index c61449e923..2fad413d0f 100644 --- a/examples/SampleApp/src/components/UserSearch/UserGridItem.tsx +++ b/examples/SampleApp/src/components/UserSearch/UserGridItem.tsx @@ -1,13 +1,10 @@ import React from 'react'; import { StyleSheet, Text, View } from 'react-native'; import { TouchableOpacity } from '@gorhom/bottom-sheet'; -import { Avatar, Close, useTheme } from 'stream-chat-react-native'; +import { Close, useTheme, UserAvatar } from 'stream-chat-react-native'; import type { UserResponse } from 'stream-chat'; - -const presenceIndicator = { cx: 7, cy: 7, r: 5 }; - const styles = StyleSheet.create({ presenceIndicatorContainer: { bottom: 0, @@ -55,13 +52,8 @@ export const UserGridItem: React.FC = ({ } = useTheme(); return ( - + + {removeButton && ( = ({ bg_gradient_end, bg_gradient_start, black, - border, grey, grey_gainsboro, white_smoke, white_snow, }, + semantics, }, } = useTheme(); const { vw } = useViewport(); @@ -199,11 +199,11 @@ export const UserSearchResults: React.FC = ({ styles.searchResultContainer, { backgroundColor: white_snow, - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > - + void; switchUser: (userId?: string) => void; messageListImplementation: MessageListImplementationConfigItem['id']; + messageInputFloating: MessageInputFloatingConfigItem['value']; messageListMode: MessageListModeConfigItem['mode']; messageListPruning: MessageListPruningConfigItem['value']; }; diff --git a/examples/SampleApp/src/hooks/useStreamChatTheme.ts b/examples/SampleApp/src/hooks/useStreamChatTheme.ts index f868e9ee2c..8a0ad4f44f 100644 --- a/examples/SampleApp/src/hooks/useStreamChatTheme.ts +++ b/examples/SampleApp/src/hooks/useStreamChatTheme.ts @@ -14,7 +14,6 @@ const getChatStyle = (colorScheme: ColorSchemeName): DeepPartial => ({ bg_user: '#17191C', black: '#FFFFFF', blue_alice: '#00193D', - border: '#141924', button_background: '#FFFFFF', button_text: '#005FFF', code_block: '#222222', @@ -43,7 +42,6 @@ const getChatStyle = (colorScheme: ColorSchemeName): DeepPartial => ({ bg_gradient_start: '#FCFCFC', black: '#000000', blue_alice: '#E9F2FF', - border: '#00000014', // 14 = 8% opacity; top: x=0, y=-1; bottom: x=0, y=1 button_background: '#005FFF', button_text: '#FFFFFF', grey: '#7A7A7A', diff --git a/examples/SampleApp/src/icons/Bell.tsx b/examples/SampleApp/src/icons/Bell.tsx index 0d8c355ffb..e2e669941a 100644 --- a/examples/SampleApp/src/icons/Bell.tsx +++ b/examples/SampleApp/src/icons/Bell.tsx @@ -7,7 +7,7 @@ import { IconProps } from '../utils/base'; export const Bell: React.FC = ({ height = 512, width = 512 }) => { const { theme: { - colors: { grey }, + semantics }, } = useTheme(); @@ -15,7 +15,7 @@ export const Bell: React.FC = ({ height = 512, width = 512 }) => { ); diff --git a/examples/SampleApp/src/icons/ShareLocationIcon.tsx b/examples/SampleApp/src/icons/ShareLocationIcon.tsx index 79c1e13584..2680f19261 100644 --- a/examples/SampleApp/src/icons/ShareLocationIcon.tsx +++ b/examples/SampleApp/src/icons/ShareLocationIcon.tsx @@ -1,24 +1,19 @@ import Svg, { Path } from 'react-native-svg'; -import { useTheme } from 'stream-chat-react-native'; +import { ColorValue } from 'react-native'; // Icon for "Share Location" button, next to input box. -export const ShareLocationIcon = () => { - const { - theme: { - colors: { grey }, - }, - } = useTheme(); +export const ShareLocationIcon = ({ stroke }: { stroke: ColorValue }) => { return ( - + ); diff --git a/examples/SampleApp/src/screens/ChannelFilesScreen.tsx b/examples/SampleApp/src/screens/ChannelFilesScreen.tsx index cde55c465a..5072e69f89 100644 --- a/examples/SampleApp/src/screens/ChannelFilesScreen.tsx +++ b/examples/SampleApp/src/screens/ChannelFilesScreen.tsx @@ -83,7 +83,8 @@ export const ChannelFilesScreen: React.FC = ({ const insets = useSafeAreaInsets(); const { theme: { - colors: { black, border, grey, white_snow }, + colors: { black, grey, white_snow }, + semantics, }, } = useTheme(); @@ -149,7 +150,7 @@ export const ChannelFilesScreen: React.FC = ({ Alert.alert('Not implemented.'); }} style={{ - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, borderBottomWidth: index === section.data.length - 1 ? 0 : 1, }} > diff --git a/examples/SampleApp/src/screens/ChannelImagesScreen.tsx b/examples/SampleApp/src/screens/ChannelImagesScreen.tsx index fd124f72a4..793b7a00e7 100644 --- a/examples/SampleApp/src/screens/ChannelImagesScreen.tsx +++ b/examples/SampleApp/src/screens/ChannelImagesScreen.tsx @@ -13,10 +13,11 @@ import Dayjs from 'dayjs'; import { SafeAreaView } from 'react-native-safe-area-context'; import { DateHeader, - Photo, useImageGalleryContext, useOverlayContext, useTheme, + ImageGalleryState, + useStateStore, } from 'stream-chat-react-native'; import { ScreenHeader } from '../components/ScreenHeader'; @@ -24,7 +25,6 @@ import { usePaginatedAttachments } from '../hooks/usePaginatedAttachments'; import { Picture } from '../icons/Picture'; import type { RouteProp } from '@react-navigation/native'; -import type { Attachment } from 'stream-chat'; import type { StackNavigatorParamList } from '../types'; @@ -61,16 +61,17 @@ export type ChannelImagesScreenProps = { route: ChannelImagesScreenRouteProp; }; +const selector = (state: ImageGalleryState) => ({ + assets: state.assets, +}); + export const ChannelImagesScreen: React.FC = ({ route: { params: { channel }, }, }) => { - const { - messages: images, - setMessages: setImages, - setSelectedMessage: setImage, - } = useImageGalleryContext(); + const { imageGalleryStateStore } = useImageGalleryContext(); + const { assets } = useStateStore(imageGalleryStateStore.state, selector); const { setOverlay } = useOverlayContext(); const { loading, loadMore, messages } = usePaginatedAttachments(channel, 'image'); const { @@ -79,8 +80,6 @@ export const ChannelImagesScreen: React.FC = ({ }, } = useTheme(); - const channelImages = useRef(images); - const [stickyHeaderDate, setStickyHeaderDate] = useState( Dayjs(messages?.[0]?.created_at).format('MMM YYYY'), ); @@ -106,30 +105,6 @@ export const ChannelImagesScreen: React.FC = ({ } }); - /** - * Photos array created from all currently available - * photo attachments - */ - const photos = messages.reduce((acc: Photo[], cur) => { - const attachmentImages = - (cur.attachments as Attachment[])?.filter( - (attachment) => - attachment.type === 'image' && - !attachment.title_link && - !attachment.og_scrape_url && - (attachment.image_url || attachment.thumb_url), - ) || []; - - const attachmentPhotos = attachmentImages.map((attachmentImage) => ({ - created_at: cur.created_at, - id: `photoId-${cur.id}-${attachmentImage.image_url || attachmentImage.thumb_url}`, - messageId: cur.id, - uri: attachmentImage.image_url || (attachmentImage.thumb_url as string), - })); - - return [...acc, ...attachmentPhotos]; - }, []); - const messagesWithImages = messages .map((message) => ({ ...message, groupStyles: [], readBy: false })) .filter((message) => { @@ -145,24 +120,11 @@ export const ChannelImagesScreen: React.FC = ({ return false; }); - /** - * This is for the useEffect to run again in the case that a message - * gets edited with more or the same number of images - */ - const imageString = messagesWithImages - .map((message) => - (message.attachments as Attachment[]) - .map((attachment) => attachment.image_url || attachment.thumb_url || '') - .join(), - ) - .join(); - useEffect(() => { - setImages(messagesWithImages); - const channelImagesCurrent = channelImages.current; - return () => setImages(channelImagesCurrent); + imageGalleryStateStore.openImageGallery({ messages: messagesWithImages }); + return () => imageGalleryStateStore.clear(); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [imageString, setImages]); + }, [imageGalleryStateStore, messagesWithImages.length]); return ( @@ -170,7 +132,7 @@ export const ChannelImagesScreen: React.FC = ({ `${item.id}-${index}`} ListEmptyComponent={EmptyListComponent} numColumns={3} @@ -180,9 +142,9 @@ export const ChannelImagesScreen: React.FC = ({ renderItem={({ item }) => ( { - setImage({ - messageId: item.messageId, - url: item.uri, + imageGalleryStateStore.openImageGallery({ + messages: messagesWithImages, + selectedAttachmentUrl: item.uri, }); setOverlay('gallery'); }} @@ -202,7 +164,7 @@ export const ChannelImagesScreen: React.FC = ({ viewAreaCoveragePercentThreshold: 50, }} /> - {photos && photos.length ? ( + {assets.length > 0 ? ( diff --git a/examples/SampleApp/src/screens/ChannelListScreen.tsx b/examples/SampleApp/src/screens/ChannelListScreen.tsx index 796406673d..0a6c6f95cd 100644 --- a/examples/SampleApp/src/screens/ChannelListScreen.tsx +++ b/examples/SampleApp/src/screens/ChannelListScreen.tsx @@ -9,7 +9,7 @@ import { View, } from 'react-native'; import { useNavigation, useScrollToTop } from '@react-navigation/native'; -import { ChannelList, CircleClose, Search, useTheme } from 'stream-chat-react-native'; +import { ChannelList, CircleClose, useTheme } from 'stream-chat-react-native'; import { Channel } from 'stream-chat'; import { ChannelPreview } from '../components/ChannelPreview'; import { ChatScreenHeader } from '../components/ChatScreenHeader'; @@ -19,6 +19,7 @@ import { usePaginatedSearchedMessages } from '../hooks/usePaginatedSearchedMessa import type { ChannelSort } from 'stream-chat'; import { useStreamChatContext } from '../context/StreamChatContext'; +import { Search } from '../icons/Search'; const styles = StyleSheet.create({ channelListContainer: { @@ -66,6 +67,7 @@ const options = { presence: true, state: true, watch: true, + message_limit: 25, }; const HeaderNetworkDownIndicator = () => null; diff --git a/examples/SampleApp/src/screens/ChannelScreen.tsx b/examples/SampleApp/src/screens/ChannelScreen.tsx index 406938e40d..92c178100a 100644 --- a/examples/SampleApp/src/screens/ChannelScreen.tsx +++ b/examples/SampleApp/src/screens/ChannelScreen.tsx @@ -3,7 +3,6 @@ import type { LocalMessage, Channel as StreamChatChannel } from 'stream-chat'; import { RouteProp, useFocusEffect, useNavigation } from '@react-navigation/native'; import { Channel, - ChannelAvatar, MessageInput, MessageList, MessageFlashList, @@ -16,10 +15,10 @@ import { AITypingIndicatorView, useTranslationContext, MessageActionsParams, + ChannelAvatar, } from 'stream-chat-react-native'; import { Platform, Pressable, StyleSheet, View } from 'react-native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useAppContext } from '../context/AppContext'; import { ScreenHeader } from '../components/ScreenHeader'; @@ -28,12 +27,13 @@ import { useChannelMembersStatus } from '../hooks/useChannelMembersStatus'; import type { StackNavigatorParamList } from '../types'; import { NetworkDownIndicator } from '../components/NetworkDownIndicator'; import { useCreateDraftFocusEffect } from '../utils/useCreateDraftFocusEffect.tsx'; -import { MessageReminderHeader } from '../components/Reminders/MessageReminderHeader.tsx'; +import { MessageHeader } from '../components/Reminders/MessageReminderHeader.tsx'; import { channelMessageActions } from '../utils/messageActions.tsx'; import { MessageLocation } from '../components/LocationSharing/MessageLocation.tsx'; import { useStreamChatContext } from '../context/StreamChatContext.tsx'; import { CustomAttachmentPickerSelectionBar } from '../components/AttachmentPickerSelectionBar.tsx'; import { MessageInfoBottomSheet } from '../components/MessageInfoBottomSheet.tsx'; +import { CustomAttachmentPickerContent } from '../components/AttachmentPickerContent.tsx'; export type ChannelScreenNavigationProp = NativeStackNavigationProp< StackNavigatorParamList, @@ -103,7 +103,7 @@ const ChannelHeader: React.FC = ({ channel }) => { opacity: pressed ? 0.5 : 1, })} > - + )} showUnreadCountBadge @@ -121,11 +121,15 @@ export const ChannelScreen: React.FC = ({ params: { channel: channelFromProp, channelId, messageId }, }, }) => { - const { chatClient, messageListImplementation, messageListMode, messageListPruning } = - useAppContext(); - const { bottom } = useSafeAreaInsets(); const { - theme: { colors }, + chatClient, + messageListImplementation, + messageListMode, + messageListPruning, + messageInputFloating, + } = useAppContext(); + const { + theme: { semantics, colors }, } = useTheme(); const { t } = useTranslationContext(); const { setThread } = useStreamChatContext(); @@ -205,10 +209,11 @@ export const ChannelScreen: React.FC = ({ chatClient, t, colors, + semantics, handleMessageInfo, }); }, - [chatClient, colors, t, handleMessageInfo], + [chatClient, t, colors, semantics, handleMessageInfo], ); if (!channel || !chatClient) { @@ -216,18 +221,19 @@ export const ChannelScreen: React.FC = ({ } return ( - + null} diff --git a/examples/SampleApp/src/screens/GroupChannelDetailsScreen.tsx b/examples/SampleApp/src/screens/GroupChannelDetailsScreen.tsx index 0746bd158d..667c855364 100644 --- a/examples/SampleApp/src/screens/GroupChannelDetailsScreen.tsx +++ b/examples/SampleApp/src/screens/GroupChannelDetailsScreen.tsx @@ -11,10 +11,10 @@ import { import { RouteProp, useNavigation } from '@react-navigation/native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { - Avatar, useChannelPreviewDisplayName, useOverlayContext, useTheme, + UserAvatar, } from 'stream-chat-react-native'; import { RoundButton } from '../components/RoundButton'; @@ -163,7 +163,8 @@ export const GroupChannelDetailsScreen: React.FC = ({ const { setOverlay } = useOverlayContext(); const { theme: { - colors: { accent_blue, accent_green, black, border, grey, white, white_smoke }, + colors: { accent_blue, accent_green, black, grey, white, white_smoke }, + semantics, }, } = useTheme(); @@ -276,16 +277,16 @@ export const GroupChannelDetailsScreen: React.FC = ({ style={[ styles.memberContainer, { - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > - {member.user?.name} @@ -306,7 +307,7 @@ export const GroupChannelDetailsScreen: React.FC = ({ style={[ styles.loadMoreButton, { - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > @@ -330,7 +331,7 @@ export const GroupChannelDetailsScreen: React.FC = ({ style={[ styles.changeNameContainer, { - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > @@ -382,7 +383,7 @@ export const GroupChannelDetailsScreen: React.FC = ({ style={[ styles.actionContainer, { - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > @@ -427,7 +428,7 @@ export const GroupChannelDetailsScreen: React.FC = ({ style={[ styles.actionContainer, { - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > @@ -457,7 +458,7 @@ export const GroupChannelDetailsScreen: React.FC = ({ style={[ styles.actionContainer, { - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > @@ -487,7 +488,7 @@ export const GroupChannelDetailsScreen: React.FC = ({ style={[ styles.actionContainer, { - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > @@ -513,7 +514,7 @@ export const GroupChannelDetailsScreen: React.FC = ({ style={[ styles.actionContainer, { - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > diff --git a/examples/SampleApp/src/screens/NewDirectMessagingScreen.tsx b/examples/SampleApp/src/screens/NewDirectMessagingScreen.tsx index 7ecb9e9992..339ee4c998 100644 --- a/examples/SampleApp/src/screens/NewDirectMessagingScreen.tsx +++ b/examples/SampleApp/src/screens/NewDirectMessagingScreen.tsx @@ -118,7 +118,8 @@ export const NewDirectMessagingScreen: React.FC = }) => { const { theme: { - colors: { accent_blue, black, border, grey, white }, + colors: { accent_blue, black, grey, white }, + semantics, }, } = useTheme(); const { chatClient } = useAppContext(); @@ -208,7 +209,7 @@ export const NewDirectMessagingScreen: React.FC = styles.searchContainer, { backgroundColor: white, - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > diff --git a/examples/SampleApp/src/screens/NewGroupChannelAddMemberScreen.tsx b/examples/SampleApp/src/screens/NewGroupChannelAddMemberScreen.tsx index eb963ed8ce..8897f77c36 100644 --- a/examples/SampleApp/src/screens/NewGroupChannelAddMemberScreen.tsx +++ b/examples/SampleApp/src/screens/NewGroupChannelAddMemberScreen.tsx @@ -77,7 +77,8 @@ export const NewGroupChannelAddMemberScreen: React.FC = ({ navigation }) const { theme: { - colors: { black, border, grey, white }, + colors: { black, grey, white }, + semantics, }, } = useTheme(); @@ -111,7 +112,7 @@ export const NewGroupChannelAddMemberScreen: React.FC = ({ navigation }) styles.inputBoxContainer, { backgroundColor: white, - borderColor: border, + borderColor: semantics.borderCoreDefault, marginBottom: selectedUsers.length === 0 ? 8 : 16, }, ]} diff --git a/examples/SampleApp/src/screens/NewGroupChannelAssignNameScreen.tsx b/examples/SampleApp/src/screens/NewGroupChannelAssignNameScreen.tsx index 6a3a039338..270ce5e532 100644 --- a/examples/SampleApp/src/screens/NewGroupChannelAssignNameScreen.tsx +++ b/examples/SampleApp/src/screens/NewGroupChannelAssignNameScreen.tsx @@ -58,13 +58,13 @@ const ConfirmButton: React.FC = (props) => { const { disabled, onPress } = props; const { theme: { - colors: { accent_blue, grey }, + semantics, }, } = useTheme(); return ( - + ); }; @@ -86,7 +86,8 @@ export const NewGroupChannelAssignNameScreen: React.FC diff --git a/examples/SampleApp/src/screens/OneOnOneChannelDetailScreen.tsx b/examples/SampleApp/src/screens/OneOnOneChannelDetailScreen.tsx index d492bc795b..36fd1d7de2 100644 --- a/examples/SampleApp/src/screens/OneOnOneChannelDetailScreen.tsx +++ b/examples/SampleApp/src/screens/OneOnOneChannelDetailScreen.tsx @@ -1,13 +1,5 @@ import React, { useState } from 'react'; -import { - Image, - ScrollView, - StyleSheet, - Switch, - Text, - TouchableOpacity, - View, -} from 'react-native'; +import { Image, ScrollView, StyleSheet, Switch, Text, TouchableOpacity, View } from 'react-native'; import { Delete, useTheme } from 'stream-chat-react-native'; import { useAppContext } from '../context/AppContext'; @@ -142,7 +134,8 @@ export const OneOnOneChannelDetailScreen: React.FC = ({ }) => { const { theme: { - colors: { accent_green, accent_red, black, border, grey, white, white_smoke }, + colors: { accent_green, accent_red, black, grey, white, white_smoke }, + semantics, }, } = useTheme(); const { chatClient } = useAppContext(); @@ -156,13 +149,13 @@ export const OneOnOneChannelDetailScreen: React.FC = ({ const user = member?.user; const [muted, setMuted] = useState( chatClient?.mutedUsers && - chatClient?.mutedUsers?.findIndex((mutedUser) => mutedUser.target.id === user?.id) > -1, + chatClient?.mutedUsers?.findIndex((mutedUser) => mutedUser.target.id === user?.id) > -1, ); const [notificationsEnabled, setNotificationsEnabled] = useState( chatClient?.mutedChannels && - chatClient.mutedChannels.findIndex( - (mutedChannel) => mutedChannel.channel?.id === channel.id, - ) > -1, + chatClient.mutedChannels.findIndex( + (mutedChannel) => mutedChannel.channel?.id === channel.id, + ) > -1, ); /** @@ -235,7 +228,7 @@ export const OneOnOneChannelDetailScreen: React.FC = ({ style={[ styles.userNameContainer, { - borderTopColor: border, + borderTopColor: semantics.borderCoreDefault, }, ]} > @@ -274,7 +267,7 @@ export const OneOnOneChannelDetailScreen: React.FC = ({ style={[ styles.actionContainer, { - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > @@ -313,7 +306,7 @@ export const OneOnOneChannelDetailScreen: React.FC = ({ style={[ styles.actionContainer, { - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > @@ -359,7 +352,7 @@ export const OneOnOneChannelDetailScreen: React.FC = ({ style={[ styles.actionContainer, { - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > @@ -389,7 +382,7 @@ export const OneOnOneChannelDetailScreen: React.FC = ({ style={[ styles.actionContainer, { - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > @@ -419,7 +412,7 @@ export const OneOnOneChannelDetailScreen: React.FC = ({ style={[ styles.actionContainer, { - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > @@ -449,7 +442,7 @@ export const OneOnOneChannelDetailScreen: React.FC = ({ style={[ styles.actionContainer, { - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > @@ -476,7 +469,7 @@ export const OneOnOneChannelDetailScreen: React.FC = ({ style={[ styles.actionContainer, { - borderBottomColor: border, + borderBottomColor: semantics.borderCoreDefault, }, ]} > diff --git a/examples/SampleApp/src/screens/SharedGroupsScreen.tsx b/examples/SampleApp/src/screens/SharedGroupsScreen.tsx index c0c0894e97..8067c2407f 100644 --- a/examples/SampleApp/src/screens/SharedGroupsScreen.tsx +++ b/examples/SampleApp/src/screens/SharedGroupsScreen.tsx @@ -1,8 +1,7 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { NavigationProp, RouteProp, useNavigation } from '@react-navigation/native'; import { - Avatar, ChannelList, ChannelListMessenger, ChannelListMessengerProps, @@ -12,6 +11,8 @@ import { useChannelPreviewDisplayName, useChannelsContext, useTheme, + Avatar, + getInitialsFromName, } from 'stream-chat-react-native'; import { ScreenHeader } from '../components/ScreenHeader'; @@ -66,6 +67,16 @@ const CustomPreview: React.FC = ({ channel }) => { }, } = useTheme(); + const displayAvatar = getChannelPreviewDisplayAvatar(channel, chatClient); + + const placeholder = useMemo(() => { + if (displayAvatar?.name) { + return {getInitialsFromName(displayAvatar?.name)}; + } else { + return ?; + } + }, [displayAvatar.name]); + if (!chatClient) { return null; } @@ -74,8 +85,6 @@ const CustomPreview: React.FC = ({ channel }) => { return null; } - const displayAvatar = getChannelPreviewDisplayAvatar(channel, chatClient); - const switchToChannel = () => { navigation.reset({ index: 1, @@ -106,17 +115,9 @@ const CustomPreview: React.FC = ({ channel }) => { > {displayAvatar.images ? ( - + ) : ( - + )} {name} diff --git a/examples/SampleApp/src/screens/ThreadScreen.tsx b/examples/SampleApp/src/screens/ThreadScreen.tsx index ddf81d1ec4..75fd66e7f3 100644 --- a/examples/SampleApp/src/screens/ThreadScreen.tsx +++ b/examples/SampleApp/src/screens/ThreadScreen.tsx @@ -1,6 +1,6 @@ import React, { useCallback } from 'react'; import { Platform, StyleSheet, View } from 'react-native'; -import { SafeAreaView } from 'react-native-safe-area-context'; + import { Channel, MessageActionsParams, @@ -26,6 +26,7 @@ import { useStreamChatContext } from '../context/StreamChatContext.tsx'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { CustomAttachmentPickerSelectionBar } from '../components/AttachmentPickerSelectionBar.tsx'; import { MessageLocation } from '../components/LocationSharing/MessageLocation.tsx'; +import { useAppContext } from '../context/AppContext.ts'; const selector = (nextValue: ThreadState) => ({ parentMessage: nextValue.parentMessage }) as const; @@ -64,7 +65,6 @@ const ThreadHeader: React.FC = ({ thread }) => { return ( @@ -79,12 +79,14 @@ export const ThreadScreen: React.FC = ({ }) => { const { theme: { + semantics, colors: { white }, }, } = useTheme(); const { client: chatClient } = useChatContext(); const { t } = useTranslationContext(); const { setThread } = useStreamChatContext(); + const { messageInputFloating, messageListImplementation } = useAppContext(); const onPressMessage: NonNullable['onPressMessage']> = ( payload, @@ -105,10 +107,11 @@ export const ThreadScreen: React.FC = ({ return channelMessageActions({ params, chatClient, + semantics, t, }); }, - [chatClient, t], + [chatClient, semantics, t], ); const onThreadDismount = useCallback(() => { @@ -116,25 +119,24 @@ export const ThreadScreen: React.FC = ({ }, [setThread]); return ( - + - - - - + + - + ); }; diff --git a/examples/SampleApp/src/screens/UserSelectorScreen.tsx b/examples/SampleApp/src/screens/UserSelectorScreen.tsx index 0448c4ead6..49296412b4 100644 --- a/examples/SampleApp/src/screens/UserSelectorScreen.tsx +++ b/examples/SampleApp/src/screens/UserSelectorScreen.tsx @@ -90,7 +90,8 @@ type Props = { export const UserSelectorScreen: React.FC = ({ navigation }) => { const { theme: { - colors: { black, border, grey, grey_gainsboro, grey_whisper, white_snow }, + colors: { black, grey, grey_gainsboro, grey_whisper, white_snow }, + semantics, }, } = useTheme(); const { switchUser } = useAppContext(); @@ -125,7 +126,7 @@ export const UserSelectorScreen: React.FC = ({ navigation }) => { onPress={() => { switchUser(u.id); }} - style={[styles.userContainer, { borderBottomColor: border }]} + style={[styles.userContainer, { borderBottomColor: semantics.borderCoreDefault }]} testID={`user-selector-button-${u.id}`} > = ({ navigation }) => { onPress={() => { navigation.navigate('AdvancedUserSelectorScreen'); }} - style={[styles.userContainer, { borderBottomColor: border }]} + style={[styles.userContainer, { borderBottomColor: semantics.borderCoreDefault }]} > void; + semantics: Theme['semantics']; }) { const { dismissOverlay, deleteForMeMessage } = params; const actions = messageActions(params); @@ -46,7 +48,8 @@ export function channelMessageActions({ }, actionType: reminder ? 'remove-from-later' : 'save-for-later', title: reminder ? 'Remove from Later' : 'Save for Later', - icon: