Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RNMobile] Support Theme Colors and Gradients #14085

Merged
merged 20 commits into from
Jun 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
16b7e6e
Point to develop branch
chipsnyder Mar 27, 2020
ce7c504
Merge remote-tracking branch 'origin/develop' into rnmobile/issue/174…
chipsnyder Mar 27, 2020
7da72a8
Merge remote-tracking branch 'origin/develop' into rnmobile/issue/174…
chipsnyder Mar 31, 2020
dd88567
Adds baseline theme support store to provide ground work to add theme…
chipsnyder Apr 8, 2020
7d54903
Merge remote-tracking branch 'origin/develop' into rnmobile/issue/174…
chipsnyder Apr 20, 2020
020cc51
Fetch and Parse color palette colors for the theme
chipsnyder Apr 22, 2020
5ad5b41
Rename ThemeSupportStore to EditorThemeStore, and removed clear theme…
chipsnyder May 5, 2020
1d38c5d
Merge remote-tracking branch 'origin/develop' into rnmobile/issue/174…
chipsnyder May 5, 2020
ef4ae30
Remove unused method calls
chipsnyder May 5, 2020
480f548
Add support for custom gradients for gutenberg
chipsnyder May 7, 2020
f218bd9
Enable local cache for theme supprt
chipsnyder May 7, 2020
2436309
Merge remote-tracking branch 'origin/develop' into rnmobile/issue/174…
chipsnyder May 8, 2020
24de75d
Update gutenberg referecne
chipsnyder May 8, 2020
383e2a4
Update gutenberg reference
chipsnyder May 11, 2020
508103d
Merge remote-tracking branch 'origin/develop' into rnmobile/issue/174…
chipsnyder May 20, 2020
4dc5f81
Update GB reference
chipsnyder May 20, 2020
39fe593
Refactor to make code more readable
chipsnyder May 22, 2020
fb480ca
Refactor file layout
chipsnyder May 26, 2020
aae9ffb
Merge remote-tracking branch 'origin/develop' into rnmobile/issue/174…
chipsnyder May 28, 2020
06352af
Merge remote-tracking branch 'origin/develop' into rnmobile/issue/174…
chipsnyder Jun 3, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ target 'WordPress' do
## Gutenberg (React Native)
## =====================
##
gutenberg :tag => '1.29.0'
gutenberg :commit => 'a3a155d0053c75985960ae7ac8ecfaa2e3732efa'

## Third party libraries
## =====================
Expand Down
138 changes: 69 additions & 69 deletions Podfile.lock

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
15.1
-----

* [**] Block editor: Adds support for theme colors and gradients.

15.0
-----
* [**] Block editor: Fix media upload progress when there's no connection.
Expand Down Expand Up @@ -38,7 +39,7 @@
* Support the superscript and subscript HTML formatting on the Block Editor and Classic Editor.
* [***] You can now draw on images to annotate them using the Edit image feature in the post editor.
* [*] Fixed a bug on the editors where changing a featured image didn't trigger that the post/page changed.

14.8.1
-----
* Fix adding and removing of featured images to posts.
Expand All @@ -59,7 +60,7 @@
* [internal] the "send magic link" screen has navigation changes that can cause regressions. See https://git.io/Jfqiz for testing details.
* Updated UI for Login and Signup epilogues.
* Fixes delayed split view resizing while rotating your device.

14.7
-----
* Classic Editor: Fixed action sheet position for additional Media sources picker on iPad
Expand All @@ -78,9 +79,9 @@
* Updated site details screen title to My Site, to avoid duplicating the title of the current site which is displayed in the screen's header area.
* You can now schedule your post, add tags or change the visibility before hitting "Publish Now" — and you don't have to go to the Post Settings for this!

* Login Epilogue: fixed issue where account information never stopped loading for some self-hosted sites.
* Updated site details screen title to My Site, to avoid duplicating the title of the current site which is displayed in the screen's header area.
* Login Epilogue: fixed issue where account information never stopped loading for some self-hosted sites.
* Updated site details screen title to My Site, to avoid duplicating the title of the current site which is displayed in the screen's header area.

14.6
-----
* [internal] the login flow with 2-factor authentication enabled has code changes that can cause regressions. See https://git.io/Jvdil for testing details.
Expand Down
46 changes: 46 additions & 0 deletions WordPress/Classes/Models/EditorTheme.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Foundation
import Gutenberg

struct EditorTheme: Codable, Equatable {
static func == (lhs: EditorTheme, rhs: EditorTheme) -> Bool {
return lhs.description == rhs.description
}

enum CodingKeys: String, CodingKey {
case themeSupport = "theme_supports"
case version
case stylesheet
}

let themeSupport: EditorThemeSupport?
let version: String?
let stylesheet: String?

var description: String {
return "\(stylesheet ?? "")-\(version ?? "")"
}

init(from decoder: Decoder) throws {
let map = try decoder.container(keyedBy: CodingKeys.self)
self.themeSupport = try? map.decode(EditorThemeSupport.self, forKey: .themeSupport)
self.version = try? map.decode(String.self, forKey: .version)
self.stylesheet = try? map.decode(String.self, forKey: .stylesheet)
}
}

struct EditorThemeSupport: Codable, GutenbergEditorTheme {

enum CodingKeys: String, CodingKey {
case colors = "editor-color-palette"
case gradients = "editor-gradient-presets"
}

let colors: [[String: String]]?
let gradients: [[String: String]]?

init(from decoder: Decoder) throws {
let map = try decoder.container(keyedBy: CodingKeys.self)
self.colors = try? map.decode([[String: String]].self, forKey: .colors)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we using try? here because this key can be optional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah if the colors or gradients are missing then the JSON will have editor-color-palette: false or editor-gradient-presets: false so try? here is to protect against the type change.

It's then left as an optional to make sure we don't send an empty array to Gutenberg and override the default colors that we might want.

self.gradients = try? map.decode([[String: String]].self, forKey: .gradients)
}
}
126 changes: 126 additions & 0 deletions WordPress/Classes/Stores/EditorThemeStore.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import WordPressFlux

struct EditorThemeQuery {
let blog: Blog
}

enum EditorThemeStoreState {
typealias StoredThemes = [String: EditorTheme]
case empty
case loaded(StoredThemes)

static func key(forBlog blog: Blog) -> String? {
return blog.hostname as String?
}

func editorTheme(forBlog blog: Blog) -> EditorTheme? {
guard let themeKey = EditorThemeStoreState.key(forBlog: blog) else {
return nil
}

switch self {
case .loaded(let themes):
return themes[themeKey]
default:
return nil
}
}

func storedThemes() -> StoredThemes {
switch self {
case .loaded(let themes):
return themes
default:
return [:]
}
}
}

extension EditorThemeStoreState: Codable {

enum Key: CodingKey {
case rawValue
case associatedValue
}

enum CodingError: Error {
case unknownValue
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Key.self)
let rawValue = try container.decode(Int.self, forKey: .rawValue)
switch rawValue {
case 0:
self = .empty
case 1:
let themes = try container.decode(StoredThemes.self, forKey: .associatedValue)
self = .loaded(themes)
default:
throw CodingError.unknownValue
}
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: Key.self)
switch self {
case .empty:
try container.encode(0, forKey: .rawValue)
case .loaded(let themes):
try container.encode(1, forKey: .rawValue)
try container.encode(themes, forKey: .associatedValue)
}
}
}

class EditorThemeStore: QueryStore<EditorThemeStoreState, EditorThemeQuery> {

private enum ErrorCode: Int {
case processingError
}

init(dispatcher: ActionDispatcher = .global) {
super.init(initialState: .empty, dispatcher: dispatcher)
}

override func queriesChanged() {

activeQueries.forEach { (query) in
fetchTheme(for: query.blog)
}
}

override func logError(_ error: String) {
DDLogError("Error loading active theme: \(error)")
}
}

private extension EditorThemeStore {

func fetchTheme(for blog: Blog) {
let requestPath = "/wp/v2/themes?status=active"
GutenbergNetworkRequest(path: requestPath, blog: blog).request { [weak self] result in
switch result {
case .success(let response):
self?.processResponse(response, for: blog)
case .failure(let error):
DDLogError("Error loading active theme: \(error)")
}
}
}

func processResponse(_ response: Any, for blog: Blog) {
guard
let responseData = try? JSONSerialization.data(withJSONObject: response, options: []),
let themeKey = EditorThemeStoreState.key(forBlog: blog),
let themeSupports = try? JSONDecoder().decode([EditorTheme].self, from: responseData),
let newTheme = themeSupports.first
else { return }

var existingThemes = state.storedThemes()
if newTheme != existingThemes[themeKey] {
existingThemes[themeKey] = newTheme
state = .loaded(existingThemes)
}
}
}
1 change: 1 addition & 0 deletions WordPress/Classes/Stores/StoreContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ class StoreContainer {
let statsInsights = StatsInsightsStore()
let statsPeriod = StatsPeriodStore()
let jetpackInstall = JetpackInstallStore()
let editorTheme = EditorThemeStore()
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import UIKit
import WPMediaPicker
import Gutenberg
import Aztec
import WordPressFlux

class GutenbergViewController: UIViewController, PostEditor {

Expand Down Expand Up @@ -231,6 +232,9 @@ class GutenbergViewController: UIViewController, PostEditor {
return gutenbergSettings.shouldPresentInformativeDialog(for: post.blog)
}()

private var themeSupportQuery: Receipt? = nil
private var themeSupportReceipt: Receipt? = nil

// MARK: - Initializers
required init(
post: AbstractPost,
Expand Down Expand Up @@ -277,6 +281,7 @@ class GutenbergViewController: UIViewController, PostEditor {

gutenberg.delegate = self
showInformativeDialogIfNecessary()
fetchEditorTheme()
}

override func viewWillAppear(_ animated: Bool) {
Expand Down Expand Up @@ -606,6 +611,7 @@ extension GutenbergViewController: GutenbergBridgeDelegate {
}
focusTitleIfNeeded()
mediaInserterHelper.refreshMediaStatus()
refreshEditorTheme()
}
}

Expand Down Expand Up @@ -878,3 +884,30 @@ private extension GutenbergViewController {
static let retryAllFailedUploadsActionTitle = NSLocalizedString("Retry all", comment: "User action to retry all failed media uploads.")
}
}

// Editor Theme Support
extension GutenbergViewController {

// GutenbergBridgeDataSource
func gutenbergEditorTheme() -> GutenbergEditorTheme? {
return StoreContainer.shared.editorTheme.state.editorTheme(forBlog: post.blog)?.themeSupport
}

private func fetchEditorTheme() {
let themeSupportStore = StoreContainer.shared.editorTheme
themeSupportQuery = themeSupportStore.query(EditorThemeQuery(blog: post.blog))
themeSupportReceipt = themeSupportStore.onStateChange { [weak self] (_, state) in
DispatchQueue.main.async {
if let strongSelf = self, let themeSupport = state.editorTheme(forBlog: strongSelf.post.blog)?.themeSupport {
strongSelf.gutenberg.updateTheme(themeSupport)
}
}
}
}

private func refreshEditorTheme() {
if let themeSupport = StoreContainer.shared.editorTheme.state.editorTheme(forBlog: post.blog)?.themeSupport {
gutenberg.updateTheme(themeSupport)
}
}
}
16 changes: 16 additions & 0 deletions WordPress/WordPress.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -577,10 +577,12 @@
43FB3F411EBD215C00FC8A62 /* LoginEpilogueBlogCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FB3F401EBD215C00FC8A62 /* LoginEpilogueBlogCell.swift */; };
43FB3F471EC10F1E00FC8A62 /* LoginEpilogueTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FB3F461EC10F1E00FC8A62 /* LoginEpilogueTableViewController.swift */; };
43FF64EF20DAA0840060A69A /* GravatarUploader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FF64EE20DAA0840060A69A /* GravatarUploader.swift */; };
4629E41D243D21400002E15C /* EditorThemeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4629E41C243D21400002E15C /* EditorThemeStore.swift */; };
4629E4212440C5B20002E15C /* GutenbergCoverUploadProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4629E4202440C5B20002E15C /* GutenbergCoverUploadProcessor.swift */; };
4629E4232440C8160002E15C /* GutenbergCoverUploadProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4629E4222440C8160002E15C /* GutenbergCoverUploadProcessorTests.swift */; };
462F4E0A18369F0B0028D2F8 /* BlogDetailsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 462F4E0718369F0B0028D2F8 /* BlogDetailsViewController.m */; };
46638DF6244904A3006E8439 /* GutenbergBlockProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46638DF5244904A3006E8439 /* GutenbergBlockProcessor.swift */; };
46963F5924649542000D356D /* EditorTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46963F5824649542000D356D /* EditorTheme.swift */; };
4B2DD0F29CD6AC353C056D41 /* Pods_WordPressUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DCE7542239FBC709B90EA85 /* Pods_WordPressUITests.framework */; };
4C8A715EBCE7E73AEE216293 /* Pods_WordPressShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F47DB4A8EC2E6844E213A3FA /* Pods_WordPressShareExtension.framework */; };
4D520D4F22972BC9002F5924 /* acknowledgements.html in Resources */ = {isa = PBXBuildFile; fileRef = 4D520D4E22972BC9002F5924 /* acknowledgements.html */; };
Expand Down Expand Up @@ -2957,11 +2959,13 @@
43FB3F401EBD215C00FC8A62 /* LoginEpilogueBlogCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginEpilogueBlogCell.swift; sourceTree = "<group>"; };
43FB3F461EC10F1E00FC8A62 /* LoginEpilogueTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginEpilogueTableViewController.swift; sourceTree = "<group>"; };
43FF64EE20DAA0840060A69A /* GravatarUploader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GravatarUploader.swift; sourceTree = "<group>"; };
4629E41C243D21400002E15C /* EditorThemeStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorThemeStore.swift; sourceTree = "<group>"; };
4629E4202440C5B20002E15C /* GutenbergCoverUploadProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GutenbergCoverUploadProcessor.swift; sourceTree = "<group>"; };
4629E4222440C8160002E15C /* GutenbergCoverUploadProcessorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GutenbergCoverUploadProcessorTests.swift; sourceTree = "<group>"; };
462F4E0618369F0B0028D2F8 /* BlogDetailsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlogDetailsViewController.h; sourceTree = "<group>"; };
462F4E0718369F0B0028D2F8 /* BlogDetailsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = BlogDetailsViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
46638DF5244904A3006E8439 /* GutenbergBlockProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GutenbergBlockProcessor.swift; sourceTree = "<group>"; };
46963F5824649542000D356D /* EditorTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorTheme.swift; sourceTree = "<group>"; };
46F84612185A8B7E009D0DA5 /* PostContentProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PostContentProvider.h; sourceTree = "<group>"; };
48690E659987FD4472EEDE5F /* Pods-WordPressNotificationContentExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressNotificationContentExtension.release.xcconfig"; path = "../Pods/Target Support Files/Pods-WordPressNotificationContentExtension/Pods-WordPressNotificationContentExtension.release.xcconfig"; sourceTree = "<group>"; };
4D520D4E22972BC9002F5924 /* acknowledgements.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = acknowledgements.html; path = "../Pods/Target Support Files/Pods-WordPress/acknowledgements.html"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5707,6 +5711,7 @@
2F706A870DFB229B00B43086 /* Models */ = {
isa = PBXGroup;
children = (
46963F5724649509000D356D /* Gutenberg */,
40A71C5F220E1941002E3D25 /* Stats */,
9A38DC63218899E4006A409B /* Revisions */,
D8CB56212181A93F00554EAE /* Site Creation */,
Expand Down Expand Up @@ -6387,6 +6392,14 @@
name = "Resources-iPad";
sourceTree = "<group>";
};
46963F5724649509000D356D /* Gutenberg */ = {
isa = PBXGroup;
children = (
46963F5824649542000D356D /* EditorTheme.swift */,
);
name = Gutenberg;
sourceTree = "<group>";
};
5703A4C722C0214C0028A343 /* Style */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -10082,6 +10095,7 @@
9A2D0B24225CB97F009E585F /* JetpackInstallStore.swift */,
9A4E271C22EF33F5001F6A6B /* AccountSettingsStore.swift */,
9A09F914230C3E9700F42AB7 /* StoreFetchingStatus.swift */,
4629E41C243D21400002E15C /* EditorThemeStore.swift */,
);
path = Stores;
sourceTree = "<group>";
Expand Down Expand Up @@ -12018,6 +12032,7 @@
E1222B671F878E4700D23173 /* WebViewControllerFactory.swift in Sources */,
E1D86E691B2B414300DD2192 /* WordPress-32-33.xcmappingmodel in Sources */,
9A2B28E8219046ED00458F2A /* ShowRevisionsListManger.swift in Sources */,
46963F5924649542000D356D /* EditorTheme.swift in Sources */,
985ED0E423C6950600B8D06A /* WidgetStyles.swift in Sources */,
57CCB3812358ED07003ECD0C /* WordPress-91-92.xcmappingmodel in Sources */,
5D2B30B91B7411C700DA15F3 /* ReaderCardDiscoverAttributionView.swift in Sources */,
Expand Down Expand Up @@ -12224,6 +12239,7 @@
E6805D311DCD399600168E4F /* WPRichTextImage.swift in Sources */,
7E4123C120F4097B00DF8486 /* FormattableContentRange.swift in Sources */,
08D978581CD2AF7D0054F19A /* MenuItemSourceHeaderView.m in Sources */,
4629E41D243D21400002E15C /* EditorThemeStore.swift in Sources */,
3FF1A853242D5FCB00373F5D /* WPTabBarController+ReaderTabNavigation.swift in Sources */,
BE1071FC1BC75E7400906AFF /* WPStyleGuide+Blog.swift in Sources */,
B56695B01D411EEB007E342F /* KeyboardDismissHelper.swift in Sources */,
Expand Down