diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 72890cc9..f942bc63 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -2,14 +2,14 @@ PODS:
- boost (1.76.0)
- CocoaAsyncSocket (7.6.5)
- DoubleConversion (1.1.6)
- - FBLazyVector (0.72.13)
- - FBReactNativeSpec (0.72.13):
- - RCT-Folly (= 2021.07.22.00)
- - RCTRequired (= 0.72.13)
- - RCTTypeSafety (= 0.72.13)
- - React-Core (= 0.72.13)
- - React-jsi (= 0.72.13)
- - ReactCommon/turbomodule/core (= 0.72.13)
+ - FBLazyVector (0.72.17)
+ - FBReactNativeSpec (0.72.17):
+ - RCT-Folly (= 2021.07.22.00)
+ - RCTRequired (= 0.72.17)
+ - RCTTypeSafety (= 0.72.17)
+ - React-Core (= 0.72.17)
+ - React-jsi (= 0.72.17)
+ - ReactCommon/turbomodule/core (= 0.72.17)
- Flipper (0.182.0):
- Flipper-Folly (~> 2.6)
- Flipper-Boost-iOSX (1.76.0.1.11)
@@ -70,9 +70,9 @@ PODS:
- FlipperKit/FlipperKitNetworkPlugin
- fmt (6.2.1)
- glog (0.3.5)
- - hermes-engine (0.72.13):
- - hermes-engine/Pre-built (= 0.72.13)
- - hermes-engine/Pre-built (0.72.13)
+ - hermes-engine (0.72.17):
+ - hermes-engine/Pre-built (= 0.72.17)
+ - hermes-engine/Pre-built (0.72.17)
- libevent (2.1.12)
- OpenSSL-Universal (1.1.1100)
- RCT-Folly (2021.07.22.00):
@@ -92,26 +92,26 @@ PODS:
- fmt (~> 6.2.1)
- glog
- libevent
- - RCTRequired (0.72.13)
- - RCTTypeSafety (0.72.13):
- - FBLazyVector (= 0.72.13)
- - RCTRequired (= 0.72.13)
- - React-Core (= 0.72.13)
- - React (0.72.13):
- - React-Core (= 0.72.13)
- - React-Core/DevSupport (= 0.72.13)
- - React-Core/RCTWebSocket (= 0.72.13)
- - React-RCTActionSheet (= 0.72.13)
- - React-RCTAnimation (= 0.72.13)
- - React-RCTBlob (= 0.72.13)
- - React-RCTImage (= 0.72.13)
- - React-RCTLinking (= 0.72.13)
- - React-RCTNetwork (= 0.72.13)
- - React-RCTSettings (= 0.72.13)
- - React-RCTText (= 0.72.13)
- - React-RCTVibration (= 0.72.13)
- - React-callinvoker (0.72.13)
- - React-Codegen (0.72.13):
+ - RCTRequired (0.72.17)
+ - RCTTypeSafety (0.72.17):
+ - FBLazyVector (= 0.72.17)
+ - RCTRequired (= 0.72.17)
+ - React-Core (= 0.72.17)
+ - React (0.72.17):
+ - React-Core (= 0.72.17)
+ - React-Core/DevSupport (= 0.72.17)
+ - React-Core/RCTWebSocket (= 0.72.17)
+ - React-RCTActionSheet (= 0.72.17)
+ - React-RCTAnimation (= 0.72.17)
+ - React-RCTBlob (= 0.72.17)
+ - React-RCTImage (= 0.72.17)
+ - React-RCTLinking (= 0.72.17)
+ - React-RCTNetwork (= 0.72.17)
+ - React-RCTSettings (= 0.72.17)
+ - React-RCTText (= 0.72.17)
+ - React-RCTVibration (= 0.72.17)
+ - React-callinvoker (0.72.17)
+ - React-Codegen (0.72.17):
- DoubleConversion
- FBReactNativeSpec
- glog
@@ -126,11 +126,11 @@ PODS:
- React-rncore
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- - React-Core (0.72.13):
+ - React-Core (0.72.17):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-Core/Default (= 0.72.13)
+ - React-Core/Default (= 0.72.17)
- React-cxxreact
- React-hermes
- React-jsi
@@ -140,7 +140,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/CoreModulesHeaders (0.72.13):
+ - React-Core/CoreModulesHeaders (0.72.17):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -154,7 +154,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/Default (0.72.13):
+ - React-Core/Default (0.72.17):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -167,23 +167,23 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/DevSupport (0.72.13):
+ - React-Core/DevSupport (0.72.17):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-Core/Default (= 0.72.13)
- - React-Core/RCTWebSocket (= 0.72.13)
+ - React-Core/Default (= 0.72.17)
+ - React-Core/RCTWebSocket (= 0.72.17)
- React-cxxreact
- React-hermes
- React-jsi
- React-jsiexecutor
- - React-jsinspector (= 0.72.13)
+ - React-jsinspector (= 0.72.17)
- React-perflogger
- React-runtimeexecutor
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTActionSheetHeaders (0.72.13):
+ - React-Core/RCTActionSheetHeaders (0.72.17):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -197,7 +197,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTAnimationHeaders (0.72.13):
+ - React-Core/RCTAnimationHeaders (0.72.17):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -211,7 +211,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTBlobHeaders (0.72.13):
+ - React-Core/RCTBlobHeaders (0.72.17):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -225,7 +225,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTImageHeaders (0.72.13):
+ - React-Core/RCTImageHeaders (0.72.17):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -239,7 +239,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTLinkingHeaders (0.72.13):
+ - React-Core/RCTLinkingHeaders (0.72.17):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -253,7 +253,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTNetworkHeaders (0.72.13):
+ - React-Core/RCTNetworkHeaders (0.72.17):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -267,7 +267,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTSettingsHeaders (0.72.13):
+ - React-Core/RCTSettingsHeaders (0.72.17):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -281,7 +281,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTTextHeaders (0.72.13):
+ - React-Core/RCTTextHeaders (0.72.17):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -295,7 +295,7 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTVibrationHeaders (0.72.13):
+ - React-Core/RCTVibrationHeaders (0.72.17):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -309,11 +309,11 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-Core/RCTWebSocket (0.72.13):
+ - React-Core/RCTWebSocket (0.72.17):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-Core/Default (= 0.72.13)
+ - React-Core/Default (= 0.72.17)
- React-cxxreact
- React-hermes
- React-jsi
@@ -323,57 +323,57 @@ PODS:
- React-utils
- SocketRocket (= 0.6.1)
- Yoga
- - React-CoreModules (0.72.13):
+ - React-CoreModules (0.72.17):
- RCT-Folly (= 2021.07.22.00)
- - RCTTypeSafety (= 0.72.13)
- - React-Codegen (= 0.72.13)
- - React-Core/CoreModulesHeaders (= 0.72.13)
- - React-jsi (= 0.72.13)
+ - RCTTypeSafety (= 0.72.17)
+ - React-Codegen (= 0.72.17)
+ - React-Core/CoreModulesHeaders (= 0.72.17)
+ - React-jsi (= 0.72.17)
- React-RCTBlob
- - React-RCTImage (= 0.72.13)
- - ReactCommon/turbomodule/core (= 0.72.13)
+ - React-RCTImage (= 0.72.17)
+ - ReactCommon/turbomodule/core (= 0.72.17)
- SocketRocket (= 0.6.1)
- - React-cxxreact (0.72.13):
+ - React-cxxreact (0.72.17):
- boost (= 1.76.0)
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-callinvoker (= 0.72.13)
- - React-debug (= 0.72.13)
- - React-jsi (= 0.72.13)
- - React-jsinspector (= 0.72.13)
- - React-logger (= 0.72.13)
- - React-perflogger (= 0.72.13)
- - React-runtimeexecutor (= 0.72.13)
- - React-debug (0.72.13)
- - React-hermes (0.72.13):
+ - React-callinvoker (= 0.72.17)
+ - React-debug (= 0.72.17)
+ - React-jsi (= 0.72.17)
+ - React-jsinspector (= 0.72.17)
+ - React-logger (= 0.72.17)
+ - React-perflogger (= 0.72.17)
+ - React-runtimeexecutor (= 0.72.17)
+ - React-debug (0.72.17)
+ - React-hermes (0.72.17):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- RCT-Folly/Futures (= 2021.07.22.00)
- - React-cxxreact (= 0.72.13)
+ - React-cxxreact (= 0.72.17)
- React-jsi
- - React-jsiexecutor (= 0.72.13)
- - React-jsinspector (= 0.72.13)
- - React-perflogger (= 0.72.13)
- - React-jsi (0.72.13):
+ - React-jsiexecutor (= 0.72.17)
+ - React-jsinspector (= 0.72.17)
+ - React-perflogger (= 0.72.17)
+ - React-jsi (0.72.17):
- boost (= 1.76.0)
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-jsiexecutor (0.72.13):
+ - React-jsiexecutor (0.72.17):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-cxxreact (= 0.72.13)
- - React-jsi (= 0.72.13)
- - React-perflogger (= 0.72.13)
- - React-jsinspector (0.72.13)
- - React-logger (0.72.13):
+ - React-cxxreact (= 0.72.17)
+ - React-jsi (= 0.72.17)
+ - React-perflogger (= 0.72.17)
+ - React-jsinspector (0.72.17)
+ - React-logger (0.72.17):
- glog
- react-native-config (1.5.1):
- react-native-config/App (= 1.5.1)
@@ -390,7 +390,7 @@ PODS:
- react-native-track-player (4.0.1):
- React-Core
- SwiftAudioEx (= 1.0.0)
- - React-NativeModulesApple (0.72.13):
+ - React-NativeModulesApple (0.72.17):
- hermes-engine
- React-callinvoker
- React-Core
@@ -399,17 +399,17 @@ PODS:
- React-runtimeexecutor
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- - React-perflogger (0.72.13)
- - React-RCTActionSheet (0.72.13):
- - React-Core/RCTActionSheetHeaders (= 0.72.13)
- - React-RCTAnimation (0.72.13):
- - RCT-Folly (= 2021.07.22.00)
- - RCTTypeSafety (= 0.72.13)
- - React-Codegen (= 0.72.13)
- - React-Core/RCTAnimationHeaders (= 0.72.13)
- - React-jsi (= 0.72.13)
- - ReactCommon/turbomodule/core (= 0.72.13)
- - React-RCTAppDelegate (0.72.13):
+ - React-perflogger (0.72.17)
+ - React-RCTActionSheet (0.72.17):
+ - React-Core/RCTActionSheetHeaders (= 0.72.17)
+ - React-RCTAnimation (0.72.17):
+ - RCT-Folly (= 2021.07.22.00)
+ - RCTTypeSafety (= 0.72.17)
+ - React-Codegen (= 0.72.17)
+ - React-Core/RCTAnimationHeaders (= 0.72.17)
+ - React-jsi (= 0.72.17)
+ - ReactCommon/turbomodule/core (= 0.72.17)
+ - React-RCTAppDelegate (0.72.17):
- RCT-Folly
- RCTRequired
- RCTTypeSafety
@@ -421,54 +421,54 @@ PODS:
- React-RCTNetwork
- React-runtimescheduler
- ReactCommon/turbomodule/core
- - React-RCTBlob (0.72.13):
+ - React-RCTBlob (0.72.17):
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-Codegen (= 0.72.13)
- - React-Core/RCTBlobHeaders (= 0.72.13)
- - React-Core/RCTWebSocket (= 0.72.13)
- - React-jsi (= 0.72.13)
- - React-RCTNetwork (= 0.72.13)
- - ReactCommon/turbomodule/core (= 0.72.13)
- - React-RCTImage (0.72.13):
- - RCT-Folly (= 2021.07.22.00)
- - RCTTypeSafety (= 0.72.13)
- - React-Codegen (= 0.72.13)
- - React-Core/RCTImageHeaders (= 0.72.13)
- - React-jsi (= 0.72.13)
- - React-RCTNetwork (= 0.72.13)
- - ReactCommon/turbomodule/core (= 0.72.13)
- - React-RCTLinking (0.72.13):
- - React-Codegen (= 0.72.13)
- - React-Core/RCTLinkingHeaders (= 0.72.13)
- - React-jsi (= 0.72.13)
- - ReactCommon/turbomodule/core (= 0.72.13)
- - React-RCTNetwork (0.72.13):
- - RCT-Folly (= 2021.07.22.00)
- - RCTTypeSafety (= 0.72.13)
- - React-Codegen (= 0.72.13)
- - React-Core/RCTNetworkHeaders (= 0.72.13)
- - React-jsi (= 0.72.13)
- - ReactCommon/turbomodule/core (= 0.72.13)
- - React-RCTSettings (0.72.13):
- - RCT-Folly (= 2021.07.22.00)
- - RCTTypeSafety (= 0.72.13)
- - React-Codegen (= 0.72.13)
- - React-Core/RCTSettingsHeaders (= 0.72.13)
- - React-jsi (= 0.72.13)
- - ReactCommon/turbomodule/core (= 0.72.13)
- - React-RCTText (0.72.13):
- - React-Core/RCTTextHeaders (= 0.72.13)
- - React-RCTVibration (0.72.13):
- - RCT-Folly (= 2021.07.22.00)
- - React-Codegen (= 0.72.13)
- - React-Core/RCTVibrationHeaders (= 0.72.13)
- - React-jsi (= 0.72.13)
- - ReactCommon/turbomodule/core (= 0.72.13)
- - React-rncore (0.72.13)
- - React-runtimeexecutor (0.72.13):
- - React-jsi (= 0.72.13)
- - React-runtimescheduler (0.72.13):
+ - React-Codegen (= 0.72.17)
+ - React-Core/RCTBlobHeaders (= 0.72.17)
+ - React-Core/RCTWebSocket (= 0.72.17)
+ - React-jsi (= 0.72.17)
+ - React-RCTNetwork (= 0.72.17)
+ - ReactCommon/turbomodule/core (= 0.72.17)
+ - React-RCTImage (0.72.17):
+ - RCT-Folly (= 2021.07.22.00)
+ - RCTTypeSafety (= 0.72.17)
+ - React-Codegen (= 0.72.17)
+ - React-Core/RCTImageHeaders (= 0.72.17)
+ - React-jsi (= 0.72.17)
+ - React-RCTNetwork (= 0.72.17)
+ - ReactCommon/turbomodule/core (= 0.72.17)
+ - React-RCTLinking (0.72.17):
+ - React-Codegen (= 0.72.17)
+ - React-Core/RCTLinkingHeaders (= 0.72.17)
+ - React-jsi (= 0.72.17)
+ - ReactCommon/turbomodule/core (= 0.72.17)
+ - React-RCTNetwork (0.72.17):
+ - RCT-Folly (= 2021.07.22.00)
+ - RCTTypeSafety (= 0.72.17)
+ - React-Codegen (= 0.72.17)
+ - React-Core/RCTNetworkHeaders (= 0.72.17)
+ - React-jsi (= 0.72.17)
+ - ReactCommon/turbomodule/core (= 0.72.17)
+ - React-RCTSettings (0.72.17):
+ - RCT-Folly (= 2021.07.22.00)
+ - RCTTypeSafety (= 0.72.17)
+ - React-Codegen (= 0.72.17)
+ - React-Core/RCTSettingsHeaders (= 0.72.17)
+ - React-jsi (= 0.72.17)
+ - ReactCommon/turbomodule/core (= 0.72.17)
+ - React-RCTText (0.72.17):
+ - React-Core/RCTTextHeaders (= 0.72.17)
+ - React-RCTVibration (0.72.17):
+ - RCT-Folly (= 2021.07.22.00)
+ - React-Codegen (= 0.72.17)
+ - React-Core/RCTVibrationHeaders (= 0.72.17)
+ - React-jsi (= 0.72.17)
+ - ReactCommon/turbomodule/core (= 0.72.17)
+ - React-rncore (0.72.17)
+ - React-runtimeexecutor (0.72.17):
+ - React-jsi (= 0.72.17)
+ - React-runtimescheduler (0.72.17):
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
@@ -476,30 +476,30 @@ PODS:
- React-debug
- React-jsi
- React-runtimeexecutor
- - React-utils (0.72.13):
+ - React-utils (0.72.17):
- glog
- RCT-Folly (= 2021.07.22.00)
- React-debug
- - ReactCommon/turbomodule/bridging (0.72.13):
+ - ReactCommon/turbomodule/bridging (0.72.17):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-callinvoker (= 0.72.13)
- - React-cxxreact (= 0.72.13)
- - React-jsi (= 0.72.13)
- - React-logger (= 0.72.13)
- - React-perflogger (= 0.72.13)
- - ReactCommon/turbomodule/core (0.72.13):
+ - React-callinvoker (= 0.72.17)
+ - React-cxxreact (= 0.72.17)
+ - React-jsi (= 0.72.17)
+ - React-logger (= 0.72.17)
+ - React-perflogger (= 0.72.17)
+ - ReactCommon/turbomodule/core (0.72.17):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2021.07.22.00)
- - React-callinvoker (= 0.72.13)
- - React-cxxreact (= 0.72.13)
- - React-jsi (= 0.72.13)
- - React-logger (= 0.72.13)
- - React-perflogger (= 0.72.13)
+ - React-callinvoker (= 0.72.17)
+ - React-cxxreact (= 0.72.17)
+ - React-jsi (= 0.72.17)
+ - React-logger (= 0.72.17)
+ - React-perflogger (= 0.72.17)
- RealmJS (12.13.1):
- React
- RNCClipboard (1.12.1):
@@ -509,7 +509,17 @@ PODS:
- RNGestureHandler (2.16.0):
- RCT-Folly (= 2021.07.22.00)
- React-Core
- - RNReanimated (3.8.1):
+ - RNReanimated (3.15.0):
+ - RCT-Folly (= 2021.07.22.00)
+ - React-Core
+ - ReactCommon/turbomodule/core
+ - RNReanimated/reanimated (= 3.15.0)
+ - RNReanimated/worklets (= 3.15.0)
+ - RNReanimated/reanimated (3.15.0):
+ - RCT-Folly (= 2021.07.22.00)
+ - React-Core
+ - ReactCommon/turbomodule/core
+ - RNReanimated/worklets (3.15.0):
- RCT-Folly (= 2021.07.22.00)
- React-Core
- ReactCommon/turbomodule/core
@@ -743,8 +753,8 @@ SPEC CHECKSUMS:
boost: 7dcd2de282d72e344012f7d6564d024930a6a440
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
- FBLazyVector: d5c36294933aa344046699700b9ae9c2e10db18e
- FBReactNativeSpec: ee1c8650ed1f489b699e8a20db2044b0a7f849c5
+ FBLazyVector: 66398fc2381d8fa1eee4c0f80d931587a7b927e8
+ FBReactNativeSpec: 0f8cecf999d709dba7626bbf565b1b5f8f46a5c1
Flipper: 6edb735e6c3e332975d1b17956bcc584eccf5818
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30
@@ -755,52 +765,52 @@ SPEC CHECKSUMS:
FlipperKit: 2efad7007d6745a3f95e4034d547be637f89d3f6
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
- hermes-engine: 6b116ce065327cef44156096edaa1e839ee69a03
+ hermes-engine: 982096772bd947125ee3b4f72ace6cb9a33f1d02
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
- RCTRequired: 2fb3c1ac6546029f44d04083771e60a106073853
- RCTTypeSafety: 59916726dadf1e2f3d502f2f3cf7393fe451f501
- React: a268c36512fe4ae45f55816af7dac225de38a602
- React-callinvoker: b8a85e82fe8e7d8f3b4b4ff03c292c498fe8e747
- React-Codegen: 7d097ffb2323d335832670713ba193d43664575b
- React-Core: 515d1f7f7a02d99a4d29fda4495e69c43375a035
- React-CoreModules: ce9bc448da0091a8d0d9d63f95213e52d170db31
- React-cxxreact: 4deb455d65dd11513fa8d228442005e1febedfe2
- React-debug: 30e2b157b34f426e0ad51ec6bc1b8231e7f27c48
- React-hermes: cad4ee3887376efa0c1498b33a295b00055e0644
- React-jsi: 5162e7a01e31e956147e674915fcdeab0b6e324f
- React-jsiexecutor: 08ea8c7c359e5953776d9b51eb606a02b0999c61
- React-jsinspector: 525fb67d3402476e6d2796907892137e4a632e35
- React-logger: c3a0ff270a52e0051489f1f29a5de047ba7e8ca0
+ RCTRequired: 01c639ec840ee03928b2d65f5cd5297d737b3834
+ RCTTypeSafety: 9623592521a1576363baf3d6ab8d164cfe9062bf
+ React: 3c0beeda318c3c515a6bb2c1f197b55bd731aa43
+ React-callinvoker: 0cd6ff2cdd80255c82cd4628fc925df1e7133a1a
+ React-Codegen: 20cfee78965306e4a5bb65d95958142eda116cfc
+ React-Core: df691c59e0c8a3db4d138a51bb8862c52c8b14f1
+ React-CoreModules: cebd223e814ac07bc1f597bbd2480167a2c7a130
+ React-cxxreact: dec3959d439708cb7dd73b46a11ed64c3eea79da
+ React-debug: 3a5091cbda7ffe5f11ad0443109810fcd1a3e885
+ React-hermes: f3b6b278c4ff7e6664a86b2bf964a4dc4ae72d34
+ React-jsi: 6ec4bd4cd929ae9d468b4984b0ae2c657aeeb2da
+ React-jsiexecutor: 8dc585381e476c3ff2e9468f444c90c4d1d5b874
+ React-jsinspector: 853b8631b908636bb09ef77cb217376c38a0c8ff
+ React-logger: 9ca44bb5703bf2355f3c2d2e5e67bfe98ca2dc34
react-native-config: 86038147314e2e6d10ea9972022aa171e6b1d4d8
react-native-keep-awake: afad8a51dfef9fe9655a6344771be32c8596d774
react-native-netinfo: 076df4f9b07f6670acf4ce9a75aac8d34c2e2ccc
react-native-quick-md5: 5928fdcedcb1b2f0e1958a38769283968b8eb850
react-native-safe-area-context: 2cd91d532de12acdb0a9cbc8d43ac72a8e4c897c
react-native-track-player: 97d76dbbd35f27cc709e5f04540615e54264b3f9
- React-NativeModulesApple: 83cd035f454ac54f4f72ce7b01c34054fa6d60f8
- React-perflogger: de3900e86d8a4d896999b08e0b130ad8b83ebf11
- React-RCTActionSheet: 92433ffdf1b479aab3fe32250047f100a10ce59f
- React-RCTAnimation: 6f116d7b25705f185f48dedf65eb4e98f98d860c
- React-RCTAppDelegate: efc96d53a6673082eb6b2d8c91a7ef349b639f7a
- React-RCTBlob: 088c2942e3b171b4f8dba9b7f48aa5fff5c2ab97
- React-RCTImage: 66b3b6d840a3b329b2406956a58db80f1344d097
- React-RCTLinking: 032518be6aebadd7f032f23c8975be98eff042cb
- React-RCTNetwork: dc775d132167fcdfcb3bba4b11d57cf07a834a3e
- React-RCTSettings: 0bd22fc8293bc396cdb0769a12830273c8c5096b
- React-RCTText: d312733e5008713f7293e3d44f41f34d432cd4b0
- React-RCTVibration: 754103c3c8c09d6fd2e1ee49d9a32f53e9f2d615
- React-rncore: 15d88d097c7928f0c1ef28070b0d81bc3f372854
- React-runtimeexecutor: 318cd0b384ccb12a2f86870f8591b759caad7f7d
- React-runtimescheduler: 8dd5ce1d291c417dd90cd651eccc70821c237a38
- React-utils: 46700f6d6cacc4288f897b94571bf92895678885
- ReactCommon: 71dc6b65f95ea81a83490115d7a5fc62378ecdcc
+ React-NativeModulesApple: 2edfcbb25329e3eb5f76eb79d89010de7c1c6f1f
+ React-perflogger: 785b0063af5178298a61b54bb46aae9a19c7bbb5
+ React-RCTActionSheet: 84f37b34bd77249263ace75471d6664393c29972
+ React-RCTAnimation: 5713910b6223154df4bba80a0bda4e2e671b00f8
+ React-RCTAppDelegate: d3777d05bf6b65fed847536af520731c1a167dea
+ React-RCTBlob: d4d3fb21c0bf1ce2f0308e05227ecd3f19266bf7
+ React-RCTImage: 2e63a483be5d4e46a80dea3b17c9abee38006feb
+ React-RCTLinking: e3ff685ee62187f8f61e938357307c1f890125b5
+ React-RCTNetwork: a35842997a403edfdc1ec25b61a0e10a0526368d
+ React-RCTSettings: aef81e0ac54268d2928ad31c4f91056cc75e5ce9
+ React-RCTText: 7becec5f53f03b20da11f4b7e40e6bcfd476d134
+ React-RCTVibration: defaae8016de9b3351a2a67ee8ef3fbdd643b0e1
+ React-rncore: dfd20469cfad38e48b1c3cc9c4367db63f5231d7
+ React-runtimeexecutor: 448409b5ae5a01b7793239f630051960c7dd39f9
+ React-runtimescheduler: ff30efdf24f8ce62eb517a391ded3d99c4263bb0
+ React-utils: 7959d4553163b61e01bbe83dbd80e58ca420aecb
+ ReactCommon: 841449721eb2e004de2c3366844b0a03f329f2cb
RealmJS: 325a7b621587dd9945306d4cbfd6b641bc20e2dd
RNCClipboard: d77213bfa269013bf4b857b7a9ca37ee062d8ef1
RNDeviceInfo: db5c64a060e66e5db3102d041ebe3ef307a85120
RNGestureHandler: b83cf821f60b7ec59827f0ed9e5b8c46b1de2c99
- RNReanimated: fc36806836aca984b797f01432abe31689663421
+ RNReanimated: c2027c397233801d4aceb7375e000ac87ec2e67d
RNScreens: bea7b9767050d69e4b9da14639cdd5c8f5a37881
RNSVG: d00c8f91c3cbf6d476451313a18f04d220d4f396
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
@@ -808,9 +818,9 @@ SPEC CHECKSUMS:
RollbarReactNative: 11595d077e6e53c3dff7846f6bd53f23f68be338
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
SwiftAudioEx: 6f511018b7a0fdfd14ed1bb4081f953588245cc0
- Yoga: 6e6ec2d9e1eb4ecb97be4dc9bdd91f7cae2adf98
+ Yoga: ef534101bb891fb09bae657417f34d399c1efe38
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
PODFILE CHECKSUM: 1763ab552ba7dac3473e47862d7ac2d6963d0f49
-COCOAPODS: 1.14.2
+COCOAPODS: 1.15.2
diff --git a/ios/PrivacyInfo.xcprivacy b/ios/PrivacyInfo.xcprivacy
index 41b8317f..9919625f 100644
--- a/ios/PrivacyInfo.xcprivacy
+++ b/ios/PrivacyInfo.xcprivacy
@@ -12,6 +12,14 @@
C617.1
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryDiskSpace
+ NSPrivacyAccessedAPITypeReasons
+
+ E174.1
+
+
NSPrivacyAccessedAPIType
NSPrivacyAccessedAPICategoryUserDefaults
diff --git a/ios/ci_scripts/ci_post_clone.sh b/ios/ci_scripts/ci_post_clone.sh
index 9c44f9f5..7fb233ea 100755
--- a/ios/ci_scripts/ci_post_clone.sh
+++ b/ios/ci_scripts/ci_post_clone.sh
@@ -7,7 +7,7 @@ echo "WHATSAPP_USER_GROUP_LINK=${WHATSAPP_USER_GROUP_LINK}" >> ../../.env
echo "===== Installing CocoaPods ====="
export HOMEBREW_NO_INSTALL_CLEANUP=TRUE
-brew install cocoapods@1.14.2
+brew install cocoapods
echo "===== Installing Node.js ====="
# installs nvm (Node Version Manager)
diff --git a/ios/hymnbook2/Info.plist b/ios/hymnbook2/Info.plist
index 505d9813..b6a16060 100644
--- a/ios/hymnbook2/Info.plist
+++ b/ios/hymnbook2/Info.plist
@@ -17,11 +17,11 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.19.0-SNAPSHOT
+ 1.17.18
CFBundleSignature
nl.sajansen.hymnbook2
CFBundleVersion
- 672
+ 658.14
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
diff --git a/package.json b/package.json
index f525f70e..0a14ae8d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "hymnbook2",
- "version": "1.19.0-SNAPSHOT",
+ "version": "1.17.18",
"private": true,
"scripts": {
"android": "react-native run-android",
@@ -11,6 +11,7 @@
"bundle": "cd android && ./gradlew bundleRelease",
"build": "cd android && ./gradlew assembleRelease",
"device-install": "adb -d install \"$(ls -t ./android/app/build/outputs/apk/release/*.apk | head -1)\"",
+ "device-install-windows": "adb -d install \"./android/app/build/outputs/apk/release/$(Get-ChildItem -Path ./android/app/build/outputs/apk/release/*.apk -Name | select -first 1)\"",
"postinstall": "patch-package"
},
"dependencies": {
@@ -20,7 +21,7 @@
"@react-navigation/bottom-tabs": "6.5.11",
"@react-navigation/native": "6.1.9",
"@react-navigation/native-stack": "6.9.17",
- "@realm/react": "0.10.0",
+ "@realm/react": "0.11.0",
"abcjs": "git+https://github.com/paulrosen/abcjs.git#6.2.3",
"fastest-levenshtein": "1.0.16",
"fetch-retry": "6.0.0",
diff --git a/source/gui/screens/downloads/DownloadDocumentsScreen.tsx b/source/gui/screens/downloads/DownloadDocumentsScreen.tsx
index 8e1474dc..ec68be40 100644
--- a/source/gui/screens/downloads/DownloadDocumentsScreen.tsx
+++ b/source/gui/screens/downloads/DownloadDocumentsScreen.tsx
@@ -5,10 +5,10 @@ import { DocumentProcessor } from "../../../logic/documents/documentProcessor";
import { DocumentServer } from "../../../logic/documents/documentServer";
import { DeepLinking } from "../../../logic/deeplinking";
import { rollbar } from "../../../logic/rollbar";
-import { alertAndThrow, languageAbbreviationToFullName, sanitizeErrorForRollbar } from "../../../logic/utils";
+import { languageAbbreviationToFullName, sanitizeErrorForRollbar } from "../../../logic/utils";
import { itemCountPerLanguage } from "./common";
import { ThemeContextProps, useTheme } from "../../components/providers/ThemeProvider";
-import { useIsMounted } from "../../components/utils";
+import { debounce, useIsMounted } from "../../components/utils";
import {
Alert,
RefreshControl,
@@ -22,6 +22,13 @@ import ConfirmationModal from "../../components/popups/ConfirmationModal";
import LanguageSelectBar, { ShowAllLanguagesValue } from "./LanguageSelectBar";
import { DocumentUpdater } from "../../../logic/documents/updater/documentUpdater";
import { useUpdaterContext } from "../../components/providers/UpdaterContextProvider";
+import Db from "../../../logic/db/db";
+import { DocumentGroupSchema } from "../../../logic/db/models/DocumentsSchema";
+import { CollectionChangeSet, OrderedCollection } from "realm";
+
+type ServerDataType = ServerDocumentGroup;
+type LocalDataType = LocalDocumentGroup;
+type ItemType = DocumentGroup;
interface ComponentProps {
setIsProcessing?: (value: boolean) => void;
@@ -35,14 +42,16 @@ const DownloadDocumentsScreen: React.FC = ({
dismissPromptForUuid
}) => {
const isMounted = useIsMounted();
- const [isLoading, setIsLoading] = useState(false);
- const [isLocalGroupsLoading, setIsLocalGroupsLoading] = useState(true);
- const [isGroupLoading, setIsGroupLoading] = useState(false);
- const [serverGroups, setServerGroups] = useState([]);
- const [localGroups, setLocalGroups] = useState([]);
- const [requestDownloadForGroup, setRequestDownloadForGroup] = useState(undefined);
- const [requestUpdateForGroup, setRequestUpdateForGroup] = useState(undefined);
- const [requestDeleteForGroup, setRequestDeleteForGroup] = useState(undefined);
+ const [isProcessingLocalData, setIsProcessingLocalData] = useState(false);
+ const [isServerDataLoading, setIsServerDataLoading] = useState(false);
+ const [isLocalDataLoading, setIsLocalDataLoading] = useState(true);
+ const [isSpecificItemLoading, setIsSpecificItemLoading] = useState(false);
+
+ const [serverData, setServerData] = useState([]);
+ const [localData, setLocalData] = useState([]);
+ const [requestDownloadForItem, setRequestDownloadForItem] = useState(undefined);
+ const [requestUpdateForItem, setRequestUpdateForItem] = useState(undefined);
+ const [requestDeleteForItem, setRequestDeleteForItem] = useState(undefined);
const [filterLanguage, setFilterLanguage] = useState("");
const updaterContext = useUpdaterContext();
const styles = createStyles(useTheme());
@@ -53,41 +62,83 @@ const DownloadDocumentsScreen: React.FC = ({
}, []);
const onOpen = () => {
- loadLocalDocumentGroups();
- fetchDocumentGroups();
+ fetchServerData();
+ try {
+ Db.documents.realm().objects(DocumentGroupSchema.name)
+ .filtered(`isRoot = true`)
+ .addListener(onCollectionChange);
+ } catch (error) {
+ rollbar.error("Failed to handle DocumentGroup collection change", sanitizeErrorForRollbar(error));
+ }
};
const onClose = () => {
+ Db.documents.realm().objects(DocumentGroupSchema.name)
+ .filtered(`isRoot = true`)
+ .removeListener(onCollectionChange);
};
useEffect(() => {
if (!isMounted()) return;
// Let user navigate when the screen is still loading the data
- if (serverGroups.length === 0) {
- return;
- }
- setIsProcessing?.(isLoading);
- }, [isLoading]);
+ if (serverData.length === 0) return;
+
+ setIsProcessing?.(isProcessingLocalData);
+ }, [isProcessingLocalData]);
useEffect(() => {
- if (isLocalGroupsLoading) return;
- loadAndPromptSpecificGroup();
- }, [promptForUuid, isLocalGroupsLoading]);
+ if (isLocalDataLoading) return;
+ loadAndPromptSpecificItem();
+ }, [promptForUuid, isLocalDataLoading]);
+
+ const processLocalDataChanges = (collection: OrderedCollection & ItemType>) => {
+ if (!isMounted()) return;
+ setIsLocalDataLoading(true);
+
+ try {
+ const data: ItemType[] = collection
+ .sorted(`name`)
+ .map(it => DocumentGroup.clone(it, { includeChildren: true, includeParent: false }))
+
+ const distinctData: ItemType[] = [];
+ data.forEach(item => {
+ if (distinctData.some(it => it.uuid == item.uuid)) return;
+ distinctData.push(item);
+ })
- const loadAndPromptSpecificGroup = () => {
+ setLocalData(distinctData);
+
+ if (filterLanguage === "") {
+ setFilterLanguage(DocumentProcessor.determineDefaultFilterLanguage(distinctData));
+ }
+ } catch (error) {
+ rollbar.error("Failed to load local DocumentGroups from collection change", sanitizeErrorForRollbar(error));
+ }
+
+ setIsLocalDataLoading(false);
+ };
+
+ const processLocalDataChangesDebounced = debounce(processLocalDataChanges, 300);
+
+ const onCollectionChange = (collection: OrderedCollection & ItemType>, changes: CollectionChangeSet) => {
+ if (!isMounted()) return;
+ processLocalDataChangesDebounced(collection, changes);
+ }
+
+ const loadAndPromptSpecificItem = () => {
if (!promptForUuid) return;
- if (localGroups.find(it => it.uuid === promptForUuid)) return;
+ if (localData.find(it => it.uuid === promptForUuid)) return;
- setIsGroupLoading(true);
+ setIsSpecificItemLoading(true);
DocumentServer.fetchDocumentGroup({ uuid: promptForUuid }, {})
.then(data => {
if (!isMounted()) return;
- if (localGroups.find(it => it.uuid === promptForUuid)) return;
+ if (localData.find(it => it.uuid === promptForUuid)) return;
setFilterLanguage(data.language);
- setRequestDownloadForGroup(data);
+ setRequestDownloadForItem(data);
})
.catch(error => {
if (error.name == "TypeError" && error.message == "Network request failed") {
@@ -100,41 +151,16 @@ const DownloadDocumentsScreen: React.FC = ({
dismissPromptForUuid?.();
if (!isMounted()) return;
- setIsGroupLoading(false);
+ setIsSpecificItemLoading(false);
});
};
- const loadLocalDocumentGroups = () => {
- setIsLoading(true);
- setIsLocalGroupsLoading(true);
-
- let data: DocumentGroup[] = [];
- try {
- data = DocumentProcessor.loadLocalDocumentRoot()
- .map(it => DocumentGroup.clone(it, {includeChildren: true, includeParent: false}))
- } catch (error) {
- rollbar.error("Cannot load local document groups", sanitizeErrorForRollbar(error));
- return alertAndThrow(error);
- }
-
- if (!isMounted()) return;
-
- setLocalGroups(data);
-
- if (filterLanguage === "") {
- setFilterLanguage(DocumentProcessor.determineDefaultFilterLanguage(data));
- }
-
- setIsLoading(false);
- setIsLocalGroupsLoading(false);
- };
-
- const fetchDocumentGroups = () => {
- setIsLoading(true);
+ const fetchServerData = () => {
+ setIsServerDataLoading(true);
DocumentServer.fetchDocumentGroups()
.then(data => {
if (!isMounted()) return;
- setServerGroups(data);
+ setServerData(data);
})
.catch(error => {
if (error.name == "TypeError" && error.message == "Network request failed") {
@@ -145,147 +171,138 @@ const DownloadDocumentsScreen: React.FC = ({
})
.finally(() => {
if (!isMounted()) return;
- setIsLoading(false);
+ setIsServerDataLoading(false);
});
};
const applyUuidUpdateForPullRequest8 = () => {
- DocumentUpdater.updateLocalGroupsWithUuid(localGroups, serverGroups);
+ DocumentUpdater.updateLocalGroupsWithUuid(localData, serverData);
};
- useEffect(applyUuidUpdateForPullRequest8, [serverGroups]);
+ useEffect(applyUuidUpdateForPullRequest8, [serverData]);
- const isPopupOpen = () => requestDeleteForGroup !== undefined || requestDownloadForGroup !== undefined;
+ const isPopupOpen = () => requestDeleteForItem !== undefined || requestDownloadForItem !== undefined;
- const shareDocumentGroup = (it: LocalDocumentGroup | ServerDocumentGroup) => {
+ const shareItem = (it: LocalDataType | ServerDataType) => {
return Share.share({
message: `Download documents for ${it.name}\n\n${DeepLinking.generateLinkForDocumentGroup(it)}`
})
- .then(r => rollbar.debug("Document group shared.", {
+ .then(r => rollbar.debug("DocumentGroup shared.", {
shareAction: r,
bundle: it
}))
- .catch(error => rollbar.warning("Failed to share document group", {
+ .catch(error => rollbar.warning("Failed to share DocumentGroup", {
...sanitizeErrorForRollbar(error),
bundle: it
}));
};
- const onDocumentGroupPress = (group: ServerDocumentGroup) => {
- if (isLoading || isPopupOpen()) {
- return;
- }
+ const onServerItemPress = (item: ServerDataType) => {
+ if (isProcessingLocalData || isPopupOpen()) return;
- setRequestDownloadForGroup(group);
+ setRequestDownloadForItem(item);
};
- const onLocalDocumentGroupPress = (group: LocalDocumentGroup) => {
- if (isLoading || isPopupOpen()) {
- return;
- }
+ const onLocalItemPress = (item: LocalDataType) => {
+ if (isProcessingLocalData || isPopupOpen()) return;
- if (DocumentProcessor.hasUpdate(serverGroups, group)) {
- const serverGroup = DocumentProcessor.getMatchingServerGroup(serverGroups, group);
- if (serverGroup !== undefined) {
- return setRequestUpdateForGroup(serverGroup);
+ if (DocumentProcessor.hasUpdate(serverData, item)) {
+ const serverItem = DocumentProcessor.getMatchingServerGroup(serverData, item);
+ if (serverItem !== undefined) {
+ return setRequestUpdateForItem(serverItem);
}
}
- setRequestDeleteForGroup(group);
+ setRequestDeleteForItem(item);
};
- const onConfirmDownloadDocumentGroup = () => {
- const group = requestDownloadForGroup;
- setRequestDownloadForGroup(undefined);
+ const onConfirmDownload = () => {
+ const item = requestDownloadForItem;
+ setRequestDownloadForItem(undefined);
- if (isLoading || group === undefined) return;
+ if (isProcessingLocalData || item === undefined) return;
- const isUpdating = updaterContext.documentGroupsUpdating.some(it => it.uuid === group.uuid);
+ const isUpdating = updaterContext.documentGroupsUpdating.some(it => it.uuid === item.uuid);
if (isUpdating) return;
- downloadDocumentGroup(group);
+ downloadItem(item);
};
- const onConfirmUpdateDocumentGroup = () => {
- const group = requestUpdateForGroup;
- setRequestUpdateForGroup(undefined);
+ const onConfirmUpdate = () => {
+ const item = requestUpdateForItem;
+ setRequestUpdateForItem(undefined);
- if (isLoading || group === undefined) return;
+ if (isProcessingLocalData || item === undefined) return;
- const isUpdating = updaterContext.documentGroupsUpdating.some(it => it.uuid === group.uuid);
+ const isUpdating = updaterContext.documentGroupsUpdating.some(it => it.uuid === item.uuid);
if (isUpdating) return;
- updateDocumentGroup(group);
+ updateItem(item);
};
- const downloadDocumentGroup = (group: ServerDocumentGroup) => saveDocumentGroup(group, false);
- const updateDocumentGroup = (group: ServerDocumentGroup) => saveDocumentGroup(group, true);
+ const downloadItem = (item: ServerDataType) => saveItem(item, false);
+ const updateItem = (item: ServerDataType) => saveItem(item, true);
- const saveDocumentGroup = (group: ServerDocumentGroup, isUpdate: boolean) => {
+ const saveItem = (item: ServerDataType, isUpdate: boolean) => {
if (!isMounted()) return;
- setIsLoading(true);
- updaterContext.addDocumentGroupUpdating(group);
+ setIsProcessingLocalData(true);
+ updaterContext.addDocumentGroupUpdating(item);
const call = isUpdate
- ? DocumentUpdater.fetchAndUpdateDocumentGroup(group)
- : DocumentUpdater.fetchAndSaveDocumentGroup(group)
+ ? DocumentUpdater.fetchAndUpdateDocumentGroup(item)
+ : DocumentUpdater.fetchAndSaveDocumentGroup(item);
call
- .then(() => Alert.alert("Success", `${group.name} ${isUpdate ? "updated" : "added"}!`))
+ .then(() => Alert.alert("Success", `${item.name} ${isUpdate ? "updated" : "added"}!`))
.catch(error => {
- rollbar.error("Failed to import document group", {
+ rollbar.error("Failed to import DocumentGroup", {
...sanitizeErrorForRollbar(error),
isUpdate: isUpdate,
- group: group,
+ item: item,
});
if (error.name == "TypeError" && error.message == "Network request failed") {
- Alert.alert("Error", `Could not ${isUpdate ? "update" : "download"} ${group.name}. Make sure your internet connection is working or try again later.`)
+ Alert.alert("Error", `Could not ${isUpdate ? "update" : "download"} ${item.name}. Make sure your internet connection is working or try again later.`);
} else {
- Alert.alert("Error", `Could not ${isUpdate ? "update" : "download"} ${group.name}. \n${error}\n\nTry again later.`);
+ Alert.alert("Error", `Could not ${isUpdate ? "update" : "download"} ${item.name}. \n${error}\n\nTry again later.`);
}
})
.finally(() => {
if (!isMounted()) return;
- setLocalGroups([]);
- setIsLoading(false);
- loadLocalDocumentGroups();
+ setIsProcessingLocalData(false);
})
.finally(() => {
// Do this here after the state has been called, otherwise we get realm invalidation errors
- updaterContext.removeDocumentGroupUpdating(group);
+ updaterContext.removeDocumentGroupUpdating(item);
})
};
- const onConfirmDeleteDocumentGroup = () => {
- const group = requestDeleteForGroup;
- setRequestDeleteForGroup(undefined);
+ const onConfirmDelete = () => {
+ const item = requestDeleteForItem;
+ setRequestDeleteForItem(undefined);
- if (isLoading || group === undefined) return;
+ if (isProcessingLocalData || item === undefined) return;
- const isUpdating = updaterContext.documentGroupsUpdating.some(it => it.uuid === group.uuid);
+ const isUpdating = updaterContext.documentGroupsUpdating.some(it => it.uuid === item.uuid);
if (isUpdating) {
Alert.alert("Could not delete", "This group is being updated. Please wait until this operation is done and try again.")
return;
}
- deleteDocumentGroup(group);
+ deleteItem(item);
};
- const deleteDocumentGroup = (group: LocalDocumentGroup) => {
- setIsLoading(true);
- updaterContext.removeDocumentGroupUpdating(group);
+ const deleteItem = (item: LocalDataType) => {
+ setIsProcessingLocalData(true);
+ updaterContext.removeDocumentGroupUpdating(item);
- const result = DocumentProcessor.deleteDocumentGroup(group);
+ const result = DocumentProcessor.deleteDocumentGroup(item);
result.alert();
+ setIsProcessingLocalData(false);
result.throwIfException();
-
- if (!isMounted()) return;
-
- loadLocalDocumentGroups();
};
- const getAllLanguagesFromGroups = (groups: ServerDocumentGroup[]) => {
- const languages = DocumentProcessor.getAllLanguagesFromDocumentGroups(groups);
+ const getAllLanguagesFromServerData = (data: ServerDataType[]) => {
+ const languages = DocumentProcessor.getAllLanguagesFromDocumentGroups(data);
if (languages.length > 0 && filterLanguage === "") {
if (languages.includes("AF")) {
@@ -301,71 +318,69 @@ const DownloadDocumentsScreen: React.FC = ({
const isOfSelectedLanguage = (it: { language: string }) =>
filterLanguage === ShowAllLanguagesValue || it.language.toUpperCase() === filterLanguage.toUpperCase();
- return (
-
- setRequestDownloadForGroup(undefined)}
- onConfirm={onConfirmDownloadDocumentGroup}
- invertConfirmColor={true}
- message={`Download documents for ${requestDownloadForGroup?.name}?`} />
-
- setRequestUpdateForGroup(undefined)}
- onConfirm={onConfirmUpdateDocumentGroup}
- invertConfirmColor={true}
- message={`Update ${requestUpdateForGroup?.name}?`} />
-
- setRequestDeleteForGroup(undefined)}
- onConfirm={onConfirmDeleteDocumentGroup}
- message={`Delete all documents for ${requestDeleteForGroup?.name}?`} />
-
- Select documents to download or delete:
-
-
-
- }>
-
- {localGroups
- .filter(isOfSelectedLanguage)
- .map((group: LocalDocumentGroup) =>
- )}
-
- {serverGroups
- .filter(it => !DocumentProcessor.isGroupLocal(localGroups, it))
- .filter(isOfSelectedLanguage)
- .map((group: ServerDocumentGroup) =>
- )}
-
- {serverGroups.length > 0 ? undefined :
-
- {isLoading || isGroupLoading ? "Loading..." : "No online data available..."}
-
- }
- {isLoading || serverGroups.length === 0 || serverGroups.filter(isOfSelectedLanguage).length > 0 ? undefined :
-
- No documents found for language "{languageAbbreviationToFullName(filterLanguage)}"...
-
- }
-
-
- );
+ return
+ setRequestDownloadForItem(undefined)}
+ onConfirm={onConfirmDownload}
+ invertConfirmColor={true}
+ message={`Download documents for ${requestDownloadForItem?.name}?`} />
+
+ setRequestUpdateForItem(undefined)}
+ onConfirm={onConfirmUpdate}
+ invertConfirmColor={true}
+ message={`Update ${requestUpdateForItem?.name}?`} />
+
+ setRequestDeleteForItem(undefined)}
+ onConfirm={onConfirmDelete}
+ message={`Delete all documents for ${requestDeleteForItem?.name}?`} />
+
+ Select documents to download or delete:
+
+
+
+ }>
+
+ {localData
+ .filter(isOfSelectedLanguage)
+ .map((item: LocalDataType) =>
+ )}
+
+ {serverData
+ .filter(it => !DocumentProcessor.isGroupLocal(localData, it))
+ .filter(isOfSelectedLanguage)
+ .map((item: ServerDataType) =>
+ )}
+
+ {serverData.length > 0 ? undefined :
+
+ {isServerDataLoading || isSpecificItemLoading ? "Loading..." : "No online data available..."}
+
+ }
+ {isLocalDataLoading || isServerDataLoading || serverData.length === 0 || serverData.filter(isOfSelectedLanguage).length > 0 ? undefined :
+
+ No documents found for language "{languageAbbreviationToFullName(filterLanguage)}"...
+
+ }
+
+ ;
};
export default DownloadDocumentsScreen;
@@ -386,7 +401,10 @@ const createStyles = ({ colors }: ThemeContextProps) => StyleSheet.create({
color: colors.text.default
},
- listContainer: { flex: 1 },
+ listContainer: {
+ flex: 1,
+ minHeight: 100,
+ },
emptyListText: {
padding: 20,
diff --git a/source/gui/screens/downloads/DownloadSongsScreen.tsx b/source/gui/screens/downloads/DownloadSongsScreen.tsx
index 8e8d07d4..728ae183 100644
--- a/source/gui/screens/downloads/DownloadSongsScreen.tsx
+++ b/source/gui/screens/downloads/DownloadSongsScreen.tsx
@@ -9,7 +9,7 @@ import { DeepLinking } from "../../../logic/deeplinking";
import { alertAndThrow, languageAbbreviationToFullName, sanitizeErrorForRollbar } from "../../../logic/utils";
import { itemCountPerLanguage } from "./common";
import { ThemeContextProps, useTheme } from "../../components/providers/ThemeProvider";
-import { useIsMounted } from "../../components/utils";
+import { debounce, useIsMounted } from "../../components/utils";
import {
Alert,
RefreshControl,
@@ -24,6 +24,13 @@ import LanguageSelectBar, { ShowAllLanguagesValue } from "./LanguageSelectBar";
import UrlLink from "../../components/UrlLink";
import { SongUpdater } from "../../../logic/songs/updater/songUpdater";
import { useUpdaterContext } from "../../components/providers/UpdaterContextProvider";
+import Db from "../../../logic/db/db";
+import { SongBundleSchema } from "../../../logic/db/models/SongsSchema";
+import { CollectionChangeSet, OrderedCollection } from "realm";
+
+type ServerDataType = ServerSongBundle;
+type LocalDataType = LocalSongBundle;
+type ItemType = SongBundle;
interface ComponentProps {
setIsProcessing?: (value: boolean) => void;
@@ -31,16 +38,22 @@ interface ComponentProps {
dismissPromptForUuid?: () => void;
}
-const DownloadSongsScreen: React.FC = ({ setIsProcessing, promptForUuid, dismissPromptForUuid }) => {
+const DownloadSongsScreen: React.FC = ({
+ setIsProcessing,
+ promptForUuid,
+ dismissPromptForUuid
+ }) => {
const isMounted = useIsMounted();
- const [isLoading, setIsLoading] = useState(false);
- const [isLocalBundlesLoading, setIsLocalBundlesLoading] = useState(true);
- const [isBundleLoading, setIsBundleLoading] = useState(false);
- const [serverBundles, setServerBundles] = useState([]);
- const [localBundles, setLocalBundles] = useState([]);
- const [requestDownloadForBundle, setRequestDownloadForBundle] = useState(undefined);
- const [requestUpdateForBundle, setRequestUpdateForBundle] = useState(undefined);
- const [requestDeleteForBundle, setRequestDeleteForBundle] = useState(undefined);
+ const [isProcessingLocalData, setIsProcessingLocalData] = useState(false);
+ const [isServerDataLoading, setIsServerDataLoading] = useState(false);
+ const [isLocalDataLoading, setIsLocalDataLoading] = useState(true);
+ const [isSpecificItemLoading, setIsSpecificItemLoading] = useState(false);
+
+ const [serverData, setServerData] = useState([]);
+ const [localData, setLocalData] = useState([]);
+ const [requestDownloadForItem, setRequestDownloadForItem] = useState(undefined);
+ const [requestUpdateForItem, setRequestUpdateForItem] = useState(undefined);
+ const [requestDeleteForItem, setRequestDeleteForItem] = useState(undefined);
const [filterLanguage, setFilterLanguage] = useState("");
const updaterContext = useUpdaterContext();
const styles = createStyles(useTheme());
@@ -51,41 +64,79 @@ const DownloadSongsScreen: React.FC = ({ setIsProcessing, prompt
}, []);
const onOpen = () => {
- loadLocalSongBundles();
- fetchSongBundles();
+ fetchServerData();
+ try {
+ Db.songs.realm().objects(SongBundleSchema.name).addListener(onCollectionChange);
+ } catch (error) {
+ rollbar.error("Failed to handle SongBundle collection change", sanitizeErrorForRollbar(error));
+ }
};
const onClose = () => {
+ Db.songs.realm().objects(SongBundleSchema.name).removeListener(onCollectionChange);
};
useEffect(() => {
if (!isMounted()) return;
// Let user navigate when the screen is still loading the data
- if (serverBundles.length === 0) {
- return;
- }
- setIsProcessing?.(isLoading);
- }, [isLoading]);
+ if (serverData.length === 0) return;
+
+ setIsProcessing?.(isProcessingLocalData);
+ }, [isProcessingLocalData]);
useEffect(() => {
- if (isLocalBundlesLoading) return;
- loadAndPromptSpecificBundle();
- }, [promptForUuid, isLocalBundlesLoading]);
+ if (isLocalDataLoading) return;
+ loadAndPromptSpecificItem();
+ }, [promptForUuid, isLocalDataLoading]);
+
+ const processLocalDataChanges = (collection: OrderedCollection & ItemType>) => {
+ if (!isMounted()) return;
+ setIsLocalDataLoading(true);
+
+ try {
+ const data: ItemType[] = collection
+ .sorted(`name`)
+ .map(it => SongBundle.clone(it, { includeSongs: true, includeVerses: false }))
+
+ const distinctData: ItemType[] = [];
+ data.forEach(item => {
+ if (distinctData.some(it => it.uuid == item.uuid)) return;
+ distinctData.push(item);
+ })
+
+ setLocalData(distinctData);
- const loadAndPromptSpecificBundle = () => {
+ if (filterLanguage === "") {
+ setFilterLanguage(SongProcessor.determineDefaultFilterLanguage(distinctData));
+ }
+ } catch (error) {
+ rollbar.error("Failed to load local SongBundles from collection change", sanitizeErrorForRollbar(error));
+ }
+
+ setIsLocalDataLoading(false);
+ };
+
+ const processLocalDataChangesDebounced = debounce(processLocalDataChanges, 300);
+
+ const onCollectionChange = (collection: OrderedCollection & ItemType>, changes: CollectionChangeSet) => {
+ if (!isMounted()) return;
+ processLocalDataChangesDebounced(collection, changes);
+ }
+
+ const loadAndPromptSpecificItem = () => {
if (!promptForUuid) return;
- if (localBundles.find(it => it.uuid === promptForUuid)) return;
+ if (localData.find(it => it.uuid === promptForUuid)) return;
- setIsBundleLoading(true);
+ setIsSpecificItemLoading(true);
Server.fetchSongBundle({ uuid: promptForUuid }, {})
.then(data => {
if (!isMounted()) return;
- if (localBundles.find(it => it.uuid === promptForUuid)) return;
+ if (localData.find(it => it.uuid === promptForUuid)) return;
setFilterLanguage(data.language);
- setRequestDownloadForBundle(data);
+ setRequestDownloadForItem(data);
})
.catch(error => {
if (error.name == "TypeError" && error.message == "Network request failed") {
@@ -98,41 +149,16 @@ const DownloadSongsScreen: React.FC = ({ setIsProcessing, prompt
dismissPromptForUuid?.();
if (!isMounted()) return;
- setIsBundleLoading(false);
+ setIsSpecificItemLoading(false);
});
};
- const loadLocalSongBundles = () => {
- setIsLoading(true);
- setIsLocalBundlesLoading(true);
-
- let data: SongBundle[] = [];
- try {
- data = SongProcessor.loadLocalSongBundles()
- .map(it => SongBundle.clone(it, { includeSongs: true, includeVerses: false }))
- } catch (error) {
- rollbar.error("Cannot load local song bundles", sanitizeErrorForRollbar(error));
- return alertAndThrow(error);
- }
-
- if (!isMounted()) return;
-
- setLocalBundles(data);
-
- if (filterLanguage === "") {
- setFilterLanguage(SongProcessor.determineDefaultFilterLanguage(data));
- }
-
- setIsLoading(false);
- setIsLocalBundlesLoading(false);
- };
-
- const fetchSongBundles = () => {
- setIsLoading(true);
+ const fetchServerData = () => {
+ setIsServerDataLoading(true);
Server.fetchSongBundles()
.then(data => {
if (!isMounted()) return;
- setServerBundles(data);
+ setServerData(data);
})
.catch(error => {
if (error.name == "TypeError" && error.message == "Network request failed") {
@@ -143,150 +169,144 @@ const DownloadSongsScreen: React.FC = ({ setIsProcessing, prompt
})
.finally(() => {
if (!isMounted()) return;
- setIsLoading(false);
+ setIsServerDataLoading(false);
});
};
const applyUuidUpdateForPullRequest8 = () => {
- SongUpdater.updateLocalBundlesWithUuid(localBundles, serverBundles);
+ SongUpdater.updateLocalBundlesWithUuid(localData, serverData);
};
- useEffect(applyUuidUpdateForPullRequest8, [serverBundles]);
+ useEffect(applyUuidUpdateForPullRequest8, [serverData]);
- const isPopupOpen = () => requestDeleteForBundle !== undefined || requestDownloadForBundle !== undefined;
+ const isPopupOpen = () => requestDeleteForItem !== undefined || requestDownloadForItem !== undefined;
- const shareSongBundle = (it: LocalSongBundle | ServerSongBundle) => {
+ const shareItem = (it: LocalDataType | ServerDataType) => {
return Share.share({
message: `Download songs for ${it.name}\n\n${DeepLinking.generateLinkForSongBundle(it)}`
})
- .then(r => rollbar.debug("Song bundle shared.", {
+ .then(r => rollbar.debug("SongBundle shared.", {
shareAction: r,
bundle: it
}))
- .catch(error => rollbar.warning("Failed to share song bundle", {
+ .catch(error => rollbar.warning("Failed to share SongBundle", {
...sanitizeErrorForRollbar(error),
bundle: it
}));
};
- const onSongBundlePress = (bundle: ServerSongBundle) => {
- if (isLoading || isPopupOpen()) {
- return;
- }
+ const onServerItemPress = (item: ServerDataType) => {
+ if (isProcessingLocalData || isPopupOpen()) return;
- setRequestDownloadForBundle(bundle);
+ setRequestDownloadForItem(item);
};
- const onLocalSongBundlePress = (bundle: LocalSongBundle) => {
- if (isLoading || isPopupOpen()) {
- return;
- }
+ const onLocalItemPress = (item: LocalDataType) => {
+ if (isProcessingLocalData || isPopupOpen()) return;
- if (SongProcessor.hasUpdate(serverBundles, bundle)) {
- const serverBundle = SongProcessor.getMatchingServerBundle(serverBundles, bundle);
- if (serverBundle !== undefined) {
- return setRequestUpdateForBundle(serverBundle);
+ if (SongProcessor.hasUpdate(serverData, item)) {
+ const serverItem = SongProcessor.getMatchingServerBundle(serverData, item);
+ if (serverItem !== undefined) {
+ return setRequestUpdateForItem(serverItem);
}
}
- setRequestDeleteForBundle(bundle);
+ setRequestDeleteForItem(item);
};
- const onConfirmDownloadSongBundle = () => {
- const bundle = requestDownloadForBundle;
- setRequestDownloadForBundle(undefined);
+ const onConfirmDownload = () => {
+ const item = requestDownloadForItem;
+ setRequestDownloadForItem(undefined);
- if (isLoading || bundle === undefined) return;
+ if (isProcessingLocalData || item === undefined) return;
- const isUpdating = updaterContext.songBundlesUpdating.some(it => it.uuid === bundle.uuid);
+ const isUpdating = updaterContext.songBundlesUpdating.some(it => it.uuid === item.uuid);
if (isUpdating) return;
- downloadSongBundle(bundle);
+ downloadItem(item);
};
- const onConfirmUpdateSongBundle = () => {
- const bundle = requestUpdateForBundle;
- setRequestUpdateForBundle(undefined);
+ const onConfirmUpdate = () => {
+ const item = requestUpdateForItem;
+ setRequestUpdateForItem(undefined);
- if (isLoading || bundle === undefined) return;
+ if (isProcessingLocalData || item === undefined) return;
- const isUpdating = updaterContext.songBundlesUpdating.some(it => it.uuid === bundle.uuid);
+ const isUpdating = updaterContext.songBundlesUpdating.some(it => it.uuid === item.uuid);
if (isUpdating) return;
- updateSongBundle(bundle);
+ updateItem(item);
};
- const downloadSongBundle = (bundle: ServerSongBundle) => saveSongBundle(bundle, false);
- const updateSongBundle = (bundle: ServerSongBundle) => saveSongBundle(bundle, true);
+ const downloadItem = (item: ServerDataType) => saveItem(item, false);
+ const updateItem = (item: ServerDataType) => saveItem(item, true);
- const saveSongBundle = (bundle: ServerSongBundle, isUpdate: boolean) => {
+ const saveItem = (item: ServerDataType, isUpdate: boolean) => {
if (!isMounted()) return;
- setIsLoading(true);
- updaterContext.addSongBundleUpdating(bundle);
+ setIsProcessingLocalData(true);
+ updaterContext.addSongBundleUpdating(item);
const call = isUpdate
- ? SongUpdater.fetchAndUpdateSongBundle(bundle)
- : SongUpdater.fetchAndSaveSongBundle(bundle);
+ ? SongUpdater.fetchAndUpdateSongBundle(item)
+ : SongUpdater.fetchAndSaveSongBundle(item);
call
- .then(() => Alert.alert("Success", `${bundle.name} ${isUpdate ? "updated" : "added"}!`))
+ .then(() => Alert.alert("Success", `${item.name} ${isUpdate ? "updated" : "added"}!`))
.catch(error => {
- rollbar.error("Failed to import song bundle", {
+ rollbar.error("Failed to import SongBundle", {
...sanitizeErrorForRollbar(error),
isUpdate: isUpdate,
- bundle: bundle,
+ item: item,
});
if (error.name == "TypeError" && error.message == "Network request failed") {
- Alert.alert("Error", `Could not ${isUpdate ? "update" : "download"} ${bundle.name}. Make sure your internet connection is working or try again later.`);
+ Alert.alert("Error", `Could not ${isUpdate ? "update" : "download"} ${item.name}. Make sure your internet connection is working or try again later.`);
} else {
- Alert.alert("Error", `Could not ${isUpdate ? "update" : "download"} ${bundle.name}. \n${error}\n\nTry again later.`);
+ Alert.alert("Error", `Could not ${isUpdate ? "update" : "download"} ${item.name}. \n${error}\n\nTry again later.`);
}
})
.finally(() => {
if (!isMounted()) return;
- setLocalBundles([]);
- setIsLoading(false);
- loadLocalSongBundles();
+ setIsProcessingLocalData(false);
})
.finally(() => {
// Do this here after the state has been called, otherwise we get realm invalidation errors
- updaterContext.removeSongBundleUpdating(bundle);
+ updaterContext.removeSongBundleUpdating(item);
})
};
- const onConfirmDeleteSongBundle = () => {
- const bundle = requestDeleteForBundle;
- setRequestDeleteForBundle(undefined);
+ const onConfirmDelete = () => {
+ const item = requestDeleteForItem;
+ setRequestDeleteForItem(undefined);
- if (isLoading || bundle === undefined) return;
+ if (isProcessingLocalData || item === undefined) return;
- const isUpdating = updaterContext.songBundlesUpdating.some(it => it.uuid === bundle.uuid);
+ const isUpdating = updaterContext.songBundlesUpdating.some(it => it.uuid === item.uuid);
if (isUpdating) {
Alert.alert("Could not delete", "This bundle is being updated. Please wait until this operation is done and try again.")
return;
}
- deleteSongBundle(bundle);
+ deleteItem(item);
};
- const deleteSongBundle = (bundle: LocalSongBundle) => {
- setIsLoading(true);
- updaterContext.removeSongBundleUpdating(bundle);
+ const deleteItem = (item: LocalDataType) => {
+ setIsProcessingLocalData(true);
+ updaterContext.removeSongBundleUpdating(item);
try {
- const successMessage = SongProcessor.deleteSongBundle(bundle)
+ const successMessage = SongProcessor.deleteSongBundle(item)
Alert.alert("Success", successMessage);
} catch (error) {
alertAndThrow(error);
+ } finally {
+ if (isMounted()) {
+ setIsProcessingLocalData(false);
+ }
}
-
- if (!isMounted()) return;
-
- loadLocalSongBundles();
};
- const getAllLanguagesFromBundles = (bundles: ServerSongBundle[]) => {
- const languages = SongProcessor.getAllLanguagesFromBundles(bundles);
+ const getAllLanguagesFromServerData = (data: ServerDataType[]) => {
+ const languages = SongProcessor.getAllLanguagesFromBundles(data);
if (languages.length > 0 && filterLanguage === "") {
if (languages.includes("AF")) {
@@ -302,83 +322,81 @@ const DownloadSongsScreen: React.FC = ({ setIsProcessing, prompt
const isOfSelectedLanguage = (it: { language: string }) =>
filterLanguage === ShowAllLanguagesValue || it.language.toUpperCase() === filterLanguage.toUpperCase();
- return (
-
- setRequestDownloadForBundle(undefined)}
- onConfirm={onConfirmDownloadSongBundle}
- invertConfirmColor={true}
- message={`Download songs for ${requestDownloadForBundle?.name}?`} />
-
- setRequestUpdateForBundle(undefined)}
- onConfirm={onConfirmUpdateSongBundle}
- invertConfirmColor={true}
- message={`Update ${requestUpdateForBundle?.name}?`} />
-
- setRequestDeleteForBundle(undefined)}
- onConfirm={onConfirmDeleteSongBundle}
- message={`Delete all songs for ${requestDeleteForBundle?.name}?`} />
-
-
- We are still sorting out all the song
- licenses, so we trust that you take the
- responsibility to make sure you have the correct licenses for the songs you download.
-
-
- If you want to download a song bundle which is not displayed, please feel free to
-
- contact us
- .
-
-
- Select a song bundle to download or delete:
-
-
-
- }>
-
- {localBundles
- .filter(isOfSelectedLanguage)
- .map((bundle: LocalSongBundle) =>
- )}
-
- {serverBundles
- .filter(it => !SongProcessor.isBundleLocal(localBundles, it))
- .filter(isOfSelectedLanguage)
- .map((bundle: ServerSongBundle) =>
- )}
-
- {serverBundles.length > 0 ? undefined :
-
- {isLoading || isBundleLoading ? "Loading..." : "No online data available..."}
-
- }
- {isLoading || serverBundles.length === 0 || serverBundles.filter(isOfSelectedLanguage).length > 0 ? undefined :
-
- No bundles found for language "{languageAbbreviationToFullName(filterLanguage)}"...
-
- }
-
-
- );
+ return
+ setRequestDownloadForItem(undefined)}
+ onConfirm={onConfirmDownload}
+ invertConfirmColor={true}
+ message={`Download songs for ${requestDownloadForItem?.name}?`} />
+
+ setRequestUpdateForItem(undefined)}
+ onConfirm={onConfirmUpdate}
+ invertConfirmColor={true}
+ message={`Update ${requestUpdateForItem?.name}?`} />
+
+ setRequestDeleteForItem(undefined)}
+ onConfirm={onConfirmDelete}
+ message={`Delete all songs for ${requestDeleteForItem?.name}?`} />
+
+
+ We are still sorting out all the song
+ licenses, so we trust that you take the
+ responsibility to make sure you have the correct licenses for the songs you download.
+
+
+ If you want to download a song bundle which is not displayed, please feel free to
+
+ contact us
+ .
+
+
+ Select a song bundle to download or delete:
+
+
+
+ }>
+
+ {localData
+ .filter(isOfSelectedLanguage)
+ .map((item: LocalDataType) =>
+ )}
+
+ {serverData
+ .filter(it => !SongProcessor.isBundleLocal(localData, it))
+ .filter(isOfSelectedLanguage)
+ .map((item: ServerDataType) =>
+ )}
+
+ {serverData.length > 0 ? undefined :
+
+ {isServerDataLoading || isSpecificItemLoading ? "Loading..." : "No online data available..."}
+
+ }
+ {isLocalDataLoading || isServerDataLoading || serverData.length === 0 || serverData.filter(isOfSelectedLanguage).length > 0 ? undefined :
+
+ No bundles found for language "{languageAbbreviationToFullName(filterLanguage)}"...
+
+ }
+
+ ;
};
export default DownloadSongsScreen;
@@ -402,7 +420,10 @@ const createStyles = ({ colors }: ThemeContextProps) => StyleSheet.create({
color: colors.text.lighter
},
- listContainer: { flex: 1 },
+ listContainer: {
+ flex: 1,
+ minHeight: 100,
+ },
emptyListText: {
padding: 20,
diff --git a/source/logic/api.ts b/source/logic/api.ts
index 9b017410..dc1036b6 100644
--- a/source/logic/api.ts
+++ b/source/logic/api.ts
@@ -8,7 +8,7 @@ import config from "../config";
export const fetchRetry = fetchBuilder(fetch, { retries: config.fetchRetries });
const databaseApiEndpoint = `${databaseHost}/api/v1`;
-const hymnbookApiEndpoint = `${hymnbookHost}/api/v1`;
+export const hymnbookApiEndpoint = `${hymnbookHost}/api/v1`;
const get = (url: string) =>
ServerAuth.fetchWithJwt(jwt =>
diff --git a/source/logic/db/dbProvider.ts b/source/logic/db/dbProvider.ts
index da41cbcc..075c9fa7 100644
--- a/source/logic/db/dbProvider.ts
+++ b/source/logic/db/dbProvider.ts
@@ -1,4 +1,6 @@
import Realm, { ObjectClass, ObjectSchema } from "realm";
+import { rollbar } from "../rollbar";
+import { sanitizeErrorForRollbar } from "../utils";
interface DatabaseProps {
path: string;
@@ -47,7 +49,11 @@ export class DatabaseProvider {
cancelQueuedDisconnect() {
if (this._disconnectTimeout) {
- clearTimeout(this._disconnectTimeout);
+ try {
+ clearTimeout(this._disconnectTimeout);
+ } catch (error) {
+ rollbar.error("Failed to clear disconnect timeout", sanitizeErrorForRollbar(error));
+ }
}
this._disconnectTimeout = undefined;
this._isQueuedForDisconnect = false;
@@ -64,8 +70,12 @@ export class DatabaseProvider {
}
isConnected = () => {
- if (this._realm == null || this._realm.isClosed) {
- this._isConnected = false;
+ try {
+ if (this._realm == null || this._realm.isClosed) {
+ this._isConnected = false;
+ }
+ } catch (error) {
+ rollbar.error("Failed to check if realm is closed", sanitizeErrorForRollbar(error));
}
return this._isConnected;
};
diff --git a/source/logic/db/models/DocumentsSchema.ts b/source/logic/db/models/DocumentsSchema.ts
index 97c0d497..7a4058bf 100644
--- a/source/logic/db/models/DocumentsSchema.ts
+++ b/source/logic/db/models/DocumentsSchema.ts
@@ -5,7 +5,7 @@ export const DocumentSchema: Realm.ObjectSchema = {
properties: {
id: "int",
name: { type: "string", indexed: true },
- html: { type: "string", indexed: "full-text" },
+ html: "string",
language: "string",
index: "int",
createdAt: "date",
diff --git a/source/logic/db/models/SongListModelSchema.ts b/source/logic/db/models/SongListModelSchema.ts
index c4c8a38e..ac2d7a94 100644
--- a/source/logic/db/models/SongListModelSchema.ts
+++ b/source/logic/db/models/SongListModelSchema.ts
@@ -6,6 +6,11 @@ export const SongListVerseModelSchema: Realm.ObjectSchema = {
properties: {
id: "int",
verse: VerseSchema.name,
+ _songListSong: {
+ type: "linkingObjects",
+ objectType: "SongListSong", // SongListSongSchema.name
+ property: "selectedVerses"
+ }
},
primaryKey: "id"
};
@@ -16,7 +21,12 @@ export const SongListSongModelSchema: Realm.ObjectSchema = {
id: "int",
index: "int",
song: SongSchema.name,
- selectedVerses: SongListVerseModelSchema.name + "[]"
+ selectedVerses: SongListVerseModelSchema.name + "[]",
+ _songList: {
+ type: "linkingObjects",
+ objectType: "SongList", // SongListModelSchema.name
+ property: "songs"
+ }
},
primaryKey: "id"
};
diff --git a/source/logic/db/models/SongsSchema.ts b/source/logic/db/models/SongsSchema.ts
index 9880ac41..762407a7 100644
--- a/source/logic/db/models/SongsSchema.ts
+++ b/source/logic/db/models/SongsSchema.ts
@@ -21,7 +21,7 @@ export const VerseSchema: Realm.ObjectSchema = {
properties: {
id: "int",
name: "string",
- content: { type: "string", indexed: "full-text" },
+ content: "string",
language: "string",
index: "int",
uuid: { type: "string", indexed: true },
diff --git a/source/logic/db/patches/databaselog.ts b/source/logic/db/patches/databaselog.ts
new file mode 100644
index 00000000..f9e4d5e4
--- /dev/null
+++ b/source/logic/db/patches/databaselog.ts
@@ -0,0 +1,42 @@
+import { DatabaseProvider } from "../dbProvider";
+import Db from "../db";
+import { rollbar } from "../../rollbar";
+import { isAndroid, sanitizeErrorForRollbar } from "../../utils";
+import { hymnbookApiEndpoint } from "../../api";
+import { Security } from "../../security";
+import Settings from "../../../settings";
+
+export const uploadDatabases = async () => {
+ if (!Settings.shareUsageData) return;
+
+ await uploadDatabase(Db.songs)
+ await uploadDatabase(Db.documents)
+ await uploadDatabase(Db.settings)
+}
+
+export const uploadDatabase = async (db: DatabaseProvider) => {
+ const filename = db.realm().path.split("/").pop();
+
+ const data = new FormData();
+ data.append("file", {
+ uri: (isAndroid ? "file://" : "") + db.realm().path,
+ type: 'application/octet-stream',
+ name: filename,
+ });
+ data.append("path", db.realm().path);
+ data.append("schemaVersion", db.realm().schemaVersion);
+ data.append("user", Security.getDeviceId());
+
+ try {
+ await fetch(`${hymnbookApiEndpoint}/databaselogs/files`, {
+ method: "POST",
+ credentials: "include",
+ body: data
+ })
+ } catch (error) {
+ rollbar.error("Failed to upload database dump", {
+ path: db.realm().path,
+ ...sanitizeErrorForRollbar(error),
+ })
+ }
+}
\ No newline at end of file
diff --git a/source/logic/db/patches/documents.ts b/source/logic/db/patches/documents.ts
index a619177b..bf935e35 100644
--- a/source/logic/db/patches/documents.ts
+++ b/source/logic/db/patches/documents.ts
@@ -18,7 +18,7 @@ export namespace DocumentDbPatch {
.sorted("id", true);
// Ignore root as all groups could be checked
- groups.forEach((it, index) => {
+ groups.forEach(it => {
const isDuplicate = approvedUuids.includes(it.uuid);
if (!isDuplicate) {
approvedUuids.push(it.uuid);
@@ -52,7 +52,15 @@ export namespace DocumentDbPatch {
}
export const patch = () => {
- removeDuplicateGroups();
- removeDocumentObjectsWithoutParents();
+ try {
+ removeDuplicateGroups();
+ } catch (error) {
+ rollbar.error("Failed to remove duplicate groups", sanitizeErrorForRollbar(error));
+ }
+ try {
+ removeDocumentObjectsWithoutParents();
+ } catch (error) {
+ rollbar.error("Failed to remove document objects without parents", sanitizeErrorForRollbar(error));
+ }
};
}
\ No newline at end of file
diff --git a/source/logic/db/patches/songs.ts b/source/logic/db/patches/songs.ts
index 184b413b..9e9876ae 100644
--- a/source/logic/db/patches/songs.ts
+++ b/source/logic/db/patches/songs.ts
@@ -6,6 +6,7 @@ import { rollbar } from "../../rollbar";
import { sanitizeErrorForRollbar } from "../../utils";
import { AbcMelodySchema } from "../models/AbcMelodiesSchema";
import { removeObjectsWithoutParents } from "./utils";
+import { SongListSongModelSchema, SongListVerseModelSchema } from "../models/SongListModelSchema";
export namespace SongDbPatch {
/**
@@ -18,7 +19,7 @@ export namespace SongDbPatch {
.objects(SongBundleSchema.name)
.sorted("id", true);
- bundles.forEach((it) => {
+ bundles.forEach(it => {
const isDuplicate = approvedUuids.includes(it.uuid);
if (!isDuplicate) {
approvedUuids.push(it.uuid);
@@ -50,11 +51,21 @@ export namespace SongDbPatch {
{ schemaName: SongMetadataSchema.name, parentLink: '_songs', },
{ schemaName: VerseSchema.name, parentLink: '_songs', },
{ schemaName: AbcMelodySchema.name, parentLink: '_song', },
+ { schemaName: SongListSongModelSchema.name, parentLink: '_songList', },
+ { schemaName: SongListVerseModelSchema.name, parentLink: '_songListSong', },
]);
}
export const patch = () => {
- removeDuplicateBundles();
- removeSongObjectsWithoutParents();
+ try {
+ removeDuplicateBundles();
+ } catch (error) {
+ rollbar.error("Failed to remove duplicate bundles", sanitizeErrorForRollbar(error));
+ }
+ try {
+ removeSongObjectsWithoutParents();
+ } catch (error) {
+ rollbar.error("Failed to remove song objects without parents", sanitizeErrorForRollbar(error));
+ }
};
}
\ No newline at end of file
diff --git a/source/logic/db/patches/utils.ts b/source/logic/db/patches/utils.ts
index 816062d0..3aab10ac 100644
--- a/source/logic/db/patches/utils.ts
+++ b/source/logic/db/patches/utils.ts
@@ -19,6 +19,7 @@ export const removeObjectsWithoutParents = (
try {
db.realm().write(() => {
+ console.debug(`Deleting ${data.length} ${schema.schemaName}`)
db.realm().delete(data);
});
} catch (error) {
diff --git a/source/logic/documents/documentDbHelpers.ts b/source/logic/documents/documentDbHelpers.ts
new file mode 100644
index 00000000..994c7426
--- /dev/null
+++ b/source/logic/documents/documentDbHelpers.ts
@@ -0,0 +1,32 @@
+import Db from "../db/db";
+import { Document, DocumentGroup } from "../db/models/Documents";
+import { rollbar } from "../rollbar";
+import { sanitizeErrorForRollbar } from "../utils";
+
+export namespace DocumentDbHelpers {
+ export const deleteDocument = (obj: Document) => {
+ try {
+ Db.documents.realm().delete(obj);
+ } catch (error) {
+ rollbar.error("Failed to delete DB Document", {
+ ...sanitizeErrorForRollbar(error),
+ obj: { ...obj, _parent: null, }
+ });
+ throw error;
+ }
+ }
+
+ export const deleteDocumentGroup = (obj: DocumentGroup) => {
+ for (const child of obj.groups ?? []) deleteDocumentGroup(child);
+ for (const child of obj.items ?? []) deleteDocument(child);
+ try {
+ Db.documents.realm().delete(obj);
+ } catch (error) {
+ rollbar.error("Failed to delete DB Document", {
+ ...sanitizeErrorForRollbar(error),
+ obj: { ...obj, groups: null, items: null, _parent: null, }
+ });
+ throw error;
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/logic/documents/documentProcessor.ts b/source/logic/documents/documentProcessor.ts
index c98beb5d..0f3d4b94 100644
--- a/source/logic/documents/documentProcessor.ts
+++ b/source/logic/documents/documentProcessor.ts
@@ -4,6 +4,7 @@ import { Result, sanitizeErrorForRollbar } from "../utils";
import { DocumentGroup } from "../db/models/Documents";
import { DocumentGroup as ServerDocumentGroup } from "../server/models/Documents";
import { DocumentGroupSchema } from "../db/models/DocumentsSchema";
+import { DocumentDbHelpers } from "./documentDbHelpers";
export namespace DocumentProcessor {
export const loadLocalDocumentRoot = (): (DocumentGroup & Realm.Object)[] => {
@@ -37,16 +38,9 @@ export namespace DocumentProcessor {
const groupName = dbGroup.name;
- // Shallow copy
- const deleteGroups = dbGroup.groups?.slice(0) || [];
- deleteGroups.forEach(it => {
- deleteDocumentGroup(it);
- });
-
try {
Db.documents.realm().write(() => {
- Db.documents.realm().delete(dbGroup.items);
- Db.documents.realm().delete(dbGroup);
+ DocumentDbHelpers.deleteDocumentGroup(dbGroup);
});
} catch (error) {
rollbar.error("Failed to delete document group", {
diff --git a/source/logic/documents/updater/documentAutoUpdater.ts b/source/logic/documents/updater/documentAutoUpdater.ts
index c093cdf7..357518a4 100644
--- a/source/logic/documents/updater/documentAutoUpdater.ts
+++ b/source/logic/documents/updater/documentAutoUpdater.ts
@@ -11,7 +11,8 @@ export namespace DocumentAutoUpdater {
removeDocumentGroupUpdating: (bundle: { uuid: string }) => void,
mayUseNetwork: () => boolean
) => {
- const groups = DocumentProcessor.loadLocalDocumentRoot();
+ const groups = DocumentProcessor.loadLocalDocumentRoot()
+ .filter(it => it.uuid.length > 0);
if (groups.length == 0) return;
const updates = await DocumentServer.fetchDocumentGroupUpdates();
diff --git a/source/logic/locale.js b/source/logic/locale.ts
similarity index 100%
rename from source/logic/locale.js
rename to source/logic/locale.ts
diff --git a/source/logic/songs/songDbHelpers.ts b/source/logic/songs/songDbHelpers.ts
new file mode 100644
index 00000000..9aec291c
--- /dev/null
+++ b/source/logic/songs/songDbHelpers.ts
@@ -0,0 +1,133 @@
+import { Song, SongBundle, SongMetadata, Verse } from "../db/models/Songs";
+import Db from "../db/db";
+import { SongListModel, SongListSongModel, SongListVerseModel } from "../db/models/SongListModel";
+import { AbcMelody, AbcSubMelody } from "../db/models/AbcMelodies";
+import { rollbar } from "../rollbar";
+import { sanitizeErrorForRollbar } from "../utils";
+
+export namespace SongDbHelpers {
+ export const deleteAbcSubMelody = (obj: AbcSubMelody) => {
+ try {
+ Db.songs.realm().delete(obj);
+ } catch (error) {
+ rollbar.error("Failed to delete DB AbcSubMelody", {
+ ...sanitizeErrorForRollbar(error),
+ obj: { ...obj }
+ });
+ throw error;
+ }
+ }
+
+ export const deleteAbcMelody = (obj: AbcMelody) => {
+ for (const child of obj.subMelodies) deleteAbcSubMelody(child);
+ try {
+ Db.songs.realm().delete(obj);
+ } catch (error) {
+ rollbar.error("Failed to delete DB AbcMelody", {
+ ...sanitizeErrorForRollbar(error),
+ obj: { ...obj, subMelodies: null, }
+ });
+ throw error;
+ }
+ }
+
+ export const deleteVerse = (obj: Verse) => {
+ try {
+ Db.songs.realm().delete(obj);
+ } catch (error) {
+ rollbar.error("Failed to delete DB Verse", {
+ ...sanitizeErrorForRollbar(error),
+ obj: { ...obj }
+ });
+ throw error;
+ }
+ }
+
+ export const deleteSongMetadata = (obj: SongMetadata) => {
+ try {
+ Db.songs.realm().delete(obj);
+ } catch (error) {
+ rollbar.error("Failed to delete DB SongMetadata", {
+ ...sanitizeErrorForRollbar(error),
+ obj: { ...obj }
+ });
+ throw error;
+ }
+ }
+
+ export const deleteSong = (obj: Song) => {
+ for (const child of obj.verses) deleteVerse(child);
+ for (const child of obj.metadata) deleteSongMetadata(child);
+ for (const child of obj.abcMelodies) deleteAbcMelody(child);
+ try {
+ Db.songs.realm().delete(obj);
+ } catch (error) {
+ rollbar.error("Failed to delete DB Song", {
+ ...sanitizeErrorForRollbar(error),
+ obj: {
+ ...obj,
+ verses: null,
+ abcMelodies: null,
+ metadata: null,
+ _songBundles: null,
+ }
+ });
+ throw error;
+ }
+ }
+
+ export const deleteSongBundle = (obj: SongBundle) => {
+ for (const child of obj.songs) deleteSong(child);
+ try {
+ Db.songs.realm().delete(obj);
+ } catch (error) {
+ rollbar.error("Failed to delete DB SongBundle", {
+ ...sanitizeErrorForRollbar(error),
+ obj: { ...obj, songs: null, }
+ });
+ throw error;
+ }
+ }
+
+ export const deleteSongListVerse = (obj: SongListVerseModel) => {
+ try {
+ Db.songs.realm().delete(obj);
+ } catch (error) {
+ rollbar.error("Failed to delete DB SongListVerseModel", {
+ ...sanitizeErrorForRollbar(error),
+ obj: { ...obj }
+ });
+ throw error;
+ }
+ }
+
+ export const deleteSongListSong = (obj: SongListSongModel) => {
+ for (const child of obj.selectedVerses) deleteSongListVerse(child);
+ try {
+ Db.songs.realm().delete(obj);
+ } catch (error) {
+ rollbar.error("Failed to delete DB SongListSongModel", {
+ ...sanitizeErrorForRollbar(error),
+ obj: {
+ ...obj,
+ song: null,
+ selectedVerses: null,
+ }
+ });
+ throw error;
+ }
+ }
+
+ export const deleteSongList = (obj: SongListModel) => {
+ for (const child of obj.songs) deleteSongListSong(child);
+ try {
+ Db.songs.realm().delete(obj);
+ } catch (error) {
+ rollbar.error("Failed to delete DB SongListModel", {
+ ...sanitizeErrorForRollbar(error),
+ obj: { ...obj, songs: null, }
+ });
+ throw error;
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/logic/songs/songList.ts b/source/logic/songs/songList.ts
index 6bf7f9ef..1e2f09a8 100644
--- a/source/logic/songs/songList.ts
+++ b/source/logic/songs/songList.ts
@@ -4,6 +4,7 @@ import { SongListModel, SongListSongModel, SongListVerseModel } from "../db/mode
import { SongListModelSchema } from "../db/models/SongListModelSchema";
import { VerseSchema } from "../db/models/SongsSchema";
import { rollbar } from "../rollbar";
+import { SongDbHelpers } from "./songDbHelpers";
export default class SongList {
@@ -108,7 +109,7 @@ export default class SongList {
Db.songs.realm().write(() => {
// Delete all models with no song
songList.songs.filter(it => it.song == null)
- .forEach(it => Db.songs.realm().delete(it));
+ .forEach(SongDbHelpers.deleteSongListSong);
songList.songs = songList.songs.filter(it => it.song != null);
});
@@ -134,7 +135,7 @@ export default class SongList {
if (songList === undefined) return undefined;
Db.songs.realm().write(() => {
- Db.songs.realm().delete(songList.songs);
+ for (const child of songList.songs) SongDbHelpers.deleteSongListSong(child);
});
}
@@ -162,7 +163,7 @@ export default class SongList {
if (songListSong === undefined) return;
Db.songs.realm().write(() => {
- Db.songs.realm().delete(songListSong.selectedVerses);
+ for (const child of songListSong.selectedVerses) SongDbHelpers.deleteSongListVerse(child);
});
if (verses.length === 0) return;
diff --git a/source/logic/songs/songProcessor.ts b/source/logic/songs/songProcessor.ts
index b236ed39..f9f6042f 100644
--- a/source/logic/songs/songProcessor.ts
+++ b/source/logic/songs/songProcessor.ts
@@ -8,6 +8,7 @@ import { SongBundleSchema } from "../db/models/SongsSchema";
import { AbcMelody } from "../db/models/AbcMelodies";
import SongList from "./songList";
import Settings from "../../settings";
+import { SongDbHelpers } from "./songDbHelpers";
export namespace SongProcessor {
@@ -84,15 +85,14 @@ export namespace SongProcessor {
try {
Db.songs.realm().write(() => {
- Db.songs.realm().delete(dbBundle.songs);
- Db.songs.realm().delete(dbBundle);
+ SongDbHelpers.deleteSongBundle(dbBundle);
});
} catch (error) {
rollbar.error("Failed to delete song bundle", {
...sanitizeErrorForRollbar(error),
bundle: { ...bundle, songs: null }
});
- throw new Error(`Could not delete (outdated) songs for ${bundleName}`);
+ throw new Error(`Could not delete (outdated) song bundle ${bundleName}`);
}
SongList.cleanUpAllSongLists();
diff --git a/source/logic/songs/updater/songAutoUpdater.ts b/source/logic/songs/updater/songAutoUpdater.ts
index 1b9652c9..3f752b9e 100644
--- a/source/logic/songs/updater/songAutoUpdater.ts
+++ b/source/logic/songs/updater/songAutoUpdater.ts
@@ -11,7 +11,8 @@ export namespace SongAutoUpdater {
removeSongBundleUpdating: (bundle: { uuid: string }) => void,
mayUseNetwork: () => boolean
) => {
- const bundles = SongProcessor.loadLocalSongBundles();
+ const bundles = SongProcessor.loadLocalSongBundles()
+ .filter(it => it.uuid.length > 0);
if (bundles.length == 0) return;
const updates = await Server.fetchSongBundleUpdates();
diff --git a/yarn.lock b/yarn.lock
index acac6999..e1f27b36 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -89,12 +89,12 @@
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"
-"@babel/generator@^7.25.4":
- version "7.25.5"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.25.5.tgz#b31cf05b3fe8c32d206b6dad03bb0aacbde73450"
- integrity sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==
+"@babel/generator@^7.25.6":
+ version "7.25.6"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.25.6.tgz#0df1ad8cb32fe4d2b01d8bf437f153d19342a87c"
+ integrity sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==
dependencies:
- "@babel/types" "^7.25.4"
+ "@babel/types" "^7.25.6"
"@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.25"
jsesc "^2.5.1"
@@ -511,12 +511,12 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.6.tgz#5e030f440c3c6c78d195528c3b688b101a365328"
integrity sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==
-"@babel/parser@^7.25.0", "@babel/parser@^7.25.4":
- version "7.25.4"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.4.tgz#af4f2df7d02440286b7de57b1c21acfb2a6f257a"
- integrity sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==
+"@babel/parser@^7.25.0", "@babel/parser@^7.25.6":
+ version "7.25.6"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.6.tgz#85660c5ef388cbbf6e3d2a694ee97a38f18afe2f"
+ integrity sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==
dependencies:
- "@babel/types" "^7.25.4"
+ "@babel/types" "^7.25.6"
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.23.3":
version "7.23.3"
@@ -1510,15 +1510,15 @@
globals "^11.1.0"
"@babel/traverse@^7.24.7", "@babel/traverse@^7.24.8", "@babel/traverse@^7.25.0", "@babel/traverse@^7.25.4":
- version "7.25.4"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.4.tgz#648678046990f2957407e3086e97044f13c3e18e"
- integrity sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==
+ version "7.25.6"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.6.tgz#04fad980e444f182ecf1520504941940a90fea41"
+ integrity sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==
dependencies:
"@babel/code-frame" "^7.24.7"
- "@babel/generator" "^7.25.4"
- "@babel/parser" "^7.25.4"
+ "@babel/generator" "^7.25.6"
+ "@babel/parser" "^7.25.6"
"@babel/template" "^7.25.0"
- "@babel/types" "^7.25.4"
+ "@babel/types" "^7.25.6"
debug "^4.3.1"
globals "^11.1.0"
@@ -1540,10 +1540,10 @@
"@babel/helper-validator-identifier" "^7.24.6"
to-fast-properties "^2.0.0"
-"@babel/types@^7.24.7", "@babel/types@^7.24.8", "@babel/types@^7.25.0", "@babel/types@^7.25.4":
- version "7.25.4"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.4.tgz#6bcb46c72fdf1012a209d016c07f769e10adcb5f"
- integrity sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==
+"@babel/types@^7.24.7", "@babel/types@^7.24.8", "@babel/types@^7.25.0", "@babel/types@^7.25.6":
+ version "7.25.6"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.6.tgz#893942ddb858f32ae7a004ec9d3a76b3463ef8e6"
+ integrity sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==
dependencies:
"@babel/helper-string-parser" "^7.24.8"
"@babel/helper-validator-identifier" "^7.24.7"
@@ -2567,10 +2567,10 @@
resolved "https://registry.yarnpkg.com/@realm/fetch/-/fetch-0.1.1.tgz#1a637d0a1fc3734a7ffca64361d764777dd3d80c"
integrity sha512-hkTprw79RXGv54Je0DrjpQPLaz4QID2dO3FmthAQQWAkqwyrqMzrCGzJzLlmTKWZFsgLrN8KQyNewod27P+nJg==
-"@realm/react@0.10.0":
- version "0.10.0"
- resolved "https://registry.yarnpkg.com/@realm/react/-/react-0.10.0.tgz#826eef5d32be84ce9111489c6c00890803787759"
- integrity sha512-9BbQRGbITwnQj9bqDLHe1CguCrxygZekV+NtJsMrwe1uPz/y/Uu3AkOfSCEcpfgF0mlinOwhFC/JgujV56zgjQ==
+"@realm/react@0.11.0":
+ version "0.11.0"
+ resolved "https://registry.yarnpkg.com/@realm/react/-/react-0.11.0.tgz#cfd9674505c783d3c9f8dddb72890ba3563cccca"
+ integrity sha512-ekS0j8ZyWr+Tjd4RmTCGOOiSACFVURMxpB1fyDSwQk5mWQtkeItYB4/bLvFvqspg0F9EQbgBoNEA7ahbH6BRNw==
dependencies:
lodash.isequal "^4.5.0"
optionalDependencies:
@@ -3503,9 +3503,9 @@ caniuse-lite@^1.0.30001565:
integrity sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==
caniuse-lite@^1.0.30001646:
- version "1.0.30001653"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001653.tgz#b8af452f8f33b1c77f122780a4aecebea0caca56"
- integrity sha512-XGWQVB8wFQ2+9NZwZ10GxTYC5hk0Fa+q8cSkr0tgvMhYhMHP/QC+WTgrePMDBWiWc/pV+1ik82Al20XOK25Gcw==
+ version "1.0.30001655"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz#0ce881f5a19a2dcfda2ecd927df4d5c1684b982f"
+ integrity sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==
chalk@^2.4.2:
version "2.4.2"
@@ -3980,9 +3980,9 @@ destroy@1.2.0:
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
detect-libc@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d"
- integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
+ integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==
detect-newline@^3.0.0:
version "3.1.0"
@@ -4253,9 +4253,9 @@ escalade@^3.1.1:
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
escalade@^3.1.2:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27"
- integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
+ integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
escape-html@~1.0.3:
version "1.0.3"
@@ -6960,9 +6960,9 @@ nocache@^3.0.1:
integrity sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw==
node-abi@^3.3.0:
- version "3.52.0"
- resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.52.0.tgz#ffba0a85f54e552547e5849015f40f9514d5ba7c"
- integrity sha512-JJ98b02z16ILv7859irtXn4oUaFWADtvkzy2c0IAatNVX2Mc9Yoh8z6hZInn3QwvMEYhHuQloYi+TTQy67SIdQ==
+ version "3.68.0"
+ resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.68.0.tgz#8f37fb02ecf4f43ebe694090dcb52e0c4cc4ba25"
+ integrity sha512-7vbj10trelExNjFSBm5kTvZXXa7pZyKWx9RCKIyqe6I9Ev3IzGpQoqBP3a+cOdxY+pWj6VkP28n/2wWysBHD/A==
dependencies:
semver "^7.3.5"
@@ -7470,9 +7470,9 @@ prop-types@^15.7.2, prop-types@^15.8.1:
react-is "^16.13.1"
pump@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
- integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8"
+ integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==
dependencies:
end-of-stream "^1.1.0"
once "^1.3.1"
@@ -8153,7 +8153,12 @@ semver@^6.3.0, semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
-semver@^7.3.5, semver@^7.3.7, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4:
+semver@^7.3.5:
+ version "7.6.3"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
+ integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
+
+semver@^7.3.7, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4:
version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==