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==