Compare commits

...

61 Commits

Author SHA1 Message Date
xmflsct 6644ca64d7
Merge pull request #327 from tooot-app/main
Final testing of v4.1
2022-06-09 23:28:13 +02:00
Zhiyuan Zheng 0864f7373a Update README.md 2022-06-09 23:27:35 +02:00
xmflsct b3cdc0964f
New Crowdin updates (#324)
* New translations timeline.json (Portuguese, Brazilian)

* New translations tabs.json (Portuguese, Brazilian)

* New translations mediaSelector.json (Portuguese, Brazilian)

* New translations contextMenu.json (Portuguese, Brazilian)

* New translations compose.json (Portuguese, Brazilian)
2022-06-09 23:25:36 +02:00
Zhiyuan Zheng db46ccc3d8 Fix weird error reportings 2022-06-09 23:25:01 +02:00
Zhiyuan Zheng e33e8550f6 Prepare for future media needs
NOT USED
2022-06-09 22:57:54 +02:00
Zhiyuan Zheng b5a5ce01a3 Using proper image transformation 2022-06-09 21:33:10 +02:00
Zhiyuan Zheng bd5a0948cf Update fastlane 2022-06-09 19:24:58 +02:00
xmflsct 315990ee6c
Merge branch 'candidate' into main 2022-06-09 01:49:35 +02:00
Zhiyuan Zheng 50c8e149c6 Prefer local language detection 2022-06-09 01:47:38 +02:00
Zhiyuan Zheng e37d5991cf Refine photo upload messaging 2022-06-08 21:36:18 +02:00
Zhiyuan Zheng 6f414d38be Fixed #321 2022-06-07 23:01:59 +02:00
Zhiyuan Zheng dcfc87a2d3 Merge branch 'main' of https://github.com/tooot-app/app 2022-06-07 22:23:04 +02:00
xmflsct d9019b787b
New Crowdin updates (#319)
* New translations actions.json (Portuguese, Brazilian)

* New translations timeline.json (Portuguese, Brazilian)

* New translations actions.json (Portuguese, Brazilian)

* New translations compose.json (Portuguese, Brazilian)

* New translations tabs.json (Portuguese, Brazilian)

* New translations actions.json (Vietnamese)

* New translations timeline.json (German)

* New translations mediaSelector.json (Italian)

* New translations contextMenu.json (Vietnamese)

* New translations contextMenu.json (Chinese Traditional)

* New translations contextMenu.json (Chinese Simplified)

* New translations contextMenu.json (Korean)

* New translations contextMenu.json (Italian)

* New translations contextMenu.json (German)

* New translations mediaSelector.json (Portuguese, Brazilian)

* New translations timeline.json (Portuguese, Brazilian)

* New translations timeline.json (Italian)

* New translations mediaSelector.json (German)

* New translations mediaSelector.json (Vietnamese)

* New translations mediaSelector.json (Chinese Traditional)

* New translations mediaSelector.json (Chinese Simplified)

* New translations mediaSelector.json (Korean)

* New translations timeline.json (Chinese Traditional)

* New translations timeline.json (Vietnamese)

* New translations timeline.json (Chinese Simplified)

* New translations timeline.json (Korean)

* New translations contextMenu.json (Portuguese, Brazilian)

* New translations mediaSelector.json (Vietnamese)

* New translations contextMenu.json (Vietnamese)

* New translations contextMenu.json (Vietnamese)

* New translations mediaSelector.json (Chinese Simplified)

* New translations contextMenu.json (German)

* New translations contextMenu.json (Italian)

* New translations contextMenu.json (Korean)

* New translations contextMenu.json (Chinese Simplified)

* New translations contextMenu.json (Portuguese, Brazilian)
2022-06-07 22:22:08 +02:00
Zhiyuan Zheng 901ba2de98 Bump packages 2022-06-07 21:53:51 +02:00
Zhiyuan Zheng af6679ceb4 Try out notification sound 2022-06-07 21:39:41 +02:00
Zhiyuan Zheng ff5aa45a90 Update .gitignore 2022-06-07 21:24:19 +02:00
Zhiyuan Zheng 78ad95a9cb File format changes by `expo prebuild` 2022-06-07 21:23:59 +02:00
Zhiyuan Zheng 0991d50240 Fixed #158 2022-06-07 20:07:14 +02:00
Zhiyuan Zheng 23bc9aa5f3 Merge branch 'release' 2022-06-06 22:50:23 +02:00
Zhiyuan Zheng faebd555e8 New context menu largely working 2022-06-06 22:49:43 +02:00
Zhiyuan Zheng baec474c5f Update build.yml 2022-06-06 14:22:51 +02:00
Zhiyuan Zheng 0cf7d0daf2 Update Expo workflow 2022-06-06 13:09:47 +02:00
Zhiyuan Zheng b18ccc5490 Fix badge not cleared on app launch 2022-06-06 12:42:38 +02:00
Zhiyuan Zheng c0d2da3f65 Fixed #137 2022-06-05 21:17:39 +02:00
Zhiyuan Zheng b13ae5dafe Fixed #117 2022-06-05 18:52:33 +02:00
Zhiyuan Zheng fc8fdec12f Fixed #108 2022-06-05 17:58:18 +02:00
xmflsct 84deb2ba58
Merge pull request #318 from tooot-app/main
Release v4.0.4
2022-06-03 23:49:59 +02:00
Zhiyuan Zheng d6768c0f6f Merge branch 'main' of https://github.com/tooot-app/app 2022-06-03 23:46:18 +02:00
xmflsct d2cb1774b4
Merge pull request #317 from tooot-app/l10n_main
New Crowdin updates
2022-06-03 23:46:10 +02:00
xmflsct a56f8c4026 New translations actions.json (Chinese Simplified) 2022-06-03 23:44:48 +02:00
Zhiyuan Zheng 7c48b31241 Bump packages 2022-06-03 23:44:27 +02:00
xmflsct 8915fcbd89 New translations actions.json (Portuguese, Brazilian) 2022-06-03 23:31:49 +02:00
xmflsct 84cd0186b7 New translations actions.json (Italian) 2022-06-03 23:31:48 +02:00
xmflsct 1fb359110d New translations actions.json (Vietnamese) 2022-06-03 23:31:47 +02:00
xmflsct bfa91676b1 New translations actions.json (Chinese Traditional) 2022-06-03 23:31:46 +02:00
xmflsct 7ba8c34c24 New translations actions.json (Chinese Simplified) 2022-06-03 23:31:45 +02:00
xmflsct 7ff43c06ee New translations actions.json (Korean) 2022-06-03 23:31:44 +02:00
xmflsct 5d788be27a New translations actions.json (German) 2022-06-03 23:31:43 +02:00
xmflsct d1b43e0fce
Merge pull request #304 from tooot-app/main
Try to fix Android storage issue
2022-05-19 00:47:13 +02:00
xmflsct 6dad65631f
Merge pull request #300 from tooot-app/main
Release v4.0.2
2022-05-18 00:27:47 +02:00
xmflsct 028ee92d64
Merge pull request #296 from tooot-app/main
Patch v4.0.1
2022-05-16 23:54:50 +02:00
Zhiyuan Zheng c2a537b929 Merge branch 'main' into release 2022-05-15 14:31:56 +02:00
Zhiyuan Zheng 36d1ee9fef Merge branch 'main' into release 2022-05-15 11:51:01 +02:00
xmflsct b407d66758
Merge pull request #294 from tooot-app/main
Release v4
2022-05-15 01:27:04 +02:00
xmflsct 824314faad
Merge pull request #293 from tooot-app/main
Another run for v4
2022-05-13 19:23:12 +02:00
xmflsct b82f5b04e1
Merge pull request #288 from tooot-app/main
Retry release v4
2022-05-13 11:45:14 +02:00
xmflsct ec853d04a6
Merge pull request #287 from tooot-app/main
Release v4
2022-05-13 10:54:23 +02:00
xmflsct e19fdc5201
Merge pull request #243 from tooot-app/main
Release v3.5.4
2022-02-23 00:09:02 +01:00
xmflsct 44374ce091
Merge pull request #236 from tooot-app/main
Release v3.5.3
2022-02-19 18:24:50 +01:00
xmflsct 6654adc50c
Merge pull request #234 from tooot-app/main
Release v3.5.2
2022-02-17 23:18:54 +01:00
xmflsct e78f8263da
Merge pull request #232 from tooot-app/main
Fix theme changing issue
2022-02-17 00:13:26 +01:00
xmflsct bf49c03c06
Merge pull request #230 from tooot-app/main
Release v3.5
2022-02-14 23:44:26 +01:00
xmflsct 047438104f
Merge pull request #227 from tooot-app/main
Update translation
2022-02-13 01:29:27 +01:00
xmflsct eb87fe689b
Merge pull request #226 from tooot-app/main
Patch v3.4.4
2022-02-12 22:17:21 +01:00
xmflsct 50815bc1c1
Merge pull request #224 from tooot-app/main
Release v3.4.4
2022-02-12 19:22:25 +01:00
xmflsct a457e9e78e
Merge pull request #221 from tooot-app/main
Try to fix toot id not found
2022-02-11 21:38:33 +01:00
xmflsct 182826d047
Merge pull request #220 from tooot-app/main
Release v3.4.2
2022-02-10 23:18:48 +01:00
xmflsct c068f9934f
Merge pull request #219 from tooot-app/main
Release v3.4.2
2022-02-10 23:16:40 +01:00
xmflsct 1dd9474d49
Merge pull request #214 from tooot-app/main
Release v3.4.1
2022-02-08 22:52:48 +01:00
xmflsct 751c7f0186
Merge pull request #213 from tooot-app/main
Final release of v3.4
2022-02-08 10:09:56 +01:00
xmflsct 76bf2e693c
Merge pull request #212 from tooot-app/main
Release v3.4
2022-02-07 23:06:48 +01:00
29 changed files with 349 additions and 97 deletions

View File

@ -17,16 +17,16 @@ GEM
artifactory (3.0.15) artifactory (3.0.15)
atomos (0.1.3) atomos (0.1.3)
aws-eventstream (1.2.0) aws-eventstream (1.2.0)
aws-partitions (1.582.0) aws-partitions (1.598.0)
aws-sdk-core (3.130.2) aws-sdk-core (3.131.1)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0) aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
jmespath (~> 1.0) jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.56.0) aws-sdk-kms (1.57.0)
aws-sdk-core (~> 3, >= 3.127.0) aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.113.2) aws-sdk-s3 (1.114.0)
aws-sdk-core (~> 3, >= 3.127.0) aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (~> 1) aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4) aws-sigv4 (~> 1.4)
@ -106,8 +106,8 @@ GEM
faraday-em_synchrony (1.0.0) faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0) faraday-excon (1.1.0)
faraday-httpclient (1.0.1) faraday-httpclient (1.0.1)
faraday-multipart (1.0.3) faraday-multipart (1.0.4)
multipart-post (>= 1.2, < 3) multipart-post (~> 2)
faraday-net_http (1.0.1) faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0) faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0) faraday-patron (1.0.0)
@ -116,7 +116,7 @@ GEM
faraday_middleware (1.2.0) faraday_middleware (1.2.0)
faraday (~> 1.0) faraday (~> 1.0)
fastimage (2.2.6) fastimage (2.2.6)
fastlane (2.205.2) fastlane (2.206.2)
CFPropertyList (>= 2.3, < 4.0.0) CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0) addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0) artifactory (~> 3.0)
@ -163,9 +163,9 @@ GEM
fourflusher (2.3.1) fourflusher (2.3.1)
fuzzy_match (2.0.4) fuzzy_match (2.0.4)
gh_inspector (1.1.3) gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.19.0) google-apis-androidpublisher_v3 (0.21.0)
google-apis-core (>= 0.4, < 2.a) google-apis-core (>= 0.4, < 2.a)
google-apis-core (0.4.2) google-apis-core (0.5.0)
addressable (~> 2.5, >= 2.5.1) addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a) googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a) httpclient (>= 2.8.1, < 3.a)
@ -178,7 +178,7 @@ GEM
google-apis-core (>= 0.4, < 2.a) google-apis-core (>= 0.4, < 2.a)
google-apis-playcustomapp_v1 (0.7.0) google-apis-playcustomapp_v1 (0.7.0)
google-apis-core (>= 0.4, < 2.a) google-apis-core (>= 0.4, < 2.a)
google-apis-storage_v1 (0.13.0) google-apis-storage_v1 (0.14.0)
google-apis-core (>= 0.4, < 2.a) google-apis-core (>= 0.4, < 2.a)
google-cloud-core (1.6.0) google-cloud-core (1.6.0)
google-cloud-env (~> 1.0) google-cloud-env (~> 1.0)
@ -202,14 +202,14 @@ GEM
os (>= 0.9, < 2.0) os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a) signet (>= 0.16, < 2.a)
highline (2.0.3) highline (2.0.3)
http-cookie (1.0.4) http-cookie (1.0.5)
domain_name (~> 0.5) domain_name (~> 0.5)
httpclient (2.8.3) httpclient (2.8.3)
i18n (1.10.0) i18n (1.10.0)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
jmespath (1.6.1) jmespath (1.6.1)
json (2.6.1) json (2.6.2)
jwt (2.3.0) jwt (2.4.1)
memoist (0.16.2) memoist (0.16.2)
mini_magick (4.11.0) mini_magick (4.11.0)
mini_mime (1.1.2) mini_mime (1.1.2)
@ -226,7 +226,7 @@ GEM
plist (3.6.0) plist (3.6.0)
public_suffix (4.0.7) public_suffix (4.0.7)
rake (13.0.6) rake (13.0.6)
representable (3.1.1) representable (3.2.0)
declarative (< 0.1.0) declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0) trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0) uber (< 0.2.0)
@ -260,7 +260,7 @@ GEM
uber (0.1.0) uber (0.1.0)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.8.1) unf_ext (0.0.8.2)
unicode-display_width (1.8.0) unicode-display_width (1.8.0)
webrick (1.7.0) webrick (1.7.0)
word_wrap (1.0.0) word_wrap (1.0.0)

