mirror of
https://github.com/yattee/yattee.git
synced 2026-10-23 21:22:53 +00:00
Read a playlist back before writing over it
Ten review findings in the remote-playlist work, all of them the same shape: something acted on what it remembered instead of what the server says now. - A confirmed-gone playlist is forgotten whole — snapshot, list entry, and both ordering fences — so a refresh that started before the delete can no longer republish it, and a list refetch that fails offline no longer leaves it on screen. - `.accountsDidChange` discards account-scoped content when the account it belonged to is no longer the effective one, as the status notification already did. - A tab that stops being offered no longer stays selected: the browse view moves to the first available one, which reloads by itself. - A Handoff account id is accepted only when that account is on the instance the playlist lives on. - Composite writes moved up into `AccountService`, over a transport whose every method is exactly one request: create-with-privacy, rename, and set-description. The credential is rechecked between each pair, so a sign-out between the two halves of a create or an edit can no longer send the second half under the previous user's secret. - Editing a playlist no longer forces it private. Invidious serialises visibility as `isListed` — `privacy.public?`, so unlisted and private are indistinguishable — which the snapshot now carries; the picker starts there, and sends nothing at all unless it was moved. Invidious keeps the stored visibility when the PATCH omits it (measured), so an untouched picker leaves it exactly as it was. - Tapping Edit on a server-side playlist awaits an authenticated refetch first and populates the form from it; a failed refetch shows an error and no sheet. - Playlist rows are keyed by row rather than by video id, which two rows of the same video share. - The add-to-playlist sheet offers only accounts whose instance is enabled and that hold a credential, alongside the write capability.
This commit is contained in:
@@ -158,7 +158,7 @@ final class RemotePlaylistsCache {
|
||||
func deletePlaylist(id: String, for account: Account) async throws {
|
||||
do {
|
||||
try await accountService.deletePlaylist(id: id, for: account)
|
||||
forgetSnapshot(id: id, for: account.id)
|
||||
forgetPlaylist(id: id, for: account.id)
|
||||
await reconcileList(for: account)
|
||||
} catch {
|
||||
// The delete may have landed even though the answer did not, so the
|
||||
@@ -347,8 +347,7 @@ final class RemotePlaylistsCache {
|
||||
do {
|
||||
try await fetchSnapshot(id: id, for: account)
|
||||
} catch let error as APIError where Self.saysPlaylistIsGone(error) {
|
||||
forgetSnapshot(id: id, for: account.id)
|
||||
forgetListEntry(id: id, for: account.id)
|
||||
forgetPlaylist(id: id, for: account.id)
|
||||
} catch {
|
||||
// Swallowed: see the doc comment on `reconcileList(for:)`.
|
||||
}
|
||||
@@ -365,17 +364,22 @@ final class RemotePlaylistsCache {
|
||||
}
|
||||
}
|
||||
|
||||
private func forgetSnapshot(id: String, for accountID: UUID) {
|
||||
/// Drops a playlist the server is known no longer to have.
|
||||
///
|
||||
/// Three things, and each one is load-bearing. The snapshot goes, so a
|
||||
/// detail view still open on it has nothing left to show. The list entry
|
||||
/// goes, because the list is refetched right after but that refetch is
|
||||
/// allowed to fail — offline, a playlist known to be gone would otherwise
|
||||
/// sit in the list until the next successful fetch. And both fences move
|
||||
/// past every request already in flight: a refresh that started before the
|
||||
/// delete describes a playlist that still existed, and arriving late does
|
||||
/// not make it exist again.
|
||||
private func forgetPlaylist(id: String, for accountID: UUID) {
|
||||
appliedSnapshotSequence[SnapshotKey(accountID: accountID, playlistID: id)] = nextSequence()
|
||||
appliedListSequence[accountID] = nextSequence()
|
||||
|
||||
snapshots[accountID]?[id] = nil
|
||||
snapshotFetchDates[accountID]?[id] = nil
|
||||
}
|
||||
|
||||
/// Takes a playlist out of the account's last-fetched list.
|
||||
///
|
||||
/// The list is refetched right after, which normally says the same thing —
|
||||
/// but a refetch that cannot reach the server would otherwise leave a
|
||||
/// playlist that is known to be gone sitting in it.
|
||||
private func forgetListEntry(id: String, for accountID: UUID) {
|
||||
lists[accountID]?.removeAll { $0.id.playlistID == id }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user