Compare commits
4 Commits
develop
...
new.semant
Author | SHA1 | Date |
---|---|---|
|
a05198548e | |
|
81c2021be7 | |
|
6b4085043c | |
|
96e24551bb |
|
@ -1,12 +1,9 @@
|
|||
defaults: &defaults
|
||||
working_directory: ~/repo
|
||||
|
||||
orbs:
|
||||
android: circleci/android@2.1.2
|
||||
|
||||
macos: &macos
|
||||
macos:
|
||||
xcode: "14.2.0"
|
||||
xcode: "13.3.0"
|
||||
resource_class: large
|
||||
|
||||
bash-env: &bash-env
|
||||
|
@ -54,14 +51,14 @@ save-gems-cache: &save-gems-cache
|
|||
update-fastlane-ios: &update-fastlane-ios
|
||||
name: Update Fastlane
|
||||
command: |
|
||||
echo "ruby-2.7.7" > ~/.ruby-version
|
||||
echo "ruby-2.6.4" > ~/.ruby-version
|
||||
bundle install
|
||||
working_directory: ios
|
||||
|
||||
update-fastlane-android: &update-fastlane-android
|
||||
name: Update Fastlane
|
||||
command: |
|
||||
echo "ruby-2.7.7" > ~/.ruby-version
|
||||
echo "ruby-2.6.4" > ~/.ruby-version
|
||||
bundle install
|
||||
working_directory: android
|
||||
|
||||
|
@ -121,26 +118,26 @@ commands:
|
|||
if [[ $CIRCLE_JOB == "android-build-official" ]]; then
|
||||
echo -e "APPLICATION_ID=chat.rocket.android" >> ./gradle.properties
|
||||
echo -e "BugsnagAPIKey=$BUGSNAG_KEY_OFFICIAL" >> ./gradle.properties
|
||||
echo $KEYSTORE_OFFICIAL_BASE64 | base64 --decode > ./app/$KEYSTORE_OFFICIAL
|
||||
echo $CHAT_ROCKET_ANDROID_STORE_FILE_BASE64_JKS | base64 --decode > ./app/$KEYSTORE_OFFICIAL
|
||||
echo -e "KEYSTORE=$KEYSTORE_OFFICIAL" >> ./gradle.properties
|
||||
echo -e "KEYSTORE_PASSWORD=$KEYSTORE_OFFICIAL_PASSWORD" >> ./gradle.properties
|
||||
echo -e "KEY_ALIAS=$KEYSTORE_OFFICIAL_ALIAS" >> ./gradle.properties
|
||||
echo -e "KEY_PASSWORD=$KEYSTORE_OFFICIAL_PASSWORD" >> ./gradle.properties
|
||||
echo -e "KEYSTORE_PASSWORD=$CHAT_ROCKET_ANDROID_STORE_PASSWORD" >> ./gradle.properties
|
||||
echo -e "KEY_ALIAS=$CHAT_ROCKET_ANDROID_KEY_ALIAS" >> ./gradle.properties
|
||||
echo -e "KEY_PASSWORD=$CHAT_ROCKET_ANDROID_KEY_PASSWORD" >> ./gradle.properties
|
||||
else
|
||||
echo -e "APPLICATION_ID=chat.rocket.reactnative" >> ./gradle.properties
|
||||
echo -e "BugsnagAPIKey=$BUGSNAG_KEY" >> ./gradle.properties
|
||||
echo $KEYSTORE_EXPERIMENTAL_BASE64 | base64 --decode > ./app/$KEYSTORE_EXPERIMENTAL
|
||||
echo -e "KEYSTORE=$KEYSTORE_EXPERIMENTAL" >> ./gradle.properties
|
||||
echo -e "KEYSTORE_PASSWORD=$KEYSTORE_EXPERIMENTAL_PASSWORD" >> ./gradle.properties
|
||||
echo -e "KEY_ALIAS=$KEYSTORE_EXPERIMENTAL_ALIAS" >> ./gradle.properties
|
||||
echo -e "KEY_PASSWORD=$KEYSTORE_EXPERIMENTAL_PASSWORD" >> ./gradle.properties
|
||||
echo $KEYSTORE_BASE64 | base64 --decode > ./app/$KEYSTORE
|
||||
echo -e "KEYSTORE=$KEYSTORE" >> ./gradle.properties
|
||||
echo -e "KEYSTORE_PASSWORD=$KEYSTORE_PASSWORD" >> ./gradle.properties
|
||||
echo -e "KEY_ALIAS=$KEY_ALIAS" >> ./gradle.properties
|
||||
echo -e "KEY_PASSWORD=$KEYSTORE_PASSWORD" >> ./gradle.properties
|
||||
fi
|
||||
working_directory: android
|
||||
|
||||
- run:
|
||||
name: Set Google Services
|
||||
command: |
|
||||
if [[ $GOOGLE_SERVICES_ANDROID ]]; then
|
||||
if [[ $KEYSTORE ]]; then
|
||||
echo $GOOGLE_SERVICES_ANDROID | base64 --decode > google-services.json
|
||||
fi
|
||||
working_directory: android/app
|
||||
|
@ -154,7 +151,7 @@ commands:
|
|||
if [[ $CIRCLE_JOB == "android-build-experimental" || "android-automatic-build-experimental" ]]; then
|
||||
./gradlew bundleExperimentalPlayRelease
|
||||
fi
|
||||
if [[ ! $GOOGLE_SERVICES_ANDROID ]]; then
|
||||
if [[ ! $KEYSTORE ]]; then
|
||||
./gradlew assembleExperimentalPlayDebug
|
||||
fi
|
||||
working_directory: android
|
||||
|
@ -203,12 +200,8 @@ commands:
|
|||
- run:
|
||||
name: Set Google Services
|
||||
command: |
|
||||
if [[ $APP_STORE_CONNECT_API_KEY_BASE64 ]]; then
|
||||
if [[ $CIRCLE_JOB == "ios-build-official" ]]; then
|
||||
echo $GOOGLE_SERVICES_IOS | base64 --decode > GoogleService-Info.plist
|
||||
else
|
||||
echo $GOOGLE_SERVICES_IOS_EXPERIMENTAL | base64 --decode > GoogleService-Info.plist
|
||||
fi
|
||||
if [[ $KEYSTORE ]]; then
|
||||
echo $GOOGLE_SERVICES_IOS | base64 --decode > GoogleService-Info.plist
|
||||
fi
|
||||
working_directory: ios
|
||||
- run:
|
||||
|
@ -230,12 +223,12 @@ commands:
|
|||
/usr/libexec/PlistBuddy -c "Set IS_OFFICIAL NO" ./NotificationService/Info.plist
|
||||
fi
|
||||
|
||||
if [[ $APP_STORE_CONNECT_API_KEY_BASE64 ]]; then
|
||||
echo $APP_STORE_CONNECT_API_KEY_BASE64 | base64 --decode > ./fastlane/app_store_connect_api_key.p8
|
||||
if [[ $APP_STORE_CONNECT_API_BASE64 ]]; then
|
||||
echo $APP_STORE_CONNECT_API_BASE64 | base64 --decode > ./fastlane/app_store_connect_api_key.p8
|
||||
if [[ $CIRCLE_JOB == "ios-build-official" ]]; then
|
||||
bundle exec fastlane ios build_official
|
||||
else
|
||||
if [[ $APP_STORE_CONNECT_API_KEY_BASE64 ]]; then
|
||||
if [[ $KEYSTORE ]]; then
|
||||
bundle exec fastlane ios build_experimental
|
||||
else
|
||||
bundle exec fastlane ios build_fork
|
||||
|
@ -325,19 +318,11 @@ commands:
|
|||
- run:
|
||||
name: Fastlane Tesflight Upload
|
||||
command: |
|
||||
echo $APP_STORE_CONNECT_API_KEY_BASE64 | base64 --decode > ./fastlane/app_store_connect_api_key.p8
|
||||
echo $APP_STORE_CONNECT_API_BASE64 | base64 --decode > ./fastlane/app_store_connect_api_key.p8
|
||||
bundle exec fastlane ios beta official:<< parameters.official >>
|
||||
working_directory: ios
|
||||
- save_cache: *save-gems-cache
|
||||
|
||||
create-e2e-account-file:
|
||||
description: "Create e2e account file"
|
||||
steps:
|
||||
- run:
|
||||
command: |
|
||||
echo $E2E_ACCOUNT | base64 --decode > ./e2e_account.ts
|
||||
working_directory: e2e
|
||||
|
||||
version: 2.1
|
||||
|
||||
# EXECUTORS
|
||||
|
@ -360,26 +345,26 @@ jobs:
|
|||
steps:
|
||||
- checkout
|
||||
|
||||
- restore_cache: *restore-npm-cache-linux
|
||||
# - restore_cache: *restore-npm-cache-linux
|
||||
|
||||
- run: *install-npm-modules
|
||||
# - run: *install-npm-modules
|
||||
|
||||
- run:
|
||||
name: Lint
|
||||
command: |
|
||||
yarn lint
|
||||
# - run:
|
||||
# name: Lint
|
||||
# command: |
|
||||
# yarn lint
|
||||
|
||||
- run:
|
||||
name: Test
|
||||
command: |
|
||||
yarn test -w 8
|
||||
# - run:
|
||||
# name: Test
|
||||
# command: |
|
||||
# yarn test -w 8
|
||||
|
||||
- run:
|
||||
name: Codecov
|
||||
command: |
|
||||
yarn codecov
|
||||
# - run:
|
||||
# name: Codecov
|
||||
# command: |
|
||||
# yarn codecov
|
||||
|
||||
- save_cache: *save-npm-cache-linux
|
||||
# - save_cache: *save-npm-cache-linux
|
||||
|
||||
# Android builds
|
||||
android-build-experimental:
|
||||
|
@ -449,94 +434,6 @@ jobs:
|
|||
- upload-to-google-play-beta:
|
||||
official: true
|
||||
|
||||
e2e-build-android:
|
||||
<<: *defaults
|
||||
executor:
|
||||
name: android/android-machine
|
||||
resource-class: xlarge
|
||||
tag: 2022.12.1
|
||||
environment:
|
||||
<<: *android-env
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache: *restore-npm-cache-linux
|
||||
- run: *install-npm-modules
|
||||
- save_cache: *save-npm-cache-linux
|
||||
- restore_cache: *restore-gradle-cache
|
||||
- run:
|
||||
name: Configure Gradle
|
||||
command: |
|
||||
echo -e "" > ./gradle.properties
|
||||
# echo -e "android.enableAapt2=false" >> ./gradle.properties
|
||||
echo -e "android.useAndroidX=true" >> ./gradle.properties
|
||||
echo -e "android.enableJetifier=true" >> ./gradle.properties
|
||||
echo -e "newArchEnabled=false" >> ./gradle.properties
|
||||
echo -e "FLIPPER_VERSION=0.125.0" >> ./gradle.properties
|
||||
echo -e "VERSIONCODE=$CIRCLE_BUILD_NUM" >> ./gradle.properties
|
||||
echo -e "APPLICATION_ID=chat.rocket.reactnative" >> ./gradle.properties
|
||||
echo -e "BugsnagAPIKey=$BUGSNAG_KEY" >> ./gradle.properties
|
||||
echo $KEYSTORE_EXPERIMENTAL_BASE64 | base64 --decode > ./app/$KEYSTORE_EXPERIMENTAL
|
||||
echo -e "KEYSTORE=$KEYSTORE_EXPERIMENTAL" >> ./gradle.properties
|
||||
echo -e "KEYSTORE_PASSWORD=$KEYSTORE_EXPERIMENTAL_PASSWORD" >> ./gradle.properties
|
||||
echo -e "KEY_ALIAS=$KEYSTORE_EXPERIMENTAL_ALIAS" >> ./gradle.properties
|
||||
echo -e "KEY_PASSWORD=$KEYSTORE_EXPERIMENTAL_PASSWORD" >> ./gradle.properties
|
||||
working_directory: android
|
||||
- run:
|
||||
name: Build Android
|
||||
command: |
|
||||
echo "RUNNING_E2E_TESTS=true" > ./.env
|
||||
yarn e2e:android-build
|
||||
- save_cache: *save-gradle-cache
|
||||
- store_artifacts:
|
||||
path: android/app/build/outputs/apk/experimentalPlay/release/app-experimental-play-release.apk
|
||||
- store_artifacts:
|
||||
path: android/app/build/outputs/apk/androidTest/experimentalPlay/release/app-experimental-play-release-androidTest.apk
|
||||
- persist_to_workspace:
|
||||
root: /home/circleci/repo
|
||||
paths:
|
||||
- android/app/build/outputs/apk/
|
||||
|
||||
e2e-test-android:
|
||||
<<: *defaults
|
||||
executor:
|
||||
name: android/android-machine
|
||||
resource-class: xlarge
|
||||
tag: 2022.12.1
|
||||
parallelism: 4
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /home/circleci/repo
|
||||
- restore_cache: *restore-npm-cache-linux
|
||||
- run: *install-npm-modules
|
||||
- save_cache: *save-npm-cache-linux
|
||||
- run: mkdir ~/junit
|
||||
- create-e2e-account-file
|
||||
- android/create-avd:
|
||||
avd-name: Pixel_API_31_AOSP
|
||||
install: true
|
||||
system-image: system-images;android-31;default;x86_64
|
||||
- run:
|
||||
name: Setup emulator
|
||||
command: |
|
||||
echo "hw.lcd.density = 440" >> ~/.android/avd/Pixel_API_31_AOSP.avd/config.ini
|
||||
echo "hw.lcd.height = 2280" >> ~/.android/avd/Pixel_API_31_AOSP.avd/config.ini
|
||||
echo "hw.lcd.width = 1080" >> ~/.android/avd/Pixel_API_31_AOSP.avd/config.ini
|
||||
- run:
|
||||
name: Run Detox Tests
|
||||
command: |
|
||||
TEST=$(circleci tests glob "e2e/tests/**/*.ts" | circleci tests split --split-by=timings)
|
||||
yarn e2e:android-test $TEST
|
||||
- store_artifacts:
|
||||
path: artifacts
|
||||
- run:
|
||||
command: cp junit.xml ~/junit/
|
||||
when: always
|
||||
- store_test_results:
|
||||
path: ~/junit
|
||||
- store_artifacts:
|
||||
path: ~/junit
|
||||
|
||||
# iOS builds
|
||||
ios-build-experimental:
|
||||
executor: mac-env
|
||||
|
@ -560,89 +457,11 @@ jobs:
|
|||
- upload-to-testflight:
|
||||
official: true
|
||||
|
||||
e2e-build-ios:
|
||||
executor: mac-env
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache: *restore-gems-cache
|
||||
- restore_cache: *restore-npm-cache-mac
|
||||
- run: *install-npm-modules
|
||||
- run: *update-fastlane-ios
|
||||
- save_cache: *save-npm-cache-mac
|
||||
- save_cache: *save-gems-cache
|
||||
- manage-pods
|
||||
- run:
|
||||
name: Configure Detox
|
||||
command: |
|
||||
brew tap wix/brew
|
||||
brew install applesimutils
|
||||
- run:
|
||||
name: Build
|
||||
command: |
|
||||
/usr/libexec/PlistBuddy -c "Set :bugsnag:apiKey $BUGSNAG_KEY" ./ios/RocketChatRN/Info.plist
|
||||
/usr/libexec/PlistBuddy -c "Set :bugsnag:apiKey $BUGSNAG_KEY" ./ios/ShareRocketChatRN/Info.plist
|
||||
yarn detox clean-framework-cache && yarn detox build-framework-cache
|
||||
echo "RUNNING_E2E_TESTS=true" > ./.env
|
||||
yarn e2e:ios-build
|
||||
- persist_to_workspace:
|
||||
root: /Users/distiller/project
|
||||
paths:
|
||||
- ios/build/Build/Products/Release-iphonesimulator/Rocket.Chat Experimental.app
|
||||
|
||||
e2e-test-ios:
|
||||
executor: mac-env
|
||||
parallelism: 5
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /Users/distiller/project
|
||||
- restore_cache: *restore-npm-cache-mac
|
||||
- run: *install-npm-modules
|
||||
- save_cache: *save-npm-cache-mac
|
||||
- run: mkdir ~/junit
|
||||
- run:
|
||||
name: Configure Detox
|
||||
command: |
|
||||
brew tap wix/brew
|
||||
brew install applesimutils
|
||||
yarn detox clean-framework-cache && yarn detox build-framework-cache
|
||||
- create-e2e-account-file
|
||||
- run:
|
||||
name: Run tests
|
||||
command: |
|
||||
TEST=$(circleci tests glob "e2e/tests/**/*.ts" | circleci tests split --split-by=timings)
|
||||
yarn e2e:ios-test $TEST
|
||||
- store_artifacts:
|
||||
path: artifacts
|
||||
- run:
|
||||
command: cp junit.xml ~/junit/
|
||||
when: always
|
||||
- store_test_results:
|
||||
path: ~/junit
|
||||
- store_artifacts:
|
||||
path: ~/junit
|
||||
|
||||
workflows:
|
||||
build-and-test:
|
||||
jobs:
|
||||
- lint-testunit
|
||||
|
||||
# E2E tests
|
||||
- e2e-hold:
|
||||
type: approval
|
||||
- e2e-build-ios:
|
||||
requires:
|
||||
- e2e-hold
|
||||
- e2e-test-ios:
|
||||
requires:
|
||||
- e2e-build-ios
|
||||
- e2e-build-android:
|
||||
requires:
|
||||
- e2e-hold
|
||||
- e2e-test-android:
|
||||
requires:
|
||||
- e2e-build-android
|
||||
|
||||
# iOS Experimental
|
||||
- ios-hold-build-experimental:
|
||||
type: approval
|
||||
|
|
91
.detoxrc.js
91
.detoxrc.js
|
@ -1,91 +0,0 @@
|
|||
/** @type {Detox.DetoxConfig} */
|
||||
module.exports = {
|
||||
testRunner: {
|
||||
args: {
|
||||
$0: 'jest',
|
||||
config: 'e2e/jest.config.js'
|
||||
},
|
||||
retries: process.env.CI ? 3 : 0
|
||||
},
|
||||
artifacts: {
|
||||
plugins: {
|
||||
screenshot: 'failing',
|
||||
video: 'failing',
|
||||
uiHierarchy: 'enabled'
|
||||
}
|
||||
},
|
||||
apps: {
|
||||
'ios.debug': {
|
||||
type: 'ios.app',
|
||||
binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/Rocket.Chat Experimental.app',
|
||||
build:
|
||||
'xcodebuild -workspace ios/RocketChatRN.xcworkspace -scheme RocketChatRN -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build'
|
||||
},
|
||||
'ios.release': {
|
||||
type: 'ios.app',
|
||||
binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/Rocket.Chat Experimental.app',
|
||||
build:
|
||||
'xcodebuild -workspace ios/RocketChatRN.xcworkspace -scheme RocketChatRN -configuration Release -sdk iphonesimulator -derivedDataPath ios/build'
|
||||
},
|
||||
'android.debug': {
|
||||
type: 'android.apk',
|
||||
binaryPath: 'android/app/build/outputs/apk/experimentalPlay/debug/app-experimental-play-debug.apk',
|
||||
build:
|
||||
'cd android ; ./gradlew assembleExperimentalPlayDebug assembleExperimentalPlayDebugAndroidTest -DtestBuildType=debug ; cd -',
|
||||
reversePorts: [8081]
|
||||
},
|
||||
'android.release': {
|
||||
type: 'android.apk',
|
||||
binaryPath: 'android/app/build/outputs/apk/experimentalPlay/release/app-experimental-play-release.apk',
|
||||
build:
|
||||
'cd android ; ./gradlew assembleExperimentalPlayRelease assembleExperimentalPlayReleaseAndroidTest -DtestBuildType=release ; cd -'
|
||||
}
|
||||
},
|
||||
devices: {
|
||||
simulator: {
|
||||
type: 'ios.simulator',
|
||||
device: {
|
||||
type: 'iPhone 14'
|
||||
}
|
||||
},
|
||||
attached: {
|
||||
type: 'android.attached',
|
||||
device: {
|
||||
adbName: '.*'
|
||||
}
|
||||
},
|
||||
emulator: {
|
||||
type: 'android.emulator',
|
||||
device: {
|
||||
avdName: 'Pixel_API_31_AOSP'
|
||||
},
|
||||
headless: process.env.CI ? true : false
|
||||
}
|
||||
},
|
||||
configurations: {
|
||||
'ios.sim.debug': {
|
||||
device: 'simulator',
|
||||
app: 'ios.debug'
|
||||
},
|
||||
'ios.sim.release': {
|
||||
device: 'simulator',
|
||||
app: 'ios.release'
|
||||
},
|
||||
'android.att.debug': {
|
||||
device: 'attached',
|
||||
app: 'android.debug'
|
||||
},
|
||||
'android.att.release': {
|
||||
device: 'attached',
|
||||
app: 'android.release'
|
||||
},
|
||||
'android.emu.debug': {
|
||||
device: 'emulator',
|
||||
app: 'android.debug'
|
||||
},
|
||||
'android.emu.release': {
|
||||
device: 'emulator',
|
||||
app: 'android.release'
|
||||
}
|
||||
}
|
||||
};
|
13
.eslintrc.js
13
.eslintrc.js
|
@ -240,8 +240,19 @@ module.exports = {
|
|||
},
|
||||
{
|
||||
files: ['e2e/**'],
|
||||
globals: {
|
||||
by: true,
|
||||
detox: true,
|
||||
device: true,
|
||||
element: true,
|
||||
waitFor: true
|
||||
},
|
||||
rules: {
|
||||
'no-await-in-loop': 0
|
||||
'import/no-extraneous-dependencies': 0,
|
||||
'no-await-in-loop': 0,
|
||||
'no-restricted-syntax': 0,
|
||||
// TODO: remove this rule when update Detox to 20 and test if the namespace Detox is available
|
||||
'no-undef': 1
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -67,6 +67,5 @@ e2e/docker/rc_test_env/docker-compose.yml
|
|||
e2e/docker/data/db
|
||||
e2e/e2e_account.js
|
||||
e2e/e2e_account.ts
|
||||
junit.xml
|
||||
|
||||
*.p8
|
|
@ -1 +1 @@
|
|||
2.7.7
|
||||
2.7.4
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -1,4 +1,4 @@
|
|||
source 'https://rubygems.org'
|
||||
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
|
||||
ruby '2.7.7'
|
||||
ruby '2.7.4'
|
||||
gem 'cocoapods', '~> 1.11', '>= 1.11.2'
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -147,7 +147,7 @@ android {
|
|||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode VERSIONCODE as Integer
|
||||
versionName "4.37.0"
|
||||
versionName "4.35.0"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
if (!isFoss) {
|
||||
manifestPlaceholders = [BugsnagAPIKey: BugsnagAPIKey as String]
|
||||
|
@ -250,7 +250,6 @@ android {
|
|||
release {
|
||||
minifyEnabled enableProguardInReleaseBuilds
|
||||
setProguardFiles([getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'])
|
||||
proguardFile "${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro"
|
||||
signingConfig signingConfigs.release
|
||||
if (!isFoss) {
|
||||
firebaseCrashlytics {
|
||||
|
@ -269,11 +268,6 @@ android {
|
|||
// pickFirst '**/x86_64/libc++_shared.so'
|
||||
// }
|
||||
|
||||
// FIXME: Remove when we update RN
|
||||
packagingOptions {
|
||||
pickFirst '**/*.so'
|
||||
}
|
||||
|
||||
// applicationVariants are e.g. debug, release
|
||||
|
||||
flavorDimensions "app", "type"
|
||||
|
@ -286,6 +280,10 @@ android {
|
|||
dimension = "app"
|
||||
buildConfigField "boolean", "IS_OFFICIAL", "false"
|
||||
}
|
||||
e2e {
|
||||
dimension = "app"
|
||||
buildConfigField "boolean", "IS_OFFICIAL", "false"
|
||||
}
|
||||
foss {
|
||||
dimension = "type"
|
||||
buildConfigField "boolean", "FDROID_BUILD", "true"
|
||||
|
@ -313,6 +311,16 @@ android {
|
|||
java.srcDirs = ['src/main/java', 'src/play/java']
|
||||
manifest.srcFile 'src/play/AndroidManifest.xml'
|
||||
}
|
||||
e2ePlayDebug {
|
||||
java.srcDirs = ['src/main/java', 'src/play/java']
|
||||
res.srcDirs = ['src/experimental/res']
|
||||
manifest.srcFile 'src/play/AndroidManifest.xml'
|
||||
}
|
||||
e2ePlayRelease {
|
||||
java.srcDirs = ['src/main/java', 'src/play/java']
|
||||
res.srcDirs = ['src/experimental/res']
|
||||
manifest.srcFile 'src/play/AndroidManifest.xml'
|
||||
}
|
||||
}
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
|
@ -377,9 +385,8 @@ dependencies {
|
|||
implementation "com.github.bumptech.glide:glide:4.9.0"
|
||||
annotationProcessor "com.github.bumptech.glide:compiler:4.9.0"
|
||||
implementation "com.tencent:mmkv-static:1.2.10"
|
||||
androidTestImplementation('com.wix:detox:+')
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'com.facebook.soloader:soloader:0.10.4'
|
||||
androidTestImplementation('com.wix:detox:+') { transitive = true }
|
||||
androidTestImplementation 'junit:junit:4.12'
|
||||
}
|
||||
|
||||
if (isNewArchitectureEnabled()) {
|
||||
|
|
|
@ -18,7 +18,7 @@ public class DetoxTest {
|
|||
@Rule
|
||||
// Replace 'MainActivity' with the value of android:name entry in
|
||||
// <activity> in AndroidManifest.xml
|
||||
public ActivityTestRule<chat.rocket.reactnative.MainActivity> mActivityRule = new ActivityTestRule<>(chat.rocket.reactnative.MainActivity.class, false, false);
|
||||
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false);
|
||||
|
||||
@Test
|
||||
public void runDetoxTests() {
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package chat.rocket.reactnative;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
|
||||
public class MainDebugApplication extends MainApplication {
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
|
||||
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
*
|
||||
* @param context
|
||||
* @param reactInstanceManager
|
||||
*/
|
||||
private static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
|
||||
ReactNativeFlipper.initializeFlipper(context, reactInstanceManager);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config xmlns:tools="http://schemas.android.com/tools">
|
||||
<base-config cleartextTrafficPermitted="true">
|
||||
<trust-anchors>
|
||||
<certificates src="system" />
|
||||
<certificates src="user"
|
||||
tools:ignore="AcceptsUserCertificates" />
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
</network-security-config>
|
|
@ -11,9 +11,6 @@
|
|||
<uses-permission android:name="android.permission.VIDEO_CAPTURE" />
|
||||
<uses-permission android:name="android.permission.AUDIO_CAPTURE" />
|
||||
|
||||
<!-- permissions related to jitsi call -->
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
|
||||
<application
|
||||
android:name="chat.rocket.reactnative.MainApplication"
|
||||
android:allowBackup="false"
|
||||
|
|
Binary file not shown.
|
@ -9,16 +9,13 @@ import com.facebook.react.ReactApplication;
|
|||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.config.ReactFeatureFlags;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
import com.reactnativecommunity.viewpager.RNCViewPagerPackage;
|
||||
import com.facebook.react.bridge.JSIModulePackage;
|
||||
import com.swmansion.reanimated.ReanimatedJSIModulePackage;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import expo.modules.ApplicationLifecycleDispatcher;
|
||||
import expo.modules.ReactNativeHostWrapper;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
@ -79,7 +76,6 @@ public class MainApplication extends Application implements ReactApplication {
|
|||
// If you opted-in for the New Architecture, we enable the TurboModule system
|
||||
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
ApplicationLifecycleDispatcher.onApplicationCreate(this);
|
||||
}
|
||||
|
||||
|
@ -88,35 +84,4 @@ public class MainApplication extends Application implements ReactApplication {
|
|||
super.onConfigurationChanged(newConfig);
|
||||
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
|
||||
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
*
|
||||
* @param context
|
||||
* @param reactInstanceManager
|
||||
*/
|
||||
private static void initializeFlipper(
|
||||
Context context, ReactInstanceManager reactInstanceManager) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
try {
|
||||
/*
|
||||
We use reflection here to pick up the class that initializes Flipper,
|
||||
since Flipper library is not available in release mode
|
||||
*/
|
||||
Class<?> aClass = Class.forName("chat.rocket.reactnative.ReactNativeFlipper");
|
||||
aClass
|
||||
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
|
||||
.invoke(null, context, reactInstanceManager);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,4 @@
|
|||
tools:ignore="AcceptsUserCertificates" />
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">10.0.2.2</domain>
|
||||
<domain includeSubdomains="true">localhost</domain>
|
||||
</domain-config>
|
||||
</network-security-config>
|
|
@ -1,5 +1,9 @@
|
|||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
def safeExtGet(prop, fallback) {
|
||||
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
||||
}
|
||||
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
def taskRequests = getGradle().getStartParameter().getTaskRequests().toString().toLowerCase()
|
||||
|
@ -71,38 +75,5 @@ allprojects {
|
|||
google()
|
||||
maven { url 'https://maven.google.com' }
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
|
||||
// https://stackoverflow.com/a/74333788/5447468
|
||||
// TODO: remove once we update RN
|
||||
exclusiveContent {
|
||||
// We get React Native's Android binaries exclusively through npm,
|
||||
// from a local Maven repo inside node_modules/react-native/.
|
||||
// (The use of exclusiveContent prevents looking elsewhere like Maven Central
|
||||
// and potentially getting a wrong version.)
|
||||
filter {
|
||||
includeGroup "com.facebook.react"
|
||||
}
|
||||
forRepository {
|
||||
maven {
|
||||
// NOTE: if you are in a monorepo, you may have "$rootDir/../../../node_modules/react-native/android"
|
||||
url "$rootDir/../node_modules/react-native/android"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subprojects { subproject ->
|
||||
afterEvaluate {
|
||||
if (!project.name.equalsIgnoreCase("app") && project.hasProperty("android")) {
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
buildToolsVersion "31.0.0"
|
||||
defaultConfig {
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 31
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ android.useAndroidX=true
|
|||
android.enableJetifier=true
|
||||
|
||||
# Version of flipper SDK to use with React Native
|
||||
FLIPPER_VERSION=0.175.0
|
||||
FLIPPER_VERSION=0.125.0
|
||||
|
||||
# Use this property to specify which architecture you want to build.
|
||||
# You can also override it from the CLI using
|
||||
|
|
|
@ -28,9 +28,7 @@ export const ROOM = createRequestTypes('ROOM', [
|
|||
'DELETE',
|
||||
'REMOVED',
|
||||
'FORWARD',
|
||||
'USER_TYPING',
|
||||
'HISTORY_REQUEST',
|
||||
'HISTORY_FINISHED'
|
||||
'USER_TYPING'
|
||||
]);
|
||||
export const INQUIRY = createRequestTypes('INQUIRY', [
|
||||
...defaultTypes,
|
||||
|
@ -40,14 +38,7 @@ export const INQUIRY = createRequestTypes('INQUIRY', [
|
|||
'QUEUE_UPDATE',
|
||||
'QUEUE_REMOVE'
|
||||
]);
|
||||
export const APP = createRequestTypes('APP', [
|
||||
'START',
|
||||
'READY',
|
||||
'INIT',
|
||||
'INIT_LOCAL_SETTINGS',
|
||||
'SET_MASTER_DETAIL',
|
||||
'SET_NOTIFICATION_PRESENCE_CAP'
|
||||
]);
|
||||
export const APP = createRequestTypes('APP', ['START', 'READY', 'INIT', 'INIT_LOCAL_SETTINGS', 'SET_MASTER_DETAIL']);
|
||||
export const MESSAGES = createRequestTypes('MESSAGES', ['REPLY_BROADCAST']);
|
||||
export const CREATE_CHANNEL = createRequestTypes('CREATE_CHANNEL', [...defaultTypes]);
|
||||
export const CREATE_DISCUSSION = createRequestTypes('CREATE_DISCUSSION', [...defaultTypes]);
|
||||
|
|
|
@ -12,11 +12,7 @@ interface ISetMasterDetail extends Action {
|
|||
isMasterDetail: boolean;
|
||||
}
|
||||
|
||||
interface ISetNotificationPresenceCap extends Action {
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
export type TActionApp = IAppStart & ISetMasterDetail & ISetNotificationPresenceCap;
|
||||
export type TActionApp = IAppStart & ISetMasterDetail;
|
||||
|
||||
interface Params {
|
||||
root: RootEnum;
|
||||
|
@ -55,10 +51,3 @@ export function setMasterDetail(isMasterDetail: boolean): ISetMasterDetail {
|
|||
isMasterDetail
|
||||
};
|
||||
}
|
||||
|
||||
export function setNotificationPresenceCap(show: boolean): ISetNotificationPresenceCap {
|
||||
return {
|
||||
type: APP.SET_NOTIFICATION_PRESENCE_CAP,
|
||||
show
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Action } from 'redux';
|
||||
|
||||
import { ERoomType, RoomType } from '../definitions';
|
||||
import { ERoomType } from '../definitions/ERoomType';
|
||||
import { ROOM } from './actionsTypes';
|
||||
|
||||
// TYPE RETURN RELATED
|
||||
|
@ -44,24 +44,7 @@ interface IUserTyping extends Action {
|
|||
status: boolean;
|
||||
}
|
||||
|
||||
export interface IRoomHistoryRequest extends Action {
|
||||
rid: string;
|
||||
t: RoomType;
|
||||
loaderId: string;
|
||||
}
|
||||
|
||||
export interface IRoomHistoryFinished extends Action {
|
||||
loaderId: string;
|
||||
}
|
||||
|
||||
export type TActionsRoom = TSubscribeRoom &
|
||||
TUnsubscribeRoom &
|
||||
ILeaveRoom &
|
||||
IDeleteRoom &
|
||||
IForwardRoom &
|
||||
IUserTyping &
|
||||
IRoomHistoryRequest &
|
||||
IRoomHistoryFinished;
|
||||
export type TActionsRoom = TSubscribeRoom & TUnsubscribeRoom & ILeaveRoom & IDeleteRoom & IForwardRoom & IUserTyping;
|
||||
|
||||
export function subscribeRoom(rid: string): TSubscribeRoom {
|
||||
return {
|
||||
|
@ -116,19 +99,3 @@ export function userTyping(rid: string, status = true): IUserTyping {
|
|||
status
|
||||
};
|
||||
}
|
||||
|
||||
export function roomHistoryRequest({ rid, t, loaderId }: { rid: string; t: RoomType; loaderId: string }): IRoomHistoryRequest {
|
||||
return {
|
||||
type: ROOM.HISTORY_REQUEST,
|
||||
rid,
|
||||
t,
|
||||
loaderId
|
||||
};
|
||||
}
|
||||
|
||||
export function roomHistoryFinished({ loaderId }: { loaderId: string }): IRoomHistoryFinished {
|
||||
return {
|
||||
type: ROOM.HISTORY_FINISHED,
|
||||
loaderId
|
||||
};
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ const ActionSheet = React.memo(
|
|||
handleComponent={renderHandle}
|
||||
enablePanDownToClose
|
||||
style={{ ...styles.container, ...bottomSheet }}
|
||||
backgroundStyle={{ backgroundColor: colors.focusedBackground }}
|
||||
backgroundStyle={{ backgroundColor: colors.backgroundColor }}
|
||||
onChange={index => index === -1 && onClose()}
|
||||
// We need this to allow horizontal swipe gesture inside the bottom sheet like in reaction picker
|
||||
enableContentPanningGesture={data?.enableContentPanningGesture ?? true}
|
||||
|
|
|
@ -126,7 +126,7 @@ const ActionSheetContentWithInputAndSubmit = ({
|
|||
/>
|
||||
) : null}
|
||||
<FooterButtons
|
||||
confirmBackgroundColor={confirmBackgroundColor || colors.actionTintColor}
|
||||
confirmBackgroundColor={confirmBackgroundColor || colors.tintColor}
|
||||
cancelAction={onCancel || hideActionSheet}
|
||||
confirmAction={() => onSubmit(inputValue)}
|
||||
cancelTitle={i18n.t('Cancel')}
|
||||
|
|
|
@ -24,7 +24,7 @@ const BottomSheetContent = React.memo(({ options, hasCancel, hide, children }: I
|
|||
hasCancel ? (
|
||||
<Touch
|
||||
onPress={hide}
|
||||
style={[styles.button, { backgroundColor: colors.auxiliaryBackground }]}
|
||||
style={[styles.button, { backgroundColor: colors.backgroundColor }]}
|
||||
accessibilityLabel={I18n.t('Cancel')}
|
||||
>
|
||||
<Text style={[styles.text, { color: colors.bodyText }]}>{I18n.t('Cancel')}</Text>
|
||||
|
@ -42,7 +42,7 @@ const BottomSheetContent = React.memo(({ options, hasCancel, hide, children }: I
|
|||
keyExtractor={item => item.title}
|
||||
bounces={true}
|
||||
renderItem={renderItem}
|
||||
style={{ backgroundColor: colors.focusedBackground }}
|
||||
style={{ backgroundColor: colors.backgroundColor }}
|
||||
keyboardDismissMode='interactive'
|
||||
indicatorStyle='black'
|
||||
contentContainerStyle={styles.content}
|
||||
|
|
|
@ -21,7 +21,7 @@ export const Item = React.memo(({ item, hide }: IActionSheetItem) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Touch onPress={onPress} style={[styles.item, { backgroundColor: themes[theme].focusedBackground }]} testID={item.testID}>
|
||||
<Touch onPress={onPress} style={[styles.item, { backgroundColor: themes[theme].backgroundColor }]} testID={item.testID}>
|
||||
{item.icon ? (
|
||||
<CustomIcon name={item.icon} size={20} color={item.danger ? themes[theme].dangerColor : themes[theme].bodyText} />
|
||||
) : null}
|
||||
|
|
|
@ -36,8 +36,8 @@ const BackgroundContainer = ({ text, loading }: IBackgroundContainer): React.Rea
|
|||
return (
|
||||
<View style={styles.container}>
|
||||
<ImageBackground source={{ uri: `message_empty_${theme}` }} style={styles.image} />
|
||||
{text && !loading ? <Text style={[styles.text, { color: themes[theme].auxiliaryTintColor }]}>{text}</Text> : null}
|
||||
{loading ? <ActivityIndicator style={styles.text} color={themes[theme].auxiliaryTintColor} /> : null}
|
||||
{text && !loading ? <Text style={[styles.text, { color: themes[theme].auxiliaryText }]}>{text}</Text> : null}
|
||||
{loading ? <ActivityIndicator style={styles.text} color={themes[theme].auxiliaryText} /> : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -61,7 +61,7 @@ const Button = ({
|
|||
disabled={disabled || loading}
|
||||
style={[
|
||||
styles.container,
|
||||
backgroundColor ? { backgroundColor } : { backgroundColor: isPrimary ? colors.actionTintColor : colors.backgroundColor },
|
||||
backgroundColor ? { backgroundColor } : { backgroundColor: isPrimary ? colors.tintColor : colors.backgroundColor },
|
||||
disabled && styles.disabled,
|
||||
style
|
||||
]}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
export const mappedIcons = {
|
||||
'status-disabled': 59837,
|
||||
'lamp-bulb': 59836,
|
||||
'phone-in': 59835,
|
||||
'basketball': 59776,
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,31 +1,34 @@
|
|||
import React from 'react';
|
||||
import { useWindowDimensions } from 'react-native';
|
||||
import { FlatList } from 'react-native-gesture-handler';
|
||||
|
||||
import { IEmoji } from '../../definitions/IEmoji';
|
||||
import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps';
|
||||
import { PressableEmoji } from './PressableEmoji';
|
||||
import { EMOJI_BUTTON_SIZE } from './styles';
|
||||
import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps';
|
||||
import { IEmoji } from '../../definitions/IEmoji';
|
||||
import { PressableEmoji } from './PressableEmoji';
|
||||
|
||||
interface IEmojiCategoryProps {
|
||||
emojis: IEmoji[];
|
||||
onEmojiSelected: (emoji: IEmoji) => void;
|
||||
tabLabel?: string; // needed for react-native-scrollable-tab-view only
|
||||
parentWidth: number;
|
||||
}
|
||||
|
||||
const EmojiCategory = ({ onEmojiSelected, emojis, parentWidth }: IEmojiCategoryProps): React.ReactElement | null => {
|
||||
if (!parentWidth) {
|
||||
return null;
|
||||
}
|
||||
const EmojiCategory = ({ onEmojiSelected, emojis }: IEmojiCategoryProps): React.ReactElement | null => {
|
||||
const { width } = useWindowDimensions();
|
||||
|
||||
const numColumns = Math.trunc(parentWidth / EMOJI_BUTTON_SIZE);
|
||||
const marginHorizontal = (parentWidth % EMOJI_BUTTON_SIZE) / 2;
|
||||
const numColumns = Math.trunc(width / EMOJI_BUTTON_SIZE);
|
||||
const marginHorizontal = (width % EMOJI_BUTTON_SIZE) / 2;
|
||||
|
||||
const renderItem = ({ item }: { item: IEmoji }) => <PressableEmoji emoji={item} onPress={onEmojiSelected} />;
|
||||
|
||||
if (!width) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
key={`emoji-category-${parentWidth}`}
|
||||
// needed to update the numColumns when the width changes
|
||||
key={`emoji-category-${width}`}
|
||||
keyExtractor={item => (typeof item === 'string' ? item : item.name)}
|
||||
data={emojis}
|
||||
renderItem={renderItem}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import ScrollableTabView from 'react-native-scrollable-tab-view';
|
||||
|
||||
|
@ -20,8 +20,6 @@ const EmojiPicker = ({
|
|||
searchedEmojis = []
|
||||
}: IEmojiPickerProps): React.ReactElement | null => {
|
||||
const { colors } = useTheme();
|
||||
const [parentWidth, setParentWidth] = useState(0);
|
||||
|
||||
const { frequentlyUsed, loaded } = useFrequentlyUsedEmoji();
|
||||
|
||||
const allCustomEmojis: ICustomEmojis = useAppSelector(
|
||||
|
@ -52,14 +50,7 @@ const EmojiPicker = ({
|
|||
if (!emojis.length) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<EmojiCategory
|
||||
parentWidth={parentWidth}
|
||||
emojis={emojis}
|
||||
onEmojiSelected={(emoji: IEmoji) => handleEmojiSelect(emoji)}
|
||||
tabLabel={label}
|
||||
/>
|
||||
);
|
||||
return <EmojiCategory emojis={emojis} onEmojiSelected={(emoji: IEmoji) => handleEmojiSelect(emoji)} tabLabel={label} />;
|
||||
};
|
||||
|
||||
if (!loaded) {
|
||||
|
@ -67,13 +58,9 @@ const EmojiPicker = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<View style={styles.emojiPickerContainer} onLayout={e => setParentWidth(e.nativeEvent.layout.width)}>
|
||||
<View style={styles.emojiPickerContainer}>
|
||||
{searching ? (
|
||||
<EmojiCategory
|
||||
emojis={searchedEmojis}
|
||||
onEmojiSelected={(emoji: IEmoji) => handleEmojiSelect(emoji)}
|
||||
parentWidth={parentWidth}
|
||||
/>
|
||||
<EmojiCategory emojis={searchedEmojis} onEmojiSelected={(emoji: IEmoji) => handleEmojiSelect(emoji)} />
|
||||
) : (
|
||||
<ScrollableTabView
|
||||
renderTabBar={() => <TabBar />}
|
||||
|
@ -81,7 +68,7 @@ const EmojiPicker = ({
|
|||
keyboardShouldPersistTaps: 'always',
|
||||
keyboardDismissMode: 'none'
|
||||
}}
|
||||
style={{ backgroundColor: colors.messageboxBackground }}
|
||||
style={{ backgroundColor: colors.backgroundColor }}
|
||||
>
|
||||
{categories.tabs.map((tab: any, i) => renderCategory(tab.category, i, tab.tabLabel))}
|
||||
</ScrollableTabView>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import { STATUS_COLORS } from '../../lib/constants';
|
||||
import UnreadBadge from '../UnreadBadge';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
@ -16,8 +15,6 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
export const BadgeUnread = ({ ...props }): React.ReactElement => <UnreadBadge {...props} style={styles.badgeContainer} small />;
|
||||
export const Badge = ({ ...props }): React.ReactElement => <UnreadBadge {...props} style={styles.badgeContainer} small />;
|
||||
|
||||
export const BadgeWarn = (): React.ReactElement => (
|
||||
<View style={[styles.badgeContainer, { width: 10, height: 10, backgroundColor: STATUS_COLORS.disabled }]} />
|
||||
);
|
||||
export default Badge;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { Header, HeaderBackground } from '@react-navigation/elements';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
|
@ -104,10 +103,9 @@ export const Badge = () => (
|
|||
<HeaderExample
|
||||
left={() => (
|
||||
<HeaderButton.Container left>
|
||||
<HeaderButton.Item iconName='threads' badge={() => <HeaderButton.BadgeUnread tunread={[1]} />} />
|
||||
<HeaderButton.Item iconName='threads' badge={() => <HeaderButton.BadgeUnread tunread={[1]} tunreadUser={[1]} />} />
|
||||
<HeaderButton.Item iconName='threads' badge={() => <HeaderButton.BadgeUnread tunread={[1]} tunreadGroup={[1]} />} />
|
||||
<HeaderButton.Drawer badge={() => <HeaderButton.BadgeWarn />} />
|
||||
<HeaderButton.Item iconName='threads' badge={() => <HeaderButton.Badge tunread={[1]} />} />
|
||||
<HeaderButton.Item iconName='threads' badge={() => <HeaderButton.Badge tunread={[1]} tunreadUser={[1]} />} />
|
||||
<HeaderButton.Item iconName='threads' badge={() => <HeaderButton.Badge tunread={[1]} tunreadGroup={[1]} />} />
|
||||
</HeaderButton.Container>
|
||||
)}
|
||||
/>
|
||||
|
@ -116,23 +114,20 @@ export const Badge = () => (
|
|||
|
||||
const ThemeStory = ({ theme }: { theme: TSupportedThemes }) => (
|
||||
<ThemeContext.Provider value={{ theme, colors: colors[theme] }}>
|
||||
<View style={{ flexDirection: 'column' }}>
|
||||
<HeaderExample
|
||||
left={() => (
|
||||
<HeaderButton.Container left>
|
||||
<HeaderButton.Drawer badge={() => <HeaderButton.BadgeWarn />} />
|
||||
<HeaderButton.Item iconName='threads' />
|
||||
</HeaderButton.Container>
|
||||
)}
|
||||
right={() => (
|
||||
<HeaderButton.Container>
|
||||
<HeaderButton.Item title='Threads' />
|
||||
<HeaderButton.Item iconName='threads' badge={() => <HeaderButton.BadgeUnread tunread={[1]} />} />
|
||||
</HeaderButton.Container>
|
||||
)}
|
||||
colors={colors[theme]}
|
||||
/>
|
||||
</View>
|
||||
<HeaderExample
|
||||
left={() => (
|
||||
<HeaderButton.Container left>
|
||||
<HeaderButton.Item iconName='threads' />
|
||||
</HeaderButton.Container>
|
||||
)}
|
||||
right={() => (
|
||||
<HeaderButton.Container>
|
||||
<HeaderButton.Item title='Threads' />
|
||||
<HeaderButton.Item iconName='threads' badge={() => <HeaderButton.Badge tunread={[1]} />} />
|
||||
</HeaderButton.Container>
|
||||
)}
|
||||
colors={colors[theme]}
|
||||
/>
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export { default as Container } from './HeaderButtonContainer';
|
||||
export { default as Item } from './HeaderButtonItem';
|
||||
export * from './HeaderButtonItemBadge';
|
||||
export { default as Badge } from './HeaderButtonItemBadge';
|
||||
export * from './Common';
|
||||
|
|
|
@ -107,7 +107,7 @@ const NotifierComponent = React.memo(({ notification, isMasterDetail }: INotifie
|
|||
styles.container,
|
||||
(isMasterDetail || isLandscape) && styles.small,
|
||||
{
|
||||
backgroundColor: themes[theme].focusedBackground,
|
||||
backgroundColor: themes[theme].backgroundColor,
|
||||
borderColor: themes[theme].separatorColor,
|
||||
marginTop: insets.top
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ const ListHeader = React.memo(({ title, translateTitle = true }: IListHeader) =>
|
|||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={[styles.title, { color: themes[theme].infoText }]} numberOfLines={1}>
|
||||
<Text style={[styles.title, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>
|
||||
{translateTitle ? I18n.t(title) : title}
|
||||
</Text>
|
||||
</View>
|
||||
|
|
|
@ -27,7 +27,7 @@ const ListInfo = React.memo(({ info, translateInfo = true }: IListInfo) => {
|
|||
const { theme } = useTheme();
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={[styles.text, { color: themes[theme].infoText }]}>{translateInfo ? I18n.t(info) : info}</Text>
|
||||
<Text style={[styles.text, { color: themes[theme].auxiliaryText }]}>{translateInfo ? I18n.t(info) : info}</Text>
|
||||
</View>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -84,7 +84,7 @@ export const ServiceList = () => (
|
|||
<ButtonService
|
||||
key={service._id}
|
||||
onPress={() => {}}
|
||||
backgroundColor={colors[theme].chatComponentBackground}
|
||||
backgroundColor={colors[theme].buttonBackgroundSecondaryDefault}
|
||||
buttonText={buttonText}
|
||||
icon={icon}
|
||||
name={service.name}
|
||||
|
|
|
@ -86,7 +86,7 @@ const Service = React.memo(
|
|||
);
|
||||
}
|
||||
|
||||
const backgroundColor = isSaml && service.buttonColor ? service.buttonColor : colors.chatComponentBackground;
|
||||
const backgroundColor = isSaml && service.buttonColor ? service.buttonColor : colors.buttonBackgroundSecondaryDefault;
|
||||
|
||||
return (
|
||||
<ButtonService
|
||||
|
|
|
@ -20,7 +20,7 @@ const ServicesSeparator = ({ services, separator, collapsed, onPress }: IService
|
|||
type='secondary'
|
||||
onPress={onPress}
|
||||
style={styles.options}
|
||||
color={colors.actionTintColor}
|
||||
color={colors.tintColor}
|
||||
/>
|
||||
<OrSeparator theme={theme} />
|
||||
</>
|
||||
|
|
|
@ -65,7 +65,7 @@ const HeaderItem = ({ item, onReaction, theme }: THeaderItem) => (
|
|||
<Touch
|
||||
testID={`message-actions-emoji-${item}`}
|
||||
onPress={() => onReaction({ emoji: item })}
|
||||
style={[styles.headerItem, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
||||
style={[styles.headerItem, { backgroundColor: themes[theme].backgroundColor }]}
|
||||
>
|
||||
{typeof item === 'string' ? (
|
||||
<Text style={styles.headerIcon}>{shortnameToUnicode(`:${item}:`)}</Text>
|
||||
|
@ -109,12 +109,12 @@ const Header = React.memo(({ handleReaction, message, isMasterDetail }: IHeader)
|
|||
}
|
||||
|
||||
return (
|
||||
<View style={[styles.container, { backgroundColor: themes[theme].focusedBackground }]}>
|
||||
<View style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]}>
|
||||
<FlatList
|
||||
data={frequentlyUsed.slice(0, quantity)}
|
||||
renderItem={renderItem}
|
||||
ListFooterComponent={renderFooter}
|
||||
style={{ backgroundColor: themes[theme].focusedBackground }}
|
||||
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||
keyExtractor={item => (typeof item === 'string' ? item : item.name)}
|
||||
showsHorizontalScrollIndicator={false}
|
||||
scrollEnabled={false}
|
||||
|
|
|
@ -40,7 +40,7 @@ const Item = ({ item }: IMessageBoxCommandsPreviewItem) => {
|
|||
{loading ? <ActivityIndicator /> : null}
|
||||
</FastImage>
|
||||
) : (
|
||||
<CustomIcon name='attach' size={36} color={themes[theme].actionTintColor} />
|
||||
<CustomIcon name='attach' size={36} color={themes[theme].tintColor} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
|
|
@ -20,7 +20,7 @@ const FixedMentionItem = ({ item, onPress }: IMessageBoxFixedMentionItem) => {
|
|||
style={[
|
||||
styles.mentionItem,
|
||||
{
|
||||
backgroundColor: themes[theme].auxiliaryBackground,
|
||||
backgroundColor: themes[theme].backgroundColor,
|
||||
borderTopColor: themes[theme].separatorColor
|
||||
}
|
||||
]}
|
||||
|
|
|
@ -47,7 +47,7 @@ const MentionItemContent = React.memo(({ trackingType, item }: IMessageBoxMentio
|
|||
return (
|
||||
<>
|
||||
<Text style={[styles.cannedItem, { color: themes[theme].titleText }]}>!{item.shortcut}</Text>
|
||||
<Text numberOfLines={1} style={[styles.cannedMentionText, { color: themes[theme].auxiliaryTintColor }]}>
|
||||
<Text numberOfLines={1} style={[styles.cannedMentionText, { color: themes[theme].auxiliaryText }]}>
|
||||
{item.text}
|
||||
</Text>
|
||||
</>
|
||||
|
@ -92,7 +92,7 @@ const MentionItem = ({ item, trackingType }: IMessageBoxMentionItem) => {
|
|||
style={[
|
||||
styles.mentionItem,
|
||||
{
|
||||
backgroundColor: themes[theme].auxiliaryBackground,
|
||||
backgroundColor: themes[theme].backgroundColor,
|
||||
borderTopColor: themes[theme].separatorColor
|
||||
}
|
||||
]}
|
||||
|
|
|
@ -25,7 +25,7 @@ const Mentions = React.memo(
|
|||
return (
|
||||
<View testID='messagebox-container'>
|
||||
<FlatList
|
||||
style={[styles.mentionList, { backgroundColor: themes[theme].auxiliaryBackground }]}
|
||||
style={[styles.mentionList, { backgroundColor: themes[theme].backgroundColor }]}
|
||||
ListHeaderComponent={() => (
|
||||
<MentionHeaderList trackingType={trackingType} hasMentions={mentions.length > 0} loading={loading} />
|
||||
)}
|
||||
|
|
|
@ -202,7 +202,7 @@ export default class RecordAudio extends React.PureComponent<IMessageBoxRecordAu
|
|||
return (
|
||||
<BorderlessButton onPress={this.startRecordingAudio} style={styles.actionButton} testID='messagebox-send-audio'>
|
||||
<View accessible accessibilityLabel={I18n.t('Send_audio_message')} accessibilityRole='button'>
|
||||
<CustomIcon name='microphone' size={24} color={themes[theme].auxiliaryTintColor} />
|
||||
<CustomIcon name='microphone' size={24} color={themes[theme].auxiliaryText} />
|
||||
</View>
|
||||
</BorderlessButton>
|
||||
);
|
||||
|
|
|
@ -66,7 +66,7 @@ const ReplyPreview = React.memo(
|
|||
const time = moment(message.ts).format(Message_TimeFormat);
|
||||
return (
|
||||
<View style={[styles.container, { backgroundColor: themes[theme].messageboxBackground }]}>
|
||||
<View style={[styles.messageContainer, { backgroundColor: themes[theme].chatComponentBackground }]}>
|
||||
<View style={[styles.messageContainer, { backgroundColor: themes[theme].surfaceTint }]}>
|
||||
<View style={styles.header}>
|
||||
<Text numberOfLines={1} style={[styles.username, { color: themes[theme].tintColor }]}>
|
||||
{useRealName ? message.u?.name : message.u?.username}
|
||||
|
|
|
@ -25,7 +25,7 @@ const BaseButton = ({ accessibilityLabel, icon, color, ...props }: IBaseButton)
|
|||
accessibilityLabel={accessibilityLabel ? i18n.t(accessibilityLabel) : accessibilityLabel}
|
||||
accessibilityRole='button'
|
||||
>
|
||||
<CustomIcon name={icon} size={24} color={color || themes[theme].auxiliaryTintColor} />
|
||||
<CustomIcon name={icon} size={24} color={color || themes[theme].auxiliaryText} />
|
||||
</View>
|
||||
</BorderlessButton>
|
||||
);
|
||||
|
|
|
@ -302,7 +302,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
if (usedCannedResponse !== nextProps.usedCannedResponse) {
|
||||
this.onChangeText(nextProps.usedCannedResponse ?? '');
|
||||
}
|
||||
if (sharing && !replying) {
|
||||
if (sharing) {
|
||||
this.setInput(nextProps.message.msg ?? '');
|
||||
return;
|
||||
}
|
||||
|
@ -857,21 +857,14 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
};
|
||||
|
||||
openShareView = (attachments: any) => {
|
||||
const { message, replyCancel, replyWithMention, replying } = this.props;
|
||||
const { message, replyCancel, replyWithMention } = this.props;
|
||||
// Start a thread with an attachment
|
||||
let value: TThreadModel | IMessage = this.thread;
|
||||
if (replyWithMention) {
|
||||
value = message;
|
||||
replyCancel();
|
||||
}
|
||||
Navigation.navigate('ShareView', {
|
||||
room: this.room,
|
||||
thread: value,
|
||||
attachments,
|
||||
replying,
|
||||
replyingMessage: message,
|
||||
closeReply: replyCancel
|
||||
});
|
||||
Navigation.navigate('ShareView', { room: this.room, thread: value, attachments });
|
||||
};
|
||||
|
||||
createDiscussion = () => {
|
||||
|
@ -1049,7 +1042,16 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
|
||||
// Legacy reply or quote (quote is a reply without mention)
|
||||
} else {
|
||||
const msg = await this.formatReplyMessage(replyingMessage, message);
|
||||
const { user, roomType } = this.props;
|
||||
const permalink = await this.getPermalink(replyingMessage);
|
||||
let msg = `[ ](${permalink}) `;
|
||||
|
||||
// if original message wasn't sent by current user and neither from a direct room
|
||||
if (user.username !== replyingMessage?.u?.username && roomType !== 'd' && replyWithMention) {
|
||||
msg += `@${replyingMessage?.u?.username} `;
|
||||
}
|
||||
|
||||
msg = `${msg} ${message}`;
|
||||
onSubmit(msg);
|
||||
}
|
||||
replyCancel();
|
||||
|
@ -1061,20 +1063,6 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
}
|
||||
};
|
||||
|
||||
formatReplyMessage = async (replyingMessage: IMessage, message = '') => {
|
||||
const { user, roomType, replyWithMention, serverVersion } = this.props;
|
||||
const permalink = await this.getPermalink(replyingMessage);
|
||||
let msg = `[ ](${permalink}) `;
|
||||
|
||||
// if original message wasn't sent by current user and neither from a direct room
|
||||
if (user.username !== replyingMessage?.u?.username && roomType !== 'd' && replyWithMention) {
|
||||
msg += `@${replyingMessage?.u?.username} `;
|
||||
}
|
||||
|
||||
const connectionString = compareServerVersion(serverVersion, 'lowerThan', '5.0.0') ? ' ' : '\n';
|
||||
return `${msg}${connectionString}${message}`;
|
||||
};
|
||||
|
||||
updateMentions = (keyword: any, type: string) => {
|
||||
if (type === MENTIONS_TRACKING_TYPE_USERS) {
|
||||
this.getUsers(keyword);
|
||||
|
@ -1284,7 +1272,7 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
|||
style={[
|
||||
styles.textArea,
|
||||
{ backgroundColor: themes[theme].messageboxBackground },
|
||||
!recording && editing && { backgroundColor: themes[theme].chatComponentBackground }
|
||||
!recording && editing && { backgroundColor: themes[theme].statusBackgroundWarning }
|
||||
]}
|
||||
testID='messagebox'
|
||||
>
|
||||
|
|
|
@ -46,7 +46,6 @@ const RoomHeaderContainer = React.memo(
|
|||
const connecting = useSelector((state: IApplicationState) => state.meteor.connecting || state.server.loading);
|
||||
const usersTyping = useSelector((state: IApplicationState) => state.usersTyping, shallowEqual);
|
||||
const connected = useSelector((state: IApplicationState) => state.meteor.connected);
|
||||
const presenceDisabled = useSelector((state: IApplicationState) => state.settings.Presence_broadcast_disabled);
|
||||
const activeUser = useSelector(
|
||||
(state: IApplicationState) => (roomUserId ? state.activeUsers?.[roomUserId] : undefined),
|
||||
shallowEqual
|
||||
|
@ -62,13 +61,9 @@ const RoomHeaderContainer = React.memo(
|
|||
|
||||
if (connected) {
|
||||
if ((type === 'd' || (tmid && roomUserId)) && activeUser) {
|
||||
if (presenceDisabled) {
|
||||
status = 'disabled';
|
||||
} else {
|
||||
const { status: statusActiveUser, statusText: statusTextActiveUser } = activeUser;
|
||||
status = statusActiveUser;
|
||||
statusText = statusTextActiveUser;
|
||||
}
|
||||
const { status: statusActiveUser, statusText: statusTextActiveUser } = activeUser;
|
||||
status = statusActiveUser;
|
||||
statusText = statusTextActiveUser;
|
||||
} else if (type === 'l' && visitor?.status) {
|
||||
const { status: statusVisitor } = visitor;
|
||||
status = statusVisitor;
|
||||
|
|
|
@ -65,7 +65,6 @@ export const UserStatus = () => (
|
|||
<RoomItem status='busy' />
|
||||
<RoomItem status='offline' />
|
||||
<RoomItem status='loading' />
|
||||
<RoomItem status='disabled' />
|
||||
<RoomItem status='wrong' />
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -15,7 +15,7 @@ const Tag = React.memo(({ name, testID }: ITag) => {
|
|||
|
||||
return (
|
||||
<View style={[styles.tagContainer, { backgroundColor: themes[theme].borderColor }]}>
|
||||
<Text style={[styles.tagText, { color: themes[theme].infoText }]} numberOfLines={1} testID={testID}>
|
||||
<Text style={[styles.tagText, { color: themes[theme].auxiliaryText }]} numberOfLines={1} testID={testID}>
|
||||
{name}
|
||||
</Text>
|
||||
</View>
|
||||
|
|
|
@ -223,7 +223,7 @@ const Touchable = ({
|
|||
onPress={handlePress}
|
||||
testID={testID}
|
||||
style={{
|
||||
backgroundColor: isFocused ? colors.chatComponentBackground : colors.backgroundColor
|
||||
backgroundColor: isFocused ? colors.buttonBackgroundSecondaryDefault : colors.backgroundColor
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -2,13 +2,13 @@ import React, { useEffect, useReducer, useRef } from 'react';
|
|||
import { Subscription } from 'rxjs';
|
||||
|
||||
import I18n from '../../i18n';
|
||||
import { useAppSelector } from '../../lib/hooks';
|
||||
import { getUserPresence } from '../../lib/methods';
|
||||
import { isGroupChat } from '../../lib/methods/helpers';
|
||||
import { formatDate } from '../../lib/methods/helpers/room';
|
||||
import { IRoomItemContainerProps } from './interfaces';
|
||||
import RoomItem from './RoomItem';
|
||||
import { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from './styles';
|
||||
import { useUserStatus } from './useUserStatus';
|
||||
|
||||
export { ROW_HEIGHT, ROW_HEIGHT_CONDENSED };
|
||||
|
||||
|
@ -42,11 +42,11 @@ const RoomItemContainer = React.memo(
|
|||
const isRead = getIsRead(item);
|
||||
const date = item.roomUpdatedAt && formatDate(item.roomUpdatedAt);
|
||||
const alert = item.alert || item.tunread?.length;
|
||||
const connected = useAppSelector(state => state.meteor.connected);
|
||||
const userStatus = useAppSelector(state => state.activeUsers[id || '']?.status);
|
||||
const [_, forceUpdate] = useReducer(x => x + 1, 1);
|
||||
const roomSubscription = useRef<Subscription | null>(null);
|
||||
|
||||
const { connected, status } = useUserStatus(item.t, item?.visitor?.status, id);
|
||||
|
||||
useEffect(() => {
|
||||
const init = () => {
|
||||
if (item?.observe) {
|
||||
|
@ -85,6 +85,8 @@ const RoomItemContainer = React.memo(
|
|||
accessibilityLabel = `, ${I18n.t('last_message')} ${date}`;
|
||||
}
|
||||
|
||||
const status = item.t === 'l' ? item.visitor?.status || item.v?.status : userStatus;
|
||||
|
||||
return (
|
||||
<RoomItem
|
||||
name={name}
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
import { TUserStatus } from '../../definitions';
|
||||
import { useAppSelector } from '../../lib/hooks';
|
||||
import { RoomTypes } from '../../lib/methods';
|
||||
|
||||
export const useUserStatus = (
|
||||
type: RoomTypes,
|
||||
liveChatStatus?: TUserStatus,
|
||||
id?: string
|
||||
): { connected: boolean; status: TUserStatus } => {
|
||||
const connected = useAppSelector(state => state.meteor.connected);
|
||||
const presenceDisabled = useAppSelector(state => state.settings.Presence_broadcast_disabled);
|
||||
const userStatus = useAppSelector(state => state.activeUsers[id || '']?.status);
|
||||
|
||||
let status = 'loading';
|
||||
if (connected) {
|
||||
if (type === 'd') {
|
||||
if (presenceDisabled) {
|
||||
status = 'disabled';
|
||||
} else {
|
||||
status = userStatus || 'loading';
|
||||
}
|
||||
} else if (type === 'l' && liveChatStatus) {
|
||||
status = liveChatStatus;
|
||||
}
|
||||
}
|
||||
return {
|
||||
connected,
|
||||
status: status as TUserStatus
|
||||
};
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import { StyleProp, ViewStyle } from 'react-native';
|
||||
import { SvgUri } from 'react-native-svg';
|
||||
|
||||
|
@ -29,12 +29,22 @@ interface IOmnichannelRoomIconProps {
|
|||
}
|
||||
|
||||
export const OmnichannelRoomIcon = ({ size, style, sourceType, status }: IOmnichannelRoomIconProps) => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [svgError, setSvgError] = useState(false);
|
||||
const baseUrl = useAppSelector(state => state.server?.server);
|
||||
const connected = useAppSelector(state => state.meteor?.connected);
|
||||
|
||||
const customIcon = (
|
||||
if (sourceType?.type === OmnichannelSourceType.APP && sourceType.id && sourceType.sidebarIcon && connected) {
|
||||
return (
|
||||
<SvgUri
|
||||
height={size}
|
||||
width={size}
|
||||
color={STATUS_COLORS[status || 'offline']}
|
||||
uri={`${baseUrl}/api/apps/public/${sourceType.id}/get-sidebar-icon?icon=${sourceType.sidebarIcon}`}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<CustomIcon
|
||||
name={iconMap[sourceType?.type || 'other']}
|
||||
size={size}
|
||||
|
@ -42,23 +52,4 @@ export const OmnichannelRoomIcon = ({ size, style, sourceType, status }: IOmnich
|
|||
color={STATUS_COLORS[status || 'offline']}
|
||||
/>
|
||||
);
|
||||
|
||||
if (!svgError && sourceType?.type === OmnichannelSourceType.APP && sourceType.id && sourceType.sidebarIcon && connected) {
|
||||
return (
|
||||
<>
|
||||
<SvgUri
|
||||
height={size}
|
||||
width={size}
|
||||
color={STATUS_COLORS[status || 'offline']}
|
||||
uri={`${baseUrl}/api/apps/public/${sourceType.id}/get-sidebar-icon?icon=${sourceType.sidebarIcon}`}
|
||||
style={style}
|
||||
onError={() => setSvgError(true)}
|
||||
onLoad={() => setLoading(false)}
|
||||
/>
|
||||
{loading ? customIcon : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return customIcon;
|
||||
};
|
||||
|
|
|
@ -23,7 +23,7 @@ const SafeAreaView = React.memo(({ style, children, vertical = true, ...props }:
|
|||
const { theme } = useTheme();
|
||||
return (
|
||||
<SafeAreaContext
|
||||
style={[styles.view, { backgroundColor: themes[theme].auxiliaryBackground }, style]}
|
||||
style={[styles.view, { backgroundColor: themes[theme].backgroundColor }, style]}
|
||||
edges={vertical ? ['right', 'left'] : undefined}
|
||||
{...props}
|
||||
>
|
||||
|
|
|
@ -28,9 +28,9 @@ const ServerItem = React.memo(({ item, onPress, onLongPress, hasCheck }: IServer
|
|||
onPress={onPress}
|
||||
onLongPress={() => onLongPress?.()}
|
||||
testID={`rooms-list-header-server-${item.id}`}
|
||||
android_ripple={{ color: themes[theme].bannerBackground }}
|
||||
android_ripple={{ color: themes[theme].buttonBackgroundSecondaryPress }}
|
||||
style={({ pressed }: { pressed: boolean }) => ({
|
||||
backgroundColor: isIOS && pressed ? themes[theme].bannerBackground : themes[theme].backgroundColor
|
||||
backgroundColor: isIOS && pressed ? themes[theme].buttonBackgroundSecondaryPress : themes[theme].backgroundColor
|
||||
})}
|
||||
>
|
||||
<View style={styles.serverItemContainer}>
|
||||
|
|
|
@ -6,15 +6,9 @@ import { IStatus } from './definition';
|
|||
import { useAppSelector } from '../../lib/hooks';
|
||||
|
||||
const StatusContainer = ({ id, style, size = 32, ...props }: Omit<IStatus, 'status'>): React.ReactElement => {
|
||||
const status = useAppSelector(state => {
|
||||
if (state.settings.Presence_broadcast_disabled) {
|
||||
return 'disabled';
|
||||
}
|
||||
if (state.meteor.connected) {
|
||||
return state.activeUsers[id] && state.activeUsers[id].status;
|
||||
}
|
||||
return 'loading';
|
||||
}) as TUserStatus;
|
||||
const status = useAppSelector(state =>
|
||||
state.meteor.connected ? state.activeUsers[id] && state.activeUsers[id].status : 'loading'
|
||||
) as TUserStatus;
|
||||
return <Status size={size} style={style} status={status} {...props} />;
|
||||
};
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ export const FormTextInput = ({
|
|||
|
||||
{showClearInput ? (
|
||||
<Touchable onPress={onClearInput} style={[styles.iconContainer, styles.iconRight]} testID='clear-text-input'>
|
||||
<CustomIcon name='input-clear' size={20} color={colors.auxiliaryTintColor} />
|
||||
<CustomIcon name='input-clear' size={20} color={colors.auxiliaryText} />
|
||||
</Touchable>
|
||||
) : null}
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ const ThreadDetails = ({ item, user, badgeColor, toggleFollowThread, style }: IT
|
|||
<CustomIcon
|
||||
size={24}
|
||||
name={isFollowing ? 'notification' : 'notification-disabled'}
|
||||
color={themes[theme].auxiliaryTintColor}
|
||||
color={themes[theme].auxiliaryText}
|
||||
/>
|
||||
</Touchable>
|
||||
</View>
|
||||
|
|
|
@ -136,7 +136,7 @@ const TwoFactor = React.memo(({ isMasterDetail }: { isMasterDetail: boolean }) =
|
|||
<Button
|
||||
title={I18n.t('Cancel')}
|
||||
type='secondary'
|
||||
backgroundColor={themes[theme].chatComponentBackground}
|
||||
backgroundColor={themes[theme].buttonBackgroundSecondaryDefault}
|
||||
style={styles.button}
|
||||
onPress={onCancel}
|
||||
/>
|
||||
|
|
|
@ -67,7 +67,7 @@ export const DatePicker = ({ element, language, action, context, loading, value,
|
|||
<Touchable
|
||||
onPress={() => onShow(!show)}
|
||||
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||
background={Touchable.Ripple(themes[theme].buttonBackgroundSecondaryPress)}
|
||||
>
|
||||
<View style={[styles.input, { borderColor: error ? themes[theme].dangerColor : themes[theme].separatorColor }]}>
|
||||
<Text style={[styles.inputText, { color: error ? themes[theme].dangerColor : themes[theme].titleText }]}>
|
||||
|
|
|
@ -29,8 +29,8 @@ const Chip = ({ item, onSelect, style }: IChip) => {
|
|||
<Touchable
|
||||
key={item.value}
|
||||
onPress={() => onSelect(item)}
|
||||
style={[styles.chip, { backgroundColor: colors.auxiliaryBackground }, style]}
|
||||
background={Touchable.Ripple(colors.bannerBackground)}
|
||||
style={[styles.chip, { backgroundColor: colors.backgroundColor }, style]}
|
||||
background={Touchable.Ripple(colors.buttonBackgroundSecondaryPress)}
|
||||
>
|
||||
<>
|
||||
{item.imageUrl ? <FastImage style={styles.chipImage} source={{ uri: item.imageUrl }} /> : null}
|
||||
|
|
|
@ -23,7 +23,7 @@ const Input = ({ children, onPress, loading, inputStyle, placeholder, disabled,
|
|||
<Touchable
|
||||
onPress={onPress}
|
||||
style={[{ backgroundColor: colors.backgroundColor }, styles.inputBorder, inputStyle]}
|
||||
background={Touchable.Ripple(colors.bannerBackground)}
|
||||
background={Touchable.Ripple(colors.buttonBackgroundSecondaryPress)}
|
||||
disabled={disabled}
|
||||
>
|
||||
<View style={[styles.input, styles.inputBorder, { borderColor: colors.separatorColor }, innerInputStyle]}>
|
||||
|
|
|
@ -71,7 +71,7 @@ export const MultiSelectContent = React.memo(
|
|||
testID='multi-select-search'
|
||||
onChangeText={handleSearch}
|
||||
placeholder={I18n.t('Search')}
|
||||
inputStyle={{ backgroundColor: colors.focusedBackground }}
|
||||
inputStyle={{ backgroundColor: colors.backgroundColor }}
|
||||
bottomSheet={isIOS}
|
||||
onSubmitEditing={() => {
|
||||
setTimeout(() => {
|
||||
|
|
|
@ -29,7 +29,7 @@ const styles = StyleSheet.create({
|
|||
const Option = ({ option: { text, value }, onOptionPress, parser, theme }: IOption) => (
|
||||
<Touchable
|
||||
onPress={() => onOptionPress({ value })}
|
||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||
background={Touchable.Ripple(themes[theme].buttonBackgroundSecondaryPress)}
|
||||
style={styles.option}
|
||||
>
|
||||
<Text>{parser.text(text)}</Text>
|
||||
|
@ -62,7 +62,7 @@ export const Overflow = ({ element, loading, action, parser }: IOverflow) => {
|
|||
<>
|
||||
<Touchable
|
||||
ref={ref => (touchable[blockId] = ref)}
|
||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||
background={Touchable.Ripple(themes[theme].buttonBackgroundSecondaryPress)}
|
||||
onPress={() => onShow(!show)}
|
||||
hitSlop={BUTTON_HIT_SLOP}
|
||||
style={styles.menu}
|
||||
|
|
|
@ -6,6 +6,7 @@ import i18n from '../../../../i18n';
|
|||
import { getSubscriptionByRoomId } from '../../../../lib/database/services/Subscription';
|
||||
import { useAppSelector } from '../../../../lib/hooks';
|
||||
import { getRoomAvatar, getUidDirectMessage } from '../../../../lib/methods/helpers';
|
||||
import { videoConfStartAndJoin } from '../../../../lib/methods/videoConf';
|
||||
import { useTheme } from '../../../../theme';
|
||||
import { useActionSheet } from '../../../ActionSheet';
|
||||
import AvatarContainer from '../../../Avatar';
|
||||
|
@ -15,12 +16,12 @@ import { BUTTON_HIT_SLOP } from '../../../message/utils';
|
|||
import StatusContainer from '../../../Status';
|
||||
import useStyle from './styles';
|
||||
|
||||
export default function StartACallActionSheet({ rid, initCall }: { rid: string; initCall: Function }): React.ReactElement {
|
||||
export default function CallAgainActionSheet({ rid }: { rid: string }): React.ReactElement {
|
||||
const style = useStyle();
|
||||
const { colors } = useTheme();
|
||||
const [user, setUser] = useState({ username: '', avatar: '', uid: '' });
|
||||
const [mic, setMic] = useState(true);
|
||||
const [cam, setCam] = useState(false);
|
||||
const [user, setUser] = useState({ username: '', avatar: '', uid: '', rid: '' });
|
||||
const [phone, setPhone] = useState(true);
|
||||
const [camera, setCamera] = useState(false);
|
||||
const username = useAppSelector(state => state.login.user.username);
|
||||
|
||||
const { hideActionSheet } = useActionSheet();
|
||||
|
@ -30,7 +31,7 @@ export default function StartACallActionSheet({ rid, initCall }: { rid: string;
|
|||
const room = await getSubscriptionByRoomId(rid);
|
||||
const uid = (await getUidDirectMessage(room)) as string;
|
||||
const avt = getRoomAvatar(room);
|
||||
setUser({ uid, username: room?.name || '', avatar: avt });
|
||||
setUser({ uid, username: room?.name || '', avatar: avt, rid: room?.id || '' });
|
||||
})();
|
||||
}, [rid]);
|
||||
|
||||
|
@ -42,27 +43,25 @@ export default function StartACallActionSheet({ rid, initCall }: { rid: string;
|
|||
<Text style={style.actionSheetHeaderTitle}>{i18n.t('Start_a_call')}</Text>
|
||||
<View style={style.actionSheetHeaderButtons}>
|
||||
<Touchable
|
||||
onPress={() => setCam(!cam)}
|
||||
style={[style.iconCallContainer, cam && style.enabledBackground, { marginRight: 6 }]}
|
||||
onPress={() => setCamera(!camera)}
|
||||
style={[style.iconCallContainer, camera && style.enabledBackground, { marginRight: 6 }]}
|
||||
hitSlop={BUTTON_HIT_SLOP}
|
||||
>
|
||||
<CustomIcon name={cam ? 'camera' : 'camera-disabled'} size={20} color={handleColor(cam)} />
|
||||
<CustomIcon name={camera ? 'camera' : 'camera-disabled'} size={16} color={handleColor(camera)} />
|
||||
</Touchable>
|
||||
<Touchable
|
||||
onPress={() => setMic(!mic)}
|
||||
style={[style.iconCallContainer, mic && style.enabledBackground]}
|
||||
onPress={() => setPhone(!phone)}
|
||||
style={[style.iconCallContainer, phone && style.enabledBackground]}
|
||||
hitSlop={BUTTON_HIT_SLOP}
|
||||
>
|
||||
<CustomIcon name={mic ? 'microphone' : 'microphone-disabled'} size={20} color={handleColor(mic)} />
|
||||
<CustomIcon name={phone ? 'microphone' : 'microphone-disabled'} size={16} color={handleColor(phone)} />
|
||||
</Touchable>
|
||||
</View>
|
||||
</View>
|
||||
<View style={style.actionSheetUsernameContainer}>
|
||||
<AvatarContainer text={user.avatar} size={36} />
|
||||
<StatusContainer size={16} id={user.uid} style={{ marginLeft: 8, marginRight: 6 }} />
|
||||
<Text style={style.actionSheetUsername} numberOfLines={1}>
|
||||
{user.username}
|
||||
</Text>
|
||||
<Text style={style.actionSheetUsername}>{user.username}</Text>
|
||||
</View>
|
||||
<View style={style.actionSheetPhotoContainer}>
|
||||
<AvatarContainer size={62} text={username} />
|
||||
|
@ -71,7 +70,7 @@ export default function StartACallActionSheet({ rid, initCall }: { rid: string;
|
|||
onPress={() => {
|
||||
hideActionSheet();
|
||||
setTimeout(() => {
|
||||
initCall({ cam, mic });
|
||||
videoConfStartAndJoin(user.rid, camera);
|
||||
}, 100);
|
||||
}}
|
||||
title={i18n.t('Call')}
|
|
@ -3,16 +3,17 @@ import { Text } from 'react-native';
|
|||
import Touchable from 'react-native-platform-touchable';
|
||||
|
||||
import i18n from '../../../../i18n';
|
||||
import { videoConfJoin } from '../../../../lib/methods/videoConf';
|
||||
import { useVideoConf } from '../../../../lib/hooks/useVideoConf';
|
||||
import useStyle from './styles';
|
||||
import { VideoConferenceBaseContainer } from './VideoConferenceBaseContainer';
|
||||
|
||||
const VideoConferenceDirect = React.memo(({ blockId }: { blockId: string }) => {
|
||||
const style = useStyle();
|
||||
const { joinCall } = useVideoConf();
|
||||
|
||||
return (
|
||||
<VideoConferenceBaseContainer variant='incoming'>
|
||||
<Touchable style={style.callToActionButton} onPress={() => videoConfJoin(blockId)}>
|
||||
<Touchable style={style.callToActionButton} onPress={() => joinCall(blockId)}>
|
||||
<Text style={style.callToActionButtonText}>{i18n.t('Join')}</Text>
|
||||
</Touchable>
|
||||
<Text style={style.callBack}>{i18n.t('Waiting_for_answer')}</Text>
|
||||
|
|
|
@ -6,7 +6,9 @@ import { IUser } from '../../../../definitions';
|
|||
import { VideoConferenceType } from '../../../../definitions/IVideoConference';
|
||||
import i18n from '../../../../i18n';
|
||||
import { useAppSelector } from '../../../../lib/hooks';
|
||||
import { useVideoConf } from '../../../../lib/hooks/useVideoConf';
|
||||
import { useSnaps } from '../../../../lib/hooks/useSnaps';
|
||||
import { useActionSheet } from '../../../ActionSheet';
|
||||
import CallAgainActionSheet from './CallAgainActionSheet';
|
||||
import { CallParticipants, TCallUsers } from './CallParticipants';
|
||||
import useStyle from './styles';
|
||||
import { VideoConferenceBaseContainer } from './VideoConferenceBaseContainer';
|
||||
|
@ -24,7 +26,8 @@ export default function VideoConferenceEnded({
|
|||
}): React.ReactElement {
|
||||
const style = useStyle();
|
||||
const username = useAppSelector(state => state.login.user.username);
|
||||
const { showInitCallActionSheet } = useVideoConf(rid);
|
||||
const { showActionSheet } = useActionSheet();
|
||||
const snaps = useSnaps([1250]);
|
||||
|
||||
const onlyAuthorOnCall = users.length === 1 && users.some(user => user.username === createdBy.username);
|
||||
|
||||
|
@ -32,7 +35,15 @@ export default function VideoConferenceEnded({
|
|||
<VideoConferenceBaseContainer variant='ended'>
|
||||
{type === 'direct' ? (
|
||||
<>
|
||||
<Touchable style={style.callToActionCallBack} onPress={showInitCallActionSheet}>
|
||||
<Touchable
|
||||
style={style.callToActionCallBack}
|
||||
onPress={() =>
|
||||
showActionSheet({
|
||||
children: <CallAgainActionSheet rid={rid} />,
|
||||
snaps
|
||||
})
|
||||
}
|
||||
>
|
||||
<Text style={style.callToActionCallBackText}>
|
||||
{createdBy.username === username ? i18n.t('Call_back') : i18n.t('Call_again')}
|
||||
</Text>
|
||||
|
|
|
@ -3,17 +3,18 @@ import { Text } from 'react-native';
|
|||
import Touchable from 'react-native-platform-touchable';
|
||||
|
||||
import i18n from '../../../../i18n';
|
||||
import { videoConfJoin } from '../../../../lib/methods/videoConf';
|
||||
import { useVideoConf } from '../../../../lib/hooks/useVideoConf';
|
||||
import { CallParticipants, TCallUsers } from './CallParticipants';
|
||||
import useStyle from './styles';
|
||||
import { VideoConferenceBaseContainer } from './VideoConferenceBaseContainer';
|
||||
|
||||
export default function VideoConferenceOutgoing({ users, blockId }: { users: TCallUsers; blockId: string }): React.ReactElement {
|
||||
const style = useStyle();
|
||||
const { joinCall } = useVideoConf();
|
||||
|
||||
return (
|
||||
<VideoConferenceBaseContainer variant='outgoing'>
|
||||
<Touchable style={style.callToActionButton} onPress={() => videoConfJoin(blockId)}>
|
||||
<Touchable style={style.callToActionButton} onPress={() => joinCall(blockId)}>
|
||||
<Text style={style.callToActionButtonText}>{i18n.t('Join')}</Text>
|
||||
</Touchable>
|
||||
<CallParticipants users={users} />
|
||||
|
|
|
@ -100,8 +100,7 @@ export default function useStyle() {
|
|||
actionSheetUsername: {
|
||||
fontSize: 16,
|
||||
...sharedStyles.textBold,
|
||||
color: colors.passcodePrimary,
|
||||
flexShrink: 1
|
||||
color: colors.passcodePrimary
|
||||
},
|
||||
enabledBackground: {
|
||||
backgroundColor: colors.conferenceCallEnabledIconBackground
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { BlockContext } from '@rocket.chat/ui-kit';
|
||||
import React, { useContext, useState } from 'react';
|
||||
|
||||
import { videoConfJoin } from '../../lib/methods/videoConf';
|
||||
import { useVideoConf } from '../../lib/hooks/useVideoConf';
|
||||
import { IText } from './interfaces';
|
||||
|
||||
export const textParser = ([{ text }]: IText[]) => text;
|
||||
|
@ -40,6 +40,7 @@ export const useBlockContext = ({ blockId, actionId, appId, initialValue }: IUse
|
|||
const { action, appId: appIdFromContext, viewId, state, language, errors, values = {} } = useContext(KitContext);
|
||||
const { value = initialValue } = values[actionId] || {};
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { joinCall } = useVideoConf();
|
||||
|
||||
const error = errors && actionId && errors[actionId];
|
||||
|
||||
|
@ -57,7 +58,7 @@ export const useBlockContext = ({ blockId, actionId, appId, initialValue }: IUse
|
|||
try {
|
||||
if (appId === 'videoconf-core' && blockId) {
|
||||
setLoading(false);
|
||||
return videoConfJoin(blockId);
|
||||
return joinCall(blockId);
|
||||
}
|
||||
await action({
|
||||
blockId,
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
import React from 'react';
|
||||
import { View, StyleSheet, Text, ViewStyle } from 'react-native';
|
||||
|
||||
import sharedStyles from '../views/Styles';
|
||||
import { useTheme } from '../theme';
|
||||
import openLink from '../lib/methods/helpers/openLink';
|
||||
import { useAppSelector } from '../lib/hooks';
|
||||
import I18n from '../i18n';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
bottomContainer: {
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
marginBottom: 32,
|
||||
marginHorizontal: 30
|
||||
},
|
||||
bottomContainerText: {
|
||||
...sharedStyles.textRegular,
|
||||
fontSize: 13,
|
||||
textAlign: 'center'
|
||||
},
|
||||
bottomContainerTextBold: {
|
||||
...sharedStyles.textSemibold,
|
||||
fontSize: 13,
|
||||
textAlign: 'center'
|
||||
}
|
||||
});
|
||||
|
||||
const UGCRules = ({ styleContainer }: { styleContainer?: ViewStyle }) => {
|
||||
const { colors, theme } = useTheme();
|
||||
const { server } = useAppSelector(state => ({
|
||||
server: state.server.server
|
||||
}));
|
||||
|
||||
const openContract = (route: string) => {
|
||||
if (!server) {
|
||||
return;
|
||||
}
|
||||
openLink(`${server}/${route}`, theme);
|
||||
};
|
||||
return (
|
||||
<View style={[styles.bottomContainer, styleContainer]}>
|
||||
<Text style={[styles.bottomContainerText, { color: colors.auxiliaryText }]}>
|
||||
{`${I18n.t('Onboarding_agree_terms')}\n`}
|
||||
<Text
|
||||
style={[styles.bottomContainerTextBold, { color: colors.actionTintColor }]}
|
||||
onPress={() => openContract('terms-of-service')}
|
||||
>
|
||||
{I18n.t('Terms_of_Service')}
|
||||
</Text>{' '}
|
||||
{I18n.t('and')}
|
||||
<Text
|
||||
style={[styles.bottomContainerTextBold, { color: colors.actionTintColor }]}
|
||||
onPress={() => openContract('privacy-policy')}
|
||||
>
|
||||
{' '}
|
||||
{I18n.t('Privacy_Policy')}
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default UGCRules;
|
|
@ -54,10 +54,10 @@ const UserItem = ({ name, username, onPress, testID, onLongPress, style, icon, i
|
|||
onLongPress={onLongPress}
|
||||
testID={testID}
|
||||
android_ripple={{
|
||||
color: colors.bannerBackground
|
||||
color: colors.buttonBackgroundSecondaryPress
|
||||
}}
|
||||
style={({ pressed }: any) => ({
|
||||
backgroundColor: isIOS && pressed ? colors.bannerBackground : 'transparent'
|
||||
backgroundColor: isIOS && pressed ? colors.buttonBackgroundSecondaryPress : 'transparent'
|
||||
})}
|
||||
>
|
||||
<View style={[styles.container, styles.button, style]}>
|
||||
|
@ -67,7 +67,7 @@ const UserItem = ({ name, username, onPress, testID, onLongPress, style, icon, i
|
|||
{name}
|
||||
</Text>
|
||||
</View>
|
||||
{icon ? <CustomIcon name={icon} size={22} color={iconColor || colors.actionTintColor} style={styles.icon} /> : null}
|
||||
{icon ? <CustomIcon name={icon} size={22} color={iconColor || colors.tintColor} style={styles.icon} /> : null}
|
||||
</View>
|
||||
</Pressable>
|
||||
);
|
||||
|
|
|
@ -37,7 +37,7 @@ const Link = React.memo(({ children, link, theme, onLinkPress }: ILink) => {
|
|||
|
||||
// if you have a [](https://rocket.chat) render https://rocket.chat
|
||||
return (
|
||||
<Text onPress={handlePress} onLongPress={onLongPress} style={{ ...styles.link, color: themes[theme].actionTintColor }}>
|
||||
<Text onPress={handlePress} onLongPress={onLongPress} style={{ ...styles.link, color: themes[theme].tintColor }}>
|
||||
{childLength !== 0 ? children : link}
|
||||
</Text>
|
||||
);
|
||||
|
|
|
@ -157,8 +157,8 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
|||
{
|
||||
...styles.codeInline,
|
||||
color: themes[theme!].bodyText,
|
||||
backgroundColor: themes[theme!].bannerBackground,
|
||||
borderColor: themes[theme!].bannerBackground
|
||||
backgroundColor: themes[theme!].buttonBackgroundSecondaryPress,
|
||||
borderColor: themes[theme!].borderColor
|
||||
},
|
||||
...style
|
||||
]}
|
||||
|
@ -176,8 +176,8 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
|||
{
|
||||
...styles.codeBlock,
|
||||
color: themes[theme!].bodyText,
|
||||
backgroundColor: themes[theme!].bannerBackground,
|
||||
borderColor: themes[theme!].bannerBackground
|
||||
backgroundColor: themes[theme!].buttonBackgroundSecondaryPress,
|
||||
borderColor: themes[theme!].borderColor
|
||||
},
|
||||
...style
|
||||
]}
|
||||
|
|
|
@ -18,7 +18,7 @@ const Code = ({ value }: ICodeProps): React.ReactElement => {
|
|||
style={[
|
||||
styles.codeBlock,
|
||||
{
|
||||
backgroundColor: colors.bannerBackground,
|
||||
backgroundColor: colors.buttonBackgroundSecondaryPress,
|
||||
borderColor: colors.borderColor
|
||||
}
|
||||
]}
|
||||
|
|
|
@ -26,7 +26,7 @@ const MessageImage = ({ img, theme }: TMessageImage) => (
|
|||
resizeMode={FastImage.resizeMode.cover}
|
||||
indicator={Progress.Pie}
|
||||
indicatorProps={{
|
||||
color: themes[theme].actionTintColor
|
||||
color: themes[theme].tintColor
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -18,29 +18,13 @@ import MarkdownContext from './MarkdownContext';
|
|||
|
||||
interface IParagraphProps {
|
||||
value: ParagraphProps['value'];
|
||||
forceTrim?: boolean;
|
||||
}
|
||||
|
||||
const Inline = ({ value, forceTrim }: IParagraphProps): React.ReactElement | null => {
|
||||
const Inline = ({ value }: IParagraphProps): React.ReactElement | null => {
|
||||
const { useRealName, username, navToRoomInfo, mentions, channels } = useContext(MarkdownContext);
|
||||
return (
|
||||
<Text style={styles.inline}>
|
||||
{value.map((block, index) => {
|
||||
// We are forcing trim when is a `[ ](https://https://open.rocket.chat/) plain_text`
|
||||
// to clean the empty spaces
|
||||
if (forceTrim) {
|
||||
if (index === 0 && block.type === 'LINK') {
|
||||
block.value.label.value =
|
||||
// Need to update the @rocket.chat/message-parser to understand that the label can be a Markup | Markup[]
|
||||
// https://github.com/RocketChat/fuselage/blob/461ecf661d9ff4a46390957c915e4352fa942a7c/packages/message-parser/src/definitions.ts#L141
|
||||
// @ts-ignore
|
||||
block.value?.label?.value?.toString().trimLeft() || block?.value?.label?.[0]?.value?.toString().trimLeft();
|
||||
}
|
||||
if (index === 1 && block.type !== 'LINK') {
|
||||
block.value = block.value?.toString().trimLeft();
|
||||
}
|
||||
}
|
||||
|
||||
{value.map(block => {
|
||||
switch (block.type) {
|
||||
case 'IMAGE':
|
||||
return <Image value={block.value} />;
|
||||
|
|
|
@ -19,7 +19,7 @@ const InlineCode = ({ value }: IInlineCodeProps) => {
|
|||
styles.codeInline,
|
||||
{
|
||||
color: themes[theme].bodyText,
|
||||
backgroundColor: themes[theme].bannerBackground,
|
||||
backgroundColor: themes[theme].buttonBackgroundSecondaryPress,
|
||||
borderColor: themes[theme].borderColor
|
||||
}
|
||||
]}
|
||||
|
|
|
@ -39,7 +39,7 @@ const Link = ({ value }: ILinkProps) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Text onPress={handlePress} onLongPress={onLongPress} style={[styles.link, { color: themes[theme].actionTintColor }]}>
|
||||
<Text onPress={handlePress} onLongPress={onLongPress} style={[styles.link, { color: themes[theme].tintColor }]}>
|
||||
{(block => {
|
||||
const blockArray = Array.isArray(block) ? block : [block];
|
||||
return blockArray.map(blockInArray => {
|
||||
|
|
|
@ -381,6 +381,27 @@ export const BlockQuote = () => (
|
|||
</View>
|
||||
);
|
||||
|
||||
const rocketChatLink = [
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
value: [
|
||||
{
|
||||
type: 'LINK',
|
||||
value: {
|
||||
src: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'https://rocket.chat'
|
||||
},
|
||||
label: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'https://rocket.chat'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const markdownLink = [
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
|
@ -466,6 +487,7 @@ const markdownLinkWithEmphasis = [
|
|||
|
||||
export const Links = () => (
|
||||
<View style={styles.container}>
|
||||
<NewMarkdown tokens={rocketChatLink} />
|
||||
<NewMarkdown tokens={markdownLink} />
|
||||
<NewMarkdown tokens={markdownLinkWithEmphasis} />
|
||||
</View>
|
||||
|
@ -784,128 +806,3 @@ export const InlineKatex = () => (
|
|||
<NewMarkdown tokens={inlineKatex} />
|
||||
</View>
|
||||
);
|
||||
|
||||
const messageQuote = {
|
||||
/**
|
||||
# Hello head 1
|
||||
[ ](https://google.com)
|
||||
*/
|
||||
headAndLink: [
|
||||
{ type: 'HEADING', level: 1, value: [{ type: 'PLAIN_TEXT', value: 'Hello head 1' }] },
|
||||
{ type: 'LINE_BREAK' },
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
value: [
|
||||
{
|
||||
type: 'LINK',
|
||||
value: { src: { type: 'PLAIN_TEXT', value: 'https://google.com' }, label: { type: 'PLAIN_TEXT', value: ' ' } }
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
/**
|
||||
# Head 1 as the first line then line break and after paragraph
|
||||
bla bla bla bla bla bla
|
||||
bla bla bla bla bla bla
|
||||
[ ](https://google.com)
|
||||
*/
|
||||
headTextAndLink: [
|
||||
{
|
||||
type: 'HEADING',
|
||||
level: 1,
|
||||
value: [{ type: 'PLAIN_TEXT', value: 'Head 1 as the first line then line break and after paragraph' }]
|
||||
},
|
||||
{ type: 'LINE_BREAK' },
|
||||
{ type: 'PARAGRAPH', value: [{ type: 'PLAIN_TEXT', value: 'bla bla bla bla bla bla ' }] },
|
||||
{ type: 'PARAGRAPH', value: [{ type: 'PLAIN_TEXT', value: 'bla bla bla bla bla bla ' }] },
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
value: [
|
||||
{
|
||||
type: 'LINK',
|
||||
value: { src: { type: 'PLAIN_TEXT', value: 'https://google.com' }, label: { type: 'PLAIN_TEXT', value: ' ' } }
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
/**
|
||||
[ ](permalink from message)\n# Head 1 after a forced line break
|
||||
asdas asd asd asd
|
||||
*/
|
||||
headTextAndQuote: [
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
value: [
|
||||
{
|
||||
type: 'LINK',
|
||||
value: {
|
||||
src: { type: 'PLAIN_TEXT', value: 'https://open.rocket.chat/direct/subaru123?msg=QB42gWcaO6BgqtLTo' },
|
||||
label: { type: 'PLAIN_TEXT', value: ' ' }
|
||||
}
|
||||
},
|
||||
{ type: 'PLAIN_TEXT', value: ' ' }
|
||||
]
|
||||
},
|
||||
{ type: 'HEADING', level: 1, value: [{ type: 'PLAIN_TEXT', value: 'Head 1 after a forced line break' }] },
|
||||
{ type: 'LINE_BREAK' },
|
||||
{ type: 'PARAGRAPH', value: [{ type: 'PLAIN_TEXT', value: 'Description' }] }
|
||||
],
|
||||
/**
|
||||
[ ](https://google.com) *There is a link before this bold separated by single space*
|
||||
*/
|
||||
linkAndBoldText: [
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
value: [
|
||||
{
|
||||
type: 'LINK',
|
||||
value: { src: { type: 'PLAIN_TEXT', value: 'https://google.com' }, label: { type: 'PLAIN_TEXT', value: ' ' } }
|
||||
},
|
||||
{ type: 'PLAIN_TEXT', value: ' ' },
|
||||
{ type: 'BOLD', value: [{ type: 'PLAIN_TEXT', value: 'There is a link before this bold separated by single space' }] }
|
||||
]
|
||||
}
|
||||
],
|
||||
simpleQuote: [
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
value: [
|
||||
{
|
||||
type: 'LINK',
|
||||
value: {
|
||||
src: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'https://open.rocket.chat/group/quoteeee9798789?msg=ZZp6t2dCRX4TqExht'
|
||||
},
|
||||
// format of label for servers greater or equal than 6.0
|
||||
label: [
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: ' '
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
value: [
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'Quoting a message wrote before'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const MessageQuote = () => (
|
||||
<View style={styles.container}>
|
||||
<NewMarkdown tokens={messageQuote.headAndLink} />
|
||||
<NewMarkdown tokens={messageQuote.headTextAndLink} />
|
||||
<NewMarkdown tokens={messageQuote.headTextAndQuote} />
|
||||
<NewMarkdown tokens={messageQuote.linkAndBoldText} />
|
||||
<NewMarkdown tokens={messageQuote.simpleQuote} />
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -12,28 +12,10 @@ interface IParagraphProps {
|
|||
}
|
||||
|
||||
const Paragraph = ({ value }: IParagraphProps) => {
|
||||
let forceTrim = false;
|
||||
const { theme } = useTheme();
|
||||
if (
|
||||
value?.[0]?.type === 'LINK' &&
|
||||
// Need to update the @rocket.chat/message-parser to understand that the label can be a Markup | Markup[]
|
||||
// https://github.com/RocketChat/fuselage/blob/461ecf661d9ff4a46390957c915e4352fa942a7c/packages/message-parser/src/definitions.ts#L141
|
||||
// @ts-ignore
|
||||
(value?.[0]?.value?.label?.value?.toString().trim() === '' || value?.[0]?.value?.label?.[0]?.value?.toString().trim() === '')
|
||||
) {
|
||||
// We are returning null when we receive a message like this: `[ ](https://open.rocket.chat/)\nplain_text`
|
||||
// to avoid render a line empty above the the message
|
||||
if (value.length === 1) {
|
||||
return null;
|
||||
}
|
||||
if (value.length === 2 && value?.[1]?.type === 'PLAIN_TEXT' && value?.[1]?.value?.toString().trim() === '') {
|
||||
return null;
|
||||
}
|
||||
forceTrim = true;
|
||||
}
|
||||
return (
|
||||
<Text style={[styles.text, { color: themes[theme].bodyText }]}>
|
||||
<Inline value={value} forceTrim={forceTrim} />
|
||||
<Inline value={value} />
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@ interface IPlainProps {
|
|||
value: PlainProps['value'];
|
||||
}
|
||||
|
||||
const Plain = ({ value }: IPlainProps): React.ReactElement => (
|
||||
const Plain = ({ value }: IPlainProps) => (
|
||||
<Text accessibilityLabel={value} style={styles.plainText}>
|
||||
{value}
|
||||
</Text>
|
||||
|
|
|
@ -4,6 +4,7 @@ import { Tasks as TasksProps } from '@rocket.chat/message-parser';
|
|||
|
||||
import Inline from './Inline';
|
||||
import styles from '../styles';
|
||||
import { themes } from '../../../lib/constants';
|
||||
import { useTheme } from '../../../theme';
|
||||
|
||||
interface ITasksProps {
|
||||
|
@ -11,15 +12,13 @@ interface ITasksProps {
|
|||
}
|
||||
|
||||
const TaskList = ({ value = [] }: ITasksProps) => {
|
||||
const { colors } = useTheme();
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<View>
|
||||
{value.map(item => (
|
||||
<View style={styles.row}>
|
||||
<Text style={[styles.text, { color: colors.bodyText }]}>{item.status ? '- [x] ' : '- [ ] '}</Text>
|
||||
<Text style={[styles.inline, { color: colors.bodyText }]}>
|
||||
<Inline value={item.value} />
|
||||
</Text>
|
||||
<Text style={[styles.text, { color: themes[theme].bodyText }]}>{item.status ? '- [x] ' : '- [ ] '}</Text>
|
||||
<Inline value={item.value} />
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
|
|
@ -300,10 +300,7 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
|
|||
theme={theme}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
styles.audioContainer,
|
||||
{ backgroundColor: themes[theme].chatComponentBackground, borderColor: themes[theme].borderColor }
|
||||
]}
|
||||
style={[styles.audioContainer, { backgroundColor: themes[theme].surfaceTint, borderColor: themes[theme].borderColor }]}
|
||||
>
|
||||
<Button disabled={isReply} loading={loading} paused={paused} onPress={this.togglePlayPause} theme={theme} />
|
||||
<Slider
|
||||
|
|
|
@ -21,7 +21,7 @@ const Broadcast = React.memo(({ author, broadcast }: IMessageBroadcast) => {
|
|||
<View style={styles.buttonContainer}>
|
||||
<Touchable
|
||||
onPress={replyBroadcast}
|
||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||
background={Touchable.Ripple(themes[theme].buttonBackgroundSecondaryPress)}
|
||||
style={[styles.button, { backgroundColor: themes[theme].tintColor }]}
|
||||
hitSlop={BUTTON_HIT_SLOP}
|
||||
testID='message-broadcast-reply'
|
||||
|
|
|
@ -10,13 +10,13 @@ import { themes } from '../../lib/constants';
|
|||
import { IMessageCallButton } from './interfaces';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const CallButton = React.memo(({ handleEnterCall }: IMessageCallButton) => {
|
||||
const CallButton = React.memo(({ callJitsi }: IMessageCallButton) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<View style={styles.buttonContainer}>
|
||||
<Touchable
|
||||
onPress={handleEnterCall}
|
||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||
onPress={callJitsi}
|
||||
background={Touchable.Ripple(themes[theme].buttonBackgroundSecondaryPress)}
|
||||
style={[styles.button, { backgroundColor: themes[theme].tintColor }]}
|
||||
hitSlop={BUTTON_HIT_SLOP}
|
||||
>
|
||||
|
|
|
@ -125,7 +125,7 @@ const CollapsibleQuote = React.memo(
|
|||
|
||||
let {
|
||||
borderColor,
|
||||
chatComponentBackground: backgroundColor,
|
||||
surfaceTint: backgroundColor,
|
||||
collapsibleQuoteBorder,
|
||||
collapsibleChevron,
|
||||
headerTintColor
|
||||
|
@ -161,7 +161,7 @@ const CollapsibleQuote = React.memo(
|
|||
borderLeftWidth: 2
|
||||
}
|
||||
]}
|
||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||
background={Touchable.Ripple(themes[theme].buttonBackgroundSecondaryPress)}
|
||||
hitSlop={BUTTON_HIT_SLOP}
|
||||
>
|
||||
<View style={styles.touchableContainer}>
|
||||
|
|
|
@ -1,20 +1,14 @@
|
|||
import React from 'react';
|
||||
|
||||
import { themes } from '../../../../lib/constants';
|
||||
import { CustomIcon } from '../../../CustomIcon';
|
||||
import styles from '../../styles';
|
||||
import { useTheme } from '../../../../theme';
|
||||
|
||||
const ReadReceipt = React.memo(({ isReadReceiptEnabled, unread }: { isReadReceiptEnabled?: boolean; unread?: boolean }) => {
|
||||
const { colors } = useTheme();
|
||||
if (isReadReceiptEnabled) {
|
||||
return (
|
||||
<CustomIcon
|
||||
name='check'
|
||||
color={!unread && unread !== null ? colors.tintColor : colors.auxiliaryTintColor}
|
||||
size={16}
|
||||
style={styles.rightIcons}
|
||||
/>
|
||||
);
|
||||
const { theme } = useTheme();
|
||||
if (isReadReceiptEnabled && !unread && unread !== null) {
|
||||
return <CustomIcon name='check' color={themes[theme].tintColor} size={16} style={styles.rightIcons} />;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
|
|
@ -53,7 +53,7 @@ const Content = React.memo(
|
|||
content = (
|
||||
<Markdown
|
||||
msg={props.msg}
|
||||
md={props.type !== 'e2e' ? props.md : undefined}
|
||||
md={props.md}
|
||||
getCustomEmoji={props.getCustomEmoji}
|
||||
enableMessageParser={user.enableMessageParserEarlyAdoption}
|
||||
username={user.username}
|
||||
|
|
|
@ -29,7 +29,7 @@ const Discussion = React.memo(
|
|||
<View style={styles.buttonContainer}>
|
||||
<Touchable
|
||||
onPress={onDiscussionPress}
|
||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||
background={Touchable.Ripple(themes[theme].buttonBackgroundSecondaryPress)}
|
||||
style={[styles.button, { backgroundColor: themes[theme].tintColor }]}
|
||||
hitSlop={BUTTON_HIT_SLOP}
|
||||
>
|
||||
|
|
|
@ -38,7 +38,7 @@ const Button = React.memo(({ children, onPress, disabled, theme }: IMessageButto
|
|||
disabled={disabled}
|
||||
onPress={onPress}
|
||||
style={styles.imageContainer}
|
||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||
background={Touchable.Ripple(themes[theme].buttonBackgroundSecondaryPress)}
|
||||
>
|
||||
{children}
|
||||
</Touchable>
|
||||
|
@ -51,7 +51,7 @@ export const MessageImage = React.memo(({ imgUri, theme }: { imgUri: string; the
|
|||
resizeMode={FastImage.resizeMode.cover}
|
||||
indicator={Progress.Pie}
|
||||
indicatorProps={{
|
||||
color: themes[theme].actionTintColor
|
||||
color: themes[theme].tintColor
|
||||
}}
|
||||
/>
|
||||
));
|
||||
|
|
|
@ -18,7 +18,7 @@ const MessageAvatar = React.memo(({ isHeader, avatar, author, small, navToRoomIn
|
|||
style={small ? styles.avatarSmall : styles.avatar}
|
||||
text={avatar ? '' : author.username}
|
||||
size={small ? 20 : 36}
|
||||
borderRadius={4}
|
||||
borderRadius={small ? 2 : 4}
|
||||
onPress={author._id === user.id ? undefined : () => navToRoomInfo(navParam)}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
avatar={avatar}
|
||||
|
|
|
@ -36,7 +36,7 @@ const AddReaction = React.memo(({ theme }: { theme: TSupportedThemes }) => {
|
|||
key='message-add-reaction'
|
||||
testID='message-add-reaction'
|
||||
style={[styles.reactionButton, { backgroundColor: themes[theme].backgroundColor }]}
|
||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||
background={Touchable.Ripple(themes[theme].buttonBackgroundSecondaryPress)}
|
||||
hitSlop={BUTTON_HIT_SLOP}
|
||||
>
|
||||
<View style={[styles.reactionContainer, { borderColor: themes[theme].borderColor }]}>
|
||||
|
@ -57,9 +57,9 @@ const Reaction = React.memo(({ reaction, getCustomEmoji, theme }: IMessageReacti
|
|||
testID={`message-reaction-${reaction.emoji}`}
|
||||
style={[
|
||||
styles.reactionButton,
|
||||
{ backgroundColor: reacted ? themes[theme].bannerBackground : themes[theme].backgroundColor }
|
||||
{ backgroundColor: reacted ? themes[theme].buttonBackgroundSecondaryPress : themes[theme].backgroundColor }
|
||||
]}
|
||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||
background={Touchable.Ripple(themes[theme].buttonBackgroundSecondaryPress)}
|
||||
hitSlop={BUTTON_HIT_SLOP}
|
||||
>
|
||||
<View style={[styles.reactionContainer, { borderColor: reacted ? themes[theme].tintColor : themes[theme].borderColor }]}>
|
||||
|
|
|
@ -100,7 +100,7 @@ const Title = React.memo(
|
|||
return (
|
||||
<View style={styles.authorContainer}>
|
||||
{attachment.author_name ? (
|
||||
<Text numberOfLines={1} style={[styles.author, { color: themes[theme].auxiliaryTintColor }]}>
|
||||
<Text numberOfLines={1} style={[styles.author, { color: themes[theme].auxiliaryText }]}>
|
||||
{attachment.author_name}
|
||||
</Text>
|
||||
) : null}
|
||||
|
@ -131,7 +131,7 @@ const Description = React.memo(
|
|||
return (
|
||||
<Markdown
|
||||
msg={text}
|
||||
style={[{ color: themes[theme].auxiliaryTintColor, fontSize: 14 }]}
|
||||
style={[{ color: themes[theme].auxiliaryText, fontSize: 14 }]}
|
||||
username={user.username}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
theme={theme}
|
||||
|
@ -242,28 +242,31 @@ const Reply = React.memo(
|
|||
borderColor
|
||||
}
|
||||
]}
|
||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||
background={Touchable.Ripple(themes[theme].buttonBackgroundSecondaryPress)}
|
||||
disabled={loading}
|
||||
>
|
||||
<View style={styles.attachmentContainer}>
|
||||
<Title attachment={attachment} timeFormat={timeFormat} theme={theme} />
|
||||
<Description attachment={attachment} getCustomEmoji={getCustomEmoji} theme={theme} />
|
||||
<UrlImage image={attachment.thumb_url} />
|
||||
<Attachments
|
||||
attachments={attachment.attachments}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
timeFormat={timeFormat}
|
||||
style={[{ color: themes[theme].auxiliaryTintColor, fontSize: 14, marginBottom: 8 }]}
|
||||
style={[{ color: themes[theme].auxiliaryText, fontSize: 14, marginBottom: 8 }]}
|
||||
isReply
|
||||
id={messageId}
|
||||
/>
|
||||
<UrlImage image={attachment.thumb_url} />
|
||||
<Description attachment={attachment} getCustomEmoji={getCustomEmoji} theme={theme} />
|
||||
<Fields attachment={attachment} getCustomEmoji={getCustomEmoji} theme={theme} />
|
||||
{loading ? (
|
||||
<View style={[styles.backdrop]}>
|
||||
<View
|
||||
style={[
|
||||
styles.backdrop,
|
||||
{ backgroundColor: themes[theme].bannerBackground, opacity: themes[theme].attachmentLoadingOpacity }
|
||||
{
|
||||
backgroundColor: themes[theme].buttonBackgroundSecondaryPress,
|
||||
opacity: themes[theme].attachmentLoadingOpacity
|
||||
}
|
||||
]}
|
||||
></View>
|
||||
<RCActivityIndicator />
|
||||
|
|
|
@ -116,11 +116,11 @@ const Url = React.memo(
|
|||
index > 0 && styles.marginTop,
|
||||
styles.container,
|
||||
{
|
||||
backgroundColor: themes[theme].chatComponentBackground,
|
||||
backgroundColor: themes[theme].surfaceTint,
|
||||
borderColor: themes[theme].borderColor
|
||||
}
|
||||
]}
|
||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||
background={Touchable.Ripple(themes[theme].buttonBackgroundSecondaryPress)}
|
||||
>
|
||||
<>
|
||||
<UrlImage image={url.image} />
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue