2017-01-22 16:22:05 +03:00
|
|
|
/**
|
|
|
|
* Tae Won Ha - http://taewon.de - @hataewon
|
|
|
|
* See LICENSE
|
|
|
|
*/
|
2017-01-17 21:47:59 +03:00
|
|
|
|
|
|
|
import Foundation
|
|
|
|
import RxSwift
|
|
|
|
|
2017-02-28 12:43:45 +03:00
|
|
|
class Context {
|
2017-01-17 21:47:59 +03:00
|
|
|
|
2017-01-25 00:39:19 +03:00
|
|
|
let stateSource: Observable<AppState>
|
2017-04-22 16:56:13 +03:00
|
|
|
let actionEmitter = ActionEmitter()
|
2017-01-17 21:47:59 +03:00
|
|
|
|
2017-04-11 20:26:34 +03:00
|
|
|
init(baseServerUrl: URL, state: AppState) {
|
2017-02-28 12:42:34 +03:00
|
|
|
self.appState = state
|
2017-02-09 02:31:49 +03:00
|
|
|
self.stateSource = self.stateSubject.asObservable()
|
|
|
|
|
2017-03-29 21:10:11 +03:00
|
|
|
let openQuicklyReducer = OpenQuicklyReducer()
|
2017-04-11 20:26:34 +03:00
|
|
|
let markdownReducer = MarkdownReducer(baseServerUrl: baseServerUrl)
|
2017-02-28 12:20:25 +03:00
|
|
|
|
2017-04-02 18:15:30 +03:00
|
|
|
let prefService = PrefService()
|
2017-02-28 12:20:25 +03:00
|
|
|
let previewService = PreviewService()
|
2017-04-30 10:25:15 +03:00
|
|
|
let httpService: HttpServerService = HttpServerService(port: baseServerUrl.port!)
|
2017-02-28 12:20:25 +03:00
|
|
|
|
2017-02-28 12:42:34 +03:00
|
|
|
// AppState
|
2017-01-22 16:22:05 +03:00
|
|
|
Observable
|
|
|
|
.of(
|
2017-04-22 12:31:42 +03:00
|
|
|
self.actionSourceForAppState()
|
2017-04-30 10:25:15 +03:00
|
|
|
.reduce(by: AppDelegateReducer(baseServerUrl: baseServerUrl).reduce)
|
|
|
|
.filterMapPair(),
|
2017-04-22 12:31:42 +03:00
|
|
|
self.actionSourceForAppState()
|
2017-04-30 10:25:15 +03:00
|
|
|
.reduce(by: UiRootReducer().reduce)
|
|
|
|
.reduce(by: openQuicklyReducer.reduceMainWindow)
|
|
|
|
.filter { $0.modified }
|
|
|
|
.apply(prefService.applyMainWindow)
|
|
|
|
.map { $0.state },
|
2017-04-22 12:31:42 +03:00
|
|
|
self.actionSourceForAppState()
|
2017-04-30 10:25:15 +03:00
|
|
|
.reduce(by: FileMonitorReducer().reduce)
|
|
|
|
.filterMapPair(),
|
2017-04-22 12:31:42 +03:00
|
|
|
self.actionSourceForAppState()
|
2017-04-30 10:25:15 +03:00
|
|
|
.reduce(by: openQuicklyReducer.reduceOpenQuicklyWindow)
|
|
|
|
.filterMapPair()
|
2017-01-22 16:22:05 +03:00
|
|
|
)
|
|
|
|
.merge()
|
2017-03-31 09:30:47 +03:00
|
|
|
.subscribe(onNext: self.emitAppState)
|
2017-04-22 17:30:40 +03:00
|
|
|
.disposed(by: self.disposeBag)
|
2017-01-17 21:47:59 +03:00
|
|
|
|
2017-02-28 12:42:34 +03:00
|
|
|
// MainWindow.State
|
2017-02-12 13:24:15 +03:00
|
|
|
Observable
|
2017-02-24 02:24:30 +03:00
|
|
|
.of(
|
2017-04-22 12:31:42 +03:00
|
|
|
self.actionSourceForMainWindow()
|
2017-04-30 10:25:15 +03:00
|
|
|
.reduce(by: MainWindowReducer().reduce)
|
|
|
|
.reduce(by: markdownReducer.reduceMainWindow)
|
|
|
|
.filter { $0.modified }
|
|
|
|
.apply(previewService.applyMainWindow)
|
|
|
|
.apply(httpService.applyMainWindow)
|
|
|
|
.map { $0.state },
|
2017-04-22 12:31:42 +03:00
|
|
|
self.actionSourceForMainWindow()
|
2017-04-30 10:25:15 +03:00
|
|
|
.reduce(by: PreviewToolReducer(baseServerUrl: baseServerUrl).reduce)
|
|
|
|
.filterMapPair(),
|
2017-04-22 12:31:42 +03:00
|
|
|
self.actionSourceForMainWindow()
|
2017-04-30 10:25:15 +03:00
|
|
|
.reduce(by: HtmlPreviewToolReducer(baseServerUrl: baseServerUrl).reduce)
|
|
|
|
.filter { $0.modified }
|
|
|
|
.apply(httpService.applyHtmlPreview)
|
|
|
|
.map { $0.state },
|
2017-04-22 12:31:42 +03:00
|
|
|
self.actionSourceForMainWindow()
|
2017-04-30 10:25:15 +03:00
|
|
|
.reduce(by: FileBrowserReducer().reduce)
|
|
|
|
.filterMapPair(),
|
2017-04-22 12:31:42 +03:00
|
|
|
self.actionSourceForMainWindow()
|
2017-04-30 10:25:15 +03:00
|
|
|
.reduce(by: OpenedFileListReducer().reduce)
|
|
|
|
.reduce(by: markdownReducer.reduceOpenedFileList)
|
|
|
|
.filter { $0.modified }
|
|
|
|
.apply(previewService.applyOpenedFileList)
|
|
|
|
.map { $0.state }
|
2017-02-24 02:24:30 +03:00
|
|
|
)
|
2017-02-12 13:24:15 +03:00
|
|
|
.merge()
|
2017-03-31 09:30:47 +03:00
|
|
|
.subscribe(onNext: self.emitAppState)
|
2017-04-22 17:30:40 +03:00
|
|
|
.disposed(by: self.disposeBag)
|
2017-02-11 20:32:22 +03:00
|
|
|
|
2017-02-28 12:42:34 +03:00
|
|
|
// Preferences
|
2017-02-28 00:45:26 +03:00
|
|
|
Observable
|
|
|
|
.of(
|
2017-04-30 10:25:15 +03:00
|
|
|
self.prefStateSource(by: PrefWindowReducer().reduce, prefService: prefService),
|
|
|
|
self.prefStateSource(by: GeneralPrefReducer().reduce, prefService: prefService),
|
2017-05-01 18:15:21 +03:00
|
|
|
self.prefStateSource(by: ToolsPrefReducer().reduce, prefService: prefService),
|
2017-04-30 10:25:15 +03:00
|
|
|
self.prefStateSource(by: AppearancePrefReducer().reduce, prefService: prefService),
|
|
|
|
self.prefStateSource(by: AdvancedPrefReducer().reduce, prefService: prefService)
|
2017-04-22 17:03:45 +03:00
|
|
|
)
|
2017-02-28 00:45:26 +03:00
|
|
|
.merge()
|
2017-03-31 09:30:47 +03:00
|
|
|
.subscribe(onNext: self.emitAppState)
|
2017-04-22 17:30:40 +03:00
|
|
|
.disposed(by: self.disposeBag)
|
2017-02-24 02:24:30 +03:00
|
|
|
|
2017-02-03 00:39:05 +03:00
|
|
|
#if DEBUG
|
2017-06-10 00:16:06 +03:00
|
|
|
// self.actionEmitter.observable.debug().subscribe().disposed(by: self.disposeBag)
|
2017-04-22 17:30:40 +03:00
|
|
|
// stateSource.debug().subscribe().disposed(by: self.disposeBag)
|
2017-02-03 00:39:05 +03:00
|
|
|
#endif
|
2017-01-17 21:47:59 +03:00
|
|
|
}
|
|
|
|
|
2017-02-12 12:53:24 +03:00
|
|
|
deinit {
|
|
|
|
self.stateSubject.onCompleted()
|
|
|
|
}
|
|
|
|
|
2017-01-25 00:39:19 +03:00
|
|
|
fileprivate let stateSubject = PublishSubject<AppState>()
|
2017-01-22 16:22:05 +03:00
|
|
|
fileprivate let scheduler = SerialDispatchQueueScheduler(qos: .userInitiated)
|
2017-01-17 21:47:59 +03:00
|
|
|
fileprivate let disposeBag = DisposeBag()
|
|
|
|
|
2017-02-06 20:57:50 +03:00
|
|
|
fileprivate var appState: AppState
|
2017-02-28 12:42:34 +03:00
|
|
|
|
2017-03-31 09:30:47 +03:00
|
|
|
fileprivate func emitAppState(_ mainWindow: UuidState<MainWindow.State>) {
|
|
|
|
self.appState.mainWindows[mainWindow.uuid] = mainWindow.payload
|
|
|
|
self.stateSubject.onNext(self.appState)
|
|
|
|
|
|
|
|
self.cleanUpAppState()
|
|
|
|
}
|
|
|
|
|
|
|
|
fileprivate func emitAppState(_ appState: AppState) {
|
|
|
|
self.appState = appState
|
|
|
|
self.stateSubject.onNext(self.appState)
|
|
|
|
|
|
|
|
self.cleanUpAppState()
|
|
|
|
}
|
|
|
|
|
|
|
|
fileprivate func cleanUpAppState() {
|
|
|
|
self.appState.mainWindows.keys.forEach { uuid in
|
2017-06-10 00:16:06 +03:00
|
|
|
self.appState.mainWindows[uuid]?.cwdToSet = nil
|
|
|
|
self.appState.mainWindows[uuid]?.currentBufferToSet = nil
|
2017-03-31 20:32:01 +03:00
|
|
|
self.appState.mainWindows[uuid]?.viewToBeFocused = nil
|
2017-03-31 09:30:47 +03:00
|
|
|
self.appState.mainWindows[uuid]?.urlsToOpen.removeAll()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-22 17:03:45 +03:00
|
|
|
fileprivate func actionSourceForAppState<ActionType>() -> Observable<StateActionPair<AppState, ActionType>> {
|
2017-04-22 12:31:42 +03:00
|
|
|
return self.actionEmitter.observable
|
2017-04-22 17:03:45 +03:00
|
|
|
.mapOmittingNil { $0 as? ActionType }
|
2017-04-22 12:31:42 +03:00
|
|
|
.map { self.appStateActionPair(for: $0) }
|
|
|
|
}
|
|
|
|
|
2017-04-22 17:03:45 +03:00
|
|
|
fileprivate func actionSourceForMainWindow<ActionType>()
|
|
|
|
-> Observable<StateActionPair<UuidState<MainWindow.State>, ActionType>> {
|
2017-04-22 12:31:42 +03:00
|
|
|
return self.actionEmitter.observable
|
2017-04-22 17:03:45 +03:00
|
|
|
.mapOmittingNil { $0 as? UuidAction<ActionType> }
|
2017-04-22 12:31:42 +03:00
|
|
|
.mapOmittingNil { self.mainWindowStateActionPair(for: $0) }
|
|
|
|
}
|
|
|
|
|
2017-04-30 10:25:15 +03:00
|
|
|
fileprivate func prefStateSource<ActionType>(
|
|
|
|
by reduce: @escaping (StateActionPair<AppState, ActionType>) -> StateActionPair<AppState, ActionType>,
|
|
|
|
prefService: PrefService
|
|
|
|
) -> Observable<AppState> {
|
|
|
|
return self.actionSourceForAppState()
|
|
|
|
.reduce(by: reduce)
|
|
|
|
.filter { $0.modified }
|
|
|
|
.apply(prefService.applyPref)
|
|
|
|
.map { $0.state }
|
|
|
|
}
|
|
|
|
|
2017-02-28 12:42:34 +03:00
|
|
|
fileprivate func appStateActionPair<ActionType>(for action: ActionType) -> StateActionPair<AppState, ActionType> {
|
|
|
|
return StateActionPair(state: self.appState, action: action, modified: false)
|
|
|
|
}
|
|
|
|
|
|
|
|
fileprivate func mainWindowStateActionPair<ActionType>(for action: UuidAction<ActionType>)
|
|
|
|
-> StateActionPair<UuidState<MainWindow.State>, ActionType>? {
|
|
|
|
guard let mainWindowState = self.appState.mainWindows[action.uuid] else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return StateActionPair(state: UuidState(uuid: action.uuid, state: mainWindowState),
|
|
|
|
action: action.payload,
|
|
|
|
modified: false)
|
|
|
|
}
|
2017-01-17 21:47:59 +03:00
|
|
|
}
|