mirror of
https://github.com/yattee/yattee.git
synced 2026-10-23 21:22:53 +00:00
Bind the playlist import to the login that listed it
Checking that some credential exists was the wrong question. Everything on screen was read with one particular login, and a replacement is a different person's account on the same server — its playlist IDs mean something else entirely, so importing "the same" playlist under it fetches whatever that ID happens to name there. The credential the list was loaded with is now recorded, and every import is checked against it rather than against whatever is current. The two checks around creating the local playlist are a suspension apart, so the login can still go between them. Nothing has been read at that point, and an empty playlist named after somebody else's is not worth keeping.
This commit is contained in:
@@ -29,6 +29,11 @@ struct ImportPlaylistsView: View {
|
||||
/// The merge started from the confirmation dialog, which runs detached
|
||||
/// from the batch that queued it. Held so the batch can wait for it.
|
||||
@State private var mergeImportTask: Task<Void, Never>?
|
||||
/// The credential the listed playlists were read with. Everything on
|
||||
/// screen belongs to it, so every import is checked against this rather
|
||||
/// than against whatever is current — a replacement is a different
|
||||
/// person's account, and its playlist IDs mean something else entirely.
|
||||
@State private var loadedCredential: String?
|
||||
|
||||
// Merge warning state
|
||||
@State private var showMergeWarning = false
|
||||
@@ -278,6 +283,8 @@ struct ImportPlaylistsView: View {
|
||||
|
||||
isLoading = true
|
||||
error = nil
|
||||
loadedCredential = credential
|
||||
loginChangedDuringImport = false
|
||||
|
||||
do {
|
||||
let fetchedPlaylists: [Playlist]
|
||||
@@ -319,7 +326,7 @@ struct ImportPlaylistsView: View {
|
||||
// Checked before anything is created: a playlist made for a login that
|
||||
// has already gone would be left behind empty, because the import
|
||||
// aborts on its first write.
|
||||
guard appEnvironment.credential(forInstance: instance) != nil else {
|
||||
guard appEnvironment.credential(forInstance: instance) == loadedCredential else {
|
||||
loginChangedDuringImport = true
|
||||
return
|
||||
}
|
||||
@@ -338,15 +345,25 @@ struct ImportPlaylistsView: View {
|
||||
// Create new local playlist and import
|
||||
let localPlaylist = appEnvironment.dataManager.createPlaylist(title: playlist.title)
|
||||
await performImport(playlist, into: localPlaylist)
|
||||
|
||||
// The guard above and the one inside are a suspension apart, so the
|
||||
// login can still go in between. Nothing was read, and an empty
|
||||
// playlist named after somebody else's is not worth keeping.
|
||||
if loginChangedDuringImport, localPlaylist.items?.isEmpty ?? true {
|
||||
appEnvironment.dataManager.deletePlaylist(localPlaylist)
|
||||
}
|
||||
}
|
||||
|
||||
private func performImport(_ playlist: Playlist, into localPlaylist: LocalPlaylist) async {
|
||||
guard let appEnvironment else { return }
|
||||
|
||||
// The login can go between choosing this playlist and reading it —
|
||||
// the merge dialog in particular waits on the user. Returning quietly
|
||||
// would leave the row importing forever and let a batch carry on.
|
||||
guard let credential = appEnvironment.credential(forInstance: instance) else {
|
||||
// The login can go, or be replaced, between choosing this playlist and
|
||||
// reading it — the merge dialog in particular waits on the user.
|
||||
// Returning quietly would leave the row importing forever and let a
|
||||
// batch carry on; reading it under a replacement would fetch whatever
|
||||
// that ID means to somebody else.
|
||||
guard let credential = appEnvironment.credential(forInstance: instance),
|
||||
credential == loadedCredential else {
|
||||
loginChangedDuringImport = true
|
||||
importingPlaylistID = nil
|
||||
importProgress = nil
|
||||
|
||||
Reference in New Issue
Block a user