mirror of
https://github.com/yattee/yattee.git
synced 2026-10-23 05:02:53 +00:00
Keep one login's verdicts and content off another's
A feed failure recorded its verdict before checking whether anyone was still waiting for it, and the verdict resolves the *currently* selected account — so a 401 from a credential the app had already moved off could mark its replacement, or a different account, as rejected indefinitely. The instance browser drops its feed, subscriptions and playlists when the selected login changes. Nothing else there would notice: the instance, the tab and the "already has data" shortcut are all unchanged between two accounts on the same server. Caches are no longer discarded while the identity is unresolved. With iCloud Drive off the scope drops to unresolved on every activation and comes back the same, and the context reads as local in between — which looked like a move and threw the caches away several times a day. Local is always in the account picker again. Its feed needs a Yattee Server and reports that itself; with correction no longer moving away from local, leaving it out of the list made it unreachable once left. And the subscriptions view reloads when an instance is switched off, which moves reads to local without touching the context or any status.
This commit is contained in:
Binary file not shown.
47
DerivedDataReview/Logs/Package/LogStoreManifest.plist
Normal file
47
DerivedDataReview/Logs/Package/LogStoreManifest.plist
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>logFormatVersion</key>
|
||||
<integer>13</integer>
|
||||
<key>logs</key>
|
||||
<dict>
|
||||
<key>D50ED115-45BF-4DD0-BB7E-A46C31531159</key>
|
||||
<dict>
|
||||
<key>className</key>
|
||||
<string>IDECommandLineBuildLog</string>
|
||||
<key>documentTypeString</key>
|
||||
<string><nil></string>
|
||||
<key>domainType</key>
|
||||
<string>Xcode.IDEActivityLogDomainType.BuildLog</string>
|
||||
<key>fileName</key>
|
||||
<string>D50ED115-45BF-4DD0-BB7E-A46C31531159.xcactivitylog</string>
|
||||
<key>hasPrimaryLog</key>
|
||||
<true/>
|
||||
<key>primaryObservable</key>
|
||||
<dict>
|
||||
<key>highLevelStatus</key>
|
||||
<string>E</string>
|
||||
<key>totalNumberOfAnalyzerIssues</key>
|
||||
<integer>0</integer>
|
||||
<key>totalNumberOfErrors</key>
|
||||
<integer>2</integer>
|
||||
<key>totalNumberOfTestFailures</key>
|
||||
<integer>0</integer>
|
||||
<key>totalNumberOfWarnings</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>signature</key>
|
||||
<string>Resolve Packages</string>
|
||||
<key>timeStartedRecording</key>
|
||||
<real>807236581.56917596</real>
|
||||
<key>timeStoppedRecording</key>
|
||||
<real>807236581.71271205</real>
|
||||
<key>title</key>
|
||||
<string>Resolve Packages</string>
|
||||
<key>uniqueIdentifier</key>
|
||||
<string>D50ED115-45BF-4DD0-BB7E-A46C31531159</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -629,8 +629,13 @@ final class AppEnvironment {
|
||||
// logins on the same instance collapse onto the same one; and not on
|
||||
// the stored context, because that does not move when a credential
|
||||
// arrives or disappears and the app falls back to local and back again.
|
||||
// Never while the identity is unresolved. With iCloud Drive off the
|
||||
// scope drops to unresolved on every activation and comes back
|
||||
// unchanged; the context reads as local in between, and treating that
|
||||
// as a move would throw the caches away several times a day. "Cannot
|
||||
// tell yet" is not grounds for anything destructive.
|
||||
let effective = dataContextValidator.effectiveContext
|
||||
if effective != lastCachedContext {
|
||||
if effective != lastCachedContext, credentialIdentityScope.isResolved {
|
||||
lastCachedContext = effective
|
||||
clearAccountScopedCaches()
|
||||
}
|
||||
|
||||
@@ -433,8 +433,8 @@ final class SubscriptionFeedCache {
|
||||
feedLoadState = .ready
|
||||
} catch {
|
||||
LoggingService.shared.error("Failed to fetch Piped feed: \(error.localizedDescription)", category: .general)
|
||||
recordOutcome(error, backend: .piped, using: appEnvironment)
|
||||
guard isCurrentAccountsResult(generation) else { return }
|
||||
recordOutcome(error, backend: .piped, using: appEnvironment)
|
||||
feedLoadState = .error(.networkError(error.localizedDescription))
|
||||
}
|
||||
|
||||
@@ -508,8 +508,8 @@ final class SubscriptionFeedCache {
|
||||
"Failed to fetch Invidious feed: \(error.localizedDescription)",
|
||||
category: .general
|
||||
)
|
||||
recordOutcome(error, backend: .invidious, using: appEnvironment)
|
||||
guard isCurrentAccountsResult(generation) else { return }
|
||||
recordOutcome(error, backend: .invidious, using: appEnvironment)
|
||||
feedLoadState = .error(.networkError(error.localizedDescription))
|
||||
}
|
||||
|
||||
|
||||
@@ -74,13 +74,11 @@ final class SubscriptionAccountValidator {
|
||||
var availableAccounts: [SubscriptionAccount] {
|
||||
var accounts: [SubscriptionAccount] = []
|
||||
|
||||
// Add local if Yattee Server is configured and enabled
|
||||
let hasYatteeServer = instancesManager.instances.contains {
|
||||
$0.type == .yatteeServer && $0.isEnabled
|
||||
}
|
||||
if hasYatteeServer {
|
||||
accounts.append(.local)
|
||||
}
|
||||
// Local is always offered. Its *feed* needs a Yattee Server and says so
|
||||
// itself when there is none; that is not a reason to make the user's
|
||||
// own data unselectable — and since correction no longer moves away
|
||||
// from local, leaving it out would make it unreachable once left.
|
||||
accounts.append(.local)
|
||||
|
||||
// Add logged-in and enabled Invidious instances
|
||||
for instance in instancesManager.instances {
|
||||
|
||||
@@ -447,6 +447,14 @@ struct InstanceBrowseView: View {
|
||||
.onReceive(NotificationCenter.default.publisher(for: .accountsDidChange)) { _ in
|
||||
refreshLoginState()
|
||||
}
|
||||
// Selecting the other login on this instance changes whose feed and
|
||||
// playlists these are. Nothing else here would notice: the instance,
|
||||
// the tab and the "already has data" shortcut are all unchanged.
|
||||
.onReceive(NotificationCenter.default.publisher(for: .dataContextDidChange)) { _ in
|
||||
discardAccountScopedContent()
|
||||
refreshLoginState()
|
||||
Task { await startContentLoad() }
|
||||
}
|
||||
.onChange(of: selectedTab) { _, _ in
|
||||
isLoading = true
|
||||
errorMessage = nil
|
||||
@@ -1179,6 +1187,15 @@ struct InstanceBrowseView: View {
|
||||
|
||||
// MARK: - Data Loading
|
||||
|
||||
/// Drops what belonged to the previously selected login.
|
||||
///
|
||||
/// Public browsing — popular and trending — is unauthenticated and stays.
|
||||
private func discardAccountScopedContent() {
|
||||
feedVideos = []
|
||||
feedSubscriptions = []
|
||||
userPlaylists = []
|
||||
}
|
||||
|
||||
private func refreshLoginState() {
|
||||
guard instance.supportsAuthentication else { return }
|
||||
isLoggedIn = appEnvironment?.isSignedIn(to: instance) ?? false
|
||||
|
||||
@@ -518,9 +518,14 @@ struct SubscriptionsView: View {
|
||||
.onReceive(NotificationCenter.default.publisher(for: .dataContextDidChange)) { _ in
|
||||
reloadForContextChange()
|
||||
}
|
||||
// Instances count for the same reason: switching one off
|
||||
// moves reads to local without touching the stored context
|
||||
// or any account status.
|
||||
.onReceive(NotificationCenter.default.publisher(for: .accountStatusesDidChange)) { _ in
|
||||
guard appEnvironment?.dataContextValidator.effectiveContext != loadedContext else { return }
|
||||
reloadForContextChange()
|
||||
reloadIfEffectiveContextMoved()
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: .instancesDidChange)) { _ in
|
||||
reloadIfEffectiveContextMoved()
|
||||
}
|
||||
.task(id: instanceConfigurationID) {
|
||||
LoggingService.shared.debug("SubscriptionsView task triggered, instanceConfigurationID: \(instanceConfigurationID)", category: .general)
|
||||
@@ -1338,6 +1343,11 @@ struct SubscriptionsView: View {
|
||||
|
||||
/// Loads subscriptions asynchronously from the current provider.
|
||||
/// For Invidious, this fetches from the API and creates temporary Subscription objects for UI.
|
||||
private func reloadIfEffectiveContextMoved() {
|
||||
guard appEnvironment?.dataContextValidator.effectiveContext != loadedContext else { return }
|
||||
reloadForContextChange()
|
||||
}
|
||||
|
||||
/// Drops what is on screen and reloads it for whatever context is current.
|
||||
private func reloadForContextChange() {
|
||||
loadedContext = appEnvironment?.dataContextValidator.effectiveContext
|
||||
|
||||
Reference in New Issue
Block a user