Upgrade RN as well as update or replace other dependencies (#8011)
* start upgrade to RN 74 * migrate react-native-fs to expo-file-system * exclude expo-asset module * fix database manager remove directory * fix: android network helper * include expo on android * temporarily disable android dep lock * replace react-native-create-thumbnail with expo-video-thumnails * update patches file version * fix android build on 74 * create local library to replace MattermostManaged, Notifications and SplitView modules with new arch support * migrate app to use new mattermost-rnutils library * remove unused flipper class for android unsigned * fix mattermost-rnutils android foldedObserver lifecycle * use mattermost-rnutils on Android * use mattermost-rnutils on iOS * path react-native-navigation to not crash when activity is not NavigationActivity * create local library for android share extension with new arch support * Replace ShareModule with @mattermost/rnshare library * remove ShareModule from android native code * update react-intl * update nodejs to 20.x.x npm to 10.x.x and dev deps * update @gorhom/bottom-sheet * use MattermostShare conditionally based on the platform * update @react-native-camera-roll/camera-roll * remove unused react-native-calendars * fix metro config * fix terminate session race condition * remove unused analytics * replace react-native-device-info with expo-application and expo-device * update @react-native-clipboard/clipboard * update @react-native-community/datetimepicker * update @react-native-community/netinfo * update @sentry/react-native * update react-native-document-picker * update react-native-gesture-handler * update react-native-share * update react-native-svg and react-native-svg-transformer * update react-native-vector-icons * update babel * update react-native-shadow-2 * update semver * remove react-native-svg-transformer and convert svg files to svg components * fix @mattermost/rnshare new-arch build on android * remove react-native-create-thumbnail resolution in build.gradle * create @mattermost/hardware-keyboard library to replace hw-keyboard-events * fix hardware-keyboard library * fix rnutils library * create @mattermost/keyboard-tracker library * replace react-native-keyboard-tracking-view with @mattermost/keyboard-tracker * fix: rnutils to not crash on lateinit context * fix: rnutils delete database * revert changes to session_manager * Removed react-native-webview and added expo-web-browser instead With expo-web-browser we no longer need the webview for SSO login the SSO login is now done by using "custom Chrome tabs" on Android and ASWebAuthenticationSession on iOS * remove patch for react-native-keyboard-tracking-view * replace react-native-linear-gradient with expo-linear-gradient * replace react-native-in-app-review with expo-store-review * fix: shared group database directory on ios * replace react-native-fast-image with expo-image * remove unusued type def for react-native-keyboard-tracking-view * replace react-native-elements and react-native-button with @rneui * node version * update sizzling methods * fix tests using jest-expo * replace jail-monkey with expo-device * update babel deps * update typescript eslint * update rn and expo * react-native-document-picker @react-native-camera-roll/camera-roll @react-native-community/datetimepicker react-native-reanimated react-native-safe-area-context * update patches * update @sentry/react-native * upgrade react-native-navigation * update expo & expo-image * upgrade to working version of @sentry/react-native * update node, cocoapods, bundler, fastlane versions * @testing-library/react-native and eslint-plugin-jest * fix: FloatingTextInput causing a crash with reanimated * update sentry, localize, @types/lodash and uuid * fix floating text input label * update react-native-video * fix: cannot calculate shadow efficiently on some components * fix: reduce motion warning for bottomSheet * fix: shadow on YouTube component * update react-native-webrtc expo and @typescript-eslint * audit fix * fix swizzling bad merge * temp use of github dependency for @mattermost libraries * feedback review * feedback review * npm audit fix * update bundle deps * update @mattermost/react-native-turbo-log * update deps
This commit is contained in:
parent
1f2a71c499
commit
b8c088cc70
313 changed files with 17912 additions and 9050 deletions
|
|
@ -69,7 +69,7 @@
|
|||
"newlines-between": "always",
|
||||
"pathGroups": [
|
||||
{
|
||||
"pattern": "{@(@actions|@app|@assets|@calls|@client|@components|@constants|@context|@database|@helpers|@hooks|@init|@managers|@queries|@screens|@selectors|@share|@store|@telemetry|@typings|@test|@utils)/**,@(@constants|@i18n|@notifications|@store|@websocket)}",
|
||||
"pattern": "{@(@actions|@app|@assets|@calls|@client|@components|@constants|@context|@database|@helpers|@hooks|@init|@managers|@queries|@screens|@selectors|@share|@store|@telemetry|@typings|@test|@utils)/**,@(@constants|@i18n|@store|@websocket)}",
|
||||
"group": "external",
|
||||
"position": "after"
|
||||
},
|
||||
|
|
|
|||
2
.github/workflows/build-android-beta.yml
vendored
2
.github/workflows/build-android-beta.yml
vendored
|
|
@ -8,7 +8,7 @@ on:
|
|||
- build-beta-android-[0-9]+
|
||||
|
||||
env:
|
||||
NODE_VERSION: 18.7.0
|
||||
NODE_VERSION: 20.13.1
|
||||
TERM: xterm
|
||||
|
||||
jobs:
|
||||
|
|
|
|||
2
.github/workflows/build-android-release.yml
vendored
2
.github/workflows/build-android-release.yml
vendored
|
|
@ -8,7 +8,7 @@ on:
|
|||
- build-release-android-[0-9]+
|
||||
|
||||
env:
|
||||
NODE_VERSION: 18.7.0
|
||||
NODE_VERSION: 20.13.1
|
||||
TERM: xterm
|
||||
|
||||
jobs:
|
||||
|
|
|
|||
2
.github/workflows/build-ios-beta.yml
vendored
2
.github/workflows/build-ios-beta.yml
vendored
|
|
@ -9,7 +9,7 @@ on:
|
|||
- build-beta-sim-[0-9]+
|
||||
|
||||
env:
|
||||
NODE_VERSION: 18.7.0
|
||||
NODE_VERSION: 20.13.1
|
||||
TERM: xterm
|
||||
|
||||
jobs:
|
||||
|
|
|
|||
2
.github/workflows/build-ios-release.yml
vendored
2
.github/workflows/build-ios-release.yml
vendored
|
|
@ -9,7 +9,7 @@ on:
|
|||
- build-release-sim-[0-9]+
|
||||
|
||||
env:
|
||||
NODE_VERSION: 18.7.0
|
||||
NODE_VERSION: 20.13.1
|
||||
TERM: xterm
|
||||
|
||||
jobs:
|
||||
|
|
|
|||
2
.github/workflows/build-pr.yml
vendored
2
.github/workflows/build-pr.yml
vendored
|
|
@ -6,7 +6,7 @@ on:
|
|||
- labeled
|
||||
|
||||
env:
|
||||
NODE_VERSION: 18.7.0
|
||||
NODE_VERSION: 20.13.1
|
||||
TERM: xterm
|
||||
|
||||
jobs:
|
||||
|
|
|
|||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
|
@ -8,7 +8,7 @@ on:
|
|||
pull_request:
|
||||
|
||||
env:
|
||||
NODE_VERSION: 18.7.0
|
||||
NODE_VERSION: 20.13.1
|
||||
TERM: xterm
|
||||
|
||||
jobs:
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -115,3 +115,6 @@ launch.json
|
|||
|
||||
# Temporary files created by Metro to check the health of the file watcher
|
||||
.metro-health-check*
|
||||
|
||||
libraries/**/**/build
|
||||
libraries/**/**/.build
|
||||
|
|
@ -1 +1 @@
|
|||
18.17
|
||||
20.13.1
|
||||
2
.nvmrc
2
.nvmrc
|
|
@ -1 +1 @@
|
|||
18.17
|
||||
20.13.1
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
{
|
||||
"rule": "cli",
|
||||
"binary": "pod",
|
||||
"semver": "1.14.3",
|
||||
"semver": "1.15.2",
|
||||
"platform": "darwin"
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
source "https://rubygems.org"
|
||||
|
||||
gem "cocoapods", "1.14.3"
|
||||
gem "cocoapods", "1.15.2"
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.6)
|
||||
CFPropertyList (3.0.7)
|
||||
base64
|
||||
nkf
|
||||
rexml
|
||||
activesupport (7.1.2)
|
||||
activesupport (7.1.3.4)
|
||||
base64
|
||||
bigdecimal
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
|
|
@ -13,19 +15,19 @@ GEM
|
|||
minitest (>= 5.1)
|
||||
mutex_m
|
||||
tzinfo (~> 2.0)
|
||||
addressable (2.8.5)
|
||||
addressable (2.8.6)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
algoliasearch (1.27.5)
|
||||
httpclient (~> 2.8, >= 2.8.3)
|
||||
json (>= 1.5.1)
|
||||
atomos (0.1.3)
|
||||
base64 (0.2.0)
|
||||
bigdecimal (3.1.4)
|
||||
bigdecimal (3.1.8)
|
||||
claide (1.1.0)
|
||||
cocoapods (1.14.3)
|
||||
cocoapods (1.15.2)
|
||||
addressable (~> 2.8)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
cocoapods-core (= 1.14.3)
|
||||
cocoapods-core (= 1.15.2)
|
||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||
cocoapods-downloader (>= 2.1, < 3.0)
|
||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||
|
|
@ -40,7 +42,7 @@ GEM
|
|||
nap (~> 1.0)
|
||||
ruby-macho (>= 2.3.0, < 3.0)
|
||||
xcodeproj (>= 1.23.0, < 2.0)
|
||||
cocoapods-core (1.14.3)
|
||||
cocoapods-core (1.15.2)
|
||||
activesupport (>= 5.0, < 8)
|
||||
addressable (~> 2.8)
|
||||
algoliasearch (~> 1.0)
|
||||
|
|
@ -60,36 +62,37 @@ GEM
|
|||
netrc (~> 0.11)
|
||||
cocoapods-try (1.2.0)
|
||||
colored2 (3.1.2)
|
||||
concurrent-ruby (1.2.2)
|
||||
concurrent-ruby (1.3.3)
|
||||
connection_pool (2.4.1)
|
||||
drb (2.2.0)
|
||||
ruby2_keywords
|
||||
drb (2.2.1)
|
||||
escape (0.0.4)
|
||||
ethon (0.16.0)
|
||||
ffi (>= 1.15.0)
|
||||
ffi (1.16.3)
|
||||
ffi (1.17.0)
|
||||
fourflusher (2.3.1)
|
||||
fuzzy_match (2.0.4)
|
||||
gh_inspector (1.1.3)
|
||||
httpclient (2.8.3)
|
||||
i18n (1.14.1)
|
||||
i18n (1.14.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
json (2.6.3)
|
||||
minitest (5.20.0)
|
||||
json (2.7.2)
|
||||
minitest (5.23.1)
|
||||
molinillo (0.8.0)
|
||||
mutex_m (0.2.0)
|
||||
nanaimo (0.3.0)
|
||||
nap (1.1.0)
|
||||
netrc (0.11.0)
|
||||
nkf (0.2.0)
|
||||
public_suffix (4.0.7)
|
||||
rexml (3.2.6)
|
||||
rexml (3.2.9)
|
||||
strscan
|
||||
ruby-macho (2.5.1)
|
||||
ruby2_keywords (0.0.5)
|
||||
strscan (3.1.0)
|
||||
typhoeus (1.4.1)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
xcodeproj (1.23.0)
|
||||
xcodeproj (1.24.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
|
|
@ -101,7 +104,7 @@ PLATFORMS
|
|||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
cocoapods (= 1.14.3)
|
||||
cocoapods (= 1.15.2)
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.26
|
||||
2.5.11
|
||||
357
NOTICE.txt
357
NOTICE.txt
|
|
@ -1163,42 +1163,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|||
THE SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## jail-monkey
|
||||
|
||||
This product contains 'jail-monkey' by Gant Laborde.
|
||||
|
||||
A React Native module for identifying jail-broken, rooted, or mock locations on iOS and Android
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/GantMan/jail-monkey#readme
|
||||
|
||||
* LICENSE: MIT
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Gant Laborde
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## mime-db
|
||||
|
|
@ -1526,77 +1490,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-native-button
|
||||
|
||||
This product contains 'react-native-button' by James Ide.
|
||||
|
||||
A button for React apps
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/ide/react-native-button
|
||||
|
||||
* LICENSE: MIT
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 James Ide
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-native-calendars
|
||||
|
||||
This product contains a modified version of 'react-native-calendars' by Wix.com.
|
||||
|
||||
React Native Calendar Components
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/wix/react-native-calendars#readme
|
||||
|
||||
* LICENSE: MIT
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Wix.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-native-create-thumbnail
|
||||
|
|
@ -1633,42 +1526,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-native-device-info
|
||||
|
||||
This product contains a modified version of 'react-native-device-info' by Rebecca Hughes.
|
||||
|
||||
Get device information using react-native
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/react-native-community/react-native-device-info#readme
|
||||
|
||||
* LICENSE: MIT
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Rebecca Hughes
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-native-document-picker
|
||||
|
|
@ -1741,40 +1598,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-native-elements
|
||||
|
||||
This product contains 'react-native-elements' by React Native Elements.
|
||||
|
||||
Cross Platform React Native UI Toolkit
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/react-native-elements/react-native-elements
|
||||
|
||||
* LICENSE: The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Nader Dabit
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-native-exception-handler
|
||||
|
|
@ -1881,42 +1704,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-native-fs
|
||||
|
||||
This product contains 'react-native-fs' by Hagen Hübel.
|
||||
|
||||
Native filesystem access for react-native
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/itinance/react-native-fs
|
||||
|
||||
* LICENSE: MIT
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Johannes Lumpe
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-native-gesture-handler
|
||||
|
|
@ -2049,42 +1836,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-native-in-app-review
|
||||
|
||||
This product contains 'react-native-in-app-review' by Mina Samir Shafik.
|
||||
|
||||
react native in app review, to rate on Play store, App Store, Generally, the in-app review flow (see figure 1 for play store, figure 2 for ios) can be triggered at any time throughout the user journey of your app. During the flow, the user has the ability
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/MinaSamir11/react-native-in-app-review#readme
|
||||
|
||||
* LICENSE: MIT
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Mina Samir
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-native-incall-manager
|
||||
|
|
@ -2151,42 +1902,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-native-keyboard-tracking-view
|
||||
|
||||
This product contains a modified version of 'react-native-keyboard-tracking-view' by Artal Druk.
|
||||
|
||||
React Native UI component which tracks the keyboard
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/wix/react-native-keyboard-tracking-view
|
||||
|
||||
* LICENSE: MIT
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Wix.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-native-keychain
|
||||
|
|
@ -2223,42 +1938,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-native-linear-gradient
|
||||
|
||||
This product contains 'react-native-linear-gradient' by Brent Vatne.
|
||||
|
||||
A <LinearGradient> element for React Native
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/react-native-community/react-native-linear-gradient#readme
|
||||
|
||||
* LICENSE: MIT
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 React Native Community
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-native-localize
|
||||
|
|
@ -2848,42 +2527,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-native-webview
|
||||
|
||||
This product contains a modified version of 'react-native-webview' by Jamon Holmgren.
|
||||
|
||||
React Native WebView component for iOS, Android, and Windows 10 (coming soon)
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/react-native-community/react-native-webview#readme
|
||||
|
||||
* LICENSE: MIT
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2015-present, Facebook, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## react-syntax-highlighter
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ android {
|
|||
matchingFallbacks = ['release']
|
||||
}
|
||||
}
|
||||
|
||||
// applicationVariants are e.g. debug, release
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
|
|
@ -190,7 +191,6 @@ repositories {
|
|||
dependencies {
|
||||
// The version of react-native is set by the React Native Gradle Plugin
|
||||
implementation("com.facebook.react:react-android")
|
||||
implementation("com.facebook.react:flipper-integration")
|
||||
|
||||
if (hermesEnabled.toBoolean()) {
|
||||
implementation("com.facebook.react:hermes-android")
|
||||
|
|
@ -198,14 +198,9 @@ dependencies {
|
|||
implementation jscFlavor
|
||||
}
|
||||
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
|
||||
implementation 'io.reactivex.rxjava3:rxjava:3.1.6'
|
||||
implementation 'io.reactivex.rxjava3:rxandroid:3.0.2'
|
||||
implementation 'androidx.window:window-core:1.1.0'
|
||||
implementation 'androidx.window:window-rxjava3:1.0.0'
|
||||
implementation 'androidx.window:window:1.0.0'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'com.google.android.material:material:1.8.0'
|
||||
implementation 'com.google.android.material:material:1.12.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation "com.google.firebase:firebase-messaging:$firebaseVersion"
|
||||
|
||||
|
|
@ -247,7 +242,7 @@ configurations.all {
|
|||
|
||||
// Run this once to be able to run the application with BUCK
|
||||
// puts all compile dependencies into folder libs for BUCK to use
|
||||
task copyDownloadableDepsToLibs(type: Copy) {
|
||||
tasks.register('copyDownloadableDepsToLibs', Copy) {
|
||||
from configurations.implementation
|
||||
into 'libs'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@
|
|||
android:exported="false"
|
||||
/>
|
||||
<activity
|
||||
android:name="com.mattermost.share.ShareActivity"
|
||||
android:name="com.mattermost.rnshare.ShareActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:theme="@style/AppTheme"
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
package com.mattermost.helpers;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.oblador.keychain.KeychainModule;
|
||||
|
||||
|
||||
public class Credentials {
|
||||
public static void getCredentialsForServer(ReactApplicationContext context, String serverUrl, ResolvePromise promise) {
|
||||
final KeychainModule keychainModule = new KeychainModule(context);
|
||||
|
||||
final WritableMap options = Arguments.createMap();
|
||||
// KeyChain module fails if `authenticationPrompt` is not set
|
||||
final WritableMap authPrompt = Arguments.createMap();
|
||||
authPrompt.putString("title", "Authenticate to retrieve secret");
|
||||
authPrompt.putString("cancel", "Cancel");
|
||||
options.putMap("authenticationPrompt", authPrompt);
|
||||
options.putString("service", serverUrl);
|
||||
|
||||
keychainModule.getGenericPasswordForOptions(options, promise);
|
||||
}
|
||||
|
||||
public static String getCredentialsForServerSync(ReactApplicationContext context, String serverUrl) {
|
||||
final String[] token = new String[1];
|
||||
Credentials.getCredentialsForServer(context, serverUrl, new ResolvePromise() {
|
||||
@Override
|
||||
public void resolve(@Nullable Object value) {
|
||||
WritableMap map = (WritableMap) value;
|
||||
if (map != null) {
|
||||
token[0] = map.getString("password");
|
||||
String service = map.getString("service");
|
||||
assert service != null;
|
||||
if (service.isEmpty()) {
|
||||
String[] credentials = token[0].split(", *");
|
||||
if (credentials.length == 2) {
|
||||
token[0] = credentials[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return token[0];
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ import androidx.core.app.RemoteInput;
|
|||
import androidx.core.graphics.drawable.IconCompat;
|
||||
|
||||
import com.mattermost.rnbeta.*;
|
||||
import com.mattermost.rnutils.helpers.NotificationHelper;
|
||||
import com.nozbe.watermelondb.WMDatabase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
@ -57,7 +58,6 @@ public class CustomPushNotificationHelper {
|
|||
public static final String CHANNEL_HIGH_IMPORTANCE_ID = "channel_01";
|
||||
public static final String CHANNEL_MIN_IMPORTANCE_ID = "channel_02";
|
||||
public static final String KEY_TEXT_REPLY = "CAN_REPLY";
|
||||
public static final int MESSAGE_NOTIFICATION_ID = 435345;
|
||||
public static final String NOTIFICATION_ID = "notificationId";
|
||||
public static final String NOTIFICATION = "notification";
|
||||
public static final String PUSH_TYPE_MESSAGE = "message";
|
||||
|
|
@ -192,7 +192,7 @@ public class CustomPushNotificationHelper {
|
|||
String channelId = bundle.getString("channel_id");
|
||||
String postId = bundle.getString("post_id");
|
||||
String rootId = bundle.getString("root_id");
|
||||
int notificationId = postId != null ? postId.hashCode() : MESSAGE_NOTIFICATION_ID;
|
||||
int notificationId = postId != null ? postId.hashCode() : NotificationHelper.INSTANCE.MESSAGE_NOTIFICATION_ID;
|
||||
|
||||
boolean is_crt_enabled = bundle.containsKey("is_crt_enabled") && Objects.equals(bundle.getString("is_crt_enabled"), "true");
|
||||
String groupId = is_crt_enabled && !android.text.TextUtils.isEmpty(rootId) ? rootId : channelId;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import com.facebook.react.bridge.ReactApplicationContext;
|
|||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
import com.mattermost.networkclient.APIClientModule;
|
||||
import com.mattermost.networkclient.ApiClientModuleImpl;
|
||||
import com.mattermost.networkclient.enums.RetryTypes;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
|
|
@ -16,13 +16,13 @@ import okhttp3.Response;
|
|||
|
||||
|
||||
public class Network {
|
||||
private static APIClientModule clientModule;
|
||||
private static ApiClientModuleImpl clientModule;
|
||||
private static final WritableMap clientOptions = Arguments.createMap();
|
||||
private static final Promise emptyPromise = new ResolvePromise();
|
||||
|
||||
public static void init(Context context) {
|
||||
final ReactApplicationContext reactContext = (APIClientModule.context == null) ? new ReactApplicationContext(context) : APIClientModule.context;
|
||||
clientModule = new APIClientModule(reactContext);
|
||||
final ReactApplicationContext reactContext = (ApiClientModuleImpl.context == null) ? new ReactApplicationContext(context) : ApiClientModuleImpl.context;
|
||||
clientModule = new ApiClientModuleImpl(reactContext);
|
||||
createClientOptions();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,312 +0,0 @@
|
|||
package com.mattermost.helpers;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.os.Bundle;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
|
||||
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class NotificationHelper {
|
||||
public static final String PUSH_NOTIFICATIONS = "PUSH_NOTIFICATIONS";
|
||||
public static final String NOTIFICATIONS_IN_GROUP = "notificationsInGroup";
|
||||
private static final String VERSION_PREFERENCE = "VERSION_PREFERENCE";
|
||||
|
||||
public static void cleanNotificationPreferencesIfNeeded(Context context) {
|
||||
try {
|
||||
PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||
String version = String.valueOf(pInfo.versionCode);
|
||||
String storedVersion = null;
|
||||
SharedPreferences pSharedPref = context.getSharedPreferences(VERSION_PREFERENCE, Context.MODE_PRIVATE);
|
||||
if (pSharedPref != null) {
|
||||
storedVersion = pSharedPref.getString("Version", "");
|
||||
}
|
||||
|
||||
if (!version.equals(storedVersion)) {
|
||||
if (pSharedPref != null) {
|
||||
SharedPreferences.Editor editor = pSharedPref.edit();
|
||||
editor.putString("Version", version);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
Map<String, JSONObject> inputMap = new HashMap<>();
|
||||
saveMap(context, inputMap);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static int getNotificationId(Bundle notification) {
|
||||
final String postId = notification.getString("post_id");
|
||||
final String channelId = notification.getString("channel_id");
|
||||
|
||||
int notificationId = CustomPushNotificationHelper.MESSAGE_NOTIFICATION_ID;
|
||||
if (postId != null) {
|
||||
notificationId = postId.hashCode();
|
||||
} else if (channelId != null) {
|
||||
notificationId = channelId.hashCode();
|
||||
}
|
||||
|
||||
return notificationId;
|
||||
}
|
||||
|
||||
public static StatusBarNotification[] getDeliveredNotifications(Context context) {
|
||||
final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
return notificationManager.getActiveNotifications();
|
||||
}
|
||||
|
||||
public static boolean addNotificationToPreferences(Context context, int notificationId, Bundle notification) {
|
||||
try {
|
||||
boolean createSummary = true;
|
||||
final String serverUrl = notification.getString("server_url");
|
||||
final String channelId = notification.getString("channel_id");
|
||||
final String rootId = notification.getString("root_id");
|
||||
final boolean isCRTEnabled = notification.containsKey("is_crt_enabled") && notification.getString("is_crt_enabled").equals("true");
|
||||
|
||||
final boolean isThreadNotification = isCRTEnabled && !android.text.TextUtils.isEmpty(rootId);
|
||||
final String groupId = isThreadNotification ? rootId : channelId;
|
||||
|
||||
Map<String, JSONObject> notificationsPerServer = loadMap(context);
|
||||
JSONObject notificationsInServer = notificationsPerServer.get(serverUrl);
|
||||
if (notificationsInServer == null) {
|
||||
notificationsInServer = new JSONObject();
|
||||
}
|
||||
|
||||
JSONObject notificationsInGroup = notificationsInServer.optJSONObject(groupId);
|
||||
if (notificationsInGroup == null) {
|
||||
notificationsInGroup = new JSONObject();
|
||||
}
|
||||
|
||||
if (notificationsInGroup.length() > 0) {
|
||||
createSummary = false;
|
||||
}
|
||||
|
||||
notificationsInGroup.put(String.valueOf(notificationId), false);
|
||||
|
||||
if (createSummary) {
|
||||
// Add the summary notification id as well
|
||||
notificationsInGroup.put(String.valueOf(notificationId + 1), true);
|
||||
}
|
||||
notificationsInServer.put(groupId, notificationsInGroup);
|
||||
notificationsPerServer.put(serverUrl, notificationsInServer);
|
||||
saveMap(context, notificationsPerServer);
|
||||
|
||||
return createSummary;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void dismissNotification(Context context, Bundle notification) {
|
||||
final boolean isCRTEnabled = notification.containsKey("is_crt_enabled") && notification.getString("is_crt_enabled").equals("true");
|
||||
final String serverUrl = notification.getString("server_url");
|
||||
final String channelId = notification.getString("channel_id");
|
||||
final String rootId = notification.getString("root_id");
|
||||
|
||||
int notificationId = getNotificationId(notification);
|
||||
|
||||
if (!android.text.TextUtils.isEmpty(serverUrl) && !android.text.TextUtils.isEmpty(channelId)) {
|
||||
boolean isThreadNotification = isCRTEnabled && !android.text.TextUtils.isEmpty(rootId);
|
||||
String notificationIdStr = String.valueOf(notificationId);
|
||||
String groupId = isThreadNotification ? rootId : channelId;
|
||||
|
||||
Map<String, JSONObject> notificationsPerServer = loadMap(context);
|
||||
JSONObject notificationsInServer = notificationsPerServer.get(serverUrl);
|
||||
if (notificationsInServer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject notificationsInGroup = notificationsInServer.optJSONObject(groupId);
|
||||
if (notificationsInGroup == null) {
|
||||
return;
|
||||
}
|
||||
boolean isSummary = notificationsInGroup.optBoolean(notificationIdStr);
|
||||
notificationsInGroup.remove(notificationIdStr);
|
||||
|
||||
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
|
||||
notificationManager.cancel(notificationId);
|
||||
StatusBarNotification[] statusNotifications = getDeliveredNotifications(context);
|
||||
boolean hasMore = false;
|
||||
|
||||
for (final StatusBarNotification status : statusNotifications) {
|
||||
Bundle bundle = status.getNotification().extras;
|
||||
if (isThreadNotification) {
|
||||
hasMore = bundle.containsKey("root_id") && bundle.getString("root_id").equals(rootId);
|
||||
} else {
|
||||
hasMore = bundle.containsKey("channel_id") && bundle.getString("channel_id").equals(channelId);
|
||||
}
|
||||
if (hasMore) break;
|
||||
}
|
||||
|
||||
if (!hasMore || isSummary) {
|
||||
notificationsInServer.remove(groupId);
|
||||
} else {
|
||||
try {
|
||||
notificationsInServer.put(groupId, notificationsInGroup);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
notificationsPerServer.put(serverUrl, notificationsInServer);
|
||||
saveMap(context, notificationsPerServer);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeChannelNotifications(Context context, String serverUrl, String channelId) {
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
Map<String, JSONObject> notificationsPerServer = loadMap(context);
|
||||
JSONObject notificationsInServer = notificationsPerServer.get(serverUrl);
|
||||
|
||||
if (notificationsInServer != null) {
|
||||
notificationsInServer.remove(channelId);
|
||||
notificationsPerServer.put(serverUrl, notificationsInServer);
|
||||
saveMap(context, notificationsPerServer);
|
||||
}
|
||||
|
||||
StatusBarNotification[] notifications = getDeliveredNotifications(context);
|
||||
for (StatusBarNotification sbn:notifications) {
|
||||
Notification n = sbn.getNotification();
|
||||
Bundle bundle = n.extras;
|
||||
String cId = bundle.getString("channel_id");
|
||||
String rootId = bundle.getString("root_id");
|
||||
boolean isCRTEnabled = bundle.containsKey("is_crt_enabled") && bundle.getString("is_crt_enabled").equals("true");
|
||||
boolean skipThreadNotification = isCRTEnabled && !android.text.TextUtils.isEmpty(rootId);
|
||||
if (Objects.equals(cId, channelId) && !skipThreadNotification) {
|
||||
notificationManager.cancel(sbn.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeThreadNotifications(Context context, String serverUrl, String threadId) {
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
Map<String, JSONObject> notificationsPerServer = loadMap(context);
|
||||
JSONObject notificationsInServer = notificationsPerServer.get(serverUrl);
|
||||
|
||||
StatusBarNotification[] notifications = getDeliveredNotifications(context);
|
||||
for (StatusBarNotification sbn:notifications) {
|
||||
Notification n = sbn.getNotification();
|
||||
Bundle bundle = n.extras;
|
||||
String rootId = bundle.getString("root_id");
|
||||
String postId = bundle.getString("post_id");
|
||||
if (Objects.equals(rootId, threadId)) {
|
||||
notificationManager.cancel(sbn.getId());
|
||||
}
|
||||
|
||||
if (Objects.equals(postId, threadId)) {
|
||||
String channelId = bundle.getString("channel_id");
|
||||
int id = sbn.getId();
|
||||
if (notificationsInServer != null && channelId != null) {
|
||||
JSONObject notificationsInChannel = notificationsInServer.optJSONObject(channelId);
|
||||
if (notificationsInChannel != null) {
|
||||
notificationsInChannel.remove(String.valueOf(id));
|
||||
try {
|
||||
notificationsInServer.put(channelId, notificationsInChannel);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
notificationManager.cancel(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (notificationsInServer != null) {
|
||||
notificationsInServer.remove(threadId);
|
||||
notificationsPerServer.put(serverUrl, notificationsInServer);
|
||||
saveMap(context, notificationsPerServer);
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeServerNotifications(Context context, String serverUrl) {
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
Map<String, JSONObject> notificationsPerServer = loadMap(context);
|
||||
notificationsPerServer.remove(serverUrl);
|
||||
saveMap(context, notificationsPerServer);
|
||||
StatusBarNotification[] notifications = getDeliveredNotifications(context);
|
||||
for (StatusBarNotification sbn:notifications) {
|
||||
Notification n = sbn.getNotification();
|
||||
Bundle bundle = n.extras;
|
||||
String url = bundle.getString("server_url");
|
||||
if (Objects.equals(url, serverUrl)) {
|
||||
notificationManager.cancel(sbn.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearChannelOrThreadNotifications(Context context, Bundle notification) {
|
||||
final String serverUrl = notification.getString("server_url");
|
||||
final String channelId = notification.getString("channel_id");
|
||||
final String rootId = notification.getString("root_id");
|
||||
if (channelId != null) {
|
||||
final boolean isCRTEnabled = notification.containsKey("is_crt_enabled") && notification.getString("is_crt_enabled").equals("true");
|
||||
// rootId is available only when CRT is enabled & clearing the thread
|
||||
final boolean isClearThread = isCRTEnabled && !android.text.TextUtils.isEmpty(rootId);
|
||||
|
||||
if (isClearThread) {
|
||||
removeThreadNotifications(context, serverUrl, rootId);
|
||||
} else {
|
||||
removeChannelNotifications(context, serverUrl, channelId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Map Structure
|
||||
*
|
||||
* { serverUrl: { groupId: { notification1: true, notification2: false } } }
|
||||
* summary notification has a value of true
|
||||
*
|
||||
*/
|
||||
|
||||
private static void saveMap(Context context, Map<String, JSONObject> inputMap) {
|
||||
SharedPreferences pSharedPref = context.getSharedPreferences(PUSH_NOTIFICATIONS, Context.MODE_PRIVATE);
|
||||
if (pSharedPref != null) {
|
||||
JSONObject json = new JSONObject(inputMap);
|
||||
String jsonString = json.toString();
|
||||
SharedPreferences.Editor editor = pSharedPref.edit();
|
||||
editor.remove(NOTIFICATIONS_IN_GROUP).apply();
|
||||
editor.putString(NOTIFICATIONS_IN_GROUP, jsonString);
|
||||
editor.apply();
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, JSONObject> loadMap(Context context) {
|
||||
Map<String, JSONObject> outputMap = new HashMap<>();
|
||||
if (context != null) {
|
||||
SharedPreferences pSharedPref = context.getSharedPreferences(PUSH_NOTIFICATIONS, Context.MODE_PRIVATE);
|
||||
try {
|
||||
if (pSharedPref != null) {
|
||||
String jsonString = pSharedPref.getString(NOTIFICATIONS_IN_GROUP, (new JSONObject()).toString());
|
||||
JSONObject json = new JSONObject(jsonString);
|
||||
Iterator<String> servers = json.keys();
|
||||
|
||||
while (servers.hasNext()) {
|
||||
String serverUrl = servers.next();
|
||||
JSONObject notificationGroup = json.getJSONObject(serverUrl);
|
||||
outputMap.put(serverUrl, notificationGroup);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return outputMap;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,274 +0,0 @@
|
|||
package com.mattermost.helpers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.content.ContentResolver;
|
||||
import android.os.Environment;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.util.Log;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
// Class based on DocumentHelper https://gist.github.com/steveevers/a5af24c226f44bb8fdc3
|
||||
|
||||
public class RealPathUtil {
|
||||
public static final String CACHE_DIR_NAME = "mmShare";
|
||||
public static String getRealPathFromURI(final Context context, final Uri uri) {
|
||||
|
||||
// DocumentProvider
|
||||
if (DocumentsContract.isDocumentUri(context, uri)) {
|
||||
// ExternalStorageProvider
|
||||
if (isExternalStorageDocument(uri)) {
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
if ("primary".equalsIgnoreCase(type)) {
|
||||
return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||
}
|
||||
} else if (isDownloadsDocument(uri)) {
|
||||
// DownloadsProvider
|
||||
|
||||
final String id = DocumentsContract.getDocumentId(uri);
|
||||
if (!TextUtils.isEmpty(id)) {
|
||||
if (id.startsWith("raw:")) {
|
||||
return id.replaceFirst("raw:", "");
|
||||
}
|
||||
try {
|
||||
return getPathFromSavingTempFile(context, uri);
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e("ReactNative", "DownloadsProvider unexpected uri " + uri);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} else if (isMediaDocument(uri)) {
|
||||
// MediaProvider
|
||||
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
Uri contentUri = null;
|
||||
if ("image".equals(type)) {
|
||||
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("video".equals(type)) {
|
||||
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("audio".equals(type)) {
|
||||
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
}
|
||||
|
||||
final String selection = "_id=?";
|
||||
final String[] selectionArgs = new String[] {
|
||||
split[1]
|
||||
};
|
||||
|
||||
String name = getDataColumn(context, contentUri, selection, selectionArgs);
|
||||
if (!TextUtils.isEmpty(name)) {
|
||||
return name;
|
||||
}
|
||||
|
||||
return getPathFromSavingTempFile(context, uri);
|
||||
}
|
||||
}
|
||||
|
||||
if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||
// MediaStore (and general)
|
||||
|
||||
if (isGooglePhotosUri(uri)) {
|
||||
return uri.getLastPathSegment();
|
||||
}
|
||||
|
||||
// Try save to tmp file, and return tmp file path
|
||||
return getPathFromSavingTempFile(context, uri);
|
||||
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||
return uri.getPath();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getPathFromSavingTempFile(Context context, final Uri uri) {
|
||||
File tmpFile;
|
||||
String fileName = "";
|
||||
|
||||
if (uri == null || uri.isRelative()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try and get the filename from the Uri
|
||||
try {
|
||||
Cursor returnCursor =
|
||||
context.getContentResolver().query(uri, null, null, null, null);
|
||||
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
returnCursor.moveToFirst();
|
||||
fileName = sanitizeFilename(returnCursor.getString(nameIndex));
|
||||
returnCursor.close();
|
||||
} catch (Exception e) {
|
||||
// just continue to get the filename with the last segment of the path
|
||||
}
|
||||
|
||||
try {
|
||||
if (TextUtils.isEmpty(fileName)) {
|
||||
fileName = sanitizeFilename(uri.getLastPathSegment().trim());
|
||||
}
|
||||
|
||||
|
||||
File cacheDir = new File(context.getCacheDir(), CACHE_DIR_NAME);
|
||||
boolean cacheDirExists = cacheDir.exists();
|
||||
if (!cacheDirExists) {
|
||||
cacheDirExists = cacheDir.mkdirs();
|
||||
}
|
||||
|
||||
if (cacheDirExists) {
|
||||
tmpFile = new File(cacheDir, fileName);
|
||||
boolean fileCreated = tmpFile.createNewFile();
|
||||
|
||||
if (fileCreated) {
|
||||
ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r");
|
||||
|
||||
try (FileInputStream inputSrc = new FileInputStream(pfd.getFileDescriptor())) {
|
||||
FileChannel src = inputSrc.getChannel();
|
||||
try (FileOutputStream outputDst = new FileOutputStream(tmpFile)) {
|
||||
FileChannel dst = outputDst.getChannel();
|
||||
dst.transferFrom(src, 0, src.size());
|
||||
src.close();
|
||||
dst.close();
|
||||
}
|
||||
}
|
||||
|
||||
pfd.close();
|
||||
}
|
||||
return tmpFile.getAbsolutePath();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getDataColumn(Context context, Uri uri, String selection,
|
||||
String[] selectionArgs) {
|
||||
|
||||
Cursor cursor = null;
|
||||
final String column = "_data";
|
||||
final String[] projection = {
|
||||
column
|
||||
};
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
|
||||
null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final int index = cursor.getColumnIndexOrThrow(column);
|
||||
return cursor.getString(index);
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isExternalStorageDocument(Uri uri) {
|
||||
return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
public static boolean isDownloadsDocument(Uri uri) {
|
||||
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
public static boolean isMediaDocument(Uri uri) {
|
||||
return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
public static boolean isGooglePhotosUri(Uri uri) {
|
||||
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
public static String getExtension(String uri) {
|
||||
String extension = "";
|
||||
if (uri == null) {
|
||||
return extension;
|
||||
}
|
||||
|
||||
extension = MimeTypeMap.getFileExtensionFromUrl(uri);
|
||||
if (!extension.equals("")) {
|
||||
return extension;
|
||||
}
|
||||
|
||||
int dot = uri.lastIndexOf(".");
|
||||
if (dot >= 0) {
|
||||
return uri.substring(dot);
|
||||
} else {
|
||||
// No extension.
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getMimeType(File file) {
|
||||
|
||||
String extension = getExtension(file.getName());
|
||||
|
||||
if (extension.length() > 0)
|
||||
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.substring(1));
|
||||
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
public static String getMimeType(String filePath) {
|
||||
File file = new File(filePath);
|
||||
return getMimeType(file);
|
||||
}
|
||||
|
||||
public static String getMimeTypeFromUri(final Context context, final Uri uri) {
|
||||
try {
|
||||
ContentResolver cR = context.getContentResolver();
|
||||
return cR.getType(uri);
|
||||
} catch (Exception e) {
|
||||
return "application/octet-stream";
|
||||
}
|
||||
}
|
||||
|
||||
public static void deleteTempFiles(final File dir) {
|
||||
try {
|
||||
if (dir.isDirectory()) {
|
||||
deleteRecursive(dir);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
private static void deleteRecursive(File fileOrDirectory) {
|
||||
if (fileOrDirectory.isDirectory()) {
|
||||
File[] files = fileOrDirectory.listFiles();
|
||||
if (files != null) {
|
||||
for (File child : files)
|
||||
deleteRecursive(child);
|
||||
}
|
||||
}
|
||||
|
||||
if (!fileOrDirectory.delete()) {
|
||||
Log.i("ReactNative", "Couldn't delete file " + fileOrDirectory.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static String sanitizeFilename(String filename) {
|
||||
if (filename == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File f = new File(filename);
|
||||
return f.getName();
|
||||
}
|
||||
}
|
||||
|
|
@ -8,10 +8,9 @@ import androidx.core.app.NotificationCompat
|
|||
import com.mattermost.helpers.CustomPushNotificationHelper
|
||||
import com.mattermost.helpers.DatabaseHelper
|
||||
import com.mattermost.helpers.Network
|
||||
import com.mattermost.helpers.NotificationHelper
|
||||
import com.mattermost.helpers.PushNotificationDataHelper
|
||||
import com.mattermost.helpers.database_extension.getServerUrlForIdentifier
|
||||
import com.mattermost.share.ShareModule
|
||||
import com.mattermost.rnutils.helpers.NotificationHelper
|
||||
import com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME
|
||||
import com.wix.reactnativenotifications.core.AppLaunchHelper
|
||||
import com.wix.reactnativenotifications.core.AppLifecycleFacade
|
||||
|
|
@ -104,8 +103,7 @@ class CustomPushNotification(
|
|||
|
||||
when (type) {
|
||||
CustomPushNotificationHelper.PUSH_TYPE_MESSAGE, CustomPushNotificationHelper.PUSH_TYPE_SESSION -> {
|
||||
val shareModule = ShareModule.getInstance()
|
||||
val currentActivityName = shareModule?.currentActivityName ?: ""
|
||||
val currentActivityName = mAppLifecycleFacade.runningReactContext.currentActivity?.componentName?.className ?: ""
|
||||
Log.i("ReactNative", currentActivityName)
|
||||
if (!mAppLifecycleFacade.isAppVisible() || currentActivityName != "MainActivity") {
|
||||
var createSummary = type == CustomPushNotificationHelper.PUSH_TYPE_MESSAGE
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
package com.mattermost.rnbeta
|
||||
|
||||
import android.os.Bundle
|
||||
|
||||
import android.view.KeyEvent
|
||||
import android.content.res.Configuration
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import com.facebook.react.ReactActivityDelegate
|
||||
import com.reactnativenavigation.NavigationActivity
|
||||
import com.github.emilioicai.hwkeyboardevent.HWKeyboardEventModule
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
|
||||
import com.facebook.react.defaults.DefaultReactActivityDelegate
|
||||
import com.mattermost.hardware.keyboard.MattermostHardwareKeyboardImpl
|
||||
import com.mattermost.rnutils.helpers.FoldableObserver
|
||||
import com.reactnativenavigation.NavigationActivity
|
||||
import expo.modules.ReactActivityDelegateWrapper
|
||||
|
||||
class MainActivity : NavigationActivity() {
|
||||
private var HWKeyboardConnected = false
|
||||
|
|
@ -26,7 +26,8 @@ class MainActivity : NavigationActivity() {
|
|||
* which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
|
||||
*/
|
||||
override fun createReactActivityDelegate(): ReactActivityDelegate =
|
||||
DefaultReactActivityDelegate(this, mainComponentName, DefaultNewArchitectureEntryPoint.fabricEnabled)
|
||||
ReactActivityDelegateWrapper(this, BuildConfig.IS_NEW_ARCHITECTURE_ENABLED,
|
||||
DefaultReactActivityDelegate(this, mainComponentName, DefaultNewArchitectureEntryPoint.fabricEnabled))
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
|
@ -46,6 +47,11 @@ class MainActivity : NavigationActivity() {
|
|||
foldableObserver.onStop()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
foldableObserver.onDestroy()
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
|
||||
|
|
@ -60,24 +66,11 @@ class MainActivity : NavigationActivity() {
|
|||
reactGateway.onWindowFocusChanged(hasFocus)
|
||||
}
|
||||
|
||||
/*
|
||||
https://mattermost.atlassian.net/browse/MM-10601
|
||||
Required by react-native-hw-keyboard-event
|
||||
(https://github.com/emilioicai/react-native-hw-keyboard-event)
|
||||
*/
|
||||
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||
if (HWKeyboardConnected) {
|
||||
val keyCode = event.keyCode
|
||||
val keyAction = event.action
|
||||
if (keyAction == KeyEvent.ACTION_UP) {
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER) {
|
||||
val keyPressed = if (event.isShiftPressed) "shift-enter" else "enter"
|
||||
HWKeyboardEventModule.getInstance().keyPressed(keyPressed)
|
||||
return true
|
||||
} else if (keyCode == KeyEvent.KEYCODE_K && event.isCtrlPressed) {
|
||||
HWKeyboardEventModule.getInstance().keyPressed("find-channels")
|
||||
return true
|
||||
}
|
||||
val ok = MattermostHardwareKeyboardImpl.dispatchKeyEvent(event)
|
||||
if (ok) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return super.dispatchKeyEvent(event)
|
||||
|
|
|
|||
|
|
@ -1,151 +1,67 @@
|
|||
package com.mattermost.rnbeta
|
||||
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
|
||||
import com.facebook.react.PackageList
|
||||
import com.facebook.react.ReactHost
|
||||
import com.facebook.react.ReactInstanceManager
|
||||
import com.facebook.react.ReactNativeHost
|
||||
import com.facebook.react.ReactPackage
|
||||
import com.facebook.react.TurboReactPackage
|
||||
import com.facebook.react.bridge.JSIModulePackage
|
||||
import com.facebook.react.bridge.JSIModuleSpec
|
||||
import com.facebook.react.bridge.JavaScriptContextHolder
|
||||
import com.facebook.react.bridge.NativeModule
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.bridge.ReactContext
|
||||
import com.facebook.react.bridge.UiThreadUtil
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
|
||||
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
|
||||
import com.facebook.react.defaults.DefaultReactNativeHost
|
||||
import com.facebook.react.flipper.ReactNativeFlipper
|
||||
import com.facebook.react.module.model.ReactModuleInfo
|
||||
import com.facebook.react.module.model.ReactModuleInfoProvider
|
||||
import com.facebook.react.modules.network.OkHttpClientProvider
|
||||
import com.facebook.soloader.SoLoader
|
||||
|
||||
import com.mattermost.helpers.RealPathUtil
|
||||
import com.mattermost.networkclient.RCTOkHttpClientFactory
|
||||
import com.mattermost.share.ShareModule
|
||||
|
||||
import com.nozbe.watermelondb.jsi.WatermelonDBJSIPackage
|
||||
|
||||
import com.mattermost.rnshare.helpers.RealPathUtil
|
||||
import com.nozbe.watermelondb.jsi.JSIInstaller
|
||||
import com.reactnativenavigation.NavigationApplication
|
||||
|
||||
import com.wix.reactnativenotifications.RNNotificationsPackage
|
||||
import com.wix.reactnativenotifications.core.AppLaunchHelper
|
||||
import com.wix.reactnativenotifications.core.AppLifecycleFacade
|
||||
import com.wix.reactnativenotifications.core.JsIOHelper
|
||||
import com.wix.reactnativenotifications.core.notification.INotificationsApplication
|
||||
import com.wix.reactnativenotifications.core.notification.IPushNotification
|
||||
|
||||
import expo.modules.ApplicationLifecycleDispatcher
|
||||
import expo.modules.ReactNativeHostWrapper
|
||||
import expo.modules.image.okhttp.ExpoImageOkHttpClientGlideModule
|
||||
import java.io.File
|
||||
|
||||
class MainApplication : NavigationApplication(), INotificationsApplication {
|
||||
var instance: MainApplication? = null
|
||||
var sharedExtensionIsOpened = false
|
||||
private var listenerAdded = false
|
||||
|
||||
override val reactNativeHost: ReactNativeHost =
|
||||
object : DefaultReactNativeHost(this) {
|
||||
override fun getPackages(): List<ReactPackage> =
|
||||
PackageList(this).packages.apply {
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// add(MyReactNativePackage())
|
||||
add(RNNotificationsPackage(this@MainApplication))
|
||||
add(object : TurboReactPackage() {
|
||||
override fun getModule(
|
||||
name: String,
|
||||
reactContext: ReactApplicationContext
|
||||
): NativeModule {
|
||||
return when (name) {
|
||||
"MattermostManaged" -> MattermostManagedModule.getInstance(
|
||||
reactContext
|
||||
)
|
||||
"MattermostShare" -> ShareModule.getInstance(reactContext)
|
||||
"Notifications" -> NotificationsModule.getInstance(
|
||||
instance,
|
||||
reactContext
|
||||
)
|
||||
"SplitView" -> SplitViewModule.getInstance(
|
||||
reactContext
|
||||
)
|
||||
else ->
|
||||
throw IllegalArgumentException("Could not find module $name")
|
||||
}
|
||||
}
|
||||
ReactNativeHostWrapper(this,
|
||||
object : DefaultReactNativeHost(this) {
|
||||
override fun getPackages(): List<ReactPackage> =
|
||||
PackageList(this).packages.apply {
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// add(MyReactNativePackage())
|
||||
add(RNNotificationsPackage(this@MainApplication))
|
||||
}
|
||||
|
||||
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
|
||||
return ReactModuleInfoProvider {
|
||||
val map: MutableMap<String, ReactModuleInfo> = java.util.HashMap()
|
||||
map["MattermostManaged"] = ReactModuleInfo(
|
||||
"MattermostManaged",
|
||||
"com.mattermost.rnbeta.MattermostManagedModule",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
map["MattermostShare"] = ReactModuleInfo(
|
||||
"MattermostShare",
|
||||
"com.mattermost.share.ShareModule",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
map["Notifications"] = ReactModuleInfo(
|
||||
"Notifications",
|
||||
"com.mattermost.rnbeta.NotificationsModule",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
map["SplitView"] = ReactModuleInfo(
|
||||
"SplitView",
|
||||
"com.mattermost.rnbeta.SplitViewModule",
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
map
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
override fun getJSMainModuleName(): String = "index"
|
||||
|
||||
override fun getJSIModulePackage(): JSIModulePackage {
|
||||
return JSIModulePackage { reactApplicationContext: ReactApplicationContext?, jsContext: JavaScriptContextHolder? ->
|
||||
val modules =
|
||||
mutableListOf<JSIModuleSpec<*>>()
|
||||
modules.addAll(
|
||||
WatermelonDBJSIPackage().getJSIModules(
|
||||
reactApplicationContext,
|
||||
jsContext
|
||||
)
|
||||
)
|
||||
modules
|
||||
}
|
||||
}
|
||||
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
|
||||
|
||||
override fun getJSMainModuleName(): String = "index"
|
||||
|
||||
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
|
||||
|
||||
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
|
||||
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
|
||||
}
|
||||
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
|
||||
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
|
||||
})
|
||||
|
||||
override val reactHost: ReactHost
|
||||
get() = getDefaultReactHost(this.applicationContext, reactNativeHost)
|
||||
get() = getDefaultReactHost(applicationContext, reactNativeHost)
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
instance = this
|
||||
val context: Context = applicationContext
|
||||
|
||||
// Delete any previous temp files created by the app
|
||||
val tempFolder = File(context.cacheDir, RealPathUtil.CACHE_DIR_NAME)
|
||||
val tempFolder = File(applicationContext.cacheDir, RealPathUtil.CACHE_DIR_NAME)
|
||||
RealPathUtil.deleteTempFiles(tempFolder)
|
||||
Log.i("ReactNative", "Cleaning temp cache " + tempFolder.absolutePath)
|
||||
|
||||
|
|
@ -157,13 +73,20 @@ class MainApplication : NavigationApplication(), INotificationsApplication {
|
|||
// with a cookie jar defined in APIClientModule and an interceptor to intercept all
|
||||
// requests that originate from React Native's OKHttpClient
|
||||
OkHttpClientProvider.setOkHttpClientFactory(RCTOkHttpClientFactory())
|
||||
ExpoImageOkHttpClientGlideModule.okHttpClient = RCTOkHttpClientFactory().createNewNetworkModuleClient()
|
||||
|
||||
SoLoader.init(this, false)
|
||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
// If you opted-in for the New Architecture, we load the native entry point for this app.
|
||||
load()
|
||||
load(bridgelessEnabled = false)
|
||||
}
|
||||
ReactNativeFlipper.initializeFlipper(this, reactNativeHost.reactInstanceManager)
|
||||
ApplicationLifecycleDispatcher.onApplicationCreate(this)
|
||||
registerJSIModules()
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig)
|
||||
}
|
||||
|
||||
override fun getPushNotification(
|
||||
|
|
@ -180,4 +103,38 @@ class MainApplication : NavigationApplication(), INotificationsApplication {
|
|||
JsIOHelper()
|
||||
)
|
||||
}
|
||||
|
||||
@SuppressLint("VisibleForTests")
|
||||
private fun runOnJSQueueThread(action: () -> Unit) {
|
||||
reactNativeHost.reactInstanceManager.currentReactContext?.runOnJSQueueThread {
|
||||
action()
|
||||
} ?: UiThreadUtil.runOnUiThread {
|
||||
reactNativeHost.reactInstanceManager.currentReactContext?.runOnJSQueueThread {
|
||||
action()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun registerJSIModules() {
|
||||
val reactInstanceManager = reactNativeHost.reactInstanceManager
|
||||
|
||||
if (!listenerAdded) {
|
||||
listenerAdded = true
|
||||
reactInstanceManager.addReactInstanceEventListener(object : ReactInstanceManager.ReactInstanceEventListener {
|
||||
override fun onReactContextInitialized(context: ReactContext) {
|
||||
runOnJSQueueThread {
|
||||
registerWatermelonJSI(context)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerWatermelonJSI(context: ReactContext) {
|
||||
val holder = context.javaScriptContextHolder?.get()
|
||||
if (holder != null) {
|
||||
JSIInstaller.install(context, holder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,263 +0,0 @@
|
|||
package com.mattermost.rnbeta;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.text.TextUtils;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import com.facebook.react.bridge.ActivityEventListener;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.BaseActivityEventListener;
|
||||
import com.facebook.react.bridge.GuardedResultAsyncTask;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
|
||||
import com.mattermost.helpers.Credentials;
|
||||
import com.reactlibrary.createthumbnail.CreateThumbnailModule;
|
||||
import com.mattermost.helpers.RealPathUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.net.URL;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MattermostManagedModule extends ReactContextBaseJavaModule {
|
||||
private static final String SAVE_EVENT = "MattermostManagedSaveFile";
|
||||
private static final Integer SAVE_REQUEST = 38641;
|
||||
private static MattermostManagedModule instance;
|
||||
private ReactApplicationContext reactContext;
|
||||
|
||||
private Promise mPickerPromise;
|
||||
private String fileContent;
|
||||
|
||||
private MattermostManagedModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.reactContext = reactContext;
|
||||
// Let the document provider know you're done by closing the stream.
|
||||
ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {
|
||||
@Override
|
||||
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {
|
||||
if (requestCode == SAVE_REQUEST) {
|
||||
if (mPickerPromise != null) {
|
||||
if (resultCode == Activity.RESULT_CANCELED) {
|
||||
mPickerPromise.reject(SAVE_EVENT, "Save operation cancelled");
|
||||
} else if (resultCode == Activity.RESULT_OK) {
|
||||
Uri uri = intent.getData();
|
||||
if (uri == null) {
|
||||
mPickerPromise.reject(SAVE_EVENT, "No data found");
|
||||
} else {
|
||||
try {
|
||||
new SaveDataTask(reactContext, fileContent, uri).execute();
|
||||
mPickerPromise.resolve(uri.toString());
|
||||
} catch (Exception e) {
|
||||
mPickerPromise.reject(SAVE_EVENT, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mPickerPromise = null;
|
||||
} else if (resultCode == Activity.RESULT_OK) {
|
||||
try {
|
||||
Uri uri = intent.getData();
|
||||
if (uri != null)
|
||||
new SaveDataTask(reactContext, fileContent, uri).execute();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
reactContext.addActivityEventListener(mActivityEventListener);
|
||||
}
|
||||
|
||||
public static MattermostManagedModule getInstance(ReactApplicationContext reactContext) {
|
||||
if (instance == null) {
|
||||
instance = new MattermostManagedModule(reactContext);
|
||||
} else {
|
||||
instance.reactContext = reactContext;
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static MattermostManagedModule getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void sendEvent(String eventName,
|
||||
@Nullable WritableMap params) {
|
||||
this.reactContext
|
||||
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
.emit(eventName, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String getName() {
|
||||
return "MattermostManaged";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getFilePath(String filePath, Promise promise) {
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
WritableMap map = Arguments.createMap();
|
||||
|
||||
if (currentActivity != null) {
|
||||
Uri uri = Uri.parse(filePath);
|
||||
String path = RealPathUtil.getRealPathFromURI(currentActivity, uri);
|
||||
if (path != null) {
|
||||
String text = "file://" + path;
|
||||
map.putString("filePath", text);
|
||||
}
|
||||
}
|
||||
|
||||
promise.resolve(map);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void saveFile(String path, final Promise promise) {
|
||||
Uri contentUri;
|
||||
String filename = "";
|
||||
if(path.startsWith("content://")) {
|
||||
contentUri = Uri.parse(path);
|
||||
} else {
|
||||
File newFile = new File(path);
|
||||
filename = newFile.getName();
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
if(currentActivity == null) {
|
||||
promise.reject(SAVE_EVENT, "Activity doesn't exist");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final String packageName = currentActivity.getPackageName();
|
||||
final String authority = packageName + ".provider";
|
||||
contentUri = FileProvider.getUriForFile(currentActivity, authority, newFile);
|
||||
}
|
||||
catch(IllegalArgumentException e) {
|
||||
promise.reject(SAVE_EVENT, e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(contentUri == null) {
|
||||
promise.reject(SAVE_EVENT, "Invalid file");
|
||||
return;
|
||||
}
|
||||
|
||||
String extension = MimeTypeMap.getFileExtensionFromUrl(path).toLowerCase();
|
||||
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
if (mimeType == null) {
|
||||
mimeType = RealPathUtil.getMimeType(path);
|
||||
}
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_CREATE_DOCUMENT);
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType(mimeType);
|
||||
intent.putExtra(Intent.EXTRA_TITLE, filename);
|
||||
|
||||
PackageManager pm = Objects.requireNonNull(getCurrentActivity()).getPackageManager();
|
||||
if (intent.resolveActivity(pm) != null) {
|
||||
try {
|
||||
getCurrentActivity().startActivityForResult(intent, SAVE_REQUEST);
|
||||
mPickerPromise = promise;
|
||||
fileContent = path;
|
||||
}
|
||||
catch(Exception e) {
|
||||
promise.reject(SAVE_EVENT, e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if(mimeType == null) {
|
||||
throw new Exception("It wasn't possible to detect the type of the file");
|
||||
}
|
||||
throw new Exception("No app associated with this mime type");
|
||||
}
|
||||
catch(Exception e) {
|
||||
promise.reject(SAVE_EVENT, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void createThumbnail(ReadableMap options, Promise promise) {
|
||||
try {
|
||||
WritableMap optionsMap = Arguments.createMap();
|
||||
optionsMap.merge(options);
|
||||
String url = options.hasKey("url") ? options.getString("url") : "";
|
||||
URL videoUrl = new URL(url);
|
||||
String serverUrl = videoUrl.getProtocol() + "://" + videoUrl.getHost() + ":" + videoUrl.getPort();
|
||||
String token = Credentials.getCredentialsForServerSync(this.reactContext, serverUrl);
|
||||
if (!TextUtils.isEmpty(token)) {
|
||||
WritableMap headers = Arguments.createMap();
|
||||
if (optionsMap.hasKey("headers")) {
|
||||
headers.merge(Objects.requireNonNull(optionsMap.getMap("headers")));
|
||||
}
|
||||
headers.putString("Authorization", "Bearer " + token);
|
||||
optionsMap.putMap("headers", headers);
|
||||
}
|
||||
CreateThumbnailModule thumb = new CreateThumbnailModule(this.reactContext);
|
||||
thumb.create(optionsMap.copy(), promise);
|
||||
} catch (Exception e) {
|
||||
promise.reject("CreateThumbnail_ERROR", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SaveDataTask extends GuardedResultAsyncTask<Object> {
|
||||
private final WeakReference<Context> weakContext;
|
||||
private final String fromFile;
|
||||
private final Uri toFile;
|
||||
|
||||
protected SaveDataTask(ReactApplicationContext reactContext, String path, Uri destination) {
|
||||
super(reactContext.getExceptionHandler());
|
||||
weakContext = new WeakReference<>(reactContext.getApplicationContext());
|
||||
fromFile = path;
|
||||
toFile = destination;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doInBackgroundGuarded() {
|
||||
try {
|
||||
ParcelFileDescriptor pfd = weakContext.get().getContentResolver().openFileDescriptor(toFile, "w");
|
||||
File input = new File(this.fromFile);
|
||||
try (FileInputStream fileInputStream = new FileInputStream(input)) {
|
||||
try (FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor())) {
|
||||
FileChannel source = fileInputStream.getChannel();
|
||||
FileChannel dest = fileOutputStream.getChannel();
|
||||
dest.transferFrom(source, 0, source.size());
|
||||
source.close();
|
||||
dest.close();
|
||||
}
|
||||
}
|
||||
pfd.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecuteGuarded(Object o) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import android.app.IntentService;
|
|||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.mattermost.helpers.NotificationHelper;
|
||||
import com.mattermost.rnutils.helpers.NotificationHelper;
|
||||
import com.wix.reactnativenotifications.core.NotificationIntentAdapter;
|
||||
|
||||
public class NotificationDismissService extends IntentService {
|
||||
|
|
@ -19,7 +19,7 @@ public class NotificationDismissService extends IntentService {
|
|||
final Context context = getApplicationContext();
|
||||
final Bundle bundle = NotificationIntentAdapter.extractPendingNotificationDataFromIntent(intent);
|
||||
|
||||
NotificationHelper.dismissNotification(context, bundle);
|
||||
NotificationHelper.INSTANCE.dismissNotification(context, bundle);
|
||||
Log.i("ReactNative", "Dismiss notification");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
package com.mattermost.rnbeta;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.mattermost.helpers.NotificationHelper;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class NotificationsModule extends ReactContextBaseJavaModule {
|
||||
private static NotificationsModule instance;
|
||||
private final MainApplication mApplication;
|
||||
|
||||
private NotificationsModule(MainApplication application, ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
mApplication = application;
|
||||
}
|
||||
|
||||
public static NotificationsModule getInstance(MainApplication application, ReactApplicationContext reactContext) {
|
||||
if (instance == null) {
|
||||
instance = new NotificationsModule(application, reactContext);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Notifications";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getDeliveredNotifications(final Promise promise) {
|
||||
Context context = mApplication.getApplicationContext();
|
||||
StatusBarNotification[] notifications = NotificationHelper.getDeliveredNotifications(context);
|
||||
WritableArray result = Arguments.createArray();
|
||||
for (StatusBarNotification sbn:notifications) {
|
||||
WritableMap map = Arguments.createMap();
|
||||
Notification n = sbn.getNotification();
|
||||
Bundle bundle = n.extras;
|
||||
Set<String> keys = bundle.keySet();
|
||||
for (String key: keys) {
|
||||
map.putString(key, bundle.getString(key));
|
||||
}
|
||||
result.pushMap(map);
|
||||
}
|
||||
promise.resolve(result);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void removeChannelNotifications(String serverUrl, String channelId) {
|
||||
Context context = mApplication.getApplicationContext();
|
||||
NotificationHelper.removeChannelNotifications(context, serverUrl, channelId);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void removeThreadNotifications(String serverUrl, String threadId) {
|
||||
Context context = mApplication.getApplicationContext();
|
||||
NotificationHelper.removeThreadNotifications(context, serverUrl, threadId);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void removeServerNotifications(String serverUrl) {
|
||||
Context context = mApplication.getApplicationContext();
|
||||
NotificationHelper.removeServerNotifications(context, serverUrl);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
package com.mattermost.rnbeta
|
||||
|
||||
import com.facebook.react.bridge.*
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
|
||||
import com.learnium.RNDeviceInfo.resolver.DeviceTypeResolver
|
||||
|
||||
class SplitViewModule(private var reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
|
||||
private var listenerCount = 0
|
||||
|
||||
companion object {
|
||||
private var instance: SplitViewModule? = null
|
||||
|
||||
fun getInstance(reactContext: ReactApplicationContext): SplitViewModule {
|
||||
if (instance == null) {
|
||||
instance = SplitViewModule(reactContext)
|
||||
} else {
|
||||
instance!!.reactContext = reactContext
|
||||
}
|
||||
|
||||
return instance!!
|
||||
}
|
||||
|
||||
fun getInstance(): SplitViewModule? {
|
||||
return instance
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun getName() = "SplitView"
|
||||
|
||||
private fun sendEvent(params: WritableMap?) {
|
||||
reactContext
|
||||
.getJSModule(RCTDeviceEventEmitter::class.java)
|
||||
.emit("SplitViewChanged", params)
|
||||
}
|
||||
|
||||
private fun getSplitViewResults(folded: Boolean) : WritableMap? {
|
||||
if (currentActivity != null) {
|
||||
val deviceResolver = DeviceTypeResolver(this.reactContext)
|
||||
val map = Arguments.createMap()
|
||||
var isSplitView = folded;
|
||||
if (currentActivity?.isInMultiWindowMode == true) {
|
||||
isSplitView = FoldableObserver.getInstance()?.isCompactView() == true
|
||||
}
|
||||
map.putBoolean("isSplitView", isSplitView)
|
||||
map.putBoolean("isTablet", deviceResolver.isTablet)
|
||||
return map
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun setDeviceFolded() {
|
||||
val map = getSplitViewResults(FoldableObserver.getInstance()?.isDeviceFolded == true)
|
||||
if (listenerCount > 0) {
|
||||
sendEvent(map)
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
fun isRunningInSplitView(): WritableMap? {
|
||||
return getSplitViewResults(FoldableObserver.getInstance()?.isDeviceFolded == true)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun addListener(eventName: String) {
|
||||
listenerCount += 1
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun removeListeners(count: Int) {
|
||||
listenerCount -= count
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package com.mattermost.share;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.ReactActivity;
|
||||
import com.mattermost.rnbeta.MainApplication;
|
||||
|
||||
public class ShareActivity extends ReactActivity {
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
return "MattermostShare";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
MainApplication app = (MainApplication) this.getApplication();
|
||||
app.setSharedExtensionIsOpened(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,259 +0,0 @@
|
|||
package com.mattermost.share;
|
||||
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.mattermost.helpers.Credentials;
|
||||
import com.mattermost.rnbeta.MainApplication;
|
||||
import com.mattermost.helpers.RealPathUtil;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class ShareModule extends ReactContextBaseJavaModule {
|
||||
private final OkHttpClient client = new OkHttpClient();
|
||||
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
private static ShareModule instance;
|
||||
private final MainApplication mApplication;
|
||||
private ReactApplicationContext mReactContext;
|
||||
private File tempFolder;
|
||||
|
||||
private ShareModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
mReactContext = reactContext;
|
||||
mApplication = (MainApplication)reactContext.getApplicationContext();
|
||||
}
|
||||
|
||||
public static ShareModule getInstance(ReactApplicationContext reactContext) {
|
||||
if (instance == null) {
|
||||
instance = new ShareModule(reactContext);
|
||||
} else {
|
||||
instance.mReactContext = reactContext;
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static ShareModule getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "MattermostShare";
|
||||
}
|
||||
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public String getCurrentActivityName() {
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
if (currentActivity != null) {
|
||||
String activityName = currentActivity.getComponentName().getClassName();
|
||||
String[] components = activityName.split("\\.");
|
||||
return components[components.length - 1];
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void clear() {
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
if (currentActivity != null && this.getCurrentActivityName().equals("ShareActivity")) {
|
||||
Intent intent = currentActivity.getIntent();
|
||||
intent.setAction("");
|
||||
intent.removeExtra(Intent.EXTRA_TEXT);
|
||||
intent.removeExtra(Intent.EXTRA_STREAM);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Map<String, Object> getConstants() {
|
||||
HashMap<String, Object> constants = new HashMap<>(1);
|
||||
constants.put("cacheDirName", RealPathUtil.CACHE_DIR_NAME);
|
||||
constants.put("isOpened", mApplication.getSharedExtensionIsOpened());
|
||||
return constants;
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void close(ReadableMap data) {
|
||||
this.clear();
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
if (currentActivity == null || !this.getCurrentActivityName().equals("ShareActivity")) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentActivity.finishAndRemoveTask();
|
||||
if (data != null && data.hasKey("serverUrl")) {
|
||||
ReadableArray files = data.getArray("files");
|
||||
String serverUrl = data.getString("serverUrl");
|
||||
final String token = Credentials.getCredentialsForServerSync(mReactContext, serverUrl);
|
||||
JSONObject postData = buildPostObject(data);
|
||||
|
||||
if (files != null && files.size() > 0) {
|
||||
uploadFiles(serverUrl, token, files, postData);
|
||||
} else {
|
||||
try {
|
||||
post(serverUrl, token, postData);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mApplication.setSharedExtensionIsOpened(false);
|
||||
RealPathUtil.deleteTempFiles(this.tempFolder);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getSharedData(Promise promise) {
|
||||
promise.resolve(processIntent());
|
||||
}
|
||||
|
||||
public WritableArray processIntent() {
|
||||
String type, action, extra;
|
||||
WritableArray items = Arguments.createArray();
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
|
||||
if (currentActivity != null) {
|
||||
this.tempFolder = new File(currentActivity.getCacheDir(), RealPathUtil.CACHE_DIR_NAME);
|
||||
Intent intent = currentActivity.getIntent();
|
||||
action = intent.getAction();
|
||||
type = intent.getType();
|
||||
extra = intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||
|
||||
if (Intent.ACTION_SEND.equals(action) && "text/plain".equals(type) && extra != null) {
|
||||
items.pushMap(ShareUtils.getTextItem(extra));
|
||||
} else if (Intent.ACTION_SEND.equals(action)) {
|
||||
if (extra != null) {
|
||||
items.pushMap(ShareUtils.getTextItem(extra));
|
||||
}
|
||||
Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
if (uri != null) {
|
||||
ReadableMap fileInfo = ShareUtils.getFileItem(currentActivity, uri);
|
||||
if (fileInfo != null) {
|
||||
items.pushMap(fileInfo);
|
||||
}
|
||||
}
|
||||
} else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
|
||||
if (extra != null) {
|
||||
items.pushMap(ShareUtils.getTextItem(extra));
|
||||
}
|
||||
|
||||
ArrayList<Uri> uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
||||
for (Uri uri : uris) {
|
||||
ReadableMap fileInfo = ShareUtils.getFileItem(currentActivity, uri);
|
||||
if (fileInfo != null) {
|
||||
items.pushMap(fileInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private JSONObject buildPostObject(ReadableMap data) {
|
||||
JSONObject json = new JSONObject();
|
||||
try {
|
||||
json.put("user_id", data.getString("userId"));
|
||||
if (data.hasKey("channelId")) {
|
||||
json.put("channel_id", data.getString("channelId"));
|
||||
}
|
||||
if (data.hasKey("message")) {
|
||||
json.put("message", data.getString("message"));
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
private void post(String serverUrl, String token, JSONObject postData) throws IOException {
|
||||
RequestBody body = RequestBody.create(postData.toString(), JSON);
|
||||
Request request = new Request.Builder()
|
||||
.header("Authorization", "BEARER " + token)
|
||||
.url(serverUrl + "/api/v4/posts")
|
||||
.post(body)
|
||||
.build();
|
||||
client.newCall(request).execute();
|
||||
}
|
||||
|
||||
private void uploadFiles(String serverUrl, String token, ReadableArray files, JSONObject postData) {
|
||||
try {
|
||||
MultipartBody.Builder builder = new MultipartBody.Builder()
|
||||
.setType(MultipartBody.FORM);
|
||||
|
||||
for(int i = 0 ; i < files.size() ; i++) {
|
||||
ReadableMap file = files.getMap(i);
|
||||
String mime = file.getString("type");
|
||||
String fullPath = file.getString("value");
|
||||
if (fullPath != null) {
|
||||
String filePath = fullPath.replaceFirst("file://", "");
|
||||
File fileInfo = new File(filePath);
|
||||
if (fileInfo.exists() && mime != null) {
|
||||
final MediaType MEDIA_TYPE = MediaType.parse(mime);
|
||||
builder.addFormDataPart("files", file.getString("filename"), RequestBody.create(fileInfo, MEDIA_TYPE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
builder.addFormDataPart("channel_id", postData.getString("channel_id"));
|
||||
RequestBody body = builder.build();
|
||||
Request request = new Request.Builder()
|
||||
.header("Authorization", "BEARER " + token)
|
||||
.url(serverUrl + "/api/v4/files")
|
||||
.post(body)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
if (response.isSuccessful()) {
|
||||
String responseData = Objects.requireNonNull(response.body()).string();
|
||||
JSONObject responseJson = new JSONObject(responseData);
|
||||
JSONArray fileInfoArray = responseJson.getJSONArray("file_infos");
|
||||
JSONArray file_ids = new JSONArray();
|
||||
for(int i = 0 ; i < fileInfoArray.length() ; i++) {
|
||||
JSONObject fileInfo = fileInfoArray.getJSONObject(i);
|
||||
file_ids.put(fileInfo.getString("id"));
|
||||
}
|
||||
postData.put("file_ids", file_ids);
|
||||
post(serverUrl, token, postData);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
package com.mattermost.share;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.media.MediaMetadataRetriever;
|
||||
import android.net.Uri;
|
||||
import android.webkit.URLUtil;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.mattermost.helpers.RealPathUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ShareUtils {
|
||||
public static ReadableMap getTextItem(String text) {
|
||||
WritableMap map = Arguments.createMap();
|
||||
map.putString("value", text);
|
||||
map.putString("type", "");
|
||||
map.putBoolean("isString", true);
|
||||
return map;
|
||||
}
|
||||
|
||||
public static ReadableMap getFileItem(Activity activity, Uri uri) {
|
||||
WritableMap map = Arguments.createMap();
|
||||
String filePath = RealPathUtil.getRealPathFromURI(activity, uri);
|
||||
if (filePath == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File file = new File(filePath);
|
||||
String type = RealPathUtil.getMimeTypeFromUri(activity, uri);
|
||||
if (type != null) {
|
||||
if (type.startsWith("image/")) {
|
||||
BitmapFactory.Options bitMapOption = ShareUtils.getImageDimensions(filePath);
|
||||
map.putInt("height", bitMapOption.outHeight);
|
||||
map.putInt("width", bitMapOption.outWidth);
|
||||
|
||||
} else if (type.startsWith("video/")) {
|
||||
File cacheDir = new File(activity.getCacheDir(), RealPathUtil.CACHE_DIR_NAME);
|
||||
addVideoThumbnailToMap(cacheDir, activity.getApplicationContext(), map, "file://" + filePath);
|
||||
}
|
||||
} else {
|
||||
type = "application/octet-stream";
|
||||
}
|
||||
|
||||
map.putString("value", "file://" + filePath);
|
||||
map.putDouble("size", (double) file.length());
|
||||
map.putString("filename", file.getName());
|
||||
map.putString("type", type);
|
||||
map.putString("extension", RealPathUtil.getExtension(filePath).replaceFirst(".", ""));
|
||||
map.putBoolean("isString", false);
|
||||
return map;
|
||||
}
|
||||
|
||||
public static BitmapFactory.Options getImageDimensions(String filePath) {
|
||||
BitmapFactory.Options bitMapOption = new BitmapFactory.Options();
|
||||
bitMapOption.inJustDecodeBounds=true;
|
||||
BitmapFactory.decodeFile(filePath, bitMapOption);
|
||||
return bitMapOption;
|
||||
}
|
||||
|
||||
private static void addVideoThumbnailToMap(File cacheDir, Context context, WritableMap map, String filePath) {
|
||||
String fileName = ("thumb-" + UUID.randomUUID().toString()) + ".png";
|
||||
OutputStream fOut = null;
|
||||
|
||||
try {
|
||||
File file = new File(cacheDir, fileName);
|
||||
Bitmap image = getBitmapAtTime(context, filePath, 1);
|
||||
if (file.createNewFile()) {
|
||||
fOut = new FileOutputStream(file);
|
||||
image.compress(Bitmap.CompressFormat.PNG, 100, fOut);
|
||||
fOut.flush();
|
||||
fOut.close();
|
||||
|
||||
map.putString("videoThumb", "file://" + file.getAbsolutePath());
|
||||
map.putInt("width", image.getWidth());
|
||||
map.putInt("height", image.getHeight());
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private static Bitmap getBitmapAtTime(Context context, String filePath, int time) {
|
||||
try {
|
||||
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
||||
if (URLUtil.isFileUrl(filePath)) {
|
||||
String decodedPath;
|
||||
try {
|
||||
decodedPath = URLDecoder.decode(filePath, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
decodedPath = filePath;
|
||||
}
|
||||
|
||||
retriever.setDataSource(decodedPath.replace("file://", ""));
|
||||
} else if (filePath.contains("content://")) {
|
||||
retriever.setDataSource(context, Uri.parse(filePath));
|
||||
}
|
||||
|
||||
Bitmap image = retriever.getFrameAtTime(time * 1000, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
|
||||
retriever.release();
|
||||
return image;
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("File doesn't exist or not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,8 @@
|
|||
android:insetLeft="@dimen/abc_edit_text_inset_horizontal_material"
|
||||
android:insetRight="@dimen/abc_edit_text_inset_horizontal_material"
|
||||
android:insetTop="@dimen/abc_edit_text_inset_top_material"
|
||||
android:insetBottom="@dimen/abc_edit_text_inset_bottom_material">
|
||||
android:insetBottom="@dimen/abc_edit_text_inset_bottom_material"
|
||||
>
|
||||
<selector>
|
||||
<!--
|
||||
This file is a copy of abc_edit_text_material (https://bit.ly/3k8fX7I).
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||
* directory of this source tree.
|
||||
*/
|
||||
package com.mattermost.flipper;
|
||||
|
||||
import android.content.Context;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
/**
|
||||
* Class responsible of loading Flipper inside your React Native application. This is the release
|
||||
* flavor of it so it's empty as we don't want to load Flipper.
|
||||
*/
|
||||
public class ReactNativeFlipper {
|
||||
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
|
||||
// Do nothing as we don't want to initialize Flipper on Release.
|
||||
}
|
||||
}
|
||||
|
|
@ -5,11 +5,11 @@ buildscript {
|
|||
compileSdkVersion = 34
|
||||
targetSdkVersion = 34
|
||||
supportLibVersion = "33.0.0"
|
||||
kotlinVersion = "1.8.21"
|
||||
kotlinVersion = "1.9.22"
|
||||
kotlin_version = kotlinVersion
|
||||
RNNKotlinVersion = kotlinVersion
|
||||
firebaseVersion = "23.3.1"
|
||||
ndkVersion = "25.1.8937393"
|
||||
firebaseVersion = "24.0.0"
|
||||
ndkVersion = "26.1.10909125"
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
|
@ -20,7 +20,7 @@ buildscript {
|
|||
classpath("com.android.tools.build:gradle")
|
||||
classpath("com.facebook.react:react-native-gradle-plugin")
|
||||
classpath('com.google.gms:google-services:4.4.0')
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
|
@ -39,24 +39,6 @@ allprojects {
|
|||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
afterEvaluate { subproject ->
|
||||
if(subproject['name'] == 'react-native-create-thumbnail'){
|
||||
def myAttribute = Attribute.of("com.android.build.api.attributes.BuildTypeAttr", String)
|
||||
dependencies.attributesSchema {
|
||||
attribute(myAttribute)
|
||||
}
|
||||
configurations {
|
||||
implementation {
|
||||
attributes {
|
||||
attribute(myAttribute, "release")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: "com.facebook.react.rootproject"
|
||||
|
||||
dependencyLocking {
|
||||
|
|
|
|||
|
|
@ -1,55 +1,55 @@
|
|||
# This is a Gradle generated file for dependency locking.
|
||||
# Manual edits can break the build and are not advised.
|
||||
# This file is expected to be part of source control.
|
||||
androidx.databinding:databinding-common:8.1.1=classpath
|
||||
androidx.databinding:databinding-compiler-common:8.1.1=classpath
|
||||
com.android.databinding:baseLibrary:8.1.1=classpath
|
||||
com.android.tools.analytics-library:crash:31.1.1=classpath
|
||||
com.android.tools.analytics-library:protos:31.1.1=classpath
|
||||
com.android.tools.analytics-library:shared:31.1.1=classpath
|
||||
com.android.tools.analytics-library:tracker:31.1.1=classpath
|
||||
androidx.databinding:databinding-common:8.2.1=classpath
|
||||
androidx.databinding:databinding-compiler-common:8.2.1=classpath
|
||||
com.android.databinding:baseLibrary:8.2.1=classpath
|
||||
com.android.tools.analytics-library:crash:31.2.1=classpath
|
||||
com.android.tools.analytics-library:protos:31.2.1=classpath
|
||||
com.android.tools.analytics-library:shared:31.2.1=classpath
|
||||
com.android.tools.analytics-library:tracker:31.2.1=classpath
|
||||
com.android.tools.build.jetifier:jetifier-core:1.0.0-beta10=classpath
|
||||
com.android.tools.build.jetifier:jetifier-processor:1.0.0-beta10=classpath
|
||||
com.android.tools.build:aapt2-proto:8.1.1-10154469=classpath
|
||||
com.android.tools.build:aaptcompiler:8.1.1=classpath
|
||||
com.android.tools.build:apksig:8.1.1=classpath
|
||||
com.android.tools.build:apkzlib:8.1.1=classpath
|
||||
com.android.tools.build:builder-model:8.1.1=classpath
|
||||
com.android.tools.build:builder-test-api:8.1.1=classpath
|
||||
com.android.tools.build:builder:8.1.1=classpath
|
||||
com.android.tools.build:bundletool:1.14.0=classpath
|
||||
com.android.tools.build:gradle-api:8.1.1=classpath
|
||||
com.android.tools.build:gradle-settings-api:8.1.1=classpath
|
||||
com.android.tools.build:gradle:8.1.1=classpath
|
||||
com.android.tools.build:manifest-merger:31.1.1=classpath
|
||||
com.android.tools.build:aapt2-proto:8.2.1-10154469=classpath
|
||||
com.android.tools.build:aaptcompiler:8.2.1=classpath
|
||||
com.android.tools.build:apksig:8.2.1=classpath
|
||||
com.android.tools.build:apkzlib:8.2.1=classpath
|
||||
com.android.tools.build:builder-model:8.2.1=classpath
|
||||
com.android.tools.build:builder-test-api:8.2.1=classpath
|
||||
com.android.tools.build:builder:8.2.1=classpath
|
||||
com.android.tools.build:bundletool:1.15.2=classpath
|
||||
com.android.tools.build:gradle-api:8.2.1=classpath
|
||||
com.android.tools.build:gradle-settings-api:8.2.1=classpath
|
||||
com.android.tools.build:gradle:8.2.1=classpath
|
||||
com.android.tools.build:manifest-merger:31.2.1=classpath
|
||||
com.android.tools.build:transform-api:2.0.0-deprecated-use-gradle-api=classpath
|
||||
com.android.tools.ddms:ddmlib:31.1.1=classpath
|
||||
com.android.tools.layoutlib:layoutlib-api:31.1.1=classpath
|
||||
com.android.tools.lint:lint-model:31.1.1=classpath
|
||||
com.android.tools.lint:lint-typedef-remover:31.1.1=classpath
|
||||
com.android.tools.utp:android-device-provider-ddmlib-proto:31.1.1=classpath
|
||||
com.android.tools.utp:android-device-provider-gradle-proto:31.1.1=classpath
|
||||
com.android.tools.utp:android-test-plugin-host-additional-test-output-proto:31.1.1=classpath
|
||||
com.android.tools.utp:android-test-plugin-host-apk-installer-proto:31.1.1=classpath
|
||||
com.android.tools.utp:android-test-plugin-host-coverage-proto:31.1.1=classpath
|
||||
com.android.tools.utp:android-test-plugin-host-emulator-control-proto:31.1.1=classpath
|
||||
com.android.tools.utp:android-test-plugin-host-logcat-proto:31.1.1=classpath
|
||||
com.android.tools.utp:android-test-plugin-host-retention-proto:31.1.1=classpath
|
||||
com.android.tools.utp:android-test-plugin-result-listener-gradle-proto:31.1.1=classpath
|
||||
com.android.tools:annotations:31.1.1=classpath
|
||||
com.android.tools:common:31.1.1=classpath
|
||||
com.android.tools:dvlib:31.1.1=classpath
|
||||
com.android.tools:repository:31.1.1=classpath
|
||||
com.android.tools:sdk-common:31.1.1=classpath
|
||||
com.android.tools:sdklib:31.1.1=classpath
|
||||
com.android:signflinger:8.1.1=classpath
|
||||
com.android:zipflinger:8.1.1=classpath
|
||||
com.android.tools.ddms:ddmlib:31.2.1=classpath
|
||||
com.android.tools.layoutlib:layoutlib-api:31.2.1=classpath
|
||||
com.android.tools.lint:lint-model:31.2.1=classpath
|
||||
com.android.tools.lint:lint-typedef-remover:31.2.1=classpath
|
||||
com.android.tools.utp:android-device-provider-ddmlib-proto:31.2.1=classpath
|
||||
com.android.tools.utp:android-device-provider-gradle-proto:31.2.1=classpath
|
||||
com.android.tools.utp:android-test-plugin-host-additional-test-output-proto:31.2.1=classpath
|
||||
com.android.tools.utp:android-test-plugin-host-apk-installer-proto:31.2.1=classpath
|
||||
com.android.tools.utp:android-test-plugin-host-coverage-proto:31.2.1=classpath
|
||||
com.android.tools.utp:android-test-plugin-host-emulator-control-proto:31.2.1=classpath
|
||||
com.android.tools.utp:android-test-plugin-host-logcat-proto:31.2.1=classpath
|
||||
com.android.tools.utp:android-test-plugin-host-retention-proto:31.2.1=classpath
|
||||
com.android.tools.utp:android-test-plugin-result-listener-gradle-proto:31.2.1=classpath
|
||||
com.android.tools:annotations:31.2.1=classpath
|
||||
com.android.tools:common:31.2.1=classpath
|
||||
com.android.tools:dvlib:31.2.1=classpath
|
||||
com.android.tools:repository:31.2.1=classpath
|
||||
com.android.tools:sdk-common:31.2.1=classpath
|
||||
com.android.tools:sdklib:31.2.1=classpath
|
||||
com.android:signflinger:8.2.1=classpath
|
||||
com.android:zipflinger:8.2.1=classpath
|
||||
com.google.android.gms:strict-version-matcher-plugin:1.2.4=classpath
|
||||
com.google.android:annotations:4.1.1.4=classpath
|
||||
com.google.api.grpc:proto-google-common-protos:2.0.1=classpath
|
||||
com.google.auto.value:auto-value-annotations:1.6.2=classpath
|
||||
com.google.code.findbugs:jsr305:3.0.2=classpath
|
||||
com.google.code.gson:gson:2.8.9=classpath
|
||||
com.google.code.gson:gson:2.10=classpath
|
||||
com.google.crypto.tink:tink:1.7.0=classpath
|
||||
com.google.dagger:dagger:2.28.3=classpath
|
||||
com.google.errorprone:error_prone_annotations:2.11.0=classpath
|
||||
|
|
@ -100,8 +100,8 @@ net.java.dev.jna:jna:5.6.0=classpath
|
|||
net.sf.jopt-simple:jopt-simple:4.9=classpath
|
||||
net.sf.kxml:kxml2:2.3.0=classpath
|
||||
org.apache.commons:commons-compress:1.21=classpath
|
||||
org.apache.httpcomponents:httpclient:4.5.13=classpath
|
||||
org.apache.httpcomponents:httpcore:4.4.15=classpath
|
||||
org.apache.httpcomponents:httpclient:4.5.14=classpath
|
||||
org.apache.httpcomponents:httpcore:4.4.16=classpath
|
||||
org.apache.httpcomponents:httpmime:4.5.6=classpath
|
||||
org.bitbucket.b_c:jose4j:0.7.0=classpath
|
||||
org.bouncycastle:bcpkix-jdk15on:1.67=classpath
|
||||
|
|
@ -112,34 +112,33 @@ org.glassfish.jaxb:jaxb-runtime:2.3.2=classpath
|
|||
org.glassfish.jaxb:txw2:2.3.2=classpath
|
||||
org.jdom:jdom2:2.0.6=classpath
|
||||
org.jetbrains.intellij.deps:trove4j:1.0.20200330=classpath
|
||||
org.jetbrains.kotlin:kotlin-android-extensions:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-compiler-runner:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-daemon-client:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-annotations:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-idea-proto:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-idea:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugins-bom:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-klib-commonizer-api:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-native-utils:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-project-model:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.8.20-RC2=classpath
|
||||
org.jetbrains.kotlin:kotlin-scripting-common:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-scripting-jvm:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-common:1.8.20-RC2=classpath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.20-RC2=classpath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20-RC2=classpath
|
||||
org.jetbrains.kotlin:kotlin-stdlib:1.8.20-RC2=classpath
|
||||
org.jetbrains.kotlin:kotlin-tooling-core:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-util-io:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-util-klib:1.8.21=classpath
|
||||
org.jetbrains.kotlin:kotlin-android-extensions:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-build-tools-api:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-compiler-runner:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-daemon-client:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-annotations:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-idea-proto:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-idea:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugins-bom:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-klib-commonizer-api:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-native-utils:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-project-model:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.9.0=classpath
|
||||
org.jetbrains.kotlin:kotlin-scripting-common:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-scripting-jvm:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0=classpath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0=classpath
|
||||
org.jetbrains.kotlin:kotlin-stdlib:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-tooling-core:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-util-io:1.9.22=classpath
|
||||
org.jetbrains.kotlin:kotlin-util-klib:1.9.22=classpath
|
||||
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.0=classpath
|
||||
org.jetbrains:annotations:13.0=classpath
|
||||
org.jvnet.staxex:stax-ex:1.8.1=classpath
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
|||
14
android/gradlew
vendored
14
android/gradlew
vendored
|
|
@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
|
|
@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
|
|
@ -202,11 +202,11 @@ fi
|
|||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
|
|
|||
20
android/gradlew.bat
vendored
20
android/gradlew.bat
vendored
|
|
@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
|
|||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
|
@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ include ':app'
|
|||
include ':reactnativenotifications'
|
||||
project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/lib/android/app')
|
||||
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
|
||||
include ':react-native-video'
|
||||
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android-exoplayer')
|
||||
include ':watermelondb-jsi'
|
||||
project(':watermelondb-jsi').projectDir = new File(rootProject.projectDir, '../node_modules/@nozbe/watermelondb/native/android-jsi')
|
||||
includeBuild('../node_modules/@react-native/gradle-plugin')
|
||||
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle")
|
||||
useExpoModules()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import {DeviceEventEmitter} from 'react-native';
|
|||
import {Events, Calls} from '@constants';
|
||||
import {t} from '@i18n';
|
||||
import {setServerCredentials} from '@init/credentials';
|
||||
import {Analytics, create} from '@managers/analytics';
|
||||
import {semverFromServerVersion} from '@utils/server';
|
||||
|
||||
import * as ClientConstants from './constants';
|
||||
|
|
@ -20,7 +19,6 @@ import type {
|
|||
} from '@mattermost/react-native-network-client';
|
||||
|
||||
export default class ClientBase {
|
||||
analytics: Analytics|undefined;
|
||||
apiClient: APIClientInterface;
|
||||
csrfToken = '';
|
||||
requestHeaders: {[x: string]: string} = {};
|
||||
|
|
@ -30,7 +28,6 @@ export default class ClientBase {
|
|||
|
||||
constructor(apiClient: APIClientInterface, serverUrl: string, bearerToken?: string, csrfToken?: string) {
|
||||
this.apiClient = apiClient;
|
||||
this.analytics = create(serverUrl);
|
||||
|
||||
if (bearerToken) {
|
||||
this.setBearerToken(bearerToken);
|
||||
|
|
|
|||
|
|
@ -64,8 +64,6 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
|||
};
|
||||
|
||||
createChannel = async (channel: Channel) => {
|
||||
this.analytics?.trackAPI('api_channels_create', {team_id: channel.team_id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelsRoute()}`,
|
||||
{method: 'post', body: channel},
|
||||
|
|
@ -73,8 +71,6 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
|||
};
|
||||
|
||||
createDirectChannel = async (userIds: string[]) => {
|
||||
this.analytics?.trackAPI('api_channels_create_direct');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelsRoute()}/direct`,
|
||||
{method: 'post', body: userIds},
|
||||
|
|
@ -82,8 +78,6 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
|||
};
|
||||
|
||||
createGroupChannel = async (userIds: string[]) => {
|
||||
this.analytics?.trackAPI('api_channels_create_group');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelsRoute()}/group`,
|
||||
{method: 'post', body: userIds},
|
||||
|
|
@ -91,8 +85,6 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
|||
};
|
||||
|
||||
deleteChannel = async (channelId: string) => {
|
||||
this.analytics?.trackAPI('api_channels_delete', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}`,
|
||||
{method: 'delete'},
|
||||
|
|
@ -100,8 +92,6 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
|||
};
|
||||
|
||||
unarchiveChannel = async (channelId: string) => {
|
||||
this.analytics?.trackAPI('api_channels_unarchive', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/restore`,
|
||||
{method: 'post'},
|
||||
|
|
@ -109,8 +99,6 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
|||
};
|
||||
|
||||
updateChannel = async (channel: Channel) => {
|
||||
this.analytics?.trackAPI('api_channels_update', {channel_id: channel.id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channel.id)}`,
|
||||
{method: 'put', body: channel},
|
||||
|
|
@ -122,8 +110,6 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
|||
};
|
||||
|
||||
updateChannelPrivacy = async (channelId: string, privacy: any) => {
|
||||
this.analytics?.trackAPI('api_channels_update_privacy', {channel_id: channelId, privacy});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/privacy`,
|
||||
{method: 'put', body: {privacy}},
|
||||
|
|
@ -131,8 +117,6 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
|||
};
|
||||
|
||||
patchChannel = async (channelId: string, channelPatch: ChannelPatch) => {
|
||||
this.analytics?.trackAPI('api_channels_patch', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/patch`,
|
||||
{method: 'put', body: channelPatch},
|
||||
|
|
@ -140,8 +124,6 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
|||
};
|
||||
|
||||
updateChannelNotifyProps = async (props: ChannelNotifyProps & {channel_id: string; user_id: string}) => {
|
||||
this.analytics?.trackAPI('api_users_update_channel_notifications', {channel_id: props.channel_id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelMemberRoute(props.channel_id, props.user_id)}/notify_props`,
|
||||
{method: 'put', body: props},
|
||||
|
|
@ -149,8 +131,6 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
|||
};
|
||||
|
||||
getChannel = async (channelId: string) => {
|
||||
this.analytics?.trackAPI('api_channel_get', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}`,
|
||||
{method: 'get'},
|
||||
|
|
@ -165,8 +145,6 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
|||
};
|
||||
|
||||
getChannelByNameAndTeamName = async (teamName: string, channelName: string, includeDeleted = false) => {
|
||||
this.analytics?.trackAPI('api_channel_get_by_name_and_teamName', {channel_name: channelName, team_name: teamName, include_deleted: includeDeleted});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamNameRoute(teamName)}/channels/name/${channelName}?include_deleted=${includeDeleted}`,
|
||||
{method: 'get'},
|
||||
|
|
@ -247,8 +225,6 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
|||
};
|
||||
|
||||
addToChannel = async (userId: string, channelId: string, postRootId = '') => {
|
||||
this.analytics?.trackAPI('api_channels_add_member', {channel_id: channelId});
|
||||
|
||||
const member = {user_id: userId, channel_id: channelId, post_root_id: postRootId};
|
||||
return this.doFetch(
|
||||
`${this.getChannelMembersRoute(channelId)}`,
|
||||
|
|
@ -257,8 +233,6 @@ const ClientChannels = <TBase extends Constructor<ClientBase>>(superclass: TBase
|
|||
};
|
||||
|
||||
removeFromChannel = async (userId: string, channelId: string) => {
|
||||
this.analytics?.trackAPI('api_channels_remove_member', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelMemberRoute(channelId, userId)}`,
|
||||
{method: 'delete'},
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ const ClientFiles = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
searchFilesWithParams = async (teamId: string, params: FileSearchParams) => {
|
||||
this.analytics?.trackAPI('api_files_search');
|
||||
const endpoint = teamId ? `${this.getTeamRoute(teamId)}/files/search` : `${this.getFilesRoute()}/search`;
|
||||
return this.doFetch(endpoint, {method: 'post', body: params});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -39,8 +39,6 @@ const ClientIntegrations = <TBase extends Constructor<ClientBase>>(superclass: T
|
|||
};
|
||||
|
||||
executeCommand = async (command: string, commandArgs = {}) => {
|
||||
this.analytics?.trackAPI('api_integrations_used');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getCommandsRoute()}/execute`,
|
||||
{method: 'post', body: {command, ...commandArgs}},
|
||||
|
|
@ -48,8 +46,6 @@ const ClientIntegrations = <TBase extends Constructor<ClientBase>>(superclass: T
|
|||
};
|
||||
|
||||
addCommand = async (command: Command) => {
|
||||
this.analytics?.trackAPI('api_integrations_created');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getCommandsRoute()}`,
|
||||
{method: 'post', body: command},
|
||||
|
|
@ -57,7 +53,6 @@ const ClientIntegrations = <TBase extends Constructor<ClientBase>>(superclass: T
|
|||
};
|
||||
|
||||
submitInteractiveDialog = async (data: DialogSubmission) => {
|
||||
this.analytics?.trackAPI('api_interactive_messages_dialog_submitted');
|
||||
return this.doFetch(
|
||||
`${this.urlVersion}/actions/dialogs/submit`,
|
||||
{method: 'post', body: data},
|
||||
|
|
|
|||
|
|
@ -37,12 +37,6 @@ export interface ClientPostsMix {
|
|||
|
||||
const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
createPost = async (post: Post) => {
|
||||
this.analytics?.trackAPI('api_posts_create', {channel_id: post.channel_id});
|
||||
|
||||
if (post.root_id != null && post.root_id !== '') {
|
||||
this.analytics?.trackAPI('api_posts_replied', {channel_id: post.channel_id});
|
||||
}
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostsRoute()}`,
|
||||
{method: 'post', body: post, noRetry: true},
|
||||
|
|
@ -50,8 +44,6 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
updatePost = async (post: Post) => {
|
||||
this.analytics?.trackAPI('api_posts_update', {channel_id: post.channel_id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(post.id)}`,
|
||||
{method: 'put', body: post},
|
||||
|
|
@ -66,8 +58,6 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
patchPost = async (postPatch: Partial<Post> & {id: string}) => {
|
||||
this.analytics?.trackAPI('api_posts_patch', {channel_id: postPatch.channel_id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(postPatch.id)}/patch`,
|
||||
{method: 'put', body: postPatch},
|
||||
|
|
@ -75,8 +65,6 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
deletePost = async (postId: string) => {
|
||||
this.analytics?.trackAPI('api_posts_delete');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(postId)}`,
|
||||
{method: 'delete'},
|
||||
|
|
@ -114,8 +102,6 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
getPostsBefore = async (channelId: string, postId = '', page = 0, perPage = PER_PAGE_DEFAULT, collapsedThreads = false, collapsedThreadsExtended = false) => {
|
||||
this.analytics?.trackAPI('api_posts_get_before', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/posts${buildQueryString({before: postId, page, per_page: perPage, collapsedThreads, collapsedThreadsExtended})}`,
|
||||
{method: 'get'},
|
||||
|
|
@ -123,8 +109,6 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
getPostsAfter = async (channelId: string, postId: string, page = 0, perPage = PER_PAGE_DEFAULT, collapsedThreads = false, collapsedThreadsExtended = false) => {
|
||||
this.analytics?.trackAPI('api_posts_get_after', {channel_id: channelId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/posts${buildQueryString({after: postId, page, per_page: perPage, collapsedThreads, collapsedThreadsExtended})}`,
|
||||
{method: 'get'},
|
||||
|
|
@ -139,8 +123,6 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
getSavedPosts = async (userId: string, channelId = '', teamId = '', page = 0, perPage = PER_PAGE_DEFAULT) => {
|
||||
this.analytics?.trackAPI('api_posts_get_flagged', {team_id: teamId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userId)}/posts/flagged${buildQueryString({channel_id: channelId, team_id: teamId, page, per_page: perPage})}`,
|
||||
{method: 'get'},
|
||||
|
|
@ -148,7 +130,6 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
getPinnedPosts = async (channelId: string) => {
|
||||
this.analytics?.trackAPI('api_posts_get_pinned', {channel_id: channelId});
|
||||
return this.doFetch(
|
||||
`${this.getChannelRoute(channelId)}/pinned`,
|
||||
{method: 'get'},
|
||||
|
|
@ -156,8 +137,6 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
markPostAsUnread = async (userId: string, postId: string) => {
|
||||
this.analytics?.trackAPI('api_post_set_unread_post');
|
||||
|
||||
// collapsed_threads_supported is not based on user preferences but to know if "CLIENT" supports CRT
|
||||
const body = JSON.stringify({collapsed_threads_supported: true});
|
||||
|
||||
|
|
@ -168,8 +147,6 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
pinPost = async (postId: string) => {
|
||||
this.analytics?.trackAPI('api_posts_pin');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(postId)}/pin`,
|
||||
{method: 'post'},
|
||||
|
|
@ -177,8 +154,6 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
unpinPost = async (postId: string) => {
|
||||
this.analytics?.trackAPI('api_posts_unpin');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getPostRoute(postId)}/unpin`,
|
||||
{method: 'post'},
|
||||
|
|
@ -186,8 +161,6 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
addReaction = async (userId: string, postId: string, emojiName: string) => {
|
||||
this.analytics?.trackAPI('api_reactions_save', {post_id: postId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getReactionsRoute()}`,
|
||||
{method: 'post', body: {user_id: userId, post_id: postId, emoji_name: emojiName}},
|
||||
|
|
@ -195,8 +168,6 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
removeReaction = async (userId: string, postId: string, emojiName: string) => {
|
||||
this.analytics?.trackAPI('api_reactions_delete', {post_id: postId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userId)}/posts/${postId}/reactions/${emojiName}`,
|
||||
{method: 'delete'},
|
||||
|
|
@ -211,7 +182,6 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
searchPostsWithParams = async (teamId: string, params: PostSearchParams) => {
|
||||
this.analytics?.trackAPI('api_posts_search');
|
||||
const endpoint = teamId ? `${this.getTeamRoute(teamId)}/posts/search` : `${this.getPostsRoute()}/search`;
|
||||
return this.doFetch(endpoint, {method: 'post', body: params});
|
||||
};
|
||||
|
|
@ -225,12 +195,6 @@ const ClientPosts = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
doPostActionWithCookie = async (postId: string, actionId: string, actionCookie: string, selectedOption = '') => {
|
||||
if (selectedOption) {
|
||||
this.analytics?.trackAPI('api_interactive_messages_menu_selected');
|
||||
} else {
|
||||
this.analytics?.trackAPI('api_interactive_messages_button_clicked');
|
||||
}
|
||||
|
||||
const msg: any = {
|
||||
selected_option: selectedOption,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ export interface ClientPreferencesMix {
|
|||
|
||||
const ClientPreferences = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
savePreferences = async (userId: string, preferences: PreferenceType[]) => {
|
||||
this.analytics?.trackAPI('action_posts_flag');
|
||||
return this.doFetch(
|
||||
`${this.getPreferencesRoute(userId)}`,
|
||||
{method: 'put', body: preferences},
|
||||
|
|
@ -26,7 +25,6 @@ const ClientPreferences = <TBase extends Constructor<ClientBase>>(superclass: TB
|
|||
};
|
||||
|
||||
deletePreferences = async (userId: string, preferences: PreferenceType[]) => {
|
||||
this.analytics?.trackAPI('action_posts_unflag');
|
||||
return this.doFetch(
|
||||
`${this.getPreferencesRoute(userId)}/delete`,
|
||||
{method: 'post', body: preferences},
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@ export interface ClientTeamsMix {
|
|||
|
||||
const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
createTeam = async (team: Team) => {
|
||||
this.analytics?.trackAPI('api_teams_create');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamsRoute()}`,
|
||||
{method: 'post', body: team},
|
||||
|
|
@ -41,8 +39,6 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
deleteTeam = async (teamId: string) => {
|
||||
this.analytics?.trackAPI('api_teams_delete');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamRoute(teamId)}`,
|
||||
{method: 'delete'},
|
||||
|
|
@ -50,8 +46,6 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
updateTeam = async (team: Team) => {
|
||||
this.analytics?.trackAPI('api_teams_update_name', {team_id: team.id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamRoute(team.id)}`,
|
||||
{method: 'put', body: team},
|
||||
|
|
@ -59,8 +53,6 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
patchTeam = async (team: Partial<Team> & {id: string}) => {
|
||||
this.analytics?.trackAPI('api_teams_patch_name', {team_id: team.id});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamRoute(team.id)}/patch`,
|
||||
{method: 'put', body: team},
|
||||
|
|
@ -82,8 +74,6 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
getTeamByName = async (teamName: string) => {
|
||||
this.analytics?.trackAPI('api_teams_get_team_by_name');
|
||||
|
||||
return this.doFetch(
|
||||
this.getTeamNameRoute(teamName),
|
||||
{method: 'get'},
|
||||
|
|
@ -133,8 +123,6 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
addToTeam = async (teamId: string, userId: string) => {
|
||||
this.analytics?.trackAPI('api_teams_invite_members', {team_id: teamId});
|
||||
|
||||
const member = {user_id: userId, team_id: teamId};
|
||||
return this.doFetch(
|
||||
`${this.getTeamMembersRoute(teamId)}`,
|
||||
|
|
@ -143,8 +131,6 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
addUsersToTeamGracefully = (teamId: string, userIds: string[]) => {
|
||||
this.analytics?.trackAPI('api_teams_batch_add_members', {team_id: teamId, count: userIds.length});
|
||||
|
||||
const members: Array<{team_id: string; user_id: string}> = [];
|
||||
userIds.forEach((id) => members.push({team_id: teamId, user_id: id}));
|
||||
|
||||
|
|
@ -155,8 +141,6 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
sendEmailInvitesToTeamGracefully = (teamId: string, emails: string[]) => {
|
||||
this.analytics?.trackAPI('api_teams_invite_members', {team_id: teamId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamRoute(teamId)}/invite/email?graceful=true`,
|
||||
{method: 'post', body: emails},
|
||||
|
|
@ -172,8 +156,6 @@ const ClientTeams = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
removeFromTeam = async (teamId: string, userId: string) => {
|
||||
this.analytics?.trackAPI('api_teams_remove_members', {team_id: teamId});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getTeamMemberRoute(teamId, userId)}`,
|
||||
{method: 'delete'},
|
||||
|
|
|
|||
|
|
@ -50,8 +50,6 @@ export interface ClientUsersMix {
|
|||
|
||||
const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) => class extends superclass {
|
||||
createUser = async (user: UserProfile, token: string, inviteId: string) => {
|
||||
this.analytics?.trackAPI('api_users_create');
|
||||
|
||||
const queryParams: any = {};
|
||||
|
||||
if (token) {
|
||||
|
|
@ -76,8 +74,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
patchUser = async (userPatch: Partial<UserProfile> & {id: string}) => {
|
||||
this.analytics?.trackAPI('api_users_patch');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userPatch.id)}/patch`,
|
||||
{method: 'put', body: userPatch},
|
||||
|
|
@ -85,8 +81,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
updateUser = async (user: UserProfile) => {
|
||||
this.analytics?.trackAPI('api_users_update');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(user.id)}`,
|
||||
{method: 'put', body: user},
|
||||
|
|
@ -94,8 +88,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
demoteUserToGuest = async (userId: string) => {
|
||||
this.analytics?.trackAPI('api_users_demote_user_to_guest');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userId)}/demote`,
|
||||
{method: 'post'},
|
||||
|
|
@ -103,8 +95,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
getKnownUsers = async () => {
|
||||
this.analytics?.trackAPI('api_get_known_users');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/known`,
|
||||
{method: 'get'},
|
||||
|
|
@ -112,8 +102,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
sendPasswordResetEmail = async (email: string) => {
|
||||
this.analytics?.trackAPI('api_users_send_password_reset');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/password/reset/send`,
|
||||
{method: 'post', body: {email}},
|
||||
|
|
@ -121,8 +109,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
setDefaultProfileImage = async (userId: string) => {
|
||||
this.analytics?.trackAPI('api_users_set_default_profile_picture');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUserRoute(userId)}/image`,
|
||||
{method: 'delete'},
|
||||
|
|
@ -130,12 +116,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
login = async (loginId: string, password: string, token = '', deviceId = '', ldapOnly = false) => {
|
||||
this.analytics?.trackAPI('api_users_login');
|
||||
|
||||
if (ldapOnly) {
|
||||
this.analytics?.trackAPI('api_users_login_ldap');
|
||||
}
|
||||
|
||||
const body: any = {
|
||||
device_id: deviceId,
|
||||
login_id: loginId,
|
||||
|
|
@ -161,7 +141,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
loginById = async (id: string, password: string, token = '', deviceId = '') => {
|
||||
this.analytics?.trackAPI('api_users_login');
|
||||
const body: any = {
|
||||
device_id: deviceId,
|
||||
id,
|
||||
|
|
@ -183,8 +162,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
logout = async () => {
|
||||
this.analytics?.trackAPI('api_users_logout');
|
||||
|
||||
const response = await this.doFetch(
|
||||
`${this.getUsersRoute()}/logout`,
|
||||
{method: 'post'},
|
||||
|
|
@ -194,8 +171,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
getProfiles = async (page = 0, perPage = PER_PAGE_DEFAULT, options = {}) => {
|
||||
this.analytics?.trackAPI('api_profiles_get');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}${buildQueryString({page, per_page: perPage, ...options})}`,
|
||||
{method: 'get'},
|
||||
|
|
@ -203,8 +178,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
getProfilesByIds = async (userIds: string[], options = {}) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_by_ids');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/ids${buildQueryString(options)}`,
|
||||
{method: 'post', body: userIds},
|
||||
|
|
@ -212,8 +185,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
getProfilesByUsernames = async (usernames: string[]) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_by_usernames');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/usernames`,
|
||||
{method: 'post', body: usernames},
|
||||
|
|
@ -221,8 +192,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
getProfilesInTeam = async (teamId: string, page = 0, perPage = PER_PAGE_DEFAULT, sort = '', options = {}) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_in_team', {team_id: teamId, sort});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}${buildQueryString({...options, in_team: teamId, page, per_page: perPage, sort})}`,
|
||||
{method: 'get'},
|
||||
|
|
@ -230,8 +199,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
getProfilesNotInTeam = async (teamId: string, groupConstrained: boolean, page = 0, perPage = PER_PAGE_DEFAULT) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_not_in_team', {team_id: teamId, group_constrained: groupConstrained});
|
||||
|
||||
const queryStringObj: any = {not_in_team: teamId, page, per_page: perPage};
|
||||
if (groupConstrained) {
|
||||
queryStringObj.group_constrained = true;
|
||||
|
|
@ -244,8 +211,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
getProfilesWithoutTeam = async (page = 0, perPage = PER_PAGE_DEFAULT, options = {}) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_without_team');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}${buildQueryString({...options, without_team: 1, page, per_page: perPage})}`,
|
||||
{method: 'get'},
|
||||
|
|
@ -253,8 +218,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
getProfilesInChannel = async (channelId: string, options: GetUsersOptions) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_in_channel', {channel_id: channelId});
|
||||
|
||||
const queryStringObj = {in_channel: channelId, ...options};
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}${buildQueryString(queryStringObj)}`,
|
||||
|
|
@ -263,8 +226,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
getProfilesInGroupChannels = async (channelsIds: string[]) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_in_group_channels', {channelsIds});
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/group_channels`,
|
||||
{method: 'post', body: channelsIds},
|
||||
|
|
@ -272,8 +233,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
getProfilesNotInChannel = async (teamId: string, channelId: string, groupConstrained: boolean, page = 0, perPage = PER_PAGE_DEFAULT) => {
|
||||
this.analytics?.trackAPI('api_profiles_get_not_in_channel', {team_id: teamId, channel_id: channelId, group_constrained: groupConstrained});
|
||||
|
||||
const queryStringObj: any = {in_team: teamId, not_in_channel: channelId, page, per_page: perPage};
|
||||
if (groupConstrained) {
|
||||
queryStringObj.group_constrained = true;
|
||||
|
|
@ -374,8 +333,6 @@ const ClientUsers = <TBase extends Constructor<ClientBase>>(superclass: TBase) =
|
|||
};
|
||||
|
||||
searchUsers = async (term: string, options: any) => {
|
||||
this.analytics?.trackAPI('api_search_users');
|
||||
|
||||
return this.doFetch(
|
||||
`${this.getUsersRoute()}/search`,
|
||||
{method: 'post', body: {term, ...options}},
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {BottomSheetScrollView} from '@gorhom/bottom-sheet';
|
||||
import {Button} from '@rneui/base';
|
||||
import React, {useCallback, useMemo} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {ScrollView, Text, View} from 'react-native';
|
||||
import Button from 'react-native-button';
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
||||
|
||||
import {dismissAnnouncement} from '@actions/local/systems';
|
||||
|
|
@ -110,7 +110,7 @@ const ExpandedAnnouncementBanner = ({
|
|||
/>
|
||||
</Scroll>
|
||||
<Button
|
||||
containerStyle={buttonStyles.okay.button}
|
||||
buttonStyle={buttonStyles.okay.button}
|
||||
onPress={close}
|
||||
>
|
||||
<FormattedText
|
||||
|
|
@ -121,7 +121,7 @@ const ExpandedAnnouncementBanner = ({
|
|||
</Button>
|
||||
{allowDismissal && (
|
||||
<Button
|
||||
containerStyle={buttonStyles.dismiss.button}
|
||||
buttonStyle={buttonStyles.dismiss.button}
|
||||
onPress={dismissBanner}
|
||||
>
|
||||
<FormattedText
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {nativeApplicationVersion, nativeBuildVersion} from 'expo-application';
|
||||
import React, {useEffect} from 'react';
|
||||
import {Keyboard, StyleSheet, type TextStyle, View} from 'react-native';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
|
||||
|
||||
import FormattedText from '@components/formatted_text';
|
||||
|
|
@ -54,8 +54,8 @@ const AppVersion = ({isWrapped = true, textStyle = {}}: AppVersionProps) => {
|
|||
defaultMessage='App Version: {version} (Build {number})'
|
||||
style={StyleSheet.flatten([style.version, textStyle])}
|
||||
values={{
|
||||
version: DeviceInfo.getVersion(),
|
||||
number: DeviceInfo.getBuildNumber(),
|
||||
version: nativeApplicationVersion,
|
||||
number: nativeBuildVersion,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import AtMentionItem from '@components/autocomplete/at_mention_item';
|
|||
import ChannelItem from '@components/channel_item';
|
||||
import {COMMAND_SUGGESTION_CHANNEL, COMMAND_SUGGESTION_USER} from '@constants/apps';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import analytics from '@managers/analytics';
|
||||
|
||||
import {AppCommandParser, type ExtendedAutocompleteSuggestion} from '../app_command_parser/app_command_parser';
|
||||
import SlashSuggestionItem from '../slash_suggestion_item';
|
||||
|
|
@ -78,8 +77,6 @@ const AppSlashSuggestion = ({
|
|||
};
|
||||
|
||||
const completeSuggestion = useCallback((command: string) => {
|
||||
analytics.get(serverUrl)?.trackCommand('complete_suggestion', `/${command} `);
|
||||
|
||||
// We are going to set a double / on iOS to prevent the auto correct from taking over and replacing it
|
||||
// with the wrong value, this is a hack but I could not found another way to solve it
|
||||
let completedDraft = `/${command} `;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import {
|
|||
|
||||
import {fetchSuggestions} from '@actions/remote/command';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import analytics from '@managers/analytics';
|
||||
import IntegrationsManager from '@managers/integrations_manager';
|
||||
|
||||
import {AppCommandParser} from './app_command_parser/app_command_parser';
|
||||
|
|
@ -138,8 +137,6 @@ const SlashSuggestion = ({
|
|||
};
|
||||
|
||||
const completeSuggestion = useCallback((command: string) => {
|
||||
analytics.get(serverUrl)?.trackCommand('complete_suggestion', `/${command} `);
|
||||
|
||||
// We are going to set a double / on iOS to prevent the auto correct from taking over and replacing it
|
||||
// with the wrong value, this is a hack but I could not found another way to solve it
|
||||
let completedDraft = `/${command} `;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import base64 from 'base-64';
|
||||
import {Image} from 'expo-image';
|
||||
import React, {useCallback, useMemo} from 'react';
|
||||
import {Image, Text, View} from 'react-native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import {Text, View} from 'react-native';
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
||||
import {SvgXml} from 'react-native-svg';
|
||||
|
||||
|
|
@ -120,7 +120,7 @@ const SlashSuggestionItem = ({
|
|||
);
|
||||
} else if (icon.startsWith('http')) {
|
||||
image = (
|
||||
<FastImage
|
||||
<Image
|
||||
source={iconAsSource}
|
||||
style={style.uriIcon}
|
||||
/>
|
||||
|
|
@ -142,7 +142,7 @@ const SlashSuggestionItem = ({
|
|||
}
|
||||
} else {
|
||||
image = (
|
||||
<FastImage
|
||||
<Image
|
||||
source={iconAsSource}
|
||||
style={style.uriIcon}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Button as ElementButton} from '@rneui/base';
|
||||
import React, {useMemo, type ReactNode} from 'react';
|
||||
import {type StyleProp, StyleSheet, Text, type TextStyle, View, type ViewStyle} from 'react-native';
|
||||
import RNButton from 'react-native-button';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles';
|
||||
|
|
@ -79,8 +79,8 @@ const Button = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<RNButton
|
||||
containerStyle={bgStyle}
|
||||
<ElementButton
|
||||
buttonStyle={bgStyle}
|
||||
onPress={onPress}
|
||||
testID={testID}
|
||||
>
|
||||
|
|
@ -93,7 +93,7 @@ const Button = ({
|
|||
{text}
|
||||
</Text>
|
||||
</View>
|
||||
</RNButton>
|
||||
</ElementButton>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,14 +2,13 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {withDatabase, withObservables} from '@nozbe/watermelondb/react';
|
||||
import {Image} from 'expo-image';
|
||||
import React from 'react';
|
||||
import {
|
||||
Image,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
Text,
|
||||
} from 'react-native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import {of as of$} from 'rxjs';
|
||||
import {switchMap} from 'rxjs/operators';
|
||||
|
||||
|
|
@ -127,11 +126,11 @@ const Emoji = (props: EmojiProps) => {
|
|||
const key = Platform.OS === 'android' ? (`${imageUrl}-${height}-${width}`) : null;
|
||||
|
||||
return (
|
||||
<FastImage
|
||||
<Image
|
||||
key={key}
|
||||
style={[commonStyle, imageStyle, {width, height}]}
|
||||
source={{uri: imageUrl}}
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
contentFit='contain'
|
||||
testID={testID}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {deleteAsync} from 'expo-file-system';
|
||||
import React, {forwardRef, useImperativeHandle, useRef, useState} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {Platform, StatusBar, type StatusBarStyle, StyleSheet, TouchableOpacity, View} from 'react-native';
|
||||
import FileViewer from 'react-native-file-viewer';
|
||||
import FileSystem from 'react-native-fs';
|
||||
import tinyColor from 'tinycolor2';
|
||||
|
||||
import ProgressBar from '@components/progress_bar';
|
||||
|
|
@ -16,7 +16,6 @@ import NetworkManager from '@managers/network_manager';
|
|||
import {alertDownloadDocumentDisabled, alertDownloadFailed, alertFailedToOpenDocument} from '@utils/document';
|
||||
import {getFullErrorMessage, isErrorWithMessage} from '@utils/errors';
|
||||
import {fileExists, getLocalFilePathFromFile} from '@utils/file';
|
||||
import {emptyFunction} from '@utils/general';
|
||||
import {logDebug} from '@utils/log';
|
||||
|
||||
import FileIcon from './file_icon';
|
||||
|
|
@ -87,7 +86,7 @@ const DocumentFile = forwardRef<DocumentFileRef, DocumentFileProps>(({background
|
|||
}
|
||||
} catch (error) {
|
||||
if (path) {
|
||||
FileSystem.unlink(path).catch(emptyFunction);
|
||||
deleteAsync(path, {idempotent: true});
|
||||
}
|
||||
setDownloading(false);
|
||||
setProgress(0);
|
||||
|
|
@ -141,7 +140,7 @@ const DocumentFile = forwardRef<DocumentFileRef, DocumentFileProps>(({background
|
|||
onDonePreviewingFile();
|
||||
|
||||
if (path) {
|
||||
FileSystem.unlink(path).catch(emptyFunction);
|
||||
deleteAsync(path, {idempotent: true});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ const File = ({
|
|||
forwardRef={ref}
|
||||
inViewPort={inViewPort}
|
||||
isSingleImage={isSingleImage}
|
||||
resizeMode={'cover'}
|
||||
contentFit={'cover'}
|
||||
wrapperWidth={wrapperWidth}
|
||||
updateFileForGallery={updateFileForGallery}
|
||||
index={index}
|
||||
|
|
@ -158,7 +158,7 @@ const File = ({
|
|||
forwardRef={ref}
|
||||
inViewPort={inViewPort}
|
||||
isSingleImage={isSingleImage}
|
||||
resizeMode={'cover'}
|
||||
contentFit={'cover'}
|
||||
wrapperWidth={wrapperWidth}
|
||||
/>
|
||||
{Boolean(nonVisibleImagesCount) &&
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {LinearGradient} from 'expo-linear-gradient';
|
||||
import React, {useCallback, useMemo, useState} from 'react';
|
||||
import {StyleSheet, useWindowDimensions, View} from 'react-native';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
|
||||
import {buildFilePreviewUrl, buildFileThumbnailUrl} from '@actions/remote/file';
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
|
|
@ -16,7 +16,7 @@ import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
|||
|
||||
import FileIcon from './file_icon';
|
||||
|
||||
import type {ResizeMode} from 'react-native-fast-image';
|
||||
import type {ImageContentFit} from 'expo-image';
|
||||
|
||||
type ImageFileProps = {
|
||||
backgroundColor?: string;
|
||||
|
|
@ -24,7 +24,7 @@ type ImageFileProps = {
|
|||
forwardRef?: React.RefObject<unknown>;
|
||||
inViewPort?: boolean;
|
||||
isSingleImage?: boolean;
|
||||
resizeMode?: ResizeMode;
|
||||
contentFit?: ImageContentFit;
|
||||
wrapperWidth?: number;
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
|||
|
||||
const ImageFile = ({
|
||||
backgroundColor, file, forwardRef, inViewPort, isSingleImage,
|
||||
resizeMode = 'cover', wrapperWidth,
|
||||
contentFit = 'cover', wrapperWidth,
|
||||
}: ImageFileProps) => {
|
||||
const dimensions = useWindowDimensions();
|
||||
const theme = useTheme();
|
||||
|
|
@ -120,7 +120,7 @@ const ImageFile = ({
|
|||
style={[isSingleImage ? null : style.imagePreview, imageDimensions]}
|
||||
tintDefaultSource={!file.localPath && !failed}
|
||||
onError={handleError}
|
||||
resizeMode={resizeMode}
|
||||
contentFit={contentFit}
|
||||
{...imageProps()}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {getThumbnailAsync} from 'expo-video-thumbnails';
|
||||
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
|
||||
import {StyleSheet, useWindowDimensions, View, NativeModules} from 'react-native';
|
||||
import {StyleSheet, useWindowDimensions, View} from 'react-native';
|
||||
|
||||
import {updateLocalFile} from '@actions/local/file';
|
||||
import {buildFilePreviewUrl, buildFileUrl} from '@actions/remote/file';
|
||||
|
|
@ -10,14 +11,14 @@ import CompassIcon from '@components/compass_icon';
|
|||
import ProgressiveImage from '@components/progressive_image';
|
||||
import {useServerUrl} from '@context/server';
|
||||
import {useTheme} from '@context/theme';
|
||||
import {getServerCredentials} from '@init/credentials';
|
||||
import {fileExists} from '@utils/file';
|
||||
import {calculateDimensions} from '@utils/images';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import FileIcon from './file_icon';
|
||||
|
||||
import type {ResizeMode} from 'react-native-fast-image';
|
||||
const {createThumbnail} = NativeModules.MattermostManaged;
|
||||
import type {ImageContentFit} from 'expo-image';
|
||||
|
||||
type Props = {
|
||||
index: number;
|
||||
|
|
@ -25,7 +26,7 @@ type Props = {
|
|||
forwardRef?: React.RefObject<unknown>;
|
||||
inViewPort?: boolean;
|
||||
isSingleImage?: boolean;
|
||||
resizeMode?: ResizeMode;
|
||||
contentFit?: ImageContentFit;
|
||||
wrapperWidth: number;
|
||||
updateFileForGallery?: (idx: number, file: FileInfo) => void;
|
||||
}
|
||||
|
|
@ -61,7 +62,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
|||
|
||||
const VideoFile = ({
|
||||
index, file, forwardRef, inViewPort, isSingleImage,
|
||||
resizeMode = 'cover', wrapperWidth, updateFileForGallery,
|
||||
contentFit = 'cover', wrapperWidth, updateFileForGallery,
|
||||
}: Props) => {
|
||||
const serverUrl = useServerUrl();
|
||||
const [failed, setFailed] = useState(false);
|
||||
|
|
@ -86,7 +87,12 @@ const VideoFile = ({
|
|||
if (!data.mini_preview || !exists) {
|
||||
const videoUrl = buildFileUrl(serverUrl, data.id!);
|
||||
if (videoUrl) {
|
||||
const {path: uri, height, width} = await createThumbnail({url: data.localPath || videoUrl, timeStamp: 2000});
|
||||
const cred = await getServerCredentials(serverUrl);
|
||||
const headers: Record<string, string> = {};
|
||||
if (cred?.token) {
|
||||
headers.Authorization = `Bearer ${cred.token}`;
|
||||
}
|
||||
const {uri, height, width} = await getThumbnailAsync(data.localPath || videoUrl, {time: 1000, headers});
|
||||
data.mini_preview = uri;
|
||||
data.height = height;
|
||||
data.width = width;
|
||||
|
|
@ -151,7 +157,7 @@ const VideoFile = ({
|
|||
forwardRef={forwardRef}
|
||||
style={[isSingleImage ? null : style.imagePreview, imageDimensions]}
|
||||
onError={handleError}
|
||||
resizeMode={resizeMode}
|
||||
contentFit={contentFit}
|
||||
{...imageProps()}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ const Icon = ({fileInfo}: Props) => {
|
|||
<ImageFile
|
||||
file={fileInfo}
|
||||
inViewPort={true}
|
||||
resizeMode={'cover'}
|
||||
contentFit={'cover'}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
@ -37,7 +37,7 @@ const Icon = ({fileInfo}: Props) => {
|
|||
<View style={styles.imageVideo}>
|
||||
<VideoFile
|
||||
file={fileInfo}
|
||||
resizeMode={'cover'}
|
||||
contentFit={'cover'}
|
||||
inViewPort={true}
|
||||
index={0}
|
||||
wrapperWidth={78}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Overlay} from '@rneui/base';
|
||||
import React, {useCallback, useMemo} from 'react';
|
||||
import {Overlay} from 'react-native-elements';
|
||||
|
||||
import {ITEM_HEIGHT} from '@components/option_item';
|
||||
import {useTheme} from '@context/theme';
|
||||
|
|
|
|||
|
|
@ -57,19 +57,13 @@ const FileResult = ({
|
|||
const [xyOffset, setXYoffset] = useState<XyOffset>(undefined);
|
||||
const {height} = Dimensions.get('window');
|
||||
|
||||
const fileRef = useCallback((element: View) => {
|
||||
if (showOptions) {
|
||||
elementsRef.current = element;
|
||||
elementsRef?.current?.measureInWindow((x, y) => {
|
||||
setOpenUp((y > height / 2));
|
||||
setXYoffset({x, y});
|
||||
});
|
||||
}
|
||||
}, [elementsRef, showOptions]);
|
||||
|
||||
const handleOptionsPress = useCallback((fInfo: FileInfo) => {
|
||||
setShowOptions(true);
|
||||
onOptionsPress(fInfo);
|
||||
elementsRef.current?.measureInWindow((x, y) => {
|
||||
setOpenUp((y > height / 2));
|
||||
setXYoffset({x, y});
|
||||
setShowOptions(true);
|
||||
onOptionsPress(fInfo);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleSetAction = useCallback((action: GalleryAction) => {
|
||||
|
|
@ -82,8 +76,9 @@ const FileResult = ({
|
|||
return (
|
||||
<>
|
||||
<View
|
||||
ref={fileRef}
|
||||
ref={elementsRef}
|
||||
style={styles.container}
|
||||
collapsable={false}
|
||||
>
|
||||
<File
|
||||
asCard={true}
|
||||
|
|
|
|||
|
|
@ -139,6 +139,14 @@ const FloatingTextInput = forwardRef<FloatingTextInputRef, FloatingTextInputProp
|
|||
|
||||
const positions = useMemo(() => getLabelPositions(styles.textInput, styles.label, styles.smallLabel), [styles]);
|
||||
const size = useMemo(() => [styles.textInput.fontSize, styles.smallLabel.fontSize], [styles]);
|
||||
const shouldShowError = (!focused && error);
|
||||
|
||||
let color = styles.label.color;
|
||||
if (shouldShowError) {
|
||||
color = theme.errorTextColor;
|
||||
} else if (focused) {
|
||||
color = theme.buttonBg;
|
||||
}
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
blur: () => inputRef.current?.blur(),
|
||||
|
|
@ -175,7 +183,6 @@ const FloatingTextInput = forwardRef<FloatingTextInputRef, FloatingTextInputProp
|
|||
return focused ? null : inputRef?.current?.focus();
|
||||
}, [focused]);
|
||||
|
||||
const shouldShowError = (!focused && error);
|
||||
const onPressAction = !isKeyboardInput && editable && onPress ? onPress : undefined;
|
||||
|
||||
const combinedContainerStyle = useMemo(() => {
|
||||
|
|
@ -225,13 +232,6 @@ const FloatingTextInput = forwardRef<FloatingTextInputRef, FloatingTextInputProp
|
|||
const toValue = positions[index];
|
||||
const toSize = size[index];
|
||||
|
||||
let color = styles.label.color;
|
||||
if (shouldShowError) {
|
||||
color = theme.errorTextColor;
|
||||
} else if (focused) {
|
||||
color = theme.buttonBg;
|
||||
}
|
||||
|
||||
return {
|
||||
top: withTiming(toValue, {duration: 100, easing: Easing.linear}),
|
||||
fontSize: withTiming(toSize, {duration: 100, easing: Easing.linear}),
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ const MarkdownImage = ({
|
|||
id={fileInfo.id!}
|
||||
imageUri={fileInfo.uri}
|
||||
onError={handleOnError}
|
||||
resizeMode='contain'
|
||||
contentFit='contain'
|
||||
style={{width, height}}
|
||||
/>
|
||||
</Animated.View>
|
||||
|
|
@ -262,7 +262,7 @@ const MarkdownImage = ({
|
|||
id={fileInfo.id!}
|
||||
imageUri={fileInfo.uri}
|
||||
onError={handleOnError}
|
||||
resizeMode='contain'
|
||||
contentFit='contain'
|
||||
style={{width, height}}
|
||||
/>
|
||||
</TouchableWithFeedback>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {LinearGradient} from 'expo-linear-gradient';
|
||||
import React, {PureComponent, type ReactNode} from 'react';
|
||||
import {injectIntl, type IntlShape} from 'react-intl';
|
||||
import {Dimensions, type EventSubscription, type LayoutChangeEvent, Platform, type ScaledSize, ScrollView, type StyleProp, TouchableOpacity, View, type ViewStyle} from 'react-native';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {CELL_MAX_WIDTH, CELL_MIN_WIDTH} from '@components/markdown/markdown_table_cell';
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ const MarkTableImage = ({disabled, imagesMetadata, location, postId, serverURL,
|
|||
imageUri={source}
|
||||
forwardRef={ref}
|
||||
onError={onLoadFailed}
|
||||
resizeMode='contain'
|
||||
contentFit='contain'
|
||||
style={{width, height}}
|
||||
/>
|
||||
</Animated.View>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Image} from 'expo-image';
|
||||
import React, {useCallback, useMemo, useState} from 'react';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {useTheme} from '@context/theme';
|
||||
|
|
@ -40,7 +40,7 @@ const OptionIcon = ({icon, iconColor, destructive}: OptionIconProps) => {
|
|||
|
||||
if (isValidUrl(icon) && !failedToLoadImage) {
|
||||
return (
|
||||
<FastImage
|
||||
<Image
|
||||
source={iconAsSource}
|
||||
style={styles.icon}
|
||||
onError={onErrorLoadingIcon}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Button} from '@rneui/base';
|
||||
import React, {useCallback} from 'react';
|
||||
import Button from 'react-native-button';
|
||||
import {type Edge, SafeAreaView} from 'react-native-safe-area-context';
|
||||
|
||||
import {switchToPenultimateChannel} from '@actions/remote/channel';
|
||||
|
|
@ -46,7 +46,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => ({
|
|||
height: 40,
|
||||
},
|
||||
closeButtonText: {
|
||||
marginTop: 7,
|
||||
marginTop: 1,
|
||||
color: 'white',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
|
|
@ -97,7 +97,7 @@ export default function Archived({
|
|||
location=''
|
||||
/>
|
||||
<Button
|
||||
containerStyle={style.closeButton}
|
||||
buttonStyle={style.closeButton}
|
||||
onPress={onCloseChannelPress}
|
||||
testID={`${testID}.close_channel.button`}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import KeyboardTrackingView, {type KeyboardTrackingViewRef} from '@mattermost/keyboard-tracker';
|
||||
import React, {type RefObject, useEffect, useState} from 'react';
|
||||
import {Platform} from 'react-native';
|
||||
import {KeyboardTrackingView, type KeyboardTrackingViewRef} from 'react-native-keyboard-tracking-view';
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
||||
|
||||
import Autocomplete from '@components/autocomplete';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {useHardwareKeyboardEvents} from '@mattermost/hardware-keyboard';
|
||||
import {useManagedConfig} from '@mattermost/react-native-emm';
|
||||
import PasteableTextInput, {type PastedFile, type PasteInputRef} from '@mattermost/react-native-paste-input';
|
||||
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
|
||||
|
|
@ -9,7 +10,6 @@ import {
|
|||
Alert, AppState, type AppStateStatus, DeviceEventEmitter, type EmitterSubscription, Keyboard,
|
||||
type NativeSyntheticEvent, Platform, type TextInputSelectionChangeEventData,
|
||||
} from 'react-native';
|
||||
import HWKeyboardEvent from 'react-native-hw-keyboard-event';
|
||||
|
||||
import {updateDraftMessage} from '@actions/local/draft';
|
||||
import {userTyping} from '@actions/websocket/users';
|
||||
|
|
@ -220,7 +220,7 @@ export default function PostInput({
|
|||
addFiles(await extractFileInfo(files));
|
||||
}, [addFiles, intl]);
|
||||
|
||||
const handleHardwareEnterPress = useCallback((keyEvent: {pressedKey: string}) => {
|
||||
const handleHardwareEnterPress = () => {
|
||||
const topScreen = NavigationStore.getVisibleScreen();
|
||||
let sourceScreen: AvailableScreens = Screens.CHANNEL;
|
||||
if (rootId) {
|
||||
|
|
@ -229,23 +229,29 @@ export default function PostInput({
|
|||
sourceScreen = Screens.HOME;
|
||||
}
|
||||
if (topScreen === sourceScreen) {
|
||||
switch (keyEvent.pressedKey) {
|
||||
case 'enter':
|
||||
sendMessage();
|
||||
break;
|
||||
case 'shift-enter': {
|
||||
let newValue: string;
|
||||
updateValue((v) => {
|
||||
newValue = v.substring(0, cursorPosition) + '\n' + v.substring(cursorPosition);
|
||||
return newValue;
|
||||
});
|
||||
updateCursorPosition((pos) => pos + 1);
|
||||
propagateValue(newValue!);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sendMessage();
|
||||
}
|
||||
}, [sendMessage, updateValue, cursorPosition, isTablet]);
|
||||
};
|
||||
|
||||
const handleHardwareShiftEnter = () => {
|
||||
const topScreen = NavigationStore.getVisibleScreen();
|
||||
let sourceScreen: AvailableScreens = Screens.CHANNEL;
|
||||
if (rootId) {
|
||||
sourceScreen = Screens.THREAD;
|
||||
} else if (isTablet) {
|
||||
sourceScreen = Screens.HOME;
|
||||
}
|
||||
|
||||
if (topScreen === sourceScreen) {
|
||||
let newValue: string;
|
||||
updateValue((v) => {
|
||||
newValue = v.substring(0, cursorPosition) + '\n' + v.substring(cursorPosition);
|
||||
return newValue;
|
||||
});
|
||||
updateCursorPosition((pos) => pos + 1);
|
||||
propagateValue(newValue!);
|
||||
}
|
||||
};
|
||||
|
||||
const onAppStateChange = useCallback((appState: AppStateStatus) => {
|
||||
if (appState !== 'active' && previousAppState.current === 'active') {
|
||||
|
|
@ -301,12 +307,10 @@ export default function PostInput({
|
|||
}
|
||||
}, [value]);
|
||||
|
||||
useEffect(() => {
|
||||
const listener = HWKeyboardEvent.onHWKeyPressed(handleHardwareEnterPress);
|
||||
return () => {
|
||||
listener.remove();
|
||||
};
|
||||
}, [handleHardwareEnterPress]);
|
||||
useHardwareKeyboardEvents({
|
||||
onEnterPressed: handleHardwareEnterPress,
|
||||
onShiftEnterPressed: handleHardwareShiftEnter,
|
||||
});
|
||||
|
||||
return (
|
||||
<PasteableTextInput
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ export default function UploadItem({
|
|||
<ImageFile
|
||||
file={file}
|
||||
forwardRef={ref}
|
||||
resizeMode='cover'
|
||||
contentFit='cover'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Image} from 'expo-image';
|
||||
import React, {type ReactNode} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {Keyboard, Platform, StyleSheet, TouchableOpacity, View} from 'react-native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
|
||||
import {buildAbsoluteUrl} from '@actions/remote/file';
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
|
|
@ -50,7 +50,7 @@ const Avatar = ({author, enablePostIconOverride, isAutoReponse, location, post}:
|
|||
if (overrideIconUrl) {
|
||||
const source = {uri: overrideIconUrl};
|
||||
iconComponent = (
|
||||
<FastImage
|
||||
<Image
|
||||
source={source}
|
||||
style={{
|
||||
height: pictureSize,
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {withDatabase, withObservables} from '@nozbe/watermelondb/react';
|
||||
import {Button} from '@rneui/base';
|
||||
import React, {useCallback, useRef} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import Button from 'react-native-button';
|
||||
import {map} from 'rxjs/operators';
|
||||
|
||||
import {handleBindingClick, postEphemeralCallResponseForPost} from '@actions/remote/apps';
|
||||
|
|
@ -36,6 +36,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
|||
const STATUS_COLORS = getStatusColors(theme);
|
||||
return {
|
||||
button: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
borderRadius: 4,
|
||||
borderColor: changeOpacity(STATUS_COLORS.default, 0.25),
|
||||
borderWidth: 2,
|
||||
|
|
@ -121,8 +122,8 @@ const ButtonBinding = ({currentTeamId, binding, post, teamID, theme}: Props) =>
|
|||
|
||||
return (
|
||||
<Button
|
||||
containerStyle={style.button}
|
||||
disabledContainerStyle={style.buttonDisabled}
|
||||
buttonStyle={style.button}
|
||||
disabledStyle={style.buttonDisabled}
|
||||
onPress={onPress}
|
||||
>
|
||||
<ButtonBindingText
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ const ImagePreview = ({expandedLink, isReplyPost, layoutWidth, link, location, m
|
|||
id={fileId}
|
||||
imageUri={imageUrl}
|
||||
onError={onError}
|
||||
resizeMode='contain'
|
||||
contentFit='contain'
|
||||
style={[style.image, {width: dimensions.width, height: dimensions.height}]}
|
||||
/>
|
||||
</Animated.View>
|
||||
|
|
|
|||
|
|
@ -9,34 +9,39 @@ exports[`AttachmentAuthor it matches snapshot when both name and icon are provid
|
|||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
<ViewManagerAdapter_ExpoImage
|
||||
contentFit="cover"
|
||||
contentPosition={
|
||||
{
|
||||
"left": "50%",
|
||||
"top": "50%",
|
||||
}
|
||||
}
|
||||
height={12}
|
||||
marginRight={3}
|
||||
nativeViewRef={"[React.ref]"}
|
||||
onError={[Function]}
|
||||
onLoad={[Function]}
|
||||
onLoadStart={[Function]}
|
||||
onProgress={[Function]}
|
||||
placeholder={[]}
|
||||
source={
|
||||
[
|
||||
{
|
||||
"overflow": "hidden",
|
||||
},
|
||||
{
|
||||
"height": 12,
|
||||
"marginRight": 3,
|
||||
"width": 12,
|
||||
"uri": "https://images.com/image.png",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<FastImageView
|
||||
defaultSource={null}
|
||||
resizeMode="cover"
|
||||
style={
|
||||
{
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
style={
|
||||
{
|
||||
"height": 12,
|
||||
"marginRight": 3,
|
||||
"width": 12,
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
transition={null}
|
||||
width={12}
|
||||
/>
|
||||
<Text
|
||||
onPress={[Function]}
|
||||
style={
|
||||
|
|
@ -65,34 +70,39 @@ exports[`AttachmentAuthor it matches snapshot when only icon is provided 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
<ViewManagerAdapter_ExpoImage
|
||||
contentFit="cover"
|
||||
contentPosition={
|
||||
{
|
||||
"left": "50%",
|
||||
"top": "50%",
|
||||
}
|
||||
}
|
||||
height={12}
|
||||
marginRight={3}
|
||||
nativeViewRef={"[React.ref]"}
|
||||
onError={[Function]}
|
||||
onLoad={[Function]}
|
||||
onLoadStart={[Function]}
|
||||
onProgress={[Function]}
|
||||
placeholder={[]}
|
||||
source={
|
||||
[
|
||||
{
|
||||
"overflow": "hidden",
|
||||
},
|
||||
{
|
||||
"height": 12,
|
||||
"marginRight": 3,
|
||||
"width": 12,
|
||||
"uri": "https://images.com/image.png",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<FastImageView
|
||||
defaultSource={null}
|
||||
resizeMode="cover"
|
||||
style={
|
||||
{
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
style={
|
||||
{
|
||||
"height": 12,
|
||||
"marginRight": 3,
|
||||
"width": 12,
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
transition={null}
|
||||
width={12}
|
||||
/>
|
||||
</View>
|
||||
`;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,35 +10,41 @@ exports[`AttachmentFooter it matches snapshot when both footer and footer_icon a
|
|||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
<ViewManagerAdapter_ExpoImage
|
||||
contentFit="cover"
|
||||
contentPosition={
|
||||
{
|
||||
"left": "50%",
|
||||
"top": "50%",
|
||||
}
|
||||
}
|
||||
height={12}
|
||||
marginRight={5}
|
||||
marginTop={1}
|
||||
nativeViewRef={"[React.ref]"}
|
||||
onError={[Function]}
|
||||
onLoad={[Function]}
|
||||
onLoadStart={[Function]}
|
||||
onProgress={[Function]}
|
||||
placeholder={[]}
|
||||
source={
|
||||
[
|
||||
{
|
||||
"overflow": "hidden",
|
||||
},
|
||||
{
|
||||
"height": 12,
|
||||
"marginRight": 5,
|
||||
"marginTop": 1,
|
||||
"width": 12,
|
||||
"uri": "https://images.com/image.png",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<FastImageView
|
||||
defaultSource={null}
|
||||
resizeMode="cover"
|
||||
style={
|
||||
{
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
style={
|
||||
{
|
||||
"height": 12,
|
||||
"marginRight": 5,
|
||||
"marginTop": 1,
|
||||
"width": 12,
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
transition={null}
|
||||
width={12}
|
||||
/>
|
||||
<Text
|
||||
ellipsizeMode="tail"
|
||||
numberOfLines={1}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Button} from '@rneui/base';
|
||||
import React, {useCallback, useRef} from 'react';
|
||||
import Button from 'react-native-button';
|
||||
|
||||
import {postActionWithCookie} from '@actions/remote/integrations';
|
||||
import {useServerUrl} from '@context/server';
|
||||
|
|
@ -26,6 +26,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
|||
const STATUS_COLORS = getStatusColors(theme);
|
||||
return {
|
||||
button: {
|
||||
backgroundColor: 'transparent',
|
||||
borderRadius: 4,
|
||||
borderColor: changeOpacity(STATUS_COLORS.default, 0.25),
|
||||
borderWidth: 2,
|
||||
|
|
@ -71,8 +72,8 @@ const ActionButton = ({buttonColor, cookie, disabled, id, name, postId, theme}:
|
|||
|
||||
return (
|
||||
<Button
|
||||
containerStyle={[style.button, customButtonStyle]}
|
||||
disabledContainerStyle={style.buttonDisabled}
|
||||
buttonStyle={[style.button, customButtonStyle]}
|
||||
disabledStyle={style.buttonDisabled}
|
||||
onPress={onPress}
|
||||
disabled={disabled}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Image} from 'expo-image';
|
||||
import React from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {Alert, Text, View} from 'react-native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {tryOpenURL} from '@utils/url';
|
||||
|
|
@ -61,7 +61,7 @@ const AttachmentAuthor = ({icon, link, name, theme}: Props) => {
|
|||
return (
|
||||
<View style={style.container}>
|
||||
{Boolean(icon) &&
|
||||
<FastImage
|
||||
<Image
|
||||
source={{uri: icon}}
|
||||
key='author_icon'
|
||||
style={style.icon}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Image} from 'expo-image';
|
||||
import React from 'react';
|
||||
import {Text, View, Platform} from 'react-native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ const AttachmentFooter = ({icon, text, theme}: Props) => {
|
|||
return (
|
||||
<View style={style.container}>
|
||||
{Boolean(icon) &&
|
||||
<FastImage
|
||||
<Image
|
||||
source={{uri: icon}}
|
||||
key='footer_icon'
|
||||
style={style.icon}
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ const AttachmentImage = ({imageUrl, imageMetadata, layoutWidth, location, postId
|
|||
imageStyle={style.attachmentMargin}
|
||||
imageUri={imageUrl}
|
||||
onError={onError}
|
||||
resizeMode='contain'
|
||||
contentFit='contain'
|
||||
style={{height, width}}
|
||||
/>
|
||||
</Animated.View>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Image} from 'expo-image';
|
||||
import React from 'react';
|
||||
import {StyleSheet, View} from 'react-native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
|
||||
type Props = {
|
||||
uri: string;
|
||||
|
|
@ -24,9 +24,9 @@ const style = StyleSheet.create({
|
|||
const AttachmentThumbnail = ({uri}: Props) => {
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<FastImage
|
||||
<Image
|
||||
source={{uri}}
|
||||
resizeMode='contain'
|
||||
contentFit='contain'
|
||||
style={style.image}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Image, type ImageSource} from 'expo-image';
|
||||
import React, {useMemo, useRef} from 'react';
|
||||
import {TouchableWithoutFeedback, useWindowDimensions} from 'react-native';
|
||||
import FastImage, {type Source} from 'react-native-fast-image';
|
||||
import Animated from 'react-native-reanimated';
|
||||
|
||||
import {View as ViewConstants} from '@constants';
|
||||
|
|
@ -106,7 +106,7 @@ const OpengraphImage = ({isReplyPost, layoutWidth, location, metadata, openGraph
|
|||
openGalleryAtIndex(galleryIdentifier, 0, [item]);
|
||||
};
|
||||
|
||||
const source: Source = {};
|
||||
const source: ImageSource = {};
|
||||
if (isValidUrl(imageUrl)) {
|
||||
source.uri = imageUrl;
|
||||
}
|
||||
|
|
@ -123,13 +123,11 @@ const OpengraphImage = ({isReplyPost, layoutWidth, location, metadata, openGraph
|
|||
<Animated.View style={[styles, style.imageContainer, dimensionsStyle]}>
|
||||
<TouchableWithoutFeedback onPress={onGestureEvent}>
|
||||
<Animated.View testID={`OpenGraphImage-${fileId}`}>
|
||||
<FastImage
|
||||
<Image
|
||||
style={[style.image, dimensionsStyle]}
|
||||
source={source}
|
||||
|
||||
// @ts-expect-error legacy ref
|
||||
contentFit='contain'
|
||||
ref={ref}
|
||||
resizeMode='contain'
|
||||
nativeID={`OpenGraphImage-${fileId}`}
|
||||
/>
|
||||
</Animated.View>
|
||||
|
|
|
|||
|
|
@ -1,19 +1,17 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {ImageBackground} from 'expo-image';
|
||||
import React, {useCallback} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {Alert, StyleSheet, TouchableOpacity, View} from 'react-native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import {Alert, StyleSheet, TouchableOpacity} from 'react-native';
|
||||
|
||||
import {useTheme} from '@context/theme';
|
||||
import {useIsTablet} from '@hooks/device';
|
||||
import {emptyFunction} from '@utils/general';
|
||||
import {calculateDimensions, getViewPortWidth} from '@utils/images';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {changeOpacity} from '@utils/theme';
|
||||
import {getYouTubeVideoId, tryOpenURL} from '@utils/url';
|
||||
|
||||
import YouTubeLogo from './youtube.svg';
|
||||
import YouTubeLogo from './youtube_logo';
|
||||
|
||||
type YouTubeProps = {
|
||||
isReplyPost: boolean;
|
||||
|
|
@ -24,7 +22,7 @@ type YouTubeProps = {
|
|||
const MAX_YOUTUBE_IMAGE_HEIGHT = 280;
|
||||
const MAX_YOUTUBE_IMAGE_WIDTH = 500;
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
const styles = StyleSheet.create({
|
||||
imageContainer: {
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'flex-start',
|
||||
|
|
@ -36,31 +34,20 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
|||
alignItems: 'center',
|
||||
borderRadius: 4,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
playButton: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
playContainer: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
backgroundColor: changeOpacity('#000', 0.24),
|
||||
borderColor: changeOpacity(theme.centerChannelColor, 0.08),
|
||||
borderRadius: 4,
|
||||
borderWidth: 1,
|
||||
},
|
||||
shadow: {
|
||||
elevation: 3,
|
||||
shadowColor: changeOpacity('#000', 0.08),
|
||||
shadowColor: changeOpacity('#000', 0.8),
|
||||
shadowOffset: {width: 0, height: 2},
|
||||
shadowOpacity: 1,
|
||||
shadowRadius: 3,
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
const YouTube = ({isReplyPost, layoutWidth, metadata}: YouTubeProps) => {
|
||||
const intl = useIntl();
|
||||
const isTablet = useIsTablet();
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
const link = metadata?.embeds![0].url;
|
||||
const videoId = getYouTubeVideoId(link);
|
||||
const dimensions = calculateDimensions(
|
||||
|
|
@ -109,17 +96,13 @@ const YouTube = ({isReplyPost, layoutWidth, metadata}: YouTubeProps) => {
|
|||
style={[styles.imageContainer, {height: dimensions.height, width: dimensions.width}]}
|
||||
onPress={playYouTubeVideo}
|
||||
>
|
||||
<FastImage
|
||||
onError={emptyFunction}
|
||||
resizeMode='cover'
|
||||
<ImageBackground
|
||||
contentFit='cover'
|
||||
style={[styles.image, dimensions]}
|
||||
source={{uri: imgUrl}}
|
||||
/>
|
||||
<View style={styles.playContainer}>
|
||||
<View style={styles.playButton}>
|
||||
<YouTubeLogo/>
|
||||
</View>
|
||||
</View>
|
||||
>
|
||||
<YouTubeLogo style={styles.shadow}/>
|
||||
</ImageBackground>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
<svg width="69" height="48" viewBox="0 0 69 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path opacity="0.9" d="M67.1402 7.49577C66.35 4.54374 64.0301 2.22389 61.0781 1.43368C55.7272 0 34.2841 0 34.2841 0C34.2841 0 12.841 0 7.49577 1.43368C4.54374 2.22389 2.22389 4.54374 1.43368 7.49577C0 12.841 0 24 0 24C0 24 0 35.159 1.43368 40.5042C2.22389 43.4563 4.54374 45.7761 7.49577 46.5663C12.841 48 34.2841 48 34.2841 48C34.2841 48 55.7272 48 61.0724 46.5663C64.0245 45.7761 66.3443 43.4563 67.1345 40.5042C68.5682 35.159 68.5682 24 68.5682 24C68.5682 24 68.5682 12.841 67.1345 7.49577H67.1402Z" fill="#FF0000"/>
|
||||
<path d="M27.4261 34.2841L45.2455 24L27.4261 13.7159V34.2841Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 701 B |
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import * as React from 'react';
|
||||
import Svg, {Path, type SvgProps} from 'react-native-svg';
|
||||
|
||||
const YouTubeLogo = (props: SvgProps) => (
|
||||
<Svg
|
||||
width={69}
|
||||
height={48}
|
||||
fill='none'
|
||||
{...props}
|
||||
>
|
||||
<Path
|
||||
fill='red'
|
||||
d='M67.14 7.496a8.587 8.587 0 0 0-6.062-6.062C55.728 0 34.284 0 34.284 0S12.841 0 7.496 1.434a8.587 8.587 0 0 0-6.062 6.062C0 12.84 0 24 0 24s0 11.159 1.434 16.504a8.587 8.587 0 0 0 6.062 6.062C12.84 48 34.284 48 34.284 48s21.443 0 26.788-1.434a8.586 8.586 0 0 0 6.063-6.062C68.568 35.16 68.568 24 68.568 24s0-11.159-1.433-16.504h.005Z'
|
||||
opacity={0.9}
|
||||
/>
|
||||
<Path
|
||||
fill='#fff'
|
||||
d='M27.426 34.284 45.246 24l-17.82-10.284v20.568Z'
|
||||
/>
|
||||
</Svg>
|
||||
);
|
||||
export default YouTubeLogo;
|
||||
|
|
@ -97,7 +97,7 @@ const Body = ({
|
|||
let message;
|
||||
|
||||
const isReplyPost = Boolean(post.rootId && (!isEphemeral || !hasBeenDeleted) && location !== THREAD);
|
||||
const hasContent = (post.metadata?.embeds?.length || (appsEnabled && post.props?.app_bindings?.length)) || post.props?.attachments?.length;
|
||||
const hasContent = Boolean((post.metadata?.embeds?.length || (appsEnabled && post.props?.app_bindings?.length)) || post.props?.attachments?.length);
|
||||
|
||||
const replyBarStyle = useCallback((): StyleProp<ViewStyle>|undefined => {
|
||||
if (!isReplyPost || (isCRTEnabled && location === Screens.PERMALINK)) {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {LinearGradient} from 'expo-linear-gradient';
|
||||
import React from 'react';
|
||||
import {TouchableOpacity, View} from 'react-native';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
|||
alignSelf: 'center',
|
||||
bottom: -70,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
shadow: {
|
||||
elevation: 4,
|
||||
shadowOpacity: 0.2,
|
||||
shadowOffset: {width: 0, height: 4},
|
||||
|
|
@ -110,7 +112,7 @@ const ScrollToEndView = ({
|
|||
<Animated.View style={[animatedStyle, styles.buttonStyle]}>
|
||||
<Pressable
|
||||
onPress={onPress}
|
||||
style={scrollButtonStyles}
|
||||
style={[scrollButtonStyles, styles.shadow]}
|
||||
>
|
||||
<CompassIcon
|
||||
size={18}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Image as ExpoImage, type ImageSource} from 'expo-image';
|
||||
import React, {useMemo} from 'react';
|
||||
import {Image as RNImage} from 'react-native';
|
||||
import FastImage, {type Source} from 'react-native-fast-image';
|
||||
import Animated from 'react-native-reanimated';
|
||||
|
||||
import {buildAbsoluteUrl} from '@actions/remote/file';
|
||||
|
|
@ -22,12 +21,11 @@ type Props = {
|
|||
forwardRef?: React.RefObject<any>;
|
||||
iconSize?: number;
|
||||
size: number;
|
||||
source?: Source | string;
|
||||
source?: ImageSource | string;
|
||||
url?: string;
|
||||
};
|
||||
|
||||
const AnimatedFastImage = Animated.createAnimatedComponent(FastImage);
|
||||
const AnimatedImage = Animated.createAnimatedComponent(RNImage);
|
||||
const AnimatedImage = Animated.createAnimatedComponent(ExpoImage);
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
||||
return {
|
||||
|
|
@ -83,10 +81,8 @@ const Image = ({author, forwardRef, iconSize, size, source, url}: Props) => {
|
|||
|
||||
if (imgSource) {
|
||||
return (
|
||||
<AnimatedFastImage
|
||||
<AnimatedImage
|
||||
key={imgSource.uri}
|
||||
|
||||
// @ts-expect-error TS expects old type ref
|
||||
ref={forwardRef}
|
||||
style={fIStyle}
|
||||
source={imgSource}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import Image from './image';
|
|||
import Status from './status';
|
||||
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {Source} from 'react-native-fast-image';
|
||||
import type {ImageSource} from 'expo-image';
|
||||
|
||||
type ProfilePictureProps = {
|
||||
author?: UserModel | UserProfile;
|
||||
|
|
@ -25,7 +25,7 @@ type ProfilePictureProps = {
|
|||
containerStyle?: StyleProp<ViewStyle>;
|
||||
statusStyle?: StyleProp<ViewStyle>;
|
||||
testID?: string;
|
||||
source?: Source | string;
|
||||
source?: ImageSource | string;
|
||||
url?: string;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,32 +1,25 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Image, ImageBackground, type ImageContentFit, type ImageStyle} from 'expo-image';
|
||||
import React, {type ReactNode, useEffect, useState} from 'react';
|
||||
import {ImageBackground, type StyleProp, StyleSheet, View, type ViewStyle, Image} from 'react-native';
|
||||
import FastImage, {type ResizeMode, type ImageStyle as FastImageStyle} from 'react-native-fast-image';
|
||||
import Animated, {interpolate, useAnimatedStyle, useDerivedValue, useSharedValue, withTiming} from 'react-native-reanimated';
|
||||
import {type StyleProp, StyleSheet, View, type ViewStyle} from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
|
||||
import {useTheme} from '@context/theme';
|
||||
import {emptyFunction} from '@utils/general';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import Thumbnail from './thumbnail';
|
||||
|
||||
import type {ImageStyles} from '@typings/global/styles';
|
||||
|
||||
const AnimatedImageBackground = Animated.createAnimatedComponent(ImageBackground);
|
||||
|
||||
const AnimatedFastImage = Animated.createAnimatedComponent(FastImage);
|
||||
const AnimatedImage = Animated.createAnimatedComponent(Image);
|
||||
|
||||
type Props = ProgressiveImageProps & {
|
||||
children?: ReactNode | ReactNode[];
|
||||
forwardRef?: React.RefObject<any>;
|
||||
id: string;
|
||||
imageStyle?: StyleProp<ImageStyles>;
|
||||
imageStyle?: StyleProp<ImageStyle>;
|
||||
isBackgroundImage?: boolean;
|
||||
onError: () => void;
|
||||
resizeMode?: ResizeMode;
|
||||
contentFit?: ImageContentFit;
|
||||
style?: StyleProp<ViewStyle>;
|
||||
tintDefaultSource?: boolean;
|
||||
};
|
||||
|
|
@ -47,36 +40,15 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
|||
|
||||
const ProgressiveImage = ({
|
||||
children, defaultSource, forwardRef, id, imageStyle, imageUri, inViewPort, isBackgroundImage,
|
||||
onError, resizeMode = 'contain', style = {}, thumbnailUri, tintDefaultSource,
|
||||
onError, contentFit = 'contain', style = {}, thumbnailUri, tintDefaultSource,
|
||||
}: Props) => {
|
||||
const [showHighResImage, setShowHighResImage] = useState(false);
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
const intensity = useSharedValue(0);
|
||||
|
||||
const defaultOpacity = useDerivedValue(() => (
|
||||
interpolate(
|
||||
intensity.value,
|
||||
[0, 100],
|
||||
[0.5, 0],
|
||||
)
|
||||
), []);
|
||||
|
||||
const onLoadImageEnd = () => {
|
||||
intensity.value = withTiming(100, {duration: 300});
|
||||
};
|
||||
|
||||
const animatedOpacity = useAnimatedStyle(() => ({
|
||||
opacity: interpolate(
|
||||
intensity.value,
|
||||
[200, 100],
|
||||
[0.2, 1],
|
||||
),
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (inViewPort) {
|
||||
setShowHighResImage(true);
|
||||
setShowHighResImage(inViewPort);
|
||||
}
|
||||
}, [inViewPort]);
|
||||
|
||||
|
|
@ -86,7 +58,7 @@ const ProgressiveImage = ({
|
|||
<AnimatedImageBackground
|
||||
key={id}
|
||||
source={{uri: imageUri}}
|
||||
resizeMode={'cover'}
|
||||
contentFit='cover'
|
||||
style={[StyleSheet.absoluteFill, imageStyle]}
|
||||
>
|
||||
{children}
|
||||
|
|
@ -106,72 +78,31 @@ const ProgressiveImage = ({
|
|||
imageStyle,
|
||||
tintDefaultSource ? styles.defaultImageTint : null,
|
||||
]}
|
||||
resizeMode={resizeMode}
|
||||
contentFit={contentFit}
|
||||
onError={onError}
|
||||
nativeID={`image-${id}`}
|
||||
recyclingKey={`image-${id}`}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const containerStyle = {backgroundColor: changeOpacity(theme.centerChannelColor, Number(defaultOpacity.value))};
|
||||
|
||||
let image;
|
||||
if (thumbnailUri) {
|
||||
if (showHighResImage && imageUri) {
|
||||
image = (
|
||||
<AnimatedFastImage
|
||||
|
||||
// @ts-expect-error ref not present in type
|
||||
ref={forwardRef}
|
||||
nativeID={`image-${id}`}
|
||||
resizeMode={resizeMode}
|
||||
onError={onError}
|
||||
source={{uri: imageUri}}
|
||||
style={[
|
||||
StyleSheet.absoluteFill,
|
||||
imageStyle,
|
||||
animatedOpacity,
|
||||
]}
|
||||
testID='progressive_image.highResImage'
|
||||
onLoadEnd={onLoadImageEnd}
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else if (imageUri) {
|
||||
image = (
|
||||
<AnimatedFastImage
|
||||
|
||||
// @ts-expect-error ref not present in type
|
||||
ref={forwardRef}
|
||||
nativeID={`image-${id}`}
|
||||
resizeMode={resizeMode}
|
||||
onError={onError}
|
||||
source={{uri: imageUri}}
|
||||
style={[StyleSheet.absoluteFill, (imageStyle as StyleProp<FastImageStyle>), animatedOpacity]}
|
||||
onLoadEnd={onLoadImageEnd}
|
||||
testID='progressive_image.highResImage'
|
||||
/>
|
||||
);
|
||||
}
|
||||
const showImage = showHighResImage || !thumbnailUri;
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
style={[styles.defaultImageContainer, style, containerStyle]}
|
||||
>
|
||||
{Boolean(thumbnailUri) &&
|
||||
<Thumbnail
|
||||
onError={emptyFunction}
|
||||
opacity={defaultOpacity}
|
||||
source={{uri: thumbnailUri}}
|
||||
style={[
|
||||
thumbnailUri ? StyleSheet.absoluteFill : undefined,
|
||||
imageStyle,
|
||||
]}
|
||||
tintColor={thumbnailUri ? undefined : theme.centerChannelColor}
|
||||
<Animated.View style={[styles.defaultImageContainer, style]}>
|
||||
<Image
|
||||
ref={forwardRef}
|
||||
placeholder={{uri: thumbnailUri}}
|
||||
placeholderContentFit='cover'
|
||||
nativeID={`image-${id}`}
|
||||
recyclingKey={`image-${id}`}
|
||||
testID='progressive_image.highResImage'
|
||||
transition={300}
|
||||
style={[StyleSheet.absoluteFill, imageStyle]}
|
||||
source={(showImage) ? {uri: imageUri} : undefined}
|
||||
autoplay={false}
|
||||
/>
|
||||
}
|
||||
{image}
|
||||
</Animated.View>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {Image, type ColorValue, type StyleProp, type ImageStyle} from 'react-native';
|
||||
import FastImage, {type ImageStyle as FastImageStyle, type Source} from 'react-native-fast-image';
|
||||
import Animated, {type SharedValue} from 'react-native-reanimated';
|
||||
|
||||
const AnimatedFastImage = Animated.createAnimatedComponent(FastImage);
|
||||
const AnimatedImage = Animated.createAnimatedComponent(Image);
|
||||
|
||||
type ThumbnailProps = {
|
||||
onError: () => void;
|
||||
opacity?: SharedValue<number>;
|
||||
source?: Source;
|
||||
style: StyleProp<ImageStyle>;
|
||||
tintColor?: ColorValue;
|
||||
}
|
||||
|
||||
const Thumbnail = ({onError, opacity, style, source, tintColor}: ThumbnailProps) => {
|
||||
if (source?.uri) {
|
||||
return (
|
||||
<AnimatedFastImage
|
||||
onError={onError}
|
||||
resizeMode='cover'
|
||||
source={source}
|
||||
style={(style as StyleProp<FastImageStyle>)}
|
||||
testID='progressive_image.miniPreview'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<AnimatedImage
|
||||
resizeMode='contain'
|
||||
onError={onError}
|
||||
source={require('@assets/images/thumb.png')}
|
||||
style={[style, {opacity: opacity?.value, tintColor}]}
|
||||
testID='progressive_image.thumbnail'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Thumbnail;
|
||||
|
|
@ -4,10 +4,10 @@
|
|||
/* eslint-disable react/prop-types */
|
||||
// We disable the prop types check here as forwardRef & typescript has a bug
|
||||
|
||||
import {SearchBar} from '@rneui/base';
|
||||
import React, {forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react';
|
||||
import {useIntl} from 'react-intl';
|
||||
import {type ActivityIndicatorProps, Keyboard, Platform, type StyleProp, TextInput, type TextInputProps, type TextStyle, type TouchableOpacityProps, type ViewStyle} from 'react-native';
|
||||
import {SearchBar} from 'react-native-elements';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {SEARCH_INPUT_HEIGHT} from '@constants/view';
|
||||
|
|
@ -42,12 +42,14 @@ export type SearchProps = TextInputProps & {
|
|||
showLoading?: boolean;
|
||||
};
|
||||
|
||||
type Selection = {start: number; end: number};
|
||||
|
||||
export type SearchRef = {
|
||||
blur: () => void;
|
||||
cancel: () => void;
|
||||
clear: () => void;
|
||||
focus: () => void;
|
||||
setNativeProps(nativeProps: object): void;
|
||||
setCaretPosition(selection: Selection): void;
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
|
||||
|
|
@ -152,8 +154,8 @@ const Search = forwardRef<SearchRef, SearchProps>((props: SearchProps, ref) => {
|
|||
focus: () => {
|
||||
searchRef.current?.focus();
|
||||
},
|
||||
setNativeProps: (nativeProps: object) => {
|
||||
searchRef.current?.setNativeProps(nativeProps);
|
||||
setCaretPosition: (nativeProps: Selection) => {
|
||||
searchRef.current?.setSelection(nativeProps.start, nativeProps.end);
|
||||
},
|
||||
}), [searchRef]);
|
||||
|
||||
|
|
@ -163,8 +165,6 @@ const Search = forwardRef<SearchRef, SearchProps>((props: SearchProps, ref) => {
|
|||
cancelButtonProps={props.cancelButtonProps || cancelButtonProps}
|
||||
cancelButtonTitle={props.cancelButtonTitle || intl.formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}
|
||||
cancelIcon={cancelIcon}
|
||||
|
||||
// @ts-expect-error clearIcon definition does not include a ReactElement
|
||||
clearIcon={clearIcon}
|
||||
containerStyle={[styles.containerStyle, props.containerStyle]}
|
||||
inputContainerStyle={[styles.inputContainerStyle, props.inputContainerStyle]}
|
||||
|
|
@ -172,15 +172,13 @@ const Search = forwardRef<SearchRef, SearchProps>((props: SearchProps, ref) => {
|
|||
returnKeyType='search'
|
||||
onCancel={onCancel}
|
||||
onClear={onClear}
|
||||
|
||||
// @ts-expect-error onChangeText type definition is wrong in elements
|
||||
onChangeText={onChangeText}
|
||||
placeholder={props.placeholder || intl.formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
|
||||
placeholderTextColor={props.placeholderTextColor || changeOpacity(theme.centerChannelColor, Platform.select({android: 0.56, default: 0.72}))}
|
||||
platform={Platform.select({android: 'android', default: 'ios'})}
|
||||
ref={searchRef}
|
||||
|
||||
// @ts-expect-error searchIcon definition does not include a ReactElement
|
||||
// @ts-expect-error legacy ref
|
||||
ref={searchRef}
|
||||
searchIcon={searchIcon}
|
||||
selectionColor={Platform.select({android: changeOpacity(props.selectionColor || theme.centerChannelColor, 0.24), default: props.selectionColor || theme.centerChannelColor})}
|
||||
testID={searchInputTestID}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Image, type ImageSource, type ImageStyle} from 'expo-image';
|
||||
import React, {useCallback} from 'react';
|
||||
import {type StyleProp, Text, type TextStyle, TouchableHighlight, View, type ViewStyle} from 'react-native';
|
||||
import FastImage, {type ImageStyle, type Source} from 'react-native-fast-image';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {useTheme} from '@context/theme';
|
||||
|
|
@ -14,10 +14,10 @@ import {isValidUrl} from '@utils/url';
|
|||
|
||||
type SlideUpPanelProps = {
|
||||
destructive?: boolean;
|
||||
leftIcon?: string | Source;
|
||||
leftIcon?: string | ImageSource;
|
||||
leftImageStyles?: StyleProp<ImageStyle>;
|
||||
leftIconStyles?: StyleProp<TextStyle>;
|
||||
rightIcon?: string | Source;
|
||||
rightIcon?: string | ImageSource;
|
||||
rightImageStyles?: StyleProp<ImageStyle>;
|
||||
rightIconStyles?: StyleProp<TextStyle>;
|
||||
onPress: () => void;
|
||||
|
|
@ -110,7 +110,7 @@ const SlideUpPanelItem = ({
|
|||
);
|
||||
};
|
||||
|
||||
const useImageAndStyle = (icon: string | Source | undefined, imageStyles: StyleProp<ImageStyle>, iconStyles: StyleProp<TextStyle>, destructive: boolean) => {
|
||||
const useImageAndStyle = (icon: string | ImageSource | undefined, imageStyles: StyleProp<ImageStyle>, iconStyles: StyleProp<TextStyle>, destructive: boolean) => {
|
||||
const theme = useTheme();
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
|
|
@ -122,7 +122,7 @@ const useImageAndStyle = (icon: string | Source | undefined, imageStyles: StyleP
|
|||
const imageStyle: StyleProp<ImageStyle> = [imageStyles];
|
||||
imageStyle.push({width: 24, height: 24});
|
||||
image = (
|
||||
<FastImage
|
||||
<Image
|
||||
source={icon}
|
||||
style={imageStyle}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Image} from 'expo-image';
|
||||
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
|
||||
import {View, Text, type StyleProp, type TextStyle} from 'react-native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
|
||||
import {buildAbsoluteUrl} from '@actions/remote/file';
|
||||
import {buildTeamIconUrl} from '@actions/remote/team';
|
||||
|
|
@ -124,7 +124,7 @@ export default function TeamIcon({
|
|||
);
|
||||
} else {
|
||||
teamIconContent = (
|
||||
<FastImage
|
||||
<Image
|
||||
style={styles.image}
|
||||
source={{uri: buildAbsoluteUrl(serverUrl, buildTeamIconUrl(serverUrl, id, lastIconUpdate))}}
|
||||
onError={handleImageError}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import React, {useMemo} from 'react';
|
||||
import {type StyleProp, Text, type TextStyle, useWindowDimensions, View, type ViewStyle} from 'react-native';
|
||||
import Animated, {type AnimatedStyleProp} from 'react-native-reanimated';
|
||||
import Animated from 'react-native-reanimated';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {useTheme} from '@context/theme';
|
||||
|
|
@ -12,7 +12,7 @@ import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
|
|||
import {typography} from '@utils/typography';
|
||||
|
||||
type ToastProps = {
|
||||
animatedStyle: AnimatedStyleProp<ViewStyle>;
|
||||
animatedStyle: StyleProp<ViewStyle>;
|
||||
children?: React.ReactNode;
|
||||
iconName?: string;
|
||||
message?: string;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {useTheme} from '@context/theme';
|
|||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
import {typography} from '@utils/typography';
|
||||
|
||||
import LongPressIllustration from './long_press_illustration.svg';
|
||||
import LongPressIllustration from './long_press_illustration';
|
||||
|
||||
type Props = {
|
||||
containerStyle?: StyleProp<ViewStyle>;
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
<svg width="69" height="79" viewBox="0 0 69 79" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="31" cy="22.5" r="8.5" fill="#3F4350" fill-opacity="0.24"/>
|
||||
<circle cx="31" cy="22" r="21" stroke="#3F4350" stroke-opacity="0.08" stroke-width="2"/>
|
||||
<circle cx="31" cy="22" r="14" stroke="#3F4350" stroke-opacity="0.16" stroke-width="2"/>
|
||||
<g clip-path="url(#clip0_510_17728)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.8817 59.3289C23.782 59.1198 23.6852 58.9036 23.5896 58.68C23.1622 57.7293 22.7481 56.7384 22.3549 55.7976L22.3548 55.7974C21.6009 53.9934 20.9241 52.3738 20.3781 51.5763C20.3725 51.5692 20.3668 51.5623 20.3613 51.5556C20.3558 51.5489 20.3504 51.5424 20.345 51.536C19.9446 51.0595 19.5411 50.5806 19.1348 50.1041C18.4711 49.3623 17.7767 48.6482 17.0534 47.9636L16.9464 47.6157C16.8366 47.4976 16.7087 47.3639 16.5684 47.2171C15.3219 45.9132 13.0949 43.5837 13.8637 42.1814C14.6752 40.7014 16.8849 40.089 17.9873 40.3238C19.0896 40.5586 20.784 41.8548 20.784 41.8548L24.2798 44.9169L26.2242 47.7033L26.2246 47.704C26.8231 47.0294 27.1529 46.6265 27.1529 46.6265C27.1529 46.6265 27.6633 34.8887 27.6327 32.189C27.602 29.4893 27.5918 21.0278 27.7654 20.3337C27.9389 19.6396 28.5054 18.0627 31.0775 18.4557C33.6496 18.8486 34.0885 20.8543 34.0885 20.8543L34.3487 24.937L34.7877 38.2773C34.7877 38.2773 35.1857 34.2609 36.2881 33.4699C37.0689 32.9085 39.0592 33.0157 40.1718 33.4699C41.514 34.0364 42.0396 38.2671 42.0396 38.2671C42.0396 38.2671 42.4224 36.093 42.9123 35.2612C43.6166 34.0721 46.0254 34.9193 46.8777 35.5572C47.5105 36.0369 48.1127 41.0025 48.1127 41.0025C48.1127 41.0025 48.0514 39.4715 48.7914 38.8183C49.3936 38.3079 51.2206 38.6499 51.731 39.1908C52.8997 40.4361 53.4049 43.9064 53.4049 43.9064L53.6652 46.0651V50.5153C53.8702 53.0229 53.7288 56.1421 52.9787 57.9734C52.7392 58.5581 52.5196 59.0372 52.3161 59.481C51.8822 60.4271 51.5219 61.213 51.1996 62.5207C51.1738 62.9302 50.9608 66.154 50.593 67.0759C50.2 68.0608 48.8936 69.4949 46.8012 70.2808C43.8644 71.417 40.7175 71.9078 37.5742 71.72C35.699 71.5932 31.0356 70.6611 30.5211 68.3298C30.3147 67.3942 29.7628 66.5331 29.1906 65.7816C27.7084 64.3506 24.9074 61.4856 23.9003 59.3577L23.8817 59.3289Z" fill="#FFBC1F"/>
|
||||
<path d="M36.8853 33.2097C36.8853 33.2097 35.5242 33.5579 36.2876 43.6064C36.5224 46.0713 34.9358 48.0759 35.0379 46.2387C35.2114 43.1766 34.6449 39.7369 34.8592 37.5629C35.3185 33.0311 36.8853 33.2097 36.8853 33.2097Z" fill="#CC8F00"/>
|
||||
<path d="M44.1322 34.6795C44.1322 34.6795 42.1269 34.4246 43.303 44.535C43.548 46.9999 43.7495 47.8259 43.0299 46.7746C41.9224 45.1517 41.9071 43.1716 41.9531 38.8235C41.999 34.2917 44.1322 34.6795 44.1322 34.6795Z" fill="#CC8F00"/>
|
||||
<path d="M49.701 38.5781C49.701 38.5781 48.3408 39.7446 49.2662 45.6491C49.8079 48.9711 50.2411 48.6577 50.088 48.8006C49.9349 48.9435 48.4294 47.9636 48.0772 42.5438C47.8185 40.4356 48.3702 39.2702 48.7002 38.9082C49.0302 38.5462 49.701 38.5781 49.701 38.5781Z" fill="#CC8F00"/>
|
||||
<path d="M30.1274 18.4205C30.1274 18.4205 28.5466 18.6497 28.6538 23.0029C28.8171 29.7037 29.1641 30.3569 29.0263 34.3784C28.8885 38.3999 29.6643 43.9319 27.9495 47.1879C26.8574 49.265 26.1123 47.6983 26.1123 47.6983C26.1123 47.6983 28.419 45.7845 27.4443 23.0131C27.1609 18.0862 30.1274 18.4205 30.1274 18.4205Z" fill="#CC8F00"/>
|
||||
<path d="M28.9839 23.4788C28.6222 22.5746 28.1701 19.3192 28.6222 18.9575C29.0743 18.5958 32.6435 18.7706 32.9626 19.2537C33.2344 19.6651 33.1435 22.4841 32.9626 23.4788C32.7818 24.4735 29.3938 24.5036 28.9839 23.4788Z" fill="#FFD470"/>
|
||||
<path d="M16.6094 46.5194C16.6094 46.5194 19.2376 48.9895 21.3096 50.8624C22.6201 52.047 24.8223 56.9738 24.8116 56.9107C25.0225 57.6341 26.6741 61.3371 28.6637 62.4778C32.9637 64.9432 31.5271 70.3587 38.1811 70.2808C43.4121 70.2196 49.7069 69.1128 51.2022 62.4778C51.2022 62.4778 50.9859 66.091 50.593 67.0759C50.2 68.0609 48.8936 69.4949 46.8012 70.2808C43.8644 71.417 40.7175 71.9078 37.5742 71.72C35.699 71.5932 31.0356 70.6612 30.5211 68.3298C30.3147 67.3943 29.7628 66.5332 29.1905 65.7816C27.7017 64.3442 24.8822 61.4598 23.8868 59.329C22.5089 56.3792 21.2484 52.8119 20.3502 51.5361C19.333 50.2723 18.2316 49.0788 17.0534 47.9637L16.6094 46.5194Z" fill="#CC8F00"/>
|
||||
<path d="M29.5988 21.7714C29.4541 20.6863 29.5988 18.7593 29.5988 18.7593C29.418 18.7593 28.7218 18.8235 28.5982 18.9717C28.4051 19.2034 28.4056 19.7928 28.4533 20.5999C28.5191 21.7135 28.7941 23.05 28.984 23.4788C29.2481 24.0751 30.4552 24.2327 30.9375 24.2327C30.4854 24.0518 29.7797 23.1278 29.5988 21.7714Z" fill="#F5AB00"/>
|
||||
<path d="M17.0483 47.9688C17.1749 46.78 16.9528 45.5799 16.4091 44.5152C15.8654 43.4504 15.0235 42.5668 13.9863 41.9723C13.578 41.7222 12.9275 42.7334 13.2796 43.1774C13.6318 43.6214 16.4091 47.1827 17.0483 47.9688Z" fill="#FFD470"/>
|
||||
<path d="M13.2812 42.4391L17.0774 46.9564C17.0774 46.9564 17.1131 47.2169 17.0986 47.5182C17.0841 47.8194 17.0485 47.9688 17.0485 47.9688C17.0485 47.9688 14.8593 45.3353 13.8282 43.886C13.2566 43.1256 13.0212 43.0897 13.2812 42.4391Z" fill="#F5AB00"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_510_17728">
|
||||
<rect width="40.9141" height="53.3511" fill="white" transform="translate(13.1597 18.4149)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.1 KiB |
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import * as React from 'react';
|
||||
import Svg, {
|
||||
type SvgProps,
|
||||
Circle,
|
||||
G,
|
||||
Path,
|
||||
Defs,
|
||||
ClipPath,
|
||||
} from 'react-native-svg';
|
||||
|
||||
const LongPressIllustration = (props: SvgProps) => (
|
||||
<Svg
|
||||
width={69}
|
||||
height={79}
|
||||
fill='none'
|
||||
{...props}
|
||||
>
|
||||
<Circle
|
||||
cx={31}
|
||||
cy={22.5}
|
||||
r={8.5}
|
||||
fill='#3F4350'
|
||||
fillOpacity={0.24}
|
||||
/>
|
||||
<Circle
|
||||
cx={31}
|
||||
cy={22}
|
||||
r={21}
|
||||
stroke='#3F4350'
|
||||
strokeOpacity={0.08}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<Circle
|
||||
cx={31}
|
||||
cy={22}
|
||||
r={14}
|
||||
stroke='#3F4350'
|
||||
strokeOpacity={0.16}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<G clipPath='url(#a)'>
|
||||
<Path
|
||||
fill='#FFBC1F'
|
||||
fillRule='evenodd'
|
||||
d='M23.882 59.329c-.1-.21-.197-.425-.292-.649-.428-.95-.842-1.942-1.235-2.882-.754-1.805-1.43-3.424-1.977-4.222l-.017-.02a35.985 35.985 0 0 0-3.308-3.592l-.107-.348c-.11-.118-.237-.252-.378-.399-1.246-1.304-3.473-3.633-2.704-5.036.811-1.48 3.02-2.092 4.123-1.857 1.103.235 2.797 1.53 2.797 1.53l3.496 3.063 1.944 2.786v.001c.6-.675.929-1.078.929-1.078s.51-11.737.48-14.437c-.031-2.7-.041-11.161.132-11.855.174-.694.74-2.271 3.313-1.878 2.572.393 3.01 2.398 3.01 2.398l.26 4.083.44 13.34s.398-4.016 1.5-4.807c.78-.562 2.771-.454 3.884 0 1.342.566 1.868 4.797 1.868 4.797s.382-2.174.872-3.006c.705-1.189 3.113-.342 3.966.296.633.48 1.235 5.445 1.235 5.445s-.062-1.53.678-2.184c.603-.51 2.43-.168 2.94.373 1.169 1.245 1.674 4.715 1.674 4.715l.26 2.16v4.45c.205 2.507.064 5.626-.686 7.457-.24.585-.46 1.064-.663 1.508-.434.946-.794 1.732-1.116 3.04-.026.41-.24 3.633-.607 4.555-.393.985-1.7 2.419-3.792 3.205a21.945 21.945 0 0 1-9.227 1.439c-1.875-.127-6.538-1.059-7.053-3.39-.206-.936-.758-1.797-1.33-2.548-1.483-1.431-4.284-4.296-5.29-6.424l-.02-.03Z'
|
||||
clipRule='evenodd'
|
||||
/>
|
||||
<Path
|
||||
fill='#CC8F00'
|
||||
d='M36.885 33.21s-1.36.348-.597 10.396c.234 2.465-1.352 4.47-1.25 2.633.173-3.062-.393-6.502-.179-8.676.46-4.532 2.026-4.353 2.026-4.353ZM44.132 34.68s-2.005-.255-.829 9.855c.245 2.465.447 3.29-.273 2.24-1.108-1.623-1.123-3.603-1.077-7.951.046-4.532 2.18-4.145 2.18-4.145ZM49.701 38.578s-1.36 1.167-.435 7.071c.542 3.322.975 3.009.822 3.152-.153.142-1.659-.837-2.01-6.257-.26-2.108.292-3.274.622-3.636.33-.362 1.001-.33 1.001-.33ZM30.127 18.42s-1.58.23-1.473 4.583c.163 6.7.51 7.354.372 11.375-.137 4.022.638 9.554-1.076 12.81-1.093 2.077-1.838.51-1.838.51s2.307-1.913 1.332-24.685c-.283-4.927 2.683-4.593 2.683-4.593Z'
|
||||
/>
|
||||
<Path
|
||||
fill='#FFD470'
|
||||
d='M28.984 23.479c-.362-.904-.814-4.16-.362-4.521.452-.362 4.022-.187 4.34.296.272.411.181 3.23 0 4.225-.18.995-3.568 1.025-3.978 0Z'
|
||||
/>
|
||||
<Path
|
||||
fill='#CC8F00'
|
||||
d='M16.61 46.52s2.628 2.47 4.7 4.342c1.31 1.185 3.512 6.112 3.502 6.049.21.723 1.862 4.426 3.852 5.567 4.3 2.465 2.863 7.88 9.517 7.803 5.231-.061 11.526-1.168 13.021-7.803 0 0-.216 3.613-.609 4.598-.393.985-1.7 2.419-3.792 3.205a21.945 21.945 0 0 1-9.227 1.439c-1.875-.127-6.538-1.059-7.053-3.39-.206-.936-.758-1.797-1.33-2.548-1.49-1.438-4.309-4.322-5.304-6.453-1.378-2.95-2.639-6.517-3.537-7.793a35.982 35.982 0 0 0-3.297-3.572l-.444-1.445Z'
|
||||
/>
|
||||
<Path
|
||||
fill='#F5AB00'
|
||||
d='M29.599 21.771c-.145-1.085 0-3.012 0-3.012-.181 0-.877.064-1 .213-.194.231-.193.82-.146 1.628.066 1.113.341 2.45.531 2.879.264.596 1.471.754 1.953.754-.452-.181-1.157-1.105-1.338-2.462Z'
|
||||
/>
|
||||
<Path
|
||||
fill='#FFD470'
|
||||
d='M17.048 47.969a6.16 6.16 0 0 0-3.062-5.997c-.408-.25-1.059.761-.706 1.205.352.444 3.13 4.006 3.768 4.792Z'
|
||||
/>
|
||||
<Path
|
||||
fill='#F5AB00'
|
||||
d='m13.281 42.44 3.796 4.516s.036.26.022.562c-.015.301-.05.45-.05.45s-2.19-2.633-3.22-4.082c-.572-.76-.808-.796-.548-1.447Z'
|
||||
/>
|
||||
</G>
|
||||
<Defs>
|
||||
<ClipPath id='a'>
|
||||
<Path
|
||||
fill='#fff'
|
||||
d='M13.16 18.415h40.914v53.35H13.16z'
|
||||
/>
|
||||
</ClipPath>
|
||||
</Defs>
|
||||
</Svg>
|
||||
);
|
||||
export default LongPressIllustration;
|
||||
|
|
@ -437,40 +437,41 @@ exports[`components/channel_list_row should show results and tutorial 1`] = `
|
|||
}
|
||||
testID="create_direct_message.user_list.user_item.1.1.profile_picture"
|
||||
>
|
||||
<View
|
||||
style={
|
||||
<ViewManagerAdapter_ExpoImage
|
||||
backgroundColor={4294967295}
|
||||
borderRadius={12}
|
||||
contentFit="cover"
|
||||
contentPosition={
|
||||
{
|
||||
"left": "50%",
|
||||
"top": "50%",
|
||||
}
|
||||
}
|
||||
height={24}
|
||||
nativeViewRef={"[React.ref]"}
|
||||
onError={[Function]}
|
||||
onLoad={[Function]}
|
||||
onLoadStart={[Function]}
|
||||
onProgress={[Function]}
|
||||
placeholder={[]}
|
||||
source={
|
||||
[
|
||||
{
|
||||
"overflow": "hidden",
|
||||
},
|
||||
{
|
||||
"backgroundColor": "#ffffff",
|
||||
"borderRadius": 12,
|
||||
"height": 24,
|
||||
"width": 24,
|
||||
"uri": "https://community.mattermost.com/api/v4/users/1/image?_=123456",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<FastImageView
|
||||
defaultSource={null}
|
||||
resizeMode="cover"
|
||||
source={
|
||||
{
|
||||
"uri": "https://community.mattermost.com/api/v4/users/1/image?_=123456",
|
||||
}
|
||||
style={
|
||||
{
|
||||
"backgroundColor": "#ffffff",
|
||||
"borderRadius": 12,
|
||||
"height": 24,
|
||||
"width": 24,
|
||||
}
|
||||
style={
|
||||
{
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
transition={null}
|
||||
width={24}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
|
|
@ -751,40 +752,41 @@ exports[`components/channel_list_row should show results no tutorial 1`] = `
|
|||
}
|
||||
testID="create_direct_message.user_list.user_item.1.1.profile_picture"
|
||||
>
|
||||
<View
|
||||
style={
|
||||
<ViewManagerAdapter_ExpoImage
|
||||
backgroundColor={4294967295}
|
||||
borderRadius={12}
|
||||
contentFit="cover"
|
||||
contentPosition={
|
||||
{
|
||||
"left": "50%",
|
||||
"top": "50%",
|
||||
}
|
||||
}
|
||||
height={24}
|
||||
nativeViewRef={"[React.ref]"}
|
||||
onError={[Function]}
|
||||
onLoad={[Function]}
|
||||
onLoadStart={[Function]}
|
||||
onProgress={[Function]}
|
||||
placeholder={[]}
|
||||
source={
|
||||
[
|
||||
{
|
||||
"overflow": "hidden",
|
||||
},
|
||||
{
|
||||
"backgroundColor": "#ffffff",
|
||||
"borderRadius": 12,
|
||||
"height": 24,
|
||||
"width": 24,
|
||||
"uri": "https://community.mattermost.com/api/v4/users/1/image?_=123456",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<FastImageView
|
||||
defaultSource={null}
|
||||
resizeMode="cover"
|
||||
source={
|
||||
{
|
||||
"uri": "https://community.mattermost.com/api/v4/users/1/image?_=123456",
|
||||
}
|
||||
style={
|
||||
{
|
||||
"backgroundColor": "#ffffff",
|
||||
"borderRadius": 12,
|
||||
"height": 24,
|
||||
"width": 24,
|
||||
}
|
||||
style={
|
||||
{
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
transition={null}
|
||||
width={24}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
|
|
@ -1099,40 +1101,41 @@ exports[`components/channel_list_row should show results no tutorial 2 users 1`]
|
|||
}
|
||||
testID="create_direct_message.user_list.user_item.1.1.profile_picture"
|
||||
>
|
||||
<View
|
||||
style={
|
||||
<ViewManagerAdapter_ExpoImage
|
||||
backgroundColor={4294967295}
|
||||
borderRadius={12}
|
||||
contentFit="cover"
|
||||
contentPosition={
|
||||
{
|
||||
"left": "50%",
|
||||
"top": "50%",
|
||||
}
|
||||
}
|
||||
height={24}
|
||||
nativeViewRef={"[React.ref]"}
|
||||
onError={[Function]}
|
||||
onLoad={[Function]}
|
||||
onLoadStart={[Function]}
|
||||
onProgress={[Function]}
|
||||
placeholder={[]}
|
||||
source={
|
||||
[
|
||||
{
|
||||
"overflow": "hidden",
|
||||
},
|
||||
{
|
||||
"backgroundColor": "#ffffff",
|
||||
"borderRadius": 12,
|
||||
"height": 24,
|
||||
"width": 24,
|
||||
"uri": "https://community.mattermost.com/api/v4/users/1/image?_=123456",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<FastImageView
|
||||
defaultSource={null}
|
||||
resizeMode="cover"
|
||||
source={
|
||||
{
|
||||
"uri": "https://community.mattermost.com/api/v4/users/1/image?_=123456",
|
||||
}
|
||||
style={
|
||||
{
|
||||
"backgroundColor": "#ffffff",
|
||||
"borderRadius": 12,
|
||||
"height": 24,
|
||||
"width": 24,
|
||||
}
|
||||
style={
|
||||
{
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
transition={null}
|
||||
width={24}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
|
|
@ -1316,40 +1319,41 @@ exports[`components/channel_list_row should show results no tutorial 2 users 1`]
|
|||
}
|
||||
testID="create_direct_message.user_list.user_item.2.2.profile_picture"
|
||||
>
|
||||
<View
|
||||
style={
|
||||
<ViewManagerAdapter_ExpoImage
|
||||
backgroundColor={4294967295}
|
||||
borderRadius={12}
|
||||
contentFit="cover"
|
||||
contentPosition={
|
||||
{
|
||||
"left": "50%",
|
||||
"top": "50%",
|
||||
}
|
||||
}
|
||||
height={24}
|
||||
nativeViewRef={"[React.ref]"}
|
||||
onError={[Function]}
|
||||
onLoad={[Function]}
|
||||
onLoadStart={[Function]}
|
||||
onProgress={[Function]}
|
||||
placeholder={[]}
|
||||
source={
|
||||
[
|
||||
{
|
||||
"overflow": "hidden",
|
||||
},
|
||||
{
|
||||
"backgroundColor": "#ffffff",
|
||||
"borderRadius": 12,
|
||||
"height": 24,
|
||||
"width": 24,
|
||||
"uri": "https://community.mattermost.com/api/v4/users/2/image?_=123456",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<FastImageView
|
||||
defaultSource={null}
|
||||
resizeMode="cover"
|
||||
source={
|
||||
{
|
||||
"uri": "https://community.mattermost.com/api/v4/users/2/image?_=123456",
|
||||
}
|
||||
style={
|
||||
{
|
||||
"backgroundColor": "#ffffff",
|
||||
"borderRadius": 12,
|
||||
"height": 24,
|
||||
"width": 24,
|
||||
}
|
||||
style={
|
||||
{
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
transition={null}
|
||||
width={24}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import FileSystem from 'react-native-fs';
|
||||
import {cacheDirectory} from 'expo-file-system';
|
||||
|
||||
import {isTablet} from '@utils/helpers';
|
||||
|
||||
export default {
|
||||
DOCUMENTS_PATH: `${FileSystem.CachesDirectoryPath}/Documents`,
|
||||
DOCUMENTS_PATH: `${cacheDirectory}/Documents`,
|
||||
IS_TABLET: isTablet(),
|
||||
PUSH_NOTIFY_ANDROID_REACT_NATIVE: 'android_rn',
|
||||
PUSH_NOTIFY_APPLE_REACT_NATIVE: 'apple_rn',
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue