From de6b95d77339baa0bd485d0940222fecf0aa9890 Mon Sep 17 00:00:00 2001 From: Alan Hamlett Date: Sun, 18 Aug 2024 00:05:06 +0200 Subject: [PATCH] Show today time in status bar --- WakaTime/AppDelegate.swift | 60 +++++++++++++++++++ WakaTime/Helpers/PropertiesManager.swift | 16 +++++ WakaTime/Views/SettingsView.swift | 24 +++++++- WakaTime/WakaTime.swift | 3 + .../SettingsWindowController.swift | 2 +- 5 files changed, 103 insertions(+), 2 deletions(-) diff --git a/WakaTime/AppDelegate.swift b/WakaTime/AppDelegate.swift index 62bbb9b..7431ac6 100644 --- a/WakaTime/AppDelegate.swift +++ b/WakaTime/AppDelegate.swift @@ -12,6 +12,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, StatusBarDelegate { var monitoredAppsWindowController = MonitoredAppsWindowController() var wakaTime: WakaTime? + @Atomic var lastTodayTime = 0 + @Atomic var lastTodayText = "" + let updater = AppUpdater(owner: "wakatime", repo: "macos-wakatime") func applicationDidFinishLaunching(_ aNotification: Notification) { @@ -57,6 +60,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, StatusBarDelegate { wakaTime = WakaTime(self) + settingsWindowController.settingsView.delegate = self + // request notifications permission UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in guard granted else { @@ -178,4 +183,59 @@ class AppDelegate: NSObject, NSApplicationDelegate, StatusBarDelegate { notificationCenter.add(request) } } + + private func setText(_ text: String) { + DispatchQueue.main.async { + Logging.default.log("Set status bar text: \(text)") + self.statusBarItem.button?.title = text.isEmpty ? text : " " + text + } + } + + internal func fetchToday() { + guard PropertiesManager.shouldDisplayTodayInStatusBar else { + setText("") + return + } + + let time = Int(NSDate().timeIntervalSince1970) + guard lastTodayTime + 300 < time else { + setText(lastTodayText) + return + } + + lastTodayTime = time + + let cli = NSString.path( + withComponents: ConfigFile.resourcesFolder + ["wakatime-cli"] + ) + let process = Process() + process.launchPath = cli + let args = [ + "--today", + "--today-hide-categories", + "true", + "--plugin", + "macos-wakatime/" + Bundle.main.version, + ] + + Logging.default.log("Fetching coding activity for Today from api: \(args)") + + process.arguments = args + let pipe = Pipe() + process.standardOutput = pipe + process.standardError = FileHandle.nullDevice + + do { + try process.execute() + setText("") + } catch { + Logging.default.log("Failed to run wakatime-cli fetching Today coding activity: \(error)") + } + + let handle = pipe.fileHandleForReading + let data = handle.readDataToEndOfFile() + let text = (String(data: data, encoding: String.Encoding.utf8) ?? "").trimmingCharacters(in: .whitespacesAndNewlines) + lastTodayText = text + setText(text) + } } diff --git a/WakaTime/Helpers/PropertiesManager.swift b/WakaTime/Helpers/PropertiesManager.swift index c21feb6..15364d5 100644 --- a/WakaTime/Helpers/PropertiesManager.swift +++ b/WakaTime/Helpers/PropertiesManager.swift @@ -17,6 +17,7 @@ class PropertiesManager { case shouldRequestA11y = "request_a11y" case shouldAutomaticallyDownloadUpdates = "should_automatically_download_updates" case hasLaunchedBefore = "has_launched_before" + case shouldDisplayTodayInStatusBar = "status_bar_text" case domainPreference = "domain_preference" case filterType = "filter_type" case denylist = "denylist" @@ -88,6 +89,21 @@ class PropertiesManager { } } + static var shouldDisplayTodayInStatusBar: Bool { + get { + guard UserDefaults.standard.string(forKey: Keys.shouldDisplayTodayInStatusBar.rawValue) != nil else { + UserDefaults.standard.set(true, forKey: Keys.shouldDisplayTodayInStatusBar.rawValue) + return true + } + + return UserDefaults.standard.bool(forKey: Keys.shouldDisplayTodayInStatusBar.rawValue) + } + set { + UserDefaults.standard.set(newValue, forKey: Keys.shouldDisplayTodayInStatusBar.rawValue) + UserDefaults.standard.synchronize() + } + } + static var hasLaunchedBefore: Bool { get { guard UserDefaults.standard.string(forKey: Keys.hasLaunchedBefore.rawValue) != nil else { diff --git a/WakaTime/Views/SettingsView.swift b/WakaTime/Views/SettingsView.swift index 776db85..55b3ce2 100644 --- a/WakaTime/Views/SettingsView.swift +++ b/WakaTime/Views/SettingsView.swift @@ -1,6 +1,8 @@ import AppKit class SettingsView: NSView, NSTextFieldDelegate, NSTextViewDelegate { + var delegate: StatusBarDelegate? + // MARK: API Key lazy var apiKeyLabel: NSTextField = { @@ -44,6 +46,16 @@ class SettingsView: NSView, NSTextFieldDelegate, NSTextViewDelegate { return checkbox }() + lazy var statusBarTextCheckbox: NSButton = { + let checkbox = NSButton( + checkboxWithTitle: "Show today’s time in status bar", + target: self, + action: #selector(enableStatusBarTextCheckboxClicked) + ) + checkbox.state = PropertiesManager.shouldDisplayTodayInStatusBar ? .on : .off + return checkbox + }() + lazy var requestA11yCheckbox: NSButton = { let checkbox = NSButton( checkboxWithTitle: "Enable stats from Xcode by requesting accessibility permission", @@ -55,7 +67,7 @@ class SettingsView: NSView, NSTextFieldDelegate, NSTextViewDelegate { }() lazy var checkboxesStackView: NSStackView = { - let stack = NSStackView(views: [launchAtLoginCheckbox, requestA11yCheckbox, enableLoggingCheckbox]) + let stack = NSStackView(views: [launchAtLoginCheckbox, statusBarTextCheckbox, requestA11yCheckbox, enableLoggingCheckbox]) stack.alignment = .leading stack.orientation = .vertical stack.spacing = 10 @@ -243,6 +255,16 @@ class SettingsView: NSView, NSTextFieldDelegate, NSTextViewDelegate { } } + @objc func enableStatusBarTextCheckboxClicked() { + PropertiesManager.shouldDisplayTodayInStatusBar = statusBarTextCheckbox.state == .on + if statusBarTextCheckbox.state == .on { + PropertiesManager.shouldDisplayTodayInStatusBar = true + } else { + PropertiesManager.shouldDisplayTodayInStatusBar = false + } + delegate?.fetchToday() + } + @objc func enableA11yCheckboxClicked() { PropertiesManager.shouldRequestA11yPermission = requestA11yCheckbox.state == .on if requestA11yCheckbox.state == .on { diff --git a/WakaTime/WakaTime.swift b/WakaTime/WakaTime.swift index 7282644..1dc3217 100644 --- a/WakaTime/WakaTime.swift +++ b/WakaTime/WakaTime.swift @@ -165,6 +165,8 @@ class WakaTime: HeartbeatEventHandler { } catch { Logging.default.log("Failed to run wakatime-cli: \(error)") } + + delegate.fetchToday() } } @@ -203,6 +205,7 @@ enum Category: String { protocol StatusBarDelegate: AnyObject { func a11yStatusChanged(_ hasPermission: Bool) func toastNotification(_ title: String) + func fetchToday() } protocol HeartbeatEventHandler { diff --git a/WakaTime/WindowControllers/SettingsWindowController.swift b/WakaTime/WindowControllers/SettingsWindowController.swift index adf433b..999378e 100644 --- a/WakaTime/WindowControllers/SettingsWindowController.swift +++ b/WakaTime/WindowControllers/SettingsWindowController.swift @@ -1,7 +1,7 @@ import AppKit class SettingsWindowController: NSWindowController, NSTextFieldDelegate { - let settingsView = SettingsView() + public let settingsView = SettingsView() convenience init() { self.init(window: nil)