Implements server selection UI
This commit is contained in:
@@ -17,6 +17,10 @@ struct MediaItem: Codable
|
||||
let playing: Bool?
|
||||
let metadata: Metadata?
|
||||
|
||||
var displayTitle: String {
|
||||
metadata?.title ?? title ?? filename ?? "item \(id)"
|
||||
}
|
||||
|
||||
// MARK: - Types
|
||||
|
||||
struct Metadata: Codable
|
||||
|
||||
@@ -78,6 +78,7 @@ struct ContentView: View
|
||||
MainView(model: model)
|
||||
.task { await watchWebsocket() }
|
||||
.task { await refresh([.nowPlaying, .playlist, .favorites]) }
|
||||
.task { await watchForSettingsChanges() }
|
||||
}
|
||||
|
||||
// MARK: - Types
|
||||
@@ -118,7 +119,7 @@ extension ContentView
|
||||
model.playlistModel.items = playlist.enumerated().map { (idx, mediaItem) in
|
||||
MediaListItem(
|
||||
id: String(mediaItem.id),
|
||||
title: mediaItem.title ?? mediaItem.filename ?? "<null>",
|
||||
title: mediaItem.displayTitle,
|
||||
filename: mediaItem.filename ?? "<null>",
|
||||
index: idx,
|
||||
isCurrent: mediaItem.current ?? false
|
||||
@@ -131,7 +132,7 @@ extension ContentView
|
||||
model.favoritesModel.items = favorites.map { mediaItem in
|
||||
MediaListItem(
|
||||
id: String(mediaItem.id),
|
||||
title: mediaItem.title ?? mediaItem.filename ?? "<null>",
|
||||
title: mediaItem.displayTitle,
|
||||
filename: mediaItem.filename ?? "<null>"
|
||||
)
|
||||
}
|
||||
@@ -186,4 +187,26 @@ extension ContentView
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func watchForSettingsChanges() async {
|
||||
let settingsChangedNotifications = NotificationCenter.default.notifications(named: .settingsChanged)
|
||||
.map({ _ in Optional.none })
|
||||
|
||||
for await _ in settingsChangedNotifications {
|
||||
let newSelectedServer = Settings.fromDefaults().selectedServer
|
||||
if newSelectedServer != model.selectedServer {
|
||||
model.selectedServer = newSelectedServer
|
||||
|
||||
// Reset view model to defaults
|
||||
model.playlistModel = MediaListViewModel(mode: .playlist)
|
||||
model.favoritesModel = MediaListViewModel(mode: .favorites)
|
||||
model.nowPlayingViewModel = NowPlayingViewModel()
|
||||
|
||||
await refresh([.playlist, .nowPlaying, .favorites])
|
||||
}
|
||||
|
||||
// Always reset this
|
||||
model.serverSelectionViewModel = ServerSelectionToolbarModifier.ViewModel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,15 +10,16 @@ import SwiftUI
|
||||
@Observable
|
||||
class MainViewModel
|
||||
{
|
||||
var selectedServer: Server? { Settings.fromDefaults().selectedServer }
|
||||
var selectedServer: Server? = Settings.fromDefaults().selectedServer
|
||||
|
||||
var connectionError: Error? = nil
|
||||
var selectedTab: Tab = .playlist
|
||||
|
||||
var playlistModel = MediaListViewModel(mode: .playlist)
|
||||
var favoritesModel = MediaListViewModel(mode: .favorites)
|
||||
var nowPlayingViewModel = NowPlayingViewModel()
|
||||
var addMediaViewModel = AddMediaBarViewModel()
|
||||
var playlistModel = MediaListViewModel(mode: .playlist)
|
||||
var favoritesModel = MediaListViewModel(mode: .favorites)
|
||||
var nowPlayingViewModel = NowPlayingViewModel()
|
||||
var addMediaViewModel = AddMediaBarViewModel()
|
||||
var serverSelectionViewModel = ServerSelectionToolbarModifier.ViewModel()
|
||||
|
||||
enum Tab: String, CaseIterable
|
||||
{
|
||||
@@ -40,16 +41,6 @@ struct MainView: View
|
||||
if !Settings.fromDefaults().isConfigured {
|
||||
model.selectedTab = .settings
|
||||
}
|
||||
|
||||
Task {
|
||||
let settingsChangedNotifications = NotificationCenter.default.notifications(named: .settingsChanged)
|
||||
.map({ _ in Optional.none })
|
||||
|
||||
for await _ in settingsChangedNotifications {
|
||||
// TODO
|
||||
// model.api = API.fromSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
@@ -57,11 +48,19 @@ struct MainView: View
|
||||
|
||||
TabView(selection: $model.selectedTab) {
|
||||
Tab(.playlist, systemImage: "list.bullet", value: .playlist) {
|
||||
MediaListView(model: model.playlistModel)
|
||||
NavigationStack {
|
||||
MediaListView(model: model.playlistModel)
|
||||
.displayingServerSelectionToolbar(model: $model.serverSelectionViewModel)
|
||||
.navigationTitle(.playlist)
|
||||
}
|
||||
}
|
||||
|
||||
Tab(.favorites, systemImage: "heart.fill", value: .favorites) {
|
||||
MediaListView(model: model.favoritesModel)
|
||||
NavigationStack {
|
||||
MediaListView(model: model.favoritesModel)
|
||||
.displayingServerSelectionToolbar(model: $model.serverSelectionViewModel)
|
||||
.navigationTitle(.favorites)
|
||||
}
|
||||
}
|
||||
|
||||
Tab(.settings, systemImage: "gear", value: .settings) {
|
||||
@@ -107,3 +106,67 @@ struct MainView: View
|
||||
}
|
||||
}
|
||||
|
||||
struct ServerSelectionToolbarModifier: ViewModifier
|
||||
{
|
||||
@Binding var model: ViewModel
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: .topBarTrailing) {
|
||||
Menu {
|
||||
Section {
|
||||
ForEach(model.selectableServers) { server in
|
||||
Button {
|
||||
model.selectedServer = server
|
||||
} label: {
|
||||
Text(server.displayName)
|
||||
if model.selectedServer == server {
|
||||
Image(systemName: "checkmark")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if false
|
||||
// TODO
|
||||
Section {
|
||||
Button(.addServer) {
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} label: {
|
||||
Label(model.selectedServer?.displayName ?? "Servers", systemImage: "chevron.down")
|
||||
.labelStyle(.titleAndIcon)
|
||||
}
|
||||
.buttonBorderShape(.capsule)
|
||||
.buttonStyle(.bordered)
|
||||
.menuStyle(.button)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Types
|
||||
|
||||
@Observable
|
||||
class ViewModel
|
||||
{
|
||||
var selectableServers: [Server] = Settings.fromDefaults().configuredServers
|
||||
var selectedServer: Server? = Settings.fromDefaults().selectedServer {
|
||||
didSet {
|
||||
Settings
|
||||
.fromDefaults()
|
||||
.selectedServer(selectedServer)
|
||||
.save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func displayingServerSelectionToolbar(model: Binding<ServerSelectionToolbarModifier.ViewModel>) -> some View {
|
||||
modifier(ServerSelectionToolbarModifier(model: model))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,14 +9,18 @@ import SwiftUI
|
||||
|
||||
struct MediaListItem: Identifiable
|
||||
{
|
||||
let id: String
|
||||
let _id: String
|
||||
let title: String
|
||||
let filename: String
|
||||
let index: Int?
|
||||
let isCurrent: Bool
|
||||
|
||||
var id: String {
|
||||
_id + filename // temporary: we get duplicate ids from the server sometimes...
|
||||
}
|
||||
|
||||
init(id: String, title: String, filename: String, index: Int? = nil, isCurrent: Bool = false) {
|
||||
self.id = id
|
||||
self._id = id
|
||||
self.title = title
|
||||
self.filename = filename
|
||||
self.index = index
|
||||
|
||||
Reference in New Issue
Block a user