Files
yattee/Yattee/Models/Navigation/SidebarItem.swift
2026-02-08 18:33:56 +01:00

236 lines
6.9 KiB
Swift

//
// SidebarItem.swift
// Yattee
//
// Represents items that can appear in the sidebar navigation.
//
import Foundation
/// Represents all possible sidebar items for navigation.
enum SidebarItem: Hashable, Identifiable {
// MARK: - Fixed Navigation Items
case home
case search
case sources
case settings
case nowPlaying
// MARK: - Dynamic Channel Items
case channel(channelID: String, name: String, source: ContentSource)
// MARK: - Dynamic Playlist Items
case playlist(id: UUID, title: String)
// MARK: - Dynamic Media Source Items
case mediaSource(id: UUID, name: String, type: MediaSourceType)
// MARK: - Dynamic Instance Items
case instance(id: UUID, name: String, type: InstanceType)
// MARK: - Collection Items
case bookmarks
case history
case downloads
case subscriptionsFeed
case manageChannels
// MARK: - Identifiable
var id: String {
switch self {
case .home:
return "home"
case .search:
return "search"
case .sources:
return "sources"
case .settings:
return "settings"
case .nowPlaying:
return "now-playing"
case .channel(let channelID, _, let source):
return "channel-\(source.provider)-\(channelID)"
case .playlist(let id, _):
return "playlist-\(id.uuidString)"
case .mediaSource(let id, _, _):
return "mediasource-\(id.uuidString)"
case .instance(let id, _, _):
return "instance-\(id.uuidString)"
case .bookmarks:
return "bookmarks"
case .history:
return "history"
case .downloads:
return "downloads"
case .subscriptionsFeed:
return "subscriptions-feed"
case .manageChannels:
return "manage-channels"
}
}
// MARK: - Display Properties
var title: String {
switch self {
case .home:
return String(localized: "tabs.home")
case .search:
return String(localized: "tabs.search")
case .sources:
return String(localized: "tabs.sources")
case .settings:
return String(localized: "tabs.settings")
case .nowPlaying:
return String(localized: "sidebar.nowPlaying")
case .channel(_, let name, _):
return name
case .playlist(_, let title):
return title
case .mediaSource(_, let name, _):
return name
case .instance(_, let name, _):
return name
case .bookmarks:
return String(localized: "home.bookmarks.title")
case .history:
return String(localized: "home.history.title")
case .downloads:
return String(localized: "home.downloads.title")
case .subscriptionsFeed:
return String(localized: "home.subscriptions.title")
case .manageChannels:
return String(localized: "sidebar.manageChannels")
}
}
var systemImage: String {
switch self {
case .home:
return "house.fill"
case .search:
return "magnifyingglass"
case .sources:
return "server.rack"
case .settings:
return "gear"
case .nowPlaying:
return "play.circle.fill"
case .channel:
return "person.circle"
case .playlist:
return "list.bullet.rectangle"
case .mediaSource(_, _, let type):
return type.systemImage
case .instance(_, _, let type):
return type.systemImage
case .bookmarks:
return "bookmark.fill"
case .history:
return "clock"
case .downloads:
return "arrow.down.circle"
case .subscriptionsFeed:
return "play.square.stack.fill"
case .manageChannels:
return "person.2"
}
}
// MARK: - Navigation
/// Converts this sidebar item to a NavigationDestination for pushing onto the navigation stack.
/// Returns nil for items that are root views (home, search) which don't push.
func navigationDestination() -> NavigationDestination? {
switch self {
case .home, .search, .sources, .settings, .nowPlaying:
// These are root tabs, not push destinations
return nil
case .channel(let channelID, _, let source):
return .channel(channelID, source)
case .playlist(let id, let title):
return .playlist(.local(id, title: title))
case .mediaSource(let id, _, _):
return .mediaSource(id)
case .instance:
// Instances are root views in the sidebar, not push destinations
return nil
case .bookmarks:
return .bookmarks
case .history:
return .history
case .downloads:
return .downloads
case .subscriptionsFeed:
return .subscriptionsFeed
case .manageChannels:
return .manageChannels
}
}
// MARK: - Item Categories
/// Whether this is a fixed navigation item (always visible).
var isFixedNavigation: Bool {
switch self {
case .home, .search, .sources, .settings, .nowPlaying:
return true
default:
return false
}
}
/// Whether this is a dynamic channel item.
var isChannel: Bool {
if case .channel = self { return true }
return false
}
/// Whether this is a dynamic playlist item.
var isPlaylist: Bool {
if case .playlist = self { return true }
return false
}
/// Whether this is a dynamic media source item.
var isMediaSource: Bool {
if case .mediaSource = self { return true }
return false
}
/// Whether this is a dynamic instance item.
var isInstance: Bool {
if case .instance = self { return true }
return false
}
}
// MARK: - Factory Methods
extension SidebarItem {
/// Creates a SidebarItem from a Subscription.
static func from(subscription: Subscription) -> SidebarItem {
.channel(
channelID: subscription.channelID,
name: subscription.name,
source: subscription.contentSource
)
}
/// Creates a SidebarItem from a LocalPlaylist.
static func from(playlist: LocalPlaylist) -> SidebarItem {
.playlist(id: playlist.id, title: playlist.title)
}
/// Creates a SidebarItem from a MediaSource.
static func from(mediaSource: MediaSource) -> SidebarItem {
.mediaSource(id: mediaSource.id, name: mediaSource.name, type: mediaSource.type)
}
/// Creates a SidebarItem from an Instance.
static func from(instance: Instance) -> SidebarItem {
.instance(id: instance.id, name: instance.displayName, type: instance.type)
}
}