mirror of https://github.com/tooot-app/app
Compare commits
415 Commits
Author | SHA1 | Date |
---|---|---|
xmflsct | 7bcef6fe1d | |
xmflsct | 6f27785a12 | |
xmflsct | 5cec5e8084 | |
xmflsct | 7b63150e73 | |
xmflsct | 997a267b89 | |
xmflsct | 41c09d4ae9 | |
xmflsct | a70dce9780 | |
xmflsct | d16178abc9 | |
xmflsct | fa8d7ed47e | |
xmflsct | 9f8064ded4 | |
vitalyster | c3aba848a5 | |
xmflsct | f64135a6df | |
xmflsct | 1de8a40cda | |
xmflsct | c5c616e3dc | |
xmflsct | 34043fc1b3 | |
xmflsct | 5b031b40c0 | |
xmflsct | 7b6c9d5130 | |
xmflsct | da623dac56 | |
xmflsct | 0a99669af6 | |
xmflsct | cdd80250d1 | |
xmflsct | 09bc6c30af | |
xmflsct | 8b3ca21ac9 | |
xmflsct | 6e5b06f3a7 | |
xmflsct | f7729f33db | |
xmflsct | f2b275d815 | |
xmflsct | 0e64350df8 | |
xmflsct | 760c8b4c68 | |
xmflsct | f1ba7ce377 | |
xmflsct | 3154540bba | |
xmflsct | 93b3ac1618 | |
xmflsct | 4b2b701cea | |
dependabot[bot] | 33709f8fa6 | |
xmflsct | 1375bc95cd | |
xmflsct | 23a8e5cd19 | |
xmflsct | c5eb1591f8 | |
xmflsct | 96c219e3f9 | |
xmflsct | a940ed4cf3 | |
xmflsct | c1e9c738c5 | |
xmflsct | 4f9e9e7a14 | |
xmflsct | df7eb26057 | |
xmflsct | a8243adf51 | |
xmflsct | 8aeeb1d806 | |
xmflsct | 05958b66f6 | |
xmflsct | 141d5916fd | |
xmflsct | ff14f086a7 | |
xmflsct | 46a30585a7 | |
xmflsct | 48e51530b1 | |
xmflsct | 63c4ffbabf | |
xmflsct | 40274ef28c | |
xmflsct | 21200a7758 | |
xmflsct | 5b670f5d13 | |
xmflsct | 36202028f9 | |
xmflsct | cde5a14cab | |
xmflsct | a4152a5bb6 | |
xmflsct | 91596a46c4 | |
xmflsct | d3f75cfc22 | |
xmflsct | 93b53ec158 | |
xmflsct | 016ea33e26 | |
xmflsct | 824730bc40 | |
xmflsct | 7a5464e38a | |
xmflsct | 768a27d37c | |
xmflsct | 6ed54c3f16 | |
xmflsct | 79fc0c814c | |
xmflsct | e74a73fbd7 | |
xmflsct | 20152790f5 | |
xmflsct | 0fc68e56d3 | |
xmflsct | 6c1eba0de7 | |
xmflsct | a1361570a5 | |
xmflsct | b308932e74 | |
xmflsct | 521d8f75fc | |
xmflsct | 86e502afdd | |
xmflsct | aa469c1174 | |
xmflsct | 0d8fdf5740 | |
xmflsct | 0ca2047f89 | |
xmflsct | 3238f44867 | |
xmflsct | c698ba84e6 | |
xmflsct | 288a5f12da | |
xmflsct | 791edcc342 | |
xmflsct | bfe37db9b6 | |
xmflsct | b5ddebe123 | |
xmflsct | 4977e91b66 | |
xmflsct | 5b640879f0 | |
xmflsct | a0b3b38d8d | |
xmflsct | f78693eee8 | |
xmflsct | 4e620f8987 | |
xmflsct | f8a3b56b11 | |
xmflsct | 63900c4338 | |
xmflsct | 1df73b070a | |
xmflsct | 173d4248d8 | |
xmflsct | 0db7f0c40a | |
xmflsct | 921d6e1f4b | |
xmflsct | 43d2601660 | |
xmflsct | 97fb5b8e6d | |
xmflsct | af991fdc56 | |
xmflsct | 6e8cefd7c2 | |
xmflsct | 5c461c0cd1 | |
xmflsct | 6dee67a4de | |
xmflsct | abf3d41780 | |
xmflsct | f58b2af631 | |
xmflsct | 0450019cac | |
xmflsct | 4ce3ce58ab | |
xmflsct | c44482aa75 | |
xmflsct | 120641b95e | |
xmflsct | 71e0c88cc2 | |
xmflsct | 702ecef243 | |
xmflsct | f505f78193 | |
xmflsct | 292fb42a22 | |
xmflsct | 216a736860 | |
xmflsct | 8b721e1d2e | |
xmflsct | de7e39a332 | |
xmflsct | 4269c1d9d1 | |
xmflsct | 35eb7ba765 | |
xmflsct | 45c54db617 | |
xmflsct | 1bff79ed4a | |
xmflsct | ca2e1e07ec | |
xmflsct | 04a56edcf4 | |
xmflsct | b1a796d3f1 | |
xmflsct | 73e4bdb290 | |
xmflsct | d2eb7156a4 | |
xmflsct | 9b0f07fbf1 | |
xmflsct | 542dfe4723 | |
xmflsct | 3b9369b509 | |
xmflsct | 8b8a540105 | |
xmflsct | 001267b9c7 | |
xmflsct | 718870e3e9 | |
xmflsct | 86e7edb963 | |
xmflsct | ad04eff7ce | |
xmflsct | 3b4b6d2f67 | |
xmflsct | c0798a2e0c | |
xmflsct | 29d5a1508d | |
xmflsct | ea54f91f29 | |
xmflsct | 6a8663f9ed | |
xmflsct | 71e3bce422 | |
xmflsct | 781752ee91 | |
xmflsct | a78fe5ffe8 | |
xmflsct | e37a95408c | |
xmflsct | d3cf0edbc9 | |
xmflsct | 79a92b5df7 | |
xmflsct | 4a340fca8e | |
xmflsct | 398f34d95d | |
xmflsct | 3eec9d1af0 | |
xmflsct | 127978da79 | |
xmflsct | 441090ab28 | |
xmflsct | 242c75079a | |
xmflsct | b679c0760f | |
xmflsct | fcc53672fa | |
xmflsct | 620dcfccdb | |
xmflsct | 2de5a251ce | |
xmflsct | 2097dceda7 | |
xmflsct | eecd871d65 | |
xmflsct | 4864f6cd5c | |
xmflsct | 0185335138 | |
xmflsct | 48481a4cd5 | |
xmflsct | 724cd76647 | |
xmflsct | 11ac2bd51d | |
xmflsct | d1ed8a0d2a | |
dependabot[bot] | 9259ee2995 | |
xmflsct | c1ebde8fe9 | |
xmflsct | 8775a0cb25 | |
xmflsct | 0b3d206190 | |
xmflsct | fd114ed0f0 | |
xmflsct | f98b8946dc | |
xmflsct | be2c223142 | |
xmflsct | 5d1459eba6 | |
xmflsct | 1dd64c4e19 | |
xmflsct | 9f6e7738bf | |
xmflsct | adddae9d72 | |
xmflsct | db41e85d07 | |
xmflsct | e225c5b6bf | |
xmflsct | 7c7c96bc42 | |
xmflsct | 242ecf76c0 | |
xmflsct | 2a774a5516 | |
xmflsct | 5ec49739fc | |
xmflsct | 3545c13ae9 | |
xmflsct | 74badd94cb | |
xmflsct | 2d9f7a90cc | |
xmflsct | 90c8e4cba8 | |
xmflsct | 2c96991ca0 | |
xmflsct | 0dacd5ec89 | |
xmflsct | 88d4de98c2 | |
xmflsct | 78b6834c3f | |
xmflsct | e60535cd1c | |
xmflsct | 5d3f773a2a | |
xmflsct | 968a3266c2 | |
xmflsct | c21e99909d | |
xmflsct | 624c1f172c | |
xmflsct | 602e010cfc | |
xmflsct | 49c51b4af2 | |
xmflsct | 574dbd3258 | |
xmflsct | dc893b0d5d | |
xmflsct | 4aa96ba2bd | |
xmflsct | 646b539949 | |
xmflsct | 7559196e42 | |
xmflsct | 57efc4c868 | |
xmflsct | f219371243 | |
xmflsct | 5551cd0e12 | |
xmflsct | 90b0178f5d | |
xmflsct | d1714fab26 | |
xmflsct | aee206fc95 | |
xmflsct | 062e6825b5 | |
xmflsct | 83cd5d4eb0 | |
xmflsct | aba239188f | |
xmflsct | 5c3e490112 | |
xmflsct | ebe57be674 | |
xmflsct | 752d33d5b3 | |
xmflsct | e447a91cfb | |
xmflsct | 8c87841fed | |
xmflsct | 95a99ef7cd | |
xmflsct | 3451a021e9 | |
xmflsct | eff1f97b14 | |
xmflsct | 40522595d0 | |
xmflsct | af5273d216 | |
xmflsct | 023f66895d | |
xmflsct | 738194d108 | |
xmflsct | aa5a607666 | |
xmflsct | 7d5c9e906a | |
xmflsct | 05a46f23b9 | |
xmflsct | 4b8885ef2c | |
xmflsct | 922d7c7917 | |
xmflsct | 653b588c29 | |
xmflsct | e8eb62e2d0 | |
xmflsct | 2a806695ca | |
dependabot[bot] | e417532d60 | |
xmflsct | d73857eef4 | |
xmflsct | 2d91d1f7fb | |
xmflsct | 9d9c16df06 | |
xmflsct | ebc6d03c6a | |
xmflsct | 82928f8611 | |
xmflsct | f1b162a020 | |
xmflsct | 47d5b02468 | |
xmflsct | 613cf1365c | |
xmflsct | 5cd83ae055 | |
xmflsct | fb6a111c55 | |
xmflsct | d93c77c4ca | |
xmflsct | a131b1277c | |
xmflsct | c2a180f4f5 | |
xmflsct | adb7a765b4 | |
xmflsct | 196f51bfca | |
xmflsct | 74e794a215 | |
xmflsct | 18ad22302d | |
xmflsct | 261987cac9 | |
xmflsct | e97eff25c5 | |
xmflsct | 3883c0307a | |
xmflsct | 34c0bbf4bb | |
xmflsct | b52b529550 | |
xmflsct | f10d52cebc | |
xmflsct | 86c3e91439 | |
xmflsct | 9a289489fa | |
xmflsct | 8814161e0e | |
xmflsct | 0efb7e5b70 | |
xmflsct | e5744aba06 | |
xmflsct | e7ca5ba63d | |
xmflsct | 57f1ed62b5 | |
xmflsct | 26d2c78517 | |
xmflsct | e5e74410d0 | |
xmflsct | 58c96c4c08 | |
xmflsct | fa066daa59 | |
xmflsct | bebf4c7101 | |
xmflsct | 81b810496b | |
xmflsct | 2a19e1fc2a | |
xmflsct | a37f66d080 | |
xmflsct | 03fd770a41 | |
xmflsct | 02a90e84f3 | |
xmflsct | 50f95900b6 | |
xmflsct | c7602df08c | |
xmflsct | 7f97908e23 | |
xmflsct | 76d4bc754b | |
xmflsct | 30be3cdb15 | |
xmflsct | 01c27d6a99 | |
xmflsct | e31ce3772a | |
xmflsct | f93fe09783 | |
xmflsct | def4c5424e | |
xmflsct | 8610be1caf | |
xmflsct | 3de4212a65 | |
xmflsct | 1918274ec9 | |
xmflsct | d68ec113c6 | |
xmflsct | 89e2b46792 | |
xmflsct | a4e6540b50 | |
xmflsct | a4e97ccb1c | |
xmflsct | b9c4b139f5 | |
xmflsct | be772f905d | |
xmflsct | cd7e740ab1 | |
xmflsct | 1025d85ae5 | |
xmflsct | 1b2d9d144f | |
xmflsct | 215534d0c8 | |
xmflsct | cfa637b23d | |
xmflsct | a3b5a132c5 | |
xmflsct | 43c0447418 | |
vitalyster | 59a2d932e3 | |
xmflsct | 972ae46d45 | |
xmflsct | 4cadeb6a6f | |
xmflsct | 0b1fdf59ca | |
xmflsct | cc6740a7c0 | |
xmflsct | 6ce78e94f8 | |
xmflsct | 9e0e8db82a | |
xmflsct | fb3f2e82d6 | |
xmflsct | 2d9dc00683 | |
xmflsct | d6d0cc0d03 | |
xmflsct | 7db8b26dd9 | |
xmflsct | 44f8900902 | |
xmflsct | 50332773c3 | |
xmflsct | fb9b7486d0 | |
xmflsct | 23a8c03be2 | |
xmflsct | 88528f5293 | |
xmflsct | 1704c0401c | |
xmflsct | ea02561cdb | |
xmflsct | 70d57ed830 | |
xmflsct | c2aa78fef8 | |
xmflsct | 5ce7d50f8f | |
xmflsct | 3cac12fb9e | |
xmflsct | c7a472124c | |
xmflsct | 8406a57143 | |
xmflsct | bd3046cc2f | |
xmflsct | c83a74d1c3 | |
xmflsct | feadcc019c | |
xmflsct | bb9fa0c752 | |
xmflsct | 06324ee51a | |
xmflsct | 62a6594f91 | |
xmflsct | 092bc5013a | |
xmflsct | ba5bc35a05 | |
xmflsct | bd5e92cab3 | |
xmflsct | 6b73485aae | |
xmflsct | 0bcd0c1725 | |
xmflsct | b067b9bdb1 | |
xmflsct | 4c6b8f0959 | |
xmflsct | 6dafbc96af | |
xmflsct | 62df29a479 | |
xmflsct | f3e1741629 | |
xmflsct | 2705b4b804 | |
xmflsct | 56d1090ca9 | |
xmflsct | ced71d6611 | |
xmflsct | ac9738d358 | |
xmflsct | 554825a9ea | |
xmflsct | 13303c4269 | |
xmflsct | eb385b8872 | |
xmflsct | 65e9f41a3b | |
xmflsct | f0734e52e7 | |
xmflsct | 7ccfdc7562 | |
xmflsct | 4a25feb346 | |
xmflsct | b677c4b7ce | |
xmflsct | 49a0e6d63e | |
xmflsct | 0de7e84f58 | |
xmflsct | e13669af56 | |
xmflsct | ca7309b862 | |
xmflsct | 14b23ac480 | |
John HU | 1029cb6a67 | |
xmflsct | a77e495b6b | |
xmflsct | 43a98be2d9 | |
xmflsct | c6aff79055 | |
xmflsct | 53ea661d9a | |
xmflsct | 892e6c6131 | |
xmflsct | 2a40043b2e | |
xmflsct | 4cddbb9bad | |
xmflsct | 1ea6aff328 | |
John HU | 71ccb4a93c | |
xmflsct | a40f0d9f82 | |
xmflsct | 34f7218c34 | |
xmflsct | 21d6baa70d | |
xmflsct | f3b46b7e9c | |
xmflsct | ea5705a6de | |
xmflsct | e1f951eb8c | |
xmflsct | 219358cf54 | |
xmflsct | e27bf7407b | |
xmflsct | b6045e5121 | |
xmflsct | 1e0e8842db | |
xmflsct | 64b367a247 | |
xmflsct | 3594500b3e | |
xmflsct | f3c40e9486 | |
xmflsct | 57e1206faf | |
xmflsct | e32125ad17 | |
xmflsct | b388853429 | |
xmflsct | 39ab9059d9 | |
xmflsct | fb7111d771 | |
xmflsct | fbdb5ed0db | |
xmflsct | 10f7d74600 | |
xmflsct | 5e90a0d8f3 | |
xmflsct | a0bfb6cb24 | |
xmflsct | b36863337e | |
xmflsct | 852095f5cf | |
xmflsct | 047464a9c1 | |
xmflsct | e95f1e8157 | |
xmflsct | 54c99eb054 | |
xmflsct | 4e8b4c0e95 | |
xmflsct | 2ac1d49f9f | |
xmflsct | dab2ca060d | |
xmflsct | bb02fadc16 | |
xmflsct | 5a77801188 | |
xmflsct | ca629befc9 | |
xmflsct | 29a092a925 | |
xmflsct | 6e7ebef99a | |
xmflsct | f37f9e857d | |
xmflsct | 72fa8c4fcf | |
xmflsct | ed71f86983 | |
John HU | 5967fff318 | |
xmflsct | 533ff1d549 | |
xmflsct | 61967f4420 | |
xmflsct | 4bd11e2246 | |
xmflsct | bace3b85de | |
xmflsct | 50b07fc5eb | |
xmflsct | d26d8f3c14 | |
xmflsct | 71bea470e0 | |
xmflsct | fea45e66bc | |
xmflsct | 909fed0644 | |
xmflsct | 96a448d602 | |
xmflsct | 2c7772d4c2 | |
Hanjiang Yu | c0aad41047 | |
xmflsct | 1bd3e150af | |
xmflsct | 47feac174a | |
xmflsct | a236adfb57 | |
xmflsct | b1f00ce99b | |
xmflsct | fea2e82bdd | |
xmflsct | b76d3f50df | |
xmflsct | a5315501fd | |
xmflsct | ef80ab895e | |
xmflsct | c59690fcb9 |
|
@ -7,24 +7,20 @@ on:
|
|||
|
||||
jobs:
|
||||
build-ios:
|
||||
runs-on: macos-12
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- name: -- Step 0 -- Extract branch name
|
||||
uses: tj-actions/branch-names@v6
|
||||
- uses: tj-actions/branch-names@v8
|
||||
id: branch
|
||||
- name: -- Step 1 -- Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: -- Step 2 -- Setup node
|
||||
uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
- name: -- Step 3 -- Install node dependencies
|
||||
run: yarn install
|
||||
- name: -- Step 4 -- Install ruby dependencies
|
||||
run: bundle install
|
||||
- name: -- Step 5 -- Run fastlane
|
||||
- run: corepack enable
|
||||
- run: yarn install
|
||||
- run: bundle install
|
||||
- run: yarn app:build ios
|
||||
env:
|
||||
DEVELOPER_DIR: /Applications/Xcode_14.1.app/Contents/Developer
|
||||
DEVELOPER_DIR: /Applications/Xcode_15.2.app/Contents/Developer
|
||||
ENVIRONMENT: ${{ steps.branch.outputs.current_branch }}
|
||||
SENTRY_ENVIRONMENT: ${{ steps.branch.outputs.current_branch }}
|
||||
LC_ALL: en_US.UTF-8
|
||||
|
@ -40,30 +36,24 @@ jobs:
|
|||
APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
|
||||
APP_STORE_CONNECT_API_KEY_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY }}
|
||||
GH_PAT_GET_RELEASE: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: yarn app:build ios
|
||||
|
||||
build-android:
|
||||
runs-on: macos-12
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- name: -- Step 0 -- Extract branch name
|
||||
uses: tj-actions/branch-names@v6
|
||||
- uses: tj-actions/branch-names@v8
|
||||
id: branch
|
||||
- name: -- Step 1 -- Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: -- Step 2 -- Setup node
|
||||
uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
- name: -- Step 3 -- Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '11'
|
||||
- name: -- Step 4 -- Install node dependencies
|
||||
run: yarn install
|
||||
- name: -- Step 5 -- Install ruby dependencies
|
||||
run: bundle install
|
||||
- name: -- Step 6 -- Run fastlane
|
||||
java-version: '17'
|
||||
- run: corepack enable
|
||||
- run: yarn install
|
||||
- run: bundle install
|
||||
- run: yarn app:build android
|
||||
env:
|
||||
ENVIRONMENT: ${{ steps.branch.outputs.current_branch }}
|
||||
SENTRY_ENVIRONMENT: ${{ steps.branch.outputs.current_branch }}
|
||||
|
@ -75,31 +65,25 @@ jobs:
|
|||
ANDROID_KEYSTORE_KEY_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_KEY_PASSWORD }}
|
||||
SUPPLY_JSON_KEY_DATA: ${{ secrets.SUPPLY_JSON_KEY_DATA }}
|
||||
GH_PAT_GET_RELEASE: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: yarn app:build android
|
||||
|
||||
create-release:
|
||||
runs-on: macos-12
|
||||
runs-on: macos-14
|
||||
needs: [build-ios, build-android]
|
||||
steps:
|
||||
- name: -- Step 0 -- Extract branch name
|
||||
uses: tj-actions/branch-names@v6
|
||||
- uses: tj-actions/branch-names@v8
|
||||
id: branch
|
||||
- name: -- Step 1 -- Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: -- Step 2 -- Setup node
|
||||
uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
- name: -- Step 3 -- Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '11'
|
||||
- name: -- Step 4 -- Install node dependencies
|
||||
run: yarn install
|
||||
- name: -- Step 5 -- Install ruby dependencies
|
||||
run: bundle install
|
||||
- name: -- Step 6 -- Run fastlane
|
||||
java-version: '17'
|
||||
- run: corepack enable
|
||||
- run: yarn install
|
||||
- run: bundle install
|
||||
- run: yarn app:build release
|
||||
env:
|
||||
ENVIRONMENT: ${{ steps.branch.outputs.current_branch }}
|
||||
SENTRY_ENVIRONMENT: ${{ steps.branch.outputs.current_branch }}
|
||||
|
@ -113,4 +97,3 @@ jobs:
|
|||
ANDROID_KEYSTORE_ALIAS: ${{ secrets.ANDROID_KEYSTORE_ALIAS }}
|
||||
ANDROID_KEYSTORE_KEY_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_KEY_PASSWORD }}
|
||||
FL_GITHUB_RELEASE_API_BEARER: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: yarn app:build release
|
||||
|
|
|
@ -32,6 +32,7 @@ DerivedData
|
|||
*.xcuserstate
|
||||
project.xcworkspace
|
||||
ios/.xcode.env.local
|
||||
ios/containers
|
||||
|
||||
# Android/IntelliJ
|
||||
#
|
||||
|
@ -42,6 +43,8 @@ local.properties
|
|||
*.iml
|
||||
*.hprof
|
||||
.cxx/
|
||||
*.keystore
|
||||
!debug.keystore
|
||||
|
||||
# node.js
|
||||
#
|
||||
|
@ -49,12 +52,6 @@ node_modules/
|
|||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# BUCK
|
||||
buck-out/
|
||||
\.buckd/
|
||||
*.keystore
|
||||
!debug.keystore
|
||||
|
||||
# Bundle artifacts
|
||||
*.jsbundle
|
||||
|
||||
|
@ -66,4 +63,16 @@ buck-out/
|
|||
web-build/
|
||||
dist/
|
||||
|
||||
# @end expo-cli
|
||||
# Temporary files created by Metro to check the health of the file watcher
|
||||
.metro-health-check*
|
||||
|
||||
# @end expo-cli
|
||||
|
||||
# yarn 3
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
|
@ -1,7 +1,7 @@
|
|||
diff --git a/node_modules/@types/react-native-share-menu/index.d.ts b/node_modules/@types/react-native-share-menu/index.d.ts
|
||||
index f52822c..ee98565 100755
|
||||
--- a/node_modules/@types/react-native-share-menu/index.d.ts
|
||||
+++ b/node_modules/@types/react-native-share-menu/index.d.ts
|
||||
diff --git a/index.d.ts b/index.d.ts
|
||||
index f52822c8bed928f387baf90fdb7342c7416a775a..6d9d480d18342832c4b07af2b10f4a63ff538e7b 100755
|
||||
--- a/index.d.ts
|
||||
+++ b/index.d.ts
|
||||
@@ -5,11 +5,9 @@
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// Minimum TypeScript Version: 3.7
|
||||
|
@ -17,12 +17,18 @@ index f52822c..ee98565 100755
|
|||
|
||||
export type ShareCallback = (share?: ShareData) => void;
|
||||
|
||||
@@ -28,7 +26,7 @@ interface ShareMenuReactView {
|
||||
dismissExtension(error?: string): void;
|
||||
openApp(): void;
|
||||
continueInApp(extraData?: object): void;
|
||||
@@ -25,10 +23,10 @@ interface ShareMenu {
|
||||
}
|
||||
|
||||
interface ShareMenuReactView {
|
||||
- dismissExtension(error?: string): void;
|
||||
- openApp(): void;
|
||||
- continueInApp(extraData?: object): void;
|
||||
- data(): Promise<{mimeType: string, data: string}>;
|
||||
+ data(): Promise<{data: {mimeType: string; data: string}[]}>;
|
||||
+ dismissExtension(error?: string): void
|
||||
+ openApp(): void
|
||||
+ continueInApp(extraData?: object): void
|
||||
+ data(): Promise<{ data: { mimeType: string; data: string }[] }>
|
||||
}
|
||||
|
||||
export const ShareMenuReactView: ShareMenuReactView;
|
|
@ -0,0 +1,64 @@
|
|||
diff --git a/ios/Sources/Common/AutoLayoutWrapperView.swift b/ios/Sources/Common/AutoLayoutWrapperView.swift
|
||||
index e2b9be9c129c66eed3eaebb4e33f5456ce98f5da..ef6a0087f524c8d228b7fee31e54fc3dba769ffa 100644
|
||||
--- a/ios/Sources/Common/AutoLayoutWrapperView.swift
|
||||
+++ b/ios/Sources/Common/AutoLayoutWrapperView.swift
|
||||
@@ -18,7 +18,11 @@ class AutoLayoutWrapperView: UIView {
|
||||
|
||||
override func addSubview(_ view: UIView) {
|
||||
if let detachedView = view as? RNIDetachedView {
|
||||
- detachedView.updateBounds(newSize: self.bounds.size);
|
||||
+ do {
|
||||
+ try detachedView.updateBounds(newSize: self.bounds.size);
|
||||
+ } catch {
|
||||
+ print("Error: \(error)");
|
||||
+ };
|
||||
};
|
||||
|
||||
super.addSubview(view);
|
||||
@@ -37,7 +41,11 @@ class AutoLayoutWrapperView: UIView {
|
||||
func updateSizeOfSubviews(newSize: CGSize? = nil){
|
||||
self.subviews.forEach {
|
||||
guard let detachedView = $0 as? RNIDetachedView else { return };
|
||||
- detachedView.updateBounds(newSize: newSize ?? self.bounds.size);
|
||||
+ do {
|
||||
+ try detachedView.updateBounds(newSize: newSize ?? self.bounds.size);
|
||||
+ } catch {
|
||||
+ print("Error: \(error)");
|
||||
+ };
|
||||
};
|
||||
};
|
||||
};
|
||||
diff --git a/ios/Sources/RNIContextMenuView/RNIContextMenuPreviewController.swift b/ios/Sources/RNIContextMenuView/RNIContextMenuPreviewController.swift
|
||||
index 2b4dc6287c68c88d6652b963ac2cc5f59251ffa9..7c8472e90dac8359f6b40ce2c096323fcf388249 100644
|
||||
--- a/ios/Sources/RNIContextMenuView/RNIContextMenuPreviewController.swift
|
||||
+++ b/ios/Sources/RNIContextMenuView/RNIContextMenuPreviewController.swift
|
||||
@@ -63,7 +63,11 @@ class RNIContextMenuPreviewController: UIViewController {
|
||||
case .STRETCH:
|
||||
guard let menuCustomPreviewView = self.menuCustomPreviewView else { return };
|
||||
|
||||
- menuCustomPreviewView.updateBounds(newSize: self.view.bounds.size);
|
||||
+ do {
|
||||
+ try menuCustomPreviewView.updateBounds(newSize: self.view.bounds.size);
|
||||
+ } catch {
|
||||
+ print("Error: \(error)");
|
||||
+ };
|
||||
self.preferredContentSize = .zero;
|
||||
|
||||
case .INHERIT:
|
||||
diff --git a/ios/Sources/RNIContextMenuView/RNIContextMenuView.swift b/ios/Sources/RNIContextMenuView/RNIContextMenuView.swift
|
||||
index affabcdee8303681f1438c6cfdb9d90d6a105ba6..7c470229e06250f4bd80d3133e381b91ff4f61c5 100644
|
||||
--- a/ios/Sources/RNIContextMenuView/RNIContextMenuView.swift
|
||||
+++ b/ios/Sources/RNIContextMenuView/RNIContextMenuView.swift
|
||||
@@ -307,7 +307,11 @@ public class RNIContextMenuView:
|
||||
.init(with: detachedView)
|
||||
);
|
||||
|
||||
- detachedView.detach();
|
||||
+ do {
|
||||
+ try detachedView.detach();
|
||||
+ } catch {
|
||||
+ print("Error: \(error)");
|
||||
+ };
|
||||
};
|
||||
|
||||
#if DEBUG
|
|
@ -0,0 +1,38 @@
|
|||
diff --git a/src/zoom.tsx b/src/zoom.tsx
|
||||
index 70ce1c8d6a43e711f06b93d1eda3b44a3ad9a659..cdc2713470f2d332b8bf3e9c97e38fd9b78281df 100644
|
||||
--- a/src/zoom.tsx
|
||||
+++ b/src/zoom.tsx
|
||||
@@ -4,6 +4,7 @@ import Animated, {
|
||||
useSharedValue,
|
||||
useAnimatedStyle,
|
||||
useDerivedValue,
|
||||
+ withDecay,
|
||||
withTiming,
|
||||
cancelAnimation,
|
||||
runOnJS,
|
||||
@@ -120,11 +121,22 @@ export function Zoom(props: Props) {
|
||||
}
|
||||
}
|
||||
})
|
||||
- .onEnd(() => {
|
||||
+ .onEnd((event) => {
|
||||
if (isPinching.value || !isZoomed.value) return;
|
||||
|
||||
- panTranslateX.value = 0;
|
||||
- panTranslateY.value = 0;
|
||||
+ const maxTranslateX = (viewWidth.value / 2) * scale.value - viewWidth.value / 2;
|
||||
+ const minTranslateX = -maxTranslateX;
|
||||
+ translationX.value = withDecay({
|
||||
+ velocity: event.velocityX,
|
||||
+ clamp: [minTranslateX, maxTranslateX]
|
||||
+ });
|
||||
+
|
||||
+ const maxTranslateY = (viewHeight.value / 2) * scale.value - viewHeight.value / 2;
|
||||
+ const minTranslateY = -maxTranslateY;
|
||||
+ translationY.value = withDecay({
|
||||
+ velocity: event.velocityY,
|
||||
+ clamp: [minTranslateY, maxTranslateY]
|
||||
+ });
|
||||
});
|
||||
|
||||
const pinch = Gesture.Pinch()
|
|
@ -1,7 +1,7 @@
|
|||
diff --git a/node_modules/react-native-share-menu/android/build.gradle b/node_modules/react-native-share-menu/android/build.gradle
|
||||
index 9557fdb..ebdeb6f 100644
|
||||
--- a/node_modules/react-native-share-menu/android/build.gradle
|
||||
+++ b/node_modules/react-native-share-menu/android/build.gradle
|
||||
diff --git a/android/build.gradle b/android/build.gradle
|
||||
index 9557fdbf2fbf97b7f7aeaf7ce86d301a8ced213d..ebdeb6f4de7846d3241101001755595c52a4b05e 100644
|
||||
--- a/android/build.gradle
|
||||
+++ b/android/build.gradle
|
||||
@@ -1,12 +1,12 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
|
@ -19,10 +19,10 @@ index 9557fdb..ebdeb6f 100644
|
|||
versionCode 1
|
||||
versionName "1.0"
|
||||
ndk {
|
||||
diff --git a/node_modules/react-native-share-menu/ios/ReactShareViewController.swift b/node_modules/react-native-share-menu/ios/ReactShareViewController.swift
|
||||
index f42bce6..ee36062 100644
|
||||
--- a/node_modules/react-native-share-menu/ios/ReactShareViewController.swift
|
||||
+++ b/node_modules/react-native-share-menu/ios/ReactShareViewController.swift
|
||||
diff --git a/ios/ReactShareViewController.swift b/ios/ReactShareViewController.swift
|
||||
index f42bce6ce7e3f48a7ddc83f3366b68fd0664b1a0..ee360622b1d03cc9661c78c6f210b84c3b19a725 100644
|
||||
--- a/ios/ReactShareViewController.swift
|
||||
+++ b/ios/ReactShareViewController.swift
|
||||
@@ -13,7 +13,7 @@ class ReactShareViewController: ShareViewController, RCTBridgeDelegate, ReactSha
|
||||
func sourceURL(for bridge: RCTBridge!) -> URL! {
|
||||
#if DEBUG
|
||||
|
@ -32,10 +32,10 @@ index f42bce6..ee36062 100644
|
|||
#else
|
||||
return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
|
||||
#endif
|
||||
diff --git a/node_modules/react-native-share-menu/ios/ShareViewController.swift b/node_modules/react-native-share-menu/ios/ShareViewController.swift
|
||||
index 12d8c92..64aa72b 100644
|
||||
--- a/node_modules/react-native-share-menu/ios/ShareViewController.swift
|
||||
+++ b/node_modules/react-native-share-menu/ios/ShareViewController.swift
|
||||
diff --git a/ios/ShareViewController.swift b/ios/ShareViewController.swift
|
||||
index 12d8c92dda20fabd9e7b55fec57b3d867414063c..8a1db0de285b18a9358a10b2ca8293a8c7d56a8e 100644
|
||||
--- a/ios/ShareViewController.swift
|
||||
+++ b/ios/ShareViewController.swift
|
||||
@@ -19,8 +19,8 @@ class ShareViewController: SLComposeServiceViewController {
|
||||
var hostAppUrlScheme: String?
|
||||
var sharedItems: [Any] = []
|
||||
|
@ -78,7 +78,7 @@ index 12d8c92..64aa72b 100644
|
|||
override func configurationItems() -> [Any]! {
|
||||
// To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
|
||||
return []
|
||||
@@ -238,11 +235,10 @@ class ShareViewController: SLComposeServiceViewController {
|
||||
@@ -238,7 +235,7 @@ class ShareViewController: SLComposeServiceViewController {
|
||||
|
||||
func completeRequest() {
|
||||
// Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
|
||||
|
@ -87,7 +87,3 @@ index 12d8c92..64aa72b 100644
|
|||
}
|
||||
|
||||
func cancelRequest() {
|
||||
extensionContext!.cancelRequest(withError: NSError())
|
||||
}
|
||||
-
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,7 @@
|
|||
compressionLevel: mixed
|
||||
|
||||
enableGlobalCache: false
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.1.0.cjs
|
9
Gemfile
9
Gemfile
|
@ -1,6 +1,13 @@
|
|||
source "https://rubygems.org"
|
||||
|
||||
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
|
||||
ruby ">= 2.6.10"
|
||||
|
||||
# Cocoapods 1.15 introduced a bug which break the build. We will remove the upper
|
||||
# bound in the template on Cocoapods with next React Native release.
|
||||
gem 'cocoapods', '>= 1.13', '< 1.15'
|
||||
gem 'activesupport', '>= 6.1.7.5', '< 7.1.0'
|
||||
|
||||
gem "fastlane"
|
||||
gem 'cocoapods'
|
||||
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
|
||||
eval_gemfile(plugins_path) if File.exist?(plugins_path)
|
||||
|
|
162
Gemfile.lock
162
Gemfile.lock
|
@ -1,48 +1,47 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.5)
|
||||
CFPropertyList (3.0.6)
|
||||
rexml
|
||||
activesupport (6.1.7)
|
||||
activesupport (7.0.4.2)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
tzinfo (~> 2.0)
|
||||
zeitwerk (~> 2.3)
|
||||
addressable (2.8.1)
|
||||
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)
|
||||
artifactory (3.0.15)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.653.0)
|
||||
aws-sdk-core (3.166.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-eventstream (1.3.0)
|
||||
aws-partitions (1.887.0)
|
||||
aws-sdk-core (3.191.0)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sigv4 (~> 1.8)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.59.0)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sdk-kms (1.77.0)
|
||||
aws-sdk-core (~> 3, >= 3.191.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.117.1)
|
||||
aws-sdk-core (~> 3, >= 3.165.0)
|
||||
aws-sdk-s3 (1.143.0)
|
||||
aws-sdk-core (~> 3, >= 3.191.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.4)
|
||||
aws-sigv4 (1.5.2)
|
||||
aws-sigv4 (~> 1.8)
|
||||
aws-sigv4 (1.8.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
claide (1.1.0)
|
||||
cocoapods (1.11.3)
|
||||
cocoapods (1.14.3)
|
||||
addressable (~> 2.8)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
cocoapods-core (= 1.11.3)
|
||||
cocoapods-core (= 1.14.3)
|
||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||
cocoapods-downloader (>= 1.4.0, < 2.0)
|
||||
cocoapods-downloader (>= 2.1, < 3.0)
|
||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||
cocoapods-search (>= 1.0.0, < 2.0)
|
||||
cocoapods-trunk (>= 1.4.0, < 2.0)
|
||||
cocoapods-trunk (>= 1.6.0, < 2.0)
|
||||
cocoapods-try (>= 1.1.0, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
escape (~> 0.0.4)
|
||||
|
@ -50,10 +49,10 @@ GEM
|
|||
gh_inspector (~> 1.0)
|
||||
molinillo (~> 0.8.0)
|
||||
nap (~> 1.0)
|
||||
ruby-macho (>= 1.0, < 3.0)
|
||||
xcodeproj (>= 1.21.0, < 2.0)
|
||||
cocoapods-core (1.11.3)
|
||||
activesupport (>= 5.0, < 7)
|
||||
ruby-macho (>= 2.3.0, < 3.0)
|
||||
xcodeproj (>= 1.23.0, < 2.0)
|
||||
cocoapods-core (1.14.3)
|
||||
activesupport (>= 5.0, < 8)
|
||||
addressable (~> 2.8)
|
||||
algoliasearch (~> 1.0)
|
||||
concurrent-ruby (~> 1.1)
|
||||
|
@ -63,7 +62,7 @@ GEM
|
|||
public_suffix (~> 4.0)
|
||||
typhoeus (~> 1.0)
|
||||
cocoapods-deintegrate (1.0.5)
|
||||
cocoapods-downloader (1.6.3)
|
||||
cocoapods-downloader (2.1)
|
||||
cocoapods-plugins (1.0.0)
|
||||
nap
|
||||
cocoapods-search (1.0.1)
|
||||
|
@ -75,19 +74,18 @@ GEM
|
|||
colored2 (3.1.2)
|
||||
commander (4.6.0)
|
||||
highline (~> 2.0.0)
|
||||
concurrent-ruby (1.1.10)
|
||||
concurrent-ruby (1.2.2)
|
||||
declarative (0.0.20)
|
||||
digest-crc (0.6.4)
|
||||
digest-crc (0.6.5)
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
domain_name (0.6.20240107)
|
||||
dotenv (2.8.1)
|
||||
emoji_regex (3.2.3)
|
||||
escape (0.0.4)
|
||||
ethon (0.15.0)
|
||||
ethon (0.16.0)
|
||||
ffi (>= 1.15.0)
|
||||
excon (0.93.1)
|
||||
faraday (1.10.2)
|
||||
excon (0.109.0)
|
||||
faraday (1.10.3)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
|
@ -115,8 +113,8 @@ GEM
|
|||
faraday-retry (1.0.3)
|
||||
faraday_middleware (1.2.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.2.6)
|
||||
fastlane (2.210.1)
|
||||
fastimage (2.3.0)
|
||||
fastlane (2.219.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
|
@ -135,20 +133,22 @@ GEM
|
|||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-apis-androidpublisher_v3 (~> 0.3)
|
||||
google-apis-playcustomapp_v1 (~> 0.1)
|
||||
google-cloud-env (>= 1.6.0, < 2.0.0)
|
||||
google-cloud-storage (~> 1.31)
|
||||
highline (~> 2.0)
|
||||
http-cookie (~> 1.0.5)
|
||||
json (< 3.0.0)
|
||||
jwt (>= 2.1.0, < 3)
|
||||
mini_magick (>= 4.9.4, < 5.0.0)
|
||||
multipart-post (~> 2.0.0)
|
||||
multipart-post (>= 2.0.0, < 3.0.0)
|
||||
naturally (~> 2.2)
|
||||
optparse (~> 0.1.1)
|
||||
optparse (>= 0.1.1)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
rubyzip (>= 2.0.0, < 3.0.0)
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-table (>= 1.4.5, < 2.0.0)
|
||||
terminal-table (~> 3)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
|
@ -156,17 +156,17 @@ GEM
|
|||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
fastlane-plugin-json (1.1.0)
|
||||
fastlane-plugin-sentry (1.14.0)
|
||||
fastlane-plugin-sentry (1.15.0)
|
||||
os (~> 1.1, >= 1.1.4)
|
||||
fastlane-plugin-versioning_android (0.1.0)
|
||||
fastlane-plugin-versioning_android (0.1.1)
|
||||
fastlane-plugin-yarn (1.2)
|
||||
ffi (1.15.5)
|
||||
ffi (1.16.3)
|
||||
fourflusher (2.3.1)
|
||||
fuzzy_match (2.0.4)
|
||||
gh_inspector (1.1.3)
|
||||
google-apis-androidpublisher_v3 (0.29.0)
|
||||
google-apis-core (>= 0.9.0, < 2.a)
|
||||
google-apis-core (0.9.1)
|
||||
google-apis-androidpublisher_v3 (0.54.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-core (0.11.3)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
httpclient (>= 2.8.1, < 3.a)
|
||||
|
@ -174,31 +174,29 @@ GEM
|
|||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.a)
|
||||
rexml
|
||||
webrick
|
||||
google-apis-iamcredentials_v1 (0.15.0)
|
||||
google-apis-core (>= 0.9.0, < 2.a)
|
||||
google-apis-playcustomapp_v1 (0.12.0)
|
||||
google-apis-core (>= 0.9.1, < 2.a)
|
||||
google-apis-storage_v1 (0.19.0)
|
||||
google-apis-core (>= 0.9.0, < 2.a)
|
||||
google-cloud-core (1.6.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-apis-iamcredentials_v1 (0.17.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-playcustomapp_v1 (0.13.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-storage_v1 (0.31.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-cloud-core (1.6.1)
|
||||
google-cloud-env (>= 1.0, < 3.a)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (1.6.0)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
google-cloud-errors (1.3.0)
|
||||
google-cloud-storage (1.43.0)
|
||||
google-cloud-errors (1.3.1)
|
||||
google-cloud-storage (1.47.0)
|
||||
addressable (~> 2.8)
|
||||
digest-crc (~> 0.4)
|
||||
google-apis-iamcredentials_v1 (~> 0.1)
|
||||
google-apis-storage_v1 (~> 0.19.0)
|
||||
google-apis-storage_v1 (~> 0.31.0)
|
||||
google-cloud-core (~> 1.6)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (1.3.0)
|
||||
googleauth (1.8.1)
|
||||
faraday (>= 0.17.3, < 3.a)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (>= 0.16, < 2.a)
|
||||
|
@ -208,64 +206,59 @@ GEM
|
|||
httpclient (2.8.3)
|
||||
i18n (1.12.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jmespath (1.6.1)
|
||||
json (2.6.2)
|
||||
jwt (2.5.0)
|
||||
memoist (0.16.2)
|
||||
mini_magick (4.11.0)
|
||||
mini_mime (1.1.2)
|
||||
minitest (5.16.3)
|
||||
jmespath (1.6.2)
|
||||
json (2.7.1)
|
||||
jwt (2.7.1)
|
||||
mini_magick (4.12.0)
|
||||
mini_mime (1.1.5)
|
||||
minitest (5.18.0)
|
||||
molinillo (0.8.0)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.0.0)
|
||||
multipart-post (2.3.0)
|
||||
nanaimo (0.3.0)
|
||||
nap (1.1.0)
|
||||
naturally (2.2.1)
|
||||
netrc (0.11.0)
|
||||
optparse (0.1.1)
|
||||
optparse (0.4.0)
|
||||
os (1.1.4)
|
||||
plist (3.6.0)
|
||||
plist (3.7.1)
|
||||
public_suffix (4.0.7)
|
||||
rake (13.0.6)
|
||||
rake (13.1.0)
|
||||
representable (3.2.0)
|
||||
declarative (< 0.1.0)
|
||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rexml (3.2.5)
|
||||
rexml (3.2.6)
|
||||
rouge (2.0.7)
|
||||
ruby-macho (2.5.1)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
security (0.1.3)
|
||||
signet (0.17.0)
|
||||
signet (0.18.0)
|
||||
addressable (~> 2.8)
|
||||
faraday (>= 0.17.5, < 3.a)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.8)
|
||||
simctl (1.6.10)
|
||||
CFPropertyList
|
||||
naturally
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
terminal-table (3.0.2)
|
||||
unicode-display_width (>= 1.1.1, < 3)
|
||||
trailblazer-option (0.1.2)
|
||||
tty-cursor (0.7.1)
|
||||
tty-screen (0.8.1)
|
||||
tty-screen (0.8.2)
|
||||
tty-spinner (0.9.3)
|
||||
tty-cursor (~> 0.7)
|
||||
typhoeus (1.4.0)
|
||||
typhoeus (1.4.1)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (2.0.5)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.8.2)
|
||||
unicode-display_width (1.8.0)
|
||||
webrick (1.7.0)
|
||||
unicode-display_width (2.5.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.22.0)
|
||||
xcodeproj (1.24.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
|
@ -276,18 +269,21 @@ GEM
|
|||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.1)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
zeitwerk (2.6.1)
|
||||
|
||||
PLATFORMS
|
||||
arm64-darwin-22
|
||||
|
||||
DEPENDENCIES
|
||||
cocoapods
|
||||
activesupport (>= 6.1.7.5, < 7.1.0)
|
||||
cocoapods (>= 1.13, < 1.15)
|
||||
fastlane
|
||||
fastlane-plugin-json
|
||||
fastlane-plugin-sentry
|
||||
fastlane-plugin-versioning_android
|
||||
fastlane-plugin-yarn
|
||||
|
||||
RUBY VERSION
|
||||
ruby 3.1.2p20
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.24
|
||||
|
|
48
README.md
48
README.md
|
@ -1,38 +1,30 @@
|
|||
# [tooot](https://tooot.app/) app for Mastodon
|
||||
# [tooot](https://tooot.app/) app for Mastodon compatible platforms
|
||||
|
||||
[![GPL-3.0](https://img.shields.io/github/license/tooot-app/push)](LICENSE) ![GitHub issues](https://img.shields.io/github/issues/tooot-app/app) ![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/tooot-app/app?include_prereleases) [![Crowdin](https://badges.crowdin.net/tooot/localized.svg)](https://crowdin.tooot.app/project/tooot)
|
||||
|
||||
![GitHub Workflow Status (candidate)](https://img.shields.io/github/workflow/status/tooot-app/app/build/candidate?label=build%20candidate) ![GitHub Workflow Status (release)](https://img.shields.io/github/workflow/status/tooot-app/app/build/release?label=build%20release)
|
||||
![GitHub Workflow Status (candidate)](https://img.shields.io/github/actions/workflow/status/tooot-app/app/build.yml?branch=candidate&label=build%20candidate) ![GitHub Workflow Status (release)](https://img.shields.io/github/actions/workflow/status/tooot-app/app/build.yml?branch=release&label=build%20release)
|
||||
|
||||
## Contribute to translation
|
||||
|
||||
Please **do not** create a pull request to update translation. tooot's translation is managed through [https://crowdin.tooot.app/](https://crowdin.tooot.app/) and Crowdin struggles to properly sync two ways. If there is a minor update and you do not want to register an account on Crowdin, please open an issue.
|
||||
|
||||
|
||||
## Special thanks
|
||||
|
||||
[@amrtf](https://crowdin.com/profile/amrtf) for Catalan and Spanish translation
|
||||
|
||||
[@forenta](https://github.com/forenta) for German translation
|
||||
|
||||
[@pat](https://piaille.fr/@pat) for French translation
|
||||
|
||||
[@andrigamerita](https://github.com/andrigamerita) for Italian translation
|
||||
|
||||
[@Hikaru](https://github.com/Hikali-47041) and [@la_la](https://mstdn.jp/@la_la_la) for Japanese translation
|
||||
|
||||
[@hellojaccc](https://github.com/hellojaccc) for Korean translation
|
||||
|
||||
[@jan-vandenberg](https://crowdin.com/profile/jan-vandenberg) for Dutch translation
|
||||
|
||||
[@luizpicolo](https://github.com/luizpicolo) for Brazilian Portuguese
|
||||
|
||||
[@janlindblom](https://github.com/janlindblom) for Swedish
|
||||
|
||||
[@ihoryan](https://crowdin.com/profile/ihoryan) for Ukrainian
|
||||
|
||||
[@duy@mas.to](https://mas.to/@duy) for Vietnamese translation
|
||||
|
||||
[@jimmyorz](https://github.com/jimmyorz) for Traditional Chinese translation
|
||||
|
||||
[@jk@mastodon.social](https://mastodon.social/@jk) for the famous Mastodon boop sound
|
||||
- [@a_mento](https://crowdin.com/profile/a_mento) for Basques translation
|
||||
- [@dzmitry.zubialevich](https://crowdin.com/profile/dzmitry.zubialevich) for Belarusian translation
|
||||
- [@amrtf](https://crowdin.com/profile/amrtf) for Catalan and Spanish translation
|
||||
- [@pat](https://piaille.fr/@pat) for French translation
|
||||
- [@forenta](https://github.com/forenta) for German translation
|
||||
- [@heracl.es](https://heracl.es/) for Greek translation
|
||||
- [@andrigamerita](https://github.com/andrigamerita) for Italian translation
|
||||
- [@Hikaru](https://github.com/Hikali-47041) and [@la_la](https://mstdn.jp/@la_la_la) for Japanese translation
|
||||
- [@hellojaccc](https://github.com/hellojaccc) for Korean translation
|
||||
- [@jan-vandenberg](https://crowdin.com/profile/jan-vandenberg) for Dutch translation
|
||||
- [@gaute](https://gauteweb.net/) for Norwegian translation
|
||||
- [@MStankiewiczOfficial](https://crowdin.com/profile/MStankiewiczOfficial) for Polish translation
|
||||
- [@luizpicolo](https://github.com/luizpicolo) for Brazilian Portuguese
|
||||
- [@janlindblom](https://github.com/janlindblom) for Swedish
|
||||
- [@ihoryan](https://crowdin.com/profile/ihoryan) for Ukrainian
|
||||
- [@duy@mas.to](https://mas.to/@duy) for Vietnamese translation
|
||||
- [@jimmyorz](https://github.com/jimmyorz) for Traditional Chinese translation
|
||||
- [@jk@mastodon.social](https://mastodon.social/@jk) for the famous Mastodon boop sound
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
# To learn about Buck see [Docs](https://buckbuild.com/).
|
||||
# To run your application with Buck:
|
||||
# - install Buck
|
||||
# - `npm start` - to start the packager
|
||||
# - `cd android`
|
||||
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
|
||||
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
|
||||
# - `buck install -r android/app` - compile, install and run application
|
||||
#
|
||||
|
||||
load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
|
||||
|
||||
lib_deps = []
|
||||
|
||||
create_aar_targets(glob(["libs/*.aar"]))
|
||||
|
||||
create_jar_targets(glob(["libs/*.jar"]))
|
||||
|
||||
android_library(
|
||||
name = "all-libs",
|
||||
exported_deps = lib_deps,
|
||||
)
|
||||
|
||||
android_library(
|
||||
name = "app-code",
|
||||
srcs = glob([
|
||||
"src/main/java/**/*.java",
|
||||
]),
|
||||
deps = [
|
||||
":all-libs",
|
||||
":build_config",
|
||||
":res",
|
||||
],
|
||||
)
|
||||
|
||||
android_build_config(
|
||||
name = "build_config",
|
||||
package = "com.xmflsct.app.tooot",
|
||||
)
|
||||
|
||||
android_resource(
|
||||
name = "res",
|
||||
package = "com.xmflsct.app.tooot",
|
||||
res = "src/main/res",
|
||||
)
|
||||
|
||||
android_binary(
|
||||
name = "app",
|
||||
keystore = "//android/keystores:debug",
|
||||
manifest = "src/main/AndroidManifest.xml",
|
||||
package_type = "debug",
|
||||
deps = [
|
||||
":app-code",
|
||||
],
|
||||
)
|
|
@ -1,215 +1,91 @@
|
|||
apply plugin: "com.android.application"
|
||||
|
||||
import com.android.build.OutputFile
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
apply plugin: "org.jetbrains.kotlin.android"
|
||||
apply plugin: "com.facebook.react"
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
|
||||
/**
|
||||
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
|
||||
* and bundleReleaseJsAndAssets).
|
||||
* These basically call `react-native bundle` with the correct arguments during the Android build
|
||||
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
|
||||
* bundle directly from the development server. Below you can see all the possible configurations
|
||||
* and their defaults. If you decide to add a configuration block, make sure to add it before the
|
||||
* `apply from: "../../node_modules/react-native/react.gradle"` line.
|
||||
*
|
||||
* project.ext.react = [
|
||||
* // the name of the generated asset file containing your JS bundle
|
||||
* bundleAssetName: "index.android.bundle",
|
||||
*
|
||||
* // the entry file for bundle generation. If none specified and
|
||||
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
|
||||
* // default. Can be overridden with ENTRY_FILE environment variable.
|
||||
* entryFile: "index.android.js",
|
||||
*
|
||||
* // https://reactnative.dev/docs/performance#enable-the-ram-format
|
||||
* bundleCommand: "ram-bundle",
|
||||
*
|
||||
* // whether to bundle JS and assets in debug mode
|
||||
* bundleInDebug: false,
|
||||
*
|
||||
* // whether to bundle JS and assets in release mode
|
||||
* bundleInRelease: true,
|
||||
*
|
||||
* // whether to bundle JS and assets in another build variant (if configured).
|
||||
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'bundleIn${productFlavor}${buildType}'
|
||||
* // 'bundleIn${buildType}'
|
||||
* // bundleInFreeDebug: true,
|
||||
* // bundleInPaidRelease: true,
|
||||
* // bundleInBeta: true,
|
||||
*
|
||||
* // whether to disable dev mode in custom build variants (by default only disabled in release)
|
||||
* // for example: to disable dev mode in the staging build type (if configured)
|
||||
* devDisabledInStaging: true,
|
||||
* // The configuration property can be in the following formats
|
||||
* // 'devDisabledIn${productFlavor}${buildType}'
|
||||
* // 'devDisabledIn${buildType}'
|
||||
*
|
||||
* // the root of your project, i.e. where "package.json" lives
|
||||
* root: "../../",
|
||||
*
|
||||
* // where to put the JS bundle asset in debug mode
|
||||
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
|
||||
*
|
||||
* // where to put the JS bundle asset in release mode
|
||||
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in debug mode
|
||||
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
|
||||
*
|
||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
||||
* // require('./image.png')), in release mode
|
||||
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
|
||||
*
|
||||
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
|
||||
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
|
||||
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
|
||||
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
|
||||
* // for example, you might want to remove it from here.
|
||||
* inputExcludes: ["android/**", "ios/**"],
|
||||
*
|
||||
* // override which node gets called and with what additional arguments
|
||||
* nodeExecutableAndArgs: ["node"],
|
||||
*
|
||||
* // supply additional arguments to the packager
|
||||
* extraPackagerArgs: []
|
||||
* ]
|
||||
* This is the configuration block to customize your React Native Android app.
|
||||
* By default you don't need to apply any configuration, just uncomment the lines you need.
|
||||
*/
|
||||
|
||||
project.ext.react = [
|
||||
enableHermes: true,
|
||||
]
|
||||
|
||||
apply from: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute().text.trim(), "../react.gradle")
|
||||
react {
|
||||
/* Folders */
|
||||
// The root of your project, i.e. where "package.json" lives. Default is '..'
|
||||
// root = file("../")
|
||||
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
|
||||
// reactNativeDir = file("../node_modules/react-native")
|
||||
// The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
|
||||
// codegenDir = file("../node_modules/@react-native/codegen")
|
||||
// The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
|
||||
// cliFile = file("../node_modules/react-native/cli.js")
|
||||
/* Variants */
|
||||
// The list of variants to that are debuggable. For those we're going to
|
||||
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
|
||||
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
|
||||
// debuggableVariants = ["liteDebug", "prodDebug"]
|
||||
/* Bundling */
|
||||
// A list containing the node command and its flags. Default is just 'node'.
|
||||
// nodeExecutableAndArgs = ["node"]
|
||||
//
|
||||
// The command to run when bundling. By default is 'bundle'
|
||||
// bundleCommand = "ram-bundle"
|
||||
//
|
||||
// The path to the CLI configuration file. Default is empty.
|
||||
// bundleConfig = file(../rn-cli.config.js)
|
||||
//
|
||||
// The name of the generated asset file containing your JS bundle
|
||||
// bundleAssetName = "MyApplication.android.bundle"
|
||||
//
|
||||
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
|
||||
// entryFile = file("../js/MyApplication.android.js")
|
||||
//
|
||||
// A list of extra flags to pass to the 'bundle' commands.
|
||||
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
|
||||
// extraPackagerArgs = []
|
||||
/* Hermes Commands */
|
||||
// The hermes compiler command to run. By default it is 'hermesc'
|
||||
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
|
||||
//
|
||||
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
|
||||
// hermesFlags = ["-O", "-output-source-map"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to true to create two separate APKs instead of one:
|
||||
* - An APK that only works on ARM devices
|
||||
* - An APK that only works on x86 devices
|
||||
* The advantage is the size of the APK is reduced by about 4MB.
|
||||
* Upload all the APKs to the Play Store and people will download
|
||||
* the correct one based on the CPU architecture of their device.
|
||||
*/
|
||||
def enableSeparateBuildPerCPUArchitecture = false
|
||||
|
||||
/**
|
||||
* Run Proguard to shrink the Java bytecode in release builds.
|
||||
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
|
||||
*/
|
||||
def enableProguardInReleaseBuilds = false
|
||||
|
||||
/**
|
||||
* The preferred build flavor of JavaScriptCore.
|
||||
* The preferred build flavor of JavaScriptCore (JSC)
|
||||
*
|
||||
* For example, to use the international variant, you can use:
|
||||
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
|
||||
*
|
||||
* The international variant includes ICU i18n library and necessary data
|
||||
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
|
||||
* give correct results when using with locales other than en-US. Note that
|
||||
* give correct results when using with locales other than en-US. Note that
|
||||
* this variant is about 6MiB larger per architecture than default.
|
||||
*/
|
||||
def jscFlavor = 'org.webkit:android-jsc:+'
|
||||
|
||||
/**
|
||||
* Whether to enable the Hermes VM.
|
||||
*
|
||||
* This should be set on project.ext.react and mirrored here. If it is not set
|
||||
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
|
||||
* and the benefits of using Hermes will therefore be sharply reduced.
|
||||
*/
|
||||
def enableHermes = project.ext.react.get("enableHermes", true);
|
||||
|
||||
/**
|
||||
* Architectures to build native code for.
|
||||
*/
|
||||
def reactNativeArchitectures() {
|
||||
def value = project.getProperties().get("reactNativeArchitectures")
|
||||
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
||||
}
|
||||
|
||||
android {
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
compileSdk rootProject.ext.compileSdkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
namespace "com.xmflsct.app.tooot"
|
||||
defaultConfig {
|
||||
applicationId 'com.xmflsct.app.tooot'
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 50
|
||||
versionName "0.2"
|
||||
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
||||
if (isNewArchitectureEnabled()) {
|
||||
// We configure the CMake build only if you decide to opt-in for the New Architecture.
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DPROJECT_BUILD_DIR=$buildDir",
|
||||
"-DREACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
|
||||
"-DREACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build",
|
||||
"-DNODE_MODULES_DIR=$rootDir/../node_modules",
|
||||
"-DANDROID_STL=c++_shared"
|
||||
}
|
||||
}
|
||||
if (!enableSeparateBuildPerCPUArchitecture) {
|
||||
ndk {
|
||||
abiFilters (*reactNativeArchitectures())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isNewArchitectureEnabled()) {
|
||||
// We configure the CMake build only if you decide to opt-in for the New Architecture.
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path "$projectDir/src/main/jni/CMakeLists.txt"
|
||||
}
|
||||
}
|
||||
def reactAndroidProjectDir = project(':ReactAndroid').projectDir
|
||||
def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
|
||||
dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
|
||||
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
|
||||
into("$buildDir/react-ndk/exported")
|
||||
}
|
||||
def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
|
||||
dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
|
||||
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
|
||||
into("$buildDir/react-ndk/exported")
|
||||
}
|
||||
afterEvaluate {
|
||||
// If you wish to add a custom TurboModule or component locally,
|
||||
// you should uncomment this line.
|
||||
// preBuild.dependsOn("generateCodegenArtifactsFromSchema")
|
||||
preDebugBuild.dependsOn(packageReactNdkDebugLibs)
|
||||
preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
|
||||
// Due to a bug inside AGP, we have to explicitly set a dependency
|
||||
// between configureCMakeDebug* tasks and the preBuild tasks.
|
||||
// This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
|
||||
configureCMakeRelWithDebInfo.dependsOn(preReleaseBuild)
|
||||
configureCMakeDebug.dependsOn(preDebugBuild)
|
||||
reactNativeArchitectures().each { architecture ->
|
||||
tasks.findByName("configureCMakeDebug[${architecture}]")?.configure {
|
||||
dependsOn("preDebugBuild")
|
||||
}
|
||||
tasks.findByName("configureCMakeRelWithDebInfo[${architecture}]")?.configure {
|
||||
dependsOn("preReleaseBuild")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
splits {
|
||||
abi {
|
||||
reset()
|
||||
enable enableSeparateBuildPerCPUArchitecture
|
||||
universalApk false // If true, also generate a universal APK
|
||||
include (*reactNativeArchitectures())
|
||||
}
|
||||
}
|
||||
signingConfigs {
|
||||
debug {
|
||||
|
@ -231,32 +107,9 @@ android {
|
|||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
}
|
||||
}
|
||||
|
||||
// applicationVariants are e.g. debug, release
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
// For each separate APK per architecture, set a unique version code as described here:
|
||||
// https://developer.android.com/studio/build/configure-apk-splits.html
|
||||
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
|
||||
def abi = output.getFilter(OutputFile.ABI)
|
||||
if (abi != null) { // null for the universal-debug, universal-release variants
|
||||
output.versionCodeOverride =
|
||||
defaultConfig.versionCode * 1000 + versionCodes.get(abi)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.3.0") {
|
||||
force = true
|
||||
}
|
||||
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||
|
||||
def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
|
||||
def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
|
||||
def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true";
|
||||
|
@ -282,58 +135,16 @@ dependencies {
|
|||
}
|
||||
}
|
||||
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
||||
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.fbjni'
|
||||
}
|
||||
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.flipper'
|
||||
exclude group:'com.squareup.okhttp3', module:'okhttp'
|
||||
}
|
||||
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.flipper'
|
||||
}
|
||||
// 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 (enableHermes) {
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation("com.facebook.react:hermes-engine:+") { // From node_modules
|
||||
exclude group:'com.facebook.fbjni'
|
||||
}
|
||||
if (hermesEnabled.toBoolean()) {
|
||||
implementation("com.facebook.react:hermes-android")
|
||||
} else {
|
||||
implementation jscFlavor
|
||||
}
|
||||
}
|
||||
|
||||
if (isNewArchitectureEnabled()) {
|
||||
// If new architecture is enabled, we let you build RN from source
|
||||
// Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
|
||||
// This will be applied to all the imported transitive dependency.
|
||||
configurations.all {
|
||||
resolutionStrategy.dependencySubstitution {
|
||||
substitute(module("com.facebook.react:react-native"))
|
||||
.using(project(":ReactAndroid"))
|
||||
.because("On New Architecture we're building React Native from source")
|
||||
substitute(module("com.facebook.react:hermes-engine"))
|
||||
.using(project(":ReactAndroid:hermes-engine"))
|
||||
.because("On New Architecture we're building Hermes from source")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
from configurations.implementation
|
||||
into 'libs'
|
||||
}
|
||||
|
||||
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute().text.trim(), "../native_modules.gradle");
|
||||
applyNativeModulesAppBuildGradle(project)
|
||||
|
||||
def isNewArchitectureEnabled() {
|
||||
// To opt-in for the New Architecture, you can either:
|
||||
// - Set `newArchEnabled` to true inside the `gradle.properties` file
|
||||
// - Invoke gradle with `-newArchEnabled=true`
|
||||
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
|
||||
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
"""Helper definitions to glob .aar and .jar targets"""
|
||||
|
||||
def create_aar_targets(aarfiles):
|
||||
for aarfile in aarfiles:
|
||||
name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
|
||||
lib_deps.append(":" + name)
|
||||
android_prebuilt_aar(
|
||||
name = name,
|
||||
aar = aarfile,
|
||||
)
|
||||
|
||||
def create_jar_targets(jarfiles):
|
||||
for jarfile in jarfiles:
|
||||
name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
|
||||
lib_deps.append(":" + name)
|
||||
prebuilt_jar(
|
||||
name = name,
|
||||
binary_jar = jarfile,
|
||||
)
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"project_info": {
|
||||
"project_number": "661638997772",
|
||||
"project_id": "xmflsct-mastodon-app",
|
||||
"storage_bucket": "xmflsct-mastodon-app.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:661638997772:android:4fd02851f757f8fa9f8b29",
|
||||
"android_client_info": {
|
||||
"package_name": "com.xmflsct.app.tooot"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "661638997772-erabggnp958v10r0tvsrh3pg880qnvqn.apps.googleusercontent.com",
|
||||
"client_type": 1,
|
||||
"android_info": {
|
||||
"package_name": "com.xmflsct.app.tooot",
|
||||
"certificate_hash": "53162f104230ee8b7b1372e4f378e2b9607ca16f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"client_id": "661638997772-6aiqk97aema0rt280i7nfar3ha2mlgno.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyDUw4s-mhQsHvs4hdIsldsi68ZIygM5MC4"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": [
|
||||
{
|
||||
"client_id": "661638997772-6aiqk97aema0rt280i7nfar3ha2mlgno.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
},
|
||||
{
|
||||
"client_id": "661638997772-65g8ce369ugck3ii4ulk6jhb3ijg51kl.apps.googleusercontent.com",
|
||||
"client_type": 2,
|
||||
"ios_info": {
|
||||
"bundle_id": "com.xmflsct.app.tooot",
|
||||
"app_store_id": "1549772269"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
|
@ -1,12 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
<application
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="28"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false" />
|
||||
</application>
|
||||
</manifest>
|
||||
tools:ignore="GoogleAppIndexingWarning"/>
|
||||
</manifest>
|
|
@ -1,70 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its 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.tooot;
|
||||
|
||||
import android.content.Context;
|
||||
import com.facebook.flipper.android.AndroidFlipperClient;
|
||||
import com.facebook.flipper.android.utils.FlipperUtils;
|
||||
import com.facebook.flipper.core.FlipperClient;
|
||||
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
|
||||
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
|
||||
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
|
||||
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
|
||||
import com.facebook.react.ReactInstanceEventListener;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.modules.network.NetworkingModule;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
public class ReactNativeFlipper {
|
||||
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
|
||||
if (FlipperUtils.shouldEnableFlipper(context)) {
|
||||
final FlipperClient client = AndroidFlipperClient.getInstance(context);
|
||||
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
|
||||
client.addPlugin(new ReactFlipperPlugin());
|
||||
client.addPlugin(new DatabasesFlipperPlugin(context));
|
||||
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
|
||||
client.addPlugin(CrashReporterPlugin.getInstance());
|
||||
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
|
||||
NetworkingModule.setCustomClientBuilder(
|
||||
new NetworkingModule.CustomClientBuilder() {
|
||||
@Override
|
||||
public void apply(OkHttpClient.Builder builder) {
|
||||
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
|
||||
}
|
||||
});
|
||||
client.addPlugin(networkFlipperPlugin);
|
||||
client.start();
|
||||
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
|
||||
// Hence we run if after all native modules have been initialized
|
||||
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
|
||||
if (reactContext == null) {
|
||||
reactInstanceManager.addReactInstanceEventListener(
|
||||
new ReactInstanceEventListener() {
|
||||
@Override
|
||||
public void onReactContextInitialized(ReactContext reactContext) {
|
||||
reactInstanceManager.removeReactInstanceEventListener(this);
|
||||
reactContext.runOnNativeModulesQueueThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
client.addPlugin(new FrescoFlipperPlugin());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
client.addPlugin(new FrescoFlipperPlugin());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools" package="com.xmflsct.app.tooot">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
||||
|
@ -9,11 +8,12 @@
|
|||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:requestLegacyExternalStorage="true">
|
||||
<!-- [Custom] Expo Notifications -->
|
||||
<meta-data android:name="expo.modules.notifications.default_notification_icon" android:resource="@drawable/ic_stat_notifications" />
|
||||
<!-- [Custom] End Expo Notifications -->
|
||||
<activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:exported="true" android:theme="@style/Theme.App.SplashScreen" android:screenOrientation="portrait" android:documentLaunchMode="never">
|
||||
<activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:exported="true" android:theme="@style/Theme.App.SplashScreen" android:documentLaunchMode="never">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
package com.xmflsct.app.tooot;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.ReactActivity;
|
||||
import com.facebook.react.ReactActivityDelegate;
|
||||
import com.facebook.react.ReactRootView;
|
||||
|
||||
import expo.modules.ReactActivityDelegateWrapper;
|
||||
|
||||
public class MainActivity extends ReactActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript.
|
||||
* This is used to schedule rendering of the component.
|
||||
*/
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
return "main";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and
|
||||
* you can specify the renderer you wish to use - the new renderer (Fabric) or the old renderer
|
||||
* (Paper).
|
||||
*/
|
||||
@Override
|
||||
protected ReactActivityDelegate createReactActivityDelegate() {
|
||||
return new ReactActivityDelegateWrapper(this, new MainActivityDelegate(this, getMainComponentName()));
|
||||
}
|
||||
public static class MainActivityDelegate extends ReactActivityDelegate {
|
||||
public MainActivityDelegate(ReactActivity activity, String mainComponentName) {
|
||||
super(activity, mainComponentName);
|
||||
}
|
||||
@Override
|
||||
protected ReactRootView createRootView() {
|
||||
ReactRootView reactRootView = new ReactRootView(getContext());
|
||||
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
|
||||
reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED);
|
||||
return reactRootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isConcurrentRootEnabled() {
|
||||
// If you opted-in for the New Architecture, we enable Concurrent Root (i.e. React 18).
|
||||
// More on this on https://reactjs.org/blog/2022/03/29/react-v18.html
|
||||
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package com.xmflsct.app.tooot;
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
|
||||
import com.facebook.react.ReactActivity
|
||||
import com.facebook.react.ReactActivityDelegate
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
|
||||
import com.facebook.react.defaults.DefaultReactActivityDelegate
|
||||
|
||||
import expo.modules.ReactActivityDelegateWrapper
|
||||
|
||||
class MainActivity : ReactActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// Set the theme to AppTheme BEFORE onCreate to support
|
||||
// coloring the background, status bar, and navigation bar.
|
||||
// This is required for expo-splash-screen.
|
||||
setTheme(R.style.AppTheme);
|
||||
super.onCreate(null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript. This is used to schedule
|
||||
* rendering of the component.
|
||||
*/
|
||||
override fun getMainComponentName(): String = "main"
|
||||
|
||||
/**
|
||||
* Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
|
||||
* which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
|
||||
*/
|
||||
override fun createReactActivityDelegate(): ReactActivityDelegate {
|
||||
return ReactActivityDelegateWrapper(
|
||||
this,
|
||||
BuildConfig.IS_NEW_ARCHITECTURE_ENABLED,
|
||||
object : DefaultReactActivityDelegate(
|
||||
this,
|
||||
mainComponentName,
|
||||
fabricEnabled
|
||||
){})
|
||||
}
|
||||
|
||||
/**
|
||||
* Align the back button behavior with Android S
|
||||
* where moving root activities to background instead of finishing activities.
|
||||
* @see <a href="https://developer.android.com/reference/android/app/Activity#onBackPressed()">onBackPressed</a>
|
||||
*/
|
||||
override fun invokeDefaultOnBackPressed() {
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
|
||||
if (!moveTaskToBack(false)) {
|
||||
// For non-root activities, use the default implementation to finish them.
|
||||
super.invokeDefaultOnBackPressed()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Use the default back button implementation on Android S
|
||||
// because it's doing more than [Activity.moveTaskToBack] in fact.
|
||||
super.invokeDefaultOnBackPressed()
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
package com.xmflsct.app.tooot;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.facebook.react.PackageList;
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.config.ReactFeatureFlags;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
|
||||
import expo.modules.ApplicationLifecycleDispatcher;
|
||||
import expo.modules.ReactNativeHostWrapper;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
import com.facebook.react.bridge.JSIModulePackage; // <- react-native-reanimated-v2
|
||||
import com.swmansion.reanimated.ReanimatedJSIModulePackage; // <- react-native-reanimated-v2
|
||||
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
private final ReactNativeHost mReactNativeHost = new ReactNativeHostWrapper(
|
||||
this,
|
||||
new ReactNativeHost(this) {
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
@SuppressWarnings("UnnecessaryLocalVariable")
|
||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// packages.add(new MyReactNativePackage());
|
||||
return packages;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getJSMainModuleName() {
|
||||
return "index";
|
||||
}
|
||||
|
||||
@Override // <- react-native-reanimated-v2
|
||||
protected JSIModulePackage getJSIModulePackage() {
|
||||
return new ReanimatedJSIModulePackage();
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public ReactNativeHost getReactNativeHost() {
|
||||
// if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
// return mNewArchitectureNativeHost;
|
||||
// } else {
|
||||
// return mReactNativeHost;
|
||||
// }
|
||||
return mReactNativeHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
// If you opted-in for the New Architecture, we enable the TurboModule system
|
||||
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||
SoLoader.init(this, /* native exopackage */ false);
|
||||
|
||||
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
ApplicationLifecycleDispatcher.onApplicationCreate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
|
||||
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||
*
|
||||
* @param context
|
||||
* @param reactInstanceManager
|
||||
*/
|
||||
private static void initializeFlipper(
|
||||
Context context, ReactInstanceManager reactInstanceManager) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
try {
|
||||
/*
|
||||
We use reflection here to pick up the class that initializes Flipper,
|
||||
since Flipper library is not available in release mode
|
||||
*/
|
||||
Class<?> aClass = Class.forName("com.xmflsct.app.tooot.ReactNativeFlipper");
|
||||
aClass
|
||||
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
|
||||
.invoke(null, context, reactInstanceManager);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package com.xmflsct.app.tooot;
|
||||
|
||||
import android.app.Application
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.NonNull
|
||||
|
||||
import com.facebook.react.PackageList
|
||||
import com.facebook.react.ReactApplication
|
||||
import com.facebook.react.ReactHost
|
||||
import com.facebook.react.ReactNativeHost
|
||||
import com.facebook.react.ReactPackage
|
||||
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.soloader.SoLoader
|
||||
|
||||
import expo.modules.ApplicationLifecycleDispatcher
|
||||
import expo.modules.ReactNativeHostWrapper
|
||||
|
||||
class MainApplication : Application(), ReactApplication {
|
||||
|
||||
override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper(
|
||||
this,
|
||||
object : DefaultReactNativeHost(this) {
|
||||
override fun getPackages(): List<ReactPackage> {
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// packages.add(new MyReactNativePackage());
|
||||
return PackageList(this).packages
|
||||
}
|
||||
|
||||
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 reactHost: ReactHost
|
||||
get() = getDefaultReactHost(this.applicationContext, reactNativeHost)
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
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()
|
||||
}
|
||||
ApplicationLifecycleDispatcher.onApplicationCreate(this)
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig)
|
||||
}
|
||||
}
|
|
@ -1,20 +1,11 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
buildToolsVersion = "31.0.0"
|
||||
buildToolsVersion = "34.0.0"
|
||||
minSdkVersion = 21
|
||||
compileSdkVersion = 31
|
||||
targetSdkVersion = 31
|
||||
kotlinVersion = '1.6.10'
|
||||
|
||||
if (System.properties['os.arch'] == "aarch64") {
|
||||
// For M1 Users we need to use the NDK 24 which added support for aarch64
|
||||
ndkVersion = "24.0.8215888"
|
||||
} else {
|
||||
// Otherwise we default to the side-by-side NDK version from AGP.
|
||||
ndkVersion = "21.4.7075529"
|
||||
}
|
||||
compileSdkVersion = 34
|
||||
targetSdkVersion = 34
|
||||
ndkVersion = "25.1.8937393"
|
||||
kotlinVersion = '1.8.0'
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
|
@ -22,30 +13,11 @@ buildscript {
|
|||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:7.2.1")
|
||||
classpath("com.android.tools.build:gradle")
|
||||
classpath("com.facebook.react:react-native-gradle-plugin")
|
||||
classpath("de.undercouch:gradle-download-task:5.0.1")
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
classpath 'com.google.gms:google-services:4.3.14'
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url(new File(["node", "--print", "require.resolve('react-native/package.json')"].execute().text.trim(), "../android"))
|
||||
}
|
||||
maven {
|
||||
// Android JSC is installed from npm
|
||||
url(new File(["node", "--print", "require.resolve('jsc-android/package.json')"].execute().text.trim(), "../dist"))
|
||||
}
|
||||
|
||||
google()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
}
|
||||
}
|
||||
apply plugin: "com.facebook.react.rootproject"
|
||||
|
|
|
@ -25,9 +25,6 @@ android.useAndroidX=true
|
|||
# Automatically convert third-party libraries to use AndroidX
|
||||
android.enableJetifier=true
|
||||
|
||||
# Version of flipper SDK to use with React Native
|
||||
FLIPPER_VERSION=0.125.0
|
||||
|
||||
# Use this property to specify which architecture you want to build.
|
||||
# You can also override it from the CLI using
|
||||
# ./gradlew <task> -PreactNativeArchitectures=x86_64
|
||||
|
@ -39,10 +36,14 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
|||
# are providing them.
|
||||
newArchEnabled=false
|
||||
|
||||
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=4096m -XX:+HeapDumpOnOutOfMemoryError
|
||||
org.gradle.daemon=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.configureondemand=true
|
||||
# Use this property to enable or disable the Hermes JS engine.
|
||||
# If set to false, you will be using JSC instead.
|
||||
hermesEnabled=true
|
||||
|
||||
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
|
||||
# org.gradle.daemon=true
|
||||
# org.gradle.parallel=true
|
||||
# org.gradle.configureondemand=true
|
||||
|
||||
# The hosted JavaScript engine
|
||||
# Supported values: expo.jsEngine = "hermes" | "jsc"
|
||||
|
|
Binary file not shown.
|
@ -1,5 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
@ -17,14 +17,56 @@
|
|||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
|
@ -37,13 +79,11 @@ do
|
|||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# 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"'
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
@ -73,6 +113,7 @@ esac
|
|||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
|
@ -89,22 +130,29 @@ location of your Java installation."
|
|||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
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
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
|
@ -122,7 +170,9 @@ fi
|
|||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
|
@ -147,16 +197,27 @@ if "$cygwin" || "$msys" ; then
|
|||
done
|
||||
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.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
@ -183,4 +244,4 @@ eval "set -- $(
|
|||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
exec "$JAVACMD" "$@"
|
|
@ -14,7 +14,7 @@
|
|||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
|
@ -25,7 +25,7 @@
|
|||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
|
@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
@ -69,20 +69,23 @@ goto fail
|
|||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
:omega
|
|
@ -1,17 +1,10 @@
|
|||
rootProject.name = 'tooot'
|
||||
|
||||
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute().text.trim(), "../scripts/autolinking.gradle");
|
||||
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle");
|
||||
useExpoModules()
|
||||
|
||||
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute().text.trim(), "../native_modules.gradle");
|
||||
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
|
||||
applyNativeModulesSettingsGradle(settings)
|
||||
|
||||
include ':app'
|
||||
|
||||
includeBuild('../node_modules/react-native-gradle-plugin')
|
||||
if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
|
||||
include(":ReactAndroid")
|
||||
project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid')
|
||||
include(":ReactAndroid:hermes-engine")
|
||||
project(":ReactAndroid:hermes-engine").projectDir = file('../node_modules/react-native/ReactAndroid/hermes-engine')
|
||||
}
|
||||
includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json')"].execute(null, rootDir).text.trim()).getParentFile())
|
||||
|
|
|
@ -8,6 +8,7 @@ export default (): ExpoConfig => ({
|
|||
slug: 'tooot',
|
||||
scheme: 'tooot',
|
||||
version,
|
||||
// @ts-ignore
|
||||
extra: { environment: process.env.ENVIRONMENT },
|
||||
privacy: 'hidden',
|
||||
ios: {
|
||||
|
@ -15,8 +16,9 @@ export default (): ExpoConfig => ({
|
|||
},
|
||||
android: {
|
||||
package: 'com.xmflsct.app.tooot',
|
||||
permissions: ['CAMERA', 'VIBRATE'],
|
||||
blockedPermissions: ['USE_BIOMETRIC', 'USE_FINGERPRINT']
|
||||
permissions: ['NOTIFICATIONS', 'CAMERA', 'VIBRATE'],
|
||||
blockedPermissions: ['USE_BIOMETRIC', 'USE_FINGERPRINT'],
|
||||
googleServicesFile: './android/app/google-services.json'
|
||||
},
|
||||
plugins: [
|
||||
[
|
||||
|
|
|
@ -1,47 +1,27 @@
|
|||
module.exports = function (api) {
|
||||
api.cache(true)
|
||||
|
||||
const plugins = [
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
[
|
||||
'module-resolver',
|
||||
{
|
||||
root: ['./'],
|
||||
alias: {
|
||||
'@assets': './assets',
|
||||
'@root': './src',
|
||||
'@api': './src/api',
|
||||
'@helpers': './src/helpers',
|
||||
'@components': './src/components',
|
||||
'@screens': './src/screens',
|
||||
'@utils': './src/utils'
|
||||
}
|
||||
}
|
||||
],
|
||||
'react-native-reanimated/plugin'
|
||||
]
|
||||
|
||||
if (
|
||||
process.env.NODE_ENV === 'production' ||
|
||||
process.env.BABEL_ENV === 'production'
|
||||
) {
|
||||
plugins.push('transform-remove-console')
|
||||
}
|
||||
api.cache(false)
|
||||
|
||||
return {
|
||||
presets: [
|
||||
'babel-preset-expo',
|
||||
presets: ['babel-preset-expo'],
|
||||
plugins: [
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
[
|
||||
'@babel/preset-react',
|
||||
'module-resolver',
|
||||
{
|
||||
importSource: '@welldone-software/why-did-you-render',
|
||||
runtime: 'automatic',
|
||||
development:
|
||||
process.env.NODE_ENV === 'development' ||
|
||||
process.env.BABEL_ENV === 'development'
|
||||
root: ['./'],
|
||||
alias: {
|
||||
'@components': './src/components',
|
||||
'@i18n': './src/i18n',
|
||||
'@screens': './src/screens',
|
||||
'@utils': './src/utils'
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
plugins
|
||||
].concat(
|
||||
process.env.NODE_ENV === 'production' || process.env.BABEL_ENV === 'production'
|
||||
? ['transform-remove-console']
|
||||
: [],
|
||||
['react-native-reanimated/plugin']
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
13
crowdin.yml
13
crowdin.yml
|
@ -1,10 +1,5 @@
|
|||
# 'preserve_hierarchy': true
|
||||
|
||||
files:
|
||||
[
|
||||
{
|
||||
'source': '/src/i18n/en/**/*.json',
|
||||
'translation': '/src/i18n/%osx_locale%/**/%original_file_name%',
|
||||
'ignore': ['*.ts'],
|
||||
},
|
||||
]
|
||||
- source: /src/i18n/en/**/*.json
|
||||
translation: /src/i18n/%osx_locale%/**/%original_file_name%
|
||||
ignore:
|
||||
- '*.ts'
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 251 KiB |
|
@ -1,6 +1,3 @@
|
|||
name({
|
||||
'default' => "tooot"
|
||||
})
|
||||
keywords({
|
||||
'default' => "Mastodon,tooot,social,decentralized,长毛象,社交,去中心"
|
||||
})
|
||||
|
|
|
@ -36,7 +36,7 @@ private_lane :build_ios do
|
|||
export_method: "app-store",
|
||||
include_symbols: true,
|
||||
output_directory: BUILD_DIRECTORY,
|
||||
silent: false
|
||||
silent: true
|
||||
)
|
||||
|
||||
case ENVIRONMENT
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../../en-US/name.txt
|
|
@ -0,0 +1 @@
|
|||
../../it/description.txt
|
|
@ -0,0 +1 @@
|
|||
../../it/subtitle.txt
|
|
@ -1 +0,0 @@
|
|||
tooot
|
|
@ -0,0 +1 @@
|
|||
../../zh-Hans/name.txt
|
|
@ -0,0 +1 @@
|
|||
../en-US/name.txt
|
|
@ -1,5 +1,10 @@
|
|||
tooot is an open source, simple yet elegant Mastodon mobile client.
|
||||
tooot is an open source, simple yet elegant Mastodon mobile client. A Mastodon (https://joinmastodon.org/) account is required to use this app.
|
||||
|
||||
A Mastodon (https://joinmastodon.org/) account is required to use this app.
|
||||
tooot supports:
|
||||
- Cross platform, including iPadOS and MacOS
|
||||
- Multiple accounts
|
||||
- Dark mode or adapt to system
|
||||
- Adjustable toot font size
|
||||
- Push notification
|
||||
|
||||
If you have suggestions, please reach out to @tooot@xmflsct.com or support@tooot.ap.
|
||||
If you have suggestions, please reach out to @tooot@xmflsct.com or support@tooot.app.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
tooot - fediverse and Mastodon
|
|
@ -1,10 +1 @@
|
|||
Enjoy toooting! This version includes following improvements and fixes:
|
||||
- Added Ukrainian (Slava Ukraini)
|
||||
- Automatic setting detected language when tooting
|
||||
- Remember public timeline type selection
|
||||
- Show diffing of edit history
|
||||
- Allow hiding boosts and replies in home timeline
|
||||
- Support toot in RTL languages
|
||||
- Added notification for admins
|
||||
- Fix whole word filter matching
|
||||
- Fix tablet cannot delete toot drafts
|
||||
Enjoy toooting! This version includes improvements and fixes.
|
|
@ -1 +1 @@
|
|||
Open source Mastodon client
|
||||
Simple, just works
|
|
@ -0,0 +1,10 @@
|
|||
tooot è un client Mastodon semplice e open source. Per utilizzare questo client, devi disporre di un account Mastodon. (https://joinmastodon.org/).
|
||||
|
||||
Tooot supporta:
|
||||
- Multipiattaforma, inclusi iPadOS e MacOS
|
||||
- Accesso a più account
|
||||
- Modalità scura o adattiva
|
||||
- Dimensione del carattere del testo regolabile
|
||||
- Notifiche push e altre funzioni
|
||||
|
||||
Per suggerimenti o commenti sull'utilizzo, contattare @tooot@xmflsct.com o support@tooot.app.
|
|
@ -0,0 +1 @@
|
|||
App open source per Mastodon
|
|
@ -1,11 +1,10 @@
|
|||
tooot是一个专门为中文用户社区所打造的开源、简洁长毛象客户端。使用此客户端需要已经拥有一个长毛象(https://joinmastodon.org/)账号。
|
||||
tooot起始于专注中文社区的简洁、开源长毛象手机客户端。使用此客户端需要已经拥有一个长毛象(https://joinmastodon.org/)账号。
|
||||
|
||||
tooot支持:
|
||||
- iPad
|
||||
- 跨平台,及iPadOS、MacOS
|
||||
- 多账号登录
|
||||
- 黑暗或自适应模式
|
||||
- 可调整正文字体大小
|
||||
- 可调正文字体尺寸
|
||||
- 消息推送
|
||||
等功能。
|
||||
|
||||
如有使用建议或意见,请联系@tooot@xmflsct.com或者support@tooot.app。
|
|
@ -0,0 +1 @@
|
|||
tooot - 探索联邦宇宙
|
|
@ -1,10 +1 @@
|
|||
toooting愉快!此版本包括以下改进和修复:
|
||||
- 增加乌克兰语(Slava Ukraini)
|
||||
- 自动识别发嘟语言
|
||||
- 记住上次公共时间轴选项
|
||||
- 显示编辑历史的差异
|
||||
- 关注列表可隐藏转嘟和回复
|
||||
- 新增管理员推送通知
|
||||
- 支持嘟文右到左文字
|
||||
- 修复过滤整词功能
|
||||
- 修复平板不能删除草稿
|
||||
tooot-ing愉快!此版本包括改进和修复。
|
|
@ -1 +1 @@
|
|||
开源毛象客户端
|
||||
简约,想你所想
|
6
index.js
6
index.js
|
@ -1,8 +1,4 @@
|
|||
import { registerRootComponent } from 'expo'
|
||||
import App from './src/App'
|
||||
|
||||
import App from '@root/App'
|
||||
|
||||
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
|
||||
// It also ensures that whether you load the app in the Expo client or in a native build,
|
||||
// the environment is set up appropriately
|
||||
registerRootComponent(App)
|
||||
|
|
38
ios/Podfile
38
ios/Podfile
|
@ -1,9 +1,20 @@
|
|||
require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
|
||||
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
|
||||
require File.join(File.dirname(`node --print "require.resolve('@react-native-community/cli-platform-ios/package.json')"`), "native_modules")
|
||||
|
||||
platform :ios, '13.0'
|
||||
install! 'cocoapods', :deterministic_uuids => false
|
||||
# Resolve react_native_pods.rb with node to allow for hoisting
|
||||
require Pod::Executable.execute_command('node', ['-p',
|
||||
'require.resolve(
|
||||
"react-native/scripts/react_native_pods.rb",
|
||||
{paths: [process.argv[1]]},
|
||||
)', __dir__]).strip
|
||||
|
||||
platform :ios, '13.4'
|
||||
prepare_react_native_project!
|
||||
flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled
|
||||
linkage = ENV['USE_FRAMEWORKS']
|
||||
if linkage != nil
|
||||
Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
|
||||
use_frameworks! :linkage => linkage.to_sym
|
||||
end
|
||||
|
||||
require 'json'
|
||||
podfile_properties = JSON.parse(File.read('./Podfile.properties.json')) rescue {}
|
||||
|
@ -12,13 +23,8 @@ target 'tooot' do
|
|||
use_expo_modules!
|
||||
config = use_native_modules!
|
||||
|
||||
# Flags change depending on the env values.
|
||||
flags = get_default_flags()
|
||||
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath],
|
||||
:hermes_enabled => true,
|
||||
:fabric_enabled => flags[:fabric_enabled],
|
||||
:flipper_configuration => FlipperConfiguration.disabled,
|
||||
# An absolute path to your application root.
|
||||
:app_path => "#{Pod::Config.instance.installation_root}/.."
|
||||
|
@ -27,11 +33,9 @@ target 'tooot' do
|
|||
post_install do |installer|
|
||||
react_native_post_install(
|
||||
installer,
|
||||
# Set `mac_catalyst_enabled` to `true` in order to apply patches
|
||||
# necessary for Mac Catalyst builds
|
||||
config[:reactNativePath],
|
||||
:mac_catalyst_enabled => false
|
||||
)
|
||||
__apply_Xcode_12_5_M1_post_install_workaround(installer)
|
||||
|
||||
# For share extension
|
||||
installer.pods_project.targets.each do |target|
|
||||
|
@ -39,15 +43,6 @@ target 'tooot' do
|
|||
config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'No'
|
||||
end
|
||||
end
|
||||
|
||||
# For Xcode 14
|
||||
installer.generated_projects.each do |project|
|
||||
project.targets.each do |target|
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
post_integrate do |installer|
|
||||
|
@ -61,7 +56,6 @@ end
|
|||
|
||||
target 'ShareExtension' do
|
||||
use_react_native!(
|
||||
:hermes_enabled => true,
|
||||
:flipper_configuration => FlipperConfiguration.disabled
|
||||
)
|
||||
|
||||
|
|
1828
ios/Podfile.lock
1828
ios/Podfile.lock
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,2 @@
|
|||
"NSPhotoLibraryAddUsageDescription" = "Дазвольце tooot захоўваць выявы ў папку камеры";
|
||||
"NSPhotoLibraryUsageDescription" = "Дазвольце tooot захоўваць выявы ў папку камеры";
|
|
@ -0,0 +1,2 @@
|
|||
"NSPhotoLibraryAddUsageDescription" = "Επιτρέψτε στο tooot να αποθηκεύει εικόνες στο ρολό της κάμερας";
|
||||
"NSPhotoLibraryUsageDescription" = "Επιτρέψτε στο tooot να αποθηκεύει εικόνες στο ρολό της κάμερας";
|
|
@ -0,0 +1,2 @@
|
|||
"NSPhotoLibraryAddUsageDescription" = "Baimendu tooot-i irudiak zure kameraren rollean gordetzeko";
|
||||
"NSPhotoLibraryUsageDescription" = "Baimendu tooot-i irudiak zure kameraren rollean gordetzeko";
|
|
@ -0,0 +1,2 @@
|
|||
"NSPhotoLibraryAddUsageDescription" = "La tooot lagre bilder på kamerarullen";
|
||||
"NSPhotoLibraryUsageDescription" = "La tooot lagre bilder på kamerarullen";
|
|
@ -0,0 +1,2 @@
|
|||
"NSPhotoLibraryAddUsageDescription" = "Zezwól toootowi na zapisywanie zdjęć w rolce z aparatu";
|
||||
"NSPhotoLibraryUsageDescription" = "Zezwól toootowi na zapisywanie zdjęć w rolce z aparatu";
|
|
@ -70,14 +70,17 @@
|
|||
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = tooot/SplashScreen.storyboard; sourceTree = "<group>"; };
|
||||
DF8133F098604A10B0D94952 /* boop.mp3 */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = boop.mp3; path = tooot/boop.mp3; sourceTree = "<group>"; };
|
||||
E613A80A28282A01003C97D6 /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = tooot/AppDelegate.mm; sourceTree = "<group>"; };
|
||||
E6179D6E29B94551001930D5 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
E6217B7E293C1EBF00B1755E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
E633A420281EAEAB000E540F /* ShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E633A427281EAEAB000E540F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
E633A42F281EAF38000E540F /* ShareViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShareViewController.swift; path = "../../node_modules/react-native-share-menu/ios/ShareViewController.swift"; sourceTree = "<group>"; };
|
||||
E633A431281EB55C000E540F /* ShareExtension-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ShareExtension-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
E63E7FF0292A828100C76FD4 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
E65BA25629EDEF8C008E0BBC /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
E66C0842291F095800DFFF60 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
E671BDF8290EAFB800287BD0 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
E690907B29C1133000489554 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
E690AF692926B737002C38A8 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
E69EBACA28DF282D0057EDEC /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
E69EBACB28DF283A0057EDEC /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
|
@ -85,8 +88,10 @@
|
|||
E69EBACD28DF284D0057EDEC /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
E69EBACE28DF28560057EDEC /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
E6A4895D293C1F740047951A /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
E6B76A1E29C1147B00187ABB /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
E6C8B26628F5F9FC0062CF2E /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
E6D64C7A294A90840098F3AC /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
E6FD3AA7299EE8A900774C18 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||
ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
@ -220,6 +225,7 @@
|
|||
buildPhases = (
|
||||
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */,
|
||||
FD10A7F022414F080027D42C /* Start Packager */,
|
||||
395686AEA3960C8699AE1CAD /* [Expo] Configure project */,
|
||||
13B07F871A680F5B00A75B9A /* Sources */,
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */,
|
||||
|
@ -300,6 +306,11 @@
|
|||
nl,
|
||||
ca,
|
||||
uk,
|
||||
nb,
|
||||
be,
|
||||
eu,
|
||||
pl,
|
||||
el,
|
||||
);
|
||||
mainGroup = 83CBB9F61A601CBA00E9B192;
|
||||
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
|
||||
|
@ -375,6 +386,25 @@
|
|||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
395686AEA3960C8699AE1CAD /* [Expo] Configure project */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[Expo] Configure project";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-tooot/expo-configure-project.sh\"\n";
|
||||
};
|
||||
49D30A53634620EF2A5C6692 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -382,7 +412,7 @@
|
|||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-tooot/Pods-tooot-frameworks.sh",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/hermes.framework/hermes",
|
||||
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
|
@ -401,12 +431,14 @@
|
|||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-tooot/Pods-tooot-resources.sh",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle",
|
||||
"${PODS_ROOT}/Sentry/Sources/Resources/PrivacyInfo.xcprivacy",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/PrivacyInfo.xcprivacy",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
|
@ -442,11 +474,11 @@
|
|||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-ShareExtension/Pods-ShareExtension-resources.sh",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
|
@ -533,6 +565,11 @@
|
|||
E6217B7E293C1EBF00B1755E /* nl */,
|
||||
E6A4895D293C1F740047951A /* ca */,
|
||||
E6D64C7A294A90840098F3AC /* uk */,
|
||||
E6FD3AA7299EE8A900774C18 /* nb */,
|
||||
E6179D6E29B94551001930D5 /* be */,
|
||||
E690907B29C1133000489554 /* eu */,
|
||||
E6B76A1E29C1147B00187ABB /* pl */,
|
||||
E65BA25629EDEF8C008E0BBC /* el */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
|
@ -559,7 +596,7 @@
|
|||
);
|
||||
INFOPLIST_FILE = tooot/Info.plist;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -573,7 +610,8 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = com.xmflsct.app.tooot;
|
||||
PRODUCT_NAME = tooot;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.xmflsct.app.tooot";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "tooot-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
|
@ -598,7 +636,7 @@
|
|||
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES;
|
||||
INFOPLIST_FILE = tooot/Info.plist;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -612,7 +650,8 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = com.xmflsct.app.tooot;
|
||||
PRODUCT_NAME = tooot;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.xmflsct.app.tooot";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "tooot-Bridging-Header.h";
|
||||
SWIFT_PRECOMPILE_BRIDGING_HEADER = YES;
|
||||
|
@ -627,7 +666,7 @@
|
|||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
|
@ -664,6 +703,7 @@
|
|||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION,
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
@ -680,8 +720,12 @@
|
|||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"\"";
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "$(inherited)";
|
||||
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "$(inherited)";
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
USE_HERMES = true;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
@ -690,7 +734,7 @@
|
|||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
|
@ -722,6 +766,10 @@
|
|||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"$(inherited)",
|
||||
_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION,
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
|
@ -736,9 +784,13 @@
|
|||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"\"";
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_CFLAGS = "$(inherited)";
|
||||
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "$(inherited)";
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
USE_HERMES = true;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
|
@ -765,8 +817,7 @@
|
|||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
"IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -781,7 +832,8 @@
|
|||
PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.xmflsct.app.tooot.ShareExtension";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match AdHoc com.xmflsct.app.tooot.ShareExtension";
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "ShareExtension/ShareExtension-Bridging-Header.h";
|
||||
|
@ -815,8 +867,7 @@
|
|||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
"IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -830,7 +881,8 @@
|
|||
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.xmflsct.app.tooot.ShareExtension";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "match AppStore com.xmflsct.app.tooot.ShareExtension";
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "ShareExtension/ShareExtension-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import <React/RCTBridgeDelegate.h>
|
||||
#import <RCTAppDelegate.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <Expo/Expo.h>
|
||||
|
||||
@interface AppDelegate : EXAppDelegateWrapper <UIApplicationDelegate, RCTBridgeDelegate>
|
||||
@interface AppDelegate : EXAppDelegateWrapper
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,91 +1,23 @@
|
|||
#import "AppDelegate.h"
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
#import <React/RCTAppSetupUtils.h>
|
||||
|
||||
#import <React/RCTLinkingManager.h>
|
||||
#import <RNShareMenu/ShareMenuManager.h>
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
#import <React/CoreModulesPlugins.h>
|
||||
#import <React/RCTCxxBridgeDelegate.h>
|
||||
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
|
||||
#import <React/RCTSurfacePresenter.h>
|
||||
#import <React/RCTSurfacePresenterBridgeAdapter.h>
|
||||
#import <ReactCommon/RCTTurboModuleManager.h>
|
||||
|
||||
#import <react/config/ReactNativeConfig.h>
|
||||
|
||||
static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
||||
|
||||
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
|
||||
RCTTurboModuleManager *_turboModuleManager;
|
||||
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
|
||||
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
|
||||
facebook::react::ContextContainer::Shared _contextContainer;
|
||||
}
|
||||
@end
|
||||
#endif
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
RCTAppSetupPrepareApp(application);
|
||||
|
||||
RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
|
||||
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
|
||||
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
|
||||
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
|
||||
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
|
||||
#endif
|
||||
|
||||
UIView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"main" initialProperties:nil];
|
||||
|
||||
// NSDictionary *initProps = [self prepareInitialProps];
|
||||
// UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"tooot", initProps);
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
rootView.backgroundColor = [UIColor colorNamed:@"SplashScreenBackgroundColor"];
|
||||
} else {
|
||||
rootView.backgroundColor = [UIColor whiteColor];
|
||||
}
|
||||
|
||||
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
UIViewController *rootViewController = [self.reactDelegate createRootViewController];
|
||||
rootViewController.view = rootView;
|
||||
self.window.rootViewController = rootViewController;
|
||||
[self.window makeKeyAndVisible];
|
||||
[super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
return YES;
|
||||
}
|
||||
|
||||
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
|
||||
///
|
||||
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
|
||||
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
|
||||
/// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
|
||||
- (BOOL)concurrentRootEnabled
|
||||
{
|
||||
// Switch this bool to turn on and off the concurrent root
|
||||
return false;
|
||||
}
|
||||
- (NSDictionary *)prepareInitialProps
|
||||
{
|
||||
NSMutableDictionary *initProps = [NSMutableDictionary new];
|
||||
#ifdef RCT_NEW_ARCH_ENABLED
|
||||
initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]);
|
||||
#endif
|
||||
return initProps;
|
||||
self.moduleName = @"main";
|
||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
}
|
||||
|
||||
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
return [self getBundleURL];
|
||||
}
|
||||
- (NSURL *)getBundleURL
|
||||
{
|
||||
#if DEBUG
|
||||
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
|
||||
|
@ -94,40 +26,6 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|||
#endif
|
||||
}
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
|
||||
#pragma mark - RCTCxxBridgeDelegate
|
||||
|
||||
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge delegate:self jsInvoker:bridge.jsCallInvoker];
|
||||
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
|
||||
}
|
||||
|
||||
#pragma mark RCTTurboModuleManagerDelegate
|
||||
|
||||
- (Class)getModuleClassFromName:(const char *)name
|
||||
{
|
||||
return RCTCoreModulesClassProvider(name);
|
||||
}
|
||||
|
||||
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name initParams: (const facebook::react::ObjCTurboModule::InitParams &)params
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
|
||||
{
|
||||
return RCTAppSetupDefaultModuleFromClass(moduleClass);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Linking API
|
||||
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
NSString *urlString = url.absoluteString;
|
||||
|
@ -147,4 +45,22 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|||
restorationHandler:restorationHandler];
|
||||
}
|
||||
|
||||
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
|
||||
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
|
||||
{
|
||||
return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
|
||||
}
|
||||
|
||||
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
|
||||
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
|
||||
{
|
||||
return [super application:application didFailToRegisterForRemoteNotificationsWithError:error];
|
||||
}
|
||||
|
||||
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
|
||||
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
|
||||
{
|
||||
return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,90 +1,90 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleAllowMixedLocalizations</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>tooot</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.2</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>com.xmflsct.app.tooot</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>tooot-share</string>
|
||||
<string>tooot</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2102022230</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.social-networking</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>$(PRODUCT_NAME) DOES NOT need microphone permission. Please reject this request.</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Allow $(PRODUCT_NAME) to save an image to your camera roll</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Allow $(PRODUCT_NAME) to access your camera roll to attach photos or videos to your toot</string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>SplashScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<false/>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<true/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIUserInterfaceStyle</key>
|
||||
<string>Automatic</string>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleAllowMixedLocalizations</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>tooot</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.2</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>com.xmflsct.app.tooot</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>tooot-share</string>
|
||||
<string>tooot</string>
|
||||
<string>https</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2102022230</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.social-networking</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<!-- Do not change NSAllowsArbitraryLoads to true, or you will risk app rejection! -->
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<false/>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string></string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>$(PRODUCT_NAME) DOES NOT need microphone permission. Please reject this request.</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Allow $(PRODUCT_NAME) to save an image to your camera roll</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Allow $(PRODUCT_NAME) to access your camera roll to attach photos or videos to your toot</string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>SplashScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<false/>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<true/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIUserInterfaceStyle</key>
|
||||
<string>Automatic</string>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>development</string>
|
||||
<string>production</string>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
module.exports = {
|
||||
transformer: { inlineRequires: true }
|
||||
}
|
||||
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config')
|
||||
|
||||
/**
|
||||
* Metro configuration
|
||||
* https://facebook.github.io/metro/docs/configuration
|
||||
*
|
||||
* @type {import('metro-config').MetroConfig}
|
||||
*/
|
||||
const config = {};
|
||||
|
||||
module.exports = mergeConfig(getDefaultConfig(__dirname), config)
|
||||
|
|
181
package.json
181
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "tooot",
|
||||
"version": "4.7.0",
|
||||
"version": "4.11.0",
|
||||
"description": "tooot for Mastodon",
|
||||
"author": "xmflsct <me@xmflsct.com>",
|
||||
"license": "GPL-3.0-or-later",
|
||||
|
@ -11,116 +11,111 @@
|
|||
"scripts": {
|
||||
"start": "react-native start",
|
||||
"android": "react-native run-android",
|
||||
"iphone": "react-native run-ios --simulator 'iPhone 14 Pro'",
|
||||
"phone": "react-native run-ios --device",
|
||||
"iphone": "react-native run-ios --simulator 'iPhone 15 Pro'",
|
||||
"ipad": "react-native run-ios --simulator 'iPad Pro (11-inch) (4th generation)'",
|
||||
"app:build": "bundle exec fastlane",
|
||||
"clean": "react-native-clean-project",
|
||||
"postinstall": "patch-package"
|
||||
"clean": "react-native-clean-project"
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/react-native-action-sheet": "^4.0.1",
|
||||
"@formatjs/intl-datetimeformat": "^6.4.3",
|
||||
"@formatjs/intl-getcanonicallocales": "^2.0.5",
|
||||
"@formatjs/intl-locale": "^3.0.11",
|
||||
"@formatjs/intl-numberformat": "^8.3.3",
|
||||
"@formatjs/intl-pluralrules": "^5.1.8",
|
||||
"@formatjs/intl-relativetimeformat": "^11.1.8",
|
||||
"@mattermost/react-native-paste-input": "^0.5.2",
|
||||
"@formatjs/intl-datetimeformat": "^6.12.2",
|
||||
"@formatjs/intl-getcanonicallocales": "^2.3.0",
|
||||
"@formatjs/intl-locale": "^3.4.5",
|
||||
"@formatjs/intl-numberformat": "^8.10.0",
|
||||
"@formatjs/intl-pluralrules": "^5.2.12",
|
||||
"@formatjs/intl-relativetimeformat": "^11.2.12",
|
||||
"@mattermost/react-native-paste-input": "^0.7.0",
|
||||
"@neverdull-agency/expo-unlimited-secure-store": "^1.0.10",
|
||||
"@react-native-async-storage/async-storage": "~1.17.11",
|
||||
"@react-native-camera-roll/camera-roll": "^5.2.0",
|
||||
"@react-native-clipboard/clipboard": "^1.11.1",
|
||||
"@react-native-community/blur": "^4.3.0",
|
||||
"@react-native-community/netinfo": "9.3.7",
|
||||
"@react-native-community/segmented-control": "^2.2.2",
|
||||
"@react-native-menu/menu": "^0.7.2",
|
||||
"@react-navigation/bottom-tabs": "^6.5.1",
|
||||
"@react-navigation/native": "^6.1.1",
|
||||
"@react-navigation/native-stack": "^6.9.6",
|
||||
"@react-navigation/stack": "^6.3.9",
|
||||
"@reduxjs/toolkit": "^1.9.1",
|
||||
"@sentry/react-native": "4.12.0",
|
||||
"@sharcoux/slider": "^6.1.1",
|
||||
"@tanstack/react-query": "^4.20.4",
|
||||
"axios": "^1.2.1",
|
||||
"@react-native-async-storage/async-storage": "~1.21.0",
|
||||
"@react-native-camera-roll/camera-roll": "^7.4.0",
|
||||
"@react-native-clipboard/clipboard": "^1.13.2",
|
||||
"@react-native-community/blur": "^4.4.0",
|
||||
"@react-native-community/netinfo": "^11.2.1",
|
||||
"@react-native-firebase/app": "^18.8.0",
|
||||
"@react-native-menu/menu": "^0.9.1",
|
||||
"@react-native-segmented-control/segmented-control": "^2.5.0",
|
||||
"@react-navigation/bottom-tabs": "^6.5.11",
|
||||
"@react-navigation/native": "^6.1.9",
|
||||
"@react-navigation/native-stack": "^6.9.17",
|
||||
"@react-navigation/stack": "^6.3.20",
|
||||
"@sentry/react-native": "^5.18.0",
|
||||
"@sharcoux/slider": "^7.1.1",
|
||||
"@tanstack/react-query": "^4.36.1",
|
||||
"axios": "^1.6.7",
|
||||
"diff": "^5.1.0",
|
||||
"expo": "^47.0.8",
|
||||
"expo-auth-session": "^3.7.3",
|
||||
"expo-av": "^13.0.2",
|
||||
"expo-constants": "^14.0.2",
|
||||
"expo-crypto": "^12.0.0",
|
||||
"expo-file-system": "^15.1.1",
|
||||
"expo-haptics": "^12.0.1",
|
||||
"expo-linking": "^3.2.3",
|
||||
"expo-localization": "^14.0.0",
|
||||
"expo-notifications": "^0.17.0",
|
||||
"expo-random": "^13.0.0",
|
||||
"expo-screen-capture": "^5.0.0",
|
||||
"expo-secure-store": "^12.0.0",
|
||||
"expo-splash-screen": "^0.17.5",
|
||||
"expo-store-review": "^6.0.0",
|
||||
"expo-video-thumbnails": "^7.0.0",
|
||||
"expo-web-browser": "~12.0.0",
|
||||
"i18next": "^22.4.5",
|
||||
"linkify-it": "^4.0.1",
|
||||
"expo": "^50.0.6",
|
||||
"expo-auth-session": "^5.4.0",
|
||||
"expo-av": "^13.10.5",
|
||||
"expo-constants": "^15.4.5",
|
||||
"expo-crypto": "^12.8.0",
|
||||
"expo-file-system": "^16.0.6",
|
||||
"expo-haptics": "^12.8.1",
|
||||
"expo-image": "^1.11.0",
|
||||
"expo-linking": "^6.2.2",
|
||||
"expo-localization": "^14.8.3",
|
||||
"expo-notifications": "^0.27.6",
|
||||
"expo-screen-capture": "^5.8.1",
|
||||
"expo-screen-orientation": "^6.4.1",
|
||||
"expo-secure-store": "^12.8.1",
|
||||
"expo-splash-screen": "^0.26.4",
|
||||
"expo-store-review": "^6.8.3",
|
||||
"expo-video-thumbnails": "^7.9.0",
|
||||
"expo-web-browser": "^12.8.2",
|
||||
"htmlparser2": "^9.1.0",
|
||||
"i18next": "^23.8.2",
|
||||
"linkify-it": "^5.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-i18next": "^12.1.1",
|
||||
"react-intl": "^6.2.5",
|
||||
"react-native": "0.70.6",
|
||||
"react-native-animated-spinkit": "^1.5.2",
|
||||
"react-native-base64": "^0.2.1",
|
||||
"react-native-blurhash": "^1.1.10",
|
||||
"react-native-fast-image": "^8.6.3",
|
||||
"react-native-feather": "^1.1.2",
|
||||
"react-native-flash-message": "^0.3.1",
|
||||
"react-native-gesture-handler": "~2.8.0",
|
||||
"react-native-htmlview": "^0.16.0",
|
||||
"react-native-image-picker": "^4.10.2",
|
||||
"react-native-ios-context-menu": "^1.15.1",
|
||||
"react-i18next": "^14.0.5",
|
||||
"react-intl": "^6.6.2",
|
||||
"react-native": "^0.73.4",
|
||||
"react-native-flash-message": "^0.4.2",
|
||||
"react-native-gesture-handler": "^2.15.0",
|
||||
"react-native-image-picker": "^7.1.0",
|
||||
"react-native-ios-context-menu": "^2.3.2",
|
||||
"react-native-ios-utilities": "^4.3.0",
|
||||
"react-native-language-detection": "^0.2.2",
|
||||
"react-native-pager-view": "^6.1.2",
|
||||
"react-native-reanimated": "^2.13.0",
|
||||
"react-native-mmkv": "^2.11.0",
|
||||
"react-native-pager-view": "^6.2.3",
|
||||
"react-native-quick-base64": "^2.0.8",
|
||||
"react-native-reanimated": "^3.6.2",
|
||||
"react-native-reanimated-zoom": "^0.3.3",
|
||||
"react-native-safe-area-context": "^4.4.1",
|
||||
"react-native-screens": "^3.18.2",
|
||||
"react-native-safe-area-context": "^4.9.0",
|
||||
"react-native-screens": "^3.29.0",
|
||||
"react-native-share-menu": "^6.0.0",
|
||||
"react-native-svg": "^13.6.0",
|
||||
"react-native-svg": "^14.1.0",
|
||||
"react-native-swipe-list-view": "^3.2.9",
|
||||
"react-native-tab-view": "^3.3.4",
|
||||
"react-redux": "^8.0.5",
|
||||
"redux-persist": "^6.0.0",
|
||||
"react-native-tab-view": "^3.5.2",
|
||||
"rn-placeholder": "^3.0.3",
|
||||
"rtl-detect": "^1.0.4",
|
||||
"valid-url": "^1.0.9",
|
||||
"zeego": "^0.5.0"
|
||||
"zeego": "^1.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.5",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.18.9",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@expo/config": "^7.0.3",
|
||||
"@types/diff": "^5.0.2",
|
||||
"@types/linkify-it": "^3.0.2",
|
||||
"@types/lodash": "^4.14.191",
|
||||
"@types/react": "~18.0.26",
|
||||
"@types/react-dom": "~18.0.9",
|
||||
"@types/react-native": "~0.70.8",
|
||||
"@types/react-native-base64": "^0.2.0",
|
||||
"@types/react-native-share-menu": "^5.0.2",
|
||||
"@types/react-timeago": "^4.1.3",
|
||||
"@types/valid-url": "^1.0.3",
|
||||
"@welldone-software/why-did-you-render": "^7.0.1",
|
||||
"babel-plugin-module-resolver": "^4.1.0",
|
||||
"@babel/core": "^7.23.9",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
|
||||
"@babel/preset-typescript": "^7.23.3",
|
||||
"@expo/config": "^8.5.4",
|
||||
"@react-native/metro-config": "^0.73.5",
|
||||
"@react-native/typescript-config": "^0.74.0",
|
||||
"@types/diff": "^5.0.9",
|
||||
"@types/linkify-it": "^3.0.5",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/react": "^18.2.55",
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"@types/react-native-share-menu": "^5.0.5",
|
||||
"babel-plugin-module-resolver": "^5.0.0",
|
||||
"babel-plugin-transform-remove-console": "^6.9.4",
|
||||
"chalk": "^4.1.2",
|
||||
"dotenv": "^16.0.3",
|
||||
"expo-cli": "^6.0.8",
|
||||
"patch-package": "^6.5.0",
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
"react-native-clean-project": "^4.0.1",
|
||||
"typescript": "^4.9.4"
|
||||
"dotenv": "^16.4.1",
|
||||
"react-native-clean-project": "^4.0.3",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"packageManager": "yarn@4.1.0",
|
||||
"resolutions": {
|
||||
"react-native-share-menu@^6.0.0": "patch:react-native-share-menu@npm%3A6.0.0#./.yarn/patches/react-native-share-menu-npm-6.0.0-f1094c3204.patch",
|
||||
"@types/react-native-share-menu@^5.0.2": "patch:@types/react-native-share-menu@npm%3A5.0.2#./.yarn/patches/@types-react-native-share-menu-npm-5.0.2-373df17ecc.patch",
|
||||
"react-native-ios-context-menu@^2.3.2": "patch:react-native-ios-context-menu@npm%3A2.3.2#~/.yarn/patches/react-native-ios-context-menu-npm-2.3.2-9099ed7858.patch",
|
||||
"react-native-reanimated-zoom@^0.3.3": "patch:react-native-reanimated-zoom@npm%3A0.3.3#./.yarn/patches/react-native-reanimated-zoom-npm-0.3.3-bbb8d84109.patch"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
diff --git a/node_modules/expo-av/ios/EXAV/EXAudioSessionManager.m b/node_modules/expo-av/ios/EXAV/EXAudioSessionManager.m
|
||||
index 81dce13..8664b90 100644
|
||||
--- a/node_modules/expo-av/ios/EXAV/EXAudioSessionManager.m
|
||||
+++ b/node_modules/expo-av/ios/EXAV/EXAudioSessionManager.m
|
||||
@@ -168,9 +168,6 @@ - (void)moduleDidBackground:(id)backgroundingModule
|
||||
// compact doesn't work, that's why we need the `|| !pointer` above
|
||||
// http://www.openradar.me/15396578
|
||||
[_foregroundedModules compact];
|
||||
-
|
||||
- // Any possible failures are silent
|
||||
- [self _updateSessionConfiguration];
|
||||
}
|
||||
|
||||
- (void)moduleDidForeground:(id)module
|
|
@ -1,23 +0,0 @@
|
|||
diff --git a/node_modules/react-native-fast-image/RNFastImage.podspec b/node_modules/react-native-fast-image/RNFastImage.podspec
|
||||
index db0fada..b23cd91 100644
|
||||
--- a/node_modules/react-native-fast-image/RNFastImage.podspec
|
||||
+++ b/node_modules/react-native-fast-image/RNFastImage.podspec
|
||||
@@ -16,6 +16,6 @@ Pod::Spec.new do |s|
|
||||
s.source_files = "ios/**/*.{h,m}"
|
||||
|
||||
s.dependency 'React-Core'
|
||||
- s.dependency 'SDWebImage', '~> 5.11.1'
|
||||
- s.dependency 'SDWebImageWebPCoder', '~> 0.8.4'
|
||||
+ s.dependency 'SDWebImage', '~> 5.14.2'
|
||||
+ s.dependency 'SDWebImageWebPCoder', '~> 0.9.1'
|
||||
end
|
||||
diff --git a/node_modules/react-native-fast-image/android/build.gradle b/node_modules/react-native-fast-image/android/build.gradle
|
||||
index 5b21cd5..19d82f8 100644
|
||||
--- a/node_modules/react-native-fast-image/android/build.gradle
|
||||
+++ b/node_modules/react-native-fast-image/android/build.gradle
|
||||
@@ -65,4 +65,5 @@ dependencies {
|
||||
implementation "com.github.bumptech.glide:glide:${glideVersion}"
|
||||
implementation "com.github.bumptech.glide:okhttp3-integration:${glideVersion}"
|
||||
annotationProcessor "com.github.bumptech.glide:compiler:${glideVersion}"
|
||||
+ implementation 'com.github.penfeizhou.android.animation:glide-plugin:2.12.0'
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
diff --git a/node_modules/react-native-htmlview/HTMLView.js b/node_modules/react-native-htmlview/HTMLView.js
|
||||
index 43f8b7e..728112b 100644
|
||||
--- a/node_modules/react-native-htmlview/HTMLView.js
|
||||
+++ b/node_modules/react-native-htmlview/HTMLView.js
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import htmlToElement from './htmlToElement';
|
||||
-import {Linking, Platform, StyleSheet, View, ViewPropTypes} from 'react-native';
|
||||
+import {Linking, Platform, StyleSheet, View} from 'react-native';
|
||||
|
||||
const boldStyle = {fontWeight: 'bold'};
|
||||
const italicStyle = {fontStyle: 'italic'};
|
||||
@@ -146,7 +146,7 @@ HtmlView.propTypes = {
|
||||
renderNode: PropTypes.func,
|
||||
RootComponent: PropTypes.func,
|
||||
rootComponentProps: PropTypes.object,
|
||||
- style: ViewPropTypes.style,
|
||||
+ style: PropTypes.any,
|
||||
stylesheet: PropTypes.object,
|
||||
TextComponent: PropTypes.func,
|
||||
textComponentProps: PropTypes.object,
|
|
@ -0,0 +1,19 @@
|
|||
module.exports = {
|
||||
dependencies: {
|
||||
'@react-native-firebase/app': {
|
||||
platforms: {
|
||||
ios: null
|
||||
}
|
||||
},
|
||||
'@react-native-menu/menu': {
|
||||
platforms: {
|
||||
ios: null
|
||||
}
|
||||
},
|
||||
'react-native-ios-context-menu': {
|
||||
platforms: {
|
||||
android: null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ declare namespace App {
|
|||
| 'Following'
|
||||
| 'Local'
|
||||
| 'LocalPublic'
|
||||
| 'Trending'
|
||||
| 'Explore'
|
||||
| 'Notifications'
|
||||
| 'Hashtag'
|
||||
| 'List'
|
||||
|
|
|
@ -1,8 +1,44 @@
|
|||
import 'i18next'
|
||||
|
||||
import common from '../i18n/en/common.json'
|
||||
import screens from '../i18n/en/screens.json'
|
||||
|
||||
import screenAccountSelection from '../i18n/en/screens/accountSelection.json'
|
||||
import screenAnnouncements from '../i18n/en/screens/announcements.json'
|
||||
import screenCompose from '../i18n/en/screens/compose.json'
|
||||
import screenImageViewer from '../i18n/en/screens/imageViewer.json'
|
||||
import screenTabs from '../i18n/en/screens/tabs.json'
|
||||
|
||||
import componentContextMenu from '../i18n/en/components/contextMenu.json'
|
||||
import componentEmojis from '../i18n/en/components/emojis.json'
|
||||
import componentInstance from '../i18n/en/components/instance.json'
|
||||
import componentMediaSelector from '../i18n/en/components/mediaSelector.json'
|
||||
import componentParse from '../i18n/en/components/parse.json'
|
||||
import componentRelationship from '../i18n/en/components/relationship.json'
|
||||
import componentTimeline from '../i18n/en/components/timeline.json'
|
||||
|
||||
declare module 'i18next' {
|
||||
interface CustomTypeOptions {
|
||||
defaultNS: 'common',
|
||||
defaultNS: 'common'
|
||||
resources: {
|
||||
common: typeof common
|
||||
screens: typeof screens
|
||||
|
||||
screenAccountSelection: typeof screenAccountSelection
|
||||
screenAnnouncements: typeof screenAnnouncements
|
||||
screenCompose: typeof screenCompose
|
||||
screenImageViewer: typeof screenImageViewer
|
||||
screenTabs: typeof screenTabs
|
||||
|
||||
componentContextMenu: typeof componentContextMenu
|
||||
componentEmojis: typeof componentEmojis
|
||||
componentInstance: typeof componentInstance
|
||||
componentMediaSelector: typeof componentMediaSelector
|
||||
componentParse: typeof componentParse
|
||||
componentRelationship: typeof componentRelationship
|
||||
componentTimeline: typeof componentTimeline
|
||||
}
|
||||
returnNull: false
|
||||
returnEmptyString: false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,9 @@ declare namespace Mastodon {
|
|||
source?: Source
|
||||
suspended?: boolean
|
||||
role?: Role
|
||||
|
||||
// Internal
|
||||
_remote?: string // domain
|
||||
}
|
||||
|
||||
type Announcement = {
|
||||
|
@ -263,7 +266,17 @@ declare namespace Mastodon {
|
|||
verified_at: string | null
|
||||
}
|
||||
|
||||
type Filter = {
|
||||
type Filter<T extends 'v1' | 'v2'> = T extends 'v2' ? Filter_V2 : Filter_V1
|
||||
type Filter_V2 = {
|
||||
id: string
|
||||
title: string
|
||||
context: ('home' | 'notifications' | 'public' | 'thread' | 'account')[]
|
||||
expires_at?: string
|
||||
filter_action: 'warn' | 'hide'
|
||||
keywords: FilterKeyword[]
|
||||
statuses: FilterStatus[]
|
||||
}
|
||||
type Filter_V1 = {
|
||||
id: string
|
||||
phrase: string
|
||||
context: ('home' | 'notifications' | 'public' | 'thread' | 'account')[]
|
||||
|
@ -272,13 +285,64 @@ declare namespace Mastodon {
|
|||
whole_word: boolean
|
||||
}
|
||||
|
||||
type FilterKeyword = { id: string; keyword: string; whole_word: boolean }
|
||||
|
||||
type FilterStatus = { id: string; status_id: string }
|
||||
|
||||
type FilterResult = {
|
||||
filter: Filter<'v2'>
|
||||
keyword_matches?: FilterKeyword['keyword'][]
|
||||
status_matches?: FilterStatus['id'][]
|
||||
}
|
||||
|
||||
type List = {
|
||||
id: string
|
||||
title: string
|
||||
replies_policy: 'none' | 'list' | 'followed'
|
||||
}
|
||||
|
||||
type Instance = {
|
||||
type Instance<T extends 'v1' | 'v2'> = T extends 'v2' ? Instance_V2 : Instance_V1
|
||||
type Instance_V2 = {
|
||||
domain: string
|
||||
title: string
|
||||
version: string
|
||||
source_url: string
|
||||
description: string
|
||||
usage: { users: { active_month: number } }
|
||||
thumbnail: { url: string; blurhash?: string; versions?: { '@1x'?: string; '@2x'?: string } }
|
||||
languages: string[]
|
||||
configuration: {
|
||||
urls: { streaming_api: string }
|
||||
accounts: { max_featured_tags: number }
|
||||
statuses: {
|
||||
max_characters: number
|
||||
max_media_attachments: number
|
||||
characters_reserved_per_url: number
|
||||
}
|
||||
media_attachments: {
|
||||
supported_mime_types: string[]
|
||||
image_size_limit: number
|
||||
image_matrix_limit: number
|
||||
video_size_limit: number
|
||||
video_frame_rate_limit: number
|
||||
video_matrix_limit: number
|
||||
}
|
||||
polls: {
|
||||
max_options: number
|
||||
max_characters_per_option: number
|
||||
min_expiration: number
|
||||
max_expiration: number
|
||||
}
|
||||
translation: { enabled: boolean }
|
||||
registrations: { enabled: boolean; approval_required: boolean; message?: string }
|
||||
contact: { email: string; account: Account }
|
||||
rules: Rule[]
|
||||
}
|
||||
|
||||
// Gotosocial
|
||||
account_domain?: string
|
||||
}
|
||||
type Instance_V1 = {
|
||||
// Base
|
||||
uri: string
|
||||
title: string
|
||||
|
@ -323,6 +387,9 @@ declare namespace Mastodon {
|
|||
max_expiration: number
|
||||
}
|
||||
}
|
||||
|
||||
// Gotosocial
|
||||
account_domain?: string
|
||||
}
|
||||
|
||||
type Mention = {
|
||||
|
@ -331,6 +398,9 @@ declare namespace Mastodon {
|
|||
username: string
|
||||
acct: string
|
||||
url: string
|
||||
|
||||
// Internal
|
||||
_remote?: string // domain
|
||||
}
|
||||
|
||||
type Notification =
|
||||
|
@ -382,6 +452,7 @@ declare namespace Mastodon {
|
|||
'posting:default:language'?: string
|
||||
'reading:expand:media'?: 'default' | 'show_all' | 'hide_all'
|
||||
'reading:expand:spoilers'?: boolean
|
||||
'reading:autoplay:gifs'?: boolean
|
||||
}
|
||||
|
||||
type PushSubscription = {
|
||||
|
@ -406,6 +477,8 @@ declare namespace Mastodon {
|
|||
id: string
|
||||
following: boolean
|
||||
showing_reblogs: boolean
|
||||
notifying?: boolean
|
||||
languages?: string[]
|
||||
followed_by: boolean
|
||||
blocking: boolean
|
||||
blocked_by: boolean
|
||||
|
@ -448,6 +521,11 @@ declare namespace Mastodon {
|
|||
updated_at: string
|
||||
}
|
||||
|
||||
type Rule = {
|
||||
id: string
|
||||
text: string
|
||||
}
|
||||
|
||||
type Status = {
|
||||
// Base
|
||||
id: string
|
||||
|
@ -459,7 +537,7 @@ declare namespace Mastodon {
|
|||
sensitive: boolean
|
||||
spoiler_text?: string
|
||||
media_attachments: Attachment[]
|
||||
application: Application
|
||||
application?: Application
|
||||
|
||||
// Attributes
|
||||
mentions: Mention[]
|
||||
|
@ -470,7 +548,7 @@ declare namespace Mastodon {
|
|||
reblogs_count: number
|
||||
favourites_count: number
|
||||
replies_count: number
|
||||
edited_at?: string // FEATURE edit_post
|
||||
edited_at?: string
|
||||
favourited: boolean
|
||||
reblogged: boolean
|
||||
muted: boolean
|
||||
|
@ -486,6 +564,11 @@ declare namespace Mastodon {
|
|||
card?: Card
|
||||
language?: string
|
||||
text?: string
|
||||
filtered?: FilterResult[]
|
||||
|
||||
// Internal
|
||||
_level?: number
|
||||
_remote?: boolean
|
||||
}
|
||||
|
||||
type StatusHistory = {
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
declare module 'gl-react-blurhash'
|
||||
declare module 'htmlparser2-without-node-native'
|
||||
declare module 'react-native-feather'
|
||||
declare module 'react-native-htmlview'
|
||||
declare module 'react-native-toast-message'
|
||||
declare module 'rtl-detect'
|
||||
|
||||
|
|
174
src/App.tsx
174
src/App.tsx
|
@ -1,123 +1,119 @@
|
|||
import { ActionSheetProvider } from '@expo/react-native-action-sheet'
|
||||
import getLanguage from '@helpers/getLanguage'
|
||||
import queryClient from '@helpers/queryClient'
|
||||
import i18n from '@root/i18n/i18n'
|
||||
import Screens from '@root/Screens'
|
||||
import audio from '@root/startup/audio'
|
||||
import dev from '@root/startup/dev'
|
||||
import log from '@root/startup/log'
|
||||
import netInfo from '@root/startup/netInfo'
|
||||
import push from '@root/startup/push'
|
||||
import sentry from '@root/startup/sentry'
|
||||
import timezone from '@root/startup/timezone'
|
||||
import { persistor, store } from '@root/store'
|
||||
import * as Sentry from '@sentry/react-native'
|
||||
import { QueryClientProvider } from '@tanstack/react-query'
|
||||
import AccessibilityManager from '@utils/accessibility/AccessibilityManager'
|
||||
import { changeLanguage } from '@utils/slices/settingsSlice'
|
||||
import { connectVerify } from '@utils/api/helpers/connect'
|
||||
import getLanguage from '@utils/helpers/getLanguage'
|
||||
import { queryClient } from '@utils/queryHooks'
|
||||
import audio from '@utils/startup/audio'
|
||||
import { dev } from '@utils/startup/dev'
|
||||
import log from '@utils/startup/log'
|
||||
import netInfo from '@utils/startup/netInfo'
|
||||
import push from '@utils/startup/push'
|
||||
import sentry from '@utils/startup/sentry'
|
||||
import { GLOBAL } from '@utils/storage'
|
||||
import { getGlobalStorage, setAccount, setGlobalStorage } from '@utils/storage/actions'
|
||||
import { migrateFromAsyncStorage, versionStorageGlobal } from '@utils/storage/migrations/toMMKV'
|
||||
import ThemeManager from '@utils/styles/ThemeManager'
|
||||
import * as Localization from 'expo-localization'
|
||||
import * as SplashScreen from 'expo-splash-screen'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { IntlProvider } from 'react-intl'
|
||||
import { LogBox, Platform } from 'react-native'
|
||||
import { Platform } from 'react-native'
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context'
|
||||
import { enableFreeze } from 'react-native-screens'
|
||||
import { QueryClientProvider } from '@tanstack/react-query'
|
||||
import { Provider } from 'react-redux'
|
||||
import { PersistGate } from 'redux-persist/integration/react'
|
||||
import i18n from './i18n'
|
||||
import Screens from './screens'
|
||||
|
||||
Platform.select({
|
||||
android: LogBox.ignoreLogs(['Setting a timer for a long period of time'])
|
||||
})
|
||||
log('log', 'App', 'delay splash')
|
||||
SplashScreen.preventAutoHideAsync()
|
||||
|
||||
dev()
|
||||
sentry()
|
||||
netInfo()
|
||||
audio()
|
||||
push()
|
||||
timezone()
|
||||
enableFreeze(true)
|
||||
|
||||
const App: React.FC = () => {
|
||||
log('log', 'App', 'rendering App')
|
||||
const [localCorrupt, setLocalCorrupt] = useState<string>()
|
||||
const [appIsReady, setAppIsReady] = useState(false)
|
||||
|
||||
const [hasMigrated, setHasMigrated] = useState<boolean>(versionStorageGlobal !== undefined)
|
||||
|
||||
useEffect(() => {
|
||||
const delaySplash = async () => {
|
||||
log('log', 'App', 'delay splash')
|
||||
try {
|
||||
await SplashScreen.preventAutoHideAsync()
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
const prepare = async () => {
|
||||
if (!hasMigrated) {
|
||||
try {
|
||||
await migrateFromAsyncStorage()
|
||||
setHasMigrated(true)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const useConnect = getGlobalStorage.boolean('app.connect')
|
||||
GLOBAL.connect = useConnect
|
||||
log('log', 'App', `connect: ${useConnect}`)
|
||||
if (useConnect) {
|
||||
await connectVerify()
|
||||
.then(() => log('log', 'App', 'connected'))
|
||||
.catch(() => log('warn', 'App', 'connect verify failed'))
|
||||
}
|
||||
|
||||
log('log', 'App', 'loading from MMKV')
|
||||
const account = getGlobalStorage.string('account.active')
|
||||
if (account) {
|
||||
await setAccount(account)
|
||||
} else {
|
||||
log('log', 'App', 'No active account available')
|
||||
const accounts = getGlobalStorage.object('accounts')
|
||||
if (accounts?.length) {
|
||||
log('log', 'App', `Setting active account ${accounts[accounts.length - 1]}`)
|
||||
await setAccount(accounts[accounts.length - 1])
|
||||
} else {
|
||||
setGlobalStorage('account.active', undefined)
|
||||
}
|
||||
}
|
||||
|
||||
log('log', 'App', `locale: ${Localization.locale}`)
|
||||
const language = getLanguage()
|
||||
if (!language) {
|
||||
if (Platform.OS !== 'ios') {
|
||||
setGlobalStorage('app.language', 'en')
|
||||
}
|
||||
i18n.changeLanguage('en')
|
||||
} else {
|
||||
i18n.changeLanguage(language)
|
||||
}
|
||||
|
||||
setAppIsReady(true)
|
||||
}
|
||||
delaySplash()
|
||||
|
||||
prepare()
|
||||
}, [])
|
||||
|
||||
const onBeforeLift = useCallback(async () => {
|
||||
let netInfoRes = undefined
|
||||
try {
|
||||
netInfoRes = await netInfo()
|
||||
} catch {}
|
||||
|
||||
if (netInfoRes && netInfoRes.corrupted && netInfoRes.corrupted.length) {
|
||||
setLocalCorrupt(netInfoRes.corrupted)
|
||||
}
|
||||
|
||||
log('log', 'App', 'hide splash')
|
||||
try {
|
||||
const onLayoutRootView = useCallback(async () => {
|
||||
if (appIsReady) {
|
||||
log('log', 'App', 'hide splash')
|
||||
await SplashScreen.hideAsync()
|
||||
return Promise.resolve()
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
return Promise.reject()
|
||||
}
|
||||
}, [])
|
||||
}, [appIsReady])
|
||||
if (!appIsReady) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||
<GestureHandlerRootView style={{ flex: 1 }} onLayout={onLayoutRootView}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Provider store={store}>
|
||||
<PersistGate
|
||||
persistor={persistor}
|
||||
onBeforeLift={onBeforeLift}
|
||||
children={bootstrapped => {
|
||||
log('log', 'App', 'bootstrapped')
|
||||
if (bootstrapped) {
|
||||
log('log', 'App', 'loading actual app :)')
|
||||
log('log', 'App', `Locale: ${Localization.locale}`)
|
||||
const language = getLanguage()
|
||||
if (!language) {
|
||||
if (Platform.OS !== 'ios') {
|
||||
store.dispatch(changeLanguage('en'))
|
||||
}
|
||||
i18n.changeLanguage('en')
|
||||
} else {
|
||||
i18n.changeLanguage(language)
|
||||
}
|
||||
|
||||
return (
|
||||
<IntlProvider locale={language}>
|
||||
<SafeAreaProvider>
|
||||
<ActionSheetProvider>
|
||||
<AccessibilityManager>
|
||||
<ThemeManager>
|
||||
<Screens localCorrupt={localCorrupt} />
|
||||
</ThemeManager>
|
||||
</AccessibilityManager>
|
||||
</ActionSheetProvider>
|
||||
</SafeAreaProvider>
|
||||
</IntlProvider>
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Provider>
|
||||
<SafeAreaProvider>
|
||||
<ActionSheetProvider>
|
||||
<AccessibilityManager>
|
||||
<ThemeManager>
|
||||
<Screens />
|
||||
</ThemeManager>
|
||||
</AccessibilityManager>
|
||||
</ActionSheetProvider>
|
||||
</SafeAreaProvider>
|
||||
</QueryClientProvider>
|
||||
</GestureHandlerRootView>
|
||||
)
|
||||
}
|
||||
|
||||
export default Sentry.wrap(App)
|
||||
export default App
|
||||
|
|
348
src/Screens.tsx
348
src/Screens.tsx
|
@ -1,348 +0,0 @@
|
|||
import { HeaderLeft } from '@components/Header'
|
||||
import { displayMessage, Message } from '@components/Message'
|
||||
import navigationRef from '@helpers/navigationRef'
|
||||
import { NavigationContainer } from '@react-navigation/native'
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack'
|
||||
import ScreenAccountSelection from '@screens/AccountSelection'
|
||||
import ScreenActions from '@screens/Actions'
|
||||
import ScreenAnnouncements from '@screens/Announcements'
|
||||
import ScreenCompose from '@screens/Compose'
|
||||
import ScreenImagesViewer from '@screens/ImagesViewer'
|
||||
import ScreenTabs from '@screens/Tabs'
|
||||
import * as Sentry from '@sentry/react-native'
|
||||
import initQuery from '@utils/initQuery'
|
||||
import { RootStackParamList } from '@utils/navigation/navigators'
|
||||
import pushUseConnect from '@utils/push/useConnect'
|
||||
import pushUseReceive from '@utils/push/useReceive'
|
||||
import pushUseRespond from '@utils/push/useRespond'
|
||||
import { updatePreviousTab } from '@utils/slices/contextsSlice'
|
||||
import { checkEmojis } from '@utils/slices/instances/checkEmojis'
|
||||
import { updateAccountPreferences } from '@utils/slices/instances/updateAccountPreferences'
|
||||
import { updateConfiguration } from '@utils/slices/instances/updateConfiguration'
|
||||
import { updateFilters } from '@utils/slices/instances/updateFilters'
|
||||
import { getInstanceActive, getInstances } from '@utils/slices/instancesSlice'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { themes } from '@utils/styles/themes'
|
||||
import * as Linking from 'expo-linking'
|
||||
import { addScreenshotListener } from 'expo-screen-capture'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Alert, Platform, StatusBar } from 'react-native'
|
||||
import ShareMenu from 'react-native-share-menu'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { useAppDispatch } from './store'
|
||||
|
||||
const Stack = createNativeStackNavigator<RootStackParamList>()
|
||||
|
||||
export interface Props {
|
||||
localCorrupt?: string
|
||||
}
|
||||
|
||||
const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
||||
const { t } = useTranslation('screens')
|
||||
const dispatch = useAppDispatch()
|
||||
const instanceActive = useSelector(getInstanceActive)
|
||||
const { colors, theme } = useTheme()
|
||||
|
||||
const routeRef = useRef<{ name?: string; params?: {} }>()
|
||||
|
||||
// Push hooks
|
||||
const instances = useSelector(getInstances, (prev, next) => prev.length === next.length)
|
||||
pushUseConnect()
|
||||
pushUseReceive()
|
||||
pushUseRespond()
|
||||
|
||||
// Prevent screenshot alert
|
||||
useEffect(() => {
|
||||
const screenshotListener = addScreenshotListener(() =>
|
||||
Alert.alert(t('screenshot.title'), t('screenshot.message'), [
|
||||
{ text: t('screenshot.button'), style: 'destructive' }
|
||||
])
|
||||
)
|
||||
Platform.select({ ios: screenshotListener })
|
||||
return () => screenshotListener.remove()
|
||||
}, [])
|
||||
|
||||
// On launch display login credentials corrupt information
|
||||
useEffect(() => {
|
||||
const showLocalCorrect = () => {
|
||||
if (localCorrupt) {
|
||||
displayMessage({
|
||||
message: t('localCorrupt.message'),
|
||||
description: localCorrupt.length ? localCorrupt : undefined,
|
||||
type: 'danger'
|
||||
})
|
||||
// @ts-ignore
|
||||
navigationRef.navigate('Screen-Tabs', {
|
||||
screen: 'Tab-Me'
|
||||
})
|
||||
}
|
||||
}
|
||||
return showLocalCorrect()
|
||||
}, [localCorrupt])
|
||||
|
||||
// Lazily update users's preferences, for e.g. composing default visibility
|
||||
useEffect(() => {
|
||||
if (instanceActive !== -1) {
|
||||
dispatch(updateConfiguration())
|
||||
dispatch(updateFilters())
|
||||
dispatch(updateAccountPreferences())
|
||||
dispatch(checkEmojis())
|
||||
}
|
||||
}, [instanceActive])
|
||||
|
||||
// Callbacks
|
||||
const navigationContainerOnReady = useCallback(() => {
|
||||
const currentRoute = navigationRef.getCurrentRoute()
|
||||
routeRef.current = {
|
||||
name: currentRoute?.name,
|
||||
params: currentRoute?.params ? JSON.stringify(currentRoute.params) : undefined
|
||||
}
|
||||
}, [])
|
||||
const navigationContainerOnStateChange = useCallback(() => {
|
||||
const previousRoute = routeRef.current
|
||||
const currentRoute = navigationRef.getCurrentRoute()
|
||||
|
||||
const matchTabName = currentRoute?.name?.match(/(Tab-.*)-Root/)
|
||||
if (matchTabName) {
|
||||
//@ts-ignore
|
||||
dispatch(updatePreviousTab(matchTabName[1]))
|
||||
}
|
||||
|
||||
if (previousRoute?.name !== currentRoute?.name) {
|
||||
Sentry.setContext('page', {
|
||||
previous: previousRoute,
|
||||
current: currentRoute
|
||||
})
|
||||
}
|
||||
|
||||
routeRef.current = currentRoute
|
||||
}, [])
|
||||
|
||||
// Deep linking for compose
|
||||
const [deeplinked, setDeeplinked] = useState(false)
|
||||
useEffect(() => {
|
||||
const getUrlAsync = async () => {
|
||||
setDeeplinked(true)
|
||||
|
||||
const initialUrl = await Linking.parseInitialURLAsync()
|
||||
|
||||
if (initialUrl.path) {
|
||||
const paths = initialUrl.path.split('/')
|
||||
|
||||
if (paths && paths.length) {
|
||||
const instanceIndex = instances.findIndex(
|
||||
instance => paths[0] === `@${instance.account.acct}@${instance.uri}`
|
||||
)
|
||||
if (instanceIndex !== -1 && instanceActive !== instanceIndex) {
|
||||
initQuery({ instance: instances[instanceIndex] })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (initialUrl.hostname === 'compose') {
|
||||
navigationRef.navigate('Screen-Compose')
|
||||
}
|
||||
}
|
||||
if (!deeplinked) {
|
||||
getUrlAsync()
|
||||
}
|
||||
}, [instanceActive, instances, deeplinked])
|
||||
|
||||
// Share Extension
|
||||
const handleShare = useCallback(
|
||||
(
|
||||
item?:
|
||||
| {
|
||||
data: { mimeType: string; data: string }[]
|
||||
mimeType: undefined
|
||||
}
|
||||
| { data: string | string[]; mimeType: string }
|
||||
) => {
|
||||
if (instanceActive < 0) {
|
||||
return
|
||||
}
|
||||
if (!item || !item.data) {
|
||||
return
|
||||
}
|
||||
|
||||
let text: string | undefined = undefined
|
||||
let media: { uri: string; mime: string }[] = []
|
||||
|
||||
const typesImage = ['png', 'jpg', 'jpeg', 'gif']
|
||||
const typesVideo = ['mp4', 'm4v', 'mov', 'webm', 'mpeg']
|
||||
const filterMedia = ({ uri, mime }: { uri: string; mime: string }) => {
|
||||
if (mime.startsWith('image/')) {
|
||||
if (!typesImage.includes(mime.split('/')[1])) {
|
||||
console.warn('Image type not supported:', mime.split('/')[1])
|
||||
displayMessage({
|
||||
message: t('shareError.imageNotSupported', {
|
||||
type: mime.split('/')[1]
|
||||
}),
|
||||
type: 'danger'
|
||||
})
|
||||
return
|
||||
}
|
||||
media.push({ uri, mime })
|
||||
} else if (mime.startsWith('video/')) {
|
||||
if (!typesVideo.includes(mime.split('/')[1])) {
|
||||
console.warn('Video type not supported:', mime.split('/')[1])
|
||||
displayMessage({
|
||||
message: t('shareError.videoNotSupported', {
|
||||
type: mime.split('/')[1]
|
||||
}),
|
||||
type: 'danger'
|
||||
})
|
||||
return
|
||||
}
|
||||
media.push({ uri, mime })
|
||||
} else {
|
||||
if (typesImage.includes(uri.split('.').pop() || '')) {
|
||||
media.push({ uri, mime: 'image/jpg' })
|
||||
return
|
||||
}
|
||||
if (typesVideo.includes(uri.split('.').pop() || '')) {
|
||||
media.push({ uri, mime: 'video/mp4' })
|
||||
return
|
||||
}
|
||||
text = !text ? uri : text.concat(text, `\n${uri}`)
|
||||
}
|
||||
}
|
||||
|
||||
switch (Platform.OS) {
|
||||
case 'ios':
|
||||
if (!Array.isArray(item.data) || !item.data) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const d of item.data) {
|
||||
if (typeof d !== 'string') {
|
||||
filterMedia({ uri: d.data, mime: d.mimeType })
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'android':
|
||||
if (!item.mimeType) {
|
||||
return
|
||||
}
|
||||
if (Array.isArray(item.data)) {
|
||||
for (const d of item.data) {
|
||||
filterMedia({ uri: d, mime: item.mimeType })
|
||||
}
|
||||
} else {
|
||||
filterMedia({ uri: item.data, mime: item.mimeType })
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if (!text && !media.length) {
|
||||
return
|
||||
} else {
|
||||
if (instances.length > 1) {
|
||||
navigationRef.navigate('Screen-AccountSelection', {
|
||||
share: { text, media }
|
||||
})
|
||||
} else {
|
||||
navigationRef.navigate('Screen-Compose', {
|
||||
type: 'share',
|
||||
text,
|
||||
media
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
[]
|
||||
)
|
||||
useEffect(() => {
|
||||
ShareMenu.getInitialShare(handleShare)
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
const listener = ShareMenu.addNewShareListener(handleShare)
|
||||
return () => {
|
||||
listener.remove()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<StatusBar
|
||||
backgroundColor={colors.backgroundDefault}
|
||||
{...(Platform.OS === 'android' && {
|
||||
barStyle: theme === 'light' ? 'dark-content' : 'light-content'
|
||||
})}
|
||||
/>
|
||||
<NavigationContainer
|
||||
ref={navigationRef}
|
||||
theme={themes[theme]}
|
||||
onReady={navigationContainerOnReady}
|
||||
onStateChange={navigationContainerOnStateChange}
|
||||
>
|
||||
<Stack.Navigator initialRouteName='Screen-Tabs'>
|
||||
<Stack.Screen
|
||||
name='Screen-Tabs'
|
||||
component={ScreenTabs}
|
||||
options={{ headerShown: false }}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name='Screen-Actions'
|
||||
component={ScreenActions}
|
||||
options={{
|
||||
presentation: 'transparentModal',
|
||||
animation: 'fade',
|
||||
headerShown: false
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='Screen-Announcements'
|
||||
component={ScreenAnnouncements}
|
||||
options={({ navigation }) => ({
|
||||
presentation: 'transparentModal',
|
||||
animation: 'fade',
|
||||
headerShown: true,
|
||||
headerShadowVisible: false,
|
||||
headerTransparent: true,
|
||||
headerStyle: { backgroundColor: 'transparent' },
|
||||
headerLeft: () => <HeaderLeft content='X' onPress={() => navigation.goBack()} />,
|
||||
title: t('screenAnnouncements:heading')
|
||||
})}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='Screen-Compose'
|
||||
component={ScreenCompose}
|
||||
options={{
|
||||
headerShown: false,
|
||||
presentation: 'fullScreenModal'
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='Screen-ImagesViewer'
|
||||
component={ScreenImagesViewer}
|
||||
options={{ headerShown: false, animation: 'fade' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name='Screen-AccountSelection'
|
||||
component={ScreenAccountSelection}
|
||||
options={({ navigation }) => ({
|
||||
title: t('screenAccountSelection:heading'),
|
||||
headerShadowVisible: false,
|
||||
presentation: 'modal',
|
||||
gestureEnabled: false,
|
||||
headerLeft: () => (
|
||||
<HeaderLeft
|
||||
type='text'
|
||||
content={t('common:buttons.cancel')}
|
||||
onPress={() => navigation.goBack()}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
|
||||
<Message />
|
||||
</NavigationContainer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(Screens, () => true)
|
|
@ -1,75 +0,0 @@
|
|||
import axios from 'axios'
|
||||
import { ctx, handleError, PagedResponse, userAgent } from './helpers'
|
||||
|
||||
export type Params = {
|
||||
method: 'get' | 'post' | 'put' | 'delete'
|
||||
domain: string
|
||||
url: string
|
||||
params?: {
|
||||
[key: string]: string | number | boolean | string[] | number[] | boolean[]
|
||||
}
|
||||
headers?: { [key: string]: string }
|
||||
body?: FormData | Object
|
||||
}
|
||||
|
||||
const apiGeneral = async <T = unknown>({
|
||||
method,
|
||||
domain,
|
||||
url,
|
||||
params,
|
||||
headers,
|
||||
body
|
||||
}: Params): Promise<PagedResponse<T>> => {
|
||||
console.log(
|
||||
ctx.bgGreen.bold(' API general ') +
|
||||
' ' +
|
||||
domain +
|
||||
' ' +
|
||||
method +
|
||||
ctx.green(' -> ') +
|
||||
`/${url}` +
|
||||
(params ? ctx.green(' -> ') : ''),
|
||||
params ? params : ''
|
||||
)
|
||||
|
||||
return axios({
|
||||
timeout: method === 'post' ? 1000 * 60 : 1000 * 15,
|
||||
method,
|
||||
baseURL: `https://${domain}/`,
|
||||
url,
|
||||
params,
|
||||
headers: {
|
||||
'Content-Type': body && body instanceof FormData ? 'multipart/form-data' : 'application/json',
|
||||
Accept: '*/*',
|
||||
...userAgent,
|
||||
...headers
|
||||
},
|
||||
...(body && { data: body })
|
||||
})
|
||||
.then(response => {
|
||||
let links: {
|
||||
prev?: { id: string; isOffset: boolean }
|
||||
next?: { id: string; isOffset: boolean }
|
||||
} = {}
|
||||
|
||||
if (response.headers?.link) {
|
||||
const linksParsed = response.headers.link.matchAll(
|
||||
new RegExp('[?&](.*?_id|offset)=(.*?)>; *rel="(.*?)"', 'gi')
|
||||
)
|
||||
for (const link of linksParsed) {
|
||||
switch (link[3]) {
|
||||
case 'prev':
|
||||
links.prev = { id: link[2], isOffset: link[1].includes('offset') }
|
||||
break
|
||||
case 'next':
|
||||
links.next = { id: link[2], isOffset: link[1].includes('offset') }
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve({ body: response.data, links })
|
||||
})
|
||||
.catch(handleError())
|
||||
}
|
||||
|
||||
export default apiGeneral
|
|
@ -1,96 +0,0 @@
|
|||
import { RootState } from '@root/store'
|
||||
import axios, { AxiosRequestConfig } from 'axios'
|
||||
import { ctx, handleError, PagedResponse, userAgent } from './helpers'
|
||||
|
||||
export type Params = {
|
||||
method: 'get' | 'post' | 'put' | 'delete' | 'patch'
|
||||
version?: 'v1' | 'v2'
|
||||
url: string
|
||||
params?: {
|
||||
[key: string]: string | number | boolean | string[] | number[] | boolean[]
|
||||
}
|
||||
headers?: { [key: string]: string }
|
||||
body?: FormData
|
||||
extras?: Omit<AxiosRequestConfig, 'method' | 'url' | 'params' | 'headers' | 'data'>
|
||||
}
|
||||
|
||||
const apiInstance = async <T = unknown>({
|
||||
method,
|
||||
version = 'v1',
|
||||
url,
|
||||
params,
|
||||
headers,
|
||||
body,
|
||||
extras
|
||||
}: Params): Promise<PagedResponse<T>> => {
|
||||
const { store } = require('@root/store')
|
||||
const state = store.getState() as RootState
|
||||
const instanceActive = state.instances.instances.findIndex(instance => instance.active)
|
||||
|
||||
let domain
|
||||
let token
|
||||
if (instanceActive !== -1 && state.instances.instances[instanceActive]) {
|
||||
domain = state.instances.instances[instanceActive].url
|
||||
token = state.instances.instances[instanceActive].token
|
||||
} else {
|
||||
console.warn(ctx.bgRed.white.bold(' API ') + ' ' + 'No instance domain is provided')
|
||||
return Promise.reject()
|
||||
}
|
||||
|
||||
console.log(
|
||||
ctx.bgGreen.bold(' API instance ') +
|
||||
' ' +
|
||||
domain +
|
||||
' ' +
|
||||
method +
|
||||
ctx.green(' -> ') +
|
||||
`/${url}` +
|
||||
(params ? ctx.green(' -> ') : ''),
|
||||
params ? params : ''
|
||||
)
|
||||
|
||||
return axios({
|
||||
timeout: method === 'post' ? 1000 * 60 : 1000 * 15,
|
||||
method,
|
||||
baseURL: `https://${domain}/api/${version}/`,
|
||||
url,
|
||||
params,
|
||||
headers: {
|
||||
'Content-Type': body && body instanceof FormData ? 'multipart/form-data' : 'application/json',
|
||||
Accept: '*/*',
|
||||
...userAgent,
|
||||
...headers,
|
||||
...(token && {
|
||||
Authorization: `Bearer ${token}`
|
||||
})
|
||||
},
|
||||
...(body && { data: body }),
|
||||
...extras
|
||||
})
|
||||
.then(response => {
|
||||
let links: {
|
||||
prev?: { id: string; isOffset: boolean }
|
||||
next?: { id: string; isOffset: boolean }
|
||||
} = {}
|
||||
|
||||
if (response.headers?.link) {
|
||||
const linksParsed = response.headers.link.matchAll(
|
||||
new RegExp('[?&](.*?_id|offset)=(.*?)>; *rel="(.*?)"', 'gi')
|
||||
)
|
||||
for (const link of linksParsed) {
|
||||
switch (link[3]) {
|
||||
case 'prev':
|
||||
links.prev = { id: link[2], isOffset: link[1].includes('offset') }
|
||||
break
|
||||
case 'next':
|
||||
links.next = { id: link[2], isOffset: link[1].includes('offset') }
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve({ body: response.data, links })
|
||||
})
|
||||
.catch(handleError())
|
||||
}
|
||||
|
||||
export default apiInstance
|
|
@ -11,7 +11,7 @@ import Icon from './Icon'
|
|||
import CustomText from './Text'
|
||||
|
||||
export interface Props {
|
||||
account: Mastodon.Account
|
||||
account: Partial<Mastodon.Account> & Pick<Mastodon.Account, 'id' | 'acct' | 'username' | 'url'>
|
||||
props?: PressableProps
|
||||
}
|
||||
|
||||
|
@ -38,15 +38,16 @@ const ComponentAccount: React.FC<PropsWithChildren & Props> = ({ account, props,
|
|||
<>
|
||||
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center' }}>
|
||||
<GracefullyImage
|
||||
uri={{ original: account.avatar, static: account.avatar_static }}
|
||||
sources={{ default: { uri: account.avatar }, static: { uri: account.avatar_static } }}
|
||||
style={{
|
||||
width: StyleConstants.Avatar.S,
|
||||
height: StyleConstants.Avatar.S,
|
||||
borderRadius: 6,
|
||||
borderRadius: StyleConstants.BorderRadius,
|
||||
marginRight: StyleConstants.Spacing.S
|
||||
}}
|
||||
dim
|
||||
/>
|
||||
<View>
|
||||
<View style={{ flex: 1 }}>
|
||||
<CustomText numberOfLines={1}>
|
||||
<ParseEmojis
|
||||
content={account.display_name || account.username}
|
||||
|
@ -68,7 +69,7 @@ const ComponentAccount: React.FC<PropsWithChildren & Props> = ({ account, props,
|
|||
</View>
|
||||
{props.onPress && !props.disabled ? (
|
||||
<Icon
|
||||
name='ChevronRight'
|
||||
name='chevron-right'
|
||||
size={StyleConstants.Font.Size.L}
|
||||
color={colors.secondary}
|
||||
style={{ marginLeft: 8 }}
|
||||
|
|
|
@ -1,38 +1,70 @@
|
|||
import { useNavigation } from '@react-navigation/native'
|
||||
import initQuery from '@utils/initQuery'
|
||||
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
||||
import { ReadableAccountType, setAccount } from '@utils/storage/actions'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React from 'react'
|
||||
import Button from './Button'
|
||||
import { Pressable } from 'react-native'
|
||||
import GracefullyImage from './GracefullyImage'
|
||||
import haptics from './haptics'
|
||||
import Icon from './Icon'
|
||||
import CustomText from './Text'
|
||||
|
||||
interface Props {
|
||||
instance: InstanceLatest
|
||||
selected?: boolean
|
||||
account: ReadableAccountType
|
||||
additionalActions?: () => void
|
||||
}
|
||||
|
||||
const AccountButton: React.FC<Props> = ({ instance, selected = false, additionalActions }) => {
|
||||
const AccountButton: React.FC<Props> = ({ account, additionalActions }) => {
|
||||
const { colors } = useTheme()
|
||||
const navigation = useNavigation()
|
||||
|
||||
return (
|
||||
<Button
|
||||
type='text'
|
||||
selected={selected}
|
||||
<Pressable
|
||||
style={{
|
||||
marginBottom: StyleConstants.Spacing.M,
|
||||
marginRight: StyleConstants.Spacing.M
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingVertical: StyleConstants.Spacing.S,
|
||||
paddingHorizontal: StyleConstants.Spacing.S * 1.5,
|
||||
borderColor: account.active ? colors.blue : colors.border,
|
||||
borderWidth: 1,
|
||||
borderRadius: 99,
|
||||
marginBottom: StyleConstants.Spacing.S
|
||||
}}
|
||||
content={`@${instance.account.acct}@${instance.uri}${selected ? ' ✓' : ''}`}
|
||||
onPress={() => {
|
||||
onPress={async () => {
|
||||
await setAccount(account.key)
|
||||
haptics('Light')
|
||||
initQuery({ instance })
|
||||
navigation.goBack()
|
||||
if (additionalActions) {
|
||||
additionalActions()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<GracefullyImage
|
||||
sources={{ default: { uri: account.avatar_static } }}
|
||||
dimension={{
|
||||
width: StyleConstants.Font.Size.L,
|
||||
height: StyleConstants.Font.Size.L
|
||||
}}
|
||||
style={{ borderRadius: 99, overflow: 'hidden' }}
|
||||
/>
|
||||
<CustomText
|
||||
fontStyle='M'
|
||||
fontWeight={account.active ? 'Bold' : 'Normal'}
|
||||
style={{
|
||||
color: account.active ? colors.blue : colors.primaryDefault,
|
||||
marginLeft: StyleConstants.Spacing.S
|
||||
}}
|
||||
children={account.acct}
|
||||
/>
|
||||
{account.active ? (
|
||||
<Icon
|
||||
name='check'
|
||||
size={StyleConstants.Font.Size.L}
|
||||
color={colors.blue}
|
||||
style={{ marginLeft: StyleConstants.Spacing.S }}
|
||||
/>
|
||||
) : null}
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,26 +1,22 @@
|
|||
import Icon from '@components/Icon'
|
||||
import Icon, { IconName } from '@components/Icon'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import { AccessibilityProps, Pressable, StyleProp, View, ViewStyle } from 'react-native'
|
||||
import { Flow } from 'react-native-animated-spinkit'
|
||||
import { Loading } from './Loading'
|
||||
import CustomText from './Text'
|
||||
|
||||
export interface Props {
|
||||
export type Props = {
|
||||
accessibilityLabel?: AccessibilityProps['accessibilityLabel']
|
||||
accessibilityHint?: AccessibilityProps['accessibilityHint']
|
||||
|
||||
style?: StyleProp<ViewStyle>
|
||||
|
||||
type: 'icon' | 'text'
|
||||
content: string
|
||||
|
||||
selected?: boolean
|
||||
loading?: boolean
|
||||
destructive?: boolean
|
||||
disabled?: boolean
|
||||
|
||||
strokeWidth?: number
|
||||
size?: 'S' | 'M' | 'L'
|
||||
fontBold?: boolean
|
||||
spacing?: 'XS' | 'S' | 'M' | 'L'
|
||||
|
@ -28,7 +24,7 @@ export interface Props {
|
|||
overlay?: boolean
|
||||
|
||||
onPress: () => void
|
||||
}
|
||||
} & ({ type: 'icon'; content: IconName } | { type: 'text'; content: string })
|
||||
|
||||
const Button: React.FC<Props> = ({
|
||||
accessibilityLabel,
|
||||
|
@ -40,7 +36,6 @@ const Button: React.FC<Props> = ({
|
|||
loading = false,
|
||||
destructive = false,
|
||||
disabled = false,
|
||||
strokeWidth,
|
||||
size = 'M',
|
||||
fontBold = false,
|
||||
spacing = 'S',
|
||||
|
@ -48,18 +43,16 @@ const Button: React.FC<Props> = ({
|
|||
overlay = false,
|
||||
onPress
|
||||
}) => {
|
||||
const { colors, theme } = useTheme()
|
||||
const { colors } = useTheme()
|
||||
|
||||
const loadingSpinkit = useMemo(
|
||||
() => (
|
||||
const loadingSpinkit = () =>
|
||||
loading ? (
|
||||
<View style={{ position: 'absolute' }}>
|
||||
<Flow size={StyleConstants.Font.Size[size]} color={colors.secondary} />
|
||||
<Loading />
|
||||
</View>
|
||||
),
|
||||
[theme]
|
||||
)
|
||||
) : null
|
||||
|
||||
const mainColor = useMemo(() => {
|
||||
const mainColor = () => {
|
||||
if (selected) {
|
||||
return colors.blue
|
||||
} else if (overlay) {
|
||||
|
@ -73,29 +66,20 @@ const Button: React.FC<Props> = ({
|
|||
return colors.primaryDefault
|
||||
}
|
||||
}
|
||||
}, [theme, disabled, loading, selected])
|
||||
}
|
||||
|
||||
const colorBackground = useMemo(() => {
|
||||
if (overlay) {
|
||||
return colors.backgroundOverlayInvert
|
||||
} else {
|
||||
return colors.backgroundDefault
|
||||
}
|
||||
}, [theme])
|
||||
|
||||
const children = useMemo(() => {
|
||||
const children = () => {
|
||||
switch (type) {
|
||||
case 'icon':
|
||||
return (
|
||||
<>
|
||||
<Icon
|
||||
name={content}
|
||||
color={mainColor}
|
||||
strokeWidth={strokeWidth}
|
||||
color={mainColor()}
|
||||
style={{ opacity: loading ? 0 : 1 }}
|
||||
size={StyleConstants.Font.Size[size] * (size === 'L' ? 1.25 : 1)}
|
||||
/>
|
||||
{loading ? loadingSpinkit : null}
|
||||
{loadingSpinkit()}
|
||||
</>
|
||||
)
|
||||
case 'text':
|
||||
|
@ -103,19 +87,19 @@ const Button: React.FC<Props> = ({
|
|||
<>
|
||||
<CustomText
|
||||
style={{
|
||||
color: mainColor,
|
||||
color: mainColor(),
|
||||
fontSize: StyleConstants.Font.Size[size] * (size === 'L' ? 1.25 : 1),
|
||||
opacity: loading ? 0 : 1
|
||||
}}
|
||||
fontWeight={fontBold ? 'Bold' : 'Normal'}
|
||||
fontWeight={fontBold || selected ? 'Bold' : 'Normal'}
|
||||
children={content}
|
||||
testID='text'
|
||||
/>
|
||||
{loading ? loadingSpinkit : null}
|
||||
{loadingSpinkit()}
|
||||
</>
|
||||
)
|
||||
}
|
||||
}, [theme, content, loading, disabled])
|
||||
}
|
||||
|
||||
const [layoutHeight, setLayoutHeight] = useState<number | undefined>()
|
||||
|
||||
|
@ -132,12 +116,12 @@ const Button: React.FC<Props> = ({
|
|||
}}
|
||||
style={[
|
||||
{
|
||||
borderRadius: 100,
|
||||
borderRadius: 99,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderWidth: overlay ? 0 : 1,
|
||||
borderColor: mainColor,
|
||||
backgroundColor: colorBackground,
|
||||
borderWidth: overlay ? 0 : selected ? 1.5 : 1,
|
||||
borderColor: mainColor(),
|
||||
backgroundColor: overlay ? colors.backgroundOverlayInvert : colors.backgroundDefault,
|
||||
paddingVertical: StyleConstants.Spacing[spacing],
|
||||
paddingHorizontal: StyleConstants.Spacing[spacing] + StyleConstants.Spacing.XS,
|
||||
width: round && layoutHeight ? layoutHeight : undefined
|
||||
|
@ -149,7 +133,7 @@ const Button: React.FC<Props> = ({
|
|||
})}
|
||||
testID='base'
|
||||
onPress={onPress}
|
||||
children={children}
|
||||
children={children()}
|
||||
disabled={selected || disabled || loading}
|
||||
/>
|
||||
)
|
||||
|
|
|
@ -4,7 +4,7 @@ import { StyleConstants } from '@utils/styles/constants'
|
|||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useContext } from 'react'
|
||||
import { Keyboard, Pressable, View } from 'react-native'
|
||||
import EmojisContext from './helpers/EmojisContext'
|
||||
import EmojisContext from './Context'
|
||||
|
||||
const EmojisButton: React.FC = () => {
|
||||
const { colors } = useTheme()
|
||||
|
@ -35,11 +35,11 @@ const EmojisButton: React.FC = () => {
|
|||
borderWidth: 2,
|
||||
borderColor: colors.primaryDefault,
|
||||
padding: StyleConstants.Spacing.Global.PagePadding / 2,
|
||||
borderRadius: 100
|
||||
borderRadius: 99
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name={emojis.current && emojis.current.length ? 'Smile' : 'Meh'}
|
||||
name={emojis.current && emojis.current.length ? 'smile' : 'meh'}
|
||||
size={24}
|
||||
color={
|
||||
emojis.current && emojis.current.length ? colors.primaryDefault : colors.disabled
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { emojis } from '@components/Emojis'
|
||||
import Icon from '@components/Icon'
|
||||
import CustomText from '@components/Text'
|
||||
import { useAppDispatch } from '@root/store'
|
||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||
import { countInstanceEmoji } from '@utils/slices/instancesSlice'
|
||||
import { connectMedia } from '@utils/api/helpers/connect'
|
||||
import { StorageAccount } from '@utils/storage/account'
|
||||
import { getAccountStorage, setAccountStorage } from '@utils/storage/actions'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import layoutAnimation from '@utils/styles/layoutAnimation'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { Image } from 'expo-image'
|
||||
import { chunk } from 'lodash'
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
@ -18,17 +20,14 @@ import {
|
|||
TextInput,
|
||||
View
|
||||
} from 'react-native'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import validUrl from 'valid-url'
|
||||
import EmojisContext from './helpers/EmojisContext'
|
||||
import EmojisContext from './Context'
|
||||
|
||||
const EmojisList = () => {
|
||||
const dispatch = useAppDispatch()
|
||||
const { reduceMotionEnabled } = useAccessibility()
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation(['common', 'screenCompose'])
|
||||
|
||||
const { emojisState, emojisDispatch } = useContext(EmojisContext)
|
||||
const { colors, mode } = useTheme()
|
||||
const { colors } = useTheme()
|
||||
|
||||
const addEmoji = (shortcode: string) => {
|
||||
if (emojisState.targetIndex === -1) {
|
||||
|
@ -69,31 +68,77 @@ const EmojisList = () => {
|
|||
>
|
||||
{item.map(emoji => {
|
||||
const uri = reduceMotionEnabled ? emoji.static_url : emoji.url
|
||||
if (validUrl.isHttpsUri(uri)) {
|
||||
return (
|
||||
<Pressable
|
||||
key={emoji.shortcode}
|
||||
onPress={() => {
|
||||
addEmoji(`:${emoji.shortcode}:`)
|
||||
dispatch(countInstanceEmoji(emoji))
|
||||
}}
|
||||
style={{ padding: StyleConstants.Spacing.S }}
|
||||
>
|
||||
<FastImage
|
||||
accessibilityLabel={t('common:customEmoji.accessibilityLabel', {
|
||||
emoji: emoji.shortcode
|
||||
})}
|
||||
accessibilityHint={t(
|
||||
'screenCompose:content.root.footer.emojis.accessibilityHint'
|
||||
)}
|
||||
source={{ uri }}
|
||||
style={{ width: 32, height: 32 }}
|
||||
/>
|
||||
</Pressable>
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<Pressable
|
||||
key={emoji.shortcode}
|
||||
onPress={() => {
|
||||
addEmoji(`:${emoji.shortcode}:`)
|
||||
|
||||
const HALF_LIFE = 60 * 60 * 24 * 7 // 1 week
|
||||
const calculateScore = (
|
||||
emoji: StorageAccount['emojis_frequent'][number]
|
||||
): number => {
|
||||
var seconds = (new Date().getTime() - emoji.lastUsed) / 1000
|
||||
var score = emoji.count + 1
|
||||
var order = Math.log(Math.max(score, 1)) / Math.LN10
|
||||
var sign = score > 0 ? 1 : score === 0 ? 0 : -1
|
||||
return (sign * order + seconds / HALF_LIFE) * 10
|
||||
}
|
||||
|
||||
const currentEmojis = getAccountStorage.object('emojis_frequent')
|
||||
const foundEmojiIndex = currentEmojis?.findIndex(
|
||||
e => e.emoji.shortcode === emoji.shortcode && e.emoji.url === emoji.url
|
||||
)
|
||||
|
||||
let newEmojisSort: StorageAccount['emojis_frequent']
|
||||
if (foundEmojiIndex === -1) {
|
||||
newEmojisSort = currentEmojis || []
|
||||
const temp = {
|
||||
emoji,
|
||||
score: 0,
|
||||
count: 0,
|
||||
lastUsed: new Date().getTime()
|
||||
}
|
||||
newEmojisSort.push({
|
||||
...temp,
|
||||
score: calculateScore(temp),
|
||||
count: temp.count + 1
|
||||
})
|
||||
} else {
|
||||
newEmojisSort =
|
||||
currentEmojis
|
||||
?.map((e, i) =>
|
||||
i === foundEmojiIndex
|
||||
? {
|
||||
...e,
|
||||
score: calculateScore(e),
|
||||
count: e.count + 1,
|
||||
lastUsed: new Date().getTime()
|
||||
}
|
||||
: e
|
||||
)
|
||||
.sort((a, b) => b.score - a.score) || []
|
||||
}
|
||||
|
||||
setAccountStorage([
|
||||
{
|
||||
key: 'emojis_frequent',
|
||||
value: newEmojisSort.sort((a, b) => b.score - a.score).slice(0, 20)
|
||||
}
|
||||
])
|
||||
}}
|
||||
style={{ padding: StyleConstants.Spacing.S }}
|
||||
>
|
||||
<Image
|
||||
accessibilityLabel={t('common:customEmoji.accessibilityLabel', {
|
||||
emoji: emoji.shortcode
|
||||
})}
|
||||
accessibilityHint={t('screenCompose:content.root.footer.emojis.accessibilityHint')}
|
||||
source={connectMedia({ uri })}
|
||||
style={{ width: 32, height: 32 }}
|
||||
/>
|
||||
</Pressable>
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
)
|
||||
|
@ -144,7 +189,7 @@ const EmojisList = () => {
|
|||
paddingRight: StyleConstants.Spacing.S
|
||||
}}
|
||||
>
|
||||
<Icon name='Search' size={StyleConstants.Font.Size.L} color={colors.secondary} />
|
||||
<Icon name='search' size={StyleConstants.Font.Size.L} color={colors.secondary} />
|
||||
</View>
|
||||
<TextInput
|
||||
style={{
|
||||
|
@ -158,7 +203,6 @@ const EmojisList = () => {
|
|||
onChangeText={setSearch}
|
||||
autoCapitalize='none'
|
||||
clearButtonMode='always'
|
||||
keyboardAppearance={mode}
|
||||
autoCorrect={false}
|
||||
spellCheck={false}
|
||||
/>
|
||||
|
@ -171,7 +215,7 @@ const EmojisList = () => {
|
|||
emojisDispatch({ type: 'target', payload: -1 })
|
||||
}}
|
||||
>
|
||||
<Icon name='ChevronDown' size={StyleConstants.Font.Size.L} color={colors.secondary} />
|
||||
<Icon name='chevron-down' size={StyleConstants.Font.Size.L} color={colors.secondary} />
|
||||
</Pressable>
|
||||
</View>
|
||||
<SectionList
|
||||
|
@ -205,7 +249,6 @@ const EmojisList = () => {
|
|||
</CustomText>
|
||||
)}
|
||||
renderItem={listItem}
|
||||
windowSize={4}
|
||||
contentContainerStyle={{
|
||||
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||
minHeight: 32 * 2 + StyleConstants.Spacing.M * 3
|
||||
|
|
|
@ -2,15 +2,13 @@ import EmojisButton from '@components/Emojis/Button'
|
|||
import EmojisList from '@components/Emojis/List'
|
||||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||
import { useEmojisQuery } from '@utils/queryHooks/emojis'
|
||||
import { getInstanceFrequentEmojis } from '@utils/slices/instancesSlice'
|
||||
import { useAccountStorage } from '@utils/storage/actions'
|
||||
import { chunk, forEach, groupBy, sortBy } from 'lodash'
|
||||
import React, { createRef, PropsWithChildren, useEffect, useReducer, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Keyboard, KeyboardAvoidingView, View } from 'react-native'
|
||||
import FastImage from 'react-native-fast-image'
|
||||
import { Edge, SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||
import { useSelector } from 'react-redux'
|
||||
import EmojisContext, { Emojis, emojisReducer, EmojisState } from './Emojis/helpers/EmojisContext'
|
||||
import EmojisContext, { Emojis, emojisReducer, EmojisState } from './Context'
|
||||
|
||||
export type Props = {
|
||||
inputProps: EmojisState['inputProps']
|
||||
|
@ -35,9 +33,9 @@ const ComponentEmojis: React.FC<Props & PropsWithChildren> = ({
|
|||
emojisDispatch({ type: 'input', payload: inputProps })
|
||||
}, [inputProps])
|
||||
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation(['componentEmojis'])
|
||||
const { data } = useEmojisQuery({})
|
||||
const frequentEmojis = useSelector(getInstanceFrequentEmojis, () => true)
|
||||
const [frequentEmojis] = useAccountStorage.object('emojis_frequent')
|
||||
useEffect(() => {
|
||||
if (data && data.length) {
|
||||
let sortedEmojis: NonNullable<Emojis['current']> = []
|
|
@ -0,0 +1,110 @@
|
|||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { Fragment } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Pressable, View, ViewStyle } from 'react-native'
|
||||
import Icon from './Icon'
|
||||
import CustomText from './Text'
|
||||
|
||||
export type Props = {
|
||||
onPress: () => void
|
||||
filter: Mastodon.Filter<'v2'>
|
||||
button?: React.ReactNode
|
||||
style?: ViewStyle
|
||||
}
|
||||
|
||||
export const Filter: React.FC<Props> = ({ onPress, filter, button, style }) => {
|
||||
const { t } = useTranslation(['common', 'screenTabs'])
|
||||
const { colors } = useTheme()
|
||||
|
||||
return (
|
||||
<Pressable onPress={onPress}>
|
||||
<View
|
||||
style={{
|
||||
paddingVertical: StyleConstants.Spacing.S,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: colors.backgroundDefault,
|
||||
...style
|
||||
}}
|
||||
>
|
||||
<View style={{ flex: 1 }}>
|
||||
<CustomText
|
||||
fontStyle='M'
|
||||
children={filter.title}
|
||||
style={{ color: colors.primaryDefault }}
|
||||
numberOfLines={1}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginVertical: StyleConstants.Spacing.XS
|
||||
}}
|
||||
>
|
||||
{filter.expires_at && new Date() > new Date(filter.expires_at) ? (
|
||||
<CustomText
|
||||
fontStyle='S'
|
||||
fontWeight='Bold'
|
||||
children={t('screenTabs:me.preferencesFilters.expired')}
|
||||
style={{ color: colors.red, marginRight: StyleConstants.Spacing.M }}
|
||||
/>
|
||||
) : null}
|
||||
{filter.keywords?.length ? (
|
||||
<CustomText
|
||||
children={t('screenTabs:me.preferencesFilters.keywords', {
|
||||
count: filter.keywords.length
|
||||
})}
|
||||
style={{ color: colors.primaryDefault }}
|
||||
/>
|
||||
) : null}
|
||||
{filter.keywords?.length && filter.statuses?.length ? (
|
||||
<CustomText
|
||||
children={t('common:separator')}
|
||||
style={{ color: colors.primaryDefault }}
|
||||
/>
|
||||
) : null}
|
||||
{filter.statuses?.length ? (
|
||||
<CustomText
|
||||
children={t('screenTabs:me.preferencesFilters.statuses', {
|
||||
count: filter.statuses.length
|
||||
})}
|
||||
style={{ color: colors.primaryDefault }}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
<CustomText
|
||||
style={{ color: colors.secondary }}
|
||||
children={
|
||||
<Trans
|
||||
ns='screenTabs'
|
||||
i18nKey='me.preferencesFilters.context'
|
||||
components={[
|
||||
<>
|
||||
{filter.context.map((c, index) => (
|
||||
<Fragment key={index}>
|
||||
<CustomText
|
||||
style={{ color: colors.secondary }}
|
||||
children={t(`screenTabs:me.preferencesFilters.contexts.${c}`)}
|
||||
/>
|
||||
<CustomText children={t('common:separator')} />
|
||||
</Fragment>
|
||||
))}
|
||||
</>
|
||||
]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
{button || (
|
||||
<Icon
|
||||
name='chevron-right'
|
||||
size={StyleConstants.Font.Size.L}
|
||||
color={colors.primaryDefault}
|
||||
style={{ marginLeft: 8 }}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</Pressable>
|
||||
)
|
||||
}
|
|
@ -1,122 +1,95 @@
|
|||
import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
|
||||
import { connectMedia } from '@utils/api/helpers/connect'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import {
|
||||
AccessibilityProps,
|
||||
Image,
|
||||
Pressable,
|
||||
StyleProp,
|
||||
StyleSheet,
|
||||
View,
|
||||
ViewStyle
|
||||
} from 'react-native'
|
||||
import FastImage, { ImageStyle } from 'react-native-fast-image'
|
||||
import { Blurhash } from 'react-native-blurhash'
|
||||
|
||||
// blurhas -> if blurhash, show before any loading succeed
|
||||
// original -> load original
|
||||
// original, remote -> if original failed, then remote
|
||||
// preview, original -> first show preview, then original
|
||||
// preview, original, remote -> first show preview, then original, if original failed, then remote
|
||||
import { Image, ImageContentFit, ImageSource, ImageStyle } from 'expo-image'
|
||||
import React, { useState } from 'react'
|
||||
import { AccessibilityProps, Pressable, StyleProp, View, ViewStyle } from 'react-native'
|
||||
|
||||
export interface Props {
|
||||
accessibilityLabel?: AccessibilityProps['accessibilityLabel']
|
||||
accessibilityHint?: AccessibilityProps['accessibilityHint']
|
||||
|
||||
hidden?: boolean
|
||||
uri: { preview?: string; original?: string; remote?: string; static?: string }
|
||||
blurhash?: string
|
||||
sources: {
|
||||
preview?: ImageSource
|
||||
default?: ImageSource
|
||||
remote?: ImageSource
|
||||
static?: ImageSource
|
||||
blurhash?: string
|
||||
}
|
||||
dimension?: { width: number; height: number }
|
||||
onPress?: () => void
|
||||
style?: StyleProp<ViewStyle>
|
||||
imageStyle?: StyleProp<ImageStyle>
|
||||
// For image viewer when there is no image size available
|
||||
setImageDimensions?: React.Dispatch<
|
||||
React.SetStateAction<{
|
||||
width: number
|
||||
height: number
|
||||
}>
|
||||
>
|
||||
imageStyle?: ImageStyle
|
||||
contentFit?: ImageContentFit
|
||||
|
||||
dim?: boolean
|
||||
withoutTransition?: boolean
|
||||
enableLiveTextInteraction?: boolean
|
||||
}
|
||||
|
||||
const GracefullyImage = ({
|
||||
accessibilityLabel,
|
||||
accessibilityHint,
|
||||
hidden = false,
|
||||
uri,
|
||||
blurhash,
|
||||
sources,
|
||||
dimension,
|
||||
onPress,
|
||||
style,
|
||||
imageStyle,
|
||||
setImageDimensions
|
||||
contentFit,
|
||||
dim,
|
||||
withoutTransition = false,
|
||||
enableLiveTextInteraction = false
|
||||
}: Props) => {
|
||||
const { reduceMotionEnabled } = useAccessibility()
|
||||
const { colors } = useTheme()
|
||||
const [originalFailed, setOriginalFailed] = useState(false)
|
||||
const [imageLoaded, setImageLoaded] = useState(false)
|
||||
const { theme } = useTheme()
|
||||
|
||||
const source = originalFailed
|
||||
? { uri: uri.remote || undefined }
|
||||
: {
|
||||
uri: reduceMotionEnabled && uri.static ? uri.static : uri.original
|
||||
}
|
||||
|
||||
const onLoad = () => {
|
||||
setImageLoaded(true)
|
||||
if (setImageDimensions && source.uri) {
|
||||
Image.getSize(source.uri, (width, height) => setImageDimensions({ width, height }))
|
||||
}
|
||||
}
|
||||
const onError = () => {
|
||||
if (!originalFailed) {
|
||||
setOriginalFailed(true)
|
||||
}
|
||||
}
|
||||
|
||||
const blurhashView = useMemo(() => {
|
||||
if (hidden || !imageLoaded) {
|
||||
if (blurhash) {
|
||||
return <Blurhash decodeAsync blurhash={blurhash} style={styles.placeholder} />
|
||||
} else {
|
||||
return <View style={[styles.placeholder, { backgroundColor: colors.shimmerDefault }]} />
|
||||
}
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}, [hidden, imageLoaded])
|
||||
const [currentSource, setCurrentSource] = useState<ImageSource | undefined>(
|
||||
sources.default || sources.remote
|
||||
)
|
||||
const source: ImageSource | undefined =
|
||||
reduceMotionEnabled && sources.static ? sources.static : currentSource
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
{...(onPress ? { accessibilityRole: 'imagebutton' } : { accessibilityRole: 'image' })}
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
accessibilityHint={accessibilityHint}
|
||||
style={[style, dimension, { backgroundColor: colors.shimmerDefault }]}
|
||||
style={[style, dimension]}
|
||||
{...(onPress ? (hidden ? { disabled: true } : { onPress }) : { disabled: true })}
|
||||
>
|
||||
{uri.preview && !imageLoaded ? (
|
||||
<FastImage
|
||||
source={{ uri: uri.preview }}
|
||||
style={[styles.placeholder, { backgroundColor: colors.shimmerDefault }]}
|
||||
<Image
|
||||
placeholderContentFit='cover'
|
||||
placeholder={hidden ? sources.blurhash : sources.blurhash || connectMedia(sources.preview)}
|
||||
source={hidden ? sources.blurhash : connectMedia(source)}
|
||||
{...(!withoutTransition && !reduceMotionEnabled && { transition: { duration: 120 } })}
|
||||
style={{ flex: 1, ...imageStyle }}
|
||||
contentFit={contentFit}
|
||||
onError={() => {
|
||||
if (
|
||||
sources.default?.uri &&
|
||||
sources.default?.uri === currentSource?.uri &&
|
||||
sources.remote
|
||||
) {
|
||||
setCurrentSource(sources.remote)
|
||||
}
|
||||
}}
|
||||
enableLiveTextInteraction={enableLiveTextInteraction}
|
||||
/>
|
||||
{dim && theme !== 'light' ? (
|
||||
<View
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
backgroundColor: 'black',
|
||||
opacity: theme === 'dark_lighter' ? 0.18 : 0.36
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<FastImage
|
||||
source={source}
|
||||
style={[{ flex: 1 }, imageStyle]}
|
||||
onLoad={onLoad}
|
||||
onError={onError}
|
||||
/>
|
||||
{blurhashView}
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
placeholder: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'absolute'
|
||||
}
|
||||
})
|
||||
|
||||
export default GracefullyImage
|
||||
|
|
|
@ -3,10 +3,11 @@ import { StackNavigationProp } from '@react-navigation/stack'
|
|||
import { TabLocalStackParamList } from '@utils/navigation/navigators'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { PropsWithChildren, useCallback, useState } from 'react'
|
||||
import { Dimensions, Pressable, View } from 'react-native'
|
||||
import React, { PropsWithChildren, useState } from 'react'
|
||||
import { Dimensions, Pressable, Text, View } from 'react-native'
|
||||
import Sparkline from './Sparkline'
|
||||
import CustomText from './Text'
|
||||
import { sumBy } from 'lodash'
|
||||
|
||||
export interface Props {
|
||||
hashtag: Mastodon.Tag
|
||||
|
@ -21,14 +22,16 @@ const ComponentHashtag: React.FC<PropsWithChildren & Props> = ({
|
|||
const { colors } = useTheme()
|
||||
const navigation = useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||
|
||||
const onPress = useCallback(() => {
|
||||
navigation.push('Tab-Shared-Hashtag', { hashtag: hashtag.name })
|
||||
}, [])
|
||||
const onPress = () => {
|
||||
navigation.push('Tab-Shared-Hashtag', { tag_name: hashtag.name })
|
||||
}
|
||||
|
||||
const padding = StyleConstants.Spacing.Global.PagePadding
|
||||
const width = Dimensions.get('window').width / 4
|
||||
const [height, setHeight] = useState<number>(0)
|
||||
|
||||
const sum = sumBy(hashtag.history, h => parseInt(h.uses))
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
accessibilityRole='button'
|
||||
|
@ -41,33 +44,43 @@ const ComponentHashtag: React.FC<PropsWithChildren & Props> = ({
|
|||
}}
|
||||
onPress={customOnPress || onPress}
|
||||
>
|
||||
<CustomText
|
||||
fontStyle='M'
|
||||
<View
|
||||
style={{
|
||||
flexShrink: 1,
|
||||
color: colors.primaryDefault,
|
||||
paddingRight: StyleConstants.Spacing.M
|
||||
}}
|
||||
numberOfLines={1}
|
||||
>
|
||||
#{hashtag.name}
|
||||
</CustomText>
|
||||
<View
|
||||
style={{ flexDirection: 'row', alignItems: 'center' }}
|
||||
onLayout={({
|
||||
nativeEvent: {
|
||||
layout: { height }
|
||||
}
|
||||
}) => setHeight(height)}
|
||||
>
|
||||
<Sparkline
|
||||
data={hashtag.history.map(h => parseInt(h.uses)).reverse()}
|
||||
width={width}
|
||||
height={height}
|
||||
margin={children ? StyleConstants.Spacing.S : undefined}
|
||||
/>
|
||||
{children}
|
||||
<CustomText fontStyle='M' style={{ color: colors.primaryDefault }} numberOfLines={1}>
|
||||
#{hashtag.name}
|
||||
{sum ? (
|
||||
<>
|
||||
{' '}
|
||||
<CustomText fontStyle='S' style={{ color: colors.secondary }}>
|
||||
({sumBy(hashtag.history, h => parseInt(h.uses))})
|
||||
</CustomText>
|
||||
</>
|
||||
) : null}
|
||||
</CustomText>
|
||||
</View>
|
||||
{hashtag.history?.length ? (
|
||||
<View
|
||||
style={{ flexDirection: 'row', alignItems: 'center', alignSelf: 'stretch' }}
|
||||
onLayout={({
|
||||
nativeEvent: {
|
||||
layout: { height }
|
||||
}
|
||||
}) => setHeight(height)}
|
||||
>
|
||||
<Sparkline
|
||||
data={hashtag.history.map(h => parseInt(h.uses)).reverse()}
|
||||
width={width}
|
||||
height={height}
|
||||
margin={children ? StyleConstants.Spacing.S : undefined}
|
||||
color={!sum ? colors.disabled : undefined}
|
||||
/>
|
||||
{children}
|
||||
</View>
|
||||
) : null}
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,67 +1,59 @@
|
|||
import Icon from '@components/Icon'
|
||||
import Icon, { IconName } from '@components/Icon'
|
||||
import CustomText from '@components/Text'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useMemo } from 'react'
|
||||
import React from 'react'
|
||||
import { Pressable } from 'react-native'
|
||||
|
||||
export interface Props {
|
||||
type?: 'icon' | 'text'
|
||||
content?: string
|
||||
export type Props = {
|
||||
native?: boolean
|
||||
background?: boolean
|
||||
|
||||
onPress: () => void
|
||||
}
|
||||
onPress?: () => void
|
||||
} & ({ type?: undefined; content?: IconName } | { type: 'text'; content: string })
|
||||
|
||||
const HeaderLeft: React.FC<Props> = ({
|
||||
type = 'icon',
|
||||
type,
|
||||
content,
|
||||
native = true,
|
||||
background = false,
|
||||
onPress
|
||||
}) => {
|
||||
const { colors, theme } = useTheme()
|
||||
const navigation = useNavigation()
|
||||
const { colors } = useTheme()
|
||||
|
||||
const children = useMemo(() => {
|
||||
const children = () => {
|
||||
switch (type) {
|
||||
case 'icon':
|
||||
case 'text':
|
||||
return (
|
||||
<CustomText fontStyle='M' style={{ color: colors.primaryDefault }} children={content} />
|
||||
)
|
||||
default:
|
||||
return (
|
||||
<Icon
|
||||
color={colors.primaryDefault}
|
||||
name={content || 'ChevronLeft'}
|
||||
name={content || 'chevron-left'}
|
||||
size={StyleConstants.Spacing.M * 1.25}
|
||||
/>
|
||||
)
|
||||
case 'text':
|
||||
return (
|
||||
<CustomText
|
||||
fontStyle='M'
|
||||
style={{ color: colors.primaryDefault }}
|
||||
children={content}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}, [theme])
|
||||
}
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
onPress={onPress}
|
||||
children={children}
|
||||
onPress={onPress ? onPress : () => navigation.goBack()}
|
||||
children={children()}
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: background
|
||||
? colors.backgroundOverlayDefault
|
||||
: undefined,
|
||||
backgroundColor: background ? colors.backgroundOverlayDefault : undefined,
|
||||
minHeight: 44,
|
||||
minWidth: 44,
|
||||
marginLeft: native
|
||||
? -StyleConstants.Spacing.S
|
||||
: StyleConstants.Spacing.S,
|
||||
...(type === 'icon' && {
|
||||
borderRadius: 100
|
||||
marginLeft: native ? -StyleConstants.Spacing.S : StyleConstants.Spacing.S,
|
||||
...(type === undefined && {
|
||||
borderRadius: 99
|
||||
}),
|
||||
...(type === 'text' && {
|
||||
paddingHorizontal: StyleConstants.Spacing.S
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
import Icon from '@components/Icon'
|
||||
import Icon, { IconName } from '@components/Icon'
|
||||
import { Loading } from '@components/Loading'
|
||||
import CustomText from '@components/Text'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { useMemo } from 'react'
|
||||
import React from 'react'
|
||||
import { AccessibilityProps, Pressable, View } from 'react-native'
|
||||
import { Flow } from 'react-native-animated-spinkit'
|
||||
|
||||
export interface Props {
|
||||
export type Props = {
|
||||
accessibilityLabel?: string
|
||||
accessibilityHint?: string
|
||||
accessibilityState?: AccessibilityProps['accessibilityState']
|
||||
|
||||
type?: 'icon' | 'text'
|
||||
content: string
|
||||
native?: boolean
|
||||
background?: boolean
|
||||
|
||||
loading?: boolean
|
||||
disabled?: boolean
|
||||
destructive?: boolean
|
||||
destructiveColor?: string
|
||||
|
||||
onPress: () => void
|
||||
}
|
||||
} & ({ type?: undefined; content: IconName } | { type: 'text'; content: string })
|
||||
|
||||
const HeaderRight: React.FC<Props> = ({
|
||||
// Accessibility - Start
|
||||
|
@ -28,58 +28,60 @@ const HeaderRight: React.FC<Props> = ({
|
|||
accessibilityHint,
|
||||
accessibilityState,
|
||||
// Accessibility - End
|
||||
type = 'icon',
|
||||
type,
|
||||
content,
|
||||
native = true,
|
||||
background = false,
|
||||
loading,
|
||||
disabled,
|
||||
destructive = false,
|
||||
destructiveColor,
|
||||
onPress
|
||||
}) => {
|
||||
const { colors, theme } = useTheme()
|
||||
const { colors } = useTheme()
|
||||
|
||||
const loadingSpinkit = useMemo(
|
||||
() => (
|
||||
const loadingSpinkit = () =>
|
||||
loading ? (
|
||||
<View style={{ position: 'absolute' }}>
|
||||
<Flow
|
||||
size={StyleConstants.Font.Size.M * 1.25}
|
||||
color={colors.secondary}
|
||||
/>
|
||||
<Loading />
|
||||
</View>
|
||||
),
|
||||
[theme]
|
||||
)
|
||||
) : null
|
||||
|
||||
const children = useMemo(() => {
|
||||
const children = () => {
|
||||
switch (type) {
|
||||
case 'icon':
|
||||
case 'text':
|
||||
return (
|
||||
<>
|
||||
<CustomText
|
||||
fontStyle='M'
|
||||
fontWeight={destructive ? 'Bold' : 'Normal'}
|
||||
style={{
|
||||
color: disabled
|
||||
? colors.secondary
|
||||
: destructive
|
||||
? destructiveColor || colors.red
|
||||
: colors.primaryDefault,
|
||||
opacity: loading ? 0 : 1
|
||||
}}
|
||||
children={content}
|
||||
/>
|
||||
{loadingSpinkit()}
|
||||
</>
|
||||
)
|
||||
default:
|
||||
return (
|
||||
<>
|
||||
<Icon
|
||||
name={content}
|
||||
style={{ opacity: loading ? 0 : 1 }}
|
||||
size={StyleConstants.Spacing.M * 1.25}
|
||||
color={disabled ? colors.secondary : colors.primaryDefault}
|
||||
color={disabled ? colors.secondary : destructive ? colors.red : colors.primaryDefault}
|
||||
/>
|
||||
{loading && loadingSpinkit}
|
||||
</>
|
||||
)
|
||||
case 'text':
|
||||
return (
|
||||
<>
|
||||
<CustomText
|
||||
fontStyle='M'
|
||||
style={{
|
||||
color: disabled ? colors.secondary : colors.primaryDefault,
|
||||
opacity: loading ? 0 : 1
|
||||
}}
|
||||
children={content}
|
||||
/>
|
||||
{loading && loadingSpinkit}
|
||||
{loadingSpinkit()}
|
||||
</>
|
||||
)
|
||||
}
|
||||
}, [theme, loading, disabled])
|
||||
}
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
|
@ -88,23 +90,17 @@ const HeaderRight: React.FC<Props> = ({
|
|||
accessibilityRole='button'
|
||||
accessibilityState={accessibilityState}
|
||||
onPress={onPress}
|
||||
children={children}
|
||||
children={children()}
|
||||
disabled={disabled || loading}
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: background
|
||||
? colors.backgroundOverlayDefault
|
||||
: undefined,
|
||||
backgroundColor: background ? colors.backgroundOverlayDefault : undefined,
|
||||
minHeight: 44,
|
||||
minWidth: 44,
|
||||
marginRight: native
|
||||
? -StyleConstants.Spacing.S
|
||||
: StyleConstants.Spacing.S,
|
||||
...(type === 'icon' && {
|
||||
borderRadius: 100
|
||||
}),
|
||||
marginRight: native ? -StyleConstants.Spacing.S : StyleConstants.Spacing.S,
|
||||
...(type === undefined && { borderRadius: 99 }),
|
||||
...(type === 'text' && {
|
||||
paddingHorizontal: StyleConstants.Spacing.S
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import HeaderLeft from '@components/Header/Left'
|
||||
import HeaderCenter from '@components/Header/Center'
|
||||
import HeaderLeft from '@components/Header/Left'
|
||||
import HeaderRight from '@components/Header/Right'
|
||||
|
||||
export { HeaderLeft, HeaderCenter, HeaderRight }
|
|
@ -0,0 +1,23 @@
|
|||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { View, ViewStyle } from 'react-native'
|
||||
|
||||
const Hr: React.FC<{ style?: ViewStyle }> = ({ style }) => {
|
||||
const { colors } = useTheme()
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
{
|
||||
borderTopColor: colors.border,
|
||||
borderTopWidth: 1,
|
||||
height: 1,
|
||||
paddingVertical: StyleConstants.Spacing.S
|
||||
},
|
||||
style
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default Hr
|
|
@ -1,15 +1,16 @@
|
|||
import React, { createElement } from 'react'
|
||||
import FeatherNames from '@expo/vector-icons/build/vendor/react-native-vector-icons/glyphmaps/Feather.json'
|
||||
import Feather from '@expo/vector-icons/Feather'
|
||||
import React from 'react'
|
||||
import { AccessibilityProps, StyleProp, View, ViewStyle } from 'react-native'
|
||||
import * as FeatherIcon from 'react-native-feather'
|
||||
|
||||
export type IconName = keyof typeof FeatherNames
|
||||
|
||||
export interface Props {
|
||||
accessibilityLabel?: AccessibilityProps['accessibilityLabel']
|
||||
|
||||
name: string
|
||||
name: IconName
|
||||
size: number
|
||||
color: string
|
||||
fill?: string
|
||||
strokeWidth?: number
|
||||
style?: StyleProp<ViewStyle>
|
||||
crossOut?: boolean
|
||||
}
|
||||
|
@ -19,8 +20,6 @@ const Icon: React.FC<Props> = ({
|
|||
name,
|
||||
size,
|
||||
color,
|
||||
fill,
|
||||
strokeWidth = 2,
|
||||
style,
|
||||
crossOut = false
|
||||
}) => {
|
||||
|
@ -37,13 +36,7 @@ const Icon: React.FC<Props> = ({
|
|||
}
|
||||
]}
|
||||
>
|
||||
{createElement(FeatherIcon[name], {
|
||||
width: size,
|
||||
height: size,
|
||||
color,
|
||||
fill,
|
||||
strokeWidth
|
||||
})}
|
||||
<Feather name={name} size={size} color={color} />
|
||||
{crossOut ? (
|
||||
<View
|
||||
style={{
|
||||
|
@ -51,7 +44,7 @@ const Icon: React.FC<Props> = ({
|
|||
transform: [{ rotate: '45deg' }],
|
||||
width: size * 1.35,
|
||||
borderBottomColor: color,
|
||||
borderBottomWidth: strokeWidth
|
||||
borderBottomWidth: 2
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React, { forwardRef, RefObject } from 'react'
|
||||
import React, { ForwardedRef, forwardRef } from 'react'
|
||||
import { Platform, TextInput, TextInputProps, View } from 'react-native'
|
||||
import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated'
|
||||
import { EmojisState } from './Emojis/helpers/EmojisContext'
|
||||
import { EmojisState } from './Emojis/Context'
|
||||
import CustomText from './Text'
|
||||
|
||||
export type Props = {
|
||||
title?: string
|
||||
multiline?: boolean
|
||||
} & Pick<NonNullable<EmojisState['inputProps'][0]>, 'value' | 'selection' | 'isFocused'> &
|
||||
invalid?: boolean
|
||||
} & Pick<NonNullable<EmojisState['inputProps'][0]>, 'value'> &
|
||||
Pick<Partial<EmojisState['inputProps'][0]>, 'isFocused' | 'selection'> &
|
||||
Omit<
|
||||
TextInputProps,
|
||||
| 'style'
|
||||
|
@ -27,12 +29,13 @@ const ComponentInput = forwardRef(
|
|||
{
|
||||
title,
|
||||
multiline = false,
|
||||
invalid = false,
|
||||
value: [value, setValue],
|
||||
selection: [selection, setSelection],
|
||||
selection,
|
||||
isFocused,
|
||||
...props
|
||||
}: Props,
|
||||
ref: RefObject<TextInput>
|
||||
ref: ForwardedRef<TextInput>
|
||||
) => {
|
||||
const { colors, mode } = useTheme()
|
||||
|
||||
|
@ -43,7 +46,7 @@ const ComponentInput = forwardRef(
|
|||
paddingHorizontal: withTiming(StyleConstants.Spacing.XS),
|
||||
left: withTiming(StyleConstants.Spacing.S),
|
||||
top: withTiming(-(StyleConstants.Font.Size.S / 2) - 2),
|
||||
backgroundColor: withTiming(colors.backgroundDefault)
|
||||
backgroundColor: colors.backgroundDefault
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
|
@ -62,7 +65,7 @@ const ComponentInput = forwardRef(
|
|||
borderWidth: 1,
|
||||
marginVertical: StyleConstants.Spacing.S,
|
||||
padding: StyleConstants.Spacing.S,
|
||||
borderColor: colors.border,
|
||||
borderColor: invalid ? colors.red : colors.border,
|
||||
flexDirection: multiline ? 'column' : 'row',
|
||||
alignItems: 'stretch'
|
||||
}}
|
||||
|
@ -78,14 +81,17 @@ const ComponentInput = forwardRef(
|
|||
}}
|
||||
value={value}
|
||||
onChangeText={setValue}
|
||||
onFocus={() => (isFocused.current = true)}
|
||||
onBlur={() => (isFocused.current = false)}
|
||||
onSelectionChange={({ nativeEvent }) => setSelection(nativeEvent.selection)}
|
||||
{...(isFocused !== undefined && {
|
||||
onFocus: () => (isFocused.current = true),
|
||||
onBlur: () => (isFocused.current = false)
|
||||
})}
|
||||
{...(selection !== undefined && {
|
||||
onSelectionChange: ({ nativeEvent }) => selection[1](nativeEvent.selection)
|
||||
})}
|
||||
{...(multiline && {
|
||||
multiline,
|
||||
numberOfLines: Platform.OS === 'android' ? 5 : undefined
|
||||
})}
|
||||
keyboardAppearance={mode}
|
||||
textAlignVertical='top'
|
||||
{...props}
|
||||
/>
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
import CustomText from '@components/Text'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React from 'react'
|
||||
import { View, ViewStyle } from 'react-native'
|
||||
import { PlaceholderLine } from 'rn-placeholder'
|
||||
|
||||
export interface Props {
|
||||
style?: ViewStyle
|
||||
header: string
|
||||
content?: string
|
||||
potentialWidth?: number
|
||||
}
|
||||
|
||||
const InstanceInfo = React.memo(
|
||||
({ style, header, content, potentialWidth }: Props) => {
|
||||
const { colors } = useTheme()
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
{
|
||||
flex: 1,
|
||||
marginTop: StyleConstants.Spacing.M,
|
||||
paddingLeft: StyleConstants.Spacing.Global.PagePadding,
|
||||
paddingRight: StyleConstants.Spacing.Global.PagePadding
|
||||
},
|
||||
style
|
||||
]}
|
||||
accessible
|
||||
>
|
||||
<CustomText
|
||||
fontStyle='S'
|
||||
style={{
|
||||
marginBottom: StyleConstants.Spacing.XS,
|
||||
color: colors.primaryDefault
|
||||
}}
|
||||
fontWeight='Bold'
|
||||
children={header}
|
||||
/>
|
||||
{content ? (
|
||||
<CustomText
|
||||
fontStyle='M'
|
||||
style={{ color: colors.primaryDefault }}
|
||||
children={content}
|
||||
/>
|
||||
) : (
|
||||
<PlaceholderLine
|
||||
width={
|
||||
potentialWidth
|
||||
? potentialWidth * StyleConstants.Font.Size.M
|
||||
: undefined
|
||||
}
|
||||
height={StyleConstants.Font.LineHeight.M}
|
||||
color={colors.shimmerDefault}
|
||||
noMargin
|
||||
style={{ borderRadius: 0 }}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
},
|
||||
(prev, next) => prev.content === next.content
|
||||
)
|
||||
|
||||
export default InstanceInfo
|
|
@ -1,27 +1,35 @@
|
|||
import Button from '@components/Button'
|
||||
import Icon from '@components/Icon'
|
||||
import browserPackage from '@helpers/browserPackage'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import apiGeneral from '@utils/api/general'
|
||||
import browserPackage from '@utils/helpers/browserPackage'
|
||||
import { featureCheck } from '@utils/helpers/featureCheck'
|
||||
import { TabMeStackNavigationProp } from '@utils/navigation/navigators'
|
||||
import { queryClient } from '@utils/queryHooks'
|
||||
import { redirectUri, useAppsMutation } from '@utils/queryHooks/apps'
|
||||
import { useInstanceQuery } from '@utils/queryHooks/instance'
|
||||
import { checkInstanceFeature, getInstances } from '@utils/slices/instancesSlice'
|
||||
import { StorageAccount } from '@utils/storage/account'
|
||||
import {
|
||||
generateAccountKey,
|
||||
getGlobalStorage,
|
||||
setAccount,
|
||||
setAccountStorage,
|
||||
setGlobalStorage
|
||||
} from '@utils/storage/actions'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import * as AuthSession from 'expo-auth-session'
|
||||
import * as WebBrowser from 'expo-web-browser'
|
||||
import * as Crypto from 'expo-crypto'
|
||||
import { Image } from 'expo-image'
|
||||
import * as Linking from 'expo-linking'
|
||||
import { debounce } from 'lodash'
|
||||
import React, { RefObject, useCallback, useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Alert, Image, KeyboardAvoidingView, Platform, TextInput, View } from 'react-native'
|
||||
import { Alert, KeyboardAvoidingView, Platform, TextInput, View } from 'react-native'
|
||||
import { ScrollView } from 'react-native-gesture-handler'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { Placeholder } from 'rn-placeholder'
|
||||
import InstanceInfo from './Info'
|
||||
import { fromByteArray } from 'react-native-quick-base64'
|
||||
import CustomText from '../Text'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { TabMeStackNavigationProp } from '@utils/navigation/navigators'
|
||||
import queryClient from '@helpers/queryClient'
|
||||
import { useAppDispatch } from '@root/store'
|
||||
import addInstance from '@utils/slices/instances/add'
|
||||
import openLink from '@components/openLink'
|
||||
|
||||
export interface Props {
|
||||
scrollViewRef?: RefObject<ScrollView>
|
||||
|
@ -34,21 +42,31 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
disableHeaderImage,
|
||||
goBack = false
|
||||
}) => {
|
||||
const { t } = useTranslation('componentInstance')
|
||||
const { t } = useTranslation(['common', 'componentInstance'])
|
||||
const { colors, mode } = useTheme()
|
||||
const navigation = useNavigation<TabMeStackNavigationProp<'Tab-Me-Root' | 'Tab-Me-Switch'>>()
|
||||
|
||||
const [domain, setDomain] = useState<string>('')
|
||||
const [errorCode, setErrorCode] = useState<number | null>(null)
|
||||
const whitelisted: boolean =
|
||||
!!domain.length &&
|
||||
!!errorCode &&
|
||||
!!(Linking.parse(`https://${domain}/`).hostname === domain) &&
|
||||
(errorCode === 401 || errorCode === 500)
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
const instances = useSelector(getInstances, () => true)
|
||||
const instanceQuery = useInstanceQuery({
|
||||
domain,
|
||||
options: { enabled: !!domain, retry: false }
|
||||
options: {
|
||||
enabled: !!domain,
|
||||
retry: false,
|
||||
onError: err => {
|
||||
if (err.status) {
|
||||
setErrorCode(err.status)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const deprecateAuthFollow = useSelector(checkInstanceFeature('deprecate_auth_follow'))
|
||||
|
||||
const appsMutation = useAppsMutation({
|
||||
retry: false,
|
||||
onSuccess: async (data, variables) => {
|
||||
|
@ -60,59 +78,156 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
const request = new AuthSession.AuthRequest({
|
||||
clientId,
|
||||
clientSecret,
|
||||
scopes: deprecateAuthFollow
|
||||
? ['read', 'write', 'push']
|
||||
: ['read', 'write', 'follow', 'push'],
|
||||
redirectUri
|
||||
scopes: variables.scopes,
|
||||
redirectUri,
|
||||
usePKCE: !['pawoo.net'].includes(domain)
|
||||
})
|
||||
await request.makeAuthUrlAsync(discovery)
|
||||
|
||||
const promptResult = await request.promptAsync(discovery)
|
||||
const promptResult = await request.promptAsync(discovery, await browserPackage())
|
||||
|
||||
if (promptResult?.type === 'success') {
|
||||
if (promptResult.type === 'success') {
|
||||
const { accessToken } = await AuthSession.exchangeCodeAsync(
|
||||
{
|
||||
clientId,
|
||||
clientSecret,
|
||||
scopes: ['read', 'write', 'follow', 'push'],
|
||||
scopes: variables.scopes,
|
||||
redirectUri,
|
||||
code: promptResult.params.code,
|
||||
extraParams: { grant_type: 'authorization_code' }
|
||||
extraParams: {
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
grant_type: 'authorization_code',
|
||||
...(request.codeVerifier && { code_verifier: request.codeVerifier })
|
||||
}
|
||||
},
|
||||
{ tokenEndpoint: `https://${variables.domain}/oauth/token` }
|
||||
)
|
||||
queryClient.clear()
|
||||
dispatch(
|
||||
addInstance({
|
||||
|
||||
const {
|
||||
body: { id, acct, avatar_static }
|
||||
} = await apiGeneral<Mastodon.Account>({
|
||||
method: 'get',
|
||||
domain,
|
||||
url: `api/v1/accounts/verify_credentials`,
|
||||
headers: { Authorization: `Bearer ${accessToken}` }
|
||||
})
|
||||
|
||||
const accounts = getGlobalStorage.object('accounts')
|
||||
const accountKey = generateAccountKey({ domain, id })
|
||||
const account = accounts?.find(account => account === accountKey)
|
||||
|
||||
const accountDetails: StorageAccount = {
|
||||
'auth.clientId': clientId,
|
||||
'auth.clientSecret': clientSecret,
|
||||
'auth.token': accessToken,
|
||||
'auth.domain': domain,
|
||||
'auth.account.id': id,
|
||||
'auth.account.acct': acct,
|
||||
'auth.account.domain':
|
||||
(instanceQuery.data as Mastodon.Instance_V2)?.domain ||
|
||||
instanceQuery.data?.account_domain ||
|
||||
((instanceQuery.data as Mastodon.Instance_V1)?.uri
|
||||
? Linking.parse((instanceQuery.data as Mastodon.Instance_V1).uri).hostname
|
||||
: undefined) ||
|
||||
(instanceQuery.data as Mastodon.Instance_V1)?.uri ||
|
||||
domain,
|
||||
token: accessToken,
|
||||
instance: instanceQuery.data!,
|
||||
appData: { clientId, clientSecret }
|
||||
})
|
||||
'auth.account.avatar_static': avatar_static,
|
||||
version: instanceQuery.data?.version || '0',
|
||||
preferences: undefined,
|
||||
notifications: {
|
||||
follow: true,
|
||||
follow_request: true,
|
||||
favourite: true,
|
||||
reblog: true,
|
||||
mention: true,
|
||||
poll: true,
|
||||
status: true,
|
||||
update: true,
|
||||
'admin.sign_up': true,
|
||||
'admin.report': true
|
||||
},
|
||||
push: {
|
||||
global: false,
|
||||
decode: false,
|
||||
alerts: {
|
||||
follow: true,
|
||||
follow_request: true,
|
||||
favourite: true,
|
||||
reblog: true,
|
||||
mention: true,
|
||||
poll: true,
|
||||
status: true,
|
||||
update: true,
|
||||
'admin.sign_up': false,
|
||||
'admin.report': false
|
||||
},
|
||||
key: fromByteArray(Crypto.getRandomBytes(16))
|
||||
},
|
||||
page_local: {
|
||||
showBoosts: true,
|
||||
showReplies: true
|
||||
},
|
||||
page_me: {
|
||||
followedTags: { shown: false },
|
||||
lists: { shown: false },
|
||||
announcements: { shown: false, unread: 0 }
|
||||
},
|
||||
page_account_timeline: {
|
||||
excludeBoosts: true,
|
||||
excludeReplies: true
|
||||
},
|
||||
drafts: [],
|
||||
emojis_frequent: []
|
||||
}
|
||||
|
||||
setAccountStorage(
|
||||
Object.keys(accountDetails).map((key: keyof StorageAccount) => ({
|
||||
key,
|
||||
value: accountDetails[key]
|
||||
})),
|
||||
accountKey
|
||||
)
|
||||
|
||||
if (!account) {
|
||||
setGlobalStorage('accounts', (accounts || []).concat([accountKey]))
|
||||
}
|
||||
await setAccount(accountKey)
|
||||
|
||||
goBack && navigation.goBack()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const processUpdate = useCallback(() => {
|
||||
const scopes = () =>
|
||||
featureCheck('deprecate_auth_follow', instanceQuery.data?.version)
|
||||
? ['read', 'write', 'push']
|
||||
: ['read', 'write', 'follow', 'push']
|
||||
|
||||
if (domain) {
|
||||
if (instances && instances.filter(instance => instance.url === domain).length) {
|
||||
Alert.alert(t('update.alert.title'), t('update.alert.message'), [
|
||||
{
|
||||
text: t('common:buttons.cancel'),
|
||||
style: 'cancel'
|
||||
},
|
||||
{
|
||||
text: t('common:buttons.continue'),
|
||||
onPress: () => appsMutation.mutate({ domain })
|
||||
}
|
||||
])
|
||||
const accounts = getGlobalStorage.object('accounts')
|
||||
if (accounts?.filter(account => account.startsWith(`${domain}/`)).length) {
|
||||
Alert.alert(
|
||||
t('componentInstance:update.alert.title'),
|
||||
t('componentInstance:update.alert.message'),
|
||||
[
|
||||
{
|
||||
text: t('common:buttons.cancel'),
|
||||
style: 'cancel'
|
||||
},
|
||||
{
|
||||
text: t('common:buttons.continue'),
|
||||
onPress: () => appsMutation.mutate({ domain, scopes: scopes() })
|
||||
}
|
||||
]
|
||||
)
|
||||
} else {
|
||||
appsMutation.mutate({ domain })
|
||||
appsMutation.mutate({ domain, scopes: scopes() })
|
||||
}
|
||||
}
|
||||
}, [domain])
|
||||
}, [domain, instanceQuery.data?.version])
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
|
@ -123,16 +238,11 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
<View style={{ flexDirection: 'row' }}>
|
||||
<Image
|
||||
source={require('assets/images/welcome.png')}
|
||||
style={{ resizeMode: 'contain', flex: 1, aspectRatio: 16 / 9 }}
|
||||
style={{ flex: 1, aspectRatio: 16 / 9 }}
|
||||
/>
|
||||
</View>
|
||||
) : null}
|
||||
<View
|
||||
style={{
|
||||
marginTop: StyleConstants.Spacing.L,
|
||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding
|
||||
}}
|
||||
>
|
||||
<View style={{ marginTop: StyleConstants.Spacing.L }}>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
|
@ -146,7 +256,11 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
borderBottomWidth: 1,
|
||||
...StyleConstants.FontStyle.M,
|
||||
color: colors.primaryDefault,
|
||||
borderBottomColor: instanceQuery.isError ? colors.red : colors.border,
|
||||
borderBottomColor: instanceQuery.isError
|
||||
? whitelisted
|
||||
? colors.yellow
|
||||
: colors.red
|
||||
: colors.border,
|
||||
...(Platform.OS === 'android' && { paddingRight: 0 })
|
||||
}}
|
||||
editable={false}
|
||||
|
@ -159,12 +273,23 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
...StyleConstants.FontStyle.M,
|
||||
marginRight: StyleConstants.Spacing.M,
|
||||
color: colors.primaryDefault,
|
||||
borderBottomColor: instanceQuery.isError ? colors.red : colors.border,
|
||||
borderBottomColor: instanceQuery.isError
|
||||
? whitelisted
|
||||
? colors.yellow
|
||||
: colors.red
|
||||
: colors.border,
|
||||
...(Platform.OS === 'android' && { paddingLeft: 0 })
|
||||
}}
|
||||
onChangeText={debounce(text => setDomain(text.replace(/^http(s)?\:\/\//i, '')), 1000, {
|
||||
trailing: true
|
||||
})}
|
||||
onChangeText={debounce(
|
||||
text => {
|
||||
setDomain(text.replace(/^http(s)?\:\/\//i, ''))
|
||||
setErrorCode(null)
|
||||
},
|
||||
1000,
|
||||
{
|
||||
trailing: true
|
||||
}
|
||||
)}
|
||||
autoCapitalize='none'
|
||||
clearButtonMode='never'
|
||||
keyboardType='url'
|
||||
|
@ -174,12 +299,13 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
text === domain &&
|
||||
instanceQuery.isSuccess &&
|
||||
instanceQuery.data &&
|
||||
instanceQuery.data.uri
|
||||
// @ts-ignore
|
||||
(instanceQuery.data.domain || instanceQuery.data.uri)
|
||||
) {
|
||||
processUpdate()
|
||||
}
|
||||
}}
|
||||
placeholder={' ' + t('server.textInput.placeholder')}
|
||||
placeholder={' ' + t('componentInstance:server.textInput.placeholder')}
|
||||
placeholderTextColor={colors.secondary}
|
||||
returnKeyType='go'
|
||||
keyboardAppearance={mode}
|
||||
|
@ -192,41 +318,27 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
/>
|
||||
<Button
|
||||
type='text'
|
||||
content={t('server.button')}
|
||||
content={t('componentInstance:server.button')}
|
||||
onPress={processUpdate}
|
||||
disabled={!instanceQuery.data?.uri}
|
||||
// @ts-ignore
|
||||
disabled={!(instanceQuery.data?.domain || instanceQuery.data?.uri) && !whitelisted}
|
||||
loading={instanceQuery.isFetching || appsMutation.isLoading}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View>
|
||||
<Placeholder>
|
||||
<InstanceInfo
|
||||
header={t('server.information.name')}
|
||||
content={instanceQuery.data?.title || undefined}
|
||||
potentialWidth={2}
|
||||
/>
|
||||
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||
<InstanceInfo
|
||||
style={{ alignItems: 'flex-start' }}
|
||||
header={t('server.information.accounts')}
|
||||
content={instanceQuery.data?.stats?.user_count?.toString() || undefined}
|
||||
potentialWidth={4}
|
||||
/>
|
||||
<InstanceInfo
|
||||
style={{ alignItems: 'center' }}
|
||||
header={t('server.information.statuses')}
|
||||
content={instanceQuery.data?.stats?.status_count?.toString() || undefined}
|
||||
potentialWidth={4}
|
||||
/>
|
||||
<InstanceInfo
|
||||
style={{ alignItems: 'flex-end' }}
|
||||
header={t('server.information.domains')}
|
||||
content={instanceQuery.data?.stats?.domain_count?.toString() || undefined}
|
||||
potentialWidth={4}
|
||||
/>
|
||||
</View>
|
||||
</Placeholder>
|
||||
{whitelisted ? (
|
||||
<CustomText
|
||||
fontStyle='S'
|
||||
style={{
|
||||
color: colors.yellow,
|
||||
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||
paddingTop: StyleConstants.Spacing.XS
|
||||
}}
|
||||
>
|
||||
{t('componentInstance:server.whitelisted')}
|
||||
</CustomText>
|
||||
) : null}
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
|
@ -235,7 +347,7 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
}}
|
||||
>
|
||||
<Icon
|
||||
name='Lock'
|
||||
name='lock'
|
||||
size={StyleConstants.Font.Size.S}
|
||||
color={colors.secondary}
|
||||
style={{
|
||||
|
@ -244,7 +356,7 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
}}
|
||||
/>
|
||||
<CustomText fontStyle='S' style={{ flex: 1, color: colors.secondary }}>
|
||||
{t('server.disclaimer.base')}
|
||||
{t('componentInstance:server.disclaimer.base')}
|
||||
</CustomText>
|
||||
</View>
|
||||
<View
|
||||
|
@ -255,7 +367,7 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
}}
|
||||
>
|
||||
<Icon
|
||||
name='CheckSquare'
|
||||
name='check-square'
|
||||
size={StyleConstants.Font.Size.S}
|
||||
color={colors.secondary}
|
||||
style={{
|
||||
|
@ -269,25 +381,18 @@ const ComponentInstance: React.FC<Props> = ({
|
|||
accessibilityRole='link'
|
||||
>
|
||||
<Trans
|
||||
i18nKey='componentInstance:server.terms.base'
|
||||
ns='componentInstance'
|
||||
i18nKey='server.terms.base'
|
||||
components={[
|
||||
<CustomText
|
||||
accessible
|
||||
style={{ color: colors.blue }}
|
||||
onPress={async () =>
|
||||
WebBrowser.openBrowserAsync('https://tooot.app/privacy-policy', {
|
||||
...(await browserPackage())
|
||||
})
|
||||
}
|
||||
onPress={async () => openLink('https://tooot.app/privacy-policy')}
|
||||
/>,
|
||||
<CustomText
|
||||
accessible
|
||||
style={{ color: colors.blue }}
|
||||
onPress={async () =>
|
||||
WebBrowser.openBrowserAsync('https://tooot.app/terms-of-service', {
|
||||
...(await browserPackage())
|
||||
})
|
||||
}
|
||||
onPress={async () => openLink('https://tooot.app/terms-of-service')}
|
||||
/>
|
||||
]}
|
||||
/>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { ActivityIndicator, ViewProps } from 'react-native'
|
||||
|
||||
export type Props = {
|
||||
size?: 'small' | 'large'
|
||||
} & ViewProps
|
||||
|
||||
export const Loading: React.FC<Props> = ({ size = 'small', ...rest }) => {
|
||||
const { colors } = useTheme()
|
||||
|
||||
return <ActivityIndicator size={size} color={colors.secondary} {...rest} />
|
||||
}
|
|
@ -1,17 +1,19 @@
|
|||
import React from 'react'
|
||||
import { View } from 'react-native'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import React from 'react'
|
||||
import { View, ViewStyle } from 'react-native'
|
||||
|
||||
export interface Props {
|
||||
style?: ViewStyle
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const MenuContainer: React.FC<Props> = ({ children }) => {
|
||||
const MenuContainer: React.FC<Props> = ({ style, children }) => {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
paddingHorizontal: StyleConstants.Spacing.Global.PagePadding,
|
||||
marginBottom: StyleConstants.Spacing.Global.PagePadding
|
||||
marginBottom: StyleConstants.Spacing.Global.PagePadding,
|
||||
...style
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react'
|
||||
import { View } from 'react-native'
|
||||
import CustomText from '@components/Text'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import CustomText from '@components/Text'
|
||||
import React from 'react'
|
||||
import { View } from 'react-native'
|
||||
|
||||
export interface Props {
|
||||
heading: string
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue