mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-17 06:27:29 +01:00
Support for private tracking with AniList and Bangumi (#1736)
Co-authored-by: MajorTanya <39014446+MajorTanya@users.noreply.github.com> Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
This commit is contained in:
@@ -25,6 +25,7 @@ data class BackupTracking(
|
||||
@ProtoNumber(10) var startedReadingDate: Long = 0,
|
||||
// finishedReadingDate is called endReadTime in 1.x
|
||||
@ProtoNumber(11) var finishedReadingDate: Long = 0,
|
||||
@ProtoNumber(12) var private: Boolean = false,
|
||||
@ProtoNumber(100) var mediaId: Long = 0,
|
||||
) {
|
||||
|
||||
@@ -48,6 +49,7 @@ data class BackupTracking(
|
||||
startDate = this@BackupTracking.startedReadingDate,
|
||||
finishDate = this@BackupTracking.finishedReadingDate,
|
||||
remoteUrl = this@BackupTracking.trackingUrl,
|
||||
private = this@BackupTracking.private,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -66,6 +68,7 @@ val backupTrackMapper = {
|
||||
remoteUrl: String,
|
||||
startDate: Long,
|
||||
finishDate: Long,
|
||||
private: Boolean,
|
||||
->
|
||||
BackupTracking(
|
||||
syncId = syncId.toInt(),
|
||||
@@ -80,5 +83,6 @@ val backupTrackMapper = {
|
||||
startedReadingDate = startDate,
|
||||
finishedReadingDate = finishDate,
|
||||
trackingUrl = remoteUrl,
|
||||
private = private,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -404,6 +404,7 @@ class MangaRestorer(
|
||||
track.remoteUrl,
|
||||
track.startDate,
|
||||
track.finishDate,
|
||||
track.private,
|
||||
track.id,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -32,12 +32,15 @@ interface Track : Serializable {
|
||||
|
||||
var tracking_url: String
|
||||
|
||||
fun copyPersonalFrom(other: Track) {
|
||||
var private: Boolean
|
||||
|
||||
fun copyPersonalFrom(other: Track, copyRemotePrivate: Boolean = true) {
|
||||
last_chapter_read = other.last_chapter_read
|
||||
score = other.score
|
||||
status = other.status
|
||||
started_reading_date = other.started_reading_date
|
||||
finished_reading_date = other.finished_reading_date
|
||||
if (copyRemotePrivate) private = other.private
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -29,4 +29,6 @@ class TrackImpl : Track {
|
||||
override var finished_reading_date: Long = 0
|
||||
|
||||
override var tracking_url: String = ""
|
||||
|
||||
override var private: Boolean = false
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ abstract class BaseTracker(
|
||||
// Application and remote support for reading dates
|
||||
override val supportsReadingDates: Boolean = false
|
||||
|
||||
override val supportsPrivateTracking: Boolean = false
|
||||
|
||||
// TODO: Store all scores as 10 point in the future maybe?
|
||||
override fun get10PointScore(track: DomainTrack): Double {
|
||||
return track.score
|
||||
@@ -120,6 +122,11 @@ abstract class BaseTracker(
|
||||
updateRemote(track)
|
||||
}
|
||||
|
||||
override suspend fun setRemotePrivate(track: Track, private: Boolean) {
|
||||
track.private = private
|
||||
updateRemote(track)
|
||||
}
|
||||
|
||||
private suspend fun updateRemote(track: Track): Unit = withIOContext {
|
||||
try {
|
||||
update(track)
|
||||
|
||||
@@ -22,6 +22,8 @@ interface Tracker {
|
||||
// Application and remote support for reading dates
|
||||
val supportsReadingDates: Boolean
|
||||
|
||||
val supportsPrivateTracking: Boolean
|
||||
|
||||
@ColorInt
|
||||
fun getLogoColor(): Int
|
||||
|
||||
@@ -82,4 +84,6 @@ interface Tracker {
|
||||
suspend fun setRemoteStartDate(track: Track, epochMillis: Long)
|
||||
|
||||
suspend fun setRemoteFinishDate(track: Track, epochMillis: Long)
|
||||
|
||||
suspend fun setRemotePrivate(track: Track, private: Boolean)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import eu.kanade.tachiyomi.data.track.model.TrackSearch
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import tachiyomi.i18n.MR
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
@@ -43,6 +42,8 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
|
||||
|
||||
override val supportsReadingDates: Boolean = true
|
||||
|
||||
override val supportsPrivateTracking: Boolean = true
|
||||
|
||||
private val scorePreference = trackPreferences.anilistScoreType()
|
||||
|
||||
init {
|
||||
@@ -183,7 +184,7 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
|
||||
override suspend fun bind(track: Track, hasReadChapters: Boolean): Track {
|
||||
val remoteTrack = api.findLibManga(track, getUsername().toInt())
|
||||
return if (remoteTrack != null) {
|
||||
track.copyPersonalFrom(remoteTrack)
|
||||
track.copyPersonalFrom(remoteTrack, copyRemotePrivate = false)
|
||||
track.library_id = remoteTrack.library_id
|
||||
|
||||
if (track.status != COMPLETED) {
|
||||
|
||||
@@ -42,8 +42,8 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||
suspend fun addLibManga(track: Track): Track {
|
||||
return withIOContext {
|
||||
val query = """
|
||||
|mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) {
|
||||
|SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status) {
|
||||
|mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}private: Boolean) {
|
||||
|SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status, private: ${'$'}private) {
|
||||
| id
|
||||
| status
|
||||
|}
|
||||
@@ -56,6 +56,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||
put("mangaId", track.remote_id)
|
||||
put("progress", track.last_chapter_read.toInt())
|
||||
put("status", track.toApiStatus())
|
||||
put("private", track.private)
|
||||
}
|
||||
}
|
||||
with(json) {
|
||||
@@ -79,11 +80,11 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||
return withIOContext {
|
||||
val query = """
|
||||
|mutation UpdateManga(
|
||||
|${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus,
|
||||
|${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}private: Boolean,
|
||||
|${'$'}score: Int, ${'$'}startedAt: FuzzyDateInput, ${'$'}completedAt: FuzzyDateInput
|
||||
|) {
|
||||
|SaveMediaListEntry(
|
||||
|id: ${'$'}listId, progress: ${'$'}progress, status: ${'$'}status,
|
||||
|id: ${'$'}listId, progress: ${'$'}progress, status: ${'$'}status, private: ${'$'}private,
|
||||
|scoreRaw: ${'$'}score, startedAt: ${'$'}startedAt, completedAt: ${'$'}completedAt
|
||||
|) {
|
||||
|id
|
||||
@@ -102,6 +103,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||
put("score", track.score.toInt())
|
||||
put("startedAt", createDate(track.started_reading_date))
|
||||
put("completedAt", createDate(track.finished_reading_date))
|
||||
put("private", track.private)
|
||||
}
|
||||
}
|
||||
authClient.newCall(POST(API_URL, body = payload.toString().toRequestBody(jsonMime)))
|
||||
@@ -190,6 +192,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||
|status
|
||||
|scoreRaw: score(format: POINT_100)
|
||||
|progress
|
||||
|private
|
||||
|startedAt {
|
||||
|year
|
||||
|month
|
||||
|
||||
@@ -49,6 +49,7 @@ data class ALUserManga(
|
||||
val startDateFuzzy: Long,
|
||||
val completedDateFuzzy: Long,
|
||||
val manga: ALManga,
|
||||
val private: Boolean,
|
||||
) {
|
||||
fun toTrack() = Track.create(TrackerManager.ANILIST).apply {
|
||||
remote_id = manga.remoteId
|
||||
@@ -60,6 +61,7 @@ data class ALUserManga(
|
||||
last_chapter_read = chaptersRead.toDouble()
|
||||
library_id = libraryId
|
||||
total_chapters = manga.totalChapters
|
||||
private = this@ALUserManga.private
|
||||
}
|
||||
|
||||
private fun toTrackStatus() = when (listStatus) {
|
||||
|
||||
@@ -28,6 +28,7 @@ data class ALUserListItem(
|
||||
val startedAt: ALFuzzyDate,
|
||||
val completedAt: ALFuzzyDate,
|
||||
val media: ALSearchItem,
|
||||
val private: Boolean,
|
||||
) {
|
||||
fun toALUserManga(): ALUserManga {
|
||||
return ALUserManga(
|
||||
@@ -38,6 +39,7 @@ data class ALUserListItem(
|
||||
startDateFuzzy = startedAt.toEpochMilli(),
|
||||
completedDateFuzzy = completedAt.toEpochMilli(),
|
||||
manga = media.toALManga(),
|
||||
private = private,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
|
||||
|
||||
private val api by lazy { BangumiApi(id, client, interceptor) }
|
||||
|
||||
override val supportsPrivateTracking: Boolean = true
|
||||
|
||||
override fun getScoreList(): ImmutableList<String> = SCORE_LIST
|
||||
|
||||
override fun displayScore(track: DomainTrack): String {
|
||||
@@ -49,7 +51,7 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
|
||||
override suspend fun bind(track: Track, hasReadChapters: Boolean): Track {
|
||||
val statusTrack = api.statusLibManga(track, getUsername())
|
||||
return if (statusTrack != null) {
|
||||
track.copyPersonalFrom(statusTrack)
|
||||
track.copyPersonalFrom(statusTrack, copyRemotePrivate = false)
|
||||
track.library_id = statusTrack.library_id
|
||||
track.score = statusTrack.score
|
||||
track.last_chapter_read = statusTrack.last_chapter_read
|
||||
|
||||
@@ -45,6 +45,7 @@ class BangumiApi(
|
||||
put("type", track.toApiStatus())
|
||||
put("rate", track.score.toInt().coerceIn(0, 10))
|
||||
put("ep_status", track.last_chapter_read.toInt())
|
||||
put("private", track.private)
|
||||
}
|
||||
.toString()
|
||||
.toRequestBody()
|
||||
@@ -62,6 +63,7 @@ class BangumiApi(
|
||||
put("type", track.toApiStatus())
|
||||
put("rate", track.score.toInt().coerceIn(0, 10))
|
||||
put("ep_status", track.last_chapter_read.toInt())
|
||||
put("private", track.private)
|
||||
}
|
||||
.toString()
|
||||
.toRequestBody()
|
||||
|
||||
@@ -30,6 +30,8 @@ class TrackSearch : Track {
|
||||
|
||||
override var finished_reading_date: Long = 0
|
||||
|
||||
override var private: Boolean = false
|
||||
|
||||
override lateinit var tracking_url: String
|
||||
|
||||
var cover_url: String = ""
|
||||
|
||||
@@ -172,6 +172,7 @@ data class TrackInfoDialogHomeScreen(
|
||||
)
|
||||
},
|
||||
onCopyLink = { context.copyTrackerLink(it) },
|
||||
onTogglePrivate = screenModel::togglePrivate,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -247,6 +248,12 @@ data class TrackInfoDialogHomeScreen(
|
||||
}
|
||||
}
|
||||
|
||||
fun togglePrivate(item: TrackItem) {
|
||||
screenModelScope.launchNonCancellable {
|
||||
item.tracker.setRemotePrivate(item.track!!.toDbTrack(), !item.track.private)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<Track>.mapToTrackItem(): List<TrackItem> {
|
||||
val loggedInTrackers = Injekt.get<TrackerManager>().loggedInTrackers()
|
||||
val source = Injekt.get<SourceManager>().getOrStub(sourceId)
|
||||
@@ -673,11 +680,14 @@ data class TrackerSearchScreen(
|
||||
queryResult = state.queryResult,
|
||||
selected = state.selected,
|
||||
onSelectedChange = screenModel::updateSelection,
|
||||
onConfirmSelection = {
|
||||
screenModel.registerTracking(state.selected!!)
|
||||
onConfirmSelection = f@{ private: Boolean ->
|
||||
val selected = state.selected ?: return@f
|
||||
selected.private = private
|
||||
screenModel.registerTracking(selected)
|
||||
navigator.pop()
|
||||
},
|
||||
onDismissRequest = navigator::pop,
|
||||
supportsPrivateTracking = screenModel.supportsPrivateTracking,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -688,6 +698,8 @@ data class TrackerSearchScreen(
|
||||
private val tracker: Tracker,
|
||||
) : StateScreenModel<Model.State>(State()) {
|
||||
|
||||
val supportsPrivateTracking = tracker.supportsPrivateTracking
|
||||
|
||||
init {
|
||||
// Run search on first launch
|
||||
if (initialQuery.isNotBlank()) {
|
||||
|
||||
Reference in New Issue
Block a user