View File

@ -15,3 +15,5 @@
[@luizpicolo](https://github.com/luizpicolo) for Brazilian Portuguese [@luizpicolo](https://github.com/luizpicolo) for Brazilian Portuguese
[@duy@mas.to](https://mas.to/@duy) for Vietnamese translation [@duy@mas.to](https://mas.to/@duy) for Vietnamese translation
[@jk@mastodon.social](https://mastodon.social/@jk) for the famous Mastodon boop sound

View File

@ -4,6 +4,8 @@ PODS:
- DoubleConversion (1.1.6) - DoubleConversion (1.1.6)
- EASClient (0.2.1): - EASClient (0.2.1):
- ExpoModulesCore - ExpoModulesCore
- EXApplication (4.1.0):
- ExpoModulesCore
- EXAV (11.2.3): - EXAV (11.2.3):
- ExpoModulesCore - ExpoModulesCore
- React-runtimeexecutor - React-runtimeexecutor
@ -23,6 +25,8 @@ PODS:
- EXFirebaseCore (5.0.0): - EXFirebaseCore (5.0.0):
- ExpoModulesCore - ExpoModulesCore
- Firebase/Core (= 8.14.0) - Firebase/Core (= 8.14.0)
- EXFont (10.1.0):
- ExpoModulesCore
- EXImageLoader (3.2.0): - EXImageLoader (3.2.0):
- ExpoModulesCore - ExpoModulesCore
- React-Core - React-Core
@ -37,8 +41,13 @@ PODS:
- ExpoModulesCore - ExpoModulesCore
- ExpoHaptics (11.2.0): - ExpoHaptics (11.2.0):
- ExpoModulesCore - ExpoModulesCore
- ExpoImageManipulator (10.3.1):
- EXImageLoader
- ExpoModulesCore
- ExpoImagePicker (13.1.1): - ExpoImagePicker (13.1.1):
- ExpoModulesCore - ExpoModulesCore
- ExpoKeepAwake (10.1.1):
- ExpoModulesCore
- ExpoLocalization (13.0.0): - ExpoLocalization (13.0.0):
- ExpoModulesCore - ExpoModulesCore
- ExpoModulesCore (0.9.2): - ExpoModulesCore (0.9.2):
@ -590,6 +599,7 @@ DEPENDENCIES:
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- EASClient (from `../node_modules/expo-eas-client/ios`) - EASClient (from `../node_modules/expo-eas-client/ios`)
- EXApplication (from `../node_modules/expo-application/ios`)
- EXAV (from `../node_modules/expo-av/ios`) - EXAV (from `../node_modules/expo-av/ios`)
- EXConstants (from `../node_modules/expo-constants/ios`) - EXConstants (from `../node_modules/expo-constants/ios`)
- EXDevice (from `../node_modules/expo-device/ios`) - EXDevice (from `../node_modules/expo-device/ios`)
@ -597,6 +607,7 @@ DEPENDENCIES:
- EXFileSystem (from `../node_modules/expo-file-system/ios`) - EXFileSystem (from `../node_modules/expo-file-system/ios`)
- EXFirebaseAnalytics (from `../node_modules/expo-firebase-analytics/ios`) - EXFirebaseAnalytics (from `../node_modules/expo-firebase-analytics/ios`)
- EXFirebaseCore (from `../node_modules/expo-firebase-core/ios`) - EXFirebaseCore (from `../node_modules/expo-firebase-core/ios`)
- EXFont (from `../node_modules/expo-font/ios`)
- EXImageLoader (from `../node_modules/expo-image-loader/ios`) - EXImageLoader (from `../node_modules/expo-image-loader/ios`)
- EXJSONUtils (from `../node_modules/expo-json-utils/ios`) - EXJSONUtils (from `../node_modules/expo-json-utils/ios`)
- EXManifests (from `../node_modules/expo-manifests/ios`) - EXManifests (from `../node_modules/expo-manifests/ios`)
@ -604,7 +615,9 @@ DEPENDENCIES:
- Expo (from `../node_modules/expo/ios`) - Expo (from `../node_modules/expo/ios`)
- ExpoCrypto (from `../node_modules/expo-crypto/ios`) - ExpoCrypto (from `../node_modules/expo-crypto/ios`)
- ExpoHaptics (from `../node_modules/expo-haptics/ios`) - ExpoHaptics (from `../node_modules/expo-haptics/ios`)
- ExpoImageManipulator (from `../node_modules/expo-image-manipulator/ios`)
- ExpoImagePicker (from `../node_modules/expo-image-picker/ios`) - ExpoImagePicker (from `../node_modules/expo-image-picker/ios`)
- ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`)
- ExpoLocalization (from `../node_modules/expo-localization/ios`) - ExpoLocalization (from `../node_modules/expo-localization/ios`)
- ExpoModulesCore (from `../node_modules/expo-modules-core/ios`) - ExpoModulesCore (from `../node_modules/expo-modules-core/ios`)
- ExpoRandom (from `../node_modules/expo-random/ios`) - ExpoRandom (from `../node_modules/expo-random/ios`)
@ -701,6 +714,8 @@ EXTERNAL SOURCES:
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
EASClient: EASClient:
:path: "../node_modules/expo-eas-client/ios" :path: "../node_modules/expo-eas-client/ios"
EXApplication:
:path: "../node_modules/expo-application/ios"
EXAV: EXAV:
:path: "../node_modules/expo-av/ios" :path: "../node_modules/expo-av/ios"
EXConstants: EXConstants:
@ -715,6 +730,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/expo-firebase-analytics/ios" :path: "../node_modules/expo-firebase-analytics/ios"
EXFirebaseCore: EXFirebaseCore:
:path: "../node_modules/expo-firebase-core/ios" :path: "../node_modules/expo-firebase-core/ios"
EXFont:
:path: "../node_modules/expo-font/ios"
EXImageLoader: EXImageLoader:
:path: "../node_modules/expo-image-loader/ios" :path: "../node_modules/expo-image-loader/ios"
EXJSONUtils: EXJSONUtils:
@ -729,8 +746,12 @@ EXTERNAL SOURCES:
:path: "../node_modules/expo-crypto/ios" :path: "../node_modules/expo-crypto/ios"
ExpoHaptics: ExpoHaptics:
:path: "../node_modules/expo-haptics/ios" :path: "../node_modules/expo-haptics/ios"
ExpoImageManipulator:
:path: "../node_modules/expo-image-manipulator/ios"
ExpoImagePicker: ExpoImagePicker:
:path: "../node_modules/expo-image-picker/ios" :path: "../node_modules/expo-image-picker/ios"
ExpoKeepAwake:
:path: "../node_modules/expo-keep-awake/ios"
ExpoLocalization: ExpoLocalization:
:path: "../node_modules/expo-localization/ios" :path: "../node_modules/expo-localization/ios"
ExpoModulesCore: ExpoModulesCore:
@ -859,6 +880,7 @@ SPEC CHECKSUMS:
boost: a7c83b31436843459a1961bfd74b96033dc77234 boost: a7c83b31436843459a1961bfd74b96033dc77234
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662 DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
EASClient: 93565f4d024559b75eac62bc7d50acaa354614f6 EASClient: 93565f4d024559b75eac62bc7d50acaa354614f6
EXApplication: d6562af1204162e0ac46d341a7d4e5dc720b33de
EXAV: 88f61c5af8415715b7ee51f084c1020235b85c56 EXAV: 88f61c5af8415715b7ee51f084c1020235b85c56
EXConstants: fdbe52259365b6a6faaa5e99a3b82cfa6bc2eb61 EXConstants: fdbe52259365b6a6faaa5e99a3b82cfa6bc2eb61
EXDevice: 0115b360059ccd32c1701744e374e3259ffbdd3c EXDevice: 0115b360059ccd32c1701744e374e3259ffbdd3c
@ -866,6 +888,7 @@ SPEC CHECKSUMS:
EXFileSystem: 2aa2d9289f84bca9532b9ccbd81504fa31eb1ded EXFileSystem: 2aa2d9289f84bca9532b9ccbd81504fa31eb1ded
EXFirebaseAnalytics: aeefc63f92277313c3ee86da6a7ecf892f345ed1 EXFirebaseAnalytics: aeefc63f92277313c3ee86da6a7ecf892f345ed1
EXFirebaseCore: bdfa87df74fa1b74a6b38957561456aabad28a4f EXFirebaseCore: bdfa87df74fa1b74a6b38957561456aabad28a4f
EXFont: 04235cc22e6fef86028feb67db452978dc6f240f
EXImageLoader: b88e053d760f85a82405b1db2de4abf11978fc9f EXImageLoader: b88e053d760f85a82405b1db2de4abf11978fc9f
EXJSONUtils: 2a74b8f40f1523cc3f92af99c91aa78201737a77 EXJSONUtils: 2a74b8f40f1523cc3f92af99c91aa78201737a77
EXManifests: 0c6134b7b6f3236a93a778c3f44ba1cfb3f9fa3d EXManifests: 0c6134b7b6f3236a93a778c3f44ba1cfb3f9fa3d
@ -873,7 +896,9 @@ SPEC CHECKSUMS:
Expo: b9fff0a1eac0f424fc68ea49b4347fb308e52e17 Expo: b9fff0a1eac0f424fc68ea49b4347fb308e52e17
ExpoCrypto: d0d0f3e20875dc450b4ec88f0fb608da5c2c6c17 ExpoCrypto: d0d0f3e20875dc450b4ec88f0fb608da5c2c6c17
ExpoHaptics: ad58ec96a25e57579c14a47c7d71f0de0de8656a ExpoHaptics: ad58ec96a25e57579c14a47c7d71f0de0de8656a
ExpoImageManipulator: b55580bbc7b10099c7707949903e7176a8542ee8
ExpoImagePicker: d9d6b4f29db437fc7796f13cee5f133f5b4b5f7c ExpoImagePicker: d9d6b4f29db437fc7796f13cee5f133f5b4b5f7c
ExpoKeepAwake: c0c494b442ecd8122974c13b93ccfb57bd408e88
ExpoLocalization: 8f619bb6eec64575cd5220bfabbd7b4e2d6f33f8 ExpoLocalization: 8f619bb6eec64575cd5220bfabbd7b4e2d6f33f8
ExpoModulesCore: e4278a668e8c13c0269ed8b8a4200989deea2973 ExpoModulesCore: e4278a668e8c13c0269ed8b8a4200989deea2973
ExpoRandom: 14df0976aa363a71a730ceb7655250f3047c0e42 ExpoRandom: 14df0976aa363a71a730ceb7655250f3047c0e42

View File

@ -56,6 +56,7 @@
"expo-file-system": "14.0.0", "expo-file-system": "14.0.0",
"expo-firebase-analytics": "7.0.0", "expo-firebase-analytics": "7.0.0",
"expo-haptics": "11.2.0", "expo-haptics": "11.2.0",
"expo-image-manipulator": "^10.3.1",
"expo-image-picker": "13.1.1", "expo-image-picker": "13.1.1",
"expo-linking": "3.1.0", "expo-linking": "3.1.0",
"expo-localization": "13.0.0", "expo-localization": "13.0.0",
@ -153,4 +154,4 @@
} }
} }
} }
} }

View File

@ -178,11 +178,11 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
} }
let text: string | undefined = undefined let text: string | undefined = undefined
let media: { path: string; mime: string }[] = [] let media: { uri: string; mime: string }[] = []
const typesImage = ['png', 'jpg', 'jpeg', 'gif'] const typesImage = ['png', 'jpg', 'jpeg', 'gif']
const typesVideo = ['mp4', 'm4v', 'mov', 'webm', 'mpeg'] const typesVideo = ['mp4', 'm4v', 'mov', 'webm', 'mpeg']
const filterMedia = ({ path, mime }: { path: string; mime: string }) => { const filterMedia = ({ uri, mime }: { uri: string; mime: string }) => {
if (mime.startsWith('image/')) { if (mime.startsWith('image/')) {
if (!typesImage.includes(mime.split('/')[1])) { if (!typesImage.includes(mime.split('/')[1])) {
console.warn('Image type not supported:', mime.split('/')[1]) console.warn('Image type not supported:', mime.split('/')[1])
@ -195,7 +195,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
}) })
return return
} }
media.push({ path, mime }) media.push({ uri, mime })
} else if (mime.startsWith('video/')) { } else if (mime.startsWith('video/')) {
if (!typesVideo.includes(mime.split('/')[1])) { if (!typesVideo.includes(mime.split('/')[1])) {
console.warn('Video type not supported:', mime.split('/')[1]) console.warn('Video type not supported:', mime.split('/')[1])
@ -208,17 +208,17 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
}) })
return return
} }
media.push({ path, mime }) media.push({ uri, mime })
} else { } else {
if (typesImage.includes(path.split('.').pop() || '')) { if (typesImage.includes(uri.split('.').pop() || '')) {
media.push({ path: path, mime: 'image/jpg' }) media.push({ uri, mime: 'image/jpg' })
return return
} }
if (typesVideo.includes(path.split('.').pop() || '')) { if (typesVideo.includes(uri.split('.').pop() || '')) {
media.push({ path: path, mime: 'video/mp4' }) media.push({ uri, mime: 'video/mp4' })
return return
} }
text = !text ? path : text.concat(text, `\n${path}`) text = !text ? uri : text.concat(text, `\n${uri}`)
} }
} }
@ -230,7 +230,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
for (const d of item.data) { for (const d of item.data) {
if (typeof d !== 'string') { if (typeof d !== 'string') {
filterMedia({ path: d.data, mime: d.mimeType }) filterMedia({ uri: d.data, mime: d.mimeType })
} }
} }
break break
@ -245,7 +245,7 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
tempData = item.data tempData = item.data
} }
for (const d of item.data) { for (const d of item.data) {
filterMedia({ path: d, mime: item.mimeType }) filterMedia({ uri: d, mime: item.mimeType })
} }
break break
} }

View File

@ -68,8 +68,8 @@ const apiGeneral = async <T = unknown>({
error.response.data.error error.response.data.error
) )
return Promise.reject({ return Promise.reject({
status: error.response.status, status: error?.response.status,
message: error.response.data.error message: error?.response.data.error
}) })
} else if (error?.request) { } else if (error?.request) {
// The request was made but no response was received // The request was made but no response was received

View File

@ -110,8 +110,8 @@ const apiInstance = async <T = unknown>({
error.response.data.error error.response.data.error
) )
return Promise.reject({ return Promise.reject({
status: error.response.status, status: error?.response.status,
message: error.response.data.error message: error?.response.data.error
}) })
} else if (error?.request) { } else if (error?.request) {
// The request was made but no response was received // The request was made but no response was received

View File

@ -66,7 +66,7 @@ const apiTooot = async <T = unknown>({
}) })
}) })
.catch(error => { .catch(error => {
if (sentry && Math.random() < 0.1) { if (sentry) {
Sentry.Native.setExtras({ Sentry.Native.setExtras({
API: 'tooot', API: 'tooot',
...(error?.response && { response: error.response }), ...(error?.response && { response: error.response }),
@ -85,8 +85,8 @@ const apiTooot = async <T = unknown>({
error.response.data.error error.response.data.error
) )
return Promise.reject({ return Promise.reject({
status: error.response.status, status: error?.response.status,
message: error.response.data.error message: error?.response.data.error
}) })
} else if (error?.request) { } else if (error?.request) {
// The request was made but no response was received // The request was made but no response was received

View File

@ -49,7 +49,7 @@ const renderNode = ({
const href = node.attribs.href const href = node.attribs.href
if (classes) { if (classes) {
if (classes.includes('hashtag')) { if (classes.includes('hashtag')) {
const tag = href.split(new RegExp(/\/tag\/(.*)|\/tags\/(.*)/)) const tag = href?.split(new RegExp(/\/tag\/(.*)|\/tags\/(.*)/))
const differentTag = routeParams?.hashtag const differentTag = routeParams?.hashtag
? routeParams.hashtag !== tag[1] && routeParams.hashtag !== tag[2] ? routeParams.hashtag !== tag[1] && routeParams.hashtag !== tag[2]
: true : true
@ -107,7 +107,7 @@ const renderNode = ({
) )
} }
} else { } else {
const domain = href.split(new RegExp(/:\/\/(.[^\/]+)/)) const domain = href?.split(new RegExp(/:\/\/(.[^\/]+)/))
// Need example here // Need example here
const content = const content =
node.children && node.children[0] && node.children[0].data node.children && node.children[0] && node.children[0].data
@ -124,11 +124,15 @@ const renderNode = ({
}} }}
onPress={async () => { onPress={async () => {
analytics('status_link_press') analytics('status_link_press')
!disableDetails && !shouldBeTag if (!disableDetails) {
? await openLink(href, navigation) if (shouldBeTag) {
: navigation.push('Tab-Shared-Hashtag', { navigation.push('Tab-Shared-Hashtag', {
hashtag: content.substring(1) hashtag: content.substring(1)
}) })
} else {
await openLink(href, navigation)
}
}
}} }}
> >
{(content && content !== href && content) || {(content && content !== href && content) ||

View File

@ -22,7 +22,7 @@ const TimelineActioned = React.memo(
const { colors } = useTheme() const { colors } = useTheme()
const navigation = const navigation =
useNavigation<StackNavigationProp<TabLocalStackParamList>>() useNavigation<StackNavigationProp<TabLocalStackParamList>>()
const name = account?.display_name || account.username const name = account?.display_name || account?.username
const iconColor = colors.primaryDefault const iconColor = colors.primaryDefault
const content = (content: string) => ( const content = (content: string) => (

View File

@ -27,7 +27,7 @@ const HeaderSharedAccount = React.memo(
numberOfLines={1} numberOfLines={1}
> >
<ParseEmojis <ParseEmojis
content={account?.display_name || account.username} content={account?.display_name || account?.username}
emojis={account.emojis} emojis={account.emojis}
fontBold fontBold
/> />

View File

@ -2,9 +2,10 @@ import analytics from '@components/analytics'
import { ActionSheetOptions } from '@expo/react-native-action-sheet' import { ActionSheetOptions } from '@expo/react-native-action-sheet'
import { store } from '@root/store' import { store } from '@root/store'
import { getInstanceConfigurationStatusMaxAttachments } from '@utils/slices/instancesSlice' import { getInstanceConfigurationStatusMaxAttachments } from '@utils/slices/instancesSlice'
import { manipulateAsync, SaveFormat } from 'expo-image-manipulator'
import * as ExpoImagePicker from 'expo-image-picker' import * as ExpoImagePicker from 'expo-image-picker'
import i18next from 'i18next' import i18next from 'i18next'
import { Alert, Linking } from 'react-native' import { Alert, Linking, Platform } from 'react-native'
import ImagePicker, { import ImagePicker, {
Image, Image,
ImageOrVideo ImageOrVideo
@ -27,7 +28,7 @@ const mediaSelector = async ({
maximum, maximum,
indicateMaximum = false, indicateMaximum = false,
showActionSheetWithOptions showActionSheetWithOptions
}: Props): Promise<ImageOrVideo[]> => { }: Props): Promise<({ uri: string } & Omit<ImageOrVideo, 'path'>)[]> => {
const checkLibraryPermission = async (): Promise<boolean> => { const checkLibraryPermission = async (): Promise<boolean> => {
const { status } = const { status } =
await ExpoImagePicker.requestMediaLibraryPermissionsAsync() await ExpoImagePicker.requestMediaLibraryPermissionsAsync()
@ -110,17 +111,40 @@ const mediaSelector = async ({
multiple: true, multiple: true,
minFiles: 1, minFiles: 1,
maxFiles: _maximum, maxFiles: _maximum,
loadingLabelText: '', smartAlbums: ['UserLibrary'],
compressImageMaxWidth: 4096, writeTempFile: false,
compressImageMaxHeight: 4096 loadingLabelText: ''
}).catch(() => {}) }).catch(() => {})
if (!images) { if (!images) {
return reject() return reject()
} }
// react-native-image-crop-picker may return HEIC as JPG that causes upload failure
if (Platform.OS === 'ios') {
for (const [index, image] of images.entries()) {
if (image.mime === 'image/heic') {
const converted = await manipulateAsync(image.sourceURL!, [], {
base64: false,
compress: 0.8,
format: SaveFormat.JPEG
})
images[index] = {
...images[index],
sourceURL: converted.uri,
mime: 'image/jpeg'
}
}
}
}
if (!resize) { if (!resize) {
return resolve(images) return resolve(
images.map(image => ({
...image,
uri: image.sourceURL || `file://${image.path}`
}))
)
} else { } else {
const croppedImages: Image[] = [] const croppedImages: Image[] = []
for (const image of images) { for (const image of images) {
@ -135,7 +159,12 @@ const mediaSelector = async ({
}).catch(() => {}) }).catch(() => {})
croppedImage && croppedImages.push(croppedImage) croppedImage && croppedImages.push(croppedImage)
} }
return resolve(croppedImages) return resolve(
croppedImages.map(image => ({
...image,
uri: `file://${image.path}`
}))
)
} }
} }
const selectVideo = async () => { const selectVideo = async () => {
@ -146,7 +175,9 @@ const mediaSelector = async ({
}).catch(() => {}) }).catch(() => {})
if (video) { if (video) {
return resolve([video]) return resolve([
{ ...video, uri: video.sourceURL || `file://${video.path}` }
])
} else { } else {
return reject() return reject()
} }

View File

@ -0,0 +1,149 @@
import { store } from '@root/store'
import { getInstanceConfigurationMediaAttachments } from '@utils/slices/instancesSlice'
import { Action, manipulateAsync, SaveFormat } from 'expo-image-manipulator'
import i18next from 'i18next'
import { Platform } from 'react-native'
import ImagePicker from 'react-native-image-crop-picker'
export interface Props {
type: 'image' | 'video'
uri: string // This can be pure path or uri starting with file://
mime?: string
transform: {
imageFormat?: SaveFormat.JPEG | SaveFormat.PNG
resize?: boolean
width?: number
height?: number
}
}
const getFileExtension = (uri: string) => {
const extension = uri.split('.').pop()
// Using mime type standard of jpeg
return extension === 'jpg' ? 'jpeg' : extension
}
const mediaTransformation = async ({
type,
uri,
mime,
transform
}: Props): Promise<{
uri: string
mime: string
width: number
height: number
}> => {
const configurationMediaAttachments =
getInstanceConfigurationMediaAttachments(store.getState())
const fileExtension = getFileExtension(uri)
switch (type) {
case 'image':
if (mime === 'image/gif' || fileExtension === 'gif') {
return Promise.reject('GIFs should not be transformed')
}
let targetFormat: SaveFormat.JPEG | SaveFormat.PNG = SaveFormat.JPEG
const supportedImageTypes =
configurationMediaAttachments.supported_mime_types.filter(mime =>
mime.startsWith('image/')
)
// @ts-ignore
const transformations: Action[] = [
!transform.resize && (transform.width || transform.height)
? {
resize: { width: transform.width, height: transform.height }
}
: null
].filter(t => !!t)
if (mime) {
if (
mime !== `image/${fileExtension}` ||
!supportedImageTypes.includes(mime)
) {
targetFormat = transform.imageFormat || SaveFormat.JPEG
} else {
targetFormat = mime.split('/').pop() as any
}
} else {
if (!fileExtension) {
return Promise.reject('Unable to get file extension')
}
if (!supportedImageTypes.includes(`image/${fileExtension}`)) {
targetFormat = transform.imageFormat || SaveFormat.JPEG
} else {
targetFormat = fileExtension as any
}
}
const converted = await manipulateAsync(uri, transformations, {
base64: false,
compress: Platform.OS === 'ios' ? 0.8 : 1,
format: targetFormat
})
if (transform.resize) {
const resized = await ImagePicker.openCropper({
mediaType: 'photo',
path: converted.uri,
width: transform.width,
height: transform.height,
cropperChooseText: i18next.t('common:buttons.apply'),
cropperCancelText: i18next.t('common:buttons.cancel'),
hideBottomControls: true
})
if (!resized) {
return Promise.reject('Resize failed')
} else {
return {
uri: resized.path,
mime: resized.mime,
width: resized.width,
height: resized.height
}
}
} else {
return {
uri: converted.uri,
mime: transform.imageFormat || SaveFormat.JPEG,
width: converted.width,
height: converted.height
}
}
case 'video':
const supportedVideoTypes =
configurationMediaAttachments.supported_mime_types.filter(mime =>
mime.startsWith('video/')
)
if (mime) {
if (mime !== `video/${fileExtension}`) {
console.warn('Video mime type and file extension does not match')
}
if (!supportedVideoTypes.includes(mime)) {
return Promise.reject('Video file type is not supported')
}
} else {
if (!fileExtension) {
return Promise.reject('Unable to get file extension')
}
if (!supportedVideoTypes.includes(`video/${fileExtension}`)) {
return Promise.reject('Video file type is not supported')
}
}
return {
uri: uri,
mime: mime || `video/${fileExtension}`,
width: 0,
height: 0
}
break
}
}
export default mediaTransformation

View File

@ -1,7 +1,7 @@
{ {
"accessibilityHint": "Ações para este toot, como o seu usuário publicado", "accessibilityHint": "Ações para este toot, como o seu usuário publicado",
"account": { "account": {
"title": "", "title": "Ações do Usuário",
"mute": { "mute": {
"action": "Silenciar usuário" "action": "Silenciar usuário"
}, },
@ -13,11 +13,11 @@
} }
}, },
"instance": { "instance": {
"title": "", "title": "Ação da Instância",
"block": { "block": {
"action": "Bloquear a instância {{instance}}", "action": "Bloquear a instância {{instance}}",
"alert": { "alert": {
"title": "", "title": "Confirmar o bloqueio da instância {{instance}}?",
"message": "Na maioria das vezes, você pode silenciar ou bloquear determinado usuário.\n\nDepois de bloquear a instância, todo seu conteúdo, incluindo seguidores, será removido!", "message": "Na maioria das vezes, você pode silenciar ou bloquear determinado usuário.\n\nDepois de bloquear a instância, todo seu conteúdo, incluindo seguidores, será removido!",
"buttons": { "buttons": {
"confirm": "Confirmar" "confirm": "Confirmar"
@ -34,7 +34,7 @@
} }
}, },
"status": { "status": {
"title": "", "title": "Ações do toot",
"edit": { "edit": {
"action": "Editar toot" "action": "Editar toot"
}, },
@ -42,29 +42,29 @@
"action": "Remover toot", "action": "Remover toot",
"alert": { "alert": {
"title": "Confirme a exclusão?", "title": "Confirme a exclusão?",
"message": "", "message": "Todos os boosts e favoritos serão limpos, incluindo todas as respostas.",
"buttons": { "buttons": {
"confirm": "Confirmar" "confirm": "Confirmar"
} }
} }
}, },
"deleteEdit": { "deleteEdit": {
"action": "", "action": "Excluir toot e repostar",
"alert": { "alert": {
"title": "", "title": "Confirmar exclusão e repostagem?",
"message": "", "message": "Todos os boosts e favoritos serão limpos, incluindo todas as respostas.",
"buttons": { "buttons": {
"confirm": "Confirmar" "confirm": "Confirmar"
} }
} }
}, },
"mute": { "mute": {
"action-muted_false": "", "action-muted_false": "Silenciar este toot e respostas",
"action-muted_true": "" "action-muted_true": "Desbloquear este toot e respostas"
}, },
"pin": { "pin": {
"action-pinned_false": "", "action-pinned_false": "Toot fixado",
"action-pinned_true": "" "action-pinned_true": "Desafixar toot"
} }
} }
} }

