Skip to content
This repository was archived by the owner on Jul 14, 2026. It is now read-only.

Commit 0f4e94b

Browse files
committed
add menu to other toolbars
1 parent 03ffc69 commit 0f4e94b

7 files changed

Lines changed: 113 additions & 11 deletions

File tree

AmpFin.xcodeproj/project.pbxproj

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@
126126
3AF3BA982C525E2900C96E1F /* QueueButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF3BA972C525E2900C96E1F /* QueueButtons.swift */; };
127127
3AF815DD2AAA1A80004BA444 /* Lyrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF815DC2AAA1A80004BA444 /* Lyrics.swift */; };
128128
3AFCD1212BC5CB6A00589301 /* Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AFCD1202BC5CB6A00589301 /* Buttons.swift */; };
129+
970719752D66280300FE4749 /* SleepTimerMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 970719742D6627F300FE4749 /* SleepTimerMenu.swift */; };
129130
EAB1E6C52CA69AFC0094D8EF /* LoginQuickConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1E6C42CA69AF20094D8EF /* LoginQuickConnectView.swift */; };
130131
/* End PBXBuildFile section */
131132

@@ -280,6 +281,7 @@
280281
3AF3BA972C525E2900C96E1F /* QueueButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueButtons.swift; sourceTree = "<group>"; };
281282
3AF815DC2AAA1A80004BA444 /* Lyrics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lyrics.swift; sourceTree = "<group>"; };
282283
3AFCD1202BC5CB6A00589301 /* Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buttons.swift; sourceTree = "<group>"; };
284+
970719742D6627F300FE4749 /* SleepTimerMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SleepTimerMenu.swift; sourceTree = "<group>"; };
283285
EAB1E6C42CA69AF20094D8EF /* LoginQuickConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginQuickConnectView.swift; sourceTree = "<group>"; };
284286
/* End PBXFileReference section */
285287

@@ -485,6 +487,7 @@
485487
3A98A0332AABA48D00EA84CF /* Utility */ = {
486488
isa = PBXGroup;
487489
children = (
490+
970719742D6627F300FE4749 /* SleepTimerMenu.swift */,
488491
3AAC768C2B02C68600E337C1 /* ItemImage.swift */,
489492
3A234E142AA8DBC900197AA7 /* SortSelector.swift */,
490493
3AF3BA972C525E2900C96E1F /* QueueButtons.swift */,
@@ -934,6 +937,7 @@
934937
3A3DA6612AAB29470091211A /* ArtistsView.swift in Sources */,
935938
3ADE8EA02AA93D39009826A3 /* AlbumView+Toolbar.swift in Sources */,
936939
3A3DA66B2AAB2DDA0091211A /* ArtistView.swift in Sources */,
940+
970719752D66280300FE4749 /* SleepTimerMenu.swift in Sources */,
937941
3A3DA6682AAB2CDA0091211A /* ArtistListRow.swift in Sources */,
938942
3A8B99402AAA0085004AAA7B /* Controls.swift in Sources */,
939943
3AA803E72CE8B05D0048329B /* LoginFormView.swift in Sources */,
@@ -1186,13 +1190,13 @@
11861190
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
11871191
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
11881192
CODE_SIGN_ENTITLEMENTS = Multiplatform/Entitlements.entitlements;
1193+
DEVELOPMENT_TEAM = AABTGX45S3;
11891194
GENERATE_INFOPLIST_FILE = YES;
11901195
INFOPLIST_FILE = Multiplatform/Info.plist;
11911196
INFOPLIST_KEY_CFBundleDisplayName = AmpFin;
11921197
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.music";
11931198
INFOPLIST_KEY_NSAppleMusicUsageDescription = "Hello, World!";
11941199
INFOPLIST_KEY_NSSiriUsageDescription = "Play your music using Siri";
1195-
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
11961200
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
11971201
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
11981202
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
@@ -1227,13 +1231,13 @@
12271231
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
12281232
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
12291233
CODE_SIGN_ENTITLEMENTS = Multiplatform/Entitlements.entitlements;
1234+
DEVELOPMENT_TEAM = AABTGX45S3;
12301235
GENERATE_INFOPLIST_FILE = YES;
12311236
INFOPLIST_FILE = Multiplatform/Info.plist;
12321237
INFOPLIST_KEY_CFBundleDisplayName = AmpFin;
12331238
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.music";
12341239
INFOPLIST_KEY_NSAppleMusicUsageDescription = "Hello, World!";
12351240
INFOPLIST_KEY_NSSiriUsageDescription = "Play your music using Siri";
1236-
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
12371241
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
12381242
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
12391243
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
@@ -1251,6 +1255,7 @@
12511255
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
12521256
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
12531257
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID_PREFIX}.ampfin";
1258+
"PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = com.aaronleopold.ampfin;
12541259
PRODUCT_NAME = AmpFin;
12551260
SDKROOT = iphoneos;
12561261
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";

Multiplatform/Album/AlbumView+Toolbar.swift

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,19 @@ private struct ToolbarMenu: View {
131131
viewModel.queue(now: $0)
132132
}
133133

134-
Menu {
135-
Button {
136-
viewModel.startSleepTimer(minutes: 1)
137-
} label: {
138-
Text("1 minute")
139-
}
140-
} label: {
141-
Label("timer", systemImage: "clock")
142-
}
134+
SleepTimerMenu(
135+
// TODO: This feels wrong, I don't fully understand reactivity in Swift
136+
hasActiveTimer: Binding<Bool>(
137+
get: { viewModel.hasSleepTimer },
138+
set: {
139+
if !$0 {
140+
viewModel.cancelSleepTimer()
141+
}
142+
}
143+
),
144+
set: viewModel.startSleepTimer,
145+
unset: viewModel.cancelSleepTimer
146+
)
143147

144148
ForEach(viewModel.album.artists) { artist in
145149
Divider()

Multiplatform/Album/AlbumViewModel.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,4 +182,9 @@ internal extension AlbumViewModel {
182182
var runtime: Double {
183183
tracks.reduce(0, { $0 + $1.runtime })
184184
}
185+
186+
@MainActor
187+
var hasSleepTimer: Bool {
188+
AudioPlayer.current.sleepTimerEndDate != nil
189+
}
185190
}

Multiplatform/Localizable.xcstrings

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
{
22
"sourceLanguage" : "en",
33
"strings" : {
4+
"%lld minutes" : {
5+
"localizations" : {
6+
"de" : {
7+
"stringUnit" : {
8+
"state" : "translated",
9+
"value" : "%lld minuten"
10+
}
11+
},
12+
"en" : {
13+
"stringUnit" : {
14+
"state" : "translated",
15+
"value" : "%lld minutes"
16+
}
17+
}
18+
}
19+
},
420
"account" : {
521
"localizations" : {
622
"de" : {
@@ -480,6 +496,9 @@
480496
}
481497
}
482498
}
499+
},
500+
"Cancel timer" : {
501+
483502
},
484503
"done" : {
485504
"localizations" : {
@@ -2140,6 +2159,9 @@
21402159
}
21412160
}
21422161
}
2162+
},
2163+
"Sleep timer" : {
2164+
21432165
},
21442166
"sort" : {
21452167
"localizations" : {

Multiplatform/Playlist/PlaylistView+Toolbar.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ internal extension PlaylistView {
7676
viewModel.queue(now: $0)
7777
}
7878

79+
SleepTimerMenu(
80+
// TODO: This feels wrong, I don't fully understand reactivity in Swift
81+
hasActiveTimer: Binding<Bool>(
82+
get: { viewModel.hasSleepTimer },
83+
set: {
84+
if !$0 {
85+
viewModel.cancelSleepTimer()
86+
}
87+
}
88+
),
89+
set: viewModel.startSleepTimer,
90+
unset: viewModel.cancelSleepTimer
91+
)
92+
93+
7994
Divider()
8095

8196
Button {

Multiplatform/Playlist/PlaylistViewModel.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ internal extension PlaylistViewModel {
7575
var downloadStatus: ItemOfflineTracker.OfflineStatus {
7676
offlineTracker.status
7777
}
78+
79+
@MainActor
80+
var hasSleepTimer: Bool {
81+
AudioPlayer.current.sleepTimerEndDate != nil
82+
}
7883
}
7984

8085
internal extension PlaylistViewModel {
@@ -137,6 +142,14 @@ internal extension PlaylistViewModel {
137142
}
138143
}
139144
}
145+
146+
func startSleepTimer(minutes: Int) {
147+
AudioPlayer.current.setSleepTimer(minutes: minutes)
148+
}
149+
150+
func cancelSleepTimer() {
151+
AudioPlayer.current.cancelSleepTimer()
152+
}
140153
}
141154

142155
internal extension PlaylistViewModel {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// SleepTimerMenu.swift
3+
// AmpFin
4+
//
5+
// Created by Aaron Leopold on 2/19/25.
6+
//
7+
8+
import SwiftUI
9+
import AFPlayback
10+
11+
internal struct SleepTimerMenu: View {
12+
private let options: [Int] = [5, 10, 30, 60, 120]
13+
14+
@Binding var hasActiveTimer: Bool
15+
let set: (_: Int) -> Void
16+
let unset: () -> Void
17+
18+
var body: some View {
19+
Menu {
20+
ForEach(0 ..< self.options.count) { index in
21+
Button {
22+
self.set(self.options[index])
23+
} label: {
24+
Text("\(self.options[index]) minutes")
25+
}
26+
}
27+
28+
Button(role: .destructive) {
29+
self.unset()
30+
} label: {
31+
Text("Cancel timer")
32+
}
33+
.disabled(!self.hasActiveTimer)
34+
} label: {
35+
Label("Sleep timer", systemImage: "clock")
36+
}
37+
}
38+
}

0 commit comments

Comments
 (0)