Improve favorites sync statuses

This commit is contained in:
Jobobby04 2024-12-08 13:35:36 -05:00
parent 3f8cce8a32
commit 76af3b59f0
4 changed files with 243 additions and 141 deletions

View File

@ -15,7 +15,6 @@ import androidx.compose.ui.window.DialogProperties
import exh.favorites.FavoritesSyncStatus
import kotlinx.coroutines.delay
import tachiyomi.core.common.i18n.stringResource
import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR
import kotlin.time.Duration.Companion.seconds
@ -23,7 +22,6 @@ import kotlin.time.Duration.Companion.seconds
data class SyncFavoritesProgressProperties(
val title: String,
val text: String,
val canDismiss: Boolean,
val positiveButtonText: String? = null,
val positiveButton: (() -> Unit)? = null,
val negativeButtonText: String? = null,
@ -34,18 +32,23 @@ data class SyncFavoritesProgressProperties(
fun SyncFavoritesProgressDialog(
status: FavoritesSyncStatus,
setStatusIdle: () -> Unit,
openManga: (Manga) -> Unit,
openManga: (Long) -> Unit,
) {
val context = LocalContext.current
val properties by produceState<SyncFavoritesProgressProperties?>(initialValue = null, status) {
when (status) {
is FavoritesSyncStatus.BadLibraryState.MangaInMultipleCategories -> value = SyncFavoritesProgressProperties(
title = context.stringResource(SYMR.strings.favorites_sync_error),
text = context.stringResource(SYMR.strings.favorites_sync_bad_library_state, status.message),
canDismiss = false,
text = context.stringResource(
SYMR.strings.favorites_sync_bad_library_state,
context.stringResource(
SYMR.strings.favorites_sync_gallery_in_multiple_categories, status.mangaTitle,
status.categories.joinToString(),
),
),
positiveButtonText = context.stringResource(SYMR.strings.show_gallery),
positiveButton = {
openManga(status.manga)
openManga(status.mangaId)
setStatusIdle()
},
negativeButtonText = context.stringResource(MR.strings.action_ok),
@ -53,31 +56,122 @@ fun SyncFavoritesProgressDialog(
)
is FavoritesSyncStatus.CompleteWithErrors -> value = SyncFavoritesProgressProperties(
title = context.stringResource(SYMR.strings.favorites_sync_done_errors),
text = context.stringResource(SYMR.strings.favorites_sync_done_errors_message, status.message),
canDismiss = false,
positiveButtonText = context.stringResource(MR.strings.action_ok),
positiveButton = setStatusIdle,
text = context.stringResource(
SYMR.strings.favorites_sync_done_errors_message,
status.messages.joinToString(separator = "\n") {
when (it) {
is FavoritesSyncStatus.SyncError.GallerySyncError.GalleryAddFail ->
context.stringResource(SYMR.strings.favorites_sync_failed_to_add_to_local) +
context.stringResource(
SYMR.strings.favorites_sync_failed_to_add_to_local_error, it.title, it.reason,
)
is FavoritesSyncStatus.Error -> value = SyncFavoritesProgressProperties(
title = context.stringResource(SYMR.strings.favorites_sync_error),
text = context.stringResource(SYMR.strings.favorites_sync_error_string, status.message),
canDismiss = false,
is FavoritesSyncStatus.SyncError.GallerySyncError.InvalidGalleryFail ->
context.stringResource(SYMR.strings.favorites_sync_failed_to_add_to_local) +
context.stringResource(
SYMR.strings.favorites_sync_failed_to_add_to_local_unknown_type, it.title, it.url,
)
is FavoritesSyncStatus.SyncError.GallerySyncError.UnableToAddGalleryToRemote ->
context.stringResource(SYMR.strings.favorites_sync_unable_to_add_to_remote, it.title, it.gid)
FavoritesSyncStatus.SyncError.GallerySyncError.UnableToDeleteFromRemote ->
context.stringResource(SYMR.strings.favorites_sync_unable_to_delete)
}
}
),
positiveButtonText = context.stringResource(MR.strings.action_ok),
positiveButton = setStatusIdle,
)
is FavoritesSyncStatus.Idle -> value = null
is FavoritesSyncStatus.Initializing, is FavoritesSyncStatus.Processing -> {
is FavoritesSyncStatus.Initializing -> {
value = SyncFavoritesProgressProperties(
title = context.stringResource(SYMR.strings.favorites_syncing),
text = status.message,
canDismiss = false,
text = context.stringResource(SYMR.strings.favorites_sync_initializing),
)
if (status is FavoritesSyncStatus.Processing && status.title != null) {
delay(5.seconds)
value = SyncFavoritesProgressProperties(
}
is FavoritesSyncStatus.SyncError -> value = SyncFavoritesProgressProperties(
title = context.stringResource(SYMR.strings.favorites_sync_error),
text = context.stringResource(
SYMR.strings.favorites_sync_error_string,
when (status) {
FavoritesSyncStatus.SyncError.NotLoggedInSyncError -> context.stringResource(SYMR.strings.please_login)
FavoritesSyncStatus.SyncError.FailedToFetchFavorites ->
context.stringResource(SYMR.strings.favorites_sync_failed_to_featch)
is FavoritesSyncStatus.SyncError.UnknownSyncError ->
context.stringResource(SYMR.strings.favorites_sync_unknown_error, status.message)
is FavoritesSyncStatus.SyncError.GallerySyncError.GalleryAddFail ->
context.stringResource(SYMR.strings.favorites_sync_failed_to_add_to_local) +
context.stringResource(
SYMR.strings.favorites_sync_failed_to_add_to_local_error, status.title, status.reason,
)
is FavoritesSyncStatus.SyncError.GallerySyncError.InvalidGalleryFail ->
context.stringResource(SYMR.strings.favorites_sync_failed_to_add_to_local) +
context.stringResource(
SYMR.strings.favorites_sync_failed_to_add_to_local_unknown_type, status.title, status.url,
)
is FavoritesSyncStatus.SyncError.GallerySyncError.UnableToAddGalleryToRemote ->
context.stringResource(SYMR.strings.favorites_sync_unable_to_add_to_remote, status.title, status.gid)
FavoritesSyncStatus.SyncError.GallerySyncError.UnableToDeleteFromRemote ->
context.stringResource(SYMR.strings.favorites_sync_unable_to_delete)
}
),
positiveButtonText = context.stringResource(MR.strings.action_ok),
positiveButton = setStatusIdle,
)
is FavoritesSyncStatus.Processing -> {
val properties = SyncFavoritesProgressProperties(
title = context.stringResource(SYMR.strings.favorites_syncing),
text = status.delayedMessage ?: status.message,
canDismiss = false,
text = when (status) {
FavoritesSyncStatus.Processing.VerifyingLibrary ->
context.stringResource(SYMR.strings.favorites_sync_verifying_library)
FavoritesSyncStatus.Processing.DownloadingFavorites ->
context.stringResource(SYMR.strings.favorites_sync_downloading)
FavoritesSyncStatus.Processing.CalculatingRemoteChanges ->
context.stringResource(SYMR.strings.favorites_sync_calculating_remote_changes)
FavoritesSyncStatus.Processing.CalculatingLocalChanges ->
context.stringResource(SYMR.strings.favorites_sync_calculating_local_changes)
FavoritesSyncStatus.Processing.SyncingCategoryNames ->
context.stringResource(SYMR.strings.favorites_sync_syncing_category_names)
is FavoritesSyncStatus.Processing.RemovingRemoteGalleries ->
context.stringResource(SYMR.strings.favorites_sync_removing_galleries, status.galleryCount)
is FavoritesSyncStatus.Processing.AddingGalleryToRemote ->
if (status.isThrottling) {
context.stringResource(
SYMR.strings.favorites_sync_processing_throttle,
context.stringResource(SYMR.strings.favorites_sync_adding_to_remote, status.index, status.total)
)
} else {
context.stringResource(SYMR.strings.favorites_sync_adding_to_remote, status.index, status.total)
}
is FavoritesSyncStatus.Processing.RemovingGalleryFromLocal ->
context.stringResource(SYMR.strings.favorites_sync_remove_from_local, status.index, status.total)
is FavoritesSyncStatus.Processing.AddingGalleryToLocal ->
if (status.isThrottling) {
context.stringResource(
SYMR.strings.favorites_sync_processing_throttle,
context.stringResource(SYMR.strings.favorites_sync_add_to_local, status.index, status.total)
)
} else {
context.stringResource(SYMR.strings.favorites_sync_add_to_local, status.index, status.total)
}
FavoritesSyncStatus.Processing.CleaningUp ->
context.stringResource(SYMR.strings.favorites_sync_cleaning_up)
},
)
value = properties
if (
status is FavoritesSyncStatus.Processing.AddingGalleryToRemote ||
status is FavoritesSyncStatus.Processing.AddingGalleryToLocal
) {
delay(5.seconds)
value = properties.copy(
text = when (status) {
is FavoritesSyncStatus.Processing.AddingGalleryToRemote ->
properties.text + "\n\n" + status.title
is FavoritesSyncStatus.Processing.AddingGalleryToLocal ->
properties.text + "\n\n" + status.title
else -> properties.text
},
)
}
}
@ -112,8 +206,8 @@ fun SyncFavoritesProgressDialog(
}
},
properties = DialogProperties(
dismissOnClickOutside = dialog.canDismiss,
dismissOnBackPress = dialog.canDismiss,
dismissOnClickOutside = false,
dismissOnBackPress = false,
),
)
}

View File

@ -335,7 +335,7 @@ data object LibraryTab : Tab {
SyncFavoritesProgressDialog(
status = screenModel.favoritesSync.status.collectAsState().value,
setStatusIdle = { screenModel.favoritesSync.status.value = FavoritesSyncStatus.Idle(context) },
openManga = { navigator.push(MangaScreen(it.id)) },
openManga = { navigator.push(MangaScreen(it)) },
)
// SY <--

View File

@ -64,18 +64,18 @@ class FavoritesSyncHelper(val context: Context) {
?: EHentai(0, true, context)
}
private val storage = LocalFavoritesStorage()
private val storage by lazy { LocalFavoritesStorage() }
private val galleryAdder = GalleryAdder()
private val galleryAdder by lazy { GalleryAdder() }
private val throttleManager = EHentaiThrottleManager()
private val throttleManager by lazy { EHentaiThrottleManager() }
private var wifiLock: WifiManager.WifiLock? = null
private var wakeLock: PowerManager.WakeLock? = null
private val logger = xLog()
private val logger by lazy { xLog() }
val status: MutableStateFlow<FavoritesSyncStatus> = MutableStateFlow(FavoritesSyncStatus.Idle(context))
val status: MutableStateFlow<FavoritesSyncStatus> = MutableStateFlow(FavoritesSyncStatus.Idle)
@Synchronized
fun runSync(scope: CoroutineScope) {
@ -83,7 +83,7 @@ class FavoritesSyncHelper(val context: Context) {
return
}
status.value = FavoritesSyncStatus.Initializing(context)
status.value = FavoritesSyncStatus.Initializing
scope.launch(Dispatchers.IO) { beginSync() }
}
@ -91,13 +91,12 @@ class FavoritesSyncHelper(val context: Context) {
private suspend fun beginSync() {
// Check if logged in
if (!prefs.enableExhentai().get()) {
status.value = FavoritesSyncStatus.Error(context.stringResource(SYMR.strings.please_login))
status.value = FavoritesSyncStatus.SyncError.NotLoggedInSyncError
return
}
// Validate library state
status.value =
FavoritesSyncStatus.Processing(context.stringResource(SYMR.strings.favorites_sync_verifying_library))
status.value = FavoritesSyncStatus.Processing.VerifyingLibrary
val libraryManga = getLibraryManga.await()
val seenManga = HashSet<Long>(libraryManga.size)
libraryManga.forEach { (manga) ->
@ -106,7 +105,7 @@ class FavoritesSyncHelper(val context: Context) {
if (manga.id in seenManga) {
val inCategories = getCategories.await(manga.id)
status.value = FavoritesSyncStatus.BadLibraryState
.MangaInMultipleCategories(manga, inCategories, context)
.MangaInMultipleCategories(manga.id, manga.title, inCategories.map { it.name })
logger.w(context.stringResource(SYMR.strings.favorites_sync_gallery_multiple_categories_error, manga.id))
return
@ -117,17 +116,15 @@ class FavoritesSyncHelper(val context: Context) {
// Download remote favorites
val favorites = try {
status.value =
FavoritesSyncStatus.Processing(context.stringResource(SYMR.strings.favorites_sync_downloading))
status.value = FavoritesSyncStatus.Processing.DownloadingFavorites
exh.fetchFavorites()
} catch (e: Exception) {
status.value =
FavoritesSyncStatus.Error(context.stringResource(SYMR.strings.favorites_sync_failed_to_featch))
status.value = FavoritesSyncStatus.SyncError.FailedToFetchFavorites
logger.e(context.stringResource(SYMR.strings.favorites_sync_could_not_fetch), e)
return
}
val errorList = mutableListOf<String>()
val errorList = mutableListOf<FavoritesSyncStatus.SyncError.GallerySyncError>()
try {
// Take wake + wifi locks
@ -157,23 +154,17 @@ class FavoritesSyncHelper(val context: Context) {
// Do not update galleries while syncing favorites
EHentaiUpdateWorker.cancelBackground(context)
status.value = FavoritesSyncStatus.Processing(
context.stringResource(SYMR.strings.favorites_sync_calculating_remote_changes),
)
status.value = FavoritesSyncStatus.Processing.CalculatingRemoteChanges
val remoteChanges = storage.getChangedRemoteEntries(favorites.first)
val localChanges = if (prefs.exhReadOnlySync().get()) {
null // Do not build local changes if they are not going to be applied
} else {
status.value = FavoritesSyncStatus.Processing(
context.stringResource(SYMR.strings.favorites_sync_calculating_local_changes),
)
status.value = FavoritesSyncStatus.Processing.CalculatingLocalChanges
storage.getChangedDbEntries()
}
// Apply remote categories
status.value = FavoritesSyncStatus.Processing(
context.stringResource(SYMR.strings.favorites_sync_syncing_category_names),
)
status.value = FavoritesSyncStatus.Processing.SyncingCategoryNames
applyRemoteCategories(favorites.second)
// Apply change sets
@ -182,8 +173,7 @@ class FavoritesSyncHelper(val context: Context) {
applyChangeSetToRemote(errorList, localChanges)
}
status.value =
FavoritesSyncStatus.Processing(context.stringResource(SYMR.strings.favorites_sync_cleaning_up))
status.value = FavoritesSyncStatus.Processing.CleaningUp
storage.snapshotEntries()
withUIContext {
@ -194,9 +184,7 @@ class FavoritesSyncHelper(val context: Context) {
logger.w(context.stringResource(SYMR.strings.favorites_sync_ignoring_exception), e)
return
} catch (e: Exception) {
status.value = FavoritesSyncStatus.Error(
context.stringResource(SYMR.strings.favorites_sync_unknown_error, e.message.orEmpty()),
)
status.value = FavoritesSyncStatus.SyncError.UnknownSyncError(e.message.orEmpty())
logger.e(context.stringResource(SYMR.strings.favorites_sync_sync_error), e)
return
} finally {
@ -215,7 +203,7 @@ class FavoritesSyncHelper(val context: Context) {
}
if (errorList.isEmpty()) {
status.value = FavoritesSyncStatus.Idle(context)
status.value = FavoritesSyncStatus.Idle
} else {
status.value = FavoritesSyncStatus.CompleteWithErrors(errorList)
}
@ -249,7 +237,7 @@ class FavoritesSyncHelper(val context: Context) {
}
}
private suspend fun addGalleryRemote(errorList: MutableList<String>, gallery: FavoriteEntry) {
private suspend fun addGalleryRemote(errorList: MutableList<FavoritesSyncStatus.SyncError.GallerySyncError>, gallery: FavoriteEntry) {
val url = "${exh.baseUrl}/gallerypopups.php?gid=${gallery.gid}&t=${gallery.token}&act=addfav"
val request = POST(
@ -263,13 +251,16 @@ class FavoritesSyncHelper(val context: Context) {
)
if (!explicitlyRetryExhRequest(10, request)) {
val errorString = "Unable to add gallery to remote server: '${gallery.title}' (GID: ${gallery.gid})!"
val error = FavoritesSyncStatus.SyncError.GallerySyncError.UnableToAddGalleryToRemote(
gallery.title,
gallery.gid
)
if (prefs.exhLenientSync().get()) {
errorList += errorString
errorList += error
} else {
status.value = FavoritesSyncStatus.Error(errorString)
throw IgnoredException(errorString)
status.value = error
throw IgnoredException(error)
}
}
}
@ -293,12 +284,13 @@ class FavoritesSyncHelper(val context: Context) {
return success
}
private suspend fun applyChangeSetToRemote(errorList: MutableList<String>, changeSet: ChangeSet) {
private suspend fun applyChangeSetToRemote(
errorList: MutableList<FavoritesSyncStatus.SyncError.GallerySyncError>,
changeSet: ChangeSet,
) {
// Apply removals
if (changeSet.removed.isNotEmpty()) {
status.value = FavoritesSyncStatus.Processing(
context.stringResource(SYMR.strings.favorites_sync_removing_galleries, changeSet.removed.size),
)
status.value = FavoritesSyncStatus.Processing.RemovingRemoteGalleries(changeSet.removed.size)
val formBody = FormBody.Builder()
.add("ddact", "delete")
@ -315,13 +307,11 @@ class FavoritesSyncHelper(val context: Context) {
)
if (!explicitlyRetryExhRequest(10, request)) {
val errorString = context.stringResource(SYMR.strings.favorites_sync_unable_to_delete)
if (prefs.exhLenientSync().get()) {
errorList += errorString
errorList += FavoritesSyncStatus.SyncError.GallerySyncError.UnableToDeleteFromRemote
} else {
status.value = FavoritesSyncStatus.Error(errorString)
throw IgnoredException(errorString)
status.value = FavoritesSyncStatus.SyncError.GallerySyncError.UnableToDeleteFromRemote
throw IgnoredException(FavoritesSyncStatus.SyncError.GallerySyncError.UnableToDeleteFromRemote)
}
}
}
@ -329,10 +319,10 @@ class FavoritesSyncHelper(val context: Context) {
// Apply additions
throttleManager.resetThrottle()
changeSet.added.forEachIndexed { index, it ->
status.value = FavoritesSyncStatus.Processing(
message = context.stringResource(SYMR.strings.favorites_sync_adding_to_remote, index + 1, changeSet.added.size),
isThrottle = needWarnThrottle(),
context = context,
status.value = FavoritesSyncStatus.Processing.AddingGalleryToRemote(
index = index + 1,
total = changeSet.added.size,
isThrottling = needWarnThrottle(),
title = it.title,
)
@ -342,14 +332,17 @@ class FavoritesSyncHelper(val context: Context) {
}
}
private suspend fun applyChangeSetToLocal(errorList: MutableList<String>, changeSet: ChangeSet) {
private suspend fun applyChangeSetToLocal(
errorList: MutableList<FavoritesSyncStatus.SyncError.GallerySyncError>,
changeSet: ChangeSet,
) {
val removedManga = mutableListOf<Manga>()
// Apply removals
changeSet.removed.forEachIndexed { index, it ->
status.value = FavoritesSyncStatus.Processing(
context.stringResource(SYMR.strings.favorites_sync_remove_from_local, index + 1, changeSet.removed.size),
title = it.title,
status.value = FavoritesSyncStatus.Processing.RemovingGalleryFromLocal(
index = index + 1,
total = changeSet.removed.size,
)
val url = it.getUrl()
@ -379,10 +372,10 @@ class FavoritesSyncHelper(val context: Context) {
// Apply additions
throttleManager.resetThrottle()
changeSet.added.forEachIndexed { index, it ->
status.value = FavoritesSyncStatus.Processing(
message = context.stringResource(SYMR.strings.favorites_sync_add_to_local, index + 1, changeSet.added.size),
isThrottle = needWarnThrottle(),
context = context,
status.value = FavoritesSyncStatus.Processing.AddingGalleryToLocal(
index = index + 1,
total = changeSet.added.size,
isThrottling = needWarnThrottle(),
title = it.title,
)
@ -405,24 +398,23 @@ class FavoritesSyncHelper(val context: Context) {
return@forEachIndexed
}
val errorString = context.stringResource(SYMR.strings.favorites_sync_failed_to_add_to_local) +
when (result) {
is GalleryAddEvent.Fail.Error -> context.stringResource(
SYMR.strings.favorites_sync_failed_to_add_to_local_error, it.title, result.logMessage,
val error = when (result) {
is GalleryAddEvent.Fail.Error -> FavoritesSyncStatus.SyncError.GallerySyncError.GalleryAddFail(
it.title, result.logMessage,
)
is GalleryAddEvent.Fail.UnknownType -> context.stringResource(
SYMR.strings.favorites_sync_failed_to_add_to_local_unknown_type, it.title, result.galleryUrl,
is GalleryAddEvent.Fail.UnknownType -> FavoritesSyncStatus.SyncError.GallerySyncError.InvalidGalleryFail(
it.title, result.galleryUrl,
)
is GalleryAddEvent.Fail.UnknownSource -> context.stringResource(
SYMR.strings.favorites_sync_failed_to_add_to_local_unknown_type, it.title, result.galleryUrl,
is GalleryAddEvent.Fail.UnknownSource -> FavoritesSyncStatus.SyncError.GallerySyncError.InvalidGalleryFail(
it.title, result.galleryUrl,
)
}
if (prefs.exhLenientSync().get()) {
errorList += errorString
errorList += error
} else {
status.value = FavoritesSyncStatus.Error(errorString)
throw IgnoredException(errorString)
status.value = error
throw IgnoredException(error)
}
} else if (result is GalleryAddEvent.Success) {
insertedMangaCategories += categories[it.category].id to result.manga
@ -438,59 +430,74 @@ class FavoritesSyncHelper(val context: Context) {
private fun needWarnThrottle() =
throttleManager.throttleTime >= THROTTLE_WARN
class IgnoredException(message: String) : RuntimeException(message)
class IgnoredException(message: FavoritesSyncStatus.SyncError.GallerySyncError) : RuntimeException(message.toString())
companion object {
private val THROTTLE_WARN = 1.seconds
}
}
@Serializable
sealed class FavoritesSyncStatus {
abstract val message: String
data class Error(override val message: String) : FavoritesSyncStatus()
data class Idle(override val message: String) : FavoritesSyncStatus() {
constructor(context: Context) : this(context.stringResource(SYMR.strings.favorites_sync_waiting_for_start))
@Serializable
sealed class SyncError : FavoritesSyncStatus() {
@Serializable
data object NotLoggedInSyncError : SyncError()
@Serializable
data object FailedToFetchFavorites : SyncError()
@Serializable
data class UnknownSyncError(val message: String) : SyncError()
@Serializable
sealed class GallerySyncError : SyncError() {
@Serializable
data class UnableToAddGalleryToRemote(val title: String, val gid: String): GallerySyncError()
@Serializable
data object UnableToDeleteFromRemote : GallerySyncError()
@Serializable
data class GalleryAddFail(val title: String, val reason: String): GallerySyncError()
@Serializable
data class InvalidGalleryFail(val title: String, val url: String): GallerySyncError()
}
}
@Serializable
data object Idle : FavoritesSyncStatus()
@Serializable
sealed class BadLibraryState : FavoritesSyncStatus() {
@Serializable
data class MangaInMultipleCategories(
val manga: Manga,
val categories: List<Category>,
override val message: String,
) : BadLibraryState() {
constructor(manga: Manga, categories: List<Category>, context: Context) :
this(
manga = manga,
categories = categories,
message = context.stringResource(
SYMR.strings.favorites_sync_gallery_in_multiple_categories, manga.title,
categories.joinToString {
it.name
},
),
)
val mangaId: Long,
val mangaTitle: String,
val categories: List<String>,
) : BadLibraryState()
}
@Serializable
data object Initializing : FavoritesSyncStatus()
@Serializable
sealed class Processing : FavoritesSyncStatus() {
data object VerifyingLibrary : Processing()
data object DownloadingFavorites : Processing()
data object CalculatingRemoteChanges : Processing()
data object CalculatingLocalChanges : Processing()
data object SyncingCategoryNames : Processing()
data class RemovingRemoteGalleries(val galleryCount: Int) : Processing()
data class AddingGalleryToRemote(
val index: Int,
val total: Int,
val isThrottling: Boolean,
val title: String,
) : Processing()
data class RemovingGalleryFromLocal(
val index: Int,
val total: Int,
) : Processing()
data class AddingGalleryToLocal(
val index: Int,
val total: Int,
val isThrottling: Boolean,
val title: String,
) : Processing()
data object CleaningUp : Processing()
}
data class Initializing(override val message: String) : FavoritesSyncStatus() {
constructor(context: Context) : this(context.stringResource(SYMR.strings.favorites_sync_initializing))
}
data class Processing(
override val message: String,
val title: String? = null,
) : FavoritesSyncStatus() {
constructor(message: String, isThrottle: Boolean, context: Context, title: String?) :
this(
if (isThrottle) {
context.stringResource(SYMR.strings.favorites_sync_processing_throttle, message)
} else {
message
},
title,
)
val delayedMessage get() = if (title != null) this.message + "\n\n" + title else null
}
data class CompleteWithErrors(val messages: List<String>) : FavoritesSyncStatus() {
override val message: String = messages.joinToString("\n")
}
@Serializable
data class CompleteWithErrors(val messages: List<SyncError.GallerySyncError>) : FavoritesSyncStatus()
}

View File

@ -515,6 +515,7 @@
<string name="favorites_sync_removing_galleries">Removing %1$d galleries from remote server</string>
<string name="favorites_sync_unable_to_delete">Unable to delete galleries from the remote servers!</string>
<string name="favorites_sync_adding_to_remote">Adding gallery %1$d of %2$d to remote server</string>
<string name="favorites_sync_unable_to_add_to_remote">Unable to add gallery to remote server: '%1$s' (GID: %2$s)!</string>
<string name="favorites_sync_remove_from_local">Removing gallery %1$d of %2$d from local library</string>
<string name="favorites_sync_add_to_local">Adding gallery %1$d of %2$d to local library</string>
<string name="favorites_sync_remote_not_exist">Remote gallery does not exist, skipping: %1$s!</string>