mirror of
https://github.com/yattee/yattee.git
synced 2025-08-06 10:44:06 +00:00
Settings for iOS/macOS
This commit is contained in:
@@ -4,8 +4,11 @@ import SwiftUI
|
||||
struct ChannelVideosView: View {
|
||||
let channel: Channel
|
||||
|
||||
@EnvironmentObject<NavigationState> private var navigationState
|
||||
@EnvironmentObject<Subscriptions> private var subscriptions
|
||||
@StateObject private var store = Store<Channel>()
|
||||
|
||||
@EnvironmentObject<InvidiousAPI> private var api
|
||||
@EnvironmentObject<NavigationModel> private var navigation
|
||||
@EnvironmentObject<SubscriptionsModel> private var subscriptions
|
||||
|
||||
@Environment(\.inNavigationView) private var inNavigationView
|
||||
|
||||
@@ -14,16 +17,8 @@ struct ChannelVideosView: View {
|
||||
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||
#endif
|
||||
|
||||
@ObservedObject private var store = Store<Channel>()
|
||||
|
||||
@Namespace private var focusNamespace
|
||||
|
||||
init(_ channel: Channel) {
|
||||
self.channel = channel
|
||||
|
||||
resource.addObserver(store)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
#if os(tvOS)
|
||||
@@ -55,12 +50,11 @@ struct ChannelVideosView: View {
|
||||
#endif
|
||||
#if !os(tvOS)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: subscriptionToolbarItemPlacement) {
|
||||
ToolbarItem {
|
||||
HStack {
|
||||
if let channel = store.item, let subscribers = channel.subscriptionsString {
|
||||
Text("**\(subscribers)** subscribers")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
Text("**\(store.item?.subscriptionsString ?? "loading")** subscribers")
|
||||
.foregroundColor(.secondary)
|
||||
.opacity(store.item?.subscriptionsString != nil ? 1 : 0)
|
||||
|
||||
subscriptionToggleButton
|
||||
}
|
||||
@@ -77,13 +71,19 @@ struct ChannelVideosView: View {
|
||||
#endif
|
||||
.modifier(UnsubscribeAlertModifier())
|
||||
.onAppear {
|
||||
resource.loadIfNeeded()
|
||||
if store.item.isNil {
|
||||
resource.addObserver(store)
|
||||
resource.load()
|
||||
}
|
||||
}
|
||||
.navigationTitle(navigationTitle)
|
||||
}
|
||||
|
||||
var resource: Resource {
|
||||
InvidiousAPI.shared.channel(channel.id)
|
||||
let resource = api.channel(channel.id)
|
||||
resource.addObserver(store)
|
||||
|
||||
return resource
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
@@ -94,7 +94,7 @@ struct ChannelVideosView: View {
|
||||
}
|
||||
#endif
|
||||
|
||||
return .status
|
||||
return .automatic
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -102,12 +102,12 @@ struct ChannelVideosView: View {
|
||||
Group {
|
||||
if subscriptions.isSubscribing(channel.id) {
|
||||
Button("Unsubscribe") {
|
||||
navigationState.presentUnsubscribeAlert(channel)
|
||||
navigation.presentUnsubscribeAlert(channel)
|
||||
}
|
||||
} else {
|
||||
Button("Subscribe") {
|
||||
subscriptions.subscribe(channel.id) {
|
||||
navigationState.sidebarSectionChanged.toggle()
|
||||
navigation.sidebarSectionChanged.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,21 +2,22 @@ import Siesta
|
||||
import SwiftUI
|
||||
|
||||
struct PopularView: View {
|
||||
@ObservedObject private var store = Store<[Video]>()
|
||||
@StateObject private var store = Store<[Video]>()
|
||||
|
||||
var resource = InvidiousAPI.shared.popular
|
||||
@EnvironmentObject<InvidiousAPI> private var api
|
||||
|
||||
init() {
|
||||
resource.addObserver(store)
|
||||
var resource: Resource {
|
||||
api.popular
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VideosView(videos: store.collection)
|
||||
.onAppear {
|
||||
resource.addObserver(store)
|
||||
resource.loadIfNeeded()
|
||||
}
|
||||
#if !os(tvOS)
|
||||
.navigationTitle("Popular")
|
||||
#endif
|
||||
.onAppear {
|
||||
resource.loadIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ struct SearchView: View {
|
||||
@Default(.searchDuration) private var searchDuration
|
||||
|
||||
@EnvironmentObject<Recents> private var recents
|
||||
@EnvironmentObject<SearchState> private var state
|
||||
@EnvironmentObject<SearchModel> private var state
|
||||
|
||||
@Environment(\.navigationStyle) private var navigationStyle
|
||||
|
||||
@@ -85,7 +85,7 @@ struct SearchView: View {
|
||||
#endif
|
||||
}
|
||||
}
|
||||
.opacity(recentsChanged ? 1 : 1)
|
||||
.redrawOn(change: recentsChanged)
|
||||
|
||||
clearAllButton
|
||||
}
|
||||
|
73
Shared/Views/SignInRequiredView.swift
Normal file
73
Shared/Views/SignInRequiredView.swift
Normal file
@@ -0,0 +1,73 @@
|
||||
import Defaults
|
||||
import SwiftUI
|
||||
|
||||
struct SignInRequiredView<Content: View>: View {
|
||||
let title: String
|
||||
let content: Content
|
||||
|
||||
@EnvironmentObject<InvidiousAPI> private var api
|
||||
@Default(.instances) private var instances
|
||||
@EnvironmentObject<NavigationModel> private var navigation
|
||||
|
||||
init(title: String, @ViewBuilder content: @escaping () -> Content) {
|
||||
self.title = title
|
||||
self.content = content()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if api.signedIn {
|
||||
content
|
||||
} else {
|
||||
prompt
|
||||
}
|
||||
}
|
||||
#if !os(tvOS)
|
||||
.navigationTitle(title)
|
||||
#endif
|
||||
}
|
||||
|
||||
var prompt: some View {
|
||||
VStack(spacing: 30) {
|
||||
Text("Sign In Required")
|
||||
.font(.title2.bold())
|
||||
|
||||
Group {
|
||||
if instances.isEmpty {
|
||||
Text("You need to create an instance and accounts\nto access **\(title)** section")
|
||||
} else {
|
||||
Text("You need to select an account\nto access **\(title)** section")
|
||||
}
|
||||
}
|
||||
.multilineTextAlignment(.center)
|
||||
.foregroundColor(.secondary)
|
||||
.font(.title3)
|
||||
.padding(.vertical)
|
||||
|
||||
if instances.isEmpty {
|
||||
openSettingsButton
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var openSettingsButton: some View {
|
||||
Button(action: {
|
||||
#if os(macOS)
|
||||
NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil)
|
||||
#else
|
||||
navigation.presentingSettings = true
|
||||
#endif
|
||||
}) {
|
||||
Text("Open Settings")
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
}
|
||||
|
||||
struct SignInRequiredView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
SignInRequiredView(title: "Subscriptions") {
|
||||
Text("Only when signed in")
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,30 +1,46 @@
|
||||
import Siesta
|
||||
import SwiftUI
|
||||
|
||||
struct SubscriptionsView: View {
|
||||
@ObservedObject private var store = Store<[Video]>()
|
||||
@StateObject private var store = Store<[Video]>()
|
||||
|
||||
var resource = InvidiousAPI.shared.feed
|
||||
@EnvironmentObject<InvidiousAPI> private var api
|
||||
|
||||
init() {
|
||||
resource.addObserver(store)
|
||||
var feed: Resource {
|
||||
api.feed
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VideosView(videos: store.collection)
|
||||
.onAppear {
|
||||
if let home = InvidiousAPI.shared.home.loadIfNeeded() {
|
||||
home.onSuccess { _ in
|
||||
resource.loadIfNeeded()
|
||||
}
|
||||
} else {
|
||||
resource.loadIfNeeded()
|
||||
SignInRequiredView(title: "Subscriptions") {
|
||||
VideosView(videos: store.collection)
|
||||
.onAppear {
|
||||
loadResources()
|
||||
}
|
||||
.onChange(of: api.account) { _ in
|
||||
loadResources(force: true)
|
||||
}
|
||||
.onChange(of: feed) { _ in
|
||||
loadResources(force: true)
|
||||
}
|
||||
}
|
||||
.refreshable {
|
||||
loadResources(force: true)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func loadResources(force: Bool = false) {
|
||||
feed.addObserver(store)
|
||||
|
||||
if let request = force ? api.home.load() : api.home.loadIfNeeded() {
|
||||
request.onSuccess { _ in
|
||||
loadFeed(force: force)
|
||||
}
|
||||
.refreshable {
|
||||
resource.load()
|
||||
}
|
||||
#if !os(tvOS)
|
||||
.navigationTitle("Subscriptions")
|
||||
#endif
|
||||
} else {
|
||||
loadFeed(force: force)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func loadFeed(force: Bool = false) {
|
||||
_ = force ? feed.load() : feed.loadIfNeeded()
|
||||
}
|
||||
}
|
||||
|
@@ -2,25 +2,23 @@ import Defaults
|
||||
import SwiftUI
|
||||
|
||||
struct VideoContextMenuView: View {
|
||||
@EnvironmentObject<NavigationState> private var navigationState
|
||||
@EnvironmentObject<InvidiousAPI> private var api
|
||||
@EnvironmentObject<NavigationModel> private var navigation
|
||||
@EnvironmentObject<Recents> private var recents
|
||||
@EnvironmentObject<Subscriptions> private var subscriptions
|
||||
@EnvironmentObject<SubscriptionsModel> private var subscriptions
|
||||
|
||||
let video: Video
|
||||
|
||||
@Default(.showingAddToPlaylist) var showingAddToPlaylist
|
||||
@Default(.videoIDToAddToPlaylist) var videoIDToAddToPlaylist
|
||||
|
||||
@State private var subscribed = false
|
||||
|
||||
var body: some View {
|
||||
Section {
|
||||
openChannelButton
|
||||
|
||||
subscriptionButton
|
||||
.opacity(subscribed ? 1 : 1)
|
||||
|
||||
if navigationState.tabSelection == .playlists {
|
||||
if navigation.tabSelection == .playlists {
|
||||
removeFromPlaylistButton
|
||||
} else {
|
||||
addToPlaylistButton
|
||||
@@ -32,9 +30,9 @@ struct VideoContextMenuView: View {
|
||||
Button("\(video.author) Channel") {
|
||||
let recent = RecentItem(from: video.channel)
|
||||
recents.open(recent)
|
||||
navigationState.tabSelection = .recentlyOpened(recent.tag)
|
||||
navigationState.isChannelOpen = true
|
||||
navigationState.sidebarSectionChanged.toggle()
|
||||
navigation.tabSelection = .recentlyOpened(recent.tag)
|
||||
navigation.isChannelOpen = true
|
||||
navigation.sidebarSectionChanged.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,13 +43,13 @@ struct VideoContextMenuView: View {
|
||||
#if os(tvOS)
|
||||
subscriptions.unsubscribe(video.channel.id)
|
||||
#else
|
||||
navigationState.presentUnsubscribeAlert(video.channel)
|
||||
navigation.presentUnsubscribeAlert(video.channel)
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
Button("Subscribe") {
|
||||
subscriptions.subscribe(video.channel.id) {
|
||||
navigationState.sidebarSectionChanged.toggle()
|
||||
navigation.sidebarSectionChanged.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,9 +65,9 @@ struct VideoContextMenuView: View {
|
||||
|
||||
var removeFromPlaylistButton: some View {
|
||||
Button("Remove from playlist", role: .destructive) {
|
||||
let resource = InvidiousAPI.shared.playlistVideo(Defaults[.selectedPlaylistID]!, video.indexID!)
|
||||
let resource = api.playlistVideo(Defaults[.selectedPlaylistID]!, video.indexID!)
|
||||
resource.request(.delete).onSuccess { _ in
|
||||
InvidiousAPI.shared.playlists.load()
|
||||
api.playlists.load()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user