Add user manga notes (#428)

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
This commit is contained in:
kunet
2025-03-29 17:18:38 -04:00
committed by GitHub
parent 132d77aa99
commit 8fbe630308
26 changed files with 596 additions and 16 deletions

View File

@@ -99,4 +99,5 @@ private fun Manga.toBackupManga() =
lastModifiedAt = this.lastModifiedAt,
favoriteModifiedAt = this.favoriteModifiedAt,
version = this.version,
notes = this.notes,
)

View File

@@ -38,8 +38,10 @@ data class BackupManga(
@ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE,
@ProtoNumber(106) var lastModifiedAt: Long = 0,
@ProtoNumber(107) var favoriteModifiedAt: Long? = null,
// Mihon values start here
@ProtoNumber(108) var excludedScanlators: List<String> = emptyList(),
@ProtoNumber(109) var version: Long = 0,
@ProtoNumber(110) var notes: String = "",
) {
fun getMangaImpl(): Manga {
return Manga.create().copy(
@@ -60,6 +62,7 @@ data class BackupManga(
lastModifiedAt = this@BackupManga.lastModifiedAt,
favoriteModifiedAt = this@BackupManga.favoriteModifiedAt,
version = this@BackupManga.version,
notes = this@BackupManga.notes,
)
}
}

View File

@@ -129,6 +129,7 @@ class MangaRestorer(
updateStrategy = manga.updateStrategy.let(UpdateStrategyColumnAdapter::encode),
version = manga.version,
isSyncing = 1,
notes = manga.notes,
)
}
return manga

View File

@@ -30,6 +30,7 @@ object MigrationFlags {
private const val CATEGORIES = 0b00010
private const val CUSTOM_COVER = 0b01000
private const val DELETE_DOWNLOADED = 0b10000
private const val NOTES = 0b100000
private val coverCache: CoverCache by injectLazy()
private val downloadCache: DownloadCache by injectLazy()
@@ -50,6 +51,10 @@ object MigrationFlags {
return value and DELETE_DOWNLOADED != 0
}
fun hasNotes(value: Int): Boolean {
return value and NOTES != 0
}
/** Returns information about applicable flags with default selections. */
fun getFlags(manga: Manga?, defaultSelectedBitMap: Int): List<MigrationFlag> {
val flags = mutableListOf<MigrationFlag>()
@@ -63,6 +68,9 @@ object MigrationFlags {
if (downloadCache.getDownloadCount(manga) > 0) {
flags += MigrationFlag.create(DELETE_DOWNLOADED, defaultSelectedBitMap, MR.strings.delete_downloaded)
}
if (manga.notes.isNotBlank()) {
flags += MigrationFlag.create(NOTES, defaultSelectedBitMap, MR.strings.action_notes)
}
}
return flags
}

View File

@@ -213,6 +213,7 @@ internal class MigrateDialogScreenModel(
val migrateCategories = MigrationFlags.hasCategories(flags)
val migrateCustomCover = MigrationFlags.hasCustomCover(flags)
val deleteDownloaded = MigrationFlags.hasDeleteDownloaded(flags)
val migrateNotes = MigrationFlags.hasNotes(flags)
try {
syncChaptersWithSource.await(sourceChapters, newManga, newSource)
@@ -299,6 +300,7 @@ internal class MigrateDialogScreenModel(
chapterFlags = oldManga.chapterFlags,
viewerFlags = oldManga.viewerFlags,
dateAdded = if (replace) oldManga.dateAdded else Instant.now().toEpochMilli(),
notes = if (migrateNotes) oldManga.notes else null,
),
)
}

View File

@@ -50,6 +50,7 @@ import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchScreen
import eu.kanade.tachiyomi.ui.category.CategoryScreen
import eu.kanade.tachiyomi.ui.home.HomeScreen
import eu.kanade.tachiyomi.ui.manga.notes.MangaNotesScreen
import eu.kanade.tachiyomi.ui.manga.track.TrackInfoDialogHomeScreen
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.ui.setting.SettingsScreen
@@ -164,6 +165,7 @@ class MangaScreen(
onMigrateClicked = {
navigator.push(MigrateSearchScreen(successState.manga.id))
}.takeIf { successState.manga.favorite },
onEditNotesClicked = { navigator.push(MangaNotesScreen(manga = successState.manga)) },
onMultiBookmarkClicked = screenModel::bookmarkChapters,
onMultiMarkAsReadClicked = screenModel::markChaptersRead,
onMarkPreviousAsReadClicked = screenModel::markPreviousChapterRead,

View File

@@ -0,0 +1,61 @@
package eu.kanade.tachiyomi.ui.manga.notes
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.manga.MangaNotesScreen
import eu.kanade.presentation.util.Screen
import kotlinx.coroutines.flow.update
import tachiyomi.core.common.util.lang.launchNonCancellable
import tachiyomi.domain.manga.interactor.UpdateMangaNotes
import tachiyomi.domain.manga.model.Manga
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class MangaNotesScreen(
private val manga: Manga,
) : Screen() {
@Composable
override fun Content() {
val navigator = LocalNavigator.currentOrThrow
val screenModel = rememberScreenModel { Model(manga) }
val state by screenModel.state.collectAsState()
MangaNotesScreen(
state = state,
navigateUp = navigator::pop,
onUpdate = screenModel::updateNotes,
)
}
private class Model(
private val manga: Manga,
private val updateMangaNotes: UpdateMangaNotes = Injekt.get(),
) : StateScreenModel<State>(State(manga, manga.notes)) {
fun updateNotes(content: String) {
if (content == state.value.notes) return
mutableState.update {
it.copy(notes = content)
}
screenModelScope.launchNonCancellable {
updateMangaNotes(manga.id, content)
}
}
}
@Immutable
data class State(
val manga: Manga,
val notes: String,
)
}