Skip to content

Commit

Permalink
Merge branch 'develop' into issue/1431-dark-mode
Browse files Browse the repository at this point in the history
  • Loading branch information
ctarda committed Nov 10, 2019
2 parents e42458b + 8cb4ef2 commit 39985ef
Show file tree
Hide file tree
Showing 17 changed files with 451 additions and 27 deletions.
1 change: 1 addition & 0 deletions Podfile
Expand Up @@ -33,6 +33,7 @@ target 'WooCommerce' do

pod 'WordPressUI', '~> 1.3.5'

pod 'WordPress-Editor-iOS', '~> 1.11.0'

# External Libraries
# ==================
Expand Down
10 changes: 9 additions & 1 deletion Podfile.lock
Expand Up @@ -41,6 +41,9 @@ PODS:
- Sentry/Core (4.4.0)
- SVProgressHUD (2.2.5)
- UIDeviceIdentifier (1.1.4)
- WordPress-Aztec-iOS (1.11.0)
- WordPress-Editor-iOS (1.11.0):
- WordPress-Aztec-iOS (= 1.11.0)
- WordPressAuthenticator (1.8.0):
- 1PasswordExtension (= 1.8.5)
- Alamofire (= 4.7.3)
Expand Down Expand Up @@ -84,6 +87,7 @@ DEPENDENCIES:
- CocoaLumberjack/Swift (~> 3.5)
- Gridicons (~> 0.19)
- KeychainAccess (~> 3.2)
- WordPress-Editor-iOS (~> 1.11.0)
- WordPressAuthenticator (~> 1.8.0)
- WordPressShared (~> 1.8.2)
- WordPressUI (~> 1.3.5)
Expand All @@ -110,6 +114,8 @@ SPEC REPOS:
- Sentry
- SVProgressHUD
- UIDeviceIdentifier
- WordPress-Aztec-iOS
- WordPress-Editor-iOS
- WordPressAuthenticator
- WordPressKit
- WordPressShared
Expand Down Expand Up @@ -137,6 +143,8 @@ SPEC CHECKSUMS:
Sentry: 26650184fe71eb7476dfd2737acb5ea6cc64b4b1
SVProgressHUD: 1428aafac632c1f86f62aa4243ec12008d7a51d6
UIDeviceIdentifier: 8f8a24b257a4d978c8d40ad1e7355b944ffbfa8c
WordPress-Aztec-iOS: 050b34d4c3adfb7c60363849049b13d60683b348
WordPress-Editor-iOS: 304098424f1051cb271546c99f906aac296b1b81
WordPressAuthenticator: 17ad69bd1fc4dc54af15fb706c61bfe81da12ddd
WordPressKit: 87ba4cce3f5269e26a09568a749ec1b8b2ba2267
WordPressShared: 09cf184caa614835f5811e8609227165201e6d3e
Expand All @@ -145,6 +153,6 @@ SPEC CHECKSUMS:
XLPagerTabStrip: 61c57fd61f611ee5f01ff1495ad6fbee8bf496c5
ZendeskSDK: f1c093a28ffcd0dc84b0cc1844801c7fc3a3dffd

PODFILE CHECKSUM: e294f33e34682e98f0c245711f604c60e5dbb32f
PODFILE CHECKSUM: 84e32c6d5f6593e3144e98e1a0e445e56c964c81

COCOAPODS: 1.8.4
3 changes: 3 additions & 0 deletions RELEASE-NOTES.txt
@@ -1,3 +1,6 @@
3.1
-----

