From 967da78f29a857cd23a96ec978f6207c765cacb3 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 17 Dec 2018 15:19:33 -0500 Subject: [PATCH 1/7] Revert "Revert "Debug scaffolding."" This reverts commit a5e71e6eaa71b4ccc7f45bc574b7a1e1c3ebb772. --- .../ConversationView/ConversationViewController.m | 13 +++++++++++++ .../ViewControllers/HomeView/HomeViewController.m | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 3284f28a3..f7786200a 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -1190,6 +1190,19 @@ typedef enum : NSUInteger { // Clear the "on open" state after the view has been presented. self.actionOnOpen = ConversationViewActionNone; + + dispatch_async(dispatch_get_main_queue(), ^{ + NSURL *_Nullable url = [[NSBundle mainBundle] URLForResource:@"qr@2x" withExtension:@"png"]; + OWSAssertDebug(url); + + DataSource *_Nullable dataSource = [DataSourcePath dataSourceWithURL:url shouldDeleteOnDeallocation:NO]; + OWSAssertDebug(dataSource); + SignalAttachment *attachment = [SignalAttachment attachmentWithDataSource:dataSource + dataUTI:(NSString *)kUTTypePNG + imageQuality:TSImageQualityOriginal]; + + [self showApprovalDialogForAttachments:@[ attachment ]]; + }); } // `viewWillDisappear` is called whenever the view *starts* to disappear, diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 586ef500e..8438abf18 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -481,6 +481,12 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations [self.searchResultsController viewDidAppear:animated]; self.hasEverAppeared = YES; + + dispatch_async(dispatch_get_main_queue(), ^{ + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:HomeViewControllerSectionConversations]; + TSThread *thread = [self threadForIndexPath:indexPath]; + [self presentThread:thread action:ConversationViewActionNone animated:YES]; + }); } - (void)viewDidDisappear:(BOOL)animated From 9378ab21920f5622b7b1530f8b528513533679b0 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 17 Dec 2018 17:05:43 -0500 Subject: [PATCH 2/7] Add undo/redo buttons to image editor. --- .../translations/en.lproj/Localizable.strings | 6 ++ .../AttachmentApprovalViewController.swift | 5 +- .../Views/ImageEditor/ImageEditorView.swift | 64 +++++++++++++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 1804286c9..fbad19f4a 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -311,9 +311,15 @@ /* Label for generic done button. */ "BUTTON_DONE" = "Done"; +/* Label for redo button. */ +"BUTTON_REDO" = "Redo"; + /* Button text to enable batch selection mode */ "BUTTON_SELECT" = "Select"; +/* Label for undo button. */ +"BUTTON_UNDO" = "Undo"; + /* Label for button that lets users call a contact again. */ "CALL_AGAIN_BUTTON_TITLE" = "Call Again"; diff --git a/SignalMessaging/ViewControllers/AttachmentApprovalViewController.swift b/SignalMessaging/ViewControllers/AttachmentApprovalViewController.swift index 44a505589..ee866096b 100644 --- a/SignalMessaging/ViewControllers/AttachmentApprovalViewController.swift +++ b/SignalMessaging/ViewControllers/AttachmentApprovalViewController.swift @@ -857,8 +857,6 @@ public class AttachmentPrepViewController: OWSViewController, PlayerProgressBarD private(set) var contentContainer: UIView! private(set) var playVideoButton: UIView? - private var imageEditorView: ImageEditorView? - // MARK: - Initializers init(attachmentItem: SignalAttachmentItem) { @@ -954,7 +952,8 @@ public class AttachmentPrepViewController: OWSViewController, PlayerProgressBarD imageMediaView.isUserInteractionEnabled = true imageMediaView.addSubview(imageEditorView) imageEditorView.autoPinEdgesToSuperviewEdges() - self.imageEditorView = imageEditorView + + imageEditorView.addControls(to: self.mediaMessageView) } #endif diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorView.swift b/SignalMessaging/Views/ImageEditor/ImageEditorView.swift index a6001f1c5..9eeec7745 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorView.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorView.swift @@ -29,8 +29,70 @@ public class ImageEditorView: UIView, ImageEditorModelDelegate { notImplemented() } + // MARK: - Buttons + + private let undoButton = UIButton(type: .custom) + private let redoButton = UIButton(type: .custom) + + @objc + public func addControls(to containerView: UIView) { + configure(button: undoButton, + label: NSLocalizedString("BUTTON_UNDO", comment: "Label for undo button."), + selector: #selector(didTapUndo(sender:))) + + configure(button: redoButton, + label: NSLocalizedString("BUTTON_REDO", comment: "Label for redo button."), + selector: #selector(didTapRedo(sender:))) + + let stackView = UIStackView(arrangedSubviews: [undoButton, redoButton]) + stackView.axis = .vertical + stackView.alignment = .center + stackView.spacing = 10 + + containerView.addSubview(stackView) + stackView.autoAlignAxis(toSuperviewAxis: .horizontal) + stackView.autoPinTrailingToSuperviewMargin(withInset: 10) + + updateButtons() + } + + private func configure(button: UIButton, + label: String, + selector: Selector) { + button.setTitle(label, for: .normal) + button.setTitleColor(.white, + for: .normal) + button.setTitleColor(.gray, + for: .disabled) + button.titleLabel?.font = UIFont.ows_dynamicTypeBody.ows_mediumWeight() + button.addTarget(self, action: selector, for: .touchUpInside) + } + + private func updateButtons() { + undoButton.isEnabled = model.canUndo() + redoButton.isEnabled = model.canRedo() + } + // MARK: - Actions + @objc func didTapUndo(sender: UIButton) { + Logger.verbose("") + guard model.canUndo() else { + owsFailDebug("Can't undo.") + return + } + model.undo() + } + + @objc func didTapRedo(sender: UIButton) { + Logger.verbose("") + guard model.canRedo() else { + owsFailDebug("Can't redo.") + return + } + model.redo() + } + // These properties are non-empty while drawing a stroke. private var currentStroke: ImageEditorStrokeItem? private var currentStrokeSamples = [ImageEditorStrokeItem.StrokeSample]() @@ -103,6 +165,8 @@ public class ImageEditorView: UIView, ImageEditorModelDelegate { // TODO: We eventually want to narrow our change events // to reflect the specific item(s) which changed. updateAllContent() + + updateButtons() } // MARK: - Accessor Overrides From 3d67c6574ddb03dd3ce5659a04caa829e1fb3b4b Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 18 Dec 2018 09:12:50 -0500 Subject: [PATCH 3/7] Suppress undo during strokes. --- .../Views/ImageEditor/ImageEditorModel.swift | 69 ++++++++++++++++--- .../Views/ImageEditor/ImageEditorView.swift | 21 +++--- 2 files changed, 71 insertions(+), 19 deletions(-) diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift b/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift index 9aa1d5896..81fdb756e 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift @@ -320,6 +320,13 @@ public class ImageEditorModel: NSObject { private var undoStack = [ImageEditorOperation]() private var redoStack = [ImageEditorOperation]() + // In some cases, we want to suppress changes to undo state. + // e.g. drawing a stroke will modify the model many times (once + // for each touch event/stroke sample), but we only want that + // to add a single undo operation. + private var isUndoSuppressed = false + private var suppressedUndoContents: ImageEditorContents? + // We don't want to allow editing of images if: // // * They are invalid. @@ -365,6 +372,28 @@ public class ImageEditorModel: NSObject { return !undoStack.isEmpty } + @objc + public func setIsUndoSuppressed(isUndoSuppressed: Bool) { + if isUndoSuppressed { + if self.isUndoSuppressed { + owsFailDebug("Undo already suppressed.") + } + if suppressedUndoContents != nil { + owsFailDebug("Unexpected suppressed undo contents.") + } + suppressedUndoContents = contents.clone() + } else { + if self.isUndoSuppressed { + if suppressedUndoContents == nil { + owsFailDebug("Missing suppressed undo contents.") + } + } + suppressedUndoContents = nil + } + + self.isUndoSuppressed = isUndoSuppressed + } + @objc public func canRedo() -> Bool { return !redoStack.isEmpty @@ -402,29 +431,47 @@ public class ImageEditorModel: NSObject { @objc public func append(item: ImageEditorItem) { - performAction { (newContents) in + performAction({ (newContents) in newContents.append(item: item) - } + }) } @objc - public func replace(item: ImageEditorItem) { - performAction { (newContents) in + public func replace(item: ImageEditorItem, + shouldRemoveUndoSuppression: Bool = false) { + performAction({ (newContents) in newContents.replace(item: item) - } + }, shouldRemoveUndoSuppression: shouldRemoveUndoSuppression) } @objc public func remove(item: ImageEditorItem) { - performAction { (newContents) in + performAction({ (newContents) in newContents.remove(item: item) - } + }) } - private func performAction(action: (ImageEditorContents) -> Void) { - let undoOperation = ImageEditorOperation(contents: contents) - undoStack.append(undoOperation) - redoStack.removeAll() + private func performAction(_ action: (ImageEditorContents) -> Void, + shouldRemoveUndoSuppression: Bool = false) { + if shouldRemoveUndoSuppression { + if !isUndoSuppressed { + owsFailDebug("Can't remove undo suppression, not suppressed.") + } + if let suppressedUndoContents = self.suppressedUndoContents { + let undoOperation = ImageEditorOperation(contents: suppressedUndoContents) + undoStack.append(undoOperation) + redoStack.removeAll() + } else { + owsFailDebug("Missing suppressed undo contents.") + } + self.isUndoSuppressed = false + + setIsUndoSuppressed(isUndoSuppressed: false) + } else if !isUndoSuppressed { + let undoOperation = ImageEditorOperation(contents: contents) + undoStack.append(undoOperation) + redoStack.removeAll() + } let newContents = contents.clone() action(newContents) diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorView.swift b/SignalMessaging/Views/ImageEditor/ImageEditorView.swift index 9eeec7745..d460c8834 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorView.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorView.swift @@ -107,6 +107,7 @@ public class ImageEditorView: UIView, ImageEditorModelDelegate { if let stroke = self.currentStroke { self.model.remove(item: stroke) } + self.model.setIsUndoSuppressed(isUndoSuppressed: false) self.currentStroke = nil self.currentStrokeSamples.removeAll() } @@ -131,9 +132,11 @@ public class ImageEditorView: UIView, ImageEditorModelDelegate { currentStrokeSamples.append(unitSampleForGestureLocation()) - let stroke = ImageEditorStrokeItem(color: strokeColor, unitSamples: self.currentStrokeSamples, unitStrokeWidth: unitStrokeWidth) - self.model.append(item: stroke) - self.currentStroke = stroke + model.setIsUndoSuppressed(isUndoSuppressed: true) + + let stroke = ImageEditorStrokeItem(color: strokeColor, unitSamples: currentStrokeSamples, unitStrokeWidth: unitStrokeWidth) + model.append(item: stroke) + currentStroke = stroke case .changed, .ended: currentStrokeSamples.append(unitSampleForGestureLocation()) @@ -146,13 +149,15 @@ public class ImageEditorView: UIView, ImageEditorModelDelegate { // Model items are immutable; we _replace_ the // stroke item rather than modify it. - let stroke = ImageEditorStrokeItem(itemId: lastStroke.itemId, color: strokeColor, unitSamples: self.currentStrokeSamples, unitStrokeWidth: unitStrokeWidth) - self.model.replace(item: stroke) - self.currentStroke = stroke + let stroke = ImageEditorStrokeItem(itemId: lastStroke.itemId, color: strokeColor, unitSamples: currentStrokeSamples, unitStrokeWidth: unitStrokeWidth) if gestureRecognizer.state == .ended { - self.currentStroke = nil - self.currentStrokeSamples.removeAll() + model.replace(item: stroke, shouldRemoveUndoSuppression: true) + currentStroke = nil + currentStrokeSamples.removeAll() + } else { + model.replace(item: stroke) + currentStroke = stroke } default: removeCurrentStroke() From cf1763e79d596371d8883e8786f378d11c9502d7 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 18 Dec 2018 09:16:32 -0500 Subject: [PATCH 4/7] Suppress undo during strokes. --- .../Views/ImageEditor/ImageEditorModel.swift | 51 ++----------------- .../Views/ImageEditor/ImageEditorView.swift | 6 +-- 2 files changed, 5 insertions(+), 52 deletions(-) diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift b/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift index 81fdb756e..a79eecee4 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift @@ -320,13 +320,6 @@ public class ImageEditorModel: NSObject { private var undoStack = [ImageEditorOperation]() private var redoStack = [ImageEditorOperation]() - // In some cases, we want to suppress changes to undo state. - // e.g. drawing a stroke will modify the model many times (once - // for each touch event/stroke sample), but we only want that - // to add a single undo operation. - private var isUndoSuppressed = false - private var suppressedUndoContents: ImageEditorContents? - // We don't want to allow editing of images if: // // * They are invalid. @@ -372,28 +365,6 @@ public class ImageEditorModel: NSObject { return !undoStack.isEmpty } - @objc - public func setIsUndoSuppressed(isUndoSuppressed: Bool) { - if isUndoSuppressed { - if self.isUndoSuppressed { - owsFailDebug("Undo already suppressed.") - } - if suppressedUndoContents != nil { - owsFailDebug("Unexpected suppressed undo contents.") - } - suppressedUndoContents = contents.clone() - } else { - if self.isUndoSuppressed { - if suppressedUndoContents == nil { - owsFailDebug("Missing suppressed undo contents.") - } - } - suppressedUndoContents = nil - } - - self.isUndoSuppressed = isUndoSuppressed - } - @objc public func canRedo() -> Bool { return !redoStack.isEmpty @@ -438,10 +409,10 @@ public class ImageEditorModel: NSObject { @objc public func replace(item: ImageEditorItem, - shouldRemoveUndoSuppression: Bool = false) { + suppressUndo: Bool = false) { performAction({ (newContents) in newContents.replace(item: item) - }, shouldRemoveUndoSuppression: shouldRemoveUndoSuppression) + }, suppressUndo: suppressUndo) } @objc @@ -452,22 +423,8 @@ public class ImageEditorModel: NSObject { } private func performAction(_ action: (ImageEditorContents) -> Void, - shouldRemoveUndoSuppression: Bool = false) { - if shouldRemoveUndoSuppression { - if !isUndoSuppressed { - owsFailDebug("Can't remove undo suppression, not suppressed.") - } - if let suppressedUndoContents = self.suppressedUndoContents { - let undoOperation = ImageEditorOperation(contents: suppressedUndoContents) - undoStack.append(undoOperation) - redoStack.removeAll() - } else { - owsFailDebug("Missing suppressed undo contents.") - } - self.isUndoSuppressed = false - - setIsUndoSuppressed(isUndoSuppressed: false) - } else if !isUndoSuppressed { + suppressUndo: Bool = false) { + if !suppressUndo { let undoOperation = ImageEditorOperation(contents: contents) undoStack.append(undoOperation) redoStack.removeAll() diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorView.swift b/SignalMessaging/Views/ImageEditor/ImageEditorView.swift index d460c8834..1465bc34b 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorView.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorView.swift @@ -107,7 +107,6 @@ public class ImageEditorView: UIView, ImageEditorModelDelegate { if let stroke = self.currentStroke { self.model.remove(item: stroke) } - self.model.setIsUndoSuppressed(isUndoSuppressed: false) self.currentStroke = nil self.currentStrokeSamples.removeAll() } @@ -132,8 +131,6 @@ public class ImageEditorView: UIView, ImageEditorModelDelegate { currentStrokeSamples.append(unitSampleForGestureLocation()) - model.setIsUndoSuppressed(isUndoSuppressed: true) - let stroke = ImageEditorStrokeItem(color: strokeColor, unitSamples: currentStrokeSamples, unitStrokeWidth: unitStrokeWidth) model.append(item: stroke) currentStroke = stroke @@ -150,13 +147,12 @@ public class ImageEditorView: UIView, ImageEditorModelDelegate { // Model items are immutable; we _replace_ the // stroke item rather than modify it. let stroke = ImageEditorStrokeItem(itemId: lastStroke.itemId, color: strokeColor, unitSamples: currentStrokeSamples, unitStrokeWidth: unitStrokeWidth) + model.replace(item: stroke, suppressUndo: true) if gestureRecognizer.state == .ended { - model.replace(item: stroke, shouldRemoveUndoSuppression: true) currentStroke = nil currentStrokeSamples.removeAll() } else { - model.replace(item: stroke) currentStroke = stroke } default: From f224c2130a385ded9cad4bc346c993e9078dc69e Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 18 Dec 2018 14:03:42 -0500 Subject: [PATCH 5/7] Suppress undo during strokes. --- SignalMessaging/Views/ImageEditor/ImageEditorModel.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift b/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift index a79eecee4..1407eef68 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift @@ -412,7 +412,8 @@ public class ImageEditorModel: NSObject { suppressUndo: Bool = false) { performAction({ (newContents) in newContents.replace(item: item) - }, suppressUndo: suppressUndo) + }, changedItemIds: [item.itemId], + suppressUndo: suppressUndo) } @objc From 2f95413bcf8719992f7beadfdf2811b263084be9 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 18 Dec 2018 14:04:07 -0500 Subject: [PATCH 6/7] Use narrow change events in image editor. --- .../Views/ImageEditor/ImageEditorModel.swift | 24 ++++++- .../Views/ImageEditor/ImageEditorView.swift | 72 ++++++++++++++----- 2 files changed, 77 insertions(+), 19 deletions(-) diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift b/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift index 1407eef68..e01f41a0c 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift @@ -139,6 +139,10 @@ public class OrderedDictionary: NSObject { return OrderedDictionary(keyValueMap: keyValueMap, orderedKeys: orderedKeys) } + public func value(forKey key: KeyType) -> ValueType? { + return keyValueMap[key] + } + public func append(key: KeyType, value: ValueType) { if keyValueMap[key] != nil { owsFailDebug("Unexpected duplicate key in key map: \(key)") @@ -239,6 +243,11 @@ public class ImageEditorContents: NSObject { return ImageEditorContents(itemMap: itemMap.clone()) } + @objc + public func item(forId itemId: String) -> ImageEditorItem? { + return itemMap.value(forKey: itemId) + } + @objc public func append(item: ImageEditorItem) { Logger.verbose("\(item.itemId)") @@ -300,6 +309,7 @@ private class ImageEditorOperation: NSObject { @objc public protocol ImageEditorModelDelegate: class { func imageEditorModelDidChange() + func imageEditorModelDidChange(changedItemIds: [String]) } // MARK: - @@ -360,6 +370,11 @@ public class ImageEditorModel: NSObject { return contents.items() } + @objc + public func item(forId itemId: String) -> ImageEditorItem? { + return contents.item(forId: itemId) + } + @objc public func canUndo() -> Bool { return !undoStack.isEmpty @@ -382,6 +397,7 @@ public class ImageEditorModel: NSObject { self.contents = undoOperation.contents + // We could diff here and yield a more narrow change event. delegate?.imageEditorModelDidChange() } @@ -397,6 +413,7 @@ public class ImageEditorModel: NSObject { self.contents = redoOperation.contents + // We could diff here and yield a more narrow change event. delegate?.imageEditorModelDidChange() } @@ -404,7 +421,7 @@ public class ImageEditorModel: NSObject { public func append(item: ImageEditorItem) { performAction({ (newContents) in newContents.append(item: item) - }) + }, changedItemIds: [item.itemId]) } @objc @@ -420,10 +437,11 @@ public class ImageEditorModel: NSObject { public func remove(item: ImageEditorItem) { performAction({ (newContents) in newContents.remove(item: item) - }) + }, changedItemIds: [item.itemId]) } private func performAction(_ action: (ImageEditorContents) -> Void, + changedItemIds: [String], suppressUndo: Bool = false) { if !suppressUndo { let undoOperation = ImageEditorOperation(contents: contents) @@ -435,6 +453,6 @@ public class ImageEditorModel: NSObject { action(newContents) contents = newContents - delegate?.imageEditorModelDidChange() + delegate?.imageEditorModelDidChange(changedItemIds: changedItemIds) } } diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorView.swift b/SignalMessaging/Views/ImageEditor/ImageEditorView.swift index 1465bc34b..c14a638ee 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorView.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorView.swift @@ -101,8 +101,6 @@ public class ImageEditorView: UIView, ImageEditorModelDelegate { public func handleTouchGesture(_ gestureRecognizer: UIGestureRecognizer) { AssertIsOnMainThread() - Logger.verbose("\(NSStringForUIGestureRecognizerState(gestureRecognizer.state))") - let removeCurrentStroke = { if let stroke = self.currentStroke { self.model.remove(item: stroke) @@ -163,13 +161,17 @@ public class ImageEditorView: UIView, ImageEditorModelDelegate { // MARK: - ImageEditorModelDelegate public func imageEditorModelDidChange() { - // TODO: We eventually want to narrow our change events - // to reflect the specific item(s) which changed. updateAllContent() updateButtons() } + public func imageEditorModelDidChange(changedItemIds: [String]) { + updateContent(changedItemIds: changedItemIds) + + updateButtons() + } + // MARK: - Accessor Overrides @objc public override var bounds: CGRect { @@ -190,33 +192,71 @@ public class ImageEditorView: UIView, ImageEditorModelDelegate { // MARK: - Content - var contentLayers = [CALayer]() + var contentLayerMap = [String: CALayer]() internal func updateAllContent() { AssertIsOnMainThread() - for layer in contentLayers { + // Don't animate changes. + CATransaction.begin() + CATransaction.setDisableActions(true) + + for layer in contentLayerMap.values { layer.removeFromSuperlayer() } - contentLayers.removeAll() + contentLayerMap.removeAll() - guard bounds.width > 0, - bounds.height > 0 else { - return + if bounds.width > 0, + bounds.height > 0 { + + for item in model.items() { + guard let layer = ImageEditorView.layerForItem(item: item, + viewSize: bounds.size) else { + continue + } + + self.layer.addSublayer(layer) + contentLayerMap[item.itemId] = layer + } } + CATransaction.commit() + } + + internal func updateContent(changedItemIds: [String]) { + AssertIsOnMainThread() + // Don't animate changes. CATransaction.begin() CATransaction.setDisableActions(true) - for item in model.items() { - guard let layer = ImageEditorView.layerForItem(item: item, - viewSize: bounds.size) else { - continue + // Remove all changed items. + for itemId in changedItemIds { + if let layer = contentLayerMap[itemId] { + layer.removeFromSuperlayer() } + contentLayerMap.removeValue(forKey: itemId) + } + + if bounds.width > 0, + bounds.height > 0 { - self.layer.addSublayer(layer) - contentLayers.append(layer) + // Create layers for inserted and updated items. + for itemId in changedItemIds { + guard let item = model.item(forId: itemId) else { + // Item was deleted. + continue + } + + // Item was inserted or updated. + guard let layer = ImageEditorView.layerForItem(item: item, + viewSize: bounds.size) else { + continue + } + + self.layer.addSublayer(layer) + contentLayerMap[item.itemId] = layer + } } CATransaction.commit() From fe02575b62e53843bda816c292d75bceda295223 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 18 Dec 2018 16:25:55 -0500 Subject: [PATCH 7/7] Revert "Revert "Revert "Debug scaffolding.""" This reverts commit da6a74bff794151ad7ba2bd6eea2e6316b4d829d. --- .../ConversationView/ConversationViewController.m | 13 ------------- .../ViewControllers/HomeView/HomeViewController.m | 6 ------ 2 files changed, 19 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index f7786200a..3284f28a3 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -1190,19 +1190,6 @@ typedef enum : NSUInteger { // Clear the "on open" state after the view has been presented. self.actionOnOpen = ConversationViewActionNone; - - dispatch_async(dispatch_get_main_queue(), ^{ - NSURL *_Nullable url = [[NSBundle mainBundle] URLForResource:@"qr@2x" withExtension:@"png"]; - OWSAssertDebug(url); - - DataSource *_Nullable dataSource = [DataSourcePath dataSourceWithURL:url shouldDeleteOnDeallocation:NO]; - OWSAssertDebug(dataSource); - SignalAttachment *attachment = [SignalAttachment attachmentWithDataSource:dataSource - dataUTI:(NSString *)kUTTypePNG - imageQuality:TSImageQualityOriginal]; - - [self showApprovalDialogForAttachments:@[ attachment ]]; - }); } // `viewWillDisappear` is called whenever the view *starts* to disappear, diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 8438abf18..586ef500e 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -481,12 +481,6 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations [self.searchResultsController viewDidAppear:animated]; self.hasEverAppeared = YES; - - dispatch_async(dispatch_get_main_queue(), ^{ - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:HomeViewControllerSectionConversations]; - TSThread *thread = [self threadForIndexPath:indexPath]; - [self presentThread:thread action:ConversationViewActionNone animated:YES]; - }); } - (void)viewDidDisappear:(BOOL)animated