feat: add icon animation setting
This commit is contained in:
@@ -40,6 +40,10 @@ final class AppState: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Published var iconAnimationsEnabled: Bool {
|
||||||
|
didSet { UserDefaults.standard.set(self.iconAnimationsEnabled, forKey: iconAnimationsEnabledKey) }
|
||||||
|
}
|
||||||
|
|
||||||
@Published var showDockIcon: Bool {
|
@Published var showDockIcon: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
UserDefaults.standard.set(self.showDockIcon, forKey: showDockIconKey)
|
UserDefaults.standard.set(self.showDockIcon, forKey: showDockIconKey)
|
||||||
@@ -90,6 +94,12 @@ final class AppState: ObservableObject {
|
|||||||
self.swabbleEnabled = voiceWakeSupported ? savedVoiceWake : false
|
self.swabbleEnabled = voiceWakeSupported ? savedVoiceWake : false
|
||||||
self.swabbleTriggerWords = UserDefaults.standard
|
self.swabbleTriggerWords = UserDefaults.standard
|
||||||
.stringArray(forKey: swabbleTriggersKey) ?? defaultVoiceWakeTriggers
|
.stringArray(forKey: swabbleTriggersKey) ?? defaultVoiceWakeTriggers
|
||||||
|
if let storedIconAnimations = UserDefaults.standard.object(forKey: iconAnimationsEnabledKey) as? Bool {
|
||||||
|
self.iconAnimationsEnabled = storedIconAnimations
|
||||||
|
} else {
|
||||||
|
self.iconAnimationsEnabled = true
|
||||||
|
UserDefaults.standard.set(true, forKey: iconAnimationsEnabledKey)
|
||||||
|
}
|
||||||
self.showDockIcon = UserDefaults.standard.bool(forKey: showDockIconKey)
|
self.showDockIcon = UserDefaults.standard.bool(forKey: showDockIconKey)
|
||||||
self.voiceWakeMicID = UserDefaults.standard.string(forKey: voiceWakeMicKey) ?? ""
|
self.voiceWakeMicID = UserDefaults.standard.string(forKey: voiceWakeMicKey) ?? ""
|
||||||
self.voiceWakeLocaleID = UserDefaults.standard.string(forKey: voiceWakeLocaleKey) ?? Locale.current.identifier
|
self.voiceWakeLocaleID = UserDefaults.standard.string(forKey: voiceWakeLocaleKey) ?? Locale.current.identifier
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ let launchdLabel = "com.steipete.clawdis"
|
|||||||
let onboardingVersionKey = "clawdis.onboardingVersion"
|
let onboardingVersionKey = "clawdis.onboardingVersion"
|
||||||
let currentOnboardingVersion = 2
|
let currentOnboardingVersion = 2
|
||||||
let pauseDefaultsKey = "clawdis.pauseEnabled"
|
let pauseDefaultsKey = "clawdis.pauseEnabled"
|
||||||
|
let iconAnimationsEnabledKey = "clawdis.iconAnimationsEnabled"
|
||||||
let swabbleEnabledKey = "clawdis.swabbleEnabled"
|
let swabbleEnabledKey = "clawdis.swabbleEnabled"
|
||||||
let swabbleTriggersKey = "clawdis.swabbleTriggers"
|
let swabbleTriggersKey = "clawdis.swabbleTriggers"
|
||||||
let showDockIconKey = "clawdis.showDockIcon"
|
let showDockIconKey = "clawdis.showDockIcon"
|
||||||
|
|||||||
@@ -33,6 +33,11 @@ struct GeneralSettings: View {
|
|||||||
subtitle: "Keep Clawdis visible in the Dock instead of menu-bar-only mode.",
|
subtitle: "Keep Clawdis visible in the Dock instead of menu-bar-only mode.",
|
||||||
binding: self.$state.showDockIcon)
|
binding: self.$state.showDockIcon)
|
||||||
|
|
||||||
|
SettingsToggleRow(
|
||||||
|
title: "Play menu bar icon animations",
|
||||||
|
subtitle: "Enable idle blinks and wiggles on the status icon.",
|
||||||
|
binding: self.$state.iconAnimationsEnabled)
|
||||||
|
|
||||||
SettingsToggleRow(
|
SettingsToggleRow(
|
||||||
title: "Enable debug tools",
|
title: "Enable debug tools",
|
||||||
subtitle: "Show the Debug tab with development utilities.",
|
subtitle: "Show the Debug tab with development utilities.",
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ struct ClawdisApp: App {
|
|||||||
isPaused: self.state.isPaused,
|
isPaused: self.state.isPaused,
|
||||||
isWorking: self.state.isWorking,
|
isWorking: self.state.isWorking,
|
||||||
earBoostActive: self.state.earBoostActive,
|
earBoostActive: self.state.earBoostActive,
|
||||||
relayStatus: self.relayManager.status)
|
relayStatus: self.relayManager.status,
|
||||||
|
animationsEnabled: self.state.iconAnimationsEnabled)
|
||||||
}
|
}
|
||||||
.menuBarExtraStyle(.menu)
|
.menuBarExtraStyle(.menu)
|
||||||
.menuBarExtraAccess(isPresented: self.$isMenuPresented) { item in
|
.menuBarExtraAccess(isPresented: self.$isMenuPresented) { item in
|
||||||
@@ -137,6 +138,7 @@ private struct CritterStatusLabel: View {
|
|||||||
var isWorking: Bool
|
var isWorking: Bool
|
||||||
var earBoostActive: Bool
|
var earBoostActive: Bool
|
||||||
var relayStatus: RelayProcessManager.Status
|
var relayStatus: RelayProcessManager.Status
|
||||||
|
var animationsEnabled: Bool
|
||||||
|
|
||||||
@State private var blinkAmount: CGFloat = 0
|
@State private var blinkAmount: CGFloat = 0
|
||||||
@State private var nextBlink = Date().addingTimeInterval(Double.random(in: 3.5...8.5))
|
@State private var nextBlink = Date().addingTimeInterval(Double.random(in: 3.5...8.5))
|
||||||
@@ -165,6 +167,11 @@ private struct CritterStatusLabel: View {
|
|||||||
.rotationEffect(.degrees(self.wiggleAngle), anchor: .center)
|
.rotationEffect(.degrees(self.wiggleAngle), anchor: .center)
|
||||||
.offset(x: self.wiggleOffset)
|
.offset(x: self.wiggleOffset)
|
||||||
.onReceive(self.ticker) { now in
|
.onReceive(self.ticker) { now in
|
||||||
|
guard self.animationsEnabled else {
|
||||||
|
self.resetMotion()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if now >= self.nextBlink {
|
if now >= self.nextBlink {
|
||||||
self.blink()
|
self.blink()
|
||||||
self.nextBlink = now.addingTimeInterval(Double.random(in: 3.5...8.5))
|
self.nextBlink = now.addingTimeInterval(Double.random(in: 3.5...8.5))
|
||||||
@@ -190,6 +197,13 @@ private struct CritterStatusLabel: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: self.isPaused) { _, _ in self.resetMotion() }
|
.onChange(of: self.isPaused) { _, _ in self.resetMotion() }
|
||||||
|
.onChange(of: self.animationsEnabled) { _, enabled in
|
||||||
|
if enabled {
|
||||||
|
self.scheduleRandomTimers(from: Date())
|
||||||
|
} else {
|
||||||
|
self.resetMotion()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,6 +280,13 @@ private struct CritterStatusLabel: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func scheduleRandomTimers(from date: Date) {
|
||||||
|
self.nextBlink = date.addingTimeInterval(Double.random(in: 3.5...8.5))
|
||||||
|
self.nextWiggle = date.addingTimeInterval(Double.random(in: 6.5...14))
|
||||||
|
self.nextLegWiggle = date.addingTimeInterval(Double.random(in: 5.0...11.0))
|
||||||
|
self.nextEarWiggle = date.addingTimeInterval(Double.random(in: 7.0...14.0))
|
||||||
|
}
|
||||||
|
|
||||||
private var relayNeedsAttention: Bool {
|
private var relayNeedsAttention: Bool {
|
||||||
switch self.relayStatus {
|
switch self.relayStatus {
|
||||||
case .failed, .stopped:
|
case .failed, .stopped:
|
||||||
|
|||||||
Reference in New Issue
Block a user