3.0
-----
- bugfix: for sites with empty site time zone in the API (usually with UTC specified in wp-admin settings) and when the site time zone is not GMT+0, the stats v4 data no longer has the wrong boundaries (example in #1357).
Expand Down
217 changes: 217 additions & 0 deletions WooCommerce/Classes/ViewRelated/Editor/AztecEditorViewController.swift
@@ -0,0 +1,217 @@
import UIKit
import Aztec
import WordPressEditor

/// Aztec's Native Editor!
final class AztecEditorViewController: UIViewController, Editor {
var onContentSave: OnContentSave?

private let content: String

private let viewProperties: EditorViewProperties

private let aztecUIConfigurator = AztecUIConfigurator()

/// The editor view.
///
private(set) lazy var editorView: Aztec.EditorView = {

let paragraphStyle = ParagraphStyle.default
paragraphStyle.lineSpacing = 4

let missingIcon = UIImage.errorStateImage

let editorView = Aztec.EditorView(
defaultFont: StyleManager.subheadlineFont,
defaultHTMLFont: StyleManager.subheadlineFont,
defaultParagraphStyle: paragraphStyle,
defaultMissingImage: missingIcon)

aztecUIConfigurator.configureEditorView(editorView,
textViewDelegate: self,
textViewAttachmentDelegate: textViewAttachmentDelegate)

return editorView
}()

/// Aztec's Awesomeness
///
private var richTextView: Aztec.TextView {
return editorView.richTextView
}

/// Aztec's Raw HTML Editor
///
private var htmlTextView: UITextView {
return editorView.htmlTextView
}

/// Aztec's Text Placeholder
///
private lazy var placeholderLabel: UILabel = {
let label = UILabel()
label.text = NSLocalizedString("Start writing...", comment: "Aztec's Text Placeholder")
label.textColor = StyleManager.wooGreyMid
label.font = StyleManager.subheadlineFont
label.isUserInteractionEnabled = false
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
return label
}()

private lazy var keyboardFrameObserver: KeyboardFrameObserver = {
let keyboardFrameObserver = KeyboardFrameObserver(onKeyboardFrameUpdate: handleKeyboardFrameUpdate(keyboardFrame:))
return keyboardFrameObserver
}()

private let textViewAttachmentDelegate: TextViewAttachmentDelegate

required init(content: String?,
viewProperties: EditorViewProperties,
textViewAttachmentDelegate: TextViewAttachmentDelegate = AztecTextViewAttachmentHandler()) {
self.content = content ?? ""
self.textViewAttachmentDelegate = textViewAttachmentDelegate
self.viewProperties = viewProperties
super.init(nibName: nil, bundle: nil)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()

registerAttachmentImageProviders()

configureNavigationBar()
configureView()
configureSubviews()

aztecUIConfigurator.configureConstraints(editorView: editorView,
editorContainerView: view,
placeholderView: placeholderLabel)

setHTML(content)

refreshPlaceholderVisibility()
}

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
startListeningToNotifications()
}
}

private extension AztecEditorViewController {
func configureNavigationBar() {
title = viewProperties.navigationTitle
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(saveButtonTapped))
}

func configureView() {
edgesForExtendedLayout = UIRectEdge()
view.backgroundColor = StyleManager.wooWhite
}

func configureSubviews() {
view.addSubview(richTextView)
view.addSubview(htmlTextView)
view.addSubview(placeholderLabel)
}

func registerAttachmentImageProviders() {
let providers: [TextViewAttachmentImageProvider] = [
SpecialTagAttachmentRenderer(),
CommentAttachmentRenderer(font: StyleManager.subheadlineBoldFont),
HTMLAttachmentRenderer(font: StyleManager.subheadlineBoldFont),
GutenpackAttachmentRenderer()
]

for provider in providers {
richTextView.registerAttachmentImageProvider(provider)
}
}
}

private extension AztecEditorViewController {
func setHTML(_ html: String) {
editorView.setHTML(html)
}

func getHTML() -> String {
return editorView.getHTML()
}

func refreshPlaceholderVisibility() {
placeholderLabel.isHidden = richTextView.isHidden || !richTextView.text.isEmpty
}
}

// MARK: Keyboard frame update handling
//
private extension AztecEditorViewController {
func handleKeyboardFrameUpdate(keyboardFrame: CGRect) {
let referenceView = editorView.activeView

let bottomInset = view.frame.maxY - (keyboardFrame.minY + self.view.layoutMargins.bottom)
let contentInsets = UIEdgeInsets(top: referenceView.contentInset.top,
left: 0,
bottom: max(0, bottomInset),
right: 0)

htmlTextView.contentInset = contentInsets
richTextView.contentInset = contentInsets

updateScrollInsets()
}

func updateScrollInsets() {
let referenceView = editorView.activeView
var scrollInsets = referenceView.contentInset
var rightMargin = (view.frame.maxX - referenceView.frame.maxX)
rightMargin -= view.safeAreaInsets.right
scrollInsets.right = -rightMargin
referenceView.scrollIndicatorInsets = scrollInsets
}
}

// MARK: - Notifications
//
private extension AztecEditorViewController {
func startListeningToNotifications() {
keyboardFrameObserver.startObservingKeyboardFrame()
}
}

// MARK: - Navigation actions
//
private extension AztecEditorViewController {
@objc func saveButtonTapped() {
let content = getHTML()
onContentSave?(content)

navigationController?.popViewController(animated: true)
}
}

// MARK: - UITextViewDelegate methods
//
extension AztecEditorViewController: UITextViewDelegate {

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
return true
}

func textViewDidChangeSelection(_ textView: UITextView) {
refreshPlaceholderVisibility()
}

func textViewDidChange(_ textView: UITextView) {
refreshPlaceholderVisibility()
}

func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
return true
}
}
@@ -0,0 +1,24 @@
import Aztec

/// Implements Aztec's `TextViewAttachmentDelegate` without media support.
final class AztecTextViewAttachmentHandler: TextViewAttachmentDelegate {
func textView(_ textView: TextView, attachment: NSTextAttachment, imageAt url: URL, onSuccess success: @escaping (UIImage) -> Void, onFailure failure: @escaping () -> Void) {
}

func textView(_ textView: TextView, urlFor imageAttachment: ImageAttachment) -> URL? {
return nil
}

func textView(_ textView: TextView, placeholderFor attachment: NSTextAttachment) -> UIImage {
return UIImage.cameraImage
}

func textView(_ textView: TextView, deletedAttachment attachment: MediaAttachment) {
}

func textView(_ textView: TextView, selected attachment: NSTextAttachment, atPosition position: CGPoint) {
}

func textView(_ textView: TextView, deselected attachment: NSTextAttachment, atPosition position: CGPoint) {
}
}
94 changes: 94 additions & 0 deletions WooCommerce/Classes/ViewRelated/Editor/AztecUIConfigurator.swift
@@ -0,0 +1,94 @@
import Aztec
import UIKit
import WordPressEditor

/// Configures the Aztec UI components, like the styling and Auto Layout constraints.
struct AztecUIConfigurator {
func configureEditorView(_ editorView: EditorView,
textViewDelegate: UITextViewDelegate,
textViewAttachmentDelegate: TextViewAttachmentDelegate) {
editorView.clipsToBounds = false
configureHTMLTextView(editorView.htmlTextView, textViewDelegate: textViewDelegate)
configureRichTextView(editorView.richTextView,
textViewDelegate: textViewDelegate,
textViewAttachmentDelegate: textViewAttachmentDelegate)
}

func configureConstraints(editorView: EditorView, editorContainerView: UIView, placeholderView: UIView) {
let richTextView = editorView.richTextView
let htmlTextView = editorView.htmlTextView

NSLayoutConstraint.activate([
richTextView.leadingAnchor.constraint(equalTo: editorContainerView.readableContentGuide.leadingAnchor),
richTextView.trailingAnchor.constraint(equalTo: editorContainerView.readableContentGuide.trailingAnchor),
richTextView.topAnchor.constraint(equalTo: editorContainerView.topAnchor),
richTextView.bottomAnchor.constraint(equalTo: editorContainerView.bottomAnchor)
])

NSLayoutConstraint.activate([
htmlTextView.leftAnchor.constraint(equalTo: richTextView.leftAnchor),
htmlTextView.rightAnchor.constraint(equalTo: richTextView.rightAnchor),
htmlTextView.topAnchor.constraint(equalTo: richTextView.topAnchor),
htmlTextView.bottomAnchor.constraint(equalTo: richTextView.bottomAnchor)
])

let insets = richTextView.textContainerInset

NSLayoutConstraint.activate([
placeholderView.leftAnchor.constraint(equalTo: richTextView.leftAnchor, constant: insets.left + richTextView.textContainer.lineFragmentPadding),
placeholderView.rightAnchor.constraint(equalTo: richTextView.rightAnchor, constant: -insets.right - richTextView.textContainer.lineFragmentPadding),
placeholderView.topAnchor.constraint(equalTo: richTextView.topAnchor, constant: insets.top),
placeholderView.bottomAnchor.constraint(lessThanOrEqualTo: richTextView.bottomAnchor, constant: insets.bottom)
])
}
}

private extension AztecUIConfigurator {
func configureHTMLTextView(_ textView: UITextView, textViewDelegate: UITextViewDelegate) {
let accessibilityLabel = NSLocalizedString("HTML Content", comment: "Post HTML content")
configureDefaultProperties(for: textView, accessibilityLabel: accessibilityLabel)

textView.isHidden = true
textView.delegate = textViewDelegate
textView.accessibilityIdentifier = "HTMLContentView"
textView.autocorrectionType = .no
textView.autocapitalizationType = .none

// We need this false to be able to set negative `scrollInset` values.
textView.clipsToBounds = false

textView.adjustsFontForContentSizeCategory = true
textView.smartDashesType = .no
textView.smartQuotesType = .no
}

func configureRichTextView(_ textView: TextView,
textViewDelegate: UITextViewDelegate,
textViewAttachmentDelegate: TextViewAttachmentDelegate) {
textView.load(WordPressPlugin())

let accessibilityLabel = NSLocalizedString("Rich Content", comment: "Post Rich content")
configureDefaultProperties(for: textView, accessibilityLabel: accessibilityLabel)

let linkAttributes: [NSAttributedString.Key: Any] = [.underlineStyle: NSUnderlineStyle.single.rawValue,
.foregroundColor: StyleManager.wooCommerceBrandColor]

textView.delegate = textViewDelegate
textView.textAttachmentDelegate = textViewAttachmentDelegate
textView.backgroundColor = StyleManager.wooWhite
textView.linkTextAttributes = linkAttributes

// We need this false to be able to set negative `scrollInset` values.
textView.clipsToBounds = false

textView.smartDashesType = .no
textView.smartQuotesType = .no
}

func configureDefaultProperties(for textView: UITextView, accessibilityLabel: String) {
textView.accessibilityLabel = accessibilityLabel
textView.keyboardDismissMode = .interactive
textView.textColor = UIColor.darkText
textView.translatesAutoresizingMaskIntoConstraints = false
}
}

0 comments on commit 39985ef

Please sign in to comment.