1
1
mirror of https://github.com/qvacua/vimr.git synced 2024-11-24 03:25:03 +03:00
vimr/VimR/Context.swift

217 lines
7.2 KiB
Swift
Raw Normal View History

2017-01-22 16:22:05 +03:00
/**
* Tae Won Ha - http://taewon.de - @hataewon
* See LICENSE
*/
import Foundation
import RxSwift
2017-02-28 12:43:45 +03:00
class Context {
2017-01-25 00:39:19 +03:00
let stateSource: Observable<AppState>
let actionEmitter = Emitter<Any>()
2017-02-28 12:42:34 +03:00
init(_ state: AppState) {
2017-02-20 20:04:45 +03:00
let baseServerUrl = URL(string: "http://localhost:\(NetUtils.openPort())")!
2017-02-28 12:42:34 +03:00
self.appState = state
self.stateSource = self.stateSubject.asObservable()
2017-02-12 13:24:15 +03:00
let actionSource = self.actionEmitter.observable
2017-03-29 21:10:11 +03:00
let openQuicklyReducer = OpenQuicklyReducer()
let previewReducer = PreviewReducer(baseServerUrl: baseServerUrl)
2017-02-28 12:20:25 +03:00
let previewService = PreviewService()
2017-03-30 01:36:57 +03:00
// For clean quit
stateSource
2017-03-30 21:03:09 +03:00
.filter { $0.quitWhenNoMainWindow && $0.mainWindows.isEmpty }
2017-03-30 01:36:57 +03:00
.subscribe(onNext: { state in NSApp.stop(self) })
.addDisposableTo(self.disposeBag)
2017-02-28 12:42:34 +03:00
// AppState
2017-01-22 16:22:05 +03:00
Observable
.of(
actionSource
.mapOmittingNil { $0 as? AppDelegate.Action }
2017-02-28 12:42:34 +03:00
.map { self.appStateActionPair(for: $0) }
2017-03-29 21:10:11 +03:00
.reduce(by: AppDelegateReducer(baseServerUrl: baseServerUrl))
2017-01-22 16:22:05 +03:00
.filter { $0.modified }
.map { $0.state },
actionSource
.mapOmittingNil { $0 as? UuidAction<MainWindow.Action> }
2017-02-28 12:42:34 +03:00
.map { self.appStateActionPair(for: $0) }
2017-03-29 21:10:11 +03:00
.reduce(by: UiRootReducer())
.reduce(by: openQuicklyReducer.forMainWindow)
.filter { $0.modified }
2017-02-28 20:53:49 +03:00
.apply(to: PrefService())
.map { $0.state },
actionSource
.mapOmittingNil { $0 as? FileMonitor.Action }
2017-02-28 12:42:34 +03:00
.map { self.appStateActionPair(for: $0) }
2017-03-29 21:10:11 +03:00
.reduce(by: FileMonitorReducer())
.filter { $0.modified }
.map { $0.state },
actionSource
.mapOmittingNil { $0 as? OpenQuicklyWindow.Action }
2017-02-28 12:42:34 +03:00
.map { self.appStateActionPair(for: $0) }
2017-03-29 21:10:11 +03:00
.reduce(by: openQuicklyReducer.forOpenQuicklyWindow)
2017-01-22 16:22:05 +03:00
.filter { $0.modified }
.map { $0.state }
)
.merge()
.subscribe(onNext: self.emitAppState)
.addDisposableTo(self.disposeBag)
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(
actionSource
.mapOmittingNil { $0 as? UuidAction<MainWindow.Action> }
2017-02-28 12:42:34 +03:00
.mapOmittingNil { self.mainWindowStateActionPair(for: $0) }
2017-03-29 21:10:11 +03:00
.reduce(by: MainWindowReducer())
.reduce(by: previewReducer.forMainWindow)
2017-02-24 02:24:30 +03:00
.filter { $0.modified }
2017-02-28 12:20:25 +03:00
.apply(to: previewService.forMainWindow)
.apply(to: HttpServerService(port: baseServerUrl.port ?? 0))
2017-02-24 02:24:30 +03:00
.map { $0.state },
actionSource
.mapOmittingNil { $0 as? UuidAction<PreviewTool.Action> }
2017-02-28 12:42:34 +03:00
.mapOmittingNil { self.mainWindowStateActionPair(for: $0) }
2017-03-29 21:10:11 +03:00
.reduce(by: PreviewToolReducer(baseServerUrl: baseServerUrl))
2017-02-24 02:24:30 +03:00
.filter { $0.modified }
.map { $0.state },
actionSource
2017-02-24 12:51:24 +03:00
.mapOmittingNil { $0 as? UuidAction<FileBrowser.Action> }
2017-02-28 12:42:34 +03:00
.mapOmittingNil { self.mainWindowStateActionPair(for: $0) }
2017-03-29 21:10:11 +03:00
.reduce(by: FileBrowserReducer())
2017-02-24 02:24:30 +03:00
.filter { $0.modified }
.map { $0.state },
actionSource
.mapOmittingNil { $0 as? UuidAction<OpenedFileList.Action> }
2017-02-28 12:42:34 +03:00
.mapOmittingNil { self.mainWindowStateActionPair(for: $0) }
2017-03-29 21:10:11 +03:00
.reduce(by: OpenedFileListReducer())
.reduce(by: previewReducer.forOpenedFileList)
.filter { $0.modified }
2017-02-28 12:20:25 +03:00
.apply(to: previewService.forOpenedFileList)
2017-02-24 02:24:30 +03:00
.map { $0.state }
)
2017-02-12 13:24:15 +03:00
.merge()
.subscribe(onNext: self.emitAppState)
.addDisposableTo(self.disposeBag)
2017-02-28 12:42:34 +03:00
// Preferences
2017-02-28 00:45:26 +03:00
Observable
.of(
actionSource
.mapOmittingNil { $0 as? PrefWindow.Action }
2017-02-28 12:42:34 +03:00
.map { self.appStateActionPair(for: $0) }
2017-03-29 21:10:11 +03:00
.reduce(by: PrefWindowReducer())
2017-02-28 00:45:26 +03:00
.filter { $0.modified }
.map { $0.state },
actionSource
.mapOmittingNil { $0 as? GeneralPref.Action }
2017-02-28 12:42:34 +03:00
.map { self.appStateActionPair(for: $0) }
2017-03-29 21:10:11 +03:00
.reduce(by: GeneralPrefReducer())
2017-02-28 11:53:27 +03:00
.filter { $0.modified }
.map { $0.state },
actionSource
.mapOmittingNil { $0 as? AppearancePref.Action }
2017-02-28 12:42:34 +03:00
.map { self.appStateActionPair(for: $0) }
2017-03-29 21:10:11 +03:00
.reduce(by: AppearancePrefReducer())
2017-02-28 00:45:26 +03:00
.filter { $0.modified }
2017-02-28 13:10:04 +03:00
.map { $0.state },
actionSource
.mapOmittingNil { $0 as? AdvancedPref.Action }
.map { self.appStateActionPair(for: $0) }
.reduce(by: AdvancedPrefReducer())
2017-02-28 13:10:04 +03:00
.filter { $0.modified }
2017-02-28 00:45:26 +03:00
.map { $0.state }
)
.merge()
.subscribe(onNext: self.emitAppState)
2017-02-28 00:45:26 +03:00
.addDisposableTo(self.disposeBag)
2017-02-24 02:24:30 +03:00
2017-02-03 00:39:05 +03:00
#if DEBUG
2017-02-24 02:24:30 +03:00
// actionSource.debug().subscribe().addDisposableTo(self.disposeBag)
// stateSource
// .filter { $0.mainWindows.values.count > 0 }
// .map { Array($0.mainWindows.values)[0].preview }
// .debug()
// .subscribe(onNext: { state in
// })
// .addDisposableTo(self.disposeBag)
2017-02-03 00:39:05 +03:00
#endif
}
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)
fileprivate let disposeBag = DisposeBag()
2017-02-06 20:57:50 +03:00
fileprivate var appState: AppState
2017-02-28 12:42:34 +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-04-01 22:02:31 +03:00
if self.appState.mainWindows[uuid]?.close == true {
self.appState.mainWindows.removeValue(forKey: uuid)
return
}
2017-03-31 20:32:01 +03:00
self.appState.mainWindows[uuid]?.viewToBeFocused = nil
self.appState.mainWindows[uuid]?.urlsToOpen.removeAll()
}
}
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)
}
}
extension Observable {
2017-03-29 21:23:04 +03:00
fileprivate func reduce<T:Reducer>(by transformer: T) -> Observable<Element> where T.Pair == Element {
2017-03-29 21:17:45 +03:00
return transformer.reduce(self)
}
// If the following is used, the compiler does not finish...
// fileprivate func transform<T:Reducer>(by transformers: [T]) -> Observable<Element> where T.Element == Element {
// return transformers.reduce(self) { (result: Observable<Element>, transformer: T) -> Observable<Element> in
// transformer.transform(result)
// }
// }
2017-03-29 21:23:04 +03:00
fileprivate func apply<S:Service>(to service: S) -> Observable<Element> where S.Pair == Element {
2017-02-03 00:39:05 +03:00
return self.do(onNext: service.apply)
}
}