mirror of
https://github.com/yattee/yattee.git
synced 2025-11-16 15:08:43 +00:00
Wrap favoritesChanged.toggle() calls in MainActor.run blocks to ensure main actor-isolated state mutations happen on the correct actor context. This resolves Swift concurrency warnings when updating state from nonisolated async contexts.
255 lines
9.0 KiB
Swift
255 lines
9.0 KiB
Swift
import Defaults
|
|
import Siesta
|
|
import SwiftUI
|
|
import UniformTypeIdentifiers
|
|
|
|
struct HomeView: View {
|
|
@ObservedObject private var accounts = AccountsModel.shared
|
|
@ObservedObject private var player = PlayerModel.shared
|
|
|
|
@State private var presentingHomeSettings = false
|
|
@State private var favoritesChanged = false
|
|
@State private var updateTask: Task<Void, Never>?
|
|
|
|
@FetchRequest(sortDescriptors: [.init(key: "watchedAt", ascending: false)])
|
|
var watches: FetchedResults<Watch>
|
|
@State private var historyID = UUID()
|
|
#if os(iOS)
|
|
@State private var recentDocumentsID = UUID()
|
|
#endif
|
|
|
|
#if !os(tvOS)
|
|
@Default(.favorites) private var favorites
|
|
@Default(.widgetsSettings) private var widgetsSettings
|
|
#endif
|
|
@Default(.showFavoritesInHome) private var showFavoritesInHome
|
|
@Default(.showOpenActionsInHome) private var showOpenActionsInHome
|
|
@Default(.showQueueInHome) private var showQueueInHome
|
|
|
|
private var navigation: NavigationModel { .shared }
|
|
|
|
var body: some View {
|
|
ScrollView(.vertical, showsIndicators: false) {
|
|
LazyVStack(spacing: 0, pinnedViews: []) {
|
|
VStack {
|
|
#if !os(tvOS)
|
|
HStack {
|
|
if showOpenActionsInHome {
|
|
AccentButton(text: "Files", imageSystemName: "folder") {
|
|
NavigationModel.shared.presentingFileImporter = true
|
|
}
|
|
AccentButton(text: "Paste", imageSystemName: "doc.on.clipboard.fill") {
|
|
OpenVideosModel.shared.openURLsFromClipboard(playbackMode: .playNow)
|
|
}
|
|
AccentButton(imageSystemName: "ellipsis") {
|
|
NavigationModel.shared.presentingOpenVideos = true
|
|
}
|
|
.frame(maxWidth: 40)
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if os(tvOS)
|
|
HStack {
|
|
if showOpenActionsInHome {
|
|
Button {
|
|
NavigationModel.shared.presentingOpenVideos = true
|
|
} label: {
|
|
Label("Open Video", systemImage: "globe")
|
|
}
|
|
}
|
|
Button {
|
|
NavigationModel.shared.presentingAccounts = true
|
|
} label: {
|
|
Label("Locations", systemImage: "globe")
|
|
}
|
|
Spacer()
|
|
HideWatchedButtons()
|
|
HideShortsButtons()
|
|
Button {
|
|
NavigationModel.shared.presentingSettings = true
|
|
} label: {
|
|
Label("Settings", systemImage: "gear")
|
|
}
|
|
}
|
|
.font(.caption)
|
|
.imageScale(.small)
|
|
.foregroundColor(.primary)
|
|
#endif
|
|
}
|
|
.padding(.top, 15)
|
|
.padding(.bottom, 15)
|
|
#if os(tvOS)
|
|
.padding(.horizontal, 40)
|
|
#else
|
|
.padding(.horizontal, 15)
|
|
#endif
|
|
|
|
if showQueueInHome {
|
|
QueueView()
|
|
#if os(tvOS)
|
|
.padding(.horizontal, 40)
|
|
#else
|
|
.padding(.horizontal, 15)
|
|
#endif
|
|
}
|
|
|
|
if !accounts.current.isNil, showFavoritesInHome {
|
|
#if os(tvOS)
|
|
ForEach(Defaults[.favorites]) { item in
|
|
FavoriteItemView(item: item, favoritesChanged: $favoritesChanged)
|
|
.animation(nil, value: favoritesChanged)
|
|
}
|
|
#else
|
|
ForEach(favorites) { item in
|
|
FavoriteItemView(item: item, favoritesChanged: $favoritesChanged)
|
|
.animation(nil, value: favoritesChanged)
|
|
#if os(macOS)
|
|
.workaroundForVerticalScrollingBug()
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if !os(tvOS)
|
|
Color.clear.padding(.bottom, 60)
|
|
#endif
|
|
}
|
|
}
|
|
.animation(nil, value: favoritesChanged)
|
|
.onAppear {
|
|
updateTask = Task {
|
|
async let favoritesUpdates: Void = {
|
|
for await _ in Defaults.updates(.favorites) {
|
|
await MainActor.run {
|
|
favoritesChanged.toggle()
|
|
}
|
|
}
|
|
}()
|
|
async let widgetsUpdates: Void = {
|
|
for await _ in Defaults.updates(.widgetsSettings) {
|
|
await MainActor.run {
|
|
favoritesChanged.toggle()
|
|
}
|
|
}
|
|
}()
|
|
_ = await(favoritesUpdates, widgetsUpdates)
|
|
}
|
|
}
|
|
.onDisappear {
|
|
updateTask?.cancel()
|
|
}
|
|
|
|
.onChange(of: player.presentingPlayer) { presenting in
|
|
if presenting {
|
|
updateTask?.cancel()
|
|
} else {
|
|
updateTask = Task {
|
|
async let favoritesUpdates: Void = {
|
|
for await _ in Defaults.updates(.favorites) {
|
|
await MainActor.run {
|
|
favoritesChanged.toggle()
|
|
}
|
|
}
|
|
}()
|
|
async let widgetsUpdates: Void = {
|
|
for await _ in Defaults.updates(.widgetsSettings) {
|
|
await MainActor.run {
|
|
favoritesChanged.toggle()
|
|
}
|
|
}
|
|
}()
|
|
_ = await(favoritesUpdates, widgetsUpdates)
|
|
}
|
|
}
|
|
}
|
|
|
|
.redrawOn(change: favoritesChanged)
|
|
|
|
#if os(tvOS)
|
|
.edgesIgnoringSafeArea(.horizontal)
|
|
#else
|
|
.navigationTitle("Home")
|
|
#endif
|
|
#if os(macOS)
|
|
.background(Color.secondaryBackground)
|
|
.frame(minWidth: Constants.contentViewMinWidth)
|
|
.toolbar {
|
|
ToolbarItemGroup(placement: .automatic) {
|
|
HideWatchedButtons()
|
|
HideShortsButtons()
|
|
HomeSettingsButton()
|
|
}
|
|
}
|
|
#endif
|
|
#if os(iOS)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .principal) {
|
|
homeMenu
|
|
}
|
|
}
|
|
#endif
|
|
#if !os(macOS)
|
|
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
|
|
favoritesChanged.toggle()
|
|
}
|
|
#endif
|
|
}
|
|
|
|
func sectionLabel(_ label: String) -> some View {
|
|
Text(label.localized())
|
|
#if os(tvOS)
|
|
.padding(.horizontal, 40)
|
|
#else
|
|
.padding(.horizontal, 15)
|
|
#endif
|
|
.font(.title3.bold())
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
|
|
#if os(iOS)
|
|
var homeMenu: some View {
|
|
Menu {
|
|
Section {
|
|
HideWatchedButtons()
|
|
HideShortsButtons()
|
|
}
|
|
Section {
|
|
Button {
|
|
navigation.presentingHomeSettings = true
|
|
} label: {
|
|
Label("Home Settings", systemImage: "gear")
|
|
}
|
|
}
|
|
} label: {
|
|
HStack(spacing: 12) {
|
|
Text("Home")
|
|
.foregroundColor(.primary)
|
|
.font(.headline)
|
|
|
|
Image(systemName: "chevron.down.circle.fill")
|
|
.foregroundColor(.accentColor)
|
|
.imageScale(.small)
|
|
}
|
|
.transaction { t in t.animation = nil }
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
struct Home_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
TabView {
|
|
NavigationView {
|
|
HomeView()
|
|
.injectFixtureEnvironmentObjects()
|
|
.tabItem {
|
|
Label("Home", systemImage: "house")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|