Feed count model

This commit is contained in:
Arkadiusz Fal 2022-12-16 22:26:14 +01:00
parent 4acf9284f4
commit 26d3fba0f3
7 changed files with 60 additions and 56 deletions

View File

@ -10,9 +10,8 @@ final class FeedModel: ObservableObject, CacheModel {
@Published var isLoading = false @Published var isLoading = false
@Published var videos = [Video]() @Published var videos = [Video]()
@Published private var page = 1 @Published private var page = 1
@Published var unwatched = [Account: Int]()
@Published var unwatchedByChannel = [Account: [Channel.ID: Int]]()
private var feedCount = UnwatchedFeedCountModel.shared
private var cacheModel = FeedCacheModel.shared private var cacheModel = FeedCacheModel.shared
private var accounts = AccountsModel.shared private var accounts = AccountsModel.shared
@ -125,12 +124,12 @@ final class FeedModel: ObservableObject, CacheModel {
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self else { return } guard let self else { return }
if unwatchedCount != self.unwatched[account] { if unwatchedCount != self.feedCount.unwatched[account] {
self.unwatched[account] = unwatchedCount self.feedCount.unwatched[account] = unwatchedCount
} }
let byChannel = Dictionary(grouping: unwatched) { $0.channel.id }.mapValues(\.count) let byChannel = Dictionary(grouping: unwatched) { $0.channel.id }.mapValues(\.count)
self.unwatchedByChannel[account] = byChannel self.feedCount.unwatchedByChannel[account] = byChannel
} }
} }
} }
@ -156,13 +155,13 @@ final class FeedModel: ObservableObject, CacheModel {
var canMarkAllFeedAsWatched: Bool { var canMarkAllFeedAsWatched: Bool {
guard let account = accounts.current, accounts.signedIn else { return false } guard let account = accounts.current, accounts.signedIn else { return false }
return (unwatched[account] ?? 0) > 0 return (feedCount.unwatched[account] ?? 0) > 0
} }
func canMarkChannelAsWatched(_ channelID: Channel.ID) -> Bool { func canMarkChannelAsWatched(_ channelID: Channel.ID) -> Bool {
guard let account = accounts.current, accounts.signedIn else { return false } guard let account = accounts.current, accounts.signedIn else { return false }
return unwatchedByChannel[account]?.keys.contains(channelID) ?? false return feedCount.unwatchedByChannel[account]?.keys.contains(channelID) ?? false
} }
func markChannelAsWatched(_ channelID: Channel.ID) { func markChannelAsWatched(_ channelID: Channel.ID) {
@ -256,7 +255,7 @@ final class FeedModel: ObservableObject, CacheModel {
var canPlayUnwatchedFeed: Bool { var canPlayUnwatchedFeed: Bool {
guard let account = accounts.current, accounts.signedIn else { return false } guard let account = accounts.current, accounts.signedIn else { return false }
return (unwatched[account] ?? 0) > 0 return (feedCount.unwatched[account] ?? 0) > 0
} }
var feedTime: Date? { var feedTime: Date? {

View File

@ -0,0 +1,32 @@
import Foundation
import SwiftUI
final class UnwatchedFeedCountModel: ObservableObject {
static let shared = UnwatchedFeedCountModel()
@Published var unwatched = [Account: Int]()
@Published var unwatchedByChannel = [Account: [Channel.ID: Int]]()
private var accounts = AccountsModel.shared
var unwatchedText: Text? {
if let account = accounts.current,
!account.anonymous,
let count = unwatched[account]
{
return Text(String(count))
}
return nil
}
func unwatchedByChannelText(_ channel: Channel) -> Text? {
if let account = accounts.current,
!account.anonymous,
let count = unwatchedByChannel[account]?[channel.id]
{
return Text(String(count))
}
return nil
}
}

View File

@ -4,8 +4,8 @@ import SwiftUI
struct AppSidebarSubscriptions: View { struct AppSidebarSubscriptions: View {
@ObservedObject private var navigation = NavigationModel.shared @ObservedObject private var navigation = NavigationModel.shared
@ObservedObject private var feed = FeedModel.shared @ObservedObject private var feed = FeedModel.shared
@ObservedObject private var feedCount = UnwatchedFeedCountModel.shared
@ObservedObject private var subscriptions = SubscribedChannelsModel.shared @ObservedObject private var subscriptions = SubscribedChannelsModel.shared
@ObservedObject private var accounts = AccountsModel.shared @ObservedObject private var accounts = AccountsModel.shared
var body: some View { var body: some View {
@ -23,9 +23,9 @@ struct AppSidebarSubscriptions: View {
} else { } else {
Label(channel.name, systemImage: RecentsModel.symbolSystemImage(channel.name)) Label(channel.name, systemImage: RecentsModel.symbolSystemImage(channel.name))
} }
feedCount.unwatchedByChannelText(channel)
} }
.backport
.badge(channelBadge(channel))
} }
.contextMenu { .contextMenu {
if subscriptions.isSubscribing(channel.id) { if subscriptions.isSubscribing(channel.id) {
@ -41,14 +41,6 @@ struct AppSidebarSubscriptions: View {
} }
} }
func channelBadge(_ channel: Channel) -> Text? {
if let count = feed.unwatchedByChannel[accounts.current]?[channel.id] {
return Text(String(count))
}
return nil
}
@ViewBuilder func toggleWatchedButton(_ channel: Channel) -> some View { @ViewBuilder func toggleWatchedButton(_ channel: Channel) -> some View {
if feed.canMarkChannelAsWatched(channel.id) { if feed.canMarkChannelAsWatched(channel.id) {
markChannelAsWatchedButton(channel) markChannelAsWatchedButton(channel)

View File

@ -7,6 +7,7 @@ struct AppTabNavigation: View {
private var player = PlayerModel.shared private var player = PlayerModel.shared
@ObservedObject private var feed = FeedModel.shared @ObservedObject private var feed = FeedModel.shared
@ObservedObject private var subscriptions = SubscribedChannelsModel.shared @ObservedObject private var subscriptions = SubscribedChannelsModel.shared
@ObservedObject private var feedCount = UnwatchedFeedCountModel.shared
@Default(.showHome) private var showHome @Default(.showHome) private var showHome
@Default(.showDocuments) private var showDocuments @Default(.showDocuments) private var showDocuments
@ -94,18 +95,7 @@ struct AppTabNavigation: View {
} }
.tag(TabSelection.subscriptions) .tag(TabSelection.subscriptions)
.backport .backport
.badge(subscriptionsBadge) .badge(feedCount.unwatchedText)
}
var subscriptionsBadge: Text? {
guard let account = accounts.current,
let unwatched = feed.unwatched[account],
unwatched > 0
else {
return nil
}
return Text("\(String(unwatched))")
} }
private var subscriptionsVisible: Bool { private var subscriptionsVisible: Bool {

View File

@ -5,6 +5,7 @@ struct Sidebar: View {
@ObservedObject private var accounts = AccountsModel.shared @ObservedObject private var accounts = AccountsModel.shared
@ObservedObject private var navigation = NavigationModel.shared @ObservedObject private var navigation = NavigationModel.shared
@ObservedObject private var feed = FeedModel.shared @ObservedObject private var feed = FeedModel.shared
@ObservedObject private var feedCount = UnwatchedFeedCountModel.shared
@Default(.showHome) private var showHome @Default(.showHome) private var showHome
@Default(.visibleSections) private var visibleSections @Default(.visibleSections) private var visibleSections
@ -78,7 +79,7 @@ struct Sidebar: View {
.accessibility(label: Text("Subscriptions")) .accessibility(label: Text("Subscriptions"))
} }
.backport .backport
.badge(subscriptionsBadge) .badge(feedCount.unwatchedText)
.contextMenu { .contextMenu {
playUnwatchedButton playUnwatchedButton
toggleWatchedButton toggleWatchedButton
@ -146,17 +147,6 @@ struct Sidebar: View {
} }
} }
private var subscriptionsBadge: Text? {
guard let account = accounts.current,
let unwatched = feed.unwatched[account],
unwatched > 0
else {
return nil
}
return Text("\(String(unwatched))")
}
private func scrollScrollViewToItem(scrollView: ScrollViewProxy, for selection: TabSelection) { private func scrollScrollViewToItem(scrollView: ScrollViewProxy, for selection: TabSelection) {
if case .recentlyOpened = selection { if case .recentlyOpened = selection {
scrollView.scrollTo("recentlyOpened") scrollView.scrollTo("recentlyOpened")

View File

@ -6,6 +6,7 @@ struct ChannelsView: View {
@ObservedObject private var feed = FeedModel.shared @ObservedObject private var feed = FeedModel.shared
@ObservedObject private var subscriptions = SubscribedChannelsModel.shared @ObservedObject private var subscriptions = SubscribedChannelsModel.shared
@ObservedObject private var accounts = AccountsModel.shared @ObservedObject private var accounts = AccountsModel.shared
@ObservedObject private var feedCount = UnwatchedFeedCountModel.shared
@Default(.showCacheStatus) private var showCacheStatus @Default(.showCacheStatus) private var showCacheStatus
@ -25,7 +26,7 @@ struct ChannelsView: View {
} }
} }
.backport .backport
.badge(channelBadge(channel)) .badge(feedCount.unwatchedByChannelText(channel))
} }
.contextMenu { .contextMenu {
if subscriptions.isSubscribing(channel.id) { if subscriptions.isSubscribing(channel.id) {
@ -84,14 +85,6 @@ struct ChannelsView: View {
#endif #endif
} }
func channelBadge(_ channel: Channel) -> Text? {
if let count = feed.unwatchedByChannel[accounts.current]?[channel.id] {
return Text(String(count))
}
return nil
}
var header: some View { var header: some View {
HStack { HStack {
#if os(tvOS) #if os(tvOS)

View File

@ -185,6 +185,9 @@
37192D5728B179D60012EEDD /* ChaptersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37192D5628B179D60012EEDD /* ChaptersView.swift */; }; 37192D5728B179D60012EEDD /* ChaptersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37192D5628B179D60012EEDD /* ChaptersView.swift */; };
37192D5828B179D60012EEDD /* ChaptersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37192D5628B179D60012EEDD /* ChaptersView.swift */; }; 37192D5828B179D60012EEDD /* ChaptersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37192D5628B179D60012EEDD /* ChaptersView.swift */; };
37192D5928B179D60012EEDD /* ChaptersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37192D5628B179D60012EEDD /* ChaptersView.swift */; }; 37192D5928B179D60012EEDD /* ChaptersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37192D5628B179D60012EEDD /* ChaptersView.swift */; };
371AC09F294D13AA0085989E /* UnwatchedFeedCountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371AC09E294D13AA0085989E /* UnwatchedFeedCountModel.swift */; };
371AC0A0294D13AA0085989E /* UnwatchedFeedCountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371AC09E294D13AA0085989E /* UnwatchedFeedCountModel.swift */; };
371AC0A1294D13AA0085989E /* UnwatchedFeedCountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371AC09E294D13AA0085989E /* UnwatchedFeedCountModel.swift */; };
371B7E5C27596B8400D21217 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371B7E5B27596B8400D21217 /* Comment.swift */; }; 371B7E5C27596B8400D21217 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371B7E5B27596B8400D21217 /* Comment.swift */; };
371B7E5D27596B8400D21217 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371B7E5B27596B8400D21217 /* Comment.swift */; }; 371B7E5D27596B8400D21217 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371B7E5B27596B8400D21217 /* Comment.swift */; };
371B7E5E27596B8400D21217 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371B7E5B27596B8400D21217 /* Comment.swift */; }; 371B7E5E27596B8400D21217 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371B7E5B27596B8400D21217 /* Comment.swift */; };
@ -1155,6 +1158,7 @@
37169AA52729E2CC0011DE61 /* AccountsBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsBridge.swift; sourceTree = "<group>"; }; 37169AA52729E2CC0011DE61 /* AccountsBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsBridge.swift; sourceTree = "<group>"; };
3717407C2949D40800FDDBC7 /* ChannelLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelLinkView.swift; sourceTree = "<group>"; }; 3717407C2949D40800FDDBC7 /* ChannelLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelLinkView.swift; sourceTree = "<group>"; };
37192D5628B179D60012EEDD /* ChaptersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChaptersView.swift; sourceTree = "<group>"; }; 37192D5628B179D60012EEDD /* ChaptersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChaptersView.swift; sourceTree = "<group>"; };
371AC09E294D13AA0085989E /* UnwatchedFeedCountModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnwatchedFeedCountModel.swift; sourceTree = "<group>"; };
371B7E5B27596B8400D21217 /* Comment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = "<group>"; }; 371B7E5B27596B8400D21217 /* Comment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = "<group>"; };
371B7E602759706A00D21217 /* CommentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentsView.swift; sourceTree = "<group>"; }; 371B7E602759706A00D21217 /* CommentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentsView.swift; sourceTree = "<group>"; };
371B7E652759786B00D21217 /* Comment+Fixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Comment+Fixtures.swift"; sourceTree = "<group>"; }; 371B7E652759786B00D21217 /* Comment+Fixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Comment+Fixtures.swift"; sourceTree = "<group>"; };
@ -2328,10 +2332,6 @@
3743B86627216A1E00261544 /* Accounts */, 3743B86627216A1E00261544 /* Accounts */,
3743B864272169E200261544 /* Applications */, 3743B864272169E200261544 /* Applications */,
377F9F79294403DC0043F856 /* Cache */, 377F9F79294403DC0043F856 /* Cache */,
3743B86527216A0600261544 /* Player */,
3751BA8127E69131007B1A60 /* ReturnYouTubeDislike */,
37FB283F2721B20800A57617 /* Search */,
374C0539272436DA009BDDBE /* SponsorBlock */,
3776ADD5287381240078EBC4 /* Captions.swift */, 3776ADD5287381240078EBC4 /* Captions.swift */,
37AAF28F26740715007FC770 /* Channel.swift */, 37AAF28F26740715007FC770 /* Channel.swift */,
37C3A24427235DA70087A57A /* ChannelPlaylist.swift */, 37C3A24427235DA70087A57A /* ChannelPlaylist.swift */,
@ -2354,21 +2354,26 @@
3756C2A92861151C00E4B059 /* NetworkStateModel.swift */, 3756C2A92861151C00E4B059 /* NetworkStateModel.swift */,
377FF88A291A60310028EB0B /* OpenVideosModel.swift */, 377FF88A291A60310028EB0B /* OpenVideosModel.swift */,
37130A5E277657300033018A /* PersistenceController.swift */, 37130A5E277657300033018A /* PersistenceController.swift */,
3743B86527216A0600261544 /* Player */,
376578882685471400D4EA09 /* Playlist.swift */, 376578882685471400D4EA09 /* Playlist.swift */,
37BA794226DBA973002A0235 /* PlaylistsModel.swift */, 37BA794226DBA973002A0235 /* PlaylistsModel.swift */,
375EC95C289EEEE000751258 /* QualityProfile.swift */, 375EC95C289EEEE000751258 /* QualityProfile.swift */,
375EC969289F232600751258 /* QualityProfilesModel.swift */, 375EC969289F232600751258 /* QualityProfilesModel.swift */,
37C194C626F6A9C8005D3B96 /* RecentsModel.swift */, 37C194C626F6A9C8005D3B96 /* RecentsModel.swift */,
3751BA8127E69131007B1A60 /* ReturnYouTubeDislike */,
37FB283F2721B20800A57617 /* Search */,
374AB3D628BCAF0000DF56FB /* SeekModel.swift */, 374AB3D628BCAF0000DF56FB /* SeekModel.swift */,
374AB3DA28BCAF7E00DF56FB /* SeekType.swift */, 374AB3DA28BCAF7E00DF56FB /* SeekType.swift */,
37EAD86E267B9ED100D9E01B /* Segment.swift */, 37EAD86E267B9ED100D9E01B /* Segment.swift */,
37F0F4E9286F397E00C06C2E /* SettingsModel.swift */, 37F0F4E9286F397E00C06C2E /* SettingsModel.swift */,
37CEE4BC2677B670005A1EFE /* SingleAssetStream.swift */, 37CEE4BC2677B670005A1EFE /* SingleAssetStream.swift */,
374C0539272436DA009BDDBE /* SponsorBlock */,
3797758A2689345500DD52A8 /* Store.swift */, 3797758A2689345500DD52A8 /* Store.swift */,
37CEE4C02677B697005A1EFE /* Stream.swift */, 37CEE4C02677B697005A1EFE /* Stream.swift */,
373CFADA269663F1003CB2C6 /* Thumbnail.swift */, 373CFADA269663F1003CB2C6 /* Thumbnail.swift */,
37C0698127260B2100F7F6CB /* ThumbnailsModel.swift */, 37C0698127260B2100F7F6CB /* ThumbnailsModel.swift */,
3705B181267B4E4900704544 /* TrendingCategory.swift */, 3705B181267B4E4900704544 /* TrendingCategory.swift */,
371AC09E294D13AA0085989E /* UnwatchedFeedCountModel.swift */,
37F5E8B5291BE9D0006C15F5 /* URLBookmarkModel.swift */, 37F5E8B5291BE9D0006C15F5 /* URLBookmarkModel.swift */,
37D4B19626717E1500C925CA /* Video.swift */, 37D4B19626717E1500C925CA /* Video.swift */,
3784CDDE27772EE40055BBF2 /* Watch.swift */, 3784CDDE27772EE40055BBF2 /* Watch.swift */,
@ -3207,6 +3212,7 @@
3751BA8327E6914F007B1A60 /* ReturnYouTubeDislikeAPI.swift in Sources */, 3751BA8327E6914F007B1A60 /* ReturnYouTubeDislikeAPI.swift in Sources */,
373031F528383A89000CFD59 /* PiPDelegate.swift in Sources */, 373031F528383A89000CFD59 /* PiPDelegate.swift in Sources */,
37F5E8BA291BEF69006C15F5 /* BaseCacheModel.swift in Sources */, 37F5E8BA291BEF69006C15F5 /* BaseCacheModel.swift in Sources */,
371AC09F294D13AA0085989E /* UnwatchedFeedCountModel.swift in Sources */,
37DD9DC62785D63A00539416 /* UIResponder+Extensions.swift in Sources */, 37DD9DC62785D63A00539416 /* UIResponder+Extensions.swift in Sources */,
370015A928BBAE7F000149FD /* ProgressBar.swift in Sources */, 370015A928BBAE7F000149FD /* ProgressBar.swift in Sources */,
37C3A24927235FAA0087A57A /* ChannelPlaylistCell.swift in Sources */, 37C3A24927235FAA0087A57A /* ChannelPlaylistCell.swift in Sources */,
@ -3327,6 +3333,7 @@
374C053C2724614F009BDDBE /* PlayerTVMenu.swift in Sources */, 374C053C2724614F009BDDBE /* PlayerTVMenu.swift in Sources */,
377FC7DD267A081A00A6BBAF /* PopularView.swift in Sources */, 377FC7DD267A081A00A6BBAF /* PopularView.swift in Sources */,
374924DB2921050B0017D862 /* LocationsSettings.swift in Sources */, 374924DB2921050B0017D862 /* LocationsSettings.swift in Sources */,
371AC0A0294D13AA0085989E /* UnwatchedFeedCountModel.swift in Sources */,
37192D5828B179D60012EEDD /* ChaptersView.swift in Sources */, 37192D5828B179D60012EEDD /* ChaptersView.swift in Sources */,
3784CDE327772EE40055BBF2 /* Watch.swift in Sources */, 3784CDE327772EE40055BBF2 /* Watch.swift in Sources */,
37E80F3D287B107F00561799 /* VideoDetailsOverlay.swift in Sources */, 37E80F3D287B107F00561799 /* VideoDetailsOverlay.swift in Sources */,
@ -3786,6 +3793,7 @@
3782B9542755667600990149 /* String+Format.swift in Sources */, 3782B9542755667600990149 /* String+Format.swift in Sources */,
37D836BE294927E700005E5E /* ChannelsCacheModel.swift in Sources */, 37D836BE294927E700005E5E /* ChannelsCacheModel.swift in Sources */,
37152EEC26EFEB95004FB96D /* LazyView.swift in Sources */, 37152EEC26EFEB95004FB96D /* LazyView.swift in Sources */,
371AC0A1294D13AA0085989E /* UnwatchedFeedCountModel.swift in Sources */,
37EF9A78275BEB8E0043B585 /* CommentView.swift in Sources */, 37EF9A78275BEB8E0043B585 /* CommentView.swift in Sources */,
37484C2726FC83E000287258 /* InstanceForm.swift in Sources */, 37484C2726FC83E000287258 /* InstanceForm.swift in Sources */,
37E6D7A22944CD3800550C3D /* CacheStatusHeader.swift in Sources */, 37E6D7A22944CD3800550C3D /* CacheStatusHeader.swift in Sources */,