Fix #960: scope subscription counts, import/export to active account

When an Invidious/Piped account is active, several views read the local
SwiftData subscription store instead of the account, so counts
contradicted each other (Home tile said 10 while the Channels list
showed the server's 2):

- Home Channels tile now uses the provider's count via new
  SubscriptionService.cachedSubscriptionCount (fetched once in the
  background when the server cache isn't populated yet)
- Export footer and export content use the active account's list;
  export runs async with a spinner and surfaces fetch errors as a toast
- CSV/OPML import routes through SubscriptionService.importSubscriptions,
  subscribing on the server for server accounts instead of silently
  writing to the invisible local store
- ChannelView subscribe-state is corrected via the provider after the
  optimistic local-store read; unused *Sync write helpers removed
- Server-account subscribe/unsubscribe/import now post
  subscriptionsDidChange so other views refresh
- New "Delete Local Subscription Data" section in Subscriptions
  settings (visible with a server account) clears the local store and
  queues CloudKit deletions so iCloud doesn't restore it
This commit is contained in:
Arkadiusz Fal
2026-07-28 18:54:38 +02:00
parent 155a5a48a0
commit cd01c08b5a
10 changed files with 339 additions and 27 deletions

View File

@@ -109,6 +109,10 @@ final class InvidiousSubscriptionProvider: SubscriptionProvider {
_ = try await fetchSubscriptions()
}
var cachedSubscriptionCount: Int? {
cachePopulated ? cachedChannels.count : nil
}
// MARK: - Private Helpers
/// Gets the authenticated Invidious instance and session ID from account settings.

View File

@@ -63,4 +63,8 @@ final class LocalSubscriptionProvider: SubscriptionProvider {
func refreshCache() async throws {
// Local provider doesn't need cache refresh - data is already local
}
var cachedSubscriptionCount: Int? {
dataManager.subscriptionCount
}
}

View File

@@ -109,6 +109,10 @@ final class PipedSubscriptionProvider: SubscriptionProvider {
_ = try await fetchSubscriptions()
}
var cachedSubscriptionCount: Int? {
cachePopulated ? cachedChannels.count : nil
}
// MARK: - Private Helpers
/// Gets the authenticated Piped instance and auth token from account settings.

View File

@@ -32,6 +32,10 @@ protocol SubscriptionProvider: Sendable {
/// - Returns: `true` if subscribed, `false` otherwise.
func isSubscribed(to channelID: String) async -> Bool
/// The subscription count known without a network fetch.
/// Returns nil when the provider hasn't populated its cache yet.
var cachedSubscriptionCount: Int? { get }
/// Refreshes the local cache of subscriptions from the remote source.
/// For local provider, this is a no-op.
func refreshCache() async throws