View File

@ -1,10 +1,10 @@
{ {
"title": "Selecionar fonte de mídia", "title": "Selecionar fonte de mídia",
"options": { "options": {
"image": "", "image": "Enviar fotos",
"image_max": "", "image_max": "Carregar fotos (máx. {{max}})",
"video": "", "video": "Enviar vídeo",
"video_max": "" "video_max": "Carregar vídeo (máx. {{max}})"
}, },
"library": { "library": {
"alert": { "alert": {

View File

@ -30,7 +30,7 @@
"default": "{{name}} boostou", "default": "{{name}} boostou",
"notification": "{{name}} deu boost no teu toot" "notification": "{{name}} deu boost no teu toot"
}, },
"update": "" "update": "Toot foi editado"
}, },
"actions": { "actions": {
"reply": { "reply": {

View File

@ -15,7 +15,7 @@
"button": { "button": {
"default": "Toot", "default": "Toot",
"conversation": "Toot DM", "conversation": "Toot DM",
"reply": "Resposta de toot", "reply": "Responder",
"deleteEdit": "Toot", "deleteEdit": "Toot",
"edit": "Toot", "edit": "Toot",
"share": "Toot" "share": "Toot"

View File

@ -32,7 +32,7 @@
"me": { "me": {
"stacks": { "stacks": {
"bookmarks": { "bookmarks": {
"name": "Favoritos" "name": "Salvos"
}, },
"conversations": { "conversations": {
"name": "Mensagens diretas" "name": "Mensagens diretas"
@ -82,7 +82,7 @@
"demo": "<p>Esta é uma demonstração também😊. Você pode escolher entre várias opções abaixo.<br /><br />Esta configuração afeta apenas o conteúdo principal dos toots, mas não os tamanhos de outra fonte.</p>", "demo": "<p>Esta é uma demonstração também😊. Você pode escolher entre várias opções abaixo.<br /><br />Esta configuração afeta apenas o conteúdo principal dos toots, mas não os tamanhos de outra fonte.</p>",
"availableSizes": "Tamanhos disponíveis", "availableSizes": "Tamanhos disponíveis",
"sizes": { "sizes": {
"S": "S", "S": "P",
"M": "M - Padrão", "M": "M - Padrão",
"L": "Grande", "L": "Grande",
"XL": "Extra grande", "XL": "Extra grande",

View File

@ -49,7 +49,7 @@ const ActionsStatus: React.FC<Props> = ({
message: t('common:message.error.message', { message: t('common:message.error.message', {
function: t(`shared.header.actions.status.${theFunction}.function`) function: t(`shared.header.actions.status.${theFunction}.function`)
}), }),
...(err.status && ...(err?.status &&
typeof err.status === 'number' && typeof err.status === 'number' &&
err.data && err.data &&
err.data.error && err.data.error &&

View File

@ -128,8 +128,8 @@ const ComposeEditAttachmentImage: React.FC<Props> = ({ index }) => {
height: imageDimensionis.height height: imageDimensionis.height
}} }}
source={{ source={{
uri: theAttachmentLocal?.path uri: theAttachmentLocal?.uri
? `file://${theAttachmentLocal?.path}` ? theAttachmentLocal.uri
: theAttachmentRemote?.preview_url : theAttachmentRemote?.preview_url
}} }}
/> />

View File

@ -34,7 +34,7 @@ const ComposeEditAttachmentRoot: React.FC<Props> = ({ index }) => {
video={ video={
video.local video.local
? ({ ? ({
url: `file://${video.local.path}`, url: video.local.uri,
preview_url: video.local.local_thumbnail, preview_url: video.local.local_thumbnail,
blurhash: video.remote?.blurhash blurhash: video.remote?.blurhash
} as Mastodon.AttachmentVideo) } as Mastodon.AttachmentVideo)

View File

@ -22,28 +22,25 @@ export const uploadAttachment = async ({
media media
}: { }: {
composeDispatch: Dispatch<ComposeAction> composeDispatch: Dispatch<ComposeAction>
media: Pick<ImageOrVideo, 'path' | 'mime' | 'width' | 'height'> media: { uri: string } & Pick<ImageOrVideo, 'mime' | 'width' | 'height'>
}) => { }) => {
const hash = await Crypto.digestStringAsync( const hash = await Crypto.digestStringAsync(
Crypto.CryptoDigestAlgorithm.SHA256, Crypto.CryptoDigestAlgorithm.SHA256,
media.path + Math.random() media.uri + Math.random()
) )
let attachmentType: string
switch (media.mime.split('/')[0]) { switch (media.mime.split('/')[0]) {
case 'image': case 'image':
attachmentType = `image/${media.path.split('.')[1]}`
composeDispatch({ composeDispatch({
type: 'attachment/upload/start', type: 'attachment/upload/start',
payload: { payload: {
local: { ...media, type: 'image', local_thumbnail: media.path, hash }, local: { ...media, type: 'image', local_thumbnail: media.uri, hash },
uploading: true uploading: true
} }
}) })
break break
case 'video': case 'video':
attachmentType = `video/${media.path.split('.')[1]}` VideoThumbnails.getThumbnailAsync(media.uri)
VideoThumbnails.getThumbnailAsync(media.path)
.then(({ uri, width, height }) => .then(({ uri, width, height }) =>
composeDispatch({ composeDispatch({
type: 'attachment/upload/start', type: 'attachment/upload/start',
@ -71,7 +68,6 @@ export const uploadAttachment = async ({
) )
break break
default: default:
attachmentType = 'unknown'
composeDispatch({ composeDispatch({
type: 'attachment/upload/start', type: 'attachment/upload/start',
payload: { payload: {
@ -105,9 +101,9 @@ export const uploadAttachment = async ({
const formData = new FormData() const formData = new FormData()
formData.append('file', { formData.append('file', {
uri: `file://${media.path}`, uri: media.uri,
name: attachmentType, name: media.uri.match(new RegExp(/.*\/(.*)/))?.[1] || 'file.jpg',
type: attachmentType type: media.mime
} as any) } as any)
return apiInstance<Mastodon.Attachment>({ return apiInstance<Mastodon.Attachment>({

View File

@ -90,7 +90,7 @@ const ComposeTextInput: React.FC = () => {
uploadAttachment({ uploadAttachment({
composeDispatch, composeDispatch,
media: { media: {
path: file.uri, uri: file.uri,
mime: file.type, mime: file.type,
width: 100, width: 100,
height: 100 height: 100

View File

@ -58,7 +58,7 @@ const composeReducer = (
attachments: { attachments: {
...state.attachments, ...state.attachments,
uploads: state.attachments.uploads.map(upload => uploads: state.attachments.uploads.map(upload =>
upload.local?.path === action.payload.local?.path upload.local?.uri === action.payload.local?.uri
? { ...upload, remote: action.payload.remote, uploading: false } ? { ...upload, remote: action.payload.remote, uploading: false }
: upload : upload
) )

View File

@ -2,11 +2,11 @@ import { ImageOrVideo } from 'react-native-image-crop-picker'
export type ExtendedAttachment = { export type ExtendedAttachment = {
remote?: Mastodon.Attachment remote?: Mastodon.Attachment
local?: Pick<ImageOrVideo, 'path' | 'width' | 'height' | 'mime'> & { local?: { uri: string } & Pick<ImageOrVideo, 'width' | 'height' | 'mime'> & {
type: 'image' | 'video' | 'unknown' type: 'image' | 'video' | 'unknown'
local_thumbnail?: string local_thumbnail?: string
hash?: string hash?: string
} }
uploading?: boolean uploading?: boolean
} }
@ -123,7 +123,7 @@ export type ComposeAction =
type: 'attachment/upload/end' type: 'attachment/upload/end'
payload: { payload: {
remote: Mastodon.Attachment remote: Mastodon.Attachment
local: Pick<ImageOrVideo, 'path' | 'width' | 'height' | 'mime'> local: { uri: string } & Pick<ImageOrVideo, 'width' | 'height' | 'mime'>
} }
} }
| { | {

View File

@ -46,7 +46,7 @@ const ProfileAvatarHeader: React.FC<Props> = ({ type, messageRef }) => {
failed: true failed: true
}, },
type, type,
data: image[0].path data: image[0].uri
}) })
}} }}
/> />

View File

@ -42,7 +42,7 @@ export type RootStackParamList = {
| { | {
type: 'share' type: 'share'
text?: string text?: string
media?: { path: string; mime: string }[] media?: { uri: string; mime: string }[]
} }
| undefined | undefined
'Screen-ImagesViewer': { 'Screen-ImagesViewer': {

View File

@ -249,7 +249,7 @@ const instancesSlice = createSlice({
// Update Instance Configuration // Update Instance Configuration
.addCase(updateConfiguration.fulfilled, (state, action) => { .addCase(updateConfiguration.fulfilled, (state, action) => {
const activeIndex = findInstanceActive(state.instances) const activeIndex = findInstanceActive(state.instances)
state.instances[activeIndex].version = action.payload.version state.instances[activeIndex].version = action.payload?.version || '0'
state.instances[activeIndex].configuration = state.instances[activeIndex].configuration =
action.payload.configuration action.payload.configuration
}) })
@ -316,7 +316,7 @@ const instancesSlice = createSlice({
state.instances[activeIndex].frequentEmojis = state.instances[ state.instances[activeIndex].frequentEmojis = state.instances[
activeIndex activeIndex
].frequentEmojis?.filter(emoji => { ].frequentEmojis?.filter(emoji => {
return action.payload.find( return action.payload?.find(
e => e =>
e.shortcode === emoji.emoji.shortcode && e.url === emoji.emoji.url e.shortcode === emoji.emoji.shortcode && e.url === emoji.emoji.url
) )
@ -381,6 +381,43 @@ export const getInstanceConfigurationStatusCharsURL = ({
instances[findInstanceActive(instances)]?.configuration?.statuses instances[findInstanceActive(instances)]?.configuration?.statuses
.characters_reserved_per_url || 23 .characters_reserved_per_url || 23
export const getInstanceConfigurationMediaAttachments = ({
instances: { instances }
}: RootState) =>
instances[findInstanceActive(instances)]?.configuration
?.media_attachments || {
supported_mime_types: [
'image/jpeg',
'image/png',
'image/gif',
'video/webm',
'video/mp4',
'video/quicktime',
'video/ogg',
'audio/wave',
'audio/wav',
'audio/x-wav',
'audio/x-pn-wave',
'audio/ogg',
'audio/vorbis',
'audio/mpeg',
'audio/mp3',
'audio/webm',
'audio/flac',
'audio/aac',
'audio/m4a',
'audio/x-m4a',
'audio/mp4',
'audio/3gpp',
'video/x-ms-asf'
],
image_size_limit: 10485760,
image_matrix_limit: 16777216,
video_size_limit: 41943040,
video_frame_rate_limit: 60,
video_matrix_limit: 2304000
}
export const getInstanceConfigurationPoll = ({ export const getInstanceConfigurationPoll = ({
instances: { instances } instances: { instances }
}: RootState) => }: RootState) =>

View File

@ -4428,6 +4428,13 @@ expo-image-loader@~3.2.0:
resolved "https://registry.yarnpkg.com/expo-image-loader/-/expo-image-loader-3.2.0.tgz#d98b021660edef7243f7c5ec011b8d0545626d41" resolved "https://registry.yarnpkg.com/expo-image-loader/-/expo-image-loader-3.2.0.tgz#d98b021660edef7243f7c5ec011b8d0545626d41"
integrity sha512-LU3Q2prn64/HxdToDmxgMIRXS1ZvD9Q3iCxRVTZn1fPQNNDciIQFE5okaa74Ogx20DFHs90r6WoUd7w9Af1OGQ== integrity sha512-LU3Q2prn64/HxdToDmxgMIRXS1ZvD9Q3iCxRVTZn1fPQNNDciIQFE5okaa74Ogx20DFHs90r6WoUd7w9Af1OGQ==
expo-image-manipulator@^10.3.1:
version "10.3.1"
resolved "https://registry.yarnpkg.com/expo-image-manipulator/-/expo-image-manipulator-10.3.1.tgz#e16dd76a550c7f5d653a2a666f26429eba311a6b"
integrity sha512-D08dMD6MerxBu23DmCIhurySQih+eLP7VxKvY5mWqYz9payjDOS1cAGs3HvXPrEDusPQFQ0uIfqc+oqeMNFBIA==
dependencies:
expo-image-loader "~3.2.0"
expo-image-picker@13.1.1: expo-image-picker@13.1.1:
version "13.1.1" version "13.1.1"
resolved "https://registry.yarnpkg.com/expo-image-picker/-/expo-image-picker-13.1.1.tgz#e039bf9748ccb7b89370ff2969c3ef07cc949192" resolved "https://registry.yarnpkg.com/expo-image-picker/-/expo-image-picker-13.1.1.tgz#e039bf9748ccb7b89370ff2969c3ef07cc949192"