diff --git a/Podfile b/Podfile index de51ed112c82..b2c63f158c83 100644 --- a/Podfile +++ b/Podfile @@ -34,16 +34,17 @@ end def wordpress_ui ## for production: - pod 'WordPressUI', '~> 1.5.2' + pod 'WordPressUI', '~> 1.5.3-beta.1' ## for development: #pod 'WordPressUI', :path => '../WordPressUI-iOS' ## while PR is in review: #pod 'WordPressUI', :git => 'https://github.com/wordpress-mobile/WordPressUI-iOS', :branch => '' + #pod 'WordPressUI', :git => 'https://github.com/wordpress-mobile/WordPressUI-iOS', :commit => '71f32a3300b4c630b41ba7ae8101896f9d297606' end def wordpress_kit - pod 'WordPressKit', '~> 4.7.0' + pod 'WordPressKit', '~> 4.7.1-beta.1' #pod 'WordPressKit', :git => 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', :tag => '4.6.0-beta.3' #pod 'WordPressKit', :git => 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', :branch => '' @@ -148,7 +149,7 @@ target 'WordPress' do ## Gutenberg (React Native) ## ===================== ## - gutenberg :commit => '65a87cfbaa47822c3f742426b2aff02c4a2dbd1d' + gutenberg :commit => 'fe07af1691e4f5f869e4824ebf26a9b8326bafec' ## Third party libraries ## ===================== @@ -161,6 +162,7 @@ target 'WordPress' do pod 'Starscream', '3.0.6' pod 'SVProgressHUD', '2.2.5' pod 'ZendeskSupportSDK', '5.0.0' + pod 'AlamofireImage', '3.5.2' pod 'AlamofireNetworkActivityIndicator', '~> 2.4' pod 'FSInteractiveMap', :git => 'https://github.com/wordpress-mobile/FSInteractiveMap.git', :tag => '0.2.0' pod 'JTAppleCalendar', '~> 8.0.2' diff --git a/Podfile.lock b/Podfile.lock index e8829a927b05..51c2f095f93c 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,6 +1,8 @@ PODS: - 1PasswordExtension (1.8.6) - Alamofire (4.8.0) + - AlamofireImage (3.5.2): + - Alamofire (~> 4.8) - AlamofireNetworkActivityIndicator (2.4.0): - Alamofire (~> 4.8) - AppCenter (2.5.1): @@ -28,14 +30,14 @@ PODS: - CocoaLumberjack/Core (3.5.2) - DoubleConversion (1.1.5) - Down (0.6.6) - - FBLazyVector (0.62.1) - - FBReactNativeSpec (0.62.1): + - FBLazyVector (0.62.2) + - FBReactNativeSpec (0.62.2): - Folly (= 2018.10.22.00) - - RCTRequired (= 0.62.1) - - RCTTypeSafety (= 0.62.1) - - React-Core (= 0.62.1) - - React-jsi (= 0.62.1) - - ReactCommon/turbomodule/core (= 0.62.1) + - RCTRequired (= 0.62.2) + - RCTTypeSafety (= 0.62.2) + - React-Core (= 0.62.2) + - React-jsi (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) - Folly (2018.10.22.00): - boost-for-react-native - DoubleConversion @@ -66,9 +68,9 @@ PODS: - Gridicons (1.0.1) - GTMSessionFetcher/Core (1.3.1) - Gutenberg (1.25.0): - - React (= 0.62.1) - - React-CoreModules (= 0.62.1) - - React-RCTImage (= 0.62.1) + - React (= 0.62.2) + - React-CoreModules (= 0.62.2) + - React-RCTImage (= 0.62.2) - RNTAztecView - JTAppleCalendar (8.0.3) - lottie-ios (3.1.6) @@ -121,170 +123,170 @@ PODS: - OHHTTPStubs/OHPathHelpers (6.1.0) - OHHTTPStubs/Swift (6.1.0): - OHHTTPStubs/Default - - RCTRequired (0.62.1) - - RCTTypeSafety (0.62.1): - - FBLazyVector (= 0.62.1) + - RCTRequired (0.62.2) + - RCTTypeSafety (0.62.2): + - FBLazyVector (= 0.62.2) - Folly (= 2018.10.22.00) - - RCTRequired (= 0.62.1) - - React-Core (= 0.62.1) + - RCTRequired (= 0.62.2) + - React-Core (= 0.62.2) - Reachability (3.2) - - React (0.62.1): - - React-Core (= 0.62.1) - - React-Core/DevSupport (= 0.62.1) - - React-Core/RCTWebSocket (= 0.62.1) - - React-RCTActionSheet (= 0.62.1) - - React-RCTAnimation (= 0.62.1) - - React-RCTBlob (= 0.62.1) - - React-RCTImage (= 0.62.1) - - React-RCTLinking (= 0.62.1) - - React-RCTNetwork (= 0.62.1) - - React-RCTSettings (= 0.62.1) - - React-RCTText (= 0.62.1) - - React-RCTVibration (= 0.62.1) - - React-Core (0.62.1): + - React (0.62.2): + - React-Core (= 0.62.2) + - React-Core/DevSupport (= 0.62.2) + - React-Core/RCTWebSocket (= 0.62.2) + - React-RCTActionSheet (= 0.62.2) + - React-RCTAnimation (= 0.62.2) + - React-RCTBlob (= 0.62.2) + - React-RCTImage (= 0.62.2) + - React-RCTLinking (= 0.62.2) + - React-RCTNetwork (= 0.62.2) + - React-RCTSettings (= 0.62.2) + - React-RCTText (= 0.62.2) + - React-RCTVibration (= 0.62.2) + - React-Core (0.62.2): - Folly (= 2018.10.22.00) - glog - - React-Core/Default (= 0.62.1) - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - React-jsiexecutor (= 0.62.1) + - React-Core/Default (= 0.62.2) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/CoreModulesHeaders (0.62.1): + - React-Core/CoreModulesHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - React-jsiexecutor (= 0.62.1) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/Default (0.62.1): + - React-Core/Default (0.62.2): - Folly (= 2018.10.22.00) - glog - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - React-jsiexecutor (= 0.62.1) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/DevSupport (0.62.1): + - React-Core/DevSupport (0.62.2): - Folly (= 2018.10.22.00) - glog - - React-Core/Default (= 0.62.1) - - React-Core/RCTWebSocket (= 0.62.1) - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - React-jsiexecutor (= 0.62.1) - - React-jsinspector (= 0.62.1) + - React-Core/Default (= 0.62.2) + - React-Core/RCTWebSocket (= 0.62.2) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) + - React-jsinspector (= 0.62.2) - Yoga - - React-Core/RCTActionSheetHeaders (0.62.1): + - React-Core/RCTActionSheetHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - React-jsiexecutor (= 0.62.1) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTAnimationHeaders (0.62.1): + - React-Core/RCTAnimationHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - React-jsiexecutor (= 0.62.1) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTBlobHeaders (0.62.1): + - React-Core/RCTBlobHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - React-jsiexecutor (= 0.62.1) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTImageHeaders (0.62.1): + - React-Core/RCTImageHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - React-jsiexecutor (= 0.62.1) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTLinkingHeaders (0.62.1): + - React-Core/RCTLinkingHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - React-jsiexecutor (= 0.62.1) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTNetworkHeaders (0.62.1): + - React-Core/RCTNetworkHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - React-jsiexecutor (= 0.62.1) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTSettingsHeaders (0.62.1): + - React-Core/RCTSettingsHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - React-jsiexecutor (= 0.62.1) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTTextHeaders (0.62.1): + - React-Core/RCTTextHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - React-jsiexecutor (= 0.62.1) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTVibrationHeaders (0.62.1): + - React-Core/RCTVibrationHeaders (0.62.2): - Folly (= 2018.10.22.00) - glog - React-Core/Default - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - React-jsiexecutor (= 0.62.1) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-Core/RCTWebSocket (0.62.1): + - React-Core/RCTWebSocket (0.62.2): - Folly (= 2018.10.22.00) - glog - - React-Core/Default (= 0.62.1) - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - React-jsiexecutor (= 0.62.1) + - React-Core/Default (= 0.62.2) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsiexecutor (= 0.62.2) - Yoga - - React-CoreModules (0.62.1): - - FBReactNativeSpec (= 0.62.1) + - React-CoreModules (0.62.2): + - FBReactNativeSpec (= 0.62.2) - Folly (= 2018.10.22.00) - - RCTTypeSafety (= 0.62.1) - - React-Core/CoreModulesHeaders (= 0.62.1) - - React-RCTImage (= 0.62.1) - - ReactCommon/turbomodule/core (= 0.62.1) - - React-cxxreact (0.62.1): + - RCTTypeSafety (= 0.62.2) + - React-Core/CoreModulesHeaders (= 0.62.2) + - React-RCTImage (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - React-cxxreact (0.62.2): - boost-for-react-native (= 1.63.0) - DoubleConversion - Folly (= 2018.10.22.00) - glog - - React-jsinspector (= 0.62.1) - - React-jsi (0.62.1): + - React-jsinspector (= 0.62.2) + - React-jsi (0.62.2): - boost-for-react-native (= 1.63.0) - DoubleConversion - Folly (= 2018.10.22.00) - glog - - React-jsi/Default (= 0.62.1) - - React-jsi/Default (0.62.1): + - React-jsi/Default (= 0.62.2) + - React-jsi/Default (0.62.2): - boost-for-react-native (= 1.63.0) - DoubleConversion - Folly (= 2018.10.22.00) - glog - - React-jsiexecutor (0.62.1): + - React-jsiexecutor (0.62.2): - DoubleConversion - Folly (= 2018.10.22.00) - glog - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - React-jsinspector (0.62.1) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - React-jsinspector (0.62.2) - react-native-keyboard-aware-scroll-view (0.8.8): - React - react-native-linear-gradient (2.5.6): @@ -298,87 +300,87 @@ PODS: - react-native-video/Video (= 4.4.1) - react-native-video/Video (4.4.1): - React-Core - - React-RCTActionSheet (0.62.1): - - React-Core/RCTActionSheetHeaders (= 0.62.1) - - React-RCTAnimation (0.62.1): - - FBReactNativeSpec (= 0.62.1) + - React-RCTActionSheet (0.62.2): + - React-Core/RCTActionSheetHeaders (= 0.62.2) + - React-RCTAnimation (0.62.2): + - FBReactNativeSpec (= 0.62.2) - Folly (= 2018.10.22.00) - - RCTTypeSafety (= 0.62.1) - - React-Core/RCTAnimationHeaders (= 0.62.1) - - ReactCommon/turbomodule/core (= 0.62.1) - - React-RCTBlob (0.62.1): - - FBReactNativeSpec (= 0.62.1) + - RCTTypeSafety (= 0.62.2) + - React-Core/RCTAnimationHeaders (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - React-RCTBlob (0.62.2): + - FBReactNativeSpec (= 0.62.2) - Folly (= 2018.10.22.00) - - React-Core/RCTBlobHeaders (= 0.62.1) - - React-Core/RCTWebSocket (= 0.62.1) - - React-jsi (= 0.62.1) - - React-RCTNetwork (= 0.62.1) - - ReactCommon/turbomodule/core (= 0.62.1) - - React-RCTImage (0.62.1): - - FBReactNativeSpec (= 0.62.1) + - React-Core/RCTBlobHeaders (= 0.62.2) + - React-Core/RCTWebSocket (= 0.62.2) + - React-jsi (= 0.62.2) + - React-RCTNetwork (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - React-RCTImage (0.62.2): + - FBReactNativeSpec (= 0.62.2) - Folly (= 2018.10.22.00) - - RCTTypeSafety (= 0.62.1) - - React-Core/RCTImageHeaders (= 0.62.1) - - React-RCTNetwork (= 0.62.1) - - ReactCommon/turbomodule/core (= 0.62.1) - - React-RCTLinking (0.62.1): - - FBReactNativeSpec (= 0.62.1) - - React-Core/RCTLinkingHeaders (= 0.62.1) - - ReactCommon/turbomodule/core (= 0.62.1) - - React-RCTNetwork (0.62.1): - - FBReactNativeSpec (= 0.62.1) + - RCTTypeSafety (= 0.62.2) + - React-Core/RCTImageHeaders (= 0.62.2) + - React-RCTNetwork (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - React-RCTLinking (0.62.2): + - FBReactNativeSpec (= 0.62.2) + - React-Core/RCTLinkingHeaders (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - React-RCTNetwork (0.62.2): + - FBReactNativeSpec (= 0.62.2) - Folly (= 2018.10.22.00) - - RCTTypeSafety (= 0.62.1) - - React-Core/RCTNetworkHeaders (= 0.62.1) - - ReactCommon/turbomodule/core (= 0.62.1) - - React-RCTSettings (0.62.1): - - FBReactNativeSpec (= 0.62.1) + - RCTTypeSafety (= 0.62.2) + - React-Core/RCTNetworkHeaders (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - React-RCTSettings (0.62.2): + - FBReactNativeSpec (= 0.62.2) - Folly (= 2018.10.22.00) - - RCTTypeSafety (= 0.62.1) - - React-Core/RCTSettingsHeaders (= 0.62.1) - - ReactCommon/turbomodule/core (= 0.62.1) - - React-RCTText (0.62.1): - - React-Core/RCTTextHeaders (= 0.62.1) - - React-RCTVibration (0.62.1): - - FBReactNativeSpec (= 0.62.1) + - RCTTypeSafety (= 0.62.2) + - React-Core/RCTSettingsHeaders (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - React-RCTText (0.62.2): + - React-Core/RCTTextHeaders (= 0.62.2) + - React-RCTVibration (0.62.2): + - FBReactNativeSpec (= 0.62.2) - Folly (= 2018.10.22.00) - - React-Core/RCTVibrationHeaders (= 0.62.1) - - ReactCommon/turbomodule/core (= 0.62.1) - - ReactCommon (0.62.1): - - ReactCommon/callinvoker (= 0.62.1) - - ReactCommon/turbomodule (= 0.62.1) - - ReactCommon/callinvoker (0.62.1): + - React-Core/RCTVibrationHeaders (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - ReactCommon (0.62.2): + - ReactCommon/callinvoker (= 0.62.2) + - ReactCommon/turbomodule (= 0.62.2) + - ReactCommon/callinvoker (0.62.2): - DoubleConversion - Folly (= 2018.10.22.00) - glog - - React-cxxreact (= 0.62.1) - - ReactCommon/turbomodule (0.62.1): + - React-cxxreact (= 0.62.2) + - ReactCommon/turbomodule (0.62.2): - DoubleConversion - Folly (= 2018.10.22.00) - glog - - React-Core (= 0.62.1) - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - ReactCommon/callinvoker (= 0.62.1) - - ReactCommon/turbomodule/core (= 0.62.1) - - ReactCommon/turbomodule/samples (= 0.62.1) - - ReactCommon/turbomodule/core (0.62.1): + - React-Core (= 0.62.2) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - ReactCommon/callinvoker (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) + - ReactCommon/turbomodule/samples (= 0.62.2) + - ReactCommon/turbomodule/core (0.62.2): - DoubleConversion - Folly (= 2018.10.22.00) - glog - - React-Core (= 0.62.1) - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - ReactCommon/callinvoker (= 0.62.1) - - ReactCommon/turbomodule/samples (0.62.1): + - React-Core (= 0.62.2) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - ReactCommon/callinvoker (= 0.62.2) + - ReactCommon/turbomodule/samples (0.62.2): - DoubleConversion - Folly (= 2018.10.22.00) - glog - - React-Core (= 0.62.1) - - React-cxxreact (= 0.62.1) - - React-jsi (= 0.62.1) - - ReactCommon/callinvoker (= 0.62.1) - - ReactCommon/turbomodule/core (= 0.62.1) + - React-Core (= 0.62.2) + - React-cxxreact (= 0.62.2) + - React-jsi (= 0.62.2) + - ReactCommon/callinvoker (= 0.62.2) + - ReactCommon/turbomodule/core (= 0.62.2) - ReactNativeDarkMode (0.0.10): - React - RNSVG (9.13.6-gb): @@ -410,7 +412,7 @@ PODS: - WordPressKit (~> 4.7.0) - WordPressShared (~> 1.8.16) - WordPressUI (~> 1.5.2) - - WordPressKit (4.7.0): + - WordPressKit (4.7.1-beta.1): - Alamofire (~> 4.8.0) - CocoaLumberjack (~> 3.4) - NSObject-SafeExpectations (= 0.0.4) @@ -421,7 +423,7 @@ PODS: - WordPressShared (1.8.16): - CocoaLumberjack (~> 3.4) - FormatterKit/TimeIntervalFormatter (= 1.8.2) - - WordPressUI (1.5.2) + - WordPressUI (1.5.3-beta.1) - WPMediaPicker (1.6.1) - wpxmlrpc (0.8.5) - Yoga (1.14.0) @@ -443,6 +445,7 @@ PODS: DEPENDENCIES: - Alamofire (= 4.8.0) + - AlamofireImage (= 3.5.2) - AlamofireNetworkActivityIndicator (~> 2.4) - AppCenter (= 2.5.1) - AppCenter/Distribute (= 2.5.1) @@ -450,15 +453,15 @@ DEPENDENCIES: - Charts (~> 3.2.2) - CocoaLumberjack (= 3.5.2) - Down (~> 0.6.6) - - FBLazyVector (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/FBLazyVector.podspec.json`) - - FBReactNativeSpec (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/FBReactNativeSpec.podspec.json`) - - Folly (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/Folly.podspec.json`) + - FBLazyVector (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/FBLazyVector.podspec.json`) + - FBReactNativeSpec (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/FBReactNativeSpec.podspec.json`) + - Folly (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/Folly.podspec.json`) - FormatterKit/TimeIntervalFormatter (= 1.8.2) - FSInteractiveMap (from `https://github.com/wordpress-mobile/FSInteractiveMap.git`, tag `0.2.0`) - Gifu (= 3.2.0) - - glog (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/glog.podspec.json`) + - glog (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/glog.podspec.json`) - Gridicons (~> 1.0.1) - - Gutenberg (from `http://github.com/wordpress-mobile/gutenberg-mobile/`, commit `65a87cfbaa47822c3f742426b2aff02c4a2dbd1d`) + - Gutenberg (from `http://github.com/wordpress-mobile/gutenberg-mobile/`, commit `fe07af1691e4f5f869e4824ebf26a9b8326bafec`) - JTAppleCalendar (~> 8.0.2) - MediaEditor (~> 1.0.1) - MRProgress (= 0.8.3) @@ -468,45 +471,45 @@ DEPENDENCIES: - OCMock (= 3.4.3) - OHHTTPStubs (= 6.1.0) - OHHTTPStubs/Swift (= 6.1.0) - - RCTRequired (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/RCTRequired.podspec.json`) - - RCTTypeSafety (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/RCTTypeSafety.podspec.json`) + - RCTRequired (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/RCTRequired.podspec.json`) + - RCTTypeSafety (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/RCTTypeSafety.podspec.json`) - Reachability (= 3.2) - - React (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React.podspec.json`) - - React-Core (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-Core.podspec.json`) - - React-CoreModules (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-CoreModules.podspec.json`) - - React-cxxreact (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-cxxreact.podspec.json`) - - React-jsi (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-jsi.podspec.json`) - - React-jsiexecutor (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-jsiexecutor.podspec.json`) - - React-jsinspector (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-jsinspector.podspec.json`) - - react-native-keyboard-aware-scroll-view (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/react-native-keyboard-aware-scroll-view.podspec.json`) - - react-native-linear-gradient (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/react-native-linear-gradient.podspec.json`) - - react-native-safe-area (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/react-native-safe-area.podspec.json`) - - react-native-slider (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/react-native-slider.podspec.json`) - - react-native-video (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/react-native-video.podspec.json`) - - React-RCTActionSheet (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTActionSheet.podspec.json`) - - React-RCTAnimation (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTAnimation.podspec.json`) - - React-RCTBlob (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTBlob.podspec.json`) - - React-RCTImage (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTImage.podspec.json`) - - React-RCTLinking (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTLinking.podspec.json`) - - React-RCTNetwork (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTNetwork.podspec.json`) - - React-RCTSettings (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTSettings.podspec.json`) - - React-RCTText (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTText.podspec.json`) - - React-RCTVibration (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTVibration.podspec.json`) - - ReactCommon (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/ReactCommon.podspec.json`) - - ReactNativeDarkMode (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/ReactNativeDarkMode.podspec.json`) - - RNSVG (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/RNSVG.podspec.json`) - - RNTAztecView (from `http://github.com/wordpress-mobile/gutenberg-mobile/`, commit `65a87cfbaa47822c3f742426b2aff02c4a2dbd1d`) + - React (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React.podspec.json`) + - React-Core (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-Core.podspec.json`) + - React-CoreModules (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-CoreModules.podspec.json`) + - React-cxxreact (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-cxxreact.podspec.json`) + - React-jsi (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-jsi.podspec.json`) + - React-jsiexecutor (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-jsiexecutor.podspec.json`) + - React-jsinspector (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-jsinspector.podspec.json`) + - react-native-keyboard-aware-scroll-view (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/react-native-keyboard-aware-scroll-view.podspec.json`) + - react-native-linear-gradient (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/react-native-linear-gradient.podspec.json`) + - react-native-safe-area (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/react-native-safe-area.podspec.json`) + - react-native-slider (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/react-native-slider.podspec.json`) + - react-native-video (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/react-native-video.podspec.json`) + - React-RCTActionSheet (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTActionSheet.podspec.json`) + - React-RCTAnimation (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTAnimation.podspec.json`) + - React-RCTBlob (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTBlob.podspec.json`) + - React-RCTImage (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTImage.podspec.json`) + - React-RCTLinking (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTLinking.podspec.json`) + - React-RCTNetwork (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTNetwork.podspec.json`) + - React-RCTSettings (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTSettings.podspec.json`) + - React-RCTText (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTText.podspec.json`) + - React-RCTVibration (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTVibration.podspec.json`) + - ReactCommon (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/ReactCommon.podspec.json`) + - ReactNativeDarkMode (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/ReactNativeDarkMode.podspec.json`) + - RNSVG (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/RNSVG.podspec.json`) + - RNTAztecView (from `http://github.com/wordpress-mobile/gutenberg-mobile/`, commit `fe07af1691e4f5f869e4824ebf26a9b8326bafec`) - SimulatorStatusMagic - Starscream (= 3.0.6) - SVProgressHUD (= 2.2.5) - WordPress-Editor-iOS (~> 1.17.1) - WordPressAuthenticator (~> 1.12.1) - - WordPressKit (~> 4.7.0) + - WordPressKit (~> 4.7.1-beta.1) - WordPressMocks (~> 0.0.8) - WordPressShared (~> 1.8.16) - - WordPressUI (~> 1.5.2) + - WordPressUI (~> 1.5.3-beta.1) - WPMediaPicker (~> 1.6.1) - - Yoga (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/Yoga.podspec.json`) + - Yoga (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/Yoga.podspec.json`) - ZendeskSupportSDK (= 5.0.0) - ZIPFoundation (~> 0.9.8) @@ -514,6 +517,7 @@ SPEC REPOS: trunk: - 1PasswordExtension - Alamofire + - AlamofireImage - AlamofireNetworkActivityIndicator - AppCenter - Automattic-Tracks-iOS @@ -565,91 +569,92 @@ SPEC REPOS: EXTERNAL SOURCES: FBLazyVector: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/FBLazyVector.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/FBLazyVector.podspec.json FBReactNativeSpec: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/FBReactNativeSpec.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/FBReactNativeSpec.podspec.json Folly: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/Folly.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/Folly.podspec.json FSInteractiveMap: :git: https://github.com/wordpress-mobile/FSInteractiveMap.git :tag: 0.2.0 glog: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/glog.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/glog.podspec.json Gutenberg: - :commit: 65a87cfbaa47822c3f742426b2aff02c4a2dbd1d + :commit: fe07af1691e4f5f869e4824ebf26a9b8326bafec :git: http://github.com/wordpress-mobile/gutenberg-mobile/ RCTRequired: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/RCTRequired.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/RCTRequired.podspec.json RCTTypeSafety: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/RCTTypeSafety.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/RCTTypeSafety.podspec.json React: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React.podspec.json React-Core: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-Core.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-Core.podspec.json React-CoreModules: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-CoreModules.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-CoreModules.podspec.json React-cxxreact: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-cxxreact.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-cxxreact.podspec.json React-jsi: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-jsi.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-jsi.podspec.json React-jsiexecutor: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-jsiexecutor.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-jsiexecutor.podspec.json React-jsinspector: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-jsinspector.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-jsinspector.podspec.json react-native-keyboard-aware-scroll-view: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/react-native-keyboard-aware-scroll-view.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/react-native-keyboard-aware-scroll-view.podspec.json react-native-linear-gradient: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/react-native-linear-gradient.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/react-native-linear-gradient.podspec.json react-native-safe-area: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/react-native-safe-area.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/react-native-safe-area.podspec.json react-native-slider: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/react-native-slider.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/react-native-slider.podspec.json react-native-video: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/react-native-video.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/react-native-video.podspec.json React-RCTActionSheet: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTActionSheet.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTActionSheet.podspec.json React-RCTAnimation: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTAnimation.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTAnimation.podspec.json React-RCTBlob: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTBlob.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTBlob.podspec.json React-RCTImage: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTImage.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTImage.podspec.json React-RCTLinking: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTLinking.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTLinking.podspec.json React-RCTNetwork: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTNetwork.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTNetwork.podspec.json React-RCTSettings: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTSettings.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTSettings.podspec.json React-RCTText: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTText.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTText.podspec.json React-RCTVibration: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/React-RCTVibration.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/React-RCTVibration.podspec.json ReactCommon: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/ReactCommon.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/ReactCommon.podspec.json ReactNativeDarkMode: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/ReactNativeDarkMode.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/ReactNativeDarkMode.podspec.json RNSVG: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/RNSVG.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/RNSVG.podspec.json RNTAztecView: - :commit: 65a87cfbaa47822c3f742426b2aff02c4a2dbd1d + :commit: fe07af1691e4f5f869e4824ebf26a9b8326bafec :git: http://github.com/wordpress-mobile/gutenberg-mobile/ Yoga: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/65a87cfbaa47822c3f742426b2aff02c4a2dbd1d/react-native-gutenberg-bridge/third-party-podspecs/Yoga.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/fe07af1691e4f5f869e4824ebf26a9b8326bafec/react-native-gutenberg-bridge/third-party-podspecs/Yoga.podspec.json CHECKOUT OPTIONS: FSInteractiveMap: :git: https://github.com/wordpress-mobile/FSInteractiveMap.git :tag: 0.2.0 Gutenberg: - :commit: 65a87cfbaa47822c3f742426b2aff02c4a2dbd1d + :commit: fe07af1691e4f5f869e4824ebf26a9b8326bafec :git: http://github.com/wordpress-mobile/gutenberg-mobile/ RNTAztecView: - :commit: 65a87cfbaa47822c3f742426b2aff02c4a2dbd1d + :commit: fe07af1691e4f5f869e4824ebf26a9b8326bafec :git: http://github.com/wordpress-mobile/gutenberg-mobile/ SPEC CHECKSUMS: 1PasswordExtension: f97cc80ae58053c331b2b6dc8843ba7103b33794 Alamofire: 3ec537f71edc9804815215393ae2b1a8ea33a844 + AlamofireImage: 63cfe3baf1370be6c498149687cf6db3e3b00999 AlamofireNetworkActivityIndicator: 9acc3de3ca6645bf0efed462396b0df13dd3e7b8 AppCenter: fddcbac6e4baae3d93a196ceb0bfe0e4ce407dec Automattic-Tracks-iOS: dbe6301bebdc1e444972475bae19299491702cef @@ -658,8 +663,8 @@ SPEC CHECKSUMS: CocoaLumberjack: 118bf4a820efc641f79fa487b75ed928dccfae23 DoubleConversion: e22e0762848812a87afd67ffda3998d9ef29170c Down: 71bf4af3c04fa093e65dffa25c4b64fa61287373 - FBLazyVector: 7a1a9ff04fcb6a77f61cf3f3e098363f3f832365 - FBReactNativeSpec: d635a0109fd933a49ad43894db0728a7bbbea9ea + FBLazyVector: 64978abf8c83aaa8d7b04d158c468f80676ddd88 + FBReactNativeSpec: 893789f647bc8888fe6f9f1d992ee32807cd966b Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51 FormatterKit: 4b8f29acc9b872d5d12a63efb560661e8f2e1b98 FSInteractiveMap: a396f610f48b76cb540baa87139d056429abda86 @@ -669,7 +674,7 @@ SPEC CHECKSUMS: GoogleToolboxForMac: 800648f8b3127618c1b59c7f97684427630c5ea3 Gridicons: 8e19276b20bb15d1fda1d4d0db96d066d170135b GTMSessionFetcher: cea130bbfe5a7edc8d06d3f0d17288c32ffe9925 - Gutenberg: 3616ff3d49ec06a96c66f86ce0b65bfa289603a4 + Gutenberg: 1fb6dfdeedddfcae87420dfcca244c1e8b6f8ea1 JTAppleCalendar: 932cadea40b1051beab10f67843451d48ba16c99 lottie-ios: 85ce835dd8c53e02509f20729fc7d6a4e6645a0a MediaEditor: 7296cd01d7a0548fb2bc909aa72153b376a56a61 @@ -679,31 +684,31 @@ SPEC CHECKSUMS: "NSURL+IDN": afc873e639c18138a1589697c3add197fe8679ca OCMock: 43565190abc78977ad44a61c0d20d7f0784d35ab OHHTTPStubs: 1e21c7d2c084b8153fc53d48400d8919d2d432d0 - RCTRequired: 16d45ec7a9d9e87366625097b1b5741d69e2cc70 - RCTTypeSafety: dea638ef2caf9db4204e8ce8f0269ff7f92f18ca + RCTRequired: 43c2c939312bcaddf2fea58afa494a567355ddf1 + RCTTypeSafety: 12314b3e82b882a06fe5e07aa45c3e3de0732278 Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 - React: 0656778479ccb391d6a5bca5ab3054fb778b4145 - React-Core: 2050c5e2ff21397f3f1f71c8a739635af865a559 - React-CoreModules: 5bc433f7515436f3a0e9daf48146ca8bdbb2242d - React-cxxreact: f52ae8684ac2e3ffbfb590a6ac2b4dd800a67e8a - React-jsi: a581d6a62ca3f64ee9ca6f5bb30be00693373e6a - React-jsiexecutor: a978091aeb975ae5f7d2322b6633db273017b15d - React-jsinspector: 5b19ab21bb201b52d0ccc5adc9fd439d7bf3a84b + React: be76b87c9baeb0a4de84452ce62cf3cac06b27fc + React-Core: 36baed46b5834dce2b45c6133e9e492cf7c1ac1a + React-CoreModules: 54262a9b4347b2ee02e2858bd8c973b68daa0942 + React-cxxreact: c234d4e99d239f5f43d14ff45efdbdf551931083 + React-jsi: 03d027b7e6f22ec8d5a01a8129566866181b747a + React-jsiexecutor: 56064d4883a8b496dde00c0eb737273b7ecddd68 + React-jsinspector: dffce2384103642d357103fb8d644030d7422650 react-native-keyboard-aware-scroll-view: ffa9152671fec9a571197ed2d02e0fcb90206e60 react-native-linear-gradient: 258ba8c61848324b1f2019bed5f460e6396137b7 react-native-safe-area: e8230b0017d76c00de6b01e2412dcf86b127c6a3 react-native-slider: b36527edad24d49d9f3b53f3078334f45558f97b react-native-video: 9de661e89386bb7ab78cc68e61a146cbdf5ad4ad - React-RCTActionSheet: a14af2209e93e9b1d0c915dd59d6206c881e7d3f - React-RCTAnimation: 1f64e577d1f58903a9c6cacc77cab4ba08aa97c3 - React-RCTBlob: 7d09470ae87c47536895a59722966e0161f6a539 - React-RCTImage: f2544bc5fce778d6d441bd396efac4c6ee46dc91 - React-RCTLinking: 113566f540ca031b06fec76f75f339bd765affee - React-RCTNetwork: 21c0dcc27c5e3bbad4b29041f42a30c5183fdc79 - React-RCTSettings: 695a02f7d969966b2630c80a82e047744bcb03f7 - React-RCTText: fbfc2f6337da31e9e9f0dc393b1d1c16f614eec2 - React-RCTVibration: e07e47f4b6bb7ecd02628189620feca1072f7331 - ReactCommon: 4adf4600a0e45e6452e96c3f9c347c7fed3231f5 + React-RCTActionSheet: 9b5bd02e3fdc992a3e09ebac4b3857bcdc9cc30d + React-RCTAnimation: 1c22698087b4f86f9c0b09786710b0fa25beb2b5 + React-RCTBlob: 5e856ce211d56478c3c8e4ce2fe873493914be40 + React-RCTImage: 6c1a022a7e1fdb76d86e3faa08ff15babae048ec + React-RCTLinking: ef315fa75dc0e05c10d36535516ac4cc9be7fcd6 + React-RCTNetwork: 271e302067e1181777be73f829fc105ed3bfeb0c + React-RCTSettings: a3439d548140b669dba4df71bebf8e95580634db + React-RCTText: 608321a0abd0533e15ae3dc02e35e12fc51b5f92 + React-RCTVibration: 9ba85362c27d453efbbb64808bbb07ca9769c9ec + ReactCommon: 99515d33368553e35eb6cc4ae23e50ea8c2945da ReactNativeDarkMode: f61376360c5d983907e5c316e8e1c853a8c2f348 RNSVG: 68a534a5db06dcbdaebfd5079349191598caef7b RNTAztecView: 3fb0f122cea9b3bcf502da8380de5cd552d43585 @@ -717,13 +722,13 @@ SPEC CHECKSUMS: WordPress-Aztec-iOS: 319620514af963ca519bd83b96a2c0ebdf3a0f03 WordPress-Editor-iOS: 497b55838ef0030cc6ca82eb92da84e661423521 WordPressAuthenticator: d69a1516525a078abc0e6ed9692d4c1c3c866317 - WordPressKit: 0602e8188245b3267269570d3d78c138e64a4eba + WordPressKit: dde0a214279fb70d7150b69ae90c7224c18fe2d0 WordPressMocks: b4064b99a073117bbc304abe82df78f2fbe60992 WordPressShared: 1bc316ed162f42af4e0fa2869437e9e28b532b01 - WordPressUI: 70cc58a253c352330b23cd8fa6dd6a2021570e18 + WordPressUI: 91b91e51b8cd7db83a2bd492c12998db711a6a57 WPMediaPicker: 59559813ec8a7929a91aa5a1db74998d8485fb9f wpxmlrpc: 6a9bdd6ab9d1b159b384b0df0f3f39de9af4fecf - Yoga: 7631a01fbfbab4a26b9d644d636ea27350df5b2c + Yoga: 27ff7d4bc3a33088c0b60904f33b4ee59355b8ba ZendeskCommonUISDK: 3c432801e31abff97d6e30441ea102eaef6b99e2 ZendeskCoreSDK: 86513e62c1ab68913416c9044463d9b687ca944f ZendeskMessagingAPISDK: 7c0cbd1d2c941f05b36f73e7db5faee5863fe8b0 @@ -733,6 +738,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: a87ab1e4badace92c75eb11dc77ede1e995b2adc ZIPFoundation: 249fa8890597086cd536bb2df5c9804d84e122b0 -PODFILE CHECKSUM: b94624005c613df92921c3fda4eb5a24a31c20aa +PODFILE CHECKSUM: 3ccbfbcd909dd9ec3407af7f06fe56b21b7eaca4 COCOAPODS: 1.8.4 diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 9ee510e772d0..c247f3a2b754 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -1,4 +1,7 @@ 14.7 +---- +* Classic Editor: Fixed action sheet position for additional Media sources picker on iPad + ----- 14.6 diff --git a/WordPress/Classes/Extensions/AbstractPost+PostInformation.swift b/WordPress/Classes/Extensions/AbstractPost+PostInformation.swift deleted file mode 100644 index 39bc42b56466..000000000000 --- a/WordPress/Classes/Extensions/AbstractPost+PostInformation.swift +++ /dev/null @@ -1,27 +0,0 @@ - -extension AbstractPost: ImageSourceInformation { - var isPrivateOnWPCom: Bool { - return isPrivate() && blog.isHostedAtWPcom - } - - var isSelfHostedWithCredentials: Bool { - return blog.isSelfHostedWithCredentials - } - - var isLocalRevision: Bool { - return self.originalIsDraft() && self.isRevision() && self.remoteStatus == .local - } - - /// Returns true if the post is a draft and has never been uploaded to the server. - var isLocalDraft: Bool { - return self.isDraft() && !self.hasRemote() - } - - /// An autosave revision may include post title, content and/or excerpt. - var hasAutosaveRevision: Bool { - guard let autosaveRevisionIdentifier = autosaveIdentifier?.intValue else { - return false - } - return autosaveRevisionIdentifier > 0 - } -} diff --git a/WordPress/Classes/Extensions/Blog+ImageSourceInformation.swift b/WordPress/Classes/Extensions/Blog+ImageSourceInformation.swift deleted file mode 100644 index 5e1c8f4125ed..000000000000 --- a/WordPress/Classes/Extensions/Blog+ImageSourceInformation.swift +++ /dev/null @@ -1,10 +0,0 @@ - -extension Blog: ImageSourceInformation { - var isPrivateOnWPCom: Bool { - return isHostedAtWPcom && isPrivate() - } - - var isSelfHostedWithCredentials: Bool { - return !isHostedAtWPcom && isBasicAuthCredentialStored() - } -} diff --git a/WordPress/Classes/Extensions/UIImageView+SiteIcon.swift b/WordPress/Classes/Extensions/UIImageView+SiteIcon.swift index 9241fd9d5f03..f7e527f9a925 100644 --- a/WordPress/Classes/Extensions/UIImageView+SiteIcon.swift +++ b/WordPress/Classes/Extensions/UIImageView+SiteIcon.swift @@ -1,6 +1,7 @@ +import AlamofireImage +import AutomatticTracks import Foundation - /// UIImageView Helper Methods that allow us to download a SiteIcon, given a website's "Icon Path" /// extension UIImageView { @@ -48,7 +49,48 @@ extension UIImageView { return } - downloadImage(from: siteIconURL, placeholderImage: placeholderImage) + let request = URLRequest(url: siteIconURL) + downloadSiteIcon(with: request, placeholderImage: placeholderImage) + } + + /// Downloads a SiteIcon image, using a specified request. + /// + /// - Parameters: + /// - request: the request for the SiteIcon. + /// - placeholderImage: Yes. It's the "place holder image". + /// + private func downloadSiteIcon( + with request: URLRequest, + placeholderImage: UIImage?) { + + af_setImage(withURLRequest: request, placeholderImage: placeholderImage) { [weak self] dataResponse in + switch dataResponse.result { + case .success(let image): + guard let self = self else { + return + } + + // In `MediaRequesAuthenticator.authenticatedRequestForPrivateAtomicSiteThroughPhoton` we're + // having to replace photon URLs for Atomic Private Sites, with a call to the Atomic Media Proxy + // endpoint. The downside of calling that endpoint is that it doesn't always return images of + // the requested size. + // + // The following lines of code ensure that we resize the image to the default Site Icon size, to + // ensure there is no UI breakage due to having larger images set here. + // + let expectedSize = CGSize(width: SiteIconDefaults.imageSize, height: SiteIconDefaults.imageSize) + + if image.size != expectedSize { + self.image = image.resizedImage(with: .scaleAspectFill, bounds: expectedSize, interpolationQuality: .default) + } else { + self.image = image + } + + self.removePlaceholderBorder() + case .failure(let error): + CrashLogging.logError(error) + } + } } @@ -66,17 +108,20 @@ extension UIImageView { return } - let request: URLRequest - if blog.isPrivate(), PrivateSiteURLProtocol.urlGoes(toWPComSite: siteIconURL) { - request = PrivateSiteURLProtocol.requestForPrivateSite(from: siteIconURL) - } else { - request = URLRequest(url: siteIconURL) + let host = MediaHost(with: blog) { error in + // We'll log the error, so we know it's there, but we won't halt execution. + CrashLogging.logError(error) } - downloadImage(usingRequest: request, placeholderImage: placeholderImage, success: { [weak self] (image) in - self?.image = image - self?.removePlaceholderBorder() - }, failure: nil) + let mediaRequestAuthenticator = MediaRequestAuthenticator() + mediaRequestAuthenticator.authenticatedRequest( + for: siteIconURL, + from: host, + onComplete: { [weak self] request in + self?.downloadSiteIcon(with: request, placeholderImage: placeholderImage) + }) { error in + CrashLogging.logError(error) + } } } diff --git a/WordPress/Classes/Models/AbstractPost+Autosave.swift b/WordPress/Classes/Models/AbstractPost+Autosave.swift new file mode 100644 index 000000000000..8f2ca445eaa3 --- /dev/null +++ b/WordPress/Classes/Models/AbstractPost+Autosave.swift @@ -0,0 +1,11 @@ +import Foundation + +extension AbstractPost { + /// An autosave revision may include post title, content and/or excerpt. + var hasAutosaveRevision: Bool { + guard let autosaveRevisionIdentifier = autosaveIdentifier?.intValue else { + return false + } + return autosaveRevisionIdentifier > 0 + } +} diff --git a/WordPress/Classes/Models/AbstractPost+Local.swift b/WordPress/Classes/Models/AbstractPost+Local.swift new file mode 100644 index 000000000000..aa15883d3e4b --- /dev/null +++ b/WordPress/Classes/Models/AbstractPost+Local.swift @@ -0,0 +1,12 @@ +import Foundation + +extension AbstractPost { + /// Returns true if the post is a draft and has never been uploaded to the server. + var isLocalDraft: Bool { + return self.isDraft() && !self.hasRemote() + } + + var isLocalRevision: Bool { + return self.originalIsDraft() && self.isRevision() && self.remoteStatus == .local + } +} diff --git a/WordPress/Classes/Models/AbstractPost.h b/WordPress/Classes/Models/AbstractPost.h index 3175e05fbbc0..d11802c97f0e 100644 --- a/WordPress/Classes/Models/AbstractPost.h +++ b/WordPress/Classes/Models/AbstractPost.h @@ -96,7 +96,7 @@ typedef NS_ENUM(NSUInteger, AbstractPostRemoteStatus) { - (NSString *)blavatarForDisplay; - (NSString *)dateStringForDisplay; - (BOOL)isMultiAuthorBlog; -- (BOOL)isPrivate; +- (BOOL)isPrivateAtWPCom; - (BOOL)supportsStats; diff --git a/WordPress/Classes/Models/AbstractPost.m b/WordPress/Classes/Models/AbstractPost.m index b3d094ed8a3a..07cd7b99679b 100644 --- a/WordPress/Classes/Models/AbstractPost.m +++ b/WordPress/Classes/Models/AbstractPost.m @@ -475,9 +475,9 @@ - (BOOL)supportsStats return [self.blog supports:BlogFeatureStats] && [self hasRemote]; } -- (BOOL)isPrivate +- (BOOL)isPrivateAtWPCom { - return self.blog.isPrivate; + return self.blog.isPrivateAtWPCom; } - (BOOL)isMultiAuthorBlog diff --git a/WordPress/Classes/Models/Blog.h b/WordPress/Classes/Models/Blog.h index fdc5584fdfb9..0b5fc47471d8 100644 --- a/WordPress/Classes/Models/Blog.h +++ b/WordPress/Classes/Models/Blog.h @@ -192,6 +192,7 @@ typedef NS_ENUM(NSInteger, SiteVisibility) { - (BOOL)isAtomic; - (BOOL)isAutomatedTransfer; - (BOOL)isPrivate; +- (BOOL)isPrivateAtWPCom; - (nullable NSArray *)sortedCategories; - (nullable id)getOptionValue:(NSString *) name; - (NSString *)loginUrl; diff --git a/WordPress/Classes/Models/Blog.m b/WordPress/Classes/Models/Blog.m index a51bfdd5c3b8..bccc3132f70a 100644 --- a/WordPress/Classes/Models/Blog.m +++ b/WordPress/Classes/Models/Blog.m @@ -331,10 +331,18 @@ - (NSString *)postFormatTextFromSlug:(NSString *)postFormatSlug return formatText; } -// WP.COM private blog. +/// Call this method to know whether the blog is private. +/// - (BOOL)isPrivate { - return (self.isHostedAtWPcom && [self.settings.privacy isEqualToNumber:@(SiteVisibilityPrivate)]); + return [self.settings.privacy isEqualToNumber:@(SiteVisibilityPrivate)]; +} + +/// Call this method to know whether the blog is private AND hosted at WP.com. +/// +- (BOOL)isPrivateAtWPCom +{ + return (self.isHostedAtWPcom && [self isPrivate]); } - (SiteVisibility)siteVisibility @@ -438,7 +446,7 @@ - (NSString *)usernameForSite { if (self.username) { return self.username; - } else if (self.account && self.isHostedAtWPcom) { + } else if (self.account && self.isAccessibleThroughWPCom) { return self.account.username; } else { // FIXME: Figure out how to get the self hosted username when using Jetpack REST (@koke 2015-06-15) diff --git a/WordPress/Classes/Models/Comment.m b/WordPress/Classes/Models/Comment.m index 446a9d3a0856..9c0c0c5dc3fb 100644 --- a/WordPress/Classes/Models/Comment.m +++ b/WordPress/Classes/Models/Comment.m @@ -102,8 +102,8 @@ - (NSDate *)dateCreated - (BOOL)isPrivateContent { - if ([self.post respondsToSelector:@selector(isPrivate)]) { - return (BOOL)[self.post performSelector:@selector(isPrivate)]; + if ([self.post respondsToSelector:@selector(isPrivateAtWPCom)]) { + return (BOOL)[self.post performSelector:@selector(isPrivateAtWPCom)]; } return NO; } diff --git a/WordPress/Classes/Models/ReaderCardContent+PostInformation.swift b/WordPress/Classes/Models/ReaderCardContent+PostInformation.swift deleted file mode 100644 index cbc965310715..000000000000 --- a/WordPress/Classes/Models/ReaderCardContent+PostInformation.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -class ReaderCardContent: ImageSourceInformation { - private let originalProvider: ReaderPostContentProvider - - init(provider: ReaderPostContentProvider) { - originalProvider = provider - } - - var isPrivateOnWPCom: Bool { - return originalProvider.isPrivate() && originalProvider.isWPCom() - } - - var isSelfHostedWithCredentials: Bool { - return !originalProvider.isWPCom() && !originalProvider.isJetpack() - } -} diff --git a/WordPress/Classes/Models/ReaderPost.h b/WordPress/Classes/Models/ReaderPost.h index 9ed8f8b7d1b3..130c20ebd7a1 100644 --- a/WordPress/Classes/Models/ReaderPost.h +++ b/WordPress/Classes/Models/ReaderPost.h @@ -26,6 +26,7 @@ extern NSString * const ReaderPostStoredCommentTextKey; @property (nonatomic, strong) NSNumber *feedID; @property (nonatomic, strong) NSNumber *feedItemID; @property (nonatomic, strong) NSString *globalID; +@property (nonatomic) BOOL isBlogAtomic; @property (nonatomic) BOOL isBlogPrivate; @property (nonatomic) BOOL isFollowing; @property (nonatomic) BOOL isLiked; diff --git a/WordPress/Classes/Models/ReaderPost.m b/WordPress/Classes/Models/ReaderPost.m index fb67c389fc34..56fb70cea524 100644 --- a/WordPress/Classes/Models/ReaderPost.m +++ b/WordPress/Classes/Models/ReaderPost.m @@ -26,6 +26,7 @@ @implementation ReaderPost @dynamic featuredImage; @dynamic feedID; @dynamic feedItemID; +@dynamic isBlogAtomic; @dynamic isBlogPrivate; @dynamic isFollowing; @dynamic isLiked; @@ -67,11 +68,7 @@ - (BOOL)isCrossPost - (BOOL)isAtomic { - // TODO: This is temporary until we start parsing a new value from the reader endpoint. - // - // Issue: https://git.io/JvNAN - // - return false; + return self.isBlogAtomic; } - (BOOL)isPrivate diff --git a/WordPress/Classes/Networking/MediaHost+ReaderPostContentProvider.swift b/WordPress/Classes/Networking/MediaHost+ReaderPostContentProvider.swift index ef7e4d55f0fd..d7dada938682 100644 --- a/WordPress/Classes/Networking/MediaHost+ReaderPostContentProvider.swift +++ b/WordPress/Classes/Networking/MediaHost+ReaderPostContentProvider.swift @@ -5,16 +5,29 @@ import Foundation /// extension MediaHost { enum ReaderPostContentProviderError: Swift.Error { + case noDefaultWordPressComAccount case baseInitializerError(error: Error, readerPostContentProvider: ReaderPostContentProvider) } init(with readerPostContentProvider: ReaderPostContentProvider, failure: (ReaderPostContentProviderError) -> ()) { let isAccessibleThroughWPCom = readerPostContentProvider.isWPCom() || readerPostContentProvider.isJetpack() + // This is the only way in which we can obtain the username and authToken here. + // It'd be nice if all data was associated with an account instead, for transparency + // and cleanliness of the code - but this'll have to do for now. + let accountService = AccountService(managedObjectContext: ContextManager.shared.mainContext) + + // We allow a nil account in case the user connected only self-hosted sites. + let account = accountService.defaultWordPressComAccount() + let username = account?.username + let authToken = account?.authToken + self.init(isAccessibleThroughWPCom: isAccessibleThroughWPCom, isPrivate: readerPostContentProvider.isPrivate(), isAtomic: readerPostContentProvider.isAtomic(), siteID: readerPostContentProvider.siteID()?.intValue, + username: username, + authToken: authToken, failure: { error in // We just associate a ReaderPostContentProvider with the underlying error for simpler debugging. failure(ReaderPostContentProviderError.baseInitializerError( diff --git a/WordPress/Classes/Networking/MediaHost.swift b/WordPress/Classes/Networking/MediaHost.swift index fe0b81f2ae17..12741a6396c7 100644 --- a/WordPress/Classes/Networking/MediaHost.swift +++ b/WordPress/Classes/Networking/MediaHost.swift @@ -19,9 +19,9 @@ enum MediaHost: Equatable { isAccessibleThroughWPCom: Bool, isPrivate: Bool, isAtomic: Bool, - siteID: Int? = nil, - username: String? = nil, - authToken: String? = nil, + siteID: Int?, + username: String?, + authToken: String?, failure: (Error) -> Void) { guard isPrivate else { diff --git a/WordPress/Classes/Networking/MediaRequestAuthenticator.swift b/WordPress/Classes/Networking/MediaRequestAuthenticator.swift index 3e5c78658bd8..7d16c47a8c82 100644 --- a/WordPress/Classes/Networking/MediaRequestAuthenticator.swift +++ b/WordPress/Classes/Networking/MediaRequestAuthenticator.swift @@ -214,11 +214,12 @@ class MediaRequestAuthenticator { return } - let contentPath = components.path[wpContentRange.lowerBound ..< components.path.endIndex] + let contentPath = String(components.path[wpContentRange.lowerBound ..< components.path.endIndex]) components.scheme = secureHttpScheme components.host = wpComApiHost - components.path = "/wpcom/v2/sites/\(siteID)/atomic-auth-proxy/file\(contentPath)" + components.path = "/wpcom/v2/sites/\(siteID)/atomic-auth-proxy/file" + components.queryItems = [URLQueryItem(name: "path", value: contentPath)] guard let finalURL = components.url else { fail(Error.cannotCreateAtomicProxyURL(components: components)) diff --git a/WordPress/Classes/Services/MediaThumbnailService.swift b/WordPress/Classes/Services/MediaThumbnailService.swift index 768f65a87260..a718ba9db68c 100644 --- a/WordPress/Classes/Services/MediaThumbnailService.swift +++ b/WordPress/Classes/Services/MediaThumbnailService.swift @@ -162,7 +162,7 @@ class MediaThumbnailService: LocalCoreDataService { return } // Get an expected WP URL, for sizing. - if media.blog.isPrivate() || (!media.blog.isHostedAtWPcom && media.blog.isBasicAuthCredentialStored()) { + if media.blog.isPrivateAtWPCom() || (!media.blog.isHostedAtWPcom && media.blog.isBasicAuthCredentialStored()) { remoteURL = WPImageURLHelper.imageURLWithSize(preferredSize, forImageURL: remoteAssetURL) } else { remoteURL = PhotonImageURLHelper.photonURL(with: preferredSize, forImageURL: remoteAssetURL) @@ -187,26 +187,10 @@ class MediaThumbnailService: LocalCoreDataService { onError(error) } } - if media.blog.isPrivate() { - let accountService = AccountService(managedObjectContext: self.managedObjectContext) - guard let authToken = accountService.defaultWordPressComAccount()?.authToken else { - // Don't have an auth token for some reason, return nothing. - onCompletion(nil) - return - } - DispatchQueue.main.async { - WPImageSource.shared().downloadImage(for: imageURL, - authToken: authToken, - withSuccess: inContextImageHandler, - failure: inContextErrorHandler) - } - } else { - DispatchQueue.main.async { - WPImageSource.shared().downloadImage(for: imageURL, - withSuccess: inContextImageHandler, - failure: inContextErrorHandler) - } - } + + let download = AuthenticatedImageDownload(url: imageURL, blog: media.blog, onSuccess: inContextImageHandler, onFailure: inContextErrorHandler) + + download.start() } // MARK: - Helpers diff --git a/WordPress/Classes/Services/ReaderPostService.m b/WordPress/Classes/Services/ReaderPostService.m index a40f3c826f26..8de9c22db52a 100644 --- a/WordPress/Classes/Services/ReaderPostService.m +++ b/WordPress/Classes/Services/ReaderPostService.m @@ -1159,6 +1159,7 @@ - (ReaderPost *)createOrReplaceFromRemotePost:(RemoteReaderPost *)remotePost for post.feedID = remotePost.feedID; post.feedItemID = remotePost.feedItemID; post.globalID = remotePost.globalID; + post.isBlogAtomic = remotePost.isBlogAtomic; post.isBlogPrivate = remotePost.isBlogPrivate; post.isFollowing = remotePost.isFollowing; post.isLiked = remotePost.isLiked; diff --git a/WordPress/Classes/System/WordPress-Bridging-Header.h b/WordPress/Classes/System/WordPress-Bridging-Header.h index bb15a59638c9..2f5a4465c89f 100644 --- a/WordPress/Classes/System/WordPress-Bridging-Header.h +++ b/WordPress/Classes/System/WordPress-Bridging-Header.h @@ -55,7 +55,6 @@ #import "WPProgressTableViewCell.h" #import "PostTag.h" #import "PostTagService.h" -#import "PrivateSiteURLProtocol.h" #import "ReachabilityUtils.h" #import "ReaderCommentsViewController.h" diff --git a/WordPress/Classes/Utility/Bottom Sheet/BottomSheetViewController.swift b/WordPress/Classes/Utility/Bottom Sheet/BottomSheetViewController.swift new file mode 100644 index 000000000000..d33b3c20d4ab --- /dev/null +++ b/WordPress/Classes/Utility/Bottom Sheet/BottomSheetViewController.swift @@ -0,0 +1,165 @@ +import UIKit + +class BottomSheetViewController: UIViewController { + enum Constants { + static let gripHeight: CGFloat = 5 + static let cornerRadius: CGFloat = 8 + static let buttonSpacing: CGFloat = 8 + static let additionalSafeAreaInsetsRegular: UIEdgeInsets = UIEdgeInsets(top: 20, left: 0, bottom: 20, right: 0) + static let minimumWidth: CGFloat = 300 + + enum Header { + static let spacing: CGFloat = 16 + static let insets: UIEdgeInsets = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 18) + } + + enum Button { + static let height: CGFloat = 54 + static let contentInsets: UIEdgeInsets = UIEdgeInsets(top: 0, left: 18, bottom: 0, right: 35) + static let titleInsets: UIEdgeInsets = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 0) + static let imageTintColor: UIColor = .neutral(.shade30) + static let font: UIFont = .preferredFont(forTextStyle: .callout) + static let textColor: UIColor = .text + } + + enum Stack { + static let insets: UIEdgeInsets = UIEdgeInsets(top: 5, left: 0, bottom: 0, right: 0) + } + } + + private weak var childViewController: DrawerPresentableViewController? + + init(childViewController: DrawerPresentableViewController) { + self.childViewController = childViewController + super.init(nibName: nil, bundle: nil) + } + + func show(from presenting: UIViewController, sourceView: UIView? = nil) { + if UIDevice.isPad() { + modalPresentationStyle = .popover + popoverPresentationController?.sourceView = sourceView ?? UIView() + popoverPresentationController?.sourceRect = sourceView?.bounds ?? .zero + } else { + transitioningDelegate = self + modalPresentationStyle = .custom + } + + presenting.present(self, animated: true) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private var gripButton: UIButton = { + let button = GripButton() + button.translatesAutoresizingMaskIntoConstraints = false + button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside) + return button + }() + + @objc func buttonPressed() { + dismiss(animated: true, completion: nil) + } + + override func viewDidLoad() { + super.viewDidLoad() + + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil) + + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil) + + view.clipsToBounds = true + view.layer.cornerRadius = Constants.cornerRadius + view.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner] + view.backgroundColor = .basicBackground + + NSLayoutConstraint.activate([ + gripButton.heightAnchor.constraint(equalToConstant: Constants.gripHeight) + ]) + + guard let childViewController = childViewController else { + return + } + + addChild(childViewController) + + let stackView = UIStackView(arrangedSubviews: [ + gripButton, + childViewController.view + ]) + + stackView.setCustomSpacing(Constants.Header.spacing, after: gripButton) + + stackView.translatesAutoresizingMaskIntoConstraints = false + stackView.axis = .vertical + + refreshForTraits() + + view.addSubview(stackView) + view.pinSubviewToSafeArea(stackView, insets: Constants.Stack.insets) + + childViewController.didMove(toParent: self) + } + + open override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + refreshForTraits() + } + + private func refreshForTraits() { + if presentingViewController?.traitCollection.horizontalSizeClass == .regular && presentingViewController?.traitCollection.verticalSizeClass != .compact { + gripButton.isHidden = true + additionalSafeAreaInsets = Constants.additionalSafeAreaInsetsRegular + } else { + gripButton.isHidden = false + additionalSafeAreaInsets = .zero + } + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + return preferredContentSize = CGSize(width: Constants.minimumWidth, height: view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height) + } + + @objc func keyboardWillShow(_ notification: NSNotification) { + self.presentedVC?.transition(to: .expanded) + } + + @objc func keyboardWillHide(_ notification: NSNotification) { + self.presentedVC?.transition(to: .collapsed) + } +} + +extension BottomSheetViewController: UIViewControllerTransitioningDelegate { + public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { + return BottomSheetAnimationController(transitionType: .presenting) + } + + public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { + return BottomSheetAnimationController(transitionType: .dismissing) + } + + public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { + return DrawerPresentationController(presentedViewController: presented, presenting: presenting) + } +} + +// MARK: - DrawerDelegate +extension BottomSheetViewController: DrawerPresentable { + var compactWidth: DrawerWidth { + childViewController?.compactWidth ?? .percentage(0.66) + } + + var expandedHeight: DrawerHeight { + return childViewController?.expandedHeight ?? .maxHeight + } + + var collapsedHeight: DrawerHeight { + return childViewController?.collapsedHeight ?? .contentHeight(200) + } + + var scrollableView: UIScrollView? { + return childViewController?.scrollableView + } +} diff --git a/WordPress/Classes/Utility/Media/ImageDownloader.swift b/WordPress/Classes/Utility/Media/ImageDownloader.swift index 40c01ea0a46a..d7b586370629 100644 --- a/WordPress/Classes/Utility/Media/ImageDownloader.swift +++ b/WordPress/Classes/Utility/Media/ImageDownloader.swift @@ -1,18 +1,32 @@ import Foundation +// MARK: - ImageDownloadTask protocol + +/// This protocol can be implemented to represent an image download task handled by the ImageDownloader. +/// +protocol ImageDownloaderTask { + /// Calling this method should cancel the task's execution. + /// + func cancel() +} + +extension Operation: ImageDownloaderTask {} +extension URLSessionTask: ImageDownloaderTask {} + +extension URLSession: ImageDownloaderTask { + func cancel() { + invalidateAndCancel() + } +} // MARK: - Image Downloading Tool -// + class ImageDownloader { /// Shared Instance! /// static let shared = ImageDownloader() - /// Public Aliases - /// - typealias Task = URLSessionDataTask - /// Internal URLSession Instance /// private let session = URLSession(configuration: .default) @@ -26,7 +40,7 @@ class ImageDownloader { /// Downloads the UIImage resource at the specified URL. On completion the received closure will be executed. /// @discardableResult - func downloadImage(at url: URL, completion: @escaping (UIImage?, Error?) -> Void) -> Task { + func downloadImage(at url: URL, completion: @escaping (UIImage?, Error?) -> Void) -> ImageDownloaderTask { var request = URLRequest(url: url) request.httpShouldHandleCookies = false request.addValue("image/*", forHTTPHeaderField: "Accept") @@ -37,7 +51,7 @@ class ImageDownloader { /// Downloads the UIImage resource at the specified endpoint. On completion the received closure will be executed. /// @discardableResult - func downloadImage(for request: URLRequest, completion: @escaping (UIImage?, Error?) -> Void) -> Task { + func downloadImage(for request: URLRequest, completion: @escaping (UIImage?, Error?) -> Void) -> ImageDownloaderTask { let task = session.dataTask(with: request) { (data, _, error) in guard let data = data, let image = UIImage(data: data) else { let error = error ?? ImageDownloaderError.failed diff --git a/WordPress/Classes/Utility/Media/ImageLoader.swift b/WordPress/Classes/Utility/Media/ImageLoader.swift index 201fd9278af6..76eacfd61da1 100644 --- a/WordPress/Classes/Utility/Media/ImageLoader.swift +++ b/WordPress/Classes/Utility/Media/ImageLoader.swift @@ -1,18 +1,6 @@ import MobileCoreServices - -/// Protocol used to abstract the information needed to load post related images. -/// -@objc protocol ImageSourceInformation { - - /// The post is private and hosted on WPcom. - /// Redundant name due to naming conflict. - /// - var isPrivateOnWPCom: Bool { get } - - /// The blog is self-hosted and there is already a basic auth credential stored. - /// - var isSelfHostedWithCredentials: Bool { get } -} +import AlamofireImage +import AutomatticTracks /// Class used together with `CachedAnimatedImageView` to facilitate the loading of both /// still images and animated gifs. @@ -68,26 +56,24 @@ import MobileCoreServices imageView.prepForReuse() } - @objc(loadImageWithURL:fromPost:andPreferredSize:) /// Load an image from a specific post, using the given URL. Supports animated images (gifs) as well. /// /// - Parameters: /// - url: The URL to load the image from. - /// - post: The post where the image is loaded from. + /// - host: The `MediaHost` of the image. /// - size: The preferred size of the image to load. /// - func loadImage(with url: URL, from source: ImageSourceInformation, preferredSize size: CGSize = .zero) { + func loadImage(with url: URL, from host: MediaHost, preferredSize size: CGSize = .zero) { if url.isGif { - loadGif(with: url, from: source, preferredSize: size) + loadGif(with: url, from: host, preferredSize: size) } else { imageView.clean() - loadStaticImage(with: url, from: source, preferredSize: size) + loadStaticImage(with: url, from: host, preferredSize: size) } } - @objc(loadImageWithURL:success:error:) /// Load an image from a specific URL. As no source is provided, we can assume - /// that this is from an external source. Supports animated images (gifs) as well. + /// that this is from a public site. Supports animated images (gifs) as well. /// /// - Parameters: /// - url: The URL to load the image from. @@ -99,91 +85,103 @@ import MobileCoreServices errorHandler = error if url.isGif { - loadGif(with: url, from: nil) + loadGif(with: url, from: .publicSite) } else { imageView.clean() - loadStaticImage(with: url, from: nil) + loadStaticImage(with: url, from: .publicSite) } } @objc(loadImageWithURL:fromPost:preferredSize:placeholder:success:error:) + func loadImage(with url: URL, from post: AbstractPost, preferredSize size: CGSize = .zero, placeholder: UIImage?, success: ImageLoaderSuccessBlock?, error: ImageLoaderFailureBlock?) { + + let host = MediaHost(with: post, failure: { error in + CrashLogging.logError(error) + }) + + loadImage(with: url, from: host, preferredSize: size, placeholder: placeholder, success: success, error: error) + } + /// Load an image from a specific post, using the given URL. Supports animated images (gifs) as well. /// /// - Parameters: /// - url: The URL to load the image from. - /// - post: The post where the image is loaded from. + /// - host: The host of the image. /// - size: The preferred size of the image to load. You can pass height 0 to set width and preserve aspect ratio. /// - placeholder: A placeholder to show while the image is loading. /// - success: A closure to be called if the image was loaded successfully. /// - error: A closure to be called if there was an error loading the image. - func loadImage(with url: URL, from source: ImageSourceInformation, preferredSize size: CGSize = .zero, placeholder: UIImage?, success: ImageLoaderSuccessBlock?, error: ImageLoaderFailureBlock?) { + func loadImage(with url: URL, from host: MediaHost, preferredSize size: CGSize = .zero, placeholder: UIImage?, success: ImageLoaderSuccessBlock?, error: ImageLoaderFailureBlock?) { self.placeholder = placeholder successHandler = success errorHandler = error - loadImage(with: url, from: source, preferredSize: size) + loadImage(with: url, from: host, preferredSize: size) } // MARK: - Private helpers /// Load an animated image from the given URL. /// - private func loadGif(with url: URL, from source: ImageSourceInformation?, preferredSize size: CGSize = .zero) { - let request: URLRequest - if url.isFileURL { - request = URLRequest(url: url) - } else if let source = source, source.isPrivateOnWPCom, PrivateSiteURLProtocol.urlGoes(toWPComSite: url) { - request = PrivateSiteURLProtocol.requestForPrivateSite(from: url) - } else { - if let photonUrl = getPhotonUrl(for: url, size: size), - source != nil { - request = URLRequest(url: photonUrl) - } else { - request = URLRequest(url: url) - } - } - downloadGif(from: request) + private func loadGif(with url: URL, from host: MediaHost, preferredSize size: CGSize = .zero) { + let mediaAuthenticator = MediaRequestAuthenticator() + mediaAuthenticator.authenticatedRequest( + for: url, + from: host, + onComplete: { request in + self.downloadGif(from: request) + }, + onFailure: { error in + CrashLogging.logError(error) + self.callErrorHandler(with: error) + }) } /// Load a static image from the given URL. /// - private func loadStaticImage(with url: URL, from source: ImageSourceInformation?, preferredSize size: CGSize = .zero) { - if url.isFileURL { - downloadImage(from: url) - } else if let source = source { - if source.isPrivateOnWPCom && PrivateSiteURLProtocol.urlGoes(toWPComSite: url) { - loadPrivateImage(with: url, from: source, preferredSize: size) - } else if source.isSelfHostedWithCredentials { - downloadImage(from: url) - } else { - loadPhotonUrl(with: url, preferredSize: size) - } - } else { - downloadImage(from: url) + private func loadStaticImage(with url: URL, from host: MediaHost, preferredSize size: CGSize = .zero) { + let finalURL: URL + + switch host { + case .publicSite: fallthrough + case .privateSelfHostedSite: + finalURL = url + case .publicWPComSite: fallthrough + case .privateAtomicWPComSite(siteID: _): + finalURL = photonUrl(with: url, preferredSize: size) + case .privateWPComSite: + finalURL = privateImageURL(with: url, from: host, preferredSize: size) + } + + let mediaRequestAuthenticator = MediaRequestAuthenticator() + + mediaRequestAuthenticator.authenticatedRequest(for: finalURL, from: host, onComplete: { request in + self.downloadImage(from: request) + }) { error in + CrashLogging.logError(error) + self.callErrorHandler(with: error) } } - /// Loads the image from a private post hosted in WPCom. + /// Constructs the URL for an image from a private post hosted in WPCom. /// - private func loadPrivateImage(with url: URL, from source: ImageSourceInformation, preferredSize size: CGSize) { + private func privateImageURL(with url: URL, from host: MediaHost, preferredSize size: CGSize) -> URL { let scale = UIScreen.main.scale let scaledSize = CGSize(width: size.width * scale, height: size.height * scale) let scaledURL = WPImageURLHelper.imageURLWithSize(scaledSize, forImageURL: url) - let request = PrivateSiteURLProtocol.requestForPrivateSite(from: scaledURL) - downloadImage(from: request) + return scaledURL } - /// Loads the image from the Photon API with the given size. + /// Gets the photon URL with the specified size, or returns the passed `URL` /// - private func loadPhotonUrl(with url: URL, preferredSize size: CGSize) { + private func photonUrl(with url: URL, preferredSize size: CGSize) -> URL { guard let photonURL = getPhotonUrl(for: url, size: size) else { - downloadImage(from: url) - return + return url } - downloadImage(from: photonURL) + return photonURL } /// Download the animated image from the given URL Request. @@ -201,24 +199,25 @@ import MobileCoreServices /// private func downloadImage(from request: URLRequest) { imageView.startLoadingAnimation() - imageView.downloadImage(usingRequest: request, placeholderImage: placeholder, success: { [weak self] (image) in - // Since a success block is specified, we need to set the image manually. - self?.imageView.image = image - self?.callSuccessHandler() - }) { [weak self] (error) in - self?.callErrorHandler(with: error) - } + imageView.af_setImage(withURLRequest: request, completion: { [weak self] dataResponse in + guard let self = self else { + return + } + + switch dataResponse.result { + case .success: + self.callSuccessHandler() + case .failure(let error): + self.callErrorHandler(with: error) + } + }) } /// Downloads the image from the given URL. /// private func downloadImage(from url: URL) { - imageView.startLoadingAnimation() - imageView.downloadImage(from: url, placeholderImage: placeholder, success: { [weak self] (_) in - self?.callSuccessHandler() - }) { [weak self] (error) in - self?.callErrorHandler(with: error) - } + let request = URLRequest(url: url) + downloadImage(from: request) } private func callSuccessHandler() { @@ -301,7 +300,12 @@ extension ImageLoader { } if url.isGif { - loadGif(with: url, from: media.blog, preferredSize: size) + let host = MediaHost(with: media.blog) { error in + // We'll log the error, so we know it's there, but we won't halt execution. + CrashLogging.logError(error) + } + + loadGif(with: url, from: host, preferredSize: size) } else if imageView.image == nil { imageView.clean() loadImage(from: media, preferredSize: size) diff --git a/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift b/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift index 40d954ef4644..8326b364d7bb 100644 --- a/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift +++ b/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift @@ -360,7 +360,7 @@ class AztecPostViewController: UIViewController, PostEditor { /// Active Downloads /// - fileprivate var activeMediaRequests = [ImageDownloader.Task]() + fileprivate var activeMediaRequests = [ImageDownloaderTask]() /// Media Library Data Source /// diff --git a/WordPress/Classes/ViewRelated/Cells/PostFeaturedImageCell.h b/WordPress/Classes/ViewRelated/Cells/PostFeaturedImageCell.h index 4da86ab3382a..eae85221395c 100644 --- a/WordPress/Classes/ViewRelated/Cells/PostFeaturedImageCell.h +++ b/WordPress/Classes/ViewRelated/Cells/PostFeaturedImageCell.h @@ -1,6 +1,6 @@ #import -@protocol ImageSourceInformation; +@class AbstractPost; @class PostFeaturedImageCell; @protocol PostFeaturedImageCellDelegate @@ -16,6 +16,6 @@ extern CGFloat const PostFeaturedImageCellMargin; @property (weak, nonatomic, nullable) id delegate; @property (strong, nonatomic, readonly, nullable) UIImage *image; -- (void)setImageWithURL:(nonnull NSURL *)url inPost:(nonnull id)postInformation withSize:(CGSize)size; +- (void)setImageWithURL:(nonnull NSURL *)url inPost:(nonnull AbstractPost *)post withSize:(CGSize)size; @end diff --git a/WordPress/Classes/ViewRelated/Cells/PostFeaturedImageCell.m b/WordPress/Classes/ViewRelated/Cells/PostFeaturedImageCell.m index 248af25858b6..c0f4103c382b 100644 --- a/WordPress/Classes/ViewRelated/Cells/PostFeaturedImageCell.m +++ b/WordPress/Classes/ViewRelated/Cells/PostFeaturedImageCell.m @@ -27,10 +27,10 @@ - (void)setup _imageLoader = [[ImageLoader alloc] initWithImageView:self.featuredImageView gifStrategy:GIFStrategyLargeGIFs]; } -- (void)setImageWithURL:(NSURL *)url inPost:(id)postInformation withSize:(CGSize)size +- (void)setImageWithURL:(NSURL *)url inPost:(AbstractPost *)post withSize:(CGSize)size { __weak PostFeaturedImageCell *weakSelf = self; - [self.imageLoader loadImageWithURL:url fromPost:postInformation preferredSize:size placeholder:nil success:^{ + [self.imageLoader loadImageWithURL:url fromPost:post preferredSize:size placeholder:nil success:^{ [weakSelf informDelegateImageLoaded]; } error:^(NSError * _Nullable error) { if (weakSelf && weakSelf.delegate) { diff --git a/WordPress/Classes/ViewRelated/Gutenberg/AztecAttachmentDelegate.swift b/WordPress/Classes/ViewRelated/Gutenberg/AztecAttachmentDelegate.swift index 33e0eda2d8e1..8dfa1929acbb 100644 --- a/WordPress/Classes/ViewRelated/Gutenberg/AztecAttachmentDelegate.swift +++ b/WordPress/Classes/ViewRelated/Gutenberg/AztecAttachmentDelegate.swift @@ -2,7 +2,7 @@ import Aztec class AztecAttachmentDelegate: TextViewAttachmentDelegate { private let post: AbstractPost - private var activeMediaRequests = [ImageDownloader.Task]() + private var activeMediaRequests = [ImageDownloaderTask]() private let mediaUtility = EditorMediaUtility() init(post: AbstractPost) { diff --git a/WordPress/Classes/ViewRelated/Gutenberg/EditorMediaUtility.swift b/WordPress/Classes/ViewRelated/Gutenberg/EditorMediaUtility.swift index af77a0ea964a..08c4a9627df1 100644 --- a/WordPress/Classes/ViewRelated/Gutenberg/EditorMediaUtility.swift +++ b/WordPress/Classes/ViewRelated/Gutenberg/EditorMediaUtility.swift @@ -1,6 +1,57 @@ +import AutomatticTracks import Aztec import Gridicons +final class AuthenticatedImageDownload: AsyncOperation { + let url: URL + let blog: Blog + private let onSuccess: (UIImage) -> () + private let onFailure: (Error) -> () + + init(url: URL, blog: Blog, onSuccess: @escaping (UIImage) -> (), onFailure: @escaping (Error) -> ()) { + self.url = url + self.blog = blog + self.onSuccess = onSuccess + self.onFailure = onFailure + } + + override func main() { + let mediaRequestAuthenticator = MediaRequestAuthenticator() + let host = MediaHost(with: blog) { error in + // We'll log the error, so we know it's there, but we won't halt execution. + CrashLogging.logError(error) + } + + mediaRequestAuthenticator.authenticatedRequest( + for: url, + from: host, + onComplete: { request in + ImageDownloader.shared.downloadImage(for: request) { (image, error) in + self.state = .isFinished + + DispatchQueue.main.async { + guard let image = image else { + DDLogError("Unable to download image for attachment with url = \(String(describing: request.url)). Details: \(String(describing: error?.localizedDescription))") + if let error = error { + self.onFailure(error) + } else { + self.onFailure(NSError(domain: NSURLErrorDomain, code: NSURLErrorUnknown, userInfo: nil)) + } + + return + } + + self.onSuccess(image) + } + } + }, + onFailure: { error in + self.state = .isFinished + self.onFailure(error) + }) + } +} + class EditorMediaUtility { private struct Constants { @@ -45,58 +96,55 @@ class EditorMediaUtility { } - func downloadImage(from url: URL, post: AbstractPost, success: @escaping (UIImage) -> Void, onFailure failure: @escaping (Error) -> Void) -> ImageDownloader.Task { + func downloadImage( + from url: URL, + post: AbstractPost, + success: @escaping (UIImage) -> Void, + onFailure failure: @escaping (Error) -> Void) -> ImageDownloaderTask { + let imageMaxDimension = max(UIScreen.main.bounds.size.width, UIScreen.main.bounds.size.height) //use height zero to maintain the aspect ratio when fetching let size = CGSize(width: imageMaxDimension, height: 0) let scale = UIScreen.main.scale + return downloadImage(from: url, size: size, scale: scale, post: post, success: success, onFailure: failure) } - func downloadImage(from url: URL, size requestSize: CGSize, scale: CGFloat, post: AbstractPost, success: @escaping (UIImage) -> Void, onFailure failure: @escaping (Error) -> Void) -> ImageDownloader.Task { - var requestURL = url + func downloadImage( + from url: URL, + size requestSize: CGSize, + scale: CGFloat, post: AbstractPost, + success: @escaping (UIImage) -> Void, + onFailure failure: @escaping (Error) -> Void) -> ImageDownloaderTask { + let imageMaxDimension = max(requestSize.width, requestSize.height) //use height zero to maintain the aspect ratio when fetching var size = CGSize(width: imageMaxDimension, height: 0) - let request: URLRequest + let requestURL: URL if url.isFileURL { - request = URLRequest(url: url) - } else if post.blog.isPrivate() && PrivateSiteURLProtocol.urlGoes(toWPComSite: url) { + requestURL = url + } else if post.isPrivateAtWPCom() && url.isHostedAtWPCom { // private wpcom image needs special handling. // the size that WPImageHelper expects is pixel size size.width = size.width * scale - requestURL = WPImageURLHelper.imageURLWithSize(size, forImageURL: requestURL) - request = PrivateSiteURLProtocol.requestForPrivateSite(from: requestURL) + requestURL = WPImageURLHelper.imageURLWithSize(size, forImageURL: url) } else if !post.blog.isHostedAtWPcom && post.blog.isBasicAuthCredentialStored() { size.width = size.width * scale - requestURL = WPImageURLHelper.imageURLWithSize(size, forImageURL: requestURL) - request = URLRequest(url: requestURL) + requestURL = WPImageURLHelper.imageURLWithSize(size, forImageURL: url) } else { // the size that PhotonImageURLHelper expects is points size - requestURL = PhotonImageURLHelper.photonURL(with: size, forImageURL: requestURL) - request = URLRequest(url: requestURL) + requestURL = PhotonImageURLHelper.photonURL(with: size, forImageURL: url) } - return ImageDownloader.shared.downloadImage(for: request) { [weak self] (image, error) in - guard let _ = self else { - return - } - - DispatchQueue.main.async { - guard let image = image else { - DDLogError("Unable to download image for attachment with url = \(url). Details: \(String(describing: error?.localizedDescription))") - if let error = error { - failure(error) - } else { - failure(NSError(domain: NSURLErrorDomain, code: NSURLErrorUnknown, userInfo: nil)) - } - return - } + let imageDownload = AuthenticatedImageDownload( + url: requestURL, + blog: post.blog, + onSuccess: success, + onFailure: failure) - success(image) - } - } + imageDownload.start() + return imageDownload } static func fetchRemoteVideoURL(for media: Media, in post: AbstractPost, completion: @escaping ( Result<(videoURL: URL, posterURL: URL?), Error> ) -> Void) { diff --git a/WordPress/Classes/ViewRelated/Gutenberg/Utils/GutenbergMediaEditorImage.swift b/WordPress/Classes/ViewRelated/Gutenberg/Utils/GutenbergMediaEditorImage.swift index 2f3cc16761ba..97d75e5cbefa 100644 --- a/WordPress/Classes/ViewRelated/Gutenberg/Utils/GutenbergMediaEditorImage.swift +++ b/WordPress/Classes/ViewRelated/Gutenberg/Utils/GutenbergMediaEditorImage.swift @@ -6,7 +6,7 @@ import MediaEditor We need the full high-quality image in the Media Editor. */ class GutenbergMediaEditorImage: AsyncImage { - private var tasks: [URLSessionDataTask] = [] + private var tasks: [ImageDownloaderTask] = [] private var originalURL: URL diff --git a/WordPress/Classes/ViewRelated/Media/StockPhotos/AztecMediaPickingCoordinator.swift b/WordPress/Classes/ViewRelated/Media/StockPhotos/AztecMediaPickingCoordinator.swift index a87f9cc7a246..82e2ca5b47bf 100644 --- a/WordPress/Classes/ViewRelated/Media/StockPhotos/AztecMediaPickingCoordinator.swift +++ b/WordPress/Classes/ViewRelated/Media/StockPhotos/AztecMediaPickingCoordinator.swift @@ -16,7 +16,9 @@ final class AztecMediaPickingCoordinator { let blog = context.blog let fromView = context.view - let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + let alertController = UIAlertController(title: nil, + message: nil, + preferredStyle: UIDevice.isPad() ? .alert : .actionSheet) if blog.supports(.stockPhotos) { alertController.addAction(freePhotoAction(origin: origin, blog: blog)) diff --git a/WordPress/Classes/ViewRelated/People/InvitePersonViewController.swift b/WordPress/Classes/ViewRelated/People/InvitePersonViewController.swift index a9aecfb5ac62..4617e0b5c986 100644 --- a/WordPress/Classes/ViewRelated/People/InvitePersonViewController.swift +++ b/WordPress/Classes/ViewRelated/People/InvitePersonViewController.swift @@ -56,7 +56,7 @@ class InvitePersonViewController: UITableViewController { let blogRoles = blog?.sortedRoles ?? [] var roles = [RemoteRole]() let inviteRole: RemoteRole - if blog.isPrivate() { + if blog.isPrivateAtWPCom() { inviteRole = RemoteRole.viewer } else { inviteRole = RemoteRole.follower diff --git a/WordPress/Classes/ViewRelated/Post/PostCardCell.swift b/WordPress/Classes/ViewRelated/Post/PostCardCell.swift index 42c5ebd9ffa5..80840121ccd3 100644 --- a/WordPress/Classes/ViewRelated/Post/PostCardCell.swift +++ b/WordPress/Classes/ViewRelated/Post/PostCardCell.swift @@ -1,3 +1,4 @@ +import AutomatticTracks import UIKit import Gridicons @@ -201,9 +202,14 @@ class PostCardCell: UITableViewCell, ConfigurablePostView { return } + let host = MediaHost(with: post) { error in + // We'll log the error, so we know it's there, but we won't halt execution. + CrashLogging.logError(error) + } + if currentLoadedFeaturedImage != url.absoluteString { currentLoadedFeaturedImage = url.absoluteString - imageLoader.loadImage(with: url, from: post, preferredSize: preferredSize) + imageLoader.loadImage(with: url, from: host, preferredSize: preferredSize) } } diff --git a/WordPress/Classes/ViewRelated/Post/PostCompactCell.swift b/WordPress/Classes/ViewRelated/Post/PostCompactCell.swift index 3aeda55a9ba7..0855595a3886 100644 --- a/WordPress/Classes/ViewRelated/Post/PostCompactCell.swift +++ b/WordPress/Classes/ViewRelated/Post/PostCompactCell.swift @@ -1,3 +1,4 @@ +import AutomatticTracks import UIKit import Gridicons @@ -100,7 +101,13 @@ class PostCompactCell: UITableViewCell, ConfigurablePostView { private func configureFeaturedImage() { if let post = post, let url = post.featuredImageURL { featuredImageView.isHidden = false - imageLoader.loadImage(with: url, from: post, preferredSize: CGSize(width: featuredImageView.frame.width, height: featuredImageView.frame.height)) + + let host = MediaHost(with: post, failure: { error in + // We'll log the error, so we know it's there, but we won't halt execution. + CrashLogging.logError(error) + }) + + imageLoader.loadImage(with: url, from: host, preferredSize: CGSize(width: featuredImageView.frame.width, height: featuredImageView.frame.height)) } else { featuredImageView.isHidden = true } diff --git a/WordPress/Classes/ViewRelated/Post/PostPreviewGenerator.swift b/WordPress/Classes/ViewRelated/Post/PostPreviewGenerator.swift index c1d1241b00ec..47385b2a2247 100644 --- a/WordPress/Classes/ViewRelated/Post/PostPreviewGenerator.swift +++ b/WordPress/Classes/ViewRelated/Post/PostPreviewGenerator.swift @@ -87,7 +87,7 @@ private extension PostPreviewGenerator { case .draft, .publishPrivate, .pending, .scheduled, .publish: return true default: - return post.blog.isPrivate() + return post.isPrivateAtWPCom() } } diff --git a/WordPress/Classes/ViewRelated/Post/PrivateSiteURLProtocol.h b/WordPress/Classes/ViewRelated/Post/PrivateSiteURLProtocol.h deleted file mode 100644 index 1a29eb914c94..000000000000 --- a/WordPress/Classes/ViewRelated/Post/PrivateSiteURLProtocol.h +++ /dev/null @@ -1,27 +0,0 @@ -#import - -@interface PrivateSiteURLProtocol : NSURLProtocol - -/** - (Un)RegisterPrivateSiteURLProtocol are convenience methods for registering and - unregistering the protocol safely. - - For performance reasons we do not want to register the protocol for the - lifecycle of the app -- potentially `canInitWithRequest` would be called for every - http request. Register the protocol for use when its needed and unregister it when - its not. - - Use registerPrivateSiteURLProtocol and unregisterPrivateSiteURLProtocol to - keep track of the number of users of the protocol. The call to - `NSURLProtcol unregisterClass:` is only made when there are no longer any uses - remaining. This will help avoid edgecases where the protcol could be potentially - unregistered by one user immediately after another user had registered it. - */ -+ (void)registerPrivateSiteURLProtocol; -+ (void)unregisterPrivateSiteURLProtocol; - -+ (nonnull NSURLRequest *)requestForPrivateSiteFromURL:(nonnull NSURL *)url; - -+ (BOOL)urlGoesToWPComSite:(nonnull NSURL *)url; - -@end diff --git a/WordPress/Classes/ViewRelated/Post/PrivateSiteURLProtocol.m b/WordPress/Classes/ViewRelated/Post/PrivateSiteURLProtocol.m deleted file mode 100644 index 236a8e9322a9..000000000000 --- a/WordPress/Classes/ViewRelated/Post/PrivateSiteURLProtocol.m +++ /dev/null @@ -1,256 +0,0 @@ -#import "PrivateSiteURLProtocol.h" -#import "AccountService.h" -#import "ContextManager.h" -#import "WPAccount.h" -#import "Blog.h" - -@interface PrivateSiteURLProtocolSession: NSObject - -+ (instancetype) sharedInstance; -- (NSURLSessionTask *)createSessionTaskForRequest:(NSURLRequest *)request forProtocol:(NSURLProtocol *)protocol; -- (void)stopSessionTask:(NSURLSessionTask *)sessionTask; - -@end - -@interface PrivateSiteURLProtocol() - -@property (nonatomic, strong) NSURLSessionTask *sessionTask; - -@end - -static NSInteger regcount = 0; -static NSString const * mutex = @"PrivateSiteURLProtocol-Mutex"; -static NSString *cachedToken; - -@implementation PrivateSiteURLProtocol - -+ (void)registerPrivateSiteURLProtocol -{ - @synchronized(mutex) { - if (regcount == 0) { - if (![NSURLProtocol registerClass:[self class]]) { - NSAssert(YES, @"Unable to register protocol"); - DDLogInfo(@"Unable to register protocol"); - } - } - regcount++; - } -} - -+ (void)unregisterPrivateSiteURLProtocol -{ - @synchronized(mutex) { - cachedToken = nil; - if (regcount > 0) { - regcount--; - if (regcount == 0) { - [NSURLProtocol unregisterClass:[self class]]; - } - } else { - DDLogInfo(@"Detected unbalanced register/unregister private site protocol."); - } - } -} - -+ (BOOL)canInitWithRequest:(NSURLRequest *)request -{ - NSString *authHeader = [request.allHTTPHeaderFields stringForKey:@"Authorization"]; - if (authHeader && [authHeader rangeOfString:@"Bearer"].location != NSNotFound){ - return NO; - } - if (![self requestGoesToWPComSite:request]){ - return NO; - } - if (![self bearerToken]) { - return NO; - } - return YES; -} - -+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request -{ - return request; -} - -+ (NSString *)bearerToken -{ - if (cachedToken) { - return cachedToken; - } - // Thread Safety: Make sure we're running on the Main Thread - if ([NSThread isMainThread]) { - NSManagedObjectContext *context = [[ContextManager sharedInstance] mainContext]; - AccountService *service = [[AccountService alloc] initWithManagedObjectContext:context]; - return service.defaultWordPressComAccount.authToken; - } - - // Otherwise, let's use a Derived Context - __block NSString *authToken = nil; - NSManagedObjectContext *derived = [[ContextManager sharedInstance] newDerivedContext]; - AccountService *service = [[AccountService alloc] initWithManagedObjectContext:derived]; - - [derived performBlockAndWait:^{ - authToken = service.defaultWordPressComAccount.authToken; - }]; - cachedToken = authToken; - return cachedToken; -} - -+ (BOOL)requestGoesToWPComSite:(NSURLRequest *)request -{ - return [self urlGoesToWPComSite:request.URL]; -} - -+ (BOOL)urlGoesToWPComSite:(NSURL *)url -{ - if ([url.scheme isEqualToString:@"https"] && [url.host hasSuffix:@".wordpress.com"]) { - return YES; - } - - return NO; -} - -+ (NSURLRequest *)requestForPrivateSiteFromURL:(NSURL *)url -{ - if (![self urlGoesToWPComSite:url]) { - return [NSURLRequest requestWithURL:url]; - } - NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:YES]; - //make sure the scheme used is https - [urlComponents setScheme:@"https"]; - NSURL *httpsURL = [urlComponents URL]; - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:httpsURL]; - NSString *bearerToken = [NSString stringWithFormat:@"Bearer %@", [self bearerToken]]; - [request addValue:bearerToken forHTTPHeaderField:@"Authorization"]; - return request; -} - -- (void)startLoading -{ - NSMutableURLRequest *mRequest = [self.request mutableCopy]; - [mRequest addValue:[NSString stringWithFormat:@"Bearer %@", [[self class] bearerToken]] forHTTPHeaderField:@"Authorization"]; - self.sessionTask = [[PrivateSiteURLProtocolSession sharedInstance] createSessionTaskForRequest:mRequest forProtocol:self]; - -} - -- (void)stopLoading -{ - [[PrivateSiteURLProtocolSession sharedInstance] stopSessionTask:self.sessionTask]; - self.sessionTask = nil; -} - -@end - -@interface PrivateSiteURLProtocolSession() - -@property (nonatomic, strong) NSMutableDictionary *taskToProtocolMapping; -@property (nonatomic, strong) NSURLSession *session; - -@end - -@implementation PrivateSiteURLProtocolSession - -+ (instancetype) sharedInstance -{ - static id _sharedInstance = nil; - static dispatch_once_t _onceToken; - dispatch_once(&_onceToken, ^{ - _sharedInstance = [[self alloc] init]; - }); - - return _sharedInstance; -} - -- (instancetype)init -{ - self = [super init]; - if (self) { - _taskToProtocolMapping = [NSMutableDictionary dictionary]; - } - return self; -} - -- (NSURLSession *)session -{ - if (_session == nil) { - NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; - _session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil]; - } - return _session; -} - -- (NSURLSessionTask *)createSessionTaskForRequest:(NSURLRequest *)request forProtocol:(NSURLProtocol *)protocol -{ - NSURLSessionTask *sessionTask = [self.session dataTaskWithRequest:request]; - [sessionTask resume]; - - self.taskToProtocolMapping[sessionTask] = protocol; - return sessionTask; -} - -- (void)stopSessionTask:(NSURLSessionTask *)sessionTask -{ - if (sessionTask == nil) { - return; - } - [self.taskToProtocolMapping removeObjectForKey:sessionTask]; - - [sessionTask cancel]; -} - -- (void)URLSession:(NSURLSession *)session - dataTask:(NSURLSessionDataTask *)dataTask - didReceiveData:(NSData *)data -{ - NSURLProtocol *protocol = self.taskToProtocolMapping[dataTask]; - id client = protocol.client; - - [client URLProtocol:protocol didLoadData:data]; -} - -- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error -{ - NSURLProtocol *protocol = self.taskToProtocolMapping[task]; - id client = protocol.client; - - if (error) { - [client URLProtocol:protocol didFailWithError:error]; - } else { - [client URLProtocolDidFinishLoading:protocol]; - } - [self.taskToProtocolMapping removeObjectForKey:task]; -} - -- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error -{ - if (session == _session) { - _session = nil; - } -} - -- (void)URLSession:(NSURLSession *)session - dataTask:(NSURLSessionDataTask *)dataTask -didReceiveResponse:(NSURLResponse *)response - completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler -{ - NSURLProtocol *protocol = self.taskToProtocolMapping[dataTask]; - id client = protocol.client; - - [client URLProtocol:protocol didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed]; - completionHandler(NSURLSessionResponseAllow); -} - -- (void)URLSession:(NSURLSession *)session - task:(NSURLSessionTask *)task -willPerformHTTPRedirection:(NSHTTPURLResponse *)response - newRequest:(NSURLRequest *)request - completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler -{ - NSURLProtocol *protocol = self.taskToProtocolMapping[task]; - id client = protocol.client; - - [client URLProtocol:protocol wasRedirectedToRequest:request redirectResponse:response]; - completionHandler(nil); -} - -@end diff --git a/WordPress/Classes/ViewRelated/Post/Revisions/Browser/Preview/RevisionPreviewTextViewManager.swift b/WordPress/Classes/ViewRelated/Post/Revisions/Browser/Preview/RevisionPreviewTextViewManager.swift index 041113b144c9..9b53f309e66f 100644 --- a/WordPress/Classes/ViewRelated/Post/Revisions/Browser/Preview/RevisionPreviewTextViewManager.swift +++ b/WordPress/Classes/ViewRelated/Post/Revisions/Browser/Preview/RevisionPreviewTextViewManager.swift @@ -6,7 +6,7 @@ class RevisionPreviewTextViewManager: NSObject { var post: AbstractPost? private let mediaUtility = EditorMediaUtility() - private var activeMediaRequests = [ImageDownloader.Task]() + private var activeMediaRequests = [ImageDownloaderTask]() private enum Constants { static let mediaPlaceholderImageSize = CGSize(width: 128, height: 128) diff --git a/WordPress/Classes/ViewRelated/Reader/ReaderCommentCell.swift b/WordPress/Classes/ViewRelated/Reader/ReaderCommentCell.swift index 8087dda6d1e3..f6088d52da86 100644 --- a/WordPress/Classes/ViewRelated/Reader/ReaderCommentCell.swift +++ b/WordPress/Classes/ViewRelated/Reader/ReaderCommentCell.swift @@ -1,3 +1,4 @@ +import AutomatticTracks import UIKit import WordPressShared import Gridicons @@ -205,7 +206,11 @@ class ReaderCommentCell: UITableViewCell { return } - textView.isPrivate = comment.isPrivateContent() + textView.mediaHost = MediaHost(with: comment.blog, failure: { error in + // We'll log the error, so we know it's there, but we won't halt execution. + CrashLogging.logError(error) + }) + // Use `content` vs `contentForDisplay`. Hierarchcial comments are already // correctly formatted during the sync process. textView.attributedText = attributedString diff --git a/WordPress/Classes/ViewRelated/Reader/ReaderCrossPostCell.swift b/WordPress/Classes/ViewRelated/Reader/ReaderCrossPostCell.swift index 9d268b7ac65f..deb3e25840b5 100644 --- a/WordPress/Classes/ViewRelated/Reader/ReaderCrossPostCell.swift +++ b/WordPress/Classes/ViewRelated/Reader/ReaderCrossPostCell.swift @@ -1,4 +1,6 @@ +import AlamofireImage import Foundation +import AutomatticTracks import WordPressShared.WPStyleGuide open class ReaderCrossPostCell: UITableViewCell { @@ -90,11 +92,23 @@ open class ReaderCrossPostCell: UITableViewCell { let placeholder = UIImage(named: blavatarPlaceholder) let size = blavatarImageView.frame.size.width * UIScreen.main.scale - let url = contentProvider?.siteIconForDisplay(ofSize: Int(size)) - if url != nil { - blavatarImageView.downloadImage(from: url, placeholderImage: placeholder) - } else { - blavatarImageView.image = placeholder + + guard let contentProvider = contentProvider, + let url = contentProvider.siteIconForDisplay(ofSize: Int(size)) else { + blavatarImageView.image = placeholder + return + } + + let host = MediaHost(with: contentProvider) { error in + CrashLogging.logError(error) + } + + let mediaAuthenticator = MediaRequestAuthenticator() + mediaAuthenticator.authenticatedRequest(for: url, from: host, onComplete: { [weak self] request in + self?.blavatarImageView.af_setImage(withURLRequest: request, placeholderImage: placeholder) + }) { [weak self] error in + CrashLogging.logError(error) + self?.blavatarImageView.image = placeholder } } diff --git a/WordPress/Classes/ViewRelated/Reader/ReaderDetailViewController.swift b/WordPress/Classes/ViewRelated/Reader/ReaderDetailViewController.swift index f642b4aa00ec..08c1f4a06c9d 100644 --- a/WordPress/Classes/ViewRelated/Reader/ReaderDetailViewController.swift +++ b/WordPress/Classes/ViewRelated/Reader/ReaderDetailViewController.swift @@ -1,3 +1,4 @@ +import AutomatticTracks import Foundation import CocoaLumberjack import WordPressShared @@ -680,10 +681,14 @@ open class ReaderDetailViewController: UIViewController, UIViewControllerRestora return } - let postInfo = ReaderCardContent(provider: post) + let host = MediaHost(with: post, failure: { error in + // We'll log the error, so we know it's there, but we won't halt execution. + CrashLogging.logError(error) + }) + let maxImageWidth = min(view.frame.width, view.frame.height) let imageWidthSize = CGSize(width: maxImageWidth, height: 0) // height 0: preserves aspect ratio. - featuredImageLoader.loadImage(with: featuredImageURL, from: postInfo, preferredSize: imageWidthSize, placeholder: nil, success: { [weak self] in + featuredImageLoader.loadImage(with: featuredImageURL, from: host, preferredSize: imageWidthSize, placeholder: nil, success: { [weak self] in guard let strongSelf = self, let size = strongSelf.featuredImageView.image?.size else { return } @@ -806,7 +811,10 @@ open class ReaderDetailViewController: UIViewController, UIViewControllerRestora return } - textView.isPrivate = post.isPrivate() + textView.mediaHost = MediaHost(with: post, failure: { error in + // We'll log the error, so we know it's there, but we won't halt execution. + CrashLogging.logError(error) + }) textView.content = post.contentForDisplay() updateRichText() diff --git a/WordPress/Classes/ViewRelated/Reader/ReaderPostCardCell.swift b/WordPress/Classes/ViewRelated/Reader/ReaderPostCardCell.swift index 840572ad37f5..11dcc36c96d0 100644 --- a/WordPress/Classes/ViewRelated/Reader/ReaderPostCardCell.swift +++ b/WordPress/Classes/ViewRelated/Reader/ReaderPostCardCell.swift @@ -1,8 +1,8 @@ +import AutomatticTracks import Foundation import WordPressShared import Gridicons - @objc public protocol ReaderPostCellDelegate: NSObjectProtocol { func readerCell(_ cell: ReaderPostCardCell, headerActionForProvider provider: ReaderPostContentProvider) func readerCell(_ cell: ReaderPostCardCell, commentActionForProvider provider: ReaderPostContentProvider) @@ -286,7 +286,7 @@ import Gridicons } fileprivate func configureHeader() { - guard let provider = contentProvider else { + guard let contentProvider = contentProvider else { return } @@ -294,24 +294,34 @@ import Gridicons avatarImageView.image = nil let size = avatarImageView.frame.size.width * UIScreen.main.scale - if let url = provider.siteIconForDisplay(ofSize: Int(size)) { - if provider.isPrivate() { - let request = PrivateSiteURLProtocol.requestForPrivateSite(from: url) - avatarImageView.downloadImage(usingRequest: request) - } else { - avatarImageView.downloadImage(from: url) - } - avatarImageView.isHidden = false - + if let url = contentProvider.siteIconForDisplay(ofSize: Int(size)) { + + let mediaRequestAuthenticator = MediaRequestAuthenticator() + let host = MediaHost(with: contentProvider, failure: { error in + // We'll log the error, so we know it's there, but we won't halt execution. + CrashLogging.logError(error) + }) + + mediaRequestAuthenticator.authenticatedRequest( + for: url, + from: host, + onComplete: { request in + self.avatarImageView.downloadImage(usingRequest: request) + self.avatarImageView.isHidden = false + }, + onFailure: { error in + CrashLogging.logError(error) + self.avatarImageView.isHidden = true + }) } else { avatarImageView.isHidden = true } var arr = [String]() - if let authorName = provider.authorForDisplay() { + if let authorName = contentProvider.authorForDisplay() { arr.append(authorName) } - if let blogName = provider.blogNameForDisplay() { + if let blogName = contentProvider.blogNameForDisplay() { arr.append(blogName) } blogNameLabel.text = arr.joined(separator: ", ") @@ -346,7 +356,7 @@ import Gridicons } fileprivate func configureFeaturedImage(_ featuredImageURL: URL) { - guard let content = contentProvider else { + guard let contentProvider = contentProvider else { return } @@ -354,8 +364,11 @@ import Gridicons currentLoadedCardImageURL = featuredImageURL.absoluteString featuredImageDesiredWidth = featuredImageView.frame.width let size = CGSize(width: featuredImageDesiredWidth, height: featuredMediaHeightConstraintConstant) - let postInfo = ReaderCardContent(provider: content) - imageLoader.loadImage(with: featuredImageURL, from: postInfo, preferredSize: size) + let host = MediaHost(with: contentProvider, failure: { error in + // We'll log the error, so we know it's there, but we won't halt execution. + CrashLogging.logError(error) + }) + imageLoader.loadImage(with: featuredImageURL, from: host, preferredSize: size) } fileprivate func configureTitle() { diff --git a/WordPress/Classes/ViewRelated/System/Action Sheet/DrawerPresentationController.swift b/WordPress/Classes/ViewRelated/System/Action Sheet/DrawerPresentationController.swift new file mode 100644 index 000000000000..256dbe75274b --- /dev/null +++ b/WordPress/Classes/ViewRelated/System/Action Sheet/DrawerPresentationController.swift @@ -0,0 +1,494 @@ +import UIKit + +public enum DrawerPosition { + case expanded + case collapsed + case closed +} + +public enum DrawerHeight { + // The maximum height for the screen + case maxHeight + + // Height is based on the specified margin from the top of the screen + case topMargin(CGFloat) + + // Height will be equal to the the content height value + case contentHeight(CGFloat) +} + +public enum DrawerWidth { + // Fills the whole screen width + case maxWidth + + // When in compact mode, fills a percentage of the screen + case percentage(CGFloat) + + // Width will be equal to the the content height value + case contentWidth(CGFloat) +} + +public protocol DrawerPresentable: AnyObject { + /// The height of the drawer when it's in the expanded position + var expandedHeight: DrawerHeight { get } + + /// The height of the drawer when it's in the collapsed position + var collapsedHeight: DrawerHeight { get } + + /// The width of the Drawer in compact screen + var compactWidth: DrawerWidth { get } + + /// Whether or not the user is allowed to swipe to switch between the expanded and collapsed position + var allowsUserTransition: Bool { get } + + /// Whether or not the user is allowed to drag to dismiss the drawer + var allowsDragToDismiss: Bool { get } + + /// Whether or not the user is allowed to tap outside the view to dismiss the drawer + var allowsTapToDismiss: Bool { get } + + /// A scroll view that should have its insets adjusted when the drawer is expanded/collapsed + var scrollableView: UIScrollView? { get } +} + +private enum Constants { + static let transitionDuration: TimeInterval = 0.5 + + static let flickVelocity: CGFloat = 300 + static let bounceAmount: CGFloat = 0.01 + + enum Defaults { + static let expandedHeight: DrawerHeight = .topMargin(20) + static let collapsedHeight: DrawerHeight = .contentHeight(0) + static let compactWidth: DrawerWidth = .percentage(0.66) + + static let allowsUserTransition: Bool = true + static let allowsTapToDismiss: Bool = true + static let allowsDragToDismiss: Bool = true + } +} + +typealias DrawerPresentableViewController = DrawerPresentable & UIViewController + +public extension DrawerPresentable where Self: UIViewController { + // Default values + var allowsUserTransition: Bool { + return Constants.Defaults.allowsUserTransition + } + + var expandedHeight: DrawerHeight { + return Constants.Defaults.expandedHeight + } + + var collapsedHeight: DrawerHeight { + return Constants.Defaults.collapsedHeight + } + + var compactWidth: DrawerWidth { + return Constants.Defaults.compactWidth + } + + var scrollableView: UIScrollView? { + return nil + } + + var allowsDragToDismiss: Bool { + return Constants.Defaults.allowsDragToDismiss + } + + var allowsTapToDismiss: Bool { + return Constants.Defaults.allowsTapToDismiss + } + + // Helpers + + /// Try to determine the correct DrawerPresentationController to use + + /// Returns the `DrawerPresentationController` for a view controller if there is one + /// This tries to determine the correct one to use in the following order: + /// - The view controller + /// - The navController + /// - The navController parentViewController + /// - The views parentViewController + var presentedVC: DrawerPresentationController? { + let presentationController = self.presentationController as? DrawerPresentationController + let navigationPresentationController = navigationController?.presentationController as? DrawerPresentationController + let navParentPresetationController = navigationController?.parent?.presentationController as? DrawerPresentationController + let parentPresentationController = parent?.presentationController as? DrawerPresentationController + + return presentationController ?? navigationPresentationController ?? navParentPresetationController ?? parentPresentationController + } +} + +public class DrawerPresentationController: FancyAlertPresentationController { + override public var frameOfPresentedViewInContainerView: CGRect { + guard let containerView = self.containerView else { + return .zero + } + + var frame = containerView.frame + let y = collapsedYPosition + var width: CGFloat = containerView.bounds.width - (containerView.safeAreaInsets.left + containerView.safeAreaInsets.right) + + frame.origin.y = y + + /// If we're in a compact vertical size class, constrain the width a bit more so it doesn't get overly wide. + if let widthForCompactSizeClass = presentableViewController?.compactWidth, + traitCollection.verticalSizeClass == .compact { + + switch widthForCompactSizeClass { + case .percentage(let percentage): + width = width * percentage + case .contentWidth(let givenWidth): + width = givenWidth + case .maxWidth: + break + } + } + frame.size.width = width + + /// If we constrain the width, this centers the view by applying the appropriate insets based on width + frame.origin.x = ((containerView.bounds.width - width) / 2) + + return frame + } + + override public func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + coordinator.animate(alongsideTransition: { _ in + self.presentedView?.frame = self.frameOfPresentedViewInContainerView + self.transition(to: self.currentPosition) + }, completion: nil) + super.viewWillTransition(to: size, with: coordinator) + } + + /// Returns the current position of the drawer + public var currentPosition: DrawerPosition = .collapsed + + + /// Animates between the drawer positions + /// - Parameter position: The position to animate to + public func transition(to position: DrawerPosition) { + currentPosition = position + + if position == .closed { + dismiss() + return + } + + var margin: CGFloat = 0 + + switch position { + case .expanded: + margin = expandedYPosition + + case .collapsed: + margin = collapsedYPosition + + default: + margin = 0 + } + + setTopMargin(margin) + } + + @objc func dismiss() { + presentedViewController.dismiss(animated: true, completion: nil) + } + + public override func presentationTransitionWillBegin() { + super.presentationTransitionWillBegin() + + configureScrollViewInsets() + } + + public override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + transition(to: currentPosition) + } + + public override func presentationTransitionDidEnd(_ completed: Bool) { + super.presentationTransitionDidEnd(completed) + + configureScrollViewInsets() + } + + + // MARK: - Internal Positions + // Helpers to calculate the Y positions for the drawer positions + + private var closedPosition: CGFloat { + guard let presentedView = self.presentedView else { + return 0 + } + + return presentedView.bounds.height + } + + private var collapsedYPosition: CGFloat { + let height = presentableViewController?.collapsedHeight ?? Constants.Defaults.collapsedHeight + + return topMargin(with: height) + } + + private var expandedYPosition: CGFloat { + let height = presentableViewController?.expandedHeight ?? Constants.Defaults.expandedHeight + + return topMargin(with: height) + } + + /// Calculates the Y position for the view based on a DrawerHeight enum + /// - Parameter drawerHeight: The drawer height to calculate + private func topMargin(with drawerHeight: DrawerHeight) -> CGFloat { + var topMargin: CGFloat + + switch drawerHeight { + case .contentHeight(let height): + topMargin = calculatedTopMargin(for: height) + + case .topMargin(let margin): + topMargin = safeAreaInsets.top + margin + + case .maxHeight: + topMargin = safeAreaInsets.top + } + + return topMargin + } + + // MARK: - Gestures + private lazy var tapGestureRecognizer: UITapGestureRecognizer = { + let gesture = UITapGestureRecognizer(target: self, action: #selector(self.dismiss(_:))) + gesture.delegate = self + return gesture + }() + + private lazy var panGestureRecognizer: UIPanGestureRecognizer = { + return UIPanGestureRecognizer(target: self, action: #selector(self.pan(_:))) + }() + + override public func containerViewWillLayoutSubviews() { + super.containerViewWillLayoutSubviews() + + addGestures() + } + + private var dragStartPoint: CGPoint? +} + +// MARK: - Dragging +private extension DrawerPresentationController { + private func addGestures() { + guard + let presentedView = self.presentedView, + let containerView = self.containerView + else { return } + + presentedView.addGestureRecognizer(panGestureRecognizer) + containerView.addGestureRecognizer(tapGestureRecognizer) + } + + /// Dismiss action for the tap gesture + /// Will prevent dismissal if the `allowsTapToDismiss` is false + /// - Parameter gesture: The tap gesture + @objc func dismiss(_ gesture: UIPanGestureRecognizer) { + let canDismiss = presentableViewController?.allowsTapToDismiss ?? Constants.Defaults.allowsTapToDismiss + + guard canDismiss else { + return + } + + dismiss() + } + + @objc func pan(_ gesture: UIPanGestureRecognizer) { + guard let presentedView = self.presentedView else { return } + + let translation = gesture.translation(in: presentedView) + let allowsUserTransition = presentableViewController?.allowsUserTransition ?? Constants.Defaults.allowsUserTransition + let allowDragToDismiss = presentableViewController?.allowsDragToDismiss ?? Constants.Defaults.allowsDragToDismiss + + switch gesture.state { + case .began: + dragStartPoint = presentedView.frame.origin + + case .changed: + let startY = dragStartPoint?.y ?? 0 + var yTranslation = translation.y + + if !allowsUserTransition || !allowDragToDismiss { + let maxBounce: CGFloat = (startY * Constants.bounceAmount) + + if yTranslation < 0 { + yTranslation = max(yTranslation, maxBounce * -1) + } else { + if !allowDragToDismiss { + yTranslation = min(yTranslation, maxBounce) + } + } + } + + let maxY = topMargin(with: .maxHeight) + + setTopMargin(max((startY + yTranslation), maxY), animated: false) + + case .ended: + /// Helper closure to prevent user transition/dismiss + let transition: (DrawerPosition) -> () = { pos in + if allowsUserTransition || pos == .closed && allowDragToDismiss { + self.transition(to: pos) + } else { + //Reset to the original position + self.transition(to: self.currentPosition) + } + } + + let velocity = gesture.velocity(in: presentedView).y + let startY = dragStartPoint?.y ?? 0 + + let currentPosition = (startY + translation.y) + let position = closestPosition(for: currentPosition) + + // Determine how to handle flicking of the view + if (abs(velocity) - Constants.flickVelocity) > 0 { + //Flick up + if velocity < 0 { + transition(.expanded) + } + else { + if position == .expanded { + transition(.collapsed) + } else { + transition(.closed) + } + } + + return + } + + transition(position) + + dragStartPoint = nil + + default: + return + } + } +} + +private extension UIScrollView { + /// A flag to determine if a scroll view is scrolling + var isScrolling: Bool { + return isDragging && !isDecelerating || isTracking + } +} + +extension DrawerPresentationController: UIGestureRecognizerDelegate { + public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { + /// Shouldn't happen; should always have container & presented view when tapped + guard + let containerView = containerView, + let presentedView = presentedView + else { + return false + } + + let touchPoint = touch.location(in: containerView) + let isInPresentedView = presentedView.frame.contains(touchPoint) + + /// Do not accept the touch if inside of the presented view + return (gestureRecognizer == tapGestureRecognizer) && isInPresentedView == false + } +} + +// MARK: - Private: Helpers +private extension DrawerPresentationController { + + private func configureScrollViewInsets() { + guard + let scrollView = presentableViewController?.scrollableView, + !scrollView.isScrolling, + let presentedView = self.presentedView + else { return } + + + let bottom = presentingViewController.view.safeAreaLayoutGuide.layoutFrame.origin.y + let margin = presentedView.frame.origin.y + bottom + + scrollView.contentInset.bottom = margin + } + + private var presentableViewController: DrawerPresentable? { + return presentedViewController as? DrawerPresentable + } + + private func calculatedTopMargin(for height: CGFloat) -> CGFloat { + guard let containerView = self.containerView else { + return 0 + } + + let bounds = containerView.bounds + let margin = bounds.maxY - (safeAreaInsets.bottom + ((height > 0) ? height : (bounds.height * 0.5))) + + // Limit the max height + return max(margin, safeAreaInsets.top) + } + + private func setTopMargin(_ margin: CGFloat, animated: Bool = true) { + guard let presentedView = self.presentedView else { + return + } + + var frame = presentedView.frame + frame.origin.y = margin + + let animations = { + presentedView.frame = frame + + self.configureScrollViewInsets() + } + + if animated { + animate(animations) + } else { + animations() + } + } + + private var safeAreaInsets: UIEdgeInsets { + guard let rootViewController = self.rootViewController else { + return .zero + } + + return rootViewController.view.safeAreaInsets + } + + func closestPosition(for yPosition: CGFloat) -> DrawerPosition { + let positions = [closedPosition, collapsedYPosition, expandedYPosition] + let closestVal = positions.min(by: { abs(yPosition - $0) < abs(yPosition - $1) }) ?? yPosition + + var returnPosition: DrawerPosition = .closed + + if closestVal == expandedYPosition { + returnPosition = .expanded + } else if closestVal == collapsedYPosition { + returnPosition = .collapsed + } + + return returnPosition + } + + private func animate(_ animations: @escaping () -> Void) { + UIView.animate(withDuration: Constants.transitionDuration, + delay: 0, + usingSpringWithDamping: 0.8, + initialSpringVelocity: 0, + options: .curveEaseInOut, + animations: animations) + } + + private var rootViewController: UIViewController? { + let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first + + return keyWindow?.rootViewController + } +} diff --git a/WordPress/Classes/ViewRelated/Views/WPRichText/WPRichContentView.swift b/WordPress/Classes/ViewRelated/Views/WPRichText/WPRichContentView.swift index e4d945c34067..bb63e5ebead9 100644 --- a/WordPress/Classes/ViewRelated/Views/WPRichText/WPRichContentView.swift +++ b/WordPress/Classes/ViewRelated/Views/WPRichText/WPRichContentView.swift @@ -53,10 +53,7 @@ class WPRichContentView: UITextView { } } - - /// Whether the view shows private content. Used when fetching images. - /// - @objc var isPrivate = false + var mediaHost: MediaHost = .publicSite @objc var content: String { get { @@ -342,12 +339,11 @@ extension WPRichContentView: WPTextAttachmentManagerDelegate { attachment.maxSize = CGSize(width: finalSize.width, height: finalSize.height) } - let contentInformation = ContentInformation(isPrivateOnWPCom: isPrivate, isSelfHostedWithCredentials: false) let index = mediaArray.count let indexPath = IndexPath(row: index, section: 1) weak var weakImage = image - image.loadImage(from: contentInformation, preferedSize: finalSize, indexPath: indexPath, onSuccess: { [weak self] indexPath in + image.loadImage(from: mediaHost, preferedSize: finalSize, indexPath: indexPath, onSuccess: { [weak self] indexPath in guard let richMedia = self?.mediaArray[indexPath.row], let img = weakImage @@ -470,18 +466,6 @@ struct RichMedia { let attachment: WPTextAttachment } -// MARK: - ContentInformation (ImageSourceInformation) - -class ContentInformation: ImageSourceInformation { - var isPrivateOnWPCom: Bool - var isSelfHostedWithCredentials: Bool - - init(isPrivateOnWPCom: Bool, isSelfHostedWithCredentials: Bool) { - self.isPrivateOnWPCom = isPrivateOnWPCom - self.isSelfHostedWithCredentials = isSelfHostedWithCredentials - } -} - // This is very much based on Aztec.LayoutManager — most of this code is pretty much copy-pasted // from there and trimmed to only contain the relevant parts. @objc fileprivate class BlockquoteBackgroundLayoutManager: NSLayoutManager { diff --git a/WordPress/Classes/ViewRelated/Views/WPRichText/WPRichTextImage.swift b/WordPress/Classes/ViewRelated/Views/WPRichText/WPRichTextImage.swift index e34cd6ae92fb..07683433c471 100644 --- a/WordPress/Classes/ViewRelated/Views/WPRichText/WPRichTextImage.swift +++ b/WordPress/Classes/ViewRelated/Views/WPRichText/WPRichTextImage.swift @@ -71,12 +71,12 @@ open class WPRichTextImage: UIControl, WPRichTextMediaAttachment { /// Load an image with the already-set contentURL property. Supports animated images (gifs) as well. /// /// - Parameters: - /// - contentInformation: The corresponding ImageSourceInformation for the contentURL + /// - host: The host for the media. /// - preferedSize: The prefered size of the image to load. /// - indexPath: The IndexPath where this view is located — returned as a param in success and error blocks. /// - onSuccess: A closure to be called if the image was loaded successfully. /// - onError: A closure to be called if there was an error loading the image. - func loadImage(from contentInformation: ImageSourceInformation, + func loadImage(from host: MediaHost, preferedSize size: CGSize = .zero, indexPath: IndexPath, onSuccess: ((IndexPath) -> Void)?, @@ -94,7 +94,7 @@ open class WPRichTextImage: UIControl, WPRichTextMediaAttachment { onError?(indexPath, error) } - imageLoader.loadImage(with: contentURL, from: contentInformation, preferredSize: size, placeholder: nil, success: successHandler, error: errorHandler) + imageLoader.loadImage(with: contentURL, from: host, preferredSize: size, placeholder: nil, success: successHandler, error: errorHandler) } func contentSize() -> CGSize { diff --git a/WordPress/Classes/WordPress.xcdatamodeld/.xccurrentversion b/WordPress/Classes/WordPress.xcdatamodeld/.xccurrentversion index 8f55dabda40d..b2610f0a8915 100644 --- a/WordPress/Classes/WordPress.xcdatamodeld/.xccurrentversion +++ b/WordPress/Classes/WordPress.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - WordPress 94.xcdatamodel + WordPress 95.xcdatamodel diff --git a/WordPress/Classes/WordPress.xcdatamodeld/WordPress 95.xcdatamodel/contents b/WordPress/Classes/WordPress.xcdatamodeld/WordPress 95.xcdatamodel/contents new file mode 100644 index 000000000000..fbf79e66e09f --- /dev/null +++ b/WordPress/Classes/WordPress.xcdatamodeld/WordPress 95.xcdatamodel/contents @@ -0,0 +1,844 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/WordPress/Resources/AppStoreStrings.po b/WordPress/Resources/AppStoreStrings.po index 62b6142e298f..5c073702a5ed 100644 --- a/WordPress/Resources/AppStoreStrings.po +++ b/WordPress/Resources/AppStoreStrings.po @@ -38,23 +38,19 @@ msgctxt "app_store_keywords" msgid "social,network,notes,jetpack,photos,writing,geotagging,media,blog,wordpress,website,blogging,design\n" msgstr "" -msgctxt "v14.5-whats-new" +msgctxt "v14.6-whats-new" msgid "" -"New blocks! Welcome the Latest Posts block, for highlighting your new stuff in posts and pages.\n" +"Site Creation has been streamlined by removing some intermediate steps. Pick the kind of site you want, enter a domain name, and you’re done.\n" "\n" -"When you create a new page, we’ll show you a few templates — pick one, and we’ll add all the necessary blocks to your page to get you started quickly.\n" +"The Block editor got more improvements: The Cover block is now available; there are alignment options in the Heading block; and a dropdown toolbar makes alignment easier for Heading, Paragraph, Image, MediaText blocks. And we removed the dimming effect on unselected blocks.\n" "\n" -"We fixed up a few block editor issues; no more crashes when pasting in HTML content or missing Quote block borders in Dark Mode.\n" +"The layout of the Notifications screen caused text to get lost under the casing of some models of mobile phone. That’s fixed!\n" "\n" -"If you scheduled a post to publish in the past, it would display as “scheduled” instead of “published.” That’s fixed, along with an issue where dates displayed incorrectly if your site’s timezone wasn’t the same as your device’s timezone.\n" +"Sometimes you’d try to @-mention someone in a comment, and the app would suggest names but then not let you select one — that’s also fixed. We also took care of a few other issues that caused the app to crash when you tried to access the Site Pages screen or use the Quick Action buttons on iPads to get to your blog posts. And speaking of pages and posts, previews are now bigger for iPads running iOS 13.\n" "\n" -"We fixed some UI inconsistencies in Dark Mode and with the Reader toolbar.\n" +"We like our Dark Mode like we like our coffee: really dark, and without any bugs floating around in it. There were some glitches in Light and Dark Modes, and things sometimes behaved oddly when you switched from one to the other. There are far fewer of those glitches now.\n" "\n" -"You can use @-mentions when writing comments in fullscreen mode.\n" -"\n" -"Sometimes Signup and Login Magic Link emails would go to your spam folder, so we added a note to the confirmation screen suggesting that folks check their spam/junk folders if they don’t see the email promptly.\n" -"\n" -"Stay safe, everyone.\n" +"Stay safe, y’all.\n" msgstr "" #. translators: This is a standard chunk of text used to tell a user what's new with a release when nothing major has changed. diff --git a/WordPress/Resources/release_notes.txt b/WordPress/Resources/release_notes.txt index fd93b37ce068..5cf44566382e 100644 --- a/WordPress/Resources/release_notes.txt +++ b/WordPress/Resources/release_notes.txt @@ -1,15 +1,11 @@ -New blocks! Welcome the Latest Posts block, for highlighting your new stuff in posts and pages. +Site Creation has been streamlined by removing some intermediate steps. Pick the kind of site you want, enter a domain name, and you’re done. -When you create a new page, we’ll show you a few templates — pick one, and we’ll add all the necessary blocks to your page to get you started quickly. +The Block editor got more improvements: The Cover block is now available; there are alignment options in the Heading block; and a dropdown toolbar makes alignment easier for Heading, Paragraph, Image, MediaText blocks. And we removed the dimming effect on unselected blocks. -We fixed up a few block editor issues; no more crashes when pasting in HTML content or missing Quote block borders in Dark Mode. +The layout of the Notifications screen caused text to get lost under the casing of some models of mobile phone. That’s fixed! -If you scheduled a post to publish in the past, it would display as “scheduled” instead of “published.” That’s fixed, along with an issue where dates displayed incorrectly if your site’s timezone wasn’t the same as your device’s timezone. +Sometimes you’d try to @-mention someone in a comment, and the app would suggest names but then not let you select one — that’s also fixed. We also took care of a few other issues that caused the app to crash when you tried to access the Site Pages screen or use the Quick Action buttons on iPads to get to your blog posts. And speaking of pages and posts, previews are now bigger for iPads running iOS 13. -We fixed some UI inconsistencies in Dark Mode and with the Reader toolbar. +We like our Dark Mode like we like our coffee: really dark, and without any bugs floating around in it. There were some glitches in Light and Dark Modes, and things sometimes behaved oddly when you switched from one to the other. There are far fewer of those glitches now. -You can use @-mentions when writing comments in fullscreen mode. - -Sometimes Signup and Login Magic Link emails would go to your spam folder, so we added a note to the confirmation screen suggesting that folks check their spam/junk folders if they don’t see the email promptly. - -Stay safe, everyone. \ No newline at end of file +Stay safe, y’all. \ No newline at end of file diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index c2aa85b6117e..e3477b3fb531 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -336,6 +336,7 @@ 319D6E8519E44F7F0013871C /* SuggestionsTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 319D6E8419E44F7F0013871C /* SuggestionsTableViewCell.m */; }; 31C9F82E1A2368A2008BB945 /* BlogDetailHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 31C9F82D1A2368A2008BB945 /* BlogDetailHeaderView.m */; }; 31EC15081A5B6675009FC8B3 /* WPStyleGuide+Suggestions.m in Sources */ = {isa = PBXBuildFile; fileRef = 31EC15071A5B6675009FC8B3 /* WPStyleGuide+Suggestions.m */; }; + 320B40DE2432533600DAB738 /* DrawerPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 320B40DD2432533600DAB738 /* DrawerPresentationController.swift */; }; 321E292623A5F10900588610 /* FullScreenCommentReplyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 328CEC5D23A532BA00A6899E /* FullScreenCommentReplyViewController.swift */; }; 323F8F3023A22C4C000BA49C /* SiteCreationRotatingMessageViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C6CDDA23A1FF0D002556FF /* SiteCreationRotatingMessageViewTests.swift */; }; 323F8F3123A22C8F000BA49C /* SiteCreationRotatingMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3221278523A0BD27002CA183 /* SiteCreationRotatingMessageView.swift */; }; @@ -634,7 +635,6 @@ 5D1181E71B4D6DEB003F3084 /* WPStyleGuide+Reader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D1181E61B4D6DEB003F3084 /* WPStyleGuide+Reader.swift */; }; 5D13FA571AF99C2100F06492 /* PageListSectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5D13FA561AF99C2100F06492 /* PageListSectionHeaderView.xib */; }; 5D146EBB189857ED0068FDC6 /* FeaturedImageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D146EBA189857ED0068FDC6 /* FeaturedImageViewController.m */; }; - 5D17F0BE1A1D4C5F0087CCB8 /* PrivateSiteURLProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D17F0BD1A1D4C5F0087CCB8 /* PrivateSiteURLProtocol.m */; }; 5D18FE9F1AFBB17400EFEED0 /* RestorePageTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D18FE9D1AFBB17400EFEED0 /* RestorePageTableViewCell.m */; }; 5D18FEA01AFBB17400EFEED0 /* RestorePageTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5D18FE9E1AFBB17400EFEED0 /* RestorePageTableViewCell.xib */; }; 5D1D04751B7A50B100CDE646 /* Reader.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5D1D04731B7A50B100CDE646 /* Reader.storyboard */; }; @@ -874,7 +874,6 @@ 748437F11F1D4ECC00E8DDAF /* notifications-last-seen.json in Resources */ = {isa = PBXBuildFile; fileRef = 748BD8881F1923D500813F9A /* notifications-last-seen.json */; }; 7484D94D20320DFE006E94B4 /* WordPressShare.js in Resources */ = {isa = PBXBuildFile; fileRef = E1AFA8C21E8E34230004A323 /* WordPressShare.js */; }; 748BD8851F19234300813F9A /* notifications-mark-as-read.json in Resources */ = {isa = PBXBuildFile; fileRef = 748BD8841F19234300813F9A /* notifications-mark-as-read.json */; }; - 749197EE209B9A2E006F5E66 /* ReaderCardContent+PostInformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 749197ED209B9A2E006F5E66 /* ReaderCardContent+PostInformation.swift */; }; 7492F78E1F9BD94500B5A04A /* ShareMediaFileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7430C4481F97F23600E2673E /* ShareMediaFileManager.swift */; }; 74989B8C2088E3650054290B /* BlogDetailsViewController+Activity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74989B8B2088E3650054290B /* BlogDetailsViewController+Activity.swift */; }; 74AC1DA1200D0CC300973CAD /* UINavigationController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74AC1DA0200D0CC300973CAD /* UINavigationController+Extensions.swift */; }; @@ -965,7 +964,6 @@ 7E58879A20FE8D9300DB6F80 /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E58879920FE8D9300DB6F80 /* Environment.swift */; }; 7E5887A020FE956100DB6F80 /* AppRatingUtilityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E58879F20FE956100DB6F80 /* AppRatingUtilityType.swift */; }; 7E729C28209A087300F76599 /* ImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E729C27209A087200F76599 /* ImageLoader.swift */; }; - 7E729C2A209A241100F76599 /* AbstractPost+PostInformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E729C29209A241100F76599 /* AbstractPost+PostInformation.swift */; }; 7E7947A9210BAC1D005BB851 /* NotificationContentRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E7947A8210BAC1D005BB851 /* NotificationContentRange.swift */; }; 7E7947AB210BAC5E005BB851 /* NotificationCommentRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E7947AA210BAC5E005BB851 /* NotificationCommentRange.swift */; }; 7E7947AD210BAC7B005BB851 /* FormattableNoticonRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E7947AC210BAC7B005BB851 /* FormattableNoticonRange.swift */; }; @@ -991,7 +989,6 @@ 7EBB4126206C388100012D98 /* StockPhotosService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EBB4125206C388100012D98 /* StockPhotosService.swift */; }; 7EC9FE0B22C627DB00C5A888 /* PostEditorAnalyticsSessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EC9FE0A22C627DB00C5A888 /* PostEditorAnalyticsSessionTests.swift */; }; 7ECD5B8120C4D823001AEBC5 /* MediaPreviewHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECD5B8020C4D823001AEBC5 /* MediaPreviewHelper.swift */; }; - 7ED3695520A9F091007B0D56 /* Blog+ImageSourceInformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ED3695420A9F091007B0D56 /* Blog+ImageSourceInformation.swift */; }; 7EDAB3F420B046FE002D1A76 /* CircularProgressView+ActivityIndicatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EDAB3F320B046FE002D1A76 /* CircularProgressView+ActivityIndicatorType.swift */; }; 7EF2EEA0210A67B60007A76B /* notifications-unapproved-comment.json in Resources */ = {isa = PBXBuildFile; fileRef = 7EF2EE9F210A67B60007A76B /* notifications-unapproved-comment.json */; }; 7EF9F65722F03C9200F79BBF /* SiteSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EF9F65622F03C9200F79BBF /* SiteSettingsScreen.swift */; }; @@ -1048,6 +1045,7 @@ 85F8E19D1B018698000859BB /* PushAuthenticationServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F8E19C1B018698000859BB /* PushAuthenticationServiceTests.swift */; }; 8B05D29123A9417E0063B9AA /* WPMediaEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B05D29023A9417E0063B9AA /* WPMediaEditor.swift */; }; 8B05D29323AA572A0063B9AA /* GutenbergMediaEditorImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B05D29223AA572A0063B9AA /* GutenbergMediaEditorImage.swift */; }; + 8B158B2A243CF07F00C66823 /* BottomSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B158B29243CF07F00C66823 /* BottomSheetViewController.swift */; }; 8B3DECAB2388506400A459C2 /* SentryStartupEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B3DECAA2388506400A459C2 /* SentryStartupEvent.swift */; }; 8B6EA62323FDE50B004BA312 /* PostServiceUploadingList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B6EA62223FDE50B004BA312 /* PostServiceUploadingList.swift */; }; 8B7623382384373E00AB3EE7 /* PageListViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B7623372384373E00AB3EE7 /* PageListViewControllerTests.swift */; }; @@ -1842,7 +1840,6 @@ E185474E1DED8D8800D875D7 /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E185474D1DED8D8800D875D7 /* UserNotifications.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; E18549D9230EED73003C620E /* BlogService+Deduplicate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E18549D8230EED73003C620E /* BlogService+Deduplicate.swift */; }; E18549DB230FBFEF003C620E /* BlogServiceDeduplicationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E18549DA230FBFEF003C620E /* BlogServiceDeduplicationTests.swift */; }; - E1928B2E1F8369F100E076C8 /* RequestAuthenticatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1928B2D1F8369F100E076C8 /* RequestAuthenticatorTests.swift */; }; E192E78C22EF453C008D725D /* WordPress-87-88.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = E192E78B22EF453C008D725D /* WordPress-87-88.xcmappingmodel */; }; E19B17AE1E5C6944007517C6 /* BasePost.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19B17AD1E5C6944007517C6 /* BasePost.swift */; }; E19B17B01E5C69A5007517C6 /* NSManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19B17AF1E5C69A5007517C6 /* NSManagedObject.swift */; }; @@ -2022,6 +2019,9 @@ F1450CF52437E1A600A28BFE /* MediaHost.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1450CF42437E1A600A28BFE /* MediaHost.swift */; }; F1450CF72437E8F800A28BFE /* MediaHostTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1450CF62437E8F800A28BFE /* MediaHostTests.swift */; }; F1450CF92437EEBB00A28BFE /* MediaRequestAuthenticatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1450CF82437EEBB00A28BFE /* MediaRequestAuthenticatorTests.swift */; }; + F15272FD243B27BC00C8DC7A /* AbstractPost+Local.swift in Sources */ = {isa = PBXBuildFile; fileRef = F15272FC243B27BC00C8DC7A /* AbstractPost+Local.swift */; }; + F15272FF243B28B700C8DC7A /* RequestAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F15272FE243B28B600C8DC7A /* RequestAuthenticator.swift */; }; + F1527301243B290E00C8DC7A /* AbstractPost+Autosave.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1527300243B290D00C8DC7A /* AbstractPost+Autosave.swift */; }; F15A230420A3EBE300625EA2 /* ImgUploadProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F126FDFE20A33BDB0010EB6E /* ImgUploadProcessor.swift */; }; F15A230520A3ECC500625EA2 /* ImgUploadProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F126FDFE20A33BDB0010EB6E /* ImgUploadProcessor.swift */; }; F1655B4822A6C2FA00227BFB /* Routes+Mbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1655B4722A6C2FA00227BFB /* Routes+Mbar.swift */; }; @@ -2038,7 +2038,6 @@ F1D690171F82914200200E30 /* BuildConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1D690141F828FF000200E30 /* BuildConfiguration.swift */; }; F1DB8D292288C14400906E2F /* Uploader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1DB8D282288C14400906E2F /* Uploader.swift */; }; F1DB8D2B2288C24500906E2F /* UploadsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1DB8D2A2288C24500906E2F /* UploadsManager.swift */; }; - F1E746E7242E459400C74D8A /* RequestAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1E746E6242E459400C74D8A /* RequestAuthenticator.swift */; }; F1F083F6241FFE930056D3B1 /* AtomicAuthenticationServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1F083F5241FFE930056D3B1 /* AtomicAuthenticationServiceTests.swift */; }; F511F8A42356A4F400895E73 /* PublishSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F511F8A32356A4F400895E73 /* PublishSettingsViewController.swift */; }; F53FF3A123E2377E001AD596 /* NewBlogDetailHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F53FF3A023E2377E001AD596 /* NewBlogDetailHeaderView.swift */; }; @@ -2661,6 +2660,7 @@ 31EC15061A5B6675009FC8B3 /* WPStyleGuide+Suggestions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WPStyleGuide+Suggestions.h"; sourceTree = ""; }; 31EC15071A5B6675009FC8B3 /* WPStyleGuide+Suggestions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WPStyleGuide+Suggestions.m"; sourceTree = ""; }; 31FA16CC1A49B3C0003E1887 /* WordPress 25.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 25.xcdatamodel"; sourceTree = ""; }; + 320B40DD2432533600DAB738 /* DrawerPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DrawerPresentationController.swift; sourceTree = ""; }; 3221278523A0BD27002CA183 /* SiteCreationRotatingMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteCreationRotatingMessageView.swift; sourceTree = ""; }; 32282CF82390B614003378A7 /* WordPress 94.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 94.xcdatamodel"; sourceTree = ""; }; 3249615023F20111004C7733 /* PostSignUpInterstitialViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostSignUpInterstitialViewController.swift; sourceTree = ""; }; @@ -2962,8 +2962,6 @@ 5D13FA561AF99C2100F06492 /* PageListSectionHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PageListSectionHeaderView.xib; sourceTree = ""; }; 5D146EB9189857ED0068FDC6 /* FeaturedImageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FeaturedImageViewController.h; sourceTree = ""; usesTabs = 0; }; 5D146EBA189857ED0068FDC6 /* FeaturedImageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FeaturedImageViewController.m; sourceTree = ""; usesTabs = 0; }; - 5D17F0BC1A1D4C5F0087CCB8 /* PrivateSiteURLProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrivateSiteURLProtocol.h; sourceTree = ""; }; - 5D17F0BD1A1D4C5F0087CCB8 /* PrivateSiteURLProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PrivateSiteURLProtocol.m; sourceTree = ""; }; 5D18FE9C1AFBB17400EFEED0 /* RestorePageTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RestorePageTableViewCell.h; sourceTree = ""; }; 5D18FE9D1AFBB17400EFEED0 /* RestorePageTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RestorePageTableViewCell.m; sourceTree = ""; }; 5D18FE9E1AFBB17400EFEED0 /* RestorePageTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RestorePageTableViewCell.xib; sourceTree = ""; }; @@ -3433,6 +3431,7 @@ 85F8E19C1B018698000859BB /* PushAuthenticationServiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushAuthenticationServiceTests.swift; sourceTree = ""; }; 8B05D29023A9417E0063B9AA /* WPMediaEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WPMediaEditor.swift; sourceTree = ""; }; 8B05D29223AA572A0063B9AA /* GutenbergMediaEditorImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GutenbergMediaEditorImage.swift; sourceTree = ""; }; + 8B158B29243CF07F00C66823 /* BottomSheetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomSheetViewController.swift; sourceTree = ""; }; 8B3DECAA2388506400A459C2 /* SentryStartupEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryStartupEvent.swift; sourceTree = ""; }; 8B6EA62223FDE50B004BA312 /* PostServiceUploadingList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostServiceUploadingList.swift; sourceTree = ""; }; 8B7623372384373E00AB3EE7 /* PageListViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageListViewControllerTests.swift; sourceTree = ""; }; @@ -4340,7 +4339,6 @@ E1874BFE161C5DBC0058BDC4 /* WordPress 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 7.xcdatamodel"; sourceTree = ""; }; E18D8AE21397C51A00000861 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; E18D8AE41397C54E00000861 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = ""; }; - E1928B2D1F8369F100E076C8 /* RequestAuthenticatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestAuthenticatorTests.swift; sourceTree = ""; }; E192E78B22EF453C008D725D /* WordPress-87-88.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = "WordPress-87-88.xcmappingmodel"; sourceTree = ""; }; E1939C671B15B4D2001AFEF7 /* WordPress 30.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 30.xcdatamodel"; sourceTree = ""; }; E19853331755E461001CC6D5 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = ""; }; @@ -4382,6 +4380,7 @@ E1B912881BB01288003C25B9 /* PeopleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeopleViewController.swift; sourceTree = ""; }; E1B9128A1BB0129C003C25B9 /* WPStyleGuide+People.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "WPStyleGuide+People.swift"; sourceTree = ""; }; E1B921BB1C0ED5A3003EA3CB /* MediaSizeSliderCellTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaSizeSliderCellTest.swift; sourceTree = ""; }; + E1BB85971F82459800797050 /* WebViewAuthenticator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewAuthenticator.swift; sourceTree = ""; }; E1BB92311FDAAFFA00F2D817 /* TextWithAccessoryButtonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextWithAccessoryButtonCell.swift; sourceTree = ""; }; E1BCFBC51C0626C5004BDADF /* WordPress 43.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 43.xcdatamodel"; sourceTree = ""; }; E1BEEC621C4E35A8000B4FA0 /* Animator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Animator.swift; sourceTree = ""; }; @@ -4610,6 +4609,9 @@ F14B5F74208E64F900439554 /* Version.public.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Version.public.xcconfig; sourceTree = ""; }; F14B5F75208E64F900439554 /* Version.internal.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Version.internal.xcconfig; sourceTree = ""; }; F14E844C2317252200D0C63E /* WordPress 90.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 90.xcdatamodel"; sourceTree = ""; }; + F15272FC243B27BC00C8DC7A /* AbstractPost+Local.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AbstractPost+Local.swift"; sourceTree = ""; }; + F15272FE243B28B600C8DC7A /* RequestAuthenticator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestAuthenticator.swift; sourceTree = ""; }; + F1527300243B290D00C8DC7A /* AbstractPost+Autosave.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AbstractPost+Autosave.swift"; sourceTree = ""; }; F1655B4722A6C2FA00227BFB /* Routes+Mbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Routes+Mbar.swift"; sourceTree = ""; }; F16601C323E9E783007950AE /* SharingAuthorizationWebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingAuthorizationWebViewController.swift; sourceTree = ""; }; F16C35D523F33DE400C81331 /* PageAutoUploadMessageProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PageAutoUploadMessageProvider.swift; path = ../Post/PageAutoUploadMessageProvider.swift; sourceTree = ""; }; @@ -4620,11 +4622,11 @@ F18B43771F849F580089B817 /* PostAttachmentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PostAttachmentTests.swift; path = Posts/PostAttachmentTests.swift; sourceTree = ""; }; F1ADCAF6241FEF0C00F150D2 /* AtomicAuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AtomicAuthenticationService.swift; sourceTree = ""; }; F1B1E7A224098FA100549E2A /* BlogTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlogTests.swift; sourceTree = ""; }; + F1BBA95E243BEFC500E9E5E6 /* WordPress 95.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 95.xcdatamodel"; sourceTree = ""; }; F1D690141F828FF000200E30 /* BuildConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildConfiguration.swift; sourceTree = ""; }; F1D690151F828FF000200E30 /* FeatureFlag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlag.swift; sourceTree = ""; }; F1DB8D282288C14400906E2F /* Uploader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Uploader.swift; sourceTree = ""; }; F1DB8D2A2288C24500906E2F /* UploadsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadsManager.swift; sourceTree = ""; }; - F1E746E6242E459400C74D8A /* RequestAuthenticator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestAuthenticator.swift; sourceTree = ""; }; F1F083F5241FFE930056D3B1 /* AtomicAuthenticationServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AtomicAuthenticationServiceTests.swift; sourceTree = ""; }; F262DFCA1F94418CE76D1D78 /* Pods-WordPressNotificationContentExtension.release-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressNotificationContentExtension.release-internal.xcconfig"; path = "../Pods/Target Support Files/Pods-WordPressNotificationContentExtension/Pods-WordPressNotificationContentExtension.release-internal.xcconfig"; sourceTree = ""; }; F373612EEEEF10E500093FF3 /* Pods-WordPress.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPress.release-alpha.xcconfig"; path = "../Pods/Target Support Files/Pods-WordPress/Pods-WordPress.release-alpha.xcconfig"; sourceTree = ""; }; @@ -5410,7 +5412,7 @@ 1A433B1B2254CBC300AE7910 /* Networking */ = { isa = PBXGroup; children = ( - F1E746E6242E459400C74D8A /* RequestAuthenticator.swift */, + F15272FE243B28B600C8DC7A /* RequestAuthenticator.swift */, 1A433B1C2254CBEE00AE7910 /* WordPressComRestApi+Defaults.swift */, ); path = Networking; @@ -5572,8 +5574,10 @@ B5176CBF1CDCE14F0083CF2D /* People Management */, 5D42A3D6175E7452005CFF05 /* AbstractPost.h */, 5D42A3D7175E7452005CFF05 /* AbstractPost.m */, + F1527300243B290D00C8DC7A /* AbstractPost+Autosave.swift */, 40232A9C230A6A740036B0B6 /* AbstractPost+HashHelpers.h */, 40232A9D230A6A740036B0B6 /* AbstractPost+HashHelpers.m */, + F15272FC243B27BC00C8DC7A /* AbstractPost+Local.swift */, E19B17B11E5C8F36007517C6 /* AbstractPost.swift */, 74729CAD205722E300D1394D /* AbstractPost+Searchable.swift */, 5D42A3D8175E7452005CFF05 /* BasePost.h */, @@ -7346,7 +7350,6 @@ 93A379EB19FFBF7900415023 /* KeychainTest.m */, 1759F1711FE017F20003EC81 /* QueueTests.swift */, 1797373620EBAA4100377B4E /* RouteMatcherTests.swift */, - E1928B2D1F8369F100E076C8 /* RequestAuthenticatorTests.swift */, 5948AD101AB73D19006E8882 /* WPAppAnalyticsTests.m */, E1E4CE0C177439D100430844 /* WPAvatarSourceTest.m */, 5981FE041AB8A89A0009E080 /* WPUserAgentTests.m */, @@ -7398,6 +7401,7 @@ 8584FDB4192437160019C02E /* Utility */ = { isa = PBXGroup; children = ( + 8B158B28243CF05900C66823 /* Bottom Sheet */, 1A433B1B2254CBC300AE7910 /* Networking */, 40247E002120FE2300AE1C3C /* Automated Transfer */, F198533821ADAA4E00DCDAE7 /* Editor */, @@ -7536,6 +7540,14 @@ name = Networking; sourceTree = ""; }; + 8B158B28243CF05900C66823 /* Bottom Sheet */ = { + isa = PBXGroup; + children = ( + 8B158B29243CF07F00C66823 /* BottomSheetViewController.swift */, + ); + path = "Bottom Sheet"; + sourceTree = ""; + }; 8B7623352384372200AB3EE7 /* Pages */ = { isa = PBXGroup; children = ( @@ -8348,8 +8360,6 @@ FFEECFFB2084DE2B009B8CDB /* PostSettingsViewController+FeaturedImageUpload.swift */, 593F26601CAB00CA00F14073 /* PostSharingController.swift */, E155EC711E9B7DCE009D7F63 /* PostTagPickerViewController.swift */, - 5D17F0BC1A1D4C5F0087CCB8 /* PrivateSiteURLProtocol.h */, - 5D17F0BD1A1D4C5F0087CCB8 /* PrivateSiteURLProtocol.m */, 5903AE1C19B60AB9009D5354 /* WPButtonForNavigationBar.h */, 5903AE1A19B60A98009D5354 /* WPButtonForNavigationBar.m */, FF0AAE0B1A16550D0089841D /* WPMediaProgressTableViewController.h */, @@ -8470,6 +8480,7 @@ E16FB7E21F8B61030004DD9F /* WebKitViewController.swift */, F5D0A64823C8FA1500B20D27 /* LinkBehavior.swift */, E1222B621F877FD700D23173 /* WebProgressView.swift */, + E1BB85971F82459800797050 /* WebViewAuthenticator.swift */, E16FB7E01F8B5D7D0004DD9F /* WebViewControllerConfiguration.swift */, E1222B661F878E4700D23173 /* WebViewControllerFactory.swift */, B526DC271B1E47FC002A8C5F /* WPStyleGuide+WebView.h */, @@ -10088,6 +10099,7 @@ F5E032E22408D537003AF350 /* Action Sheet */ = { isa = PBXGroup; children = ( + 320B40DD2432533600DAB738 /* DrawerPresentationController.swift */, F5E032E32408D537003AF350 /* ActionSheetViewController.swift */, F5E032E42408D537003AF350 /* GripView.swift */, F5E032E52408D537003AF350 /* BottomSheetPresentationController.swift */, @@ -11682,7 +11694,6 @@ 1E9D544D23C4C56300F6A9E0 /* GutenbergRollout.swift in Sources */, 17E24F5420FCF1D900BD70A3 /* Routes+MySites.swift in Sources */, 0828D7FA1E6E09AE00C7C7D4 /* WPAppAnalytics+Media.swift in Sources */, - 7ED3695520A9F091007B0D56 /* Blog+ImageSourceInformation.swift in Sources */, 591AA5011CEF9BF20074934F /* Post.swift in Sources */, C545E0A21811B9880020844C /* ContextManager.m in Sources */, E161B7EC1F839345000FDF0B /* CookieJar.swift in Sources */, @@ -11707,8 +11718,8 @@ F580C3C123D22E2D0038E243 /* PreviewDeviceLabel.swift in Sources */, 5D7DEA2919D488DD0032EE77 /* WPStyleGuide+ReaderComments.swift in Sources */, 40C403F62215D66A00E8C894 /* TopViewedPostStatsRecordValue+CoreDataProperties.swift in Sources */, - F1E746E7242E459400C74D8A /* RequestAuthenticator.swift in Sources */, 82FC61251FA8ADAD00A1757E /* ActivityListViewController.swift in Sources */, + F15272FF243B28B700C8DC7A /* RequestAuthenticator.swift in Sources */, E66E2A651FE4311300788F22 /* SettingsTitleSubtitleController.swift in Sources */, E1222B671F878E4700D23173 /* WebViewControllerFactory.swift in Sources */, E1D86E691B2B414300DD2192 /* WordPress-32-33.xcmappingmodel in Sources */, @@ -11809,6 +11820,7 @@ 7E53AB0020FE55A9005796FE /* ActivityContentRouter.swift in Sources */, 738B9A5C21B85EB00005062B /* UIView+ContentLayout.swift in Sources */, E1B9128F1BB05B1D003C25B9 /* PeopleCell.swift in Sources */, + F15272FD243B27BC00C8DC7A /* AbstractPost+Local.swift in Sources */, 3F43603123F31E09001DEE70 /* MeScenePresenter.swift in Sources */, 313AE4A019E3F20400AAFABE /* CommentViewController.m in Sources */, 7E3E7A6620E44F200075D159 /* HeaderContentGroup.swift in Sources */, @@ -11992,6 +12004,7 @@ 4054F4562214E3C800D261AB /* TagsCategoriesStatsRecordValue+CoreDataClass.swift in Sources */, E11000991CDB5F1E00E33887 /* KeychainTools.swift in Sources */, D800D87020998A7300E7C7E5 /* SavedForLaterMenuItemCreator.swift in Sources */, + 8B158B2A243CF07F00C66823 /* BottomSheetViewController.swift in Sources */, E6D2E1671B8AAD8C0000ED14 /* ReaderTagStreamHeader.swift in Sources */, 321E292623A5F10900588610 /* FullScreenCommentReplyViewController.swift in Sources */, E6D3B1431D1C702600008D4B /* ReaderFollowedSitesViewController.swift in Sources */, @@ -12187,7 +12200,6 @@ E6C0ED3B231DA23400A08B57 /* AccountService+MergeDuplicates.swift in Sources */, 0845B8C61E833C56001BA771 /* URL+Helpers.swift in Sources */, 17D975AF1EF7F6F100303D63 /* WPStyleGuide+Aztec.swift in Sources */, - 5D17F0BE1A1D4C5F0087CCB8 /* PrivateSiteURLProtocol.m in Sources */, E14B40FF1C58B93F005046F6 /* SettingsCommon.swift in Sources */, B50C0C661EF42B6400372C65 /* FormatBarItemProviders.swift in Sources */, 439F4F352196537500F8D0C7 /* RevisionDiffViewController.swift in Sources */, @@ -12239,7 +12251,6 @@ 7E7947AB210BAC5E005BB851 /* NotificationCommentRange.swift in Sources */, D816C1E920E0880400C4D82F /* NotificationAction.swift in Sources */, E19B17B01E5C69A5007517C6 /* NSManagedObject.swift in Sources */, - 749197EE209B9A2E006F5E66 /* ReaderCardContent+PostInformation.swift in Sources */, FF355D981FB492DD00244E6D /* ExportableAsset.swift in Sources */, E15644EF1CE0E53B00D96E64 /* PlanListViewModel.swift in Sources */, 983002A822FA05D600F03DBB /* AddInsightTableViewController.swift in Sources */, @@ -12263,6 +12274,7 @@ 7E3E7A6020E44E490075D159 /* FooterContentGroup.swift in Sources */, 59A3CADD1CD2FF0C009BFA1B /* BasePageListCell.m in Sources */, 436D56202117312700CEAA33 /* RegisterDomainSuggestionsTableViewController.swift in Sources */, + 320B40DE2432533600DAB738 /* DrawerPresentationController.swift in Sources */, E114D79A153D85A800984182 /* WPError.m in Sources */, 7E53AB0220FE5EAE005796FE /* ContentRouter.swift in Sources */, E16273E11B2ACEB600088AF7 /* BlogToBlog32to33.swift in Sources */, @@ -12295,6 +12307,7 @@ E6E57CD51D0F08B200C22E3E /* ReaderMenuViewController.swift in Sources */, 08D978591CD2AF7D0054F19A /* MenuItemSourceTextBar.m in Sources */, 40E469952017FB1F0030DB5F /* PluginDirectoryViewModel.swift in Sources */, + F1527301243B290E00C8DC7A /* AbstractPost+Autosave.swift in Sources */, 745A41B02065405600299D75 /* ReaderPost+Searchable.swift in Sources */, E1A8CACB1C22FF7C0038689E /* MeViewController.swift in Sources */, 9A341E5321997A1F0036662E /* BlogService+BlogAuthors.swift in Sources */, @@ -12813,7 +12826,6 @@ 3F09CCAE24292EFD00D00A8C /* ReaderTabItem.swift in Sources */, 5D839AAB187F0D8000811F4A /* PostGeolocationCell.m in Sources */, 4054F4422213635000D261AB /* TopCommentsAuthorStatsRecordValue+CoreDataClass.swift in Sources */, - 7E729C2A209A241100F76599 /* AbstractPost+PostInformation.swift in Sources */, 8236EB502024ED8C007C7CF9 /* NotificationsViewController+JetpackPrompt.swift in Sources */, 5D732F971AE84E3C00CD89E7 /* PostListFooterView.m in Sources */, 1767494E1D3633A000B8D1D1 /* ThemeBrowserSearchHeaderView.swift in Sources */, @@ -13370,7 +13382,6 @@ D821C81B21003AE9002ED995 /* FormattableContentGroupTests.swift in Sources */, 93D86B981C691E71003D8E3E /* LocalCoreDataServiceTests.m in Sources */, 400A2C932217B463000A8A59 /* ReferrerStatsRecordValueTests.swift in Sources */, - E1928B2E1F8369F100E076C8 /* RequestAuthenticatorTests.swift in Sources */, 73178C2921BEE09300E37C9A /* SiteSegmentsStepTests.swift in Sources */, 08F8CD311EBD2A960049D0C0 /* MediaImageExporterTests.swift in Sources */, 400A2C912217B308000A8A59 /* CountryStatsRecordValueTests.swift in Sources */, @@ -16448,6 +16459,7 @@ E125443B12BF5A7200D87A0A /* WordPress.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + F1BBA95E243BEFC500E9E5E6 /* WordPress 95.xcdatamodel */, 32282CF82390B614003378A7 /* WordPress 94.xcdatamodel */, 32A29A16236BC8BC009488C2 /* WordPress 93.xcdatamodel */, 57CCB37E2358E5DC003ECD0C /* WordPress 92.xcdatamodel */, @@ -16543,7 +16555,7 @@ 8350E15911D28B4A00A7B073 /* WordPress.xcdatamodel */, E125443D12BF5A7200D87A0A /* WordPress 2.xcdatamodel */, ); - currentVersion = 32282CF82390B614003378A7 /* WordPress 94.xcdatamodel */; + currentVersion = F1BBA95E243BEFC500E9E5E6 /* WordPress 95.xcdatamodel */; name = WordPress.xcdatamodeld; path = Classes/WordPress.xcdatamodeld; sourceTree = ""; diff --git a/WordPress/WordPressTest/MediaHostTests.swift b/WordPress/WordPressTest/MediaHostTests.swift index bdaab3d450ae..af97c30856e3 100644 --- a/WordPress/WordPressTest/MediaHostTests.swift +++ b/WordPress/WordPressTest/MediaHostTests.swift @@ -3,24 +3,42 @@ import XCTest class MediaHostTests: XCTestCase { func testInitializationWithPublicSite() { - let host = MediaHost(isAccessibleThroughWPCom: false, isPrivate: false, isAtomic: false) { error in - XCTFail("This should not be called.") + let host = MediaHost( + isAccessibleThroughWPCom: false, + isPrivate: false, + isAtomic: false, + siteID: nil, + username: nil, + authToken: nil) { error in + XCTFail("This should not be called.") } XCTAssertEqual(host, .publicSite) } func testInitializationWithPublicWPComSite() { - let host = MediaHost(isAccessibleThroughWPCom: true, isPrivate: false, isAtomic: false) { error in - XCTFail("This should not be called.") + let host = MediaHost( + isAccessibleThroughWPCom: true, + isPrivate: false, + isAtomic: false, + siteID: nil, + username: nil, + authToken: nil) { error in + XCTFail("This should not be called.") } XCTAssertEqual(host, .publicWPComSite) } func testInitializationWithPrivateSelfHostedSite() { - let host = MediaHost(isAccessibleThroughWPCom: false, isPrivate: true, isAtomic: false) { error in - XCTFail("This should not be called.") + let host = MediaHost( + isAccessibleThroughWPCom: false, + isPrivate: true, + isAtomic: false, + siteID: nil, + username: nil, + authToken: nil) { error in + XCTFail("This should not be called.") } XCTAssertEqual(host, .privateSelfHostedSite) @@ -33,6 +51,8 @@ class MediaHostTests: XCTestCase { isAccessibleThroughWPCom: true, isPrivate: true, isAtomic: false, + siteID: nil, + username: nil, authToken: authToken) { error in XCTFail("This should not be called.") @@ -70,7 +90,8 @@ class MediaHostTests: XCTestCase { isPrivate: true, isAtomic: true, siteID: siteID, - username: username) { error in + username: username, + authToken: nil) { error in if error == .wpComPrivateSiteWithoutAuthToken { expectation.fulfill() } @@ -89,6 +110,7 @@ class MediaHostTests: XCTestCase { isPrivate: true, isAtomic: true, siteID: siteID, + username: nil, authToken: authToken) { error in if error == .wpComPrivateSiteWithoutUsername { expectation.fulfill() @@ -101,8 +123,14 @@ class MediaHostTests: XCTestCase { func testInitializationWithPrivateAtomicWPComSiteWithoutSiteIDFails() { let expectation = self.expectation(description: "The error closure will be called") - let _ = MediaHost(isAccessibleThroughWPCom: true, isPrivate: true, isAtomic: true) { error in - expectation.fulfill() + let _ = MediaHost( + isAccessibleThroughWPCom: true, + isPrivate: true, + isAtomic: true, + siteID: nil, + username: nil, + authToken: nil) { error in + expectation.fulfill() } waitForExpectations(timeout: 0.05)