diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/TrackMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/TrackMutation.kt index 945c3dbea5..846553b407 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/TrackMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/TrackMutation.kt @@ -1,5 +1,7 @@ package suwayomi.tachidesk.graphql.mutations +import com.expediagroup.graphql.generator.annotations.GraphQLDeprecated +import com.expediagroup.graphql.generator.annotations.GraphQLDescription import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction @@ -133,6 +135,36 @@ class TrackMutation { } } + data class UnbindTrackInput( + val clientMutationId: String? = null, + val recordId: Int, + @GraphQLDescription("This will only work if the tracker of the track record supports deleting tracks") + val deleteRemoteTrack: Boolean? = null, + ) + + data class UnbindTrackPayload( + val clientMutationId: String?, + val trackRecord: TrackRecordType?, + ) + + fun unbindTrack(input: UnbindTrackInput): CompletableFuture { + val (clientMutationId, recordId, deleteRemoteTrack) = input + + return future { + Track.unbind(recordId, deleteRemoteTrack) + val trackRecord = + transaction { + TrackRecordTable.select { + TrackRecordTable.id eq recordId + }.firstOrNull() + } + UnbindTrackPayload( + clientMutationId, + trackRecord?.let { TrackRecordType(it) }, + ) + } + } + data class TrackProgressInput( val clientMutationId: String? = null, val mangaId: Int, @@ -168,6 +200,7 @@ class TrackMutation { val scoreString: String? = null, val startDate: Long? = null, val finishDate: Long? = null, + @GraphQLDeprecated("Replaced with \"unbindTrack\" mutation", replaceWith = ReplaceWith("unbindTrack")) val unbind: Boolean? = null, ) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/TrackType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/TrackType.kt index bf205d51e0..8fe0f5b9d8 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/TrackType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/TrackType.kt @@ -20,6 +20,7 @@ class TrackerType( val icon: String, val isLoggedIn: Boolean, val authUrl: String?, + val supportsTrackDeletion: Boolean?, ) : Node { constructor(tracker: Tracker) : this( tracker.isLoggedIn, @@ -36,6 +37,7 @@ class TrackerType( } else { tracker.authUrl() }, + tracker.supportsTrackDeletion, ) fun statuses(dataFetchingEnvironment: DataFetchingEnvironment): CompletableFuture> { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/Track.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/Track.kt index 999e56ef41..8401768a33 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/Track.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/Track.kt @@ -15,6 +15,7 @@ import org.jetbrains.exposed.sql.insertAndGetId import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction import org.jetbrains.exposed.sql.update +import suwayomi.tachidesk.manga.impl.track.tracker.DeletableTrackService import suwayomi.tachidesk.manga.impl.track.tracker.TrackerManager import suwayomi.tachidesk.manga.impl.track.tracker.model.Track import suwayomi.tachidesk.manga.impl.track.tracker.model.toTrack @@ -186,11 +187,29 @@ object Track { } } - suspend fun update(input: UpdateInput) { - if (input.unbind == true) { + suspend fun unbind( + recordId: Int, + deleteRemoteTrack: Boolean? = false, + ) { + val recordDb = transaction { - TrackRecordTable.deleteWhere { TrackRecordTable.id eq input.recordId } + TrackRecordTable.select { TrackRecordTable.id eq recordId }.first() } + + val tracker = TrackerManager.getTracker(recordDb[TrackRecordTable.trackerId])!! + + if (deleteRemoteTrack == true && tracker is DeletableTrackService) { + tracker.delete(recordDb.toTrack()) + } + + transaction { + TrackRecordTable.deleteWhere { TrackRecordTable.id eq recordId } + } + } + + suspend fun update(input: UpdateInput) { + if (input.unbind == true) { + unbind(input.recordId) return } val recordDb = diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/DeletableTrackService.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/DeletableTrackService.kt index bca514a886..bf9e6cd765 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/DeletableTrackService.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/DeletableTrackService.kt @@ -6,5 +6,5 @@ import suwayomi.tachidesk.manga.impl.track.tracker.model.Track * For track services api that support deleting a manga entry for a user's list */ interface DeletableTrackService { - suspend fun delete(track: Track): Track + suspend fun delete(track: Track) } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/Tracker.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/Tracker.kt index 3cbc733f1d..e763a1575f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/Tracker.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/Tracker.kt @@ -17,6 +17,8 @@ abstract class Tracker(val id: Int, val name: String) { // Application and remote support for reading dates open val supportsReadingDates: Boolean = false + abstract val supportsTrackDeletion: Boolean + override fun toString() = "$name ($id) (isLoggedIn= $isLoggedIn, isAuthExpired= ${getIfAuthExpired()})" abstract fun getLogo(): String diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/Anilist.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/Anilist.kt index 48b538968e..b32fb3a81e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/Anilist.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/Anilist.kt @@ -1,7 +1,6 @@ package suwayomi.tachidesk.manga.impl.track.tracker.anilist import android.annotation.StringRes -import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import mu.KotlinLogging @@ -29,6 +28,8 @@ class Anilist(id: Int) : Tracker(id, "AniList"), DeletableTrackService { const val POINT_3 = "POINT_3" } + override val supportsTrackDeletion: Boolean = true + private val json: Json by injectLazy() private val interceptor by lazy { AnilistInterceptor(this) } @@ -157,13 +158,13 @@ class Anilist(id: Int) : Tracker(id, "AniList"), DeletableTrackService { return api.updateLibManga(track) } - override suspend fun delete(track: Track): Track { + override suspend fun delete(track: Track) { if (track.library_id == null || track.library_id!! == 0L) { - val libManga = api.findLibManga(track, getUsername().toInt()) ?: return track + val libManga = api.findLibManga(track, getUsername().toInt()) ?: return track.library_id = libManga.library_id } - return api.deleteLibManga(track) + api.deleteLibManga(track) } override suspend fun bind( diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/AnilistApi.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/AnilistApi.kt index 2862504aa9..ddc7577e3b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/AnilistApi.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/AnilistApi.kt @@ -115,7 +115,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) { } } - suspend fun deleteLibManga(track: Track): Track { + suspend fun deleteLibManga(track: Track) { return withIOContext { val query = """ @@ -135,7 +135,6 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) { } authClient.newCall(POST(API_URL, body = payload.toString().toRequestBody(jsonMime))) .awaitSuccess() - track } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/MangaUpdates.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/MangaUpdates.kt index 9e15eb3623..10e22215ff 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/MangaUpdates.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/MangaUpdates.kt @@ -1,5 +1,6 @@ package suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates +import suwayomi.tachidesk.manga.impl.track.tracker.DeletableTrackService import suwayomi.tachidesk.manga.impl.track.tracker.Tracker import suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto.ListItem import suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto.Rating @@ -8,8 +9,7 @@ import suwayomi.tachidesk.manga.impl.track.tracker.mangaupdates.dto.toTrackSearc import suwayomi.tachidesk.manga.impl.track.tracker.model.Track import suwayomi.tachidesk.manga.impl.track.tracker.model.TrackSearch -class MangaUpdates(id: Int) : Tracker(id, "MangaUpdates") { - // , DeletableTracker +class MangaUpdates(id: Int) : Tracker(id, "MangaUpdates"), DeletableTrackService { companion object { const val READING_LIST = 0 const val WISH_LIST = 1 @@ -31,6 +31,8 @@ class MangaUpdates(id: Int) : Tracker(id, "MangaUpdates") { } } + override val supportsTrackDeletion: Boolean = true + private val interceptor by lazy { MangaUpdatesInterceptor(this) } private val api by lazy { MangaUpdatesApi(interceptor, client) } @@ -74,9 +76,9 @@ class MangaUpdates(id: Int) : Tracker(id, "MangaUpdates") { return track } - // override suspend fun delete(track: Track) { - // api.deleteSeriesFromList(track) - // } + override suspend fun delete(track: Track) { + api.deleteSeriesFromList(track) + } override suspend fun bind( track: Track, diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/myanimelist/MyAnimeList.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/myanimelist/MyAnimeList.kt index 87e0d36ed6..799cc83364 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/myanimelist/MyAnimeList.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/myanimelist/MyAnimeList.kt @@ -1,7 +1,6 @@ package suwayomi.tachidesk.manga.impl.track.tracker.myanimelist import android.annotation.StringRes -import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import mu.KotlinLogging @@ -26,6 +25,8 @@ class MyAnimeList(id: Int) : Tracker(id, "MyAnimeList"), DeletableTrackService { private const val SEARCH_LIST_PREFIX = "my:" } + override val supportsTrackDeletion: Boolean = true + private val json: Json by injectLazy() private val interceptor by lazy { MyAnimeListInterceptor(this) } @@ -94,8 +95,8 @@ class MyAnimeList(id: Int) : Tracker(id, "MyAnimeList"), DeletableTrackService { return api.updateItem(track) } - override suspend fun delete(track: Track): Track { - return api.deleteItem(track) + override suspend fun delete(track: Track) { + api.deleteItem(track) } override suspend fun bind( diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/myanimelist/MyAnimeListApi.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/myanimelist/MyAnimeListApi.kt index 1998a678ea..93cdf3cb6b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/myanimelist/MyAnimeListApi.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/myanimelist/MyAnimeListApi.kt @@ -164,18 +164,15 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI } } - suspend fun deleteItem(track: Track): Track { + suspend fun deleteItem(track: Track) { return withIOContext { val request = Request.Builder() .url(mangaUrl(track.media_id).toString()) .delete() .build() - with(json) { - authClient.newCall(request) - .awaitSuccess() - track - } + authClient.newCall(request) + .awaitSuccess() } }