diff --git a/Kit/helpers.swift b/Kit/helpers.swift
index 4b19d381..b5c0fd4c 100644
--- a/Kit/helpers.swift
+++ b/Kit/helpers.swift
@@ -1316,6 +1316,13 @@ public class PreferencesSection: NSStackView {
}
public func setRowVisibility(_ at: Int, newState: Bool) {
+ if at == 0 {
+ self.container.subviews[0].isHidden = !newState
+ if self.container.subviews.count > 1 {
+ self.container.subviews[1].isHidden = !newState
+ }
+ return
+ }
for i in self.container.subviews.indices where i/2 == at && Double(i).remainder(dividingBy: 2) == 0 {
self.container.subviews[i-1].isHidden = !newState
self.container.subviews[i].isHidden = !newState
diff --git a/Kit/plugins/Remote.swift b/Kit/plugins/Remote.swift
index bfaeafc3..60c64ce5 100644
--- a/Kit/plugins/Remote.swift
+++ b/Kit/plugins/Remote.swift
@@ -14,15 +14,26 @@ import Cocoa
public class Remote {
public static let shared = Remote()
- static public var host = URL(string: "https://api.system-stats.com")! // https://api.system-stats.com http://localhost:8008
+ static public var host = URL(string: "http://localhost:8008")! // https://api.system-stats.com http://localhost:8008
- public var state: Bool {
- get { Store.shared.bool(key: "remote_state", defaultValue: false) }
+ public var monitoring: Bool {
+ get { Store.shared.bool(key: "remote_monitoring", defaultValue: false) }
set {
- Store.shared.set(key: "remote_state", value: newValue)
+ Store.shared.set(key: "remote_monitoring", value: newValue)
if newValue {
self.start()
- } else {
+ } else if !self.control {
+ self.stop()
+ }
+ }
+ }
+ public var control: Bool {
+ get { Store.shared.bool(key: "remote_control", defaultValue: false) }
+ set {
+ Store.shared.set(key: "remote_control", value: newValue)
+ if newValue {
+ self.start()
+ } else if !self.monitoring {
self.stop()
}
}
@@ -31,19 +42,18 @@ public class Remote {
public var isAuthorized: Bool = false
public var auth: RemoteAuth = RemoteAuth()
+ private let log: NextLog
private var ws: WebSocketManager = WebSocketManager()
private var wsURL: URL?
private var isConnecting = false
public init() {
+ self.log = NextLog.shared.copy(category: "Remote")
self.id = UUID(uuidString: Store.shared.string(key: "telemetry_id", defaultValue: UUID().uuidString)) ?? UUID()
- DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
- if self.state {
- self.start()
- } else {
- self.stop()
- }
+ if self.auth.hasCredentials() {
+ info("Found auth credentials for remote monitoring, starting Remote...", log: self.log)
+ self.start()
}
NotificationCenter.default.addObserver(self, selector: #selector(self.successLogin), name: .remoteLoginSuccess, object: nil)
@@ -56,7 +66,11 @@ public class Remote {
public func login() {
self.auth.login { url in
- guard let url else { return }
+ guard let url else {
+ error("Empty url when try to login", log: self.log)
+ return
+ }
+ debug("Open \(url) to login to Stats Remote", log: self.log)
NSWorkspace.shared.open(url)
}
}
@@ -64,24 +78,21 @@ public class Remote {
public func logout() {
self.auth.logout()
self.isAuthorized = false
- self.state = false
self.ws.disconnect()
- NotificationCenter.default.post(name: .remoteState, object: nil, userInfo: ["auth": self.isAuthorized, "state": self.state])
+ debug("Logout successfully from Stats Remote", log: self.log)
+ NotificationCenter.default.post(name: .remoteState, object: nil, userInfo: ["auth": self.isAuthorized])
}
public func send(key: String, value: Codable) {
- guard self.state && self.isAuthorized,
- let blobData = try? JSONEncoder().encode(value) else { return }
+ guard self.monitoring && self.isAuthorized, let blobData = try? JSONEncoder().encode(value) else { return }
self.ws.send(key: key, data: blobData)
}
@objc private func successLogin() {
self.isAuthorized = true
- NotificationCenter.default.post(name: .remoteState, object: nil, userInfo: ["auth": self.isAuthorized, "state": self.state])
-
- if self.state {
- self.ws.connect()
- }
+ NotificationCenter.default.post(name: .remoteState, object: nil, userInfo: ["auth": self.isAuthorized])
+ self.ws.connect()
+ debug("Login successfully on Stats Remote", log: self.log)
}
public func start() {
@@ -89,7 +100,7 @@ public class Remote {
guard let self else { return }
self.isAuthorized = status
- NotificationCenter.default.post(name: .remoteState, object: nil, userInfo: ["auth": self.isAuthorized, "state": self.state])
+ NotificationCenter.default.post(name: .remoteState, object: nil, userInfo: ["auth": self.isAuthorized])
if status {
self.ws.connect()
@@ -99,7 +110,7 @@ public class Remote {
private func stop() {
self.ws.disconnect()
- NotificationCenter.default.post(name: .remoteState, object: nil, userInfo: ["auth": self.isAuthorized, "state": self.state])
+ NotificationCenter.default.post(name: .remoteState, object: nil, userInfo: ["auth": self.isAuthorized])
}
}
@@ -128,8 +139,15 @@ public class RemoteAuth {
}
public func isAuthorized(completion: @escaping (Bool) -> Void) {
+ if !self.hasCredentials() {
+ completion(false)
+ return
+ }
self.validate(completion)
}
+ public func hasCredentials() -> Bool {
+ return !self.accessToken.isEmpty && !self.refreshToken.isEmpty
+ }
public func login(completion: @escaping (URL?) -> Void) {
self.registerDevice { device in
@@ -335,14 +353,17 @@ class WebSocketManager: NSObject {
private let reconnectDelay: TimeInterval = 3.0
private var pingTimer: Timer?
private var reachability: Reachability = Reachability(start: true)
+ private let log: NextLog
override init() {
+ self.log = NextLog.shared.copy(category: "Remote WS")
+
super.init()
self.session = URLSession(configuration: .default, delegate: self, delegateQueue: .main)
self.reachability.reachable = {
- if Remote.shared.state {
+ if Remote.shared.isAuthorized {
self.connect()
}
}
@@ -367,19 +388,25 @@ class WebSocketManager: NSObject {
self.webSocket?.resume()
self.receiveMessage()
self.isDisconnected = false
+ debug("connected successfully", log: self.log)
}
}
public func disconnect() {
+ if self.webSocket == nil && !self.isConnected { return }
self.isDisconnected = true
self.webSocket?.cancel(with: .normalClosure, reason: nil)
self.webSocket = nil
self.isConnected = false
+ debug("disconnected gracefully", log: self.log)
}
private func reconnect() {
guard !self.isDisconnected else { return }
DispatchQueue.main.asyncAfter(deadline: .now() + self.reconnectDelay) { [weak self] in
+ if let log = self?.log {
+ debug("trying to reconnect after some interruption", log: log)
+ }
self?.connect()
}
}
diff --git a/Stats/Supporting Files/Info.plist b/Stats/Supporting Files/Info.plist
index 3f6e9567..93001f5d 100755
--- a/Stats/Supporting Files/Info.plist
+++ b/Stats/Supporting Files/Info.plist
@@ -17,7 +17,7 @@
CFBundleShortVersionString
$(MARKETING_VERSION)
CFBundleVersion
- 689
+ 690
Description
Simple macOS system monitor in your menu bar
LSApplicationCategoryType
diff --git a/Stats/Views/AppSettings.swift b/Stats/Views/AppSettings.swift
index 49b78fa4..445699bc 100644
--- a/Stats/Views/AppSettings.swift
+++ b/Stats/Views/AppSettings.swift
@@ -42,7 +42,6 @@ class ApplicationSettings: NSStackView {
private var updateSelector: NSPopUpButton?
private var startAtLoginBtn: NSSwitch?
private var telemetryBtn: NSSwitch?
- private var remoteBtn: NSSwitch?
private var combinedModulesView: PreferencesSection?
private var fanHelperView: PreferencesSection?
@@ -129,17 +128,19 @@ class ApplicationSettings: NSStackView {
self.combinedModulesView?.setRowVisibility(3, newState: self.combinedModulesState)
self.combinedModulesView?.setRowVisibility(4, newState: self.combinedModulesState)
- self.remoteBtn = switchView(
- action: #selector(self.toggleRemoteState),
- state: Remote.shared.state
- )
-
- self.remoteView = PreferencesSection(label: localizedString("Stats Remote"), [
- PreferencesRow(localizedString("Monitoring"), component: self.remoteBtn!),
+ self.remoteView = PreferencesSection(label: localizedString("Stats Remote (beta)"), [
+ PreferencesRow(localizedString("Authorization"), component: buttonView(#selector(self.loginToRemote), text: localizedString("Login"))),
PreferencesRow(localizedString("Identificator"), component: textView(Remote.shared.id.uuidString)),
+ PreferencesRow(localizedString("Monitoring"), component: switchView(
+ action: #selector(self.toggleRemoteMonitoringState),
+ state: Remote.shared.monitoring
+ )),
PreferencesRow(component: buttonView(#selector(self.logoutFromRemote), text: localizedString("Logout")))
])
scrollView.stackView.addArrangedSubview(self.remoteView!)
+ self.remoteView?.setRowVisibility(1, newState: false)
+ self.remoteView?.setRowVisibility(2, newState: false)
+ self.remoteView?.setRowVisibility(3, newState: false)
scrollView.stackView.addArrangedSubview(PreferencesSection(label: localizedString("Settings"), [
PreferencesRow(
@@ -184,7 +185,7 @@ class ApplicationSettings: NSStackView {
scrollView.stackView.addArrangedSubview(PreferencesSection(label: localizedString("Stress tests"), tests))
NotificationCenter.default.addObserver(self, selector: #selector(self.toggleUninstallHelperButton), name: .fanHelperState, object: nil)
- NotificationCenter.default.addObserver(self, selector: #selector(self.remoteState), name: .remoteState, object: nil)
+ NotificationCenter.default.addObserver(self, selector: #selector(self.handleRemoteState), name: .remoteState, object: nil)
}
required init?(coder: NSCoder) {
@@ -420,34 +421,38 @@ class ApplicationSettings: NSStackView {
}
}
+ @objc private func toggleRemoteMonitoringState(_ sender: NSButton) {
+ Remote.shared.monitoring = sender.state == NSControl.StateValue.on
+ }
+
+ @objc private func handleRemoteState(_ notification: Notification) {
+ guard let auth = notification.userInfo?["auth"] as? Bool else { return }
+ self.setRemoteSettings(auth)
+ }
+
+ @objc private func loginToRemote() {
+ Remote.shared.login()
+ }
+
@objc private func logoutFromRemote() {
Remote.shared.logout()
}
- @objc private func remoteState(_ notification: Notification) {
- guard let state = notification.userInfo?["state"] as? Bool, let auth = notification.userInfo?["auth"] as? Bool else { return }
- self.setRemoteSettings(state, auth)
- }
-
- private func setRemoteSettings(_ state: Bool, _ auth: Bool) {
+ private func setRemoteSettings(_ auth: Bool) {
DispatchQueue.main.async {
- if state && auth {
- self.remoteBtn?.state = .on
+ if auth {
self.remoteView?.setRowVisibility(1, newState: true)
self.remoteView?.setRowVisibility(2, newState: true)
- return
- } else if state && !auth {
- Remote.shared.login()
+ self.remoteView?.setRowVisibility(3, newState: true)
+ self.remoteView?.setRowVisibility(0, newState: false)
+ } else {
+ self.remoteView?.setRowVisibility(0, newState: true)
+ self.remoteView?.setRowVisibility(1, newState: false)
+ self.remoteView?.setRowVisibility(2, newState: false)
+ self.remoteView?.setRowVisibility(3, newState: false)
}
- self.remoteBtn?.state = .off
- self.remoteView?.setRowVisibility(1, newState: false)
- self.remoteView?.setRowVisibility(2, newState: false)
}
}
-
- @objc private func toggleRemoteState(_ sender: NSButton) {
- Remote.shared.state = sender.state == NSControl.StateValue.on
- }
}
private class ModuleSelectorView: NSStackView {
diff --git a/Widgets/Supporting Files/Info.plist b/Widgets/Supporting Files/Info.plist
index 95f9f6f3..5b84a3ce 100644
--- a/Widgets/Supporting Files/Info.plist
+++ b/Widgets/Supporting Files/Info.plist
@@ -13,7 +13,7 @@
CFBundleShortVersionString
2.11.37
CFBundleVersion
- 689
+ 690
NSExtension
NSExtensionPointIdentifier