diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist
index f908704a9..d15ed8979 100644
--- a/Signal/Signal-Info.plist
+++ b/Signal/Signal-Info.plist
@@ -7,7 +7,7 @@
CarthageVersion
0.31.2
OSXVersion
- 10.14.1
+ 10.14.2
WebRTCCommit
ca71024b4993ba95e3e6b8d0758004cffc54ddaf M70
diff --git a/Signal/test/views/ImageEditorTest.swift b/Signal/test/views/ImageEditorTest.swift
index 9c9315c22..1c2352b6c 100644
--- a/Signal/test/views/ImageEditorTest.swift
+++ b/Signal/test/views/ImageEditorTest.swift
@@ -6,6 +6,14 @@ import XCTest
@testable import Signal
@testable import SignalMessaging
+extension ImageEditorModel {
+ func itemIds() -> [String] {
+ return items().map { (item) in
+ item.itemId
+ }
+ }
+}
+
class ImageEditorTest: SignalBaseTest {
override func setUp() {
@@ -19,7 +27,10 @@ class ImageEditorTest: SignalBaseTest {
func testImageEditorContents() {
let contents = ImageEditorContents()
- let item = ImageEditorItem()
+ XCTAssertEqual(0, contents.itemMap.count)
+ XCTAssertEqual(0, contents.itemIds.count)
+
+ let item = ImageEditorItem(itemType: .test)
contents.append(item: item)
XCTAssertEqual(1, contents.itemMap.count)
XCTAssertEqual(1, contents.itemIds.count)
@@ -36,7 +47,7 @@ class ImageEditorTest: SignalBaseTest {
XCTAssertEqual(0, contentsCopy.itemMap.count)
XCTAssertEqual(0, contentsCopy.itemIds.count)
- let modifiedItem = ImageEditorItem(itemId: item.itemId)
+ let modifiedItem = ImageEditorItem(itemId: item.itemId, itemType: item.itemType)
contents.replace(item: modifiedItem)
XCTAssertEqual(1, contents.itemMap.count)
XCTAssertEqual(1, contents.itemIds.count)
@@ -71,11 +82,12 @@ class ImageEditorTest: SignalBaseTest {
XCTAssertFalse(imageEditor.canRedo())
XCTAssertEqual(0, imageEditor.itemCount())
- let itemA = ImageEditorItem()
+ let itemA = ImageEditorItem(itemType: .test)
imageEditor.append(item: itemA)
XCTAssertTrue(imageEditor.canUndo())
XCTAssertFalse(imageEditor.canRedo())
XCTAssertEqual(1, imageEditor.itemCount())
+ XCTAssertEqual([itemA.itemId], imageEditor.itemIds())
imageEditor.undo()
XCTAssertFalse(imageEditor.canUndo())
@@ -86,27 +98,31 @@ class ImageEditorTest: SignalBaseTest {
XCTAssertTrue(imageEditor.canUndo())
XCTAssertFalse(imageEditor.canRedo())
XCTAssertEqual(1, imageEditor.itemCount())
+ XCTAssertEqual([itemA.itemId], imageEditor.itemIds())
imageEditor.undo()
XCTAssertFalse(imageEditor.canUndo())
XCTAssertTrue(imageEditor.canRedo())
XCTAssertEqual(0, imageEditor.itemCount())
- let itemB = ImageEditorItem()
+ let itemB = ImageEditorItem(itemType: .test)
imageEditor.append(item: itemB)
XCTAssertTrue(imageEditor.canUndo())
XCTAssertFalse(imageEditor.canRedo())
XCTAssertEqual(1, imageEditor.itemCount())
+ XCTAssertEqual([itemB.itemId], imageEditor.itemIds())
- let itemC = ImageEditorItem()
+ let itemC = ImageEditorItem(itemType: .test)
imageEditor.append(item: itemC)
XCTAssertTrue(imageEditor.canUndo())
XCTAssertFalse(imageEditor.canRedo())
XCTAssertEqual(2, imageEditor.itemCount())
+ XCTAssertEqual([itemB.itemId, itemC.itemId], imageEditor.itemIds())
imageEditor.undo()
XCTAssertTrue(imageEditor.canUndo())
XCTAssertTrue(imageEditor.canRedo())
XCTAssertEqual(1, imageEditor.itemCount())
+ XCTAssertEqual([itemB.itemId], imageEditor.itemIds())
}
}
diff --git a/SignalMessaging/Views/ImageEditor/ImageEditor.swift b/SignalMessaging/Views/ImageEditor/ImageEditor.swift
index e09148ea0..9ca18d8f8 100644
--- a/SignalMessaging/Views/ImageEditor/ImageEditor.swift
+++ b/SignalMessaging/Views/ImageEditor/ImageEditor.swift
@@ -9,23 +9,36 @@ import UIKit
case invalidInput
}
+@objc
+public enum ImageEditorItemType: Int {
+ case test
+}
+
// MARK: -
+// Instances of ImageEditorItem should be treated
+// as immutable, once configured.
@objc
public class ImageEditorItem: NSObject {
@objc
public let itemId: String
@objc
- public override required init() {
+ public let itemType: ImageEditorItemType
+
+ @objc
+ public required init(itemType: ImageEditorItemType) {
self.itemId = UUID().uuidString
+ self.itemType = itemType
super.init()
}
@objc
- public init(itemId: String) {
+ public required init(itemId: String,
+ itemType: ImageEditorItemType) {
self.itemId = itemId
+ self.itemType = itemType
super.init()
}
@@ -33,9 +46,14 @@ public class ImageEditorItem: NSObject {
// MARK: -
+// Instances of ImageEditorContents should be treated
+// as immutable, once configured.
public class ImageEditorContents: NSObject {
+ // This represents the current state of each item.
var itemMap = [String: ImageEditorItem]()
+
+ // This represents the back-to-front ordering of the items.
var itemIds = [String]()
@objc
@@ -50,6 +68,8 @@ public class ImageEditorContents: NSObject {
self.itemIds = itemIds
}
+ // Since the contents are immutable, we only modify copies
+ // made with this method.
@objc
public func clone() -> ImageEditorContents {
return ImageEditorContents(itemMap: itemMap, itemIds: itemIds)
@@ -120,11 +140,29 @@ public class ImageEditorContents: NSObject {
}
return itemIds.count
}
+
+ @objc
+ public func items() -> [ImageEditorItem] {
+ var items = [ImageEditorItem]()
+ for itemId in itemIds {
+ guard let item = self.itemMap[itemId] else {
+ owsFailDebug("Missing item")
+ continue
+ }
+ items.append(item)
+ }
+ return items
+ }
}
// MARK: -
// Used to represent undo/redo operations.
+//
+// Because the image editor's "contents" and "items"
+// are immutable, these operations simply take a
+// snapshot of the current contents which can be used
+// (multiple times) to preserve/restore editor state.
private class ImageEditorOperation: NSObject {
let contents: ImageEditorContents
@@ -149,6 +187,10 @@ public class ImageEditorModel: NSObject {
private var undoStack = [ImageEditorOperation]()
private var redoStack = [ImageEditorOperation]()
+ // We don't want to allow editing of images if:
+ //
+ // * They are invalid.
+ // * We can't determine their size / aspect-ratio.
@objc
public required init(srcImagePath: String) throws {
self.srcImagePath = srcImagePath
@@ -179,6 +221,11 @@ public class ImageEditorModel: NSObject {
return contents.itemCount()
}
+ @objc
+ public func items() -> [ImageEditorItem] {
+ return contents.items()
+ }
+
@objc
public func canUndo() -> Bool {
return !undoStack.isEmpty