Commit d624dc04 authored by Ruben Daniels's avatar Ruben Daniels
Browse files

Add setting to toggle CVU listener

parent 0bab175f
Showing with 160 additions and 32 deletions
+160 -32
......@@ -158,6 +158,7 @@
866F87EC24572525000E5FAB /* ThumbGridRendererView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 866F87EB24572525000E5FAB /* ThumbGridRendererView.swift */; };
8672C27C2497F35A006F0D42 /* CustomRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8672C27B2497F35A006F0D42 /* CustomRenderer.swift */; };
867527BA24B76F5B002B5006 /* CachePubSub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 867527B924B76F5B002B5006 /* CachePubSub.swift */; };
867527BC24B8290B002B5006 /* SettingsPubSub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 867527BB24B8290B002B5006 /* SettingsPubSub.swift */; };
86865D3324487B390010271A /* SettingsPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86865D3224487B390010271A /* SettingsPane.swift */; };
86865D36244996900010271A /* styles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86865D35244996900010271A /* styles.swift */; };
86865D38244996AA0010271A /* FlowStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86865D37244996AA0010271A /* FlowStack.swift */; };
......@@ -414,6 +415,7 @@
866F87EB24572525000E5FAB /* ThumbGridRendererView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbGridRendererView.swift; sourceTree = "<group>"; };
8672C27B2497F35A006F0D42 /* CustomRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRenderer.swift; sourceTree = "<group>"; };
867527B924B76F5B002B5006 /* CachePubSub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachePubSub.swift; sourceTree = "<group>"; };
867527BB24B8290B002B5006 /* SettingsPubSub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPubSub.swift; sourceTree = "<group>"; };
86865D3224487B390010271A /* SettingsPane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPane.swift; sourceTree = "<group>"; wrapsLines = 0; };
86865D35244996900010271A /* styles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = styles.swift; sourceTree = "<group>"; };
86865D37244996AA0010271A /* FlowStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowStack.swift; sourceTree = "<group>"; };
......@@ -914,6 +916,7 @@
861C52C124A5A9B100B3230C /* schema.swift */,
8665ADD8241A8387005B3FA5 /* Cache.swift */,
867527B924B76F5B002B5006 /* CachePubSub.swift */,
867527BB24B8290B002B5006 /* SettingsPubSub.swift */,
868EF696247784C600C0375F /* ResultSet.swift */,
86A5D8392437321700F8FAD8 /* Sync.swift */,
5D78D071242531BE00F7BD3B /* MainNavigation.swift */,
......@@ -1566,6 +1569,7 @@
8690AF022472D8C300FC49F4 /* view.swift in Sources */,
868EF659247678BC00C0375F /* Colors.swift in Sources */,
5D301F62245C372400183BD2 /* ListRendererView.swift in Sources */,
867527BC24B8290B002B5006 /* SettingsPubSub.swift in Sources */,
86C3A73524518763006D4F55 /* ItemCell.swift in Sources */,
B81237ED24A43E42003E8BCB /* MapHelper.swift in Sources */,
868EF6992477960D00C0375F /* HashableClass.swift in Sources */,
......
......@@ -37,13 +37,13 @@ AuditItem[] {
}
Spacer
Text {
text: "{.date}"
text: "{.dateCreated}"
font: 11 regular
color: #888
}
}
Text {
text: "{.contents}"
text: "{.content}"
font: 14 light
removeWhiteSpace: true
maxChar: 100
......
......@@ -134,10 +134,6 @@ class DebugHistory: ObservableObject {
))
}
if Settings.get("device/debug/autoShowErrorConsole") ?? false {
showErrorConsole = true
}
print("\(time()) WARNING: \(message.replace("\n", "\n "))")
}
......
......@@ -12,7 +12,8 @@ public class Views {
private var recursionCounter = 0
private var realm: Realm
private var cancellable: AnyCancellable?
private var CVUWatcher: AnyCancellable? = nil
private var settingWatcher: AnyCancellable? = nil
init(_ rlm: Realm) {
realm = rlm
......@@ -24,15 +25,28 @@ public class Views {
try setCurrentLanguage(context?.settings.get("user/language") ?? "English")
// Subscribe to changes in CVUStoredDefinition
#warning("Add a setting to control this (turn on and off)")
cancellable = context?.cache.subscribe(query: "CVUStoredDefinition").sink { items in // CVUStoredDefinition AND domain='user'
self.reloadViews(items)
settingWatcher = context?.settings.subscribe("device/debug/autoReloadCVU", type:Bool.self).sink {
if let value = $0 as? Bool {
if value && self.CVUWatcher == nil {
self.listenForChanges()
}
else if !value, let c = self.CVUWatcher {
c.cancel()
self.CVUWatcher = nil
}
}
}
// Done
try callback()
}
public func listenForChanges() {
// Subscribe to changes in CVUStoredDefinition
CVUWatcher = context?.cache.subscribe(query: "CVUStoredDefinition").sink { items in // CVUStoredDefinition AND domain='user'
self.reloadViews(items)
}
}
// TODO: refactor when implementing settings UI call this when changing the language
public func setCurrentLanguage(_ language: String) throws {
......
......@@ -40,20 +40,6 @@ struct SettingsPane: View {
var body: some View {
NavigationView {
Form {
// Section(header: Text("General")) {
// DatePicker(selection: getBinding("/user/formatting/date"), in: ...Date(), displayedComponents: .date) {
// Text("Date Format")
// }
//
// Picker(selection: $citySelected, label: Text("Choose a city:")) {
// ForEach(0 ..< Self.cities.count) {
// Text(Self.cities[$0])
// }
// }
// if addOwnCity {
// TextField("Enter your own city" ,text: $ownCity)
// }
// }
NavigationLink(destination: Form {
Section(
header: Text("Pod Connection"),
......@@ -126,8 +112,11 @@ struct SettingsPane: View {
header: Text("Debug")
) {
Toggle(isOn: getBinding("/device/debug/autoShowErrorConsole")) {
Text("Automatically pop up the debug console")
Text("Automatically pop up the debug console on errors")
}
Toggle(isOn: getBinding("/device/debug/autoReloadCVU")) {
Text("Automatically reload CVU when it changes")
}
}
}) {
Text("Debug")
......
......@@ -560,7 +560,17 @@ public class Cache {
let excluded = ["uid", "dateCreated", "dateAccessed", "dateModified"]
for prop in properties {
if !excluded.contains(prop.name), values[prop.name] != nil {
fromCache[prop.name] = values[prop.name] as Any?
if prop.type == .date {
if let date = values[prop.name] as? Int {
fromCache[prop.name] = Date(timeIntervalSince1970: Double(date/1000))
}
else {
throw "Invalid date received for \(prop.name) got \(String(describing: values[prop.name] ?? "") )"
}
}
else {
fromCache[prop.name] = values[prop.name] as Any?
}
}
}
fromCache["dateModified"] = Date()
......
......@@ -226,7 +226,7 @@ struct QueryPublisher: Publisher {
}
func receive<S>(subscriber: S) where S : Subscriber,
S.Failure == QueryPublisher.Failure, S.Input == [Item] {
S.Failure == QueryPublisher.Failure, S.Input == QueryPublisher.Output {
// TODO
let subscription = QuerySubscription(
......
......@@ -6,6 +6,7 @@
import Foundation
import RealmSwift
import Combine
/// This class stores the settings used in the memri app. Settings may include things like how to format dates, whether to show certain
/// buttons by default, etc.
......@@ -14,6 +15,9 @@ public class Settings {
let realm: Realm
/// Default settings
var settings: Results<Setting>
private var listeners = [String: [UUID]]()
private var callbacks = [UUID: (Any?) -> Void]()
/// Init settings with the relam database
/// - Parameter rlm: realm database object
......@@ -64,7 +68,8 @@ public class Settings {
}
private func getSearchPaths(_ path: String) throws -> [String] {
let splits = path.split(separator: "/")
let p = path.first == "/" ? String(path.suffix(path.count - 1)) : path
let splits = p.split(separator: "/")
let type = splits.first
let query = splits.dropFirst().joined(separator: "/")
......@@ -88,6 +93,7 @@ public class Settings {
throw "Missing scope 'user' or 'device' as the start of the path"
}
try setSetting(searchPaths[0], value as? AnyCodable ?? AnyCodable(value))
fire(searchPaths[0], (value as? AnyCodable)?.value ?? value)
} catch {
debugHistory.error("\(error)")
print(error)
......@@ -97,7 +103,7 @@ public class Settings {
/// get setting for given path
/// - Parameter path: path for the setting
/// - Returns: setting value
public func getSetting<T: Decodable>(_ path: String) throws -> T? {
public func getSetting<T: Decodable>(_ path: String, type: T.Type = T.self) throws -> T? {
let item = settings.first(where: { $0.key == path })
if let item = item, let json = item.json {
......@@ -136,7 +142,38 @@ public class Settings {
}
}
}
private func fire(_ path:String, _ value:Any?) {
if let list = self.listeners[path] {
for id in list {
if let f = self.callbacks[id] {
f(value)
}
}
}
}
func addListener<T:Decodable>(_ path:String, _ id:UUID, type: T.Type = T.self, _ f: @escaping (Any?) -> Void) throws {
guard let normalizedPath = try getSearchPaths(path).first else {
throw "Invalid path"
}
if self.listeners[normalizedPath] == nil { self.listeners[normalizedPath] = [] }
if !(self.listeners[normalizedPath]?.contains(id) ?? false) {
self.listeners[normalizedPath]?.append(id)
self.callbacks[id] = f
if let value = get(path, type:T.self) {
fire(normalizedPath, value)
}
}
}
func removeListener(_ path:String, _ id:UUID) {
self.listeners[path]?.removeAll(where: { $0 == id })
self.callbacks.removeValue(forKey: id)
}
/// Get *global* setting value for given path
/// - Parameter path: global setting path
/// - Returns: setting value
......
//
// SettingsPubSub.swift
//
// Copyright © 2020 memri. All rights reserved.
//
import Combine
import Foundation
import RealmSwift
final class SettingSubscription<SubscriberType: Subscriber, T:Decodable>: Subscription
where SubscriberType.Input == Any? {
private var id = UUID()
private var subscriber: SubscriberType?
private let path: String
private let settings: Settings
init (settings: Settings, subscriber: SubscriberType, path: String, type: T.Type) {
self.subscriber = subscriber
self.path = path
self.settings = settings
do {
try self.settings.addListener(path, id, type:type) { value in
_ = subscriber.receive(value)
}
}
catch let error {
debugHistory.warn("Unable to set listener for setting: \(path) : \(error)")
}
}
func request(_ demand: Subscribers.Demand) {
// We do nothing here as we only want to send events when they occur.
// See, for more info: https://developer.apple.com/documentation/combine/subscribers/demand
}
func cancel() {
self.settings.removeListener(path, id)
subscriber = nil
}
}
struct SettingPublisher<T:Decodable>: Publisher {
typealias Output = Any?
typealias Failure = Never
let path: String
let settings: Settings
let type: T.Type
init(settings: Settings, path: String, type: T.Type) {
self.path = path
self.settings = settings
self.type = type
}
func receive<S>(subscriber: S) where S : Subscriber,
S.Failure == SettingPublisher.Failure, S.Input == SettingPublisher.Output {
// TODO
let subscription = SettingSubscription(
settings: self.settings,
subscriber: subscriber,
path: path,
type: type
)
subscriber.receive(subscription: subscription)
}
}
extension Settings {
func subscribe<T>(_ path: String, type: T.Type = T.self) -> SettingPublisher<T> {
return SettingPublisher(settings: self, path: path, type: T.self)
}
}
......@@ -160,7 +160,7 @@ class Sync {
// We no longer need to process this log item
realmWriteIfAvailable(self.realm) {
self.realm.delete(audititem)
audititem.setSyncStateActionNeeded("")
}
}
} else {
......
......@@ -7,6 +7,7 @@
{ "_type": "Setting", "key": "defaults/pod/username", "value": ""},
{ "_type": "Setting", "key": "defaults/pod/password", "value": ""},
{ "_type": "Setting", "key": "defaults/debug/autoShowErrorConsole", "value": false},
{ "_type": "Setting", "key": "defaults/debug/autoReloadCVU", "value": true},
{
"_type": "NavigationItem",
"type": "heading",
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment