Skip to content

Commit 4f46969

Browse files
authored
feat: remember shuffle and repeat (#49)
1 parent 5557adc commit 4f46969

File tree

4 files changed

+86
-0
lines changed

4 files changed

+86
-0
lines changed

Core/Services/Player/PlayerService.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ final class PlayerService: NSObject, PlayerServiceProtocol {
130130
static let volumeKey = "playerVolume"
131131
/// UserDefaults key for persisting volume before mute.
132132
static let volumeBeforeMuteKey = "playerVolumeBeforeMute"
133+
/// UserDefaults key for persisting shuffle state.
134+
static let shuffleEnabledKey = "playerShuffleEnabled"
135+
/// UserDefaults key for persisting repeat mode.
136+
static let repeatModeKey = "playerRepeatMode"
133137

134138
// MARK: - Initialization
135139

@@ -150,6 +154,29 @@ final class PlayerService: NSObject, PlayerServiceProtocol {
150154
self.volumeBeforeMute = self.volume > 0 ? self.volume : 1.0
151155
}
152156

157+
// Restore shuffle and repeat settings if enabled in settings
158+
if SettingsManager.shared.rememberPlaybackSettings {
159+
if UserDefaults.standard.object(forKey: Self.shuffleEnabledKey) != nil {
160+
self.shuffleEnabled = UserDefaults.standard.bool(forKey: Self.shuffleEnabledKey)
161+
self.logger.info("Restored shuffle state: \(self.shuffleEnabled)")
162+
}
163+
164+
if let savedRepeatMode = UserDefaults.standard.string(forKey: Self.repeatModeKey) {
165+
switch savedRepeatMode {
166+
case "all":
167+
self.repeatMode = .all
168+
case "one":
169+
self.repeatMode = .one
170+
case "off":
171+
self.repeatMode = .off
172+
default:
173+
self.logger.warning("Unexpected repeat mode value in UserDefaults: \(savedRepeatMode), defaulting to off")
174+
self.repeatMode = .off
175+
}
176+
self.logger.info("Restored repeat mode: \(String(describing: self.repeatMode))")
177+
}
178+
}
179+
153180
// Load mock state for UI tests
154181
self.loadMockStateIfNeeded()
155182
}
@@ -618,6 +645,10 @@ final class PlayerService: NSObject, PlayerServiceProtocol {
618645
/// Toggles shuffle mode.
619646
func toggleShuffle() {
620647
self.shuffleEnabled.toggle()
648+
// Persist shuffle state to UserDefaults if setting is enabled
649+
if SettingsManager.shared.rememberPlaybackSettings {
650+
UserDefaults.standard.set(self.shuffleEnabled, forKey: Self.shuffleEnabledKey)
651+
}
621652
let status = self.shuffleEnabled ? "enabled" : "disabled"
622653
self.logger.info("Shuffle mode: \(status)")
623654
}
@@ -632,6 +663,18 @@ final class PlayerService: NSObject, PlayerServiceProtocol {
632663
case .one:
633664
self.repeatMode = .off
634665
}
666+
// Persist repeat mode to UserDefaults if setting is enabled
667+
if SettingsManager.shared.rememberPlaybackSettings {
668+
let modeString = switch self.repeatMode {
669+
case .off:
670+
"off"
671+
case .all:
672+
"all"
673+
case .one:
674+
"one"
675+
}
676+
UserDefaults.standard.set(modeString, forKey: Self.repeatModeKey)
677+
}
635678
let mode = self.repeatMode
636679
self.logger.info("Repeat mode: \(String(describing: mode))")
637680
}

Core/Services/SettingsManager.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ final class SettingsManager {
1313
static let showNowPlayingNotifications = "settings.showNowPlayingNotifications"
1414
static let defaultLaunchPage = "settings.defaultLaunchPage"
1515
static let hapticFeedbackEnabled = "settings.hapticFeedbackEnabled"
16+
static let rememberPlaybackSettings = "settings.rememberPlaybackSettings"
1617
}
1718

1819
// MARK: - Launch Page Options
@@ -81,6 +82,18 @@ final class SettingsManager {
8182
}
8283
}
8384

85+
/// Whether to remember shuffle/repeat settings across app restarts.
86+
var rememberPlaybackSettings: Bool {
87+
didSet {
88+
UserDefaults.standard.set(self.rememberPlaybackSettings, forKey: Keys.rememberPlaybackSettings)
89+
// Clear stale values when setting is disabled to prevent unexpected restoration
90+
if !self.rememberPlaybackSettings {
91+
UserDefaults.standard.removeObject(forKey: "playerShuffleEnabled")
92+
UserDefaults.standard.removeObject(forKey: "playerRepeatMode")
93+
}
94+
}
95+
}
96+
8497
/// The last page the user was on (for "Last Used" option).
8598
var lastUsedPage: LaunchPage = .home
8699

@@ -90,6 +103,7 @@ final class SettingsManager {
90103
// Load persisted settings or use defaults
91104
self.showNowPlayingNotifications = UserDefaults.standard.object(forKey: Keys.showNowPlayingNotifications) as? Bool ?? true
92105
self.hapticFeedbackEnabled = UserDefaults.standard.object(forKey: Keys.hapticFeedbackEnabled) as? Bool ?? true
106+
self.rememberPlaybackSettings = UserDefaults.standard.object(forKey: Keys.rememberPlaybackSettings) as? Bool ?? false
93107

94108
if let rawValue = UserDefaults.standard.string(forKey: Keys.defaultLaunchPage),
95109
let page = LaunchPage(rawValue: rawValue)

Tests/KasetTests/SettingsManagerTests.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,31 @@ struct SettingsManagerTests {
6666
#expect(manager.hapticFeedbackEnabled == true)
6767
}
6868

69+
@Test("Default rememberPlaybackSettings is false")
70+
func defaultRememberPlaybackSettings() {
71+
let manager = SettingsManager.shared
72+
#expect(manager.rememberPlaybackSettings == false)
73+
}
74+
75+
@Test("Disabling rememberPlaybackSettings clears persisted values")
76+
func disablingRememberPlaybackSettingsClearsValues() {
77+
let manager = SettingsManager.shared
78+
let shuffleKey = "playerShuffleEnabled"
79+
let repeatKey = "playerRepeatMode"
80+
81+
// Set up some persisted values
82+
UserDefaults.standard.set(true, forKey: shuffleKey)
83+
UserDefaults.standard.set("all", forKey: repeatKey)
84+
85+
// Enable then disable the setting
86+
manager.rememberPlaybackSettings = true
87+
manager.rememberPlaybackSettings = false
88+
89+
// Verify values are cleared
90+
#expect(UserDefaults.standard.object(forKey: shuffleKey) == nil)
91+
#expect(UserDefaults.standard.object(forKey: repeatKey) == nil)
92+
}
93+
6994
// MARK: - launchPage Computed Property Tests
7095

7196
@Test("launchPage returns defaultLaunchPage for non-lastUsed")

Views/macOS/GeneralSettingsView.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ struct GeneralSettingsView: View {
4545
Toggle("Haptic Feedback", isOn: self.$settings.hapticFeedbackEnabled)
4646
.help("Provide tactile feedback for actions on Force Touch trackpads")
4747

48+
// Remember Playback Settings
49+
Toggle("Remember Shuffle & Repeat", isOn: self.$settings.rememberPlaybackSettings)
50+
.help("Save shuffle and repeat settings across app restarts")
51+
4852
// Default Launch Page
4953
Picker("Default Page on Launch", selection: self.$settings.defaultLaunchPage) {
5054
ForEach(SettingsManager.LaunchPage.allCases) { page in

0 commit comments

Comments
 (0)