Skip to content
This repository has been archived by the owner on Dec 14, 2021. It is now read-only.

Commit

Permalink
touch item wip
Browse files Browse the repository at this point in the history
  • Loading branch information
sashei committed Mar 15, 2018
1 parent c5ae068 commit b7d51d2
Show file tree
Hide file tree
Showing 13 changed files with 390 additions and 40 deletions.
1 change: 1 addition & 0 deletions Cartfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ github "Quick/Quick"
github "Quick/Nimble"
github "ReactiveX/RxSwift"
github "RxSwiftCommunity/RxDataSources" ~> 3.0
github "RxSwiftCommunity/RxOptional" ~> 3.1.3
github "mozilla-mobile/telemetry-ios" "v1.0.4"
1 change: 1 addition & 0 deletions Cartfile.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ github "Quick/Nimble" "v7.0.3"
github "Quick/Quick" "v1.2.0"
github "ReactiveX/RxSwift" "4.1.2"
github "RxSwiftCommunity/RxDataSources" "3.0.2"
github "RxSwiftCommunity/RxOptional" "3.3.0"
github "mozilla-mobile/telemetry-ios" "v1.0.4"
10 changes: 10 additions & 0 deletions Lockbox.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
7AA541B3BC9C4F5E6A725222 /* FxAView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA54C12A1BC616CA2344584 /* FxAView.swift */; };
7AA541BD28063282A0B8B352 /* Observable+Spec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA547CE3404CC8123CA455D /* Observable+Spec.swift */; };
7AA5435895EE131D6B5BD4ED /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA5450E45A4DB17626F4684 /* WebView.swift */; };
7AA5436C61447D25F2167706 /* Date+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA54DEAC9AF2E17ED0FADC3 /* Date+.swift */; };
7AA5439445A0DE14E73A038A /* DataStoreSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA5439EEDE4AB222842E7C8 /* DataStoreSpec.swift */; };
7AA543A00DEEDF6947B578EC /* UserInfoStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA54146E0C9269A0CE8D53A /* UserInfoStore.swift */; };
7AA543D2B55F2968B976F8EF /* OAuthInfoSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA544EC2E10034239FCB256 /* OAuthInfoSpec.swift */; };
Expand Down Expand Up @@ -109,6 +110,8 @@
7D7AD94B204EFBA4002D35EE /* StatusAlert.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D7AD94A204EFBA4002D35EE /* StatusAlert.xib */; };
7D8296591F9FA4E800ED1ADD /* RxSwift.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7DF469C31F9F8F8000375C74 /* RxSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
7D88BD461F9A6F5B0082A838 /* ItemEntrySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D88BD451F9A6F5B0082A838 /* ItemEntrySpec.swift */; };
7D92DAF320586FBA00195A1B /* RxOptional.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D92DAF220586FBA00195A1B /* RxOptional.framework */; };
7D92DAF420586FC200195A1B /* RxOptional.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7D92DAF220586FBA00195A1B /* RxOptional.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
7DA4C6832028F84800B61DD8 /* ItemListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DA4C6802028F84800B61DD8 /* ItemListView.swift */; };
7DA4C6862028F85A00B61DD8 /* ItemListPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DA4C6852028F85A00B61DD8 /* ItemListPresenter.swift */; };
7DA4C68D2028F99200B61DD8 /* ItemListViewSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DA4C6872028F86E00B61DD8 /* ItemListViewSpec.swift */; };
Expand Down Expand Up @@ -160,6 +163,7 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
7D92DAF420586FC200195A1B /* RxOptional.framework in CopyFiles */,
7D302BEB1FD7284600D2FD4B /* RxCocoa.framework in CopyFiles */,
7DEA1C4420362E18008AD7C4 /* Differentiator.framework in CopyFiles */,
7D8296591F9FA4E800ED1ADD /* RxSwift.framework in CopyFiles */,
Expand Down Expand Up @@ -234,6 +238,7 @@
7AA54CDEA56C47263160AD0E /* Dispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dispatcher.swift; sourceTree = "<group>"; };
7AA54D4248CF43B76BACDBD3 /* FxAStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FxAStore.swift; sourceTree = "<group>"; };
7AA54D4E439D76B910034E2A /* ErrorAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorAction.swift; sourceTree = "<group>"; };
7AA54DEAC9AF2E17ED0FADC3 /* Date+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Date+.swift"; sourceTree = "<group>"; };
7AA54E126924935F1ADF4E8E /* ItemDetailStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemDetailStore.swift; sourceTree = "<group>"; };
7AA54E38ACC2FDBBF700D5E1 /* RouteAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouteAction.swift; sourceTree = "<group>"; };
7AA54E826632AFFBA1677E5E /* UserInfoStoreSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInfoStoreSpec.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -287,6 +292,7 @@
7D6FB87A204F00E2005E23AA /* StatusAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusAlert.swift; sourceTree = "<group>"; };
7D7AD94A204EFBA4002D35EE /* StatusAlert.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatusAlert.xib; sourceTree = "<group>"; };
7D88BD451F9A6F5B0082A838 /* ItemEntrySpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemEntrySpec.swift; sourceTree = "<group>"; };
7D92DAF220586FBA00195A1B /* RxOptional.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxOptional.framework; path = Carthage/Build/iOS/RxOptional.framework; sourceTree = "<group>"; };
7D9D64921F9F8EE300279C39 /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/iOS/RxCocoa.framework; sourceTree = "<group>"; };
7DA4C6802028F84800B61DD8 /* ItemListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemListView.swift; sourceTree = "<group>"; };
7DA4C6852028F85A00B61DD8 /* ItemListPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemListPresenter.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -326,6 +332,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
7D92DAF320586FBA00195A1B /* RxOptional.framework in Frameworks */,
7D4D2B3A20362DC500238143 /* RxDataSources.framework in Frameworks */,
7DEA1C4320362E18008AD7C4 /* Differentiator.framework in Frameworks */,
7DF469C41F9F8F8000375C74 /* RxSwift.framework in Frameworks */,
Expand Down Expand Up @@ -468,6 +475,7 @@
7AA5450E45A4DB17626F4684 /* WebView.swift */,
7AA54FE4F9D6DAEF984F63E6 /* Observable+.swift */,
7AA542A3362BE85B313A88B8 /* UIImage+.swift */,
7AA54DEAC9AF2E17ED0FADC3 /* Date+.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -499,6 +507,7 @@
7D1B1A8A1F98F86400C1F5FF /* Frameworks */ = {
isa = PBXGroup;
children = (
7D92DAF220586FBA00195A1B /* RxOptional.framework */,
7DEA1C4120362E09008AD7C4 /* Differentiator.framework */,
7D320B3420360F9000891F73 /* RxDataSources.framework */,
D83C9FBD1FAD3D5800D08AAE /* Telemetry.framework */,
Expand Down Expand Up @@ -835,6 +844,7 @@
7AA54EC7A9CA9BBD57AFEAF9 /* FilterCell.swift in Sources */,
7AA544AA5AE3DE25FE0B0B74 /* CopyAction.swift in Sources */,
7AA545D6182E36466BC8256A /* CopyConfirmationDisplayStore.swift in Sources */,
7AA5436C61447D25F2167706 /* Date+.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
86 changes: 74 additions & 12 deletions lockbox-ios/Action/DataStoreAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,21 @@ enum DataStoreError: Error {
}

enum JSCallbackFunction: String {
case OpenComplete, InitializeComplete, UnlockComplete, LockComplete, ListComplete
case OpenComplete, InitializeComplete, UnlockComplete, LockComplete, ListComplete, UpdateComplete

static let allValues: [JSCallbackFunction] = [
.OpenComplete,
.InitializeComplete,
.UnlockComplete,
.LockComplete,
.ListComplete
.ListComplete,
.UpdateComplete
]
}

enum DataStoreAction: Action {
case list(list: [Item])
case list(list: [String: Item])
case updated(item: Item)
case locked(locked: Bool)
case initialized(initialized: Bool)
case opened(opened: Bool)
Expand All @@ -34,8 +36,10 @@ enum DataStoreAction: Action {
extension DataStoreAction: Equatable {
static func ==(lhs: DataStoreAction, rhs: DataStoreAction) -> Bool {
switch (lhs, rhs) {
case (.list(let lhList), .list(let rhList)):
return lhList.elementsEqual(rhList)
case (.list, .list):
return true
case (.updated(let lhItem), .updated(let rhItem)):
return lhItem == rhItem
case (.locked(let lhLocked), .locked(let rhLocked)):
return lhLocked == rhLocked
case (.initialized(let lhInitialized), .initialized(let rhInitialized)):
Expand Down Expand Up @@ -63,7 +67,8 @@ class DataStoreActionHandler: NSObject, ActionHandler {
private var initializeSubject = PublishSubject<Void>()
private var unlockSubject = PublishSubject<Void>()
private var lockSubject = PublishSubject<Void>()
private var listSubject = PublishSubject<[Item]>()
private var listSubject = PublishSubject<[String: Item]>()
private var updateSubject = PublishSubject<Item>()

internal var webViewConfiguration: WKWebViewConfiguration {
let webConfig = WKWebViewConfiguration()
Expand Down Expand Up @@ -182,12 +187,26 @@ class DataStoreActionHandler: NSObject, ActionHandler {
self?.dispatcher.dispatch(action: DataStoreAction.list(list: itemList))
}, onError: { [weak self] error in
self?.dispatcher.dispatch(action: ErrorAction(error: error))
self?.listSubject = PublishSubject<[Item]>()
self?.listSubject = PublishSubject<[String: Item]>()
})
.disposed(by: self.disposeBag)

self._list()
}

public func touch(_ item: Item) {
self.updateSubject
.take(1)
.subscribe(onNext: { [weak self] item in
self?.dispatcher.dispatch(action: DataStoreAction.updated(item: item))
}, onError: { [weak self] error in
self?.dispatcher.dispatch(action: ErrorAction(error: error))
self?.updateSubject = PublishSubject<Item>()
})
.disposed(by: self.disposeBag)

self._touch(item)
}
}

// javascript interaction
Expand Down Expand Up @@ -273,6 +292,26 @@ extension DataStoreActionHandler {
.disposed(by: self.disposeBag)
}

private func _touch(_ item: Item) {
self.openSubject.take(1)
.flatMap { _ in
self.checkState()
}
.flatMap { _ -> Single<Any> in
if item.id == nil {
throw DataStoreError.NoIDPassed
}

let jsonItem = try self.parser.jsonStringFromItem(item)

return self.webView.evaluateJavaScript("\(self.dataStoreName).touch(\(jsonItem))")
}
.subscribe(onError: { error in
self.updateSubject.onError(error)
})
.disposed(by: self.disposeBag)
}

private func checkState() -> Single<Bool> {
return _initialized().asObservable()
.flatMap { initialized -> Observable<Bool> in
Expand All @@ -291,6 +330,23 @@ extension DataStoreActionHandler {
}
.asSingle()
}

private func completeSubjectWithBody(messageBody: Any, subject: PublishSubject<Item>) {
guard let itemDictionary = messageBody as? [String: Any] else {
subject.onError(DataStoreError.UnexpectedType)
return
}

var item: Item
do {
item = try self.parser.itemFromDictionary(itemDictionary)
} catch {
subject.onError(error)
return
}

subject.onNext(item)
}
}

extension DataStoreActionHandler: WKScriptMessageHandler, WKNavigationDelegate {
Expand All @@ -313,22 +369,28 @@ extension DataStoreActionHandler: WKScriptMessageHandler, WKNavigationDelegate {
self.unlockSubject.onNext(())
case .LockComplete:
self.lockSubject.onNext(())
case .UpdateComplete:
self.completeSubjectWithBody(messageBody: message.body, subject: self.updateSubject)
case .ListComplete:
guard let listBody = message.body as? [[Any]] else {
self.dispatcher.dispatch(action: ErrorAction(error: DataStoreError.UnexpectedType))
break
}

let itemList = listBody.flatMap { (anyList: [Any]) -> Item? in
guard let itemDictionary = anyList[1] as? [String: Any],
let itemDictionary = listBody.reduce([:]) { dict, anyList -> [String: Item] in
guard let itemId = anyList[0] as? String,
let itemDictionary = anyList[1] as? [String: Any],
let item = try? self.parser.itemFromDictionary(itemDictionary) else {
return nil
return dict
}

return item
var updatedDict = dict
updatedDict[itemId] = item

return updatedDict
}

self.listSubject.onNext(itemList)
self.listSubject.onNext(itemDictionary)
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions lockbox-ios/Common/Extensions/Date+.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import Foundation

extension Date {
var iso8601: String {
return ISO8601DateFormatter().string(from: self)
}
}
15 changes: 14 additions & 1 deletion lockbox-ios/Model/Item.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ class Item: Codable, Equatable {
var lastUsed: String?
var entry: ItemEntry

enum CodingKeys: String, CodingKey {
case id = "id"
case disabled = "disabled"
case title = "title"
case origins = "origins"
case tags = "tags"
case created = "created"
case modified = "modified"
case lastUsed = "last_used"
case entry = "entry"
}

init(origins: [String], entry: ItemEntry) {
self.origins = origins
self.entry = entry
Expand All @@ -23,7 +35,8 @@ class Item: Codable, Equatable {
static func ==(lhs: Item, rhs: Item) -> Bool {
return lhs.id == rhs.id &&
lhs.entry == rhs.entry &&
lhs.origins.elementsEqual(rhs.origins)
lhs.origins.elementsEqual(rhs.origins) &&
lhs.modified == rhs.modified

}

Expand Down
4 changes: 4 additions & 0 deletions lockbox-ios/Presenter/ItemDetailPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class ItemDetailPresenter {
private var itemDetailStore: ItemDetailStore
private var copyDisplayStore: CopyConfirmationDisplayStore
private var routeActionHandler: RouteActionHandler
private var dataStoreActionHandler: DataStoreActionHandler
private var copyActionHandler: CopyActionHandler
private var itemDetailActionHandler: ItemDetailActionHandler
private var disposeBag = DisposeBag()
Expand Down Expand Up @@ -54,6 +55,7 @@ class ItemDetailPresenter {
text = item.entry.password ?? ""
}

target.dataStoreActionHandler.touch(item)
target.copyActionHandler.invoke(CopyAction(text: text, fieldName: value))
})
.disposed(by: target.disposeBag)
Expand All @@ -66,13 +68,15 @@ class ItemDetailPresenter {
itemDetailStore: ItemDetailStore = ItemDetailStore.shared,
copyDisplayStore: CopyConfirmationDisplayStore = CopyConfirmationDisplayStore.shared,
routeActionHandler: RouteActionHandler = RouteActionHandler.shared,
dataStoreActionHandler: DataStoreActionHandler = DataStoreActionHandler.shared,
copyActionHandler: CopyActionHandler = CopyActionHandler.shared,
itemDetailActionHandler: ItemDetailActionHandler = ItemDetailActionHandler.shared) {
self.view = view
self.dataStore = dataStore
self.itemDetailStore = itemDetailStore
self.copyDisplayStore = copyDisplayStore
self.routeActionHandler = routeActionHandler
self.dataStoreActionHandler = dataStoreActionHandler
self.copyActionHandler = copyActionHandler
self.itemDetailActionHandler = itemDetailActionHandler

Expand Down
28 changes: 21 additions & 7 deletions lockbox-ios/Store/DataStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,22 @@
import Foundation
import RxSwift
import RxCocoa
import RxOptional

class DataStore {
public static let shared = DataStore()

fileprivate let disposeBag = DisposeBag()
fileprivate var itemList = ReplaySubject<[Item]>.create(bufferSize: 1)
fileprivate var itemList = ReplaySubject<[String: Item]>.create(bufferSize: 1)
fileprivate var initialized = ReplaySubject<Bool>.create(bufferSize: 1)
fileprivate var opened = ReplaySubject<Bool>.create(bufferSize: 1)
fileprivate var locked = ReplaySubject<Bool>.create(bufferSize: 1)

public var onItemList: Observable<[Item]> {
return self.itemList.asObservable()
.map { itemDictionary -> [Item] in
return Array(itemDictionary.values)
}
.distinctUntilChanged { lhList, rhList in
return lhList.elementsEqual(rhList)
}
Expand All @@ -41,6 +45,19 @@ class DataStore {
switch action {
case .list(let list):
self.itemList.onNext(list)
case .updated(let item):
self.itemList.take(1)
.map { items in
guard let id = item.id else {
return items
}

var updatedItems = items
updatedItems[id] = item
return updatedItems
}
.bind(to: self.itemList)
.disposed(by: self.disposeBag)
case .locked(let locked):
self.locked.onNext(locked)
case .initialized(let initialized):
Expand All @@ -54,13 +71,10 @@ class DataStore {

public func onItem(_ itemId: String) -> Observable<Item> {
return self.itemList.asObservable()
.flatMap { list in
Observable.from(list)
}
.filterByType(class: Item.self)
.filter { item in
return item.id == itemId
.map { items -> Item? in
return items[itemId]
}
.filterNil()
.distinctUntilChanged()
}
}
10 changes: 10 additions & 0 deletions lockbox-ios/lockbox-datastore/dswrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,14 @@ class SwiftInteropDataStore extends DataStoreModule.DataStore {
}
})
}

async touch(item) {
return super.touch(item).then( function(updatedItem) {
try {
webkit.messageHandlers.UpdateComplete.postMessage(updatedItem)
} catch (err) {
console.log("callback function not available")
}
})
}
}
Loading

0 comments on commit b7d51d2

Please sign in to comment.