From a51eaefcaa538e6512c6f0a57f3d3b5ce2f39a79 Mon Sep 17 00:00:00 2001 From: Paul Von Schrottky Date: Tue, 20 Oct 2020 15:25:36 -0300 Subject: [PATCH 1/4] Add support for X-posting in Gutenberg - Add xpost support to Gutenberg in `GutenbergViewController` - Extend `SuggestionsTableView` to support xposts - Extend `SuggestionService` to support xposts - Make filtering suggestions (both for xposts and @-mentions) case insensitive and diacritic insensitive) - Add a 60 second throttle to both xpost and @-mention network calls - Make `UserSuggestion` conform to `Decodable` --- Podfile | 2 +- Podfile.lock | 166 +++++++++--------- WordPress/Classes/Models/Blog.h | 2 + WordPress/Classes/Models/Blog.m | 2 + .../Models/SiteSuggestion+CoreDataClass.swift | 6 +- .../Models/UserSuggestion+CoreDataClass.swift | 52 ++++-- .../Classes/Services/SuggestionService.swift | 153 ++++++++++------ .../BuildInformation/FeatureFlag.swift | 5 + .../Comments/CommentViewController.swift | 2 +- ...FullScreenCommentReplyViewController.swift | 2 +- .../Gutenberg/GutenbergViewController.swift | 16 +- .../NotificationDetailsViewController.swift | 2 +- .../Reader/ReaderCommentsViewController.swift | 2 +- .../Suggestions/SuggestionsTableView.h | 5 +- .../Suggestions/SuggestionsTableView.m | 2 +- .../Suggestions/SuggestionsTableView.swift | 47 ++--- WordPress/WordPress.xcodeproj/project.pbxproj | 4 +- 17 files changed, 286 insertions(+), 184 deletions(-) diff --git a/Podfile b/Podfile index bd10de0fd99c..2d666a89f5bd 100644 --- a/Podfile +++ b/Podfile @@ -154,7 +154,7 @@ target 'WordPress' do ## Gutenberg (React Native) ## ===================== ## - gutenberg :tag => 'v1.43.0-alpha2' + gutenberg :commit => 'eda7547baa0dd4d180895d0f6a8bed7d20a85453' ## Third party libraries ## ===================== diff --git a/Podfile.lock b/Podfile.lock index 64f07a7360d3..e4e8e121732f 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -449,14 +449,14 @@ DEPENDENCIES: - CocoaLumberjack (~> 3.0) - CropViewController (= 2.5.3) - Down (~> 0.6.6) - - FBLazyVector (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/FBLazyVector.podspec.json`) - - FBReactNativeSpec (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/FBReactNativeSpec.podspec.json`) - - Folly (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/Folly.podspec.json`) + - FBLazyVector (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/FBLazyVector.podspec.json`) + - FBReactNativeSpec (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/FBReactNativeSpec.podspec.json`) + - Folly (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/Folly.podspec.json`) - 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/v1.43.0-alpha2/third-party-podspecs/glog.podspec.json`) + - glog (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/glog.podspec.json`) - Gridicons (~> 1.0.2) - - Gutenberg (from `http://github.com/wordpress-mobile/gutenberg-mobile/`, tag `v1.43.0-alpha2`) + - Gutenberg (from `http://github.com/wordpress-mobile/gutenberg-mobile/`, commit `eda7547baa0dd4d180895d0f6a8bed7d20a85453`) - JTAppleCalendar (~> 8.0.2) - MediaEditor (~> 1.2.1) - MRProgress (= 0.8.3) @@ -466,41 +466,41 @@ DEPENDENCIES: - OCMock (= 3.4.3) - OHHTTPStubs (= 6.1.0) - OHHTTPStubs/Swift (= 6.1.0) - - RCTRequired (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/RCTRequired.podspec.json`) - - RCTTypeSafety (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/RCTTypeSafety.podspec.json`) + - RCTRequired (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/RCTRequired.podspec.json`) + - RCTTypeSafety (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/RCTTypeSafety.podspec.json`) - Reachability (= 3.2) - - React (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React.podspec.json`) - - React-Core (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-Core.podspec.json`) - - React-CoreModules (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-CoreModules.podspec.json`) - - React-cxxreact (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-cxxreact.podspec.json`) - - React-jsi (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-jsi.podspec.json`) - - React-jsiexecutor (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-jsiexecutor.podspec.json`) - - React-jsinspector (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-jsinspector.podspec.json`) - - react-native-blur (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/react-native-blur.podspec.json`) - - react-native-get-random-values (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/react-native-get-random-values.podspec.json`) - - react-native-keyboard-aware-scroll-view (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/react-native-keyboard-aware-scroll-view.podspec.json`) - - react-native-linear-gradient (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/react-native-linear-gradient.podspec.json`) - - react-native-safe-area (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/react-native-safe-area.podspec.json`) - - react-native-safe-area-context (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/react-native-safe-area-context.podspec.json`) - - react-native-slider (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/react-native-slider.podspec.json`) - - react-native-video (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/react-native-video.podspec.json`) - - React-RCTActionSheet (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTActionSheet.podspec.json`) - - React-RCTAnimation (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTAnimation.podspec.json`) - - React-RCTBlob (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTBlob.podspec.json`) - - React-RCTImage (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTImage.podspec.json`) - - React-RCTLinking (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTLinking.podspec.json`) - - React-RCTNetwork (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTNetwork.podspec.json`) - - React-RCTSettings (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTSettings.podspec.json`) - - React-RCTText (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTText.podspec.json`) - - React-RCTVibration (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTVibration.podspec.json`) - - ReactCommon (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/ReactCommon.podspec.json`) - - ReactNativeDarkMode (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/ReactNativeDarkMode.podspec.json`) - - RNCMaskedView (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/RNCMaskedView.podspec.json`) - - RNGestureHandler (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/RNGestureHandler.podspec.json`) - - RNReanimated (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/RNReanimated.podspec.json`) - - RNScreens (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/RNScreens.podspec.json`) - - RNSVG (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/RNSVG.podspec.json`) - - RNTAztecView (from `http://github.com/wordpress-mobile/gutenberg-mobile/`, tag `v1.43.0-alpha2`) + - React (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React.podspec.json`) + - React-Core (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-Core.podspec.json`) + - React-CoreModules (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-CoreModules.podspec.json`) + - React-cxxreact (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-cxxreact.podspec.json`) + - React-jsi (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-jsi.podspec.json`) + - React-jsiexecutor (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-jsiexecutor.podspec.json`) + - React-jsinspector (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-jsinspector.podspec.json`) + - react-native-blur (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/react-native-blur.podspec.json`) + - react-native-get-random-values (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/react-native-get-random-values.podspec.json`) + - react-native-keyboard-aware-scroll-view (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/react-native-keyboard-aware-scroll-view.podspec.json`) + - react-native-linear-gradient (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/react-native-linear-gradient.podspec.json`) + - react-native-safe-area (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/react-native-safe-area.podspec.json`) + - react-native-safe-area-context (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/react-native-safe-area-context.podspec.json`) + - react-native-slider (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/react-native-slider.podspec.json`) + - react-native-video (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/react-native-video.podspec.json`) + - React-RCTActionSheet (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTActionSheet.podspec.json`) + - React-RCTAnimation (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTAnimation.podspec.json`) + - React-RCTBlob (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTBlob.podspec.json`) + - React-RCTImage (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTImage.podspec.json`) + - React-RCTLinking (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTLinking.podspec.json`) + - React-RCTNetwork (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTNetwork.podspec.json`) + - React-RCTSettings (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTSettings.podspec.json`) + - React-RCTText (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTText.podspec.json`) + - React-RCTVibration (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTVibration.podspec.json`) + - ReactCommon (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/ReactCommon.podspec.json`) + - ReactNativeDarkMode (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/ReactNativeDarkMode.podspec.json`) + - RNCMaskedView (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/RNCMaskedView.podspec.json`) + - RNGestureHandler (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/RNGestureHandler.podspec.json`) + - RNReanimated (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/RNReanimated.podspec.json`) + - RNScreens (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/RNScreens.podspec.json`) + - RNSVG (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/RNSVG.podspec.json`) + - RNTAztecView (from `http://github.com/wordpress-mobile/gutenberg-mobile/`, commit `eda7547baa0dd4d180895d0f6a8bed7d20a85453`) - Starscream (= 3.0.6) - SVProgressHUD (= 2.2.5) - WordPress-Editor-iOS (~> 1.19.3) @@ -510,7 +510,7 @@ DEPENDENCIES: - WordPressShared (~> 1.13.0) - WordPressUI (~> 1.7.4-beta.1) - WPMediaPicker (~> 1.7.2) - - Yoga (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/Yoga.podspec.json`) + - Yoga (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/Yoga.podspec.json`) - ZendeskSupportSDK (= 5.1.1) - ZIPFoundation (~> 0.9.8) @@ -571,108 +571,108 @@ SPEC REPOS: EXTERNAL SOURCES: FBLazyVector: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/FBLazyVector.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/FBLazyVector.podspec.json FBReactNativeSpec: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/FBReactNativeSpec.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/FBReactNativeSpec.podspec.json Folly: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/Folly.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/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/v1.43.0-alpha2/third-party-podspecs/glog.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/glog.podspec.json Gutenberg: + :commit: eda7547baa0dd4d180895d0f6a8bed7d20a85453 :git: http://github.com/wordpress-mobile/gutenberg-mobile/ :submodules: true - :tag: v1.43.0-alpha2 RCTRequired: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/RCTRequired.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/RCTRequired.podspec.json RCTTypeSafety: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/RCTTypeSafety.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/RCTTypeSafety.podspec.json React: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React.podspec.json React-Core: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-Core.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-Core.podspec.json React-CoreModules: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-CoreModules.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-CoreModules.podspec.json React-cxxreact: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-cxxreact.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-cxxreact.podspec.json React-jsi: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-jsi.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-jsi.podspec.json React-jsiexecutor: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-jsiexecutor.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-jsiexecutor.podspec.json React-jsinspector: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-jsinspector.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-jsinspector.podspec.json react-native-blur: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/react-native-blur.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/react-native-blur.podspec.json react-native-get-random-values: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/react-native-get-random-values.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/react-native-get-random-values.podspec.json react-native-keyboard-aware-scroll-view: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/react-native-keyboard-aware-scroll-view.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/react-native-keyboard-aware-scroll-view.podspec.json react-native-linear-gradient: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/react-native-linear-gradient.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/react-native-linear-gradient.podspec.json react-native-safe-area: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/react-native-safe-area.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/react-native-safe-area.podspec.json react-native-safe-area-context: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/react-native-safe-area-context.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/react-native-safe-area-context.podspec.json react-native-slider: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/react-native-slider.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/react-native-slider.podspec.json react-native-video: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/react-native-video.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/react-native-video.podspec.json React-RCTActionSheet: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTActionSheet.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTActionSheet.podspec.json React-RCTAnimation: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTAnimation.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTAnimation.podspec.json React-RCTBlob: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTBlob.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTBlob.podspec.json React-RCTImage: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTImage.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTImage.podspec.json React-RCTLinking: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTLinking.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTLinking.podspec.json React-RCTNetwork: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTNetwork.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTNetwork.podspec.json React-RCTSettings: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTSettings.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTSettings.podspec.json React-RCTText: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTText.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTText.podspec.json React-RCTVibration: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/React-RCTVibration.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/React-RCTVibration.podspec.json ReactCommon: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/ReactCommon.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/ReactCommon.podspec.json ReactNativeDarkMode: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/ReactNativeDarkMode.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/ReactNativeDarkMode.podspec.json RNCMaskedView: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/RNCMaskedView.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/RNCMaskedView.podspec.json RNGestureHandler: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/RNGestureHandler.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/RNGestureHandler.podspec.json RNReanimated: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/RNReanimated.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/RNReanimated.podspec.json RNScreens: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/RNScreens.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/RNScreens.podspec.json RNSVG: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/RNSVG.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/RNSVG.podspec.json RNTAztecView: + :commit: eda7547baa0dd4d180895d0f6a8bed7d20a85453 :git: http://github.com/wordpress-mobile/gutenberg-mobile/ :submodules: true - :tag: v1.43.0-alpha2 Yoga: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0-alpha2/third-party-podspecs/Yoga.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/eda7547baa0dd4d180895d0f6a8bed7d20a85453/third-party-podspecs/Yoga.podspec.json CHECKOUT OPTIONS: FSInteractiveMap: :git: https://github.com/wordpress-mobile/FSInteractiveMap.git :tag: 0.2.0 Gutenberg: + :commit: eda7547baa0dd4d180895d0f6a8bed7d20a85453 :git: http://github.com/wordpress-mobile/gutenberg-mobile/ :submodules: true - :tag: v1.43.0-alpha2 RNCMaskedView: :commit: d4ccf2bba163679c4550ce6ba0119604cd5e6379 :git: https://github.com/react-native-community/react-native-masked-view.git RNTAztecView: + :commit: eda7547baa0dd4d180895d0f6a8bed7d20a85453 :git: http://github.com/wordpress-mobile/gutenberg-mobile/ :submodules: true - :tag: v1.43.0-alpha2 SPEC CHECKSUMS: 1PasswordExtension: f97cc80ae58053c331b2b6dc8843ba7103b33794 @@ -769,6 +769,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: dcb2596ad05a63d662e8c7924357babbf327b421 ZIPFoundation: 249fa8890597086cd536bb2df5c9804d84e122b0 -PODFILE CHECKSUM: 2fd7b5e045c068b43de83b7f418278e998277687 +PODFILE CHECKSUM: 8d2d893ae2a0c5e160c91ab4cba567307a5b5775 COCOAPODS: 1.9.3 diff --git a/WordPress/Classes/Models/Blog.h b/WordPress/Classes/Models/Blog.h index da0922f06b14..956d1876e09c 100644 --- a/WordPress/Classes/Models/Blog.h +++ b/WordPress/Classes/Models/Blog.h @@ -38,6 +38,8 @@ typedef NS_ENUM(NSUInteger, BlogFeature) { BlogFeatureActivity, /// Does the blog support mentions? BlogFeatureMentions, + /// Does the blog support xposts? + BlogFeatureXposts, /// Does the blog support push notifications? BlogFeaturePushNotifications, /// Does the blog support theme browsing? diff --git a/WordPress/Classes/Models/Blog.m b/WordPress/Classes/Models/Blog.m index d20bae415ba4..78872e8f1181 100644 --- a/WordPress/Classes/Models/Blog.m +++ b/WordPress/Classes/Models/Blog.m @@ -490,6 +490,8 @@ - (BOOL)supports:(BlogFeature)feature return [self isHostedAtWPcom]; case BlogFeatureMentions: return [self isAccessibleThroughWPCom]; + case BlogFeatureXposts: + return [self isAccessibleThroughWPCom]; case BlogFeatureReblog: case BlogFeaturePlans: return [self isHostedAtWPcom] && [self isAdmin]; diff --git a/WordPress/Classes/Models/SiteSuggestion+CoreDataClass.swift b/WordPress/Classes/Models/SiteSuggestion+CoreDataClass.swift index 0d7905a31954..cb0982a30986 100644 --- a/WordPress/Classes/Models/SiteSuggestion+CoreDataClass.swift +++ b/WordPress/Classes/Models/SiteSuggestion+CoreDataClass.swift @@ -10,7 +10,7 @@ enum DecoderError: Error { } @objc(SiteSuggestion) -public class SiteSuggestion: NSManagedObject, Decodable { +public class SiteSuggestion: NSManagedObject, Decodable, Comparable { enum CodingKeys: String, CodingKey { case title = "title" case siteURL = "siteurl" @@ -31,4 +31,8 @@ public class SiteSuggestion: NSManagedObject, Decodable { self.subdomain = try container.decode(String.self, forKey: .subdomain) self.blavatarURL = try container.decode(URL.self, forKey: .blavatarURL) } + + public static func < (lhs: SiteSuggestion, rhs: SiteSuggestion) -> Bool { + return (lhs.title ?? "").localizedCaseInsensitiveCompare(rhs.title ?? "") == .orderedAscending + } } diff --git a/WordPress/Classes/Models/UserSuggestion+CoreDataClass.swift b/WordPress/Classes/Models/UserSuggestion+CoreDataClass.swift index 5426f6547271..0cafc188694b 100644 --- a/WordPress/Classes/Models/UserSuggestion+CoreDataClass.swift +++ b/WordPress/Classes/Models/UserSuggestion+CoreDataClass.swift @@ -2,30 +2,46 @@ import Foundation import CoreData @objc(UserSuggestion) -public class UserSuggestion: NSManagedObject { +public class UserSuggestion: NSManagedObject, Decodable, Comparable { - convenience init?(dictionary: [String: Any], context: NSManagedObjectContext) { - let userLoginValue = dictionary["user_login"] as? String - let displayNameValue = dictionary["display_name"] as? String + enum CodingKeys: String, CodingKey { + case displayName = "display_name" + case imageURL = "image_URL" + case username = "user_login" + } - // A user suggestion is only valid when at least one of these is present. - guard userLoginValue != nil || displayNameValue != nil else { - return nil + required convenience public init(from decoder: Decoder) throws { + guard let managedObjectContext = decoder.userInfo[CodingUserInfoKey.managedObjectContext] as? NSManagedObjectContext else { + throw DecoderError.missingManagedObjectContext } - guard let entityDescription = NSEntityDescription.entity(forEntityName: "UserSuggestion", in: context) else { - return nil - } - self.init(entity: entityDescription, insertInto: context) + self.init(context: managedObjectContext) - username = userLoginValue - displayName = displayNameValue + let container = try decoder.container(keyedBy: CodingKeys.self) + self.displayName = try container.decode(String.self, forKey: .displayName) + self.imageURL = try container.decode(URL.self, forKey: .imageURL) + self.username = try container.decode(String.self, forKey: .username) + } - if let imageURLString = dictionary["image_URL"] as? String { - imageURL = URL(string: imageURLString) - } else { - imageURL = nil - } + public static func < (lhs: UserSuggestion, rhs: UserSuggestion) -> Bool { + return (lhs.displayName ?? "").localizedCaseInsensitiveCompare(rhs.displayName ?? "") == .orderedAscending } +} + +public class UserSuggestionsPayload: Decodable { + let suggestions: [UserSuggestion] + + enum CodingKeys: String, CodingKey { + case suggestions = "suggestions" + } + + public required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let allSuggestions = try container.decode([UserSuggestion].self, forKey: .suggestions) + suggestions = allSuggestions.filter { suggestion in + // A user suggestion is only valid when at least one of these is present. + suggestion.displayName != nil || suggestion.username != nil + } + } } diff --git a/WordPress/Classes/Services/SuggestionService.swift b/WordPress/Classes/Services/SuggestionService.swift index 4c4c50a274ab..183b6d5956f3 100644 --- a/WordPress/Classes/Services/SuggestionService.swift +++ b/WordPress/Classes/Services/SuggestionService.swift @@ -1,99 +1,148 @@ import Foundation -/// A service to fetch and persist a list of users that can be @-mentioned in a post or comment. +/// A service to fetch and persist a list of suggestions, which can be either: +/// - a list of users that can be @-mentioned in a post or comment, or; +/// - a list of sites that can be cross-posted to from within the Gutenberg editor class SuggestionService { - private var blogsCurrentlyBeingRequested = [Blog]() + // Used to keep track of requests made to the suggestions endpoints + struct Request: Equatable { + let type: SuggestionType + let blog: Blog + + static func ==(lhs: Request, rhs: Request) -> Bool { + return lhs.type == rhs.type && lhs.blog == rhs.blog + } + } static let shared = SuggestionService() + private var suggestionsRequested = [Request]() + var requestDates = [Blog: Date]() /** - Fetch cached suggestions if available, otherwise from the network if the device is online. + Fetch cached suggestions if available, otherwise from the network if the device is online. - @param the blog/site to retrieve suggestions for - @param completion callback containing list of suggestions, or nil if unavailable + @param type The type of suggestion + @param blog The blog/site to retrieve suggestions for + @param completion A callback containing list of suggestions, or nil if unavailable */ - func suggestions(for blog: Blog, completion: @escaping ([UserSuggestion]?) -> Void) { + func suggestionsOf(type: SuggestionType, for blog: Blog, completion: (([NSManagedObject]?) -> Void)?) { + + let throttleDuration: TimeInterval = 60 // seconds + if let requestDate = requestDates[blog], Date().timeIntervalSince(requestDate) < throttleDuration { + completion?(nil) + return + } - if let suggestions = retrievePersistedSuggestions(for: blog), suggestions.isEmpty == false { - completion(suggestions) + if let suggestions = retrievePersistedSuggestionsOf(type: type, for: blog), suggestions.isEmpty == false { + completion?(suggestions) } else if ReachabilityUtils.isInternetReachable() { - fetchAndPersistSuggestions(for: blog, completion: completion) + requestDates[blog] = Date() + fetchAndPersistSuggestionsOf(type: type, for: blog, completion: completion) } else { - completion(nil) + completion?(nil) } } /** - Performs a REST API request for the given blog. - Persists response objects to Core Data. - - @param blog/site to retrieve suggestions for + Performs a REST API request to fetch user or site suggestions for the given blog. + Persists response objects to Core Data. + @param type The type of suggestion + @param blog The blog/site to retrieve suggestions for + @param completion A callback containing list of suggestions, or nil if unavailable */ - private func fetchAndPersistSuggestions(for blog: Blog, completion: @escaping ([UserSuggestion]?) -> Void) { + private func fetchAndPersistSuggestionsOf(type: SuggestionType, for blog: Blog, completion: (([NSManagedObject]?) -> Void)?) { + + let request = Request(type: type, blog: blog) // if there is already a request in place for this blog, just wait - guard !blogsCurrentlyBeingRequested.contains(blog) else { return } + guard !suggestionsRequested.contains(request) else { return } - guard let siteID = blog.dotComID else { return } + let path: String + let params: [String: AnyObject] - let suggestPath = "rest/v1.1/users/suggest" - let params = ["site_id": siteID] + switch type { + case .mention: + path = "rest/v1.1/users/suggest" + guard let siteID = blog.dotComID else { + completion?(nil) + return + } + params = [ "site_id": siteID ] + case .xpost: + guard let hostname = blog.hostname else { + completion?(nil) + return + } + path = "/wpcom/v2/sites/\(hostname)/xposts" + params = [ "decode_html": true ] as [String: AnyObject] + } // add this blog to currently being requested list - blogsCurrentlyBeingRequested.append(blog) + suggestionsRequested.append(request) - defaultAccount()?.wordPressComRestApi.GET(suggestPath, parameters: params, success: { [weak self] responseObject, httpResponse in + defaultAccount()?.wordPressComRestApi.GET(path, parameters: params, success: { [weak self] responseObject, httpResponse in guard let `self` = self else { return } - guard let payload = responseObject as? [String: Any] else { return } - guard let restSuggestions = payload["suggestions"] as? [[String: Any]] else { return } - let context = ContextManager.shared.mainContext - - // Delete any existing `UserSuggestion` objects - self.retrievePersistedSuggestions(for: blog)?.forEach { suggestion in - context.delete(suggestion) - } + do { + let context = ContextManager.shared.mainContext + let data = try JSONSerialization.data(withJSONObject: responseObject) + let decoder = JSONDecoder() + decoder.userInfo[CodingUserInfoKey.managedObjectContext] = context - // Create new `UserSuggestion` objects - let suggestions = restSuggestions.compactMap { UserSuggestion(dictionary: $0, context: context) } + try self.purgeManagedObjectsOf(type: type, in: blog, using: context) - // Associate `UserSuggestion` objects with blog - blog.userSuggestions = Set(suggestions) + switch type { + case .mention: + let payload = try decoder.decode(UserSuggestionsPayload.self, from: data) + blog.userSuggestions = Set(payload.suggestions) + case .xpost: + let suggestions = try decoder.decode([SiteSuggestion].self, from: data) + blog.siteSuggestions = Set(suggestions) + } - // Save the changes - try? blog.managedObjectContext?.save() + try ContextManager.shared.mainContext.save() - completion(suggestions) + completion?(self.retrievePersistedSuggestionsOf(type: .xpost, for: blog)) + } catch { + completion?(nil) + } // remove blog from the currently being requested list - self.blogsCurrentlyBeingRequested.removeAll { $0 == blog } + self.suggestionsRequested.removeAll { $0 == request } }, failure: { [weak self] error, _ in guard let `self` = self else { return } - completion(nil) + completion?(nil) // remove blog from the currently being requested list - self.blogsCurrentlyBeingRequested.removeAll { $0 == blog} + self.suggestionsRequested.removeAll { $0 == request} DDLogVerbose("[Rest API] ! \(error.localizedDescription)") }) } /** - Tells the caller if it is a good idea to show suggestions right now for a given blog/site. + Tells the caller if it is a good idea to show suggestions right now for a given blog/site. - @param blog blog/site to check for - @return BOOL Whether the caller should show suggestions + @param type The type of suggestion + @param blog The blog/site to check for + @return BOOL Whether the caller should show suggestions */ - func shouldShowSuggestions(for blog: Blog) -> Bool { + func shouldShowSuggestionsOf(type: SuggestionType, for blog: Blog) -> Bool { // The device must be online or there must be already persisted suggestions - guard ReachabilityUtils.isInternetReachable() || retrievePersistedSuggestions(for: blog)?.isEmpty == false else { - return false + guard ReachabilityUtils.isInternetReachable() || retrievePersistedSuggestionsOf(type: type, for: blog)?.isEmpty == false else { return false } + + switch type { + case .mention: return blog.supports(.mentions) + case .xpost: return blog.supports(.xposts) } + } - return blog.supports(.mentions) + private func purgeManagedObjectsOf(type: SuggestionType, in blog: Blog, using managedObjectContext: NSManagedObjectContext) throws { + retrievePersistedSuggestionsOf(type: type, for: blog)?.forEach { managedObjectContext.delete($0) } + try managedObjectContext.save() } private func defaultAccount() -> WPAccount? { @@ -102,16 +151,18 @@ class SuggestionService { return accountService.defaultWordPressComAccount() } - private func retrievePersistedSuggestions(for blog: Blog) -> [UserSuggestion]? { - guard let suggestions = blog.userSuggestions else { return nil } - return Array(suggestions) + func retrievePersistedSuggestionsOf(type: SuggestionType, for blog: Blog) -> [NSManagedObject]? { + switch type { + case .mention: return blog.userSuggestions?.sorted() + case .xpost: return blog.siteSuggestions?.sorted() + } } /** - Retrieve the persisted blog/site for a given site ID + Retrieve the persisted blog/site for a given site ID. @param siteID the dotComID to retrieve - @return Blog the blog/site + @return The blog/site for the given site ID */ func persistedBlog(for siteID: NSNumber) -> Blog? { let context = ContextManager.shared.mainContext diff --git a/WordPress/Classes/Utility/BuildInformation/FeatureFlag.swift b/WordPress/Classes/Utility/BuildInformation/FeatureFlag.swift index 1da555ce179b..d44ec5f855c3 100644 --- a/WordPress/Classes/Utility/BuildInformation/FeatureFlag.swift +++ b/WordPress/Classes/Utility/BuildInformation/FeatureFlag.swift @@ -9,6 +9,7 @@ enum FeatureFlag: Int, CaseIterable, OverrideableFlag { case swiftCoreData case homepageSettings case gutenbergMentions + case gutenbergXposts case gutenbergModalLayoutPicker case whatIsNew case newNavBarAppearance @@ -37,6 +38,8 @@ enum FeatureFlag: Int, CaseIterable, OverrideableFlag { return true case .gutenbergMentions: return true + case .gutenbergXposts: + return true case .gutenbergModalLayoutPicker: return true case .whatIsNew: @@ -91,6 +94,8 @@ extension FeatureFlag { return "Homepage Settings" case .gutenbergMentions: return "Mentions in Gutenberg" + case .gutenbergXposts: + return "Xposts in Gutenberg" case .gutenbergModalLayoutPicker: return "Gutenberg Modal Layout Picker" case .whatIsNew: diff --git a/WordPress/Classes/ViewRelated/Comments/CommentViewController.swift b/WordPress/Classes/ViewRelated/Comments/CommentViewController.swift index b89bb78b740d..43d5baee1b28 100644 --- a/WordPress/Classes/ViewRelated/Comments/CommentViewController.swift +++ b/WordPress/Classes/ViewRelated/Comments/CommentViewController.swift @@ -3,6 +3,6 @@ import Foundation @objc public extension CommentViewController { func shouldShowSuggestions(for siteID: NSNumber?) -> Bool { guard let siteID = siteID, let blog = SuggestionService.shared.persistedBlog(for: siteID) else { return false } - return SuggestionService.shared.shouldShowSuggestions(for: blog) + return SuggestionService.shared.shouldShowSuggestionsOf(type: .mention, for: blog) } } diff --git a/WordPress/Classes/ViewRelated/Comments/FullScreenCommentReplyViewController.swift b/WordPress/Classes/ViewRelated/Comments/FullScreenCommentReplyViewController.swift index be83e5c66474..01f8d466396d 100644 --- a/WordPress/Classes/ViewRelated/Comments/FullScreenCommentReplyViewController.swift +++ b/WordPress/Classes/ViewRelated/Comments/FullScreenCommentReplyViewController.swift @@ -358,7 +358,7 @@ private extension FullScreenCommentReplyViewController { /// Determine if suggestions are enabled and visible for this site var shouldShowSuggestions: Bool { guard let siteID = self.siteID, let blog = SuggestionService.shared.persistedBlog(for: siteID) else { return false } - return SuggestionService.shared.shouldShowSuggestions(for: blog) + return SuggestionService.shared.shouldShowSuggestionsOf(type: .mention, for: blog) } // This should be moved elsewhere diff --git a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift index 96af353d759e..0870d444c83a 100644 --- a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift +++ b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift @@ -346,6 +346,11 @@ class GutenbergViewController: UIViewController, PostEditor { }, failure: { (error) in DDLogError("Error syncing JETPACK: \(String(describing: error))") }) + + let xpostSuggestions = SuggestionService.shared.retrievePersistedSuggestionsOf(type: .xpost, for: post.blog) + if xpostSuggestions == nil || xpostSuggestions?.isEmpty == true { + SuggestionService.shared.suggestionsOf(type: .xpost, for: post.blog, completion: nil) + } } override func viewWillAppear(_ animated: Bool) { @@ -853,6 +858,12 @@ extension GutenbergViewController: GutenbergBridgeDelegate { }) } + func gutenbergDidRequestXpost(callback: @escaping (Swift.Result) -> Void) { + DispatchQueue.main.async(execute: { [weak self] in + self?.showSuggestions(type: .xpost, callback: callback) + }) + } + func gutenbergDidRequestStarterPageTemplatesTooltipShown() -> Bool { return gutenbergSettings.starterPageTemplatesTooltipShown } @@ -879,6 +890,8 @@ extension GutenbergViewController { return } + guard SuggestionService.shared.shouldShowSuggestionsOf(type: type, for: post.blog) else { return } + previousFirstResponder = view.findFirstResponder() let suggestionsController = GutenbergSuggestionsViewController(siteID: siteID, suggestionType: type) suggestionsController.onCompletion = { (result) in @@ -950,7 +963,8 @@ extension GutenbergViewController: GutenbergBridgeDataSource { func gutenbergCapabilities() -> [Capabilities: Bool] { return [ - .mentions: FeatureFlag.gutenbergMentions.enabled && SuggestionService.shared.shouldShowSuggestions(for: post.blog), + .mentions: FeatureFlag.gutenbergMentions.enabled && post.blog.supports(.mentions), + .xposts: FeatureFlag.gutenbergXposts.enabled && post.blog.supports(.xposts), .unsupportedBlockEditor: isUnsupportedBlockEditorEnabled, .canEnableUnsupportedBlockEditor: post.blog.jetpack?.isConnected ?? false, .modalLayoutPicker: FeatureFlag.gutenbergModalLayoutPicker.enabled, diff --git a/WordPress/Classes/ViewRelated/Notifications/Controllers/NotificationDetailsViewController.swift b/WordPress/Classes/ViewRelated/Notifications/Controllers/NotificationDetailsViewController.swift index bb9d16bc7e7e..3c01342a5f62 100644 --- a/WordPress/Classes/ViewRelated/Notifications/Controllers/NotificationDetailsViewController.swift +++ b/WordPress/Classes/ViewRelated/Notifications/Controllers/NotificationDetailsViewController.swift @@ -548,7 +548,7 @@ private extension NotificationDetailsViewController { let blog = SuggestionService.shared.persistedBlog(for: siteID) else { return false } - return shouldAttachReplyView && SuggestionService.shared.shouldShowSuggestions(for: blog) + return shouldAttachReplyView && SuggestionService.shared.shouldShowSuggestionsOf(type: .mention, for: blog) } } diff --git a/WordPress/Classes/ViewRelated/Reader/ReaderCommentsViewController.swift b/WordPress/Classes/ViewRelated/Reader/ReaderCommentsViewController.swift index 38ae44c4188a..7ad4602791ce 100644 --- a/WordPress/Classes/ViewRelated/Reader/ReaderCommentsViewController.swift +++ b/WordPress/Classes/ViewRelated/Reader/ReaderCommentsViewController.swift @@ -3,6 +3,6 @@ import Foundation @objc public extension ReaderCommentsViewController { func shouldShowSuggestions(for siteID: NSNumber?) -> Bool { guard let siteID = siteID, let blog = SuggestionService.shared.persistedBlog(for: siteID) else { return false } - return SuggestionService.shared.shouldShowSuggestions(for: blog) + return SuggestionService.shared.shouldShowSuggestionsOf(type: .mention, for: blog) } } diff --git a/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.h b/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.h index 9aede401426c..e54a0bbdcb39 100644 --- a/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.h +++ b/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.h @@ -1,7 +1,8 @@ #import typedef NS_CLOSED_ENUM(NSUInteger, SuggestionType) { - SuggestionTypeMention + SuggestionTypeMention, + SuggestionTypeXpost }; @protocol SuggestionsTableViewDelegate; @@ -20,7 +21,7 @@ typedef NS_CLOSED_ENUM(NSUInteger, SuggestionType) { - (nonnull instancetype)initWithSiteID:(NSNumber *_Nullable)siteID suggestionType:(SuggestionType)suggestionType - delegate:(id _Nonnull)suggestionsDelegate; + delegate:(id _Nonnull)suggestionsDelegate NS_DESIGNATED_INITIALIZER; /** Enables or disables the SuggestionsTableView component. diff --git a/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.m b/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.m index 0e695281d65e..52187a4984e1 100644 --- a/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.m +++ b/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.m @@ -319,7 +319,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N - (NSArray *)suggestions { if (!_suggestions && _siteID != nil) { - [self fetchSuggestionsFor:_siteID]; + [self fetchSuggestionsOfType: self.suggestionType for:_siteID]; } return _suggestions; } diff --git a/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.swift b/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.swift index 1ee7b4bbd4ca..cc14740d8d33 100644 --- a/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.swift +++ b/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.swift @@ -4,23 +4,29 @@ extension SuggestionType { var trigger: String { switch self { case .mention: return "@" + case .xpost: return "+" } } } @objc public extension SuggestionsTableView { - func suggestions(for siteID: NSNumber, completion: @escaping ([UserSuggestion]?) -> Void) { + var suggestionTrigger: String { return suggestionType.trigger } + + func fetchSuggestionsOf(type: SuggestionType, for siteID: NSNumber) { guard let blog = SuggestionService.shared.persistedBlog(for: siteID) else { return } - SuggestionService.shared.suggestions(for: blog, completion: completion) + SuggestionService.shared.suggestionsOf(type: type, for: blog, completion: { suggestions in + self.suggestions = suggestions + self.showSuggestions(forWord: self.searchText) + }) } - var suggestionTrigger: String { return suggestionType.trigger } - func predicate(for searchQuery: String) -> NSPredicate { switch suggestionType { case .mention: - return NSPredicate(format: "(displayName contains[c] %@) OR (username contains[c] %@)", searchQuery, searchQuery) + return NSPredicate(format: "(displayName contains[cd] %@) OR (username contains[cd] %@)", searchQuery, searchQuery) + case .xpost: + return NSPredicate(format: "(title contains[cd] %@) OR (siteURL.absoluteString contains[cd] %@)", searchQuery, searchQuery) } } @@ -29,7 +35,10 @@ extension SuggestionType { switch (suggestionType, suggestion) { case (.mention, let suggestion as UserSuggestion): title = suggestion.username - default: title = nil + case (.xpost, let suggestion as SiteSuggestion): + title = suggestion.subdomain + default: + return nil } return title.map { suggestionType.trigger.appending($0) } } @@ -38,22 +47,29 @@ extension SuggestionType { switch (suggestionType, suggestion) { case (.mention, let suggestion as UserSuggestion): return suggestion.displayName - default: return nil + case (.xpost, let suggestion as SiteSuggestion): + return suggestion.title + default: + return nil } } private func imageURLForSuggestion(at indexPath: IndexPath) -> URL? { let suggestion = searchResults[indexPath.row] - switch (suggestionType, suggestion) { case (.mention, let suggestion as UserSuggestion): return suggestion.imageURL - default: return nil + case (.xpost, let suggestion as SiteSuggestion): + return suggestion.blavatarURL + default: + return nil } } func loadImage(for suggestion: AnyObject, in cell: SuggestionsTableViewCell, at indexPath: IndexPath) { + cell.iconImageView.image = UIImage(named: "gravatar") + guard let imageURL = imageURLForSuggestion(at: indexPath) else { return } cell.imageDownloadHash = imageURL.hashValue @@ -65,27 +81,18 @@ extension SuggestionType { } } - func fetchSuggestions(for siteID: NSNumber) { - switch self.suggestionType { - case .mention: - suggestions(for: siteID) { userSuggestions in - self.suggestions = userSuggestions - self.showSuggestions(forWord: self.searchText) - } - } - } - private func suggestionText(for suggestion: Any) -> String? { switch (suggestionType, suggestion) { case (.mention, let suggestion as UserSuggestion): return suggestion.username + case (.xpost, let suggestion as SiteSuggestion): + return suggestion.title default: return nil } } private func retrieveIcon(for imageURL: URL?, success: @escaping (UIImage?) -> Void) { let imageSize = CGSize(width: SuggestionsTableViewCellIconSize, height: SuggestionsTableViewCellIconSize) - if let image = cachedIcon(for: imageURL, with: imageSize) { success(image) } else { diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 372dc774a6d0..2ea36b5aaa30 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -5713,7 +5713,7 @@ path = GutenbergWeb; sourceTree = ""; }; - 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + 29B97314FDCFA39411CA2CEA = { isa = PBXGroup; children = ( 98D31BBF239720E4009CFF43 /* MainInterface.storyboard */, @@ -11371,7 +11371,7 @@ bg, sk, ); - mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + mainGroup = 29B97314FDCFA39411CA2CEA; productRefGroup = 19C28FACFE9D520D11CA2CBB /* Products */; projectDirPath = ""; projectRoot = ""; From 6b523b5f03c165db16b583cb6558f1277e7b3982 Mon Sep 17 00:00:00 2001 From: Paul Von Schrottky Date: Thu, 17 Dec 2020 20:41:19 -0300 Subject: [PATCH 2/4] Updated Gutenberg Pod --- Podfile | 2 +- Podfile.lock | 174 +++++++++++++++++++++++++-------------------------- 2 files changed, 88 insertions(+), 88 deletions(-) diff --git a/Podfile b/Podfile index 40728a1a0fd6..e35053c65ca3 100644 --- a/Podfile +++ b/Podfile @@ -154,7 +154,7 @@ target 'WordPress' do ## Gutenberg (React Native) ## ===================== ## - gutenberg :tag => 'v1.43.0' + gutenberg :commit => '0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d' ## Third party libraries ## ===================== diff --git a/Podfile.lock b/Podfile.lock index 727f15b92982..63f5ca5b84e4 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -73,7 +73,7 @@ PODS: - GTMSessionFetcher/Core (1.5.0) - GTMSessionFetcher/Full (1.5.0): - GTMSessionFetcher/Core (= 1.5.0) - - Gutenberg (1.43.0): + - Gutenberg (1.42.1): - React (= 0.61.5) - React-CoreModules (= 0.61.5) - React-RCTImage (= 0.61.5) @@ -381,7 +381,7 @@ PODS: - React - RNSVG (9.13.6-gb): - React - - RNTAztecView (1.43.0): + - RNTAztecView (1.42.1): - React-Core - WordPress-Aztec-iOS (~> 1.19.3) - Sentry (4.5.0): @@ -449,14 +449,14 @@ DEPENDENCIES: - CocoaLumberjack (~> 3.0) - CropViewController (= 2.5.3) - Down (~> 0.6.6) - - FBLazyVector (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/FBLazyVector.podspec.json`) - - FBReactNativeSpec (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/FBReactNativeSpec.podspec.json`) - - Folly (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/Folly.podspec.json`) + - FBLazyVector (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/FBLazyVector.podspec.json`) + - FBReactNativeSpec (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/FBReactNativeSpec.podspec.json`) + - Folly (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/Folly.podspec.json`) - 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/v1.43.0/third-party-podspecs/glog.podspec.json`) + - glog (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/glog.podspec.json`) - Gridicons (~> 1.1.0-beta) - - Gutenberg (from `http://github.com/wordpress-mobile/gutenberg-mobile/`, tag `v1.43.0`) + - Gutenberg (from `http://github.com/wordpress-mobile/gutenberg-mobile/`, commit `0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d`) - JTAppleCalendar (~> 8.0.2) - MediaEditor (~> 1.2.1) - MRProgress (= 0.8.3) @@ -466,41 +466,41 @@ DEPENDENCIES: - OCMock (= 3.4.3) - OHHTTPStubs (= 6.1.0) - OHHTTPStubs/Swift (= 6.1.0) - - RCTRequired (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RCTRequired.podspec.json`) - - RCTTypeSafety (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RCTTypeSafety.podspec.json`) + - RCTRequired (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/RCTRequired.podspec.json`) + - RCTTypeSafety (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/RCTTypeSafety.podspec.json`) - Reachability (= 3.2) - - React (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React.podspec.json`) - - React-Core (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-Core.podspec.json`) - - React-CoreModules (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-CoreModules.podspec.json`) - - React-cxxreact (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-cxxreact.podspec.json`) - - React-jsi (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-jsi.podspec.json`) - - React-jsiexecutor (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-jsiexecutor.podspec.json`) - - React-jsinspector (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-jsinspector.podspec.json`) - - react-native-blur (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-blur.podspec.json`) - - react-native-get-random-values (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-get-random-values.podspec.json`) - - react-native-keyboard-aware-scroll-view (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-keyboard-aware-scroll-view.podspec.json`) - - react-native-linear-gradient (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-linear-gradient.podspec.json`) - - react-native-safe-area (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-safe-area.podspec.json`) - - react-native-safe-area-context (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-safe-area-context.podspec.json`) - - react-native-slider (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-slider.podspec.json`) - - react-native-video (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-video.podspec.json`) - - React-RCTActionSheet (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTActionSheet.podspec.json`) - - React-RCTAnimation (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTAnimation.podspec.json`) - - React-RCTBlob (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTBlob.podspec.json`) - - React-RCTImage (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTImage.podspec.json`) - - React-RCTLinking (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTLinking.podspec.json`) - - React-RCTNetwork (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTNetwork.podspec.json`) - - React-RCTSettings (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTSettings.podspec.json`) - - React-RCTText (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTText.podspec.json`) - - React-RCTVibration (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTVibration.podspec.json`) - - ReactCommon (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/ReactCommon.podspec.json`) - - ReactNativeDarkMode (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/ReactNativeDarkMode.podspec.json`) - - RNCMaskedView (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNCMaskedView.podspec.json`) - - RNGestureHandler (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNGestureHandler.podspec.json`) - - RNReanimated (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNReanimated.podspec.json`) - - RNScreens (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNScreens.podspec.json`) - - RNSVG (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNSVG.podspec.json`) - - RNTAztecView (from `http://github.com/wordpress-mobile/gutenberg-mobile/`, tag `v1.43.0`) + - React (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React.podspec.json`) + - React-Core (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-Core.podspec.json`) + - React-CoreModules (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-CoreModules.podspec.json`) + - React-cxxreact (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-cxxreact.podspec.json`) + - React-jsi (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-jsi.podspec.json`) + - React-jsiexecutor (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-jsiexecutor.podspec.json`) + - React-jsinspector (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-jsinspector.podspec.json`) + - react-native-blur (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/react-native-blur.podspec.json`) + - react-native-get-random-values (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/react-native-get-random-values.podspec.json`) + - react-native-keyboard-aware-scroll-view (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/react-native-keyboard-aware-scroll-view.podspec.json`) + - react-native-linear-gradient (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/react-native-linear-gradient.podspec.json`) + - react-native-safe-area (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/react-native-safe-area.podspec.json`) + - react-native-safe-area-context (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/react-native-safe-area-context.podspec.json`) + - react-native-slider (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/react-native-slider.podspec.json`) + - react-native-video (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/react-native-video.podspec.json`) + - React-RCTActionSheet (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTActionSheet.podspec.json`) + - React-RCTAnimation (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTAnimation.podspec.json`) + - React-RCTBlob (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTBlob.podspec.json`) + - React-RCTImage (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTImage.podspec.json`) + - React-RCTLinking (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTLinking.podspec.json`) + - React-RCTNetwork (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTNetwork.podspec.json`) + - React-RCTSettings (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTSettings.podspec.json`) + - React-RCTText (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTText.podspec.json`) + - React-RCTVibration (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTVibration.podspec.json`) + - ReactCommon (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/ReactCommon.podspec.json`) + - ReactNativeDarkMode (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/ReactNativeDarkMode.podspec.json`) + - RNCMaskedView (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/RNCMaskedView.podspec.json`) + - RNGestureHandler (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/RNGestureHandler.podspec.json`) + - RNReanimated (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/RNReanimated.podspec.json`) + - RNScreens (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/RNScreens.podspec.json`) + - RNSVG (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/RNSVG.podspec.json`) + - RNTAztecView (from `http://github.com/wordpress-mobile/gutenberg-mobile/`, commit `0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d`) - Starscream (= 3.0.6) - SVProgressHUD (= 2.2.5) - WordPress-Editor-iOS (~> 1.19.3) @@ -510,7 +510,7 @@ DEPENDENCIES: - WordPressShared (~> 1.14.0-beta.1) - WordPressUI (~> 1.9.0-beta.1) - WPMediaPicker (~> 1.7.2) - - Yoga (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/Yoga.podspec.json`) + - Yoga (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/Yoga.podspec.json`) - ZendeskSupportSDK (= 5.1.1) - ZIPFoundation (~> 0.9.8) @@ -571,108 +571,108 @@ SPEC REPOS: EXTERNAL SOURCES: FBLazyVector: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/FBLazyVector.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/FBLazyVector.podspec.json FBReactNativeSpec: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/FBReactNativeSpec.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/FBReactNativeSpec.podspec.json Folly: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/Folly.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/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/v1.43.0/third-party-podspecs/glog.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/glog.podspec.json Gutenberg: + :commit: 0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d :git: http://github.com/wordpress-mobile/gutenberg-mobile/ :submodules: true - :tag: v1.43.0 RCTRequired: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RCTRequired.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/RCTRequired.podspec.json RCTTypeSafety: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RCTTypeSafety.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/RCTTypeSafety.podspec.json React: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React.podspec.json React-Core: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-Core.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-Core.podspec.json React-CoreModules: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-CoreModules.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-CoreModules.podspec.json React-cxxreact: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-cxxreact.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-cxxreact.podspec.json React-jsi: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-jsi.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-jsi.podspec.json React-jsiexecutor: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-jsiexecutor.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-jsiexecutor.podspec.json React-jsinspector: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-jsinspector.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-jsinspector.podspec.json react-native-blur: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-blur.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/react-native-blur.podspec.json react-native-get-random-values: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-get-random-values.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/react-native-get-random-values.podspec.json react-native-keyboard-aware-scroll-view: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-keyboard-aware-scroll-view.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/react-native-keyboard-aware-scroll-view.podspec.json react-native-linear-gradient: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-linear-gradient.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/react-native-linear-gradient.podspec.json react-native-safe-area: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-safe-area.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/react-native-safe-area.podspec.json react-native-safe-area-context: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-safe-area-context.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/react-native-safe-area-context.podspec.json react-native-slider: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-slider.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/react-native-slider.podspec.json react-native-video: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-video.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/react-native-video.podspec.json React-RCTActionSheet: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTActionSheet.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTActionSheet.podspec.json React-RCTAnimation: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTAnimation.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTAnimation.podspec.json React-RCTBlob: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTBlob.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTBlob.podspec.json React-RCTImage: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTImage.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTImage.podspec.json React-RCTLinking: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTLinking.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTLinking.podspec.json React-RCTNetwork: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTNetwork.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTNetwork.podspec.json React-RCTSettings: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTSettings.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTSettings.podspec.json React-RCTText: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTText.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTText.podspec.json React-RCTVibration: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTVibration.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/React-RCTVibration.podspec.json ReactCommon: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/ReactCommon.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/ReactCommon.podspec.json ReactNativeDarkMode: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/ReactNativeDarkMode.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/ReactNativeDarkMode.podspec.json RNCMaskedView: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNCMaskedView.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/RNCMaskedView.podspec.json RNGestureHandler: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNGestureHandler.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/RNGestureHandler.podspec.json RNReanimated: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNReanimated.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/RNReanimated.podspec.json RNScreens: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNScreens.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/RNScreens.podspec.json RNSVG: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNSVG.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/RNSVG.podspec.json RNTAztecView: + :commit: 0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d :git: http://github.com/wordpress-mobile/gutenberg-mobile/ :submodules: true - :tag: v1.43.0 Yoga: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/Yoga.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d/third-party-podspecs/Yoga.podspec.json CHECKOUT OPTIONS: FSInteractiveMap: :git: https://github.com/wordpress-mobile/FSInteractiveMap.git :tag: 0.2.0 Gutenberg: + :commit: 0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d :git: http://github.com/wordpress-mobile/gutenberg-mobile/ :submodules: true - :tag: v1.43.0 RNCMaskedView: :commit: d4ccf2bba163679c4550ce6ba0119604cd5e6379 :git: https://github.com/react-native-community/react-native-masked-view.git RNTAztecView: + :commit: 0a1d378b4fa8b9d23e545fd76c5e7227f098bc2d :git: http://github.com/wordpress-mobile/gutenberg-mobile/ :submodules: true - :tag: v1.43.0 SPEC CHECKSUMS: 1PasswordExtension: f97cc80ae58053c331b2b6dc8843ba7103b33794 @@ -700,7 +700,7 @@ SPEC CHECKSUMS: Gridicons: e70ede7ab55f21ee8f5b21f7c8fe196a42f4958e GTMAppAuth: 197a8dabfea5d665224aa00d17f164fc2248dab9 GTMSessionFetcher: b3503b20a988c4e20cc189aa798fd18220133f52 - Gutenberg: 22950a4deedc6da729cbfdad88138404b49d5e69 + Gutenberg: 92e69c9de45c43ac22c9039913939e61c43d0da6 JTAppleCalendar: 932cadea40b1051beab10f67843451d48ba16c99 lottie-ios: 85ce835dd8c53e02509f20729fc7d6a4e6645a0a MediaEditor: 20cdeb46bdecd040b8bc94467ac85a52b53b193a @@ -744,7 +744,7 @@ SPEC CHECKSUMS: RNReanimated: 13f7a6a22667c4f00aac217bc66f94e8560b3d59 RNScreens: 6833ac5c29cf2f03eed12103140530bbd75b6aea RNSVG: 68a534a5db06dcbdaebfd5079349191598caef7b - RNTAztecView: 9260b3824459ae1cc31b3595cd08ab9fc2332c4c + RNTAztecView: a2053fcfd76527819c04456a455f82061330cd80 Sentry: ab6c209f23700d1460691dbc90e19ed0a05d496b Sodium: 63c0ca312a932e6da481689537d4b35568841bdc Starscream: ef3ece99d765eeccb67de105bfa143f929026cf5 @@ -769,6 +769,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: dcb2596ad05a63d662e8c7924357babbf327b421 ZIPFoundation: 249fa8890597086cd536bb2df5c9804d84e122b0 -PODFILE CHECKSUM: bb0dd1f7ed9434cbad3b6cfc0a46ad920b07c429 +PODFILE CHECKSUM: 8eaae4e2c2f9dcf710dde65051cb044dc2acb588 COCOAPODS: 1.10.0 From d32bd4f6aa2c70d27a81f009e6a154ff48ece665 Mon Sep 17 00:00:00 2001 From: Paul Von Schrottky Date: Tue, 22 Dec 2020 10:31:34 -0300 Subject: [PATCH 3/4] Fix infinite loop when adding suggestions The use of the suggestions property getter to trigger the call to the API was prone to issues including multiple calls to the API being made instead of just one. --- .../ViewRelated/Suggestions/SuggestionsTableView.h | 2 +- .../ViewRelated/Suggestions/SuggestionsTableView.m | 12 ++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.h b/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.h index e54a0bbdcb39..7ffe5493b159 100644 --- a/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.h +++ b/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.h @@ -21,7 +21,7 @@ typedef NS_CLOSED_ENUM(NSUInteger, SuggestionType) { - (nonnull instancetype)initWithSiteID:(NSNumber *_Nullable)siteID suggestionType:(SuggestionType)suggestionType - delegate:(id _Nonnull)suggestionsDelegate NS_DESIGNATED_INITIALIZER; + delegate:(id _Nonnull)suggestionsDelegate; /** Enables or disables the SuggestionsTableView component. diff --git a/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.m b/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.m index 52187a4984e1..c2b911d52e5f 100644 --- a/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.m +++ b/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.m @@ -41,6 +41,8 @@ - (instancetype)initWithSiteID:(NSNumber *)siteID [self setupTableView]; [self setupConstraints]; [self startObservingNotifications]; + + [self fetchSuggestionsOfType: self.suggestionType for:_siteID]; } return self; } @@ -314,14 +316,4 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N return cell; } -#pragma mark - Suggestion list management - -- (NSArray *)suggestions -{ - if (!_suggestions && _siteID != nil) { - [self fetchSuggestionsOfType: self.suggestionType for:_siteID]; - } - return _suggestions; -} - @end From 4ad27f65e33786cab5533261dc808cdad61f0fc0 Mon Sep 17 00:00:00 2001 From: Paul Von Schrottky Date: Wed, 23 Dec 2020 10:02:56 -0300 Subject: [PATCH 4/4] Added a first-iteration implementation of Xposts --- Podfile | 2 +- Podfile.lock | 166 +++++++++--------- RELEASE-NOTES.txt | 1 + .../Models/SiteSuggestion+CoreDataClass.swift | 6 +- .../Models/UserSuggestion+CoreDataClass.swift | 52 ++---- .../Services/SiteSuggestionService.swift | 119 +++++++++++++ .../Classes/Services/SuggestionService.swift | 153 ++++++---------- .../Comments/CommentViewController.swift | 2 +- ...FullScreenCommentReplyViewController.swift | 2 +- .../Gutenberg/GutenbergViewController.swift | 16 +- .../NotificationDetailsViewController.swift | 2 +- .../Reader/ReaderCommentsViewController.swift | 2 +- .../Suggestions/SuggestionsTableView.m | 12 +- .../Suggestions/SuggestionsTableView.swift | 35 +++- WordPress/WordPress.xcodeproj/project.pbxproj | 8 +- 15 files changed, 328 insertions(+), 250 deletions(-) create mode 100644 WordPress/Classes/Services/SiteSuggestionService.swift diff --git a/Podfile b/Podfile index c1b7dba4a735..681bdd2f20c9 100644 --- a/Podfile +++ b/Podfile @@ -154,7 +154,7 @@ target 'WordPress' do ## Gutenberg (React Native) ## ===================== ## - gutenberg :tag => 'v1.43.0' + gutenberg :tag => 'v1.44.0-alpha1' ## Third party libraries ## ===================== diff --git a/Podfile.lock b/Podfile.lock index 73bf8b667b40..1ba96efe6f9b 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -449,14 +449,14 @@ DEPENDENCIES: - CocoaLumberjack (~> 3.0) - CropViewController (= 2.5.3) - Down (~> 0.6.6) - - FBLazyVector (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/FBLazyVector.podspec.json`) - - FBReactNativeSpec (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/FBReactNativeSpec.podspec.json`) - - Folly (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/Folly.podspec.json`) + - FBLazyVector (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/FBLazyVector.podspec.json`) + - FBReactNativeSpec (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/FBReactNativeSpec.podspec.json`) + - Folly (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/Folly.podspec.json`) - 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/v1.43.0/third-party-podspecs/glog.podspec.json`) + - glog (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/glog.podspec.json`) - Gridicons (~> 1.1.0) - - Gutenberg (from `http://github.com/wordpress-mobile/gutenberg-mobile/`, tag `v1.43.0`) + - Gutenberg (from `http://github.com/wordpress-mobile/gutenberg-mobile/`, tag `v1.44.0-alpha1`) - JTAppleCalendar (~> 8.0.2) - MediaEditor (~> 1.2.1) - MRProgress (= 0.8.3) @@ -466,41 +466,41 @@ DEPENDENCIES: - OCMock (= 3.4.3) - OHHTTPStubs (= 6.1.0) - OHHTTPStubs/Swift (= 6.1.0) - - RCTRequired (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RCTRequired.podspec.json`) - - RCTTypeSafety (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RCTTypeSafety.podspec.json`) + - RCTRequired (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/RCTRequired.podspec.json`) + - RCTTypeSafety (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/RCTTypeSafety.podspec.json`) - Reachability (= 3.2) - - React (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React.podspec.json`) - - React-Core (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-Core.podspec.json`) - - React-CoreModules (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-CoreModules.podspec.json`) - - React-cxxreact (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-cxxreact.podspec.json`) - - React-jsi (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-jsi.podspec.json`) - - React-jsiexecutor (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-jsiexecutor.podspec.json`) - - React-jsinspector (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-jsinspector.podspec.json`) - - react-native-blur (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-blur.podspec.json`) - - react-native-get-random-values (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-get-random-values.podspec.json`) - - react-native-keyboard-aware-scroll-view (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-keyboard-aware-scroll-view.podspec.json`) - - react-native-linear-gradient (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-linear-gradient.podspec.json`) - - react-native-safe-area (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-safe-area.podspec.json`) - - react-native-safe-area-context (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-safe-area-context.podspec.json`) - - react-native-slider (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-slider.podspec.json`) - - react-native-video (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-video.podspec.json`) - - React-RCTActionSheet (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTActionSheet.podspec.json`) - - React-RCTAnimation (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTAnimation.podspec.json`) - - React-RCTBlob (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTBlob.podspec.json`) - - React-RCTImage (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTImage.podspec.json`) - - React-RCTLinking (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTLinking.podspec.json`) - - React-RCTNetwork (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTNetwork.podspec.json`) - - React-RCTSettings (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTSettings.podspec.json`) - - React-RCTText (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTText.podspec.json`) - - React-RCTVibration (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTVibration.podspec.json`) - - ReactCommon (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/ReactCommon.podspec.json`) - - ReactNativeDarkMode (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/ReactNativeDarkMode.podspec.json`) - - RNCMaskedView (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNCMaskedView.podspec.json`) - - RNGestureHandler (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNGestureHandler.podspec.json`) - - RNReanimated (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNReanimated.podspec.json`) - - RNScreens (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNScreens.podspec.json`) - - RNSVG (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNSVG.podspec.json`) - - RNTAztecView (from `http://github.com/wordpress-mobile/gutenberg-mobile/`, tag `v1.43.0`) + - React (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React.podspec.json`) + - React-Core (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-Core.podspec.json`) + - React-CoreModules (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-CoreModules.podspec.json`) + - React-cxxreact (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-cxxreact.podspec.json`) + - React-jsi (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-jsi.podspec.json`) + - React-jsiexecutor (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-jsiexecutor.podspec.json`) + - React-jsinspector (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-jsinspector.podspec.json`) + - react-native-blur (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/react-native-blur.podspec.json`) + - react-native-get-random-values (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/react-native-get-random-values.podspec.json`) + - react-native-keyboard-aware-scroll-view (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/react-native-keyboard-aware-scroll-view.podspec.json`) + - react-native-linear-gradient (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/react-native-linear-gradient.podspec.json`) + - react-native-safe-area (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/react-native-safe-area.podspec.json`) + - react-native-safe-area-context (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/react-native-safe-area-context.podspec.json`) + - react-native-slider (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/react-native-slider.podspec.json`) + - react-native-video (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/react-native-video.podspec.json`) + - React-RCTActionSheet (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTActionSheet.podspec.json`) + - React-RCTAnimation (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTAnimation.podspec.json`) + - React-RCTBlob (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTBlob.podspec.json`) + - React-RCTImage (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTImage.podspec.json`) + - React-RCTLinking (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTLinking.podspec.json`) + - React-RCTNetwork (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTNetwork.podspec.json`) + - React-RCTSettings (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTSettings.podspec.json`) + - React-RCTText (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTText.podspec.json`) + - React-RCTVibration (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTVibration.podspec.json`) + - ReactCommon (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/ReactCommon.podspec.json`) + - ReactNativeDarkMode (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/ReactNativeDarkMode.podspec.json`) + - RNCMaskedView (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/RNCMaskedView.podspec.json`) + - RNGestureHandler (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/RNGestureHandler.podspec.json`) + - RNReanimated (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/RNReanimated.podspec.json`) + - RNScreens (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/RNScreens.podspec.json`) + - RNSVG (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/RNSVG.podspec.json`) + - RNTAztecView (from `http://github.com/wordpress-mobile/gutenberg-mobile/`, tag `v1.44.0-alpha1`) - Starscream (= 3.0.6) - SVProgressHUD (= 2.2.5) - WordPress-Editor-iOS (~> 1.19.3) @@ -510,7 +510,7 @@ DEPENDENCIES: - WordPressShared (~> 1.14.0) - WordPressUI (~> 1.9.0-beta.2) - WPMediaPicker (~> 1.7.2) - - Yoga (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/Yoga.podspec.json`) + - Yoga (from `https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/Yoga.podspec.json`) - ZendeskSupportSDK (= 5.1.1) - ZIPFoundation (~> 0.9.8) @@ -571,92 +571,92 @@ SPEC REPOS: EXTERNAL SOURCES: FBLazyVector: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/FBLazyVector.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/FBLazyVector.podspec.json FBReactNativeSpec: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/FBReactNativeSpec.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/FBReactNativeSpec.podspec.json Folly: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/Folly.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/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/v1.43.0/third-party-podspecs/glog.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/glog.podspec.json Gutenberg: :git: http://github.com/wordpress-mobile/gutenberg-mobile/ :submodules: true - :tag: v1.43.0 + :tag: v1.44.0-alpha1 RCTRequired: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RCTRequired.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/RCTRequired.podspec.json RCTTypeSafety: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RCTTypeSafety.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/RCTTypeSafety.podspec.json React: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React.podspec.json React-Core: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-Core.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-Core.podspec.json React-CoreModules: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-CoreModules.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-CoreModules.podspec.json React-cxxreact: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-cxxreact.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-cxxreact.podspec.json React-jsi: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-jsi.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-jsi.podspec.json React-jsiexecutor: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-jsiexecutor.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-jsiexecutor.podspec.json React-jsinspector: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-jsinspector.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-jsinspector.podspec.json react-native-blur: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-blur.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/react-native-blur.podspec.json react-native-get-random-values: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-get-random-values.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/react-native-get-random-values.podspec.json react-native-keyboard-aware-scroll-view: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-keyboard-aware-scroll-view.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/react-native-keyboard-aware-scroll-view.podspec.json react-native-linear-gradient: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-linear-gradient.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/react-native-linear-gradient.podspec.json react-native-safe-area: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-safe-area.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/react-native-safe-area.podspec.json react-native-safe-area-context: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-safe-area-context.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/react-native-safe-area-context.podspec.json react-native-slider: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-slider.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/react-native-slider.podspec.json react-native-video: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/react-native-video.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/react-native-video.podspec.json React-RCTActionSheet: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTActionSheet.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTActionSheet.podspec.json React-RCTAnimation: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTAnimation.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTAnimation.podspec.json React-RCTBlob: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTBlob.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTBlob.podspec.json React-RCTImage: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTImage.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTImage.podspec.json React-RCTLinking: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTLinking.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTLinking.podspec.json React-RCTNetwork: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTNetwork.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTNetwork.podspec.json React-RCTSettings: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTSettings.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTSettings.podspec.json React-RCTText: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTText.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTText.podspec.json React-RCTVibration: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/React-RCTVibration.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/React-RCTVibration.podspec.json ReactCommon: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/ReactCommon.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/ReactCommon.podspec.json ReactNativeDarkMode: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/ReactNativeDarkMode.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/ReactNativeDarkMode.podspec.json RNCMaskedView: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNCMaskedView.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/RNCMaskedView.podspec.json RNGestureHandler: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNGestureHandler.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/RNGestureHandler.podspec.json RNReanimated: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNReanimated.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/RNReanimated.podspec.json RNScreens: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNScreens.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/RNScreens.podspec.json RNSVG: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/RNSVG.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/RNSVG.podspec.json RNTAztecView: :git: http://github.com/wordpress-mobile/gutenberg-mobile/ :submodules: true - :tag: v1.43.0 + :tag: v1.44.0-alpha1 Yoga: - :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.43.0/third-party-podspecs/Yoga.podspec.json + :podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.44.0-alpha1/third-party-podspecs/Yoga.podspec.json CHECKOUT OPTIONS: FSInteractiveMap: @@ -665,14 +665,14 @@ CHECKOUT OPTIONS: Gutenberg: :git: http://github.com/wordpress-mobile/gutenberg-mobile/ :submodules: true - :tag: v1.43.0 + :tag: v1.44.0-alpha1 RNCMaskedView: :commit: d4ccf2bba163679c4550ce6ba0119604cd5e6379 :git: https://github.com/react-native-community/react-native-masked-view.git RNTAztecView: :git: http://github.com/wordpress-mobile/gutenberg-mobile/ :submodules: true - :tag: v1.43.0 + :tag: v1.44.0-alpha1 SPEC CHECKSUMS: 1PasswordExtension: f97cc80ae58053c331b2b6dc8843ba7103b33794 @@ -769,6 +769,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: dcb2596ad05a63d662e8c7924357babbf327b421 ZIPFoundation: 249fa8890597086cd536bb2df5c9804d84e122b0 -PODFILE CHECKSUM: ce7bc2e4ca3cb5ad33824ee53d47480fea6797ac +PODFILE CHECKSUM: 70a8096d523b5b41cdb29d6a2190afe9872dfe4f COCOAPODS: 1.10.0 diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 45692f2fe80d..1dc41bc3f222 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -6,6 +6,7 @@ * [**] Choose a Domain will now return more options in the search results, sort the results to have exact matches first, and let you know if no exact matches were found. [#15482] * [**] Page List: Adds duplicate page functionality [#15515] * [*] Invite People: add link to user roles definition web page. [#15530] +* [***] Block Editor: Cross-post suggestions are now available by typing the + character (or long-pressing the toolbar button labelled with an @-symbol) in a post on a P2 site [#15139] 16.4 ----- diff --git a/WordPress/Classes/Models/SiteSuggestion+CoreDataClass.swift b/WordPress/Classes/Models/SiteSuggestion+CoreDataClass.swift index cb0982a30986..0d7905a31954 100644 --- a/WordPress/Classes/Models/SiteSuggestion+CoreDataClass.swift +++ b/WordPress/Classes/Models/SiteSuggestion+CoreDataClass.swift @@ -10,7 +10,7 @@ enum DecoderError: Error { } @objc(SiteSuggestion) -public class SiteSuggestion: NSManagedObject, Decodable, Comparable { +public class SiteSuggestion: NSManagedObject, Decodable { enum CodingKeys: String, CodingKey { case title = "title" case siteURL = "siteurl" @@ -31,8 +31,4 @@ public class SiteSuggestion: NSManagedObject, Decodable, Comparable { self.subdomain = try container.decode(String.self, forKey: .subdomain) self.blavatarURL = try container.decode(URL.self, forKey: .blavatarURL) } - - public static func < (lhs: SiteSuggestion, rhs: SiteSuggestion) -> Bool { - return (lhs.title ?? "").localizedCaseInsensitiveCompare(rhs.title ?? "") == .orderedAscending - } } diff --git a/WordPress/Classes/Models/UserSuggestion+CoreDataClass.swift b/WordPress/Classes/Models/UserSuggestion+CoreDataClass.swift index 0cafc188694b..5426f6547271 100644 --- a/WordPress/Classes/Models/UserSuggestion+CoreDataClass.swift +++ b/WordPress/Classes/Models/UserSuggestion+CoreDataClass.swift @@ -2,46 +2,30 @@ import Foundation import CoreData @objc(UserSuggestion) -public class UserSuggestion: NSManagedObject, Decodable, Comparable { +public class UserSuggestion: NSManagedObject { - enum CodingKeys: String, CodingKey { - case displayName = "display_name" - case imageURL = "image_URL" - case username = "user_login" - } + convenience init?(dictionary: [String: Any], context: NSManagedObjectContext) { + let userLoginValue = dictionary["user_login"] as? String + let displayNameValue = dictionary["display_name"] as? String - required convenience public init(from decoder: Decoder) throws { - guard let managedObjectContext = decoder.userInfo[CodingUserInfoKey.managedObjectContext] as? NSManagedObjectContext else { - throw DecoderError.missingManagedObjectContext + // A user suggestion is only valid when at least one of these is present. + guard userLoginValue != nil || displayNameValue != nil else { + return nil } - self.init(context: managedObjectContext) - - let container = try decoder.container(keyedBy: CodingKeys.self) - self.displayName = try container.decode(String.self, forKey: .displayName) - self.imageURL = try container.decode(URL.self, forKey: .imageURL) - self.username = try container.decode(String.self, forKey: .username) - } - - public static func < (lhs: UserSuggestion, rhs: UserSuggestion) -> Bool { - return (lhs.displayName ?? "").localizedCaseInsensitiveCompare(rhs.displayName ?? "") == .orderedAscending - } -} - -public class UserSuggestionsPayload: Decodable { + guard let entityDescription = NSEntityDescription.entity(forEntityName: "UserSuggestion", in: context) else { + return nil + } + self.init(entity: entityDescription, insertInto: context) - let suggestions: [UserSuggestion] + username = userLoginValue + displayName = displayNameValue - enum CodingKeys: String, CodingKey { - case suggestions = "suggestions" - } - - public required init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let allSuggestions = try container.decode([UserSuggestion].self, forKey: .suggestions) - suggestions = allSuggestions.filter { suggestion in - // A user suggestion is only valid when at least one of these is present. - suggestion.displayName != nil || suggestion.username != nil + if let imageURLString = dictionary["image_URL"] as? String { + imageURL = URL(string: imageURLString) + } else { + imageURL = nil } } + } diff --git a/WordPress/Classes/Services/SiteSuggestionService.swift b/WordPress/Classes/Services/SiteSuggestionService.swift new file mode 100644 index 000000000000..5e1c54815fcf --- /dev/null +++ b/WordPress/Classes/Services/SiteSuggestionService.swift @@ -0,0 +1,119 @@ +import Foundation + +/// A service to fetch and persist a list of sites that can be xpost to from a post. +class SiteSuggestionService { + + private var blogsCurrentlyBeingRequested = [Blog]() + + static let shared = SiteSuggestionService() + + /** + Fetch cached suggestions if available, otherwise from the network if the device is online. + + @param the blog/site to retrieve suggestions for + @param completion callback containing list of suggestions, or nil if unavailable + */ + func suggestions(for blog: Blog, completion: @escaping ([SiteSuggestion]?) -> Void) { + + if let suggestions = retrievePersistedSuggestions(for: blog), suggestions.isEmpty == false { + completion(suggestions) + } else if ReachabilityUtils.isInternetReachable() { + fetchAndPersistSuggestions(for: blog, completion: completion) + } else { + completion(nil) + } + } + + /** + Performs a REST API request for the given blog. + Persists response objects to Core Data. + + @param blog/site to retrieve suggestions for + */ + private func fetchAndPersistSuggestions(for blog: Blog, completion: @escaping ([SiteSuggestion]?) -> Void) { + + // if there is already a request in place for this blog, just wait + guard !blogsCurrentlyBeingRequested.contains(blog) else { return } + + guard let hostname = blog.hostname else { return } + + let suggestPath = "/wpcom/v2/sites/\(hostname)/xposts" + let params = ["decode_html": true] as [String: AnyObject] + + // add this blog to currently being requested list + blogsCurrentlyBeingRequested.append(blog) + + defaultAccount()?.wordPressComRestApi.GET(suggestPath, parameters: params, success: { [weak self] responseObject, httpResponse in + guard let `self` = self else { return } + + let context = ContextManager.shared.mainContext + guard let data = try? JSONSerialization.data(withJSONObject: responseObject) else { return } + let decoder = JSONDecoder() + decoder.userInfo[CodingUserInfoKey.managedObjectContext] = context + guard let suggestions = try? decoder.decode([SiteSuggestion].self, from: data) else { return } + + // Delete any existing `SiteSuggestion` objects + self.retrievePersistedSuggestions(for: blog)?.forEach { suggestion in + context.delete(suggestion) + } + + // Associate `SiteSuggestion` objects with blog + blog.siteSuggestions = Set(suggestions) + + // Save the changes + try? ContextManager.shared.mainContext.save() + + completion(suggestions) + + // remove blog from the currently being requested list + self.blogsCurrentlyBeingRequested.removeAll { $0 == blog } + }, failure: { [weak self] error, _ in + guard let `self` = self else { return } + + completion([]) + + // remove blog from the currently being requested list + self.blogsCurrentlyBeingRequested.removeAll { $0 == blog} + + DDLogVerbose("[Rest API] ! \(error.localizedDescription)") + }) + } + + /** + Tells the caller if it is a good idea to show suggestions right now for a given blog/site. + + @param blog blog/site to check for + @return BOOL Whether the caller should show suggestions + */ + func shouldShowSuggestions(for blog: Blog) -> Bool { + + // The device must be online or there must be already persisted suggestions + guard ReachabilityUtils.isInternetReachable() || retrievePersistedSuggestions(for: blog)?.isEmpty == false else { + return false + } + + return blog.supports(.xposts) + } + + private func defaultAccount() -> WPAccount? { + let context = ContextManager.shared.mainContext + let accountService = AccountService(managedObjectContext: context) + return accountService.defaultWordPressComAccount() + } + + func retrievePersistedSuggestions(for blog: Blog) -> [SiteSuggestion]? { + guard let suggestions = blog.siteSuggestions else { return nil } + return Array(suggestions) + } + + /** + Retrieve the persisted blog/site for a given site ID + + @param siteID the dotComID to retrieve + @return Blog the blog/site + */ + func persistedBlog(for siteID: NSNumber) -> Blog? { + let context = ContextManager.shared.mainContext + return BlogService(managedObjectContext: context).blog(byBlogId: siteID) + } +} diff --git a/WordPress/Classes/Services/SuggestionService.swift b/WordPress/Classes/Services/SuggestionService.swift index 183b6d5956f3..4c4c50a274ab 100644 --- a/WordPress/Classes/Services/SuggestionService.swift +++ b/WordPress/Classes/Services/SuggestionService.swift @@ -1,148 +1,99 @@ import Foundation -/// A service to fetch and persist a list of suggestions, which can be either: -/// - a list of users that can be @-mentioned in a post or comment, or; -/// - a list of sites that can be cross-posted to from within the Gutenberg editor +/// A service to fetch and persist a list of users that can be @-mentioned in a post or comment. class SuggestionService { - // Used to keep track of requests made to the suggestions endpoints - struct Request: Equatable { - let type: SuggestionType - let blog: Blog - - static func ==(lhs: Request, rhs: Request) -> Bool { - return lhs.type == rhs.type && lhs.blog == rhs.blog - } - } + private var blogsCurrentlyBeingRequested = [Blog]() static let shared = SuggestionService() - private var suggestionsRequested = [Request]() - var requestDates = [Blog: Date]() /** - Fetch cached suggestions if available, otherwise from the network if the device is online. + Fetch cached suggestions if available, otherwise from the network if the device is online. - @param type The type of suggestion - @param blog The blog/site to retrieve suggestions for - @param completion A callback containing list of suggestions, or nil if unavailable + @param the blog/site to retrieve suggestions for + @param completion callback containing list of suggestions, or nil if unavailable */ - func suggestionsOf(type: SuggestionType, for blog: Blog, completion: (([NSManagedObject]?) -> Void)?) { - - let throttleDuration: TimeInterval = 60 // seconds - if let requestDate = requestDates[blog], Date().timeIntervalSince(requestDate) < throttleDuration { - completion?(nil) - return - } + func suggestions(for blog: Blog, completion: @escaping ([UserSuggestion]?) -> Void) { - if let suggestions = retrievePersistedSuggestionsOf(type: type, for: blog), suggestions.isEmpty == false { - completion?(suggestions) + if let suggestions = retrievePersistedSuggestions(for: blog), suggestions.isEmpty == false { + completion(suggestions) } else if ReachabilityUtils.isInternetReachable() { - requestDates[blog] = Date() - fetchAndPersistSuggestionsOf(type: type, for: blog, completion: completion) + fetchAndPersistSuggestions(for: blog, completion: completion) } else { - completion?(nil) + completion(nil) } } /** - Performs a REST API request to fetch user or site suggestions for the given blog. - Persists response objects to Core Data. - @param type The type of suggestion - @param blog The blog/site to retrieve suggestions for - @param completion A callback containing list of suggestions, or nil if unavailable - */ - private func fetchAndPersistSuggestionsOf(type: SuggestionType, for blog: Blog, completion: (([NSManagedObject]?) -> Void)?) { + Performs a REST API request for the given blog. + Persists response objects to Core Data. - let request = Request(type: type, blog: blog) + @param blog/site to retrieve suggestions for + */ + private func fetchAndPersistSuggestions(for blog: Blog, completion: @escaping ([UserSuggestion]?) -> Void) { // if there is already a request in place for this blog, just wait - guard !suggestionsRequested.contains(request) else { return } + guard !blogsCurrentlyBeingRequested.contains(blog) else { return } - let path: String - let params: [String: AnyObject] + guard let siteID = blog.dotComID else { return } - switch type { - case .mention: - path = "rest/v1.1/users/suggest" - guard let siteID = blog.dotComID else { - completion?(nil) - return - } - params = [ "site_id": siteID ] - case .xpost: - guard let hostname = blog.hostname else { - completion?(nil) - return - } - path = "/wpcom/v2/sites/\(hostname)/xposts" - params = [ "decode_html": true ] as [String: AnyObject] - } + let suggestPath = "rest/v1.1/users/suggest" + let params = ["site_id": siteID] // add this blog to currently being requested list - suggestionsRequested.append(request) + blogsCurrentlyBeingRequested.append(blog) - defaultAccount()?.wordPressComRestApi.GET(path, parameters: params, success: { [weak self] responseObject, httpResponse in + defaultAccount()?.wordPressComRestApi.GET(suggestPath, parameters: params, success: { [weak self] responseObject, httpResponse in guard let `self` = self else { return } + guard let payload = responseObject as? [String: Any] else { return } + guard let restSuggestions = payload["suggestions"] as? [[String: Any]] else { return } - do { - let context = ContextManager.shared.mainContext - let data = try JSONSerialization.data(withJSONObject: responseObject) - let decoder = JSONDecoder() - decoder.userInfo[CodingUserInfoKey.managedObjectContext] = context + let context = ContextManager.shared.mainContext - try self.purgeManagedObjectsOf(type: type, in: blog, using: context) + // Delete any existing `UserSuggestion` objects + self.retrievePersistedSuggestions(for: blog)?.forEach { suggestion in + context.delete(suggestion) + } - switch type { - case .mention: - let payload = try decoder.decode(UserSuggestionsPayload.self, from: data) - blog.userSuggestions = Set(payload.suggestions) - case .xpost: - let suggestions = try decoder.decode([SiteSuggestion].self, from: data) - blog.siteSuggestions = Set(suggestions) - } + // Create new `UserSuggestion` objects + let suggestions = restSuggestions.compactMap { UserSuggestion(dictionary: $0, context: context) } - try ContextManager.shared.mainContext.save() + // Associate `UserSuggestion` objects with blog + blog.userSuggestions = Set(suggestions) - completion?(self.retrievePersistedSuggestionsOf(type: .xpost, for: blog)) - } catch { - completion?(nil) - } + // Save the changes + try? blog.managedObjectContext?.save() + + completion(suggestions) // remove blog from the currently being requested list - self.suggestionsRequested.removeAll { $0 == request } + self.blogsCurrentlyBeingRequested.removeAll { $0 == blog } }, failure: { [weak self] error, _ in guard let `self` = self else { return } - completion?(nil) + completion(nil) // remove blog from the currently being requested list - self.suggestionsRequested.removeAll { $0 == request} + self.blogsCurrentlyBeingRequested.removeAll { $0 == blog} DDLogVerbose("[Rest API] ! \(error.localizedDescription)") }) } /** - Tells the caller if it is a good idea to show suggestions right now for a given blog/site. + Tells the caller if it is a good idea to show suggestions right now for a given blog/site. - @param type The type of suggestion - @param blog The blog/site to check for - @return BOOL Whether the caller should show suggestions + @param blog blog/site to check for + @return BOOL Whether the caller should show suggestions */ - func shouldShowSuggestionsOf(type: SuggestionType, for blog: Blog) -> Bool { + func shouldShowSuggestions(for blog: Blog) -> Bool { // The device must be online or there must be already persisted suggestions - guard ReachabilityUtils.isInternetReachable() || retrievePersistedSuggestionsOf(type: type, for: blog)?.isEmpty == false else { return false } - - switch type { - case .mention: return blog.supports(.mentions) - case .xpost: return blog.supports(.xposts) + guard ReachabilityUtils.isInternetReachable() || retrievePersistedSuggestions(for: blog)?.isEmpty == false else { + return false } - } - private func purgeManagedObjectsOf(type: SuggestionType, in blog: Blog, using managedObjectContext: NSManagedObjectContext) throws { - retrievePersistedSuggestionsOf(type: type, for: blog)?.forEach { managedObjectContext.delete($0) } - try managedObjectContext.save() + return blog.supports(.mentions) } private func defaultAccount() -> WPAccount? { @@ -151,18 +102,16 @@ class SuggestionService { return accountService.defaultWordPressComAccount() } - func retrievePersistedSuggestionsOf(type: SuggestionType, for blog: Blog) -> [NSManagedObject]? { - switch type { - case .mention: return blog.userSuggestions?.sorted() - case .xpost: return blog.siteSuggestions?.sorted() - } + private func retrievePersistedSuggestions(for blog: Blog) -> [UserSuggestion]? { + guard let suggestions = blog.userSuggestions else { return nil } + return Array(suggestions) } /** - Retrieve the persisted blog/site for a given site ID. + Retrieve the persisted blog/site for a given site ID @param siteID the dotComID to retrieve - @return The blog/site for the given site ID + @return Blog the blog/site */ func persistedBlog(for siteID: NSNumber) -> Blog? { let context = ContextManager.shared.mainContext diff --git a/WordPress/Classes/ViewRelated/Comments/CommentViewController.swift b/WordPress/Classes/ViewRelated/Comments/CommentViewController.swift index 43d5baee1b28..b89bb78b740d 100644 --- a/WordPress/Classes/ViewRelated/Comments/CommentViewController.swift +++ b/WordPress/Classes/ViewRelated/Comments/CommentViewController.swift @@ -3,6 +3,6 @@ import Foundation @objc public extension CommentViewController { func shouldShowSuggestions(for siteID: NSNumber?) -> Bool { guard let siteID = siteID, let blog = SuggestionService.shared.persistedBlog(for: siteID) else { return false } - return SuggestionService.shared.shouldShowSuggestionsOf(type: .mention, for: blog) + return SuggestionService.shared.shouldShowSuggestions(for: blog) } } diff --git a/WordPress/Classes/ViewRelated/Comments/FullScreenCommentReplyViewController.swift b/WordPress/Classes/ViewRelated/Comments/FullScreenCommentReplyViewController.swift index 01f8d466396d..be83e5c66474 100644 --- a/WordPress/Classes/ViewRelated/Comments/FullScreenCommentReplyViewController.swift +++ b/WordPress/Classes/ViewRelated/Comments/FullScreenCommentReplyViewController.swift @@ -358,7 +358,7 @@ private extension FullScreenCommentReplyViewController { /// Determine if suggestions are enabled and visible for this site var shouldShowSuggestions: Bool { guard let siteID = self.siteID, let blog = SuggestionService.shared.persistedBlog(for: siteID) else { return false } - return SuggestionService.shared.shouldShowSuggestionsOf(type: .mention, for: blog) + return SuggestionService.shared.shouldShowSuggestions(for: blog) } // This should be moved elsewhere diff --git a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift index 0e9b6c61bec0..ae7fd656f5cb 100644 --- a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift +++ b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift @@ -345,11 +345,6 @@ class GutenbergViewController: UIViewController, PostEditor { }, failure: { (error) in DDLogError("Error syncing JETPACK: \(String(describing: error))") }) - - let xpostSuggestions = SuggestionService.shared.retrievePersistedSuggestionsOf(type: .xpost, for: post.blog) - if xpostSuggestions == nil || xpostSuggestions?.isEmpty == true { - SuggestionService.shared.suggestionsOf(type: .xpost, for: post.blog, completion: nil) - } } override func viewWillAppear(_ animated: Bool) { @@ -889,7 +884,12 @@ extension GutenbergViewController { return } - guard SuggestionService.shared.shouldShowSuggestionsOf(type: type, for: post.blog) else { return } + switch type { + case .mention: + guard SuggestionService.shared.shouldShowSuggestions(for: post.blog) else { return } + case .xpost: + guard SiteSuggestionService.shared.shouldShowSuggestions(for: post.blog) else { return } + } previousFirstResponder = view.findFirstResponder() let suggestionsController = GutenbergSuggestionsViewController(siteID: siteID, suggestionType: type) @@ -962,8 +962,8 @@ extension GutenbergViewController: GutenbergBridgeDataSource { func gutenbergCapabilities() -> [Capabilities: Bool] { return [ - .mentions: FeatureFlag.gutenbergMentions.enabled && post.blog.supports(.mentions), - .xposts: FeatureFlag.gutenbergXposts.enabled && post.blog.supports(.xposts), + .mentions: FeatureFlag.gutenbergMentions.enabled && SuggestionService.shared.shouldShowSuggestions(for: post.blog), + .xposts: FeatureFlag.gutenbergXposts.enabled && SiteSuggestionService.shared.shouldShowSuggestions(for: post.blog), .unsupportedBlockEditor: isUnsupportedBlockEditorEnabled, .canEnableUnsupportedBlockEditor: post.blog.jetpack?.isConnected ?? false, .modalLayoutPicker: FeatureFlag.gutenbergModalLayoutPicker.enabled, diff --git a/WordPress/Classes/ViewRelated/Notifications/Controllers/NotificationDetailsViewController.swift b/WordPress/Classes/ViewRelated/Notifications/Controllers/NotificationDetailsViewController.swift index 201eead30ab8..6ca4d848f2f8 100644 --- a/WordPress/Classes/ViewRelated/Notifications/Controllers/NotificationDetailsViewController.swift +++ b/WordPress/Classes/ViewRelated/Notifications/Controllers/NotificationDetailsViewController.swift @@ -547,7 +547,7 @@ private extension NotificationDetailsViewController { let blog = SuggestionService.shared.persistedBlog(for: siteID) else { return false } - return shouldAttachReplyView && SuggestionService.shared.shouldShowSuggestionsOf(type: .mention, for: blog) + return shouldAttachReplyView && SuggestionService.shared.shouldShowSuggestions(for: blog) } } diff --git a/WordPress/Classes/ViewRelated/Reader/ReaderCommentsViewController.swift b/WordPress/Classes/ViewRelated/Reader/ReaderCommentsViewController.swift index 7ad4602791ce..38ae44c4188a 100644 --- a/WordPress/Classes/ViewRelated/Reader/ReaderCommentsViewController.swift +++ b/WordPress/Classes/ViewRelated/Reader/ReaderCommentsViewController.swift @@ -3,6 +3,6 @@ import Foundation @objc public extension ReaderCommentsViewController { func shouldShowSuggestions(for siteID: NSNumber?) -> Bool { guard let siteID = siteID, let blog = SuggestionService.shared.persistedBlog(for: siteID) else { return false } - return SuggestionService.shared.shouldShowSuggestionsOf(type: .mention, for: blog) + return SuggestionService.shared.shouldShowSuggestions(for: blog) } } diff --git a/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.m b/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.m index c2b911d52e5f..0e695281d65e 100644 --- a/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.m +++ b/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.m @@ -41,8 +41,6 @@ - (instancetype)initWithSiteID:(NSNumber *)siteID [self setupTableView]; [self setupConstraints]; [self startObservingNotifications]; - - [self fetchSuggestionsOfType: self.suggestionType for:_siteID]; } return self; } @@ -316,4 +314,14 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N return cell; } +#pragma mark - Suggestion list management + +- (NSArray *)suggestions +{ + if (!_suggestions && _siteID != nil) { + [self fetchSuggestionsFor:_siteID]; + } + return _suggestions; +} + @end diff --git a/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.swift b/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.swift index cc14740d8d33..08e27010ba62 100644 --- a/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.swift +++ b/WordPress/Classes/ViewRelated/Suggestions/SuggestionsTableView.swift @@ -11,20 +11,22 @@ extension SuggestionType { @objc public extension SuggestionsTableView { - var suggestionTrigger: String { return suggestionType.trigger } + func userSuggestions(for siteID: NSNumber, completion: @escaping ([UserSuggestion]?) -> Void) { + guard let blog = SuggestionService.shared.persistedBlog(for: siteID) else { return } + SuggestionService.shared.suggestions(for: blog, completion: completion) + } - func fetchSuggestionsOf(type: SuggestionType, for siteID: NSNumber) { + func siteSuggestions(for siteID: NSNumber, completion: @escaping ([SiteSuggestion]?) -> Void) { guard let blog = SuggestionService.shared.persistedBlog(for: siteID) else { return } - SuggestionService.shared.suggestionsOf(type: type, for: blog, completion: { suggestions in - self.suggestions = suggestions - self.showSuggestions(forWord: self.searchText) - }) + SiteSuggestionService.shared.suggestions(for: blog, completion: completion) } + var suggestionTrigger: String { return suggestionType.trigger } + func predicate(for searchQuery: String) -> NSPredicate { switch suggestionType { case .mention: - return NSPredicate(format: "(displayName contains[cd] %@) OR (username contains[cd] %@)", searchQuery, searchQuery) + return NSPredicate(format: "(displayName contains[c] %@) OR (username contains[c] %@)", searchQuery, searchQuery) case .xpost: return NSPredicate(format: "(title contains[cd] %@) OR (siteURL.absoluteString contains[cd] %@)", searchQuery, searchQuery) } @@ -56,6 +58,7 @@ extension SuggestionType { private func imageURLForSuggestion(at indexPath: IndexPath) -> URL? { let suggestion = searchResults[indexPath.row] + switch (suggestionType, suggestion) { case (.mention, let suggestion as UserSuggestion): return suggestion.imageURL @@ -67,9 +70,7 @@ extension SuggestionType { } func loadImage(for suggestion: AnyObject, in cell: SuggestionsTableViewCell, at indexPath: IndexPath) { - cell.iconImageView.image = UIImage(named: "gravatar") - guard let imageURL = imageURLForSuggestion(at: indexPath) else { return } cell.imageDownloadHash = imageURL.hashValue @@ -81,6 +82,21 @@ extension SuggestionType { } } + func fetchSuggestions(for siteID: NSNumber) { + switch self.suggestionType { + case .mention: + userSuggestions(for: siteID) { userSuggestions in + self.suggestions = userSuggestions + self.showSuggestions(forWord: self.searchText) + } + case .xpost: + siteSuggestions(for: siteID) { siteSuggestions in + self.suggestions = siteSuggestions + self.showSuggestions(forWord: self.searchText) + } + } + } + private func suggestionText(for suggestion: Any) -> String? { switch (suggestionType, suggestion) { case (.mention, let suggestion as UserSuggestion): @@ -93,6 +109,7 @@ extension SuggestionType { private func retrieveIcon(for imageURL: URL?, success: @escaping (UIImage?) -> Void) { let imageSize = CGSize(width: SuggestionsTableViewCellIconSize, height: SuggestionsTableViewCellIconSize) + if let image = cachedIcon(for: imageURL, with: imageSize) { success(image) } else { diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 2ef4125ed4a9..f39f98f35ce3 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -1536,6 +1536,7 @@ B0AC50DD251E96270039E022 /* ReaderCommentsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0AC50DC251E96270039E022 /* ReaderCommentsViewController.swift */; }; B0B68A9C252FA91E0001B28C /* UserSuggestion+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B68A9A252FA91E0001B28C /* UserSuggestion+CoreDataClass.swift */; }; B0B68A9D252FA91E0001B28C /* UserSuggestion+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B68A9B252FA91E0001B28C /* UserSuggestion+CoreDataProperties.swift */; }; + B0F2EFBF259378E600C7EB6D /* SiteSuggestionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F2EFBE259378E600C7EB6D /* SiteSuggestionService.swift */; }; B5015C581D4FDBB300C9449E /* NotificationActionsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5015C571D4FDBB300C9449E /* NotificationActionsService.swift */; }; B50248AF1C96FF6200AFBDED /* WPStyleGuide+Share.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50248AE1C96FF6200AFBDED /* WPStyleGuide+Share.swift */; }; B50248C21C96FFCC00AFBDED /* WordPressShare-Lumberjack.m in Sources */ = {isa = PBXBuildFile; fileRef = B50248BC1C96FFCC00AFBDED /* WordPressShare-Lumberjack.m */; }; @@ -4107,6 +4108,7 @@ B0B68A9A252FA91E0001B28C /* UserSuggestion+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserSuggestion+CoreDataClass.swift"; sourceTree = ""; }; B0B68A9B252FA91E0001B28C /* UserSuggestion+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserSuggestion+CoreDataProperties.swift"; sourceTree = ""; }; B0DDC2EB252F7C4F002BAFB3 /* WordPress 100.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 100.xcdatamodel"; sourceTree = ""; }; + B0F2EFBE259378E600C7EB6D /* SiteSuggestionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteSuggestionService.swift; sourceTree = ""; }; B5015C571D4FDBB300C9449E /* NotificationActionsService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationActionsService.swift; sourceTree = ""; }; B50248AE1C96FF6200AFBDED /* WPStyleGuide+Share.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "WPStyleGuide+Share.swift"; path = "WordPressShareExtension/WPStyleGuide+Share.swift"; sourceTree = SOURCE_ROOT; }; B50248B81C96FFB000AFBDED /* WordPressShare-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "WordPressShare-Bridging-Header.h"; path = "WordPressShareExtension/WordPressShare-Bridging-Header.h"; sourceTree = SOURCE_ROOT; }; @@ -5732,7 +5734,7 @@ path = GutenbergWeb; sourceTree = ""; }; - 29B97314FDCFA39411CA2CEA = { + 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { isa = PBXGroup; children = ( 98D31BBF239720E4009CFF43 /* MainInterface.storyboard */, @@ -8426,6 +8428,7 @@ D8CB561F2181A8CE00554EAE /* SiteSegmentsService.swift */, D8A468E421828D940094B82F /* SiteVerticalsService.swift */, B03B9233250BC593000A40AF /* SuggestionService.swift */, + B0F2EFBE259378E600C7EB6D /* SiteSuggestionService.swift */, 59A9AB331B4C33A500A433DC /* ThemeService.h */, 59A9AB341B4C33A500A433DC /* ThemeService.m */, 93DEB88019E5BF7100F9546D /* TodayExtensionService.h */, @@ -11404,7 +11407,7 @@ bg, sk, ); - mainGroup = 29B97314FDCFA39411CA2CEA; + mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; productRefGroup = 19C28FACFE9D520D11CA2CBB /* Products */; projectDirPath = ""; projectRoot = ""; @@ -13285,6 +13288,7 @@ 5D8D53F119250412003C8859 /* BlogSelectorViewController.m in Sources */, 17523381246C4F9200870B4A /* HomepageSettingsViewController.swift in Sources */, C81CCD7F243BF7A600A83E27 /* TenorMediaGroup.swift in Sources */, + B0F2EFBF259378E600C7EB6D /* SiteSuggestionService.swift in Sources */, 4388FF0020A4E19C00783948 /* NotificationsViewController+PushPrimer.swift in Sources */, 5D3D559718F88C3500782892 /* ReaderPostService.m in Sources */, 7EA30DB521ADA20F0092F894 /* EditorMediaUtility.swift in Sources */,