Improve favorites sync statuses
This commit is contained in:
parent
3f8cce8a32
commit
76af3b59f0
@ -15,7 +15,6 @@ import androidx.compose.ui.window.DialogProperties
|
|||||||
import exh.favorites.FavoritesSyncStatus
|
import exh.favorites.FavoritesSyncStatus
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import tachiyomi.core.common.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
import tachiyomi.domain.manga.model.Manga
|
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.i18n.sy.SYMR
|
import tachiyomi.i18n.sy.SYMR
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
@ -23,7 +22,6 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
data class SyncFavoritesProgressProperties(
|
data class SyncFavoritesProgressProperties(
|
||||||
val title: String,
|
val title: String,
|
||||||
val text: String,
|
val text: String,
|
||||||
val canDismiss: Boolean,
|
|
||||||
val positiveButtonText: String? = null,
|
val positiveButtonText: String? = null,
|
||||||
val positiveButton: (() -> Unit)? = null,
|
val positiveButton: (() -> Unit)? = null,
|
||||||
val negativeButtonText: String? = null,
|
val negativeButtonText: String? = null,
|
||||||
@ -34,18 +32,23 @@ data class SyncFavoritesProgressProperties(
|
|||||||
fun SyncFavoritesProgressDialog(
|
fun SyncFavoritesProgressDialog(
|
||||||
status: FavoritesSyncStatus,
|
status: FavoritesSyncStatus,
|
||||||
setStatusIdle: () -> Unit,
|
setStatusIdle: () -> Unit,
|
||||||
openManga: (Manga) -> Unit,
|
openManga: (Long) -> Unit,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val properties by produceState<SyncFavoritesProgressProperties?>(initialValue = null, status) {
|
val properties by produceState<SyncFavoritesProgressProperties?>(initialValue = null, status) {
|
||||||
when (status) {
|
when (status) {
|
||||||
is FavoritesSyncStatus.BadLibraryState.MangaInMultipleCategories -> value = SyncFavoritesProgressProperties(
|
is FavoritesSyncStatus.BadLibraryState.MangaInMultipleCategories -> value = SyncFavoritesProgressProperties(
|
||||||
title = context.stringResource(SYMR.strings.favorites_sync_error),
|
title = context.stringResource(SYMR.strings.favorites_sync_error),
|
||||||
text = context.stringResource(SYMR.strings.favorites_sync_bad_library_state, status.message),
|
text = context.stringResource(
|
||||||
canDismiss = false,
|
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),
|
positiveButtonText = context.stringResource(SYMR.strings.show_gallery),
|
||||||
positiveButton = {
|
positiveButton = {
|
||||||
openManga(status.manga)
|
openManga(status.mangaId)
|
||||||
setStatusIdle()
|
setStatusIdle()
|
||||||
},
|
},
|
||||||
negativeButtonText = context.stringResource(MR.strings.action_ok),
|
negativeButtonText = context.stringResource(MR.strings.action_ok),
|
||||||
@ -53,31 +56,122 @@ fun SyncFavoritesProgressDialog(
|
|||||||
)
|
)
|
||||||
is FavoritesSyncStatus.CompleteWithErrors -> value = SyncFavoritesProgressProperties(
|
is FavoritesSyncStatus.CompleteWithErrors -> value = SyncFavoritesProgressProperties(
|
||||||
title = context.stringResource(SYMR.strings.favorites_sync_done_errors),
|
title = context.stringResource(SYMR.strings.favorites_sync_done_errors),
|
||||||
text = context.stringResource(SYMR.strings.favorites_sync_done_errors_message, status.message),
|
text = context.stringResource(
|
||||||
canDismiss = false,
|
SYMR.strings.favorites_sync_done_errors_message,
|
||||||
positiveButtonText = context.stringResource(MR.strings.action_ok),
|
status.messages.joinToString(separator = "\n") {
|
||||||
positiveButton = setStatusIdle,
|
when (it) {
|
||||||
)
|
is FavoritesSyncStatus.SyncError.GallerySyncError.GalleryAddFail ->
|
||||||
is FavoritesSyncStatus.Error -> value = SyncFavoritesProgressProperties(
|
context.stringResource(SYMR.strings.favorites_sync_failed_to_add_to_local) +
|
||||||
title = context.stringResource(SYMR.strings.favorites_sync_error),
|
context.stringResource(
|
||||||
text = context.stringResource(SYMR.strings.favorites_sync_error_string, status.message),
|
SYMR.strings.favorites_sync_failed_to_add_to_local_error, it.title, it.reason,
|
||||||
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),
|
positiveButtonText = context.stringResource(MR.strings.action_ok),
|
||||||
positiveButton = setStatusIdle,
|
positiveButton = setStatusIdle,
|
||||||
)
|
)
|
||||||
is FavoritesSyncStatus.Idle -> value = null
|
is FavoritesSyncStatus.Idle -> value = null
|
||||||
is FavoritesSyncStatus.Initializing, is FavoritesSyncStatus.Processing -> {
|
is FavoritesSyncStatus.Initializing -> {
|
||||||
value = SyncFavoritesProgressProperties(
|
value = SyncFavoritesProgressProperties(
|
||||||
title = context.stringResource(SYMR.strings.favorites_syncing),
|
title = context.stringResource(SYMR.strings.favorites_syncing),
|
||||||
text = status.message,
|
text = context.stringResource(SYMR.strings.favorites_sync_initializing),
|
||||||
canDismiss = false,
|
|
||||||
)
|
)
|
||||||
if (status is FavoritesSyncStatus.Processing && status.title != null) {
|
}
|
||||||
|
|
||||||
|
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 = 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)
|
delay(5.seconds)
|
||||||
value = SyncFavoritesProgressProperties(
|
value = properties.copy(
|
||||||
title = context.stringResource(SYMR.strings.favorites_syncing),
|
text = when (status) {
|
||||||
text = status.delayedMessage ?: status.message,
|
is FavoritesSyncStatus.Processing.AddingGalleryToRemote ->
|
||||||
canDismiss = false,
|
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(
|
properties = DialogProperties(
|
||||||
dismissOnClickOutside = dialog.canDismiss,
|
dismissOnClickOutside = false,
|
||||||
dismissOnBackPress = dialog.canDismiss,
|
dismissOnBackPress = false,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -335,7 +335,7 @@ data object LibraryTab : Tab {
|
|||||||
SyncFavoritesProgressDialog(
|
SyncFavoritesProgressDialog(
|
||||||
status = screenModel.favoritesSync.status.collectAsState().value,
|
status = screenModel.favoritesSync.status.collectAsState().value,
|
||||||
setStatusIdle = { screenModel.favoritesSync.status.value = FavoritesSyncStatus.Idle(context) },
|
setStatusIdle = { screenModel.favoritesSync.status.value = FavoritesSyncStatus.Idle(context) },
|
||||||
openManga = { navigator.push(MangaScreen(it.id)) },
|
openManga = { navigator.push(MangaScreen(it)) },
|
||||||
)
|
)
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
|
@ -64,18 +64,18 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
?: EHentai(0, true, 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 wifiLock: WifiManager.WifiLock? = null
|
||||||
private var wakeLock: PowerManager.WakeLock? = 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
|
@Synchronized
|
||||||
fun runSync(scope: CoroutineScope) {
|
fun runSync(scope: CoroutineScope) {
|
||||||
@ -83,7 +83,7 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
status.value = FavoritesSyncStatus.Initializing(context)
|
status.value = FavoritesSyncStatus.Initializing
|
||||||
|
|
||||||
scope.launch(Dispatchers.IO) { beginSync() }
|
scope.launch(Dispatchers.IO) { beginSync() }
|
||||||
}
|
}
|
||||||
@ -91,13 +91,12 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
private suspend fun beginSync() {
|
private suspend fun beginSync() {
|
||||||
// Check if logged in
|
// Check if logged in
|
||||||
if (!prefs.enableExhentai().get()) {
|
if (!prefs.enableExhentai().get()) {
|
||||||
status.value = FavoritesSyncStatus.Error(context.stringResource(SYMR.strings.please_login))
|
status.value = FavoritesSyncStatus.SyncError.NotLoggedInSyncError
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate library state
|
// Validate library state
|
||||||
status.value =
|
status.value = FavoritesSyncStatus.Processing.VerifyingLibrary
|
||||||
FavoritesSyncStatus.Processing(context.stringResource(SYMR.strings.favorites_sync_verifying_library))
|
|
||||||
val libraryManga = getLibraryManga.await()
|
val libraryManga = getLibraryManga.await()
|
||||||
val seenManga = HashSet<Long>(libraryManga.size)
|
val seenManga = HashSet<Long>(libraryManga.size)
|
||||||
libraryManga.forEach { (manga) ->
|
libraryManga.forEach { (manga) ->
|
||||||
@ -106,7 +105,7 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
if (manga.id in seenManga) {
|
if (manga.id in seenManga) {
|
||||||
val inCategories = getCategories.await(manga.id)
|
val inCategories = getCategories.await(manga.id)
|
||||||
status.value = FavoritesSyncStatus.BadLibraryState
|
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))
|
logger.w(context.stringResource(SYMR.strings.favorites_sync_gallery_multiple_categories_error, manga.id))
|
||||||
return
|
return
|
||||||
@ -117,17 +116,15 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
|
|
||||||
// Download remote favorites
|
// Download remote favorites
|
||||||
val favorites = try {
|
val favorites = try {
|
||||||
status.value =
|
status.value = FavoritesSyncStatus.Processing.DownloadingFavorites
|
||||||
FavoritesSyncStatus.Processing(context.stringResource(SYMR.strings.favorites_sync_downloading))
|
|
||||||
exh.fetchFavorites()
|
exh.fetchFavorites()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
status.value =
|
status.value = FavoritesSyncStatus.SyncError.FailedToFetchFavorites
|
||||||
FavoritesSyncStatus.Error(context.stringResource(SYMR.strings.favorites_sync_failed_to_featch))
|
|
||||||
logger.e(context.stringResource(SYMR.strings.favorites_sync_could_not_fetch), e)
|
logger.e(context.stringResource(SYMR.strings.favorites_sync_could_not_fetch), e)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val errorList = mutableListOf<String>()
|
val errorList = mutableListOf<FavoritesSyncStatus.SyncError.GallerySyncError>()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Take wake + wifi locks
|
// Take wake + wifi locks
|
||||||
@ -157,23 +154,17 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
// Do not update galleries while syncing favorites
|
// Do not update galleries while syncing favorites
|
||||||
EHentaiUpdateWorker.cancelBackground(context)
|
EHentaiUpdateWorker.cancelBackground(context)
|
||||||
|
|
||||||
status.value = FavoritesSyncStatus.Processing(
|
status.value = FavoritesSyncStatus.Processing.CalculatingRemoteChanges
|
||||||
context.stringResource(SYMR.strings.favorites_sync_calculating_remote_changes),
|
|
||||||
)
|
|
||||||
val remoteChanges = storage.getChangedRemoteEntries(favorites.first)
|
val remoteChanges = storage.getChangedRemoteEntries(favorites.first)
|
||||||
val localChanges = if (prefs.exhReadOnlySync().get()) {
|
val localChanges = if (prefs.exhReadOnlySync().get()) {
|
||||||
null // Do not build local changes if they are not going to be applied
|
null // Do not build local changes if they are not going to be applied
|
||||||
} else {
|
} else {
|
||||||
status.value = FavoritesSyncStatus.Processing(
|
status.value = FavoritesSyncStatus.Processing.CalculatingLocalChanges
|
||||||
context.stringResource(SYMR.strings.favorites_sync_calculating_local_changes),
|
|
||||||
)
|
|
||||||
storage.getChangedDbEntries()
|
storage.getChangedDbEntries()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply remote categories
|
// Apply remote categories
|
||||||
status.value = FavoritesSyncStatus.Processing(
|
status.value = FavoritesSyncStatus.Processing.SyncingCategoryNames
|
||||||
context.stringResource(SYMR.strings.favorites_sync_syncing_category_names),
|
|
||||||
)
|
|
||||||
applyRemoteCategories(favorites.second)
|
applyRemoteCategories(favorites.second)
|
||||||
|
|
||||||
// Apply change sets
|
// Apply change sets
|
||||||
@ -182,8 +173,7 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
applyChangeSetToRemote(errorList, localChanges)
|
applyChangeSetToRemote(errorList, localChanges)
|
||||||
}
|
}
|
||||||
|
|
||||||
status.value =
|
status.value = FavoritesSyncStatus.Processing.CleaningUp
|
||||||
FavoritesSyncStatus.Processing(context.stringResource(SYMR.strings.favorites_sync_cleaning_up))
|
|
||||||
storage.snapshotEntries()
|
storage.snapshotEntries()
|
||||||
|
|
||||||
withUIContext {
|
withUIContext {
|
||||||
@ -194,9 +184,7 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
logger.w(context.stringResource(SYMR.strings.favorites_sync_ignoring_exception), e)
|
logger.w(context.stringResource(SYMR.strings.favorites_sync_ignoring_exception), e)
|
||||||
return
|
return
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
status.value = FavoritesSyncStatus.Error(
|
status.value = FavoritesSyncStatus.SyncError.UnknownSyncError(e.message.orEmpty())
|
||||||
context.stringResource(SYMR.strings.favorites_sync_unknown_error, e.message.orEmpty()),
|
|
||||||
)
|
|
||||||
logger.e(context.stringResource(SYMR.strings.favorites_sync_sync_error), e)
|
logger.e(context.stringResource(SYMR.strings.favorites_sync_sync_error), e)
|
||||||
return
|
return
|
||||||
} finally {
|
} finally {
|
||||||
@ -215,7 +203,7 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (errorList.isEmpty()) {
|
if (errorList.isEmpty()) {
|
||||||
status.value = FavoritesSyncStatus.Idle(context)
|
status.value = FavoritesSyncStatus.Idle
|
||||||
} else {
|
} else {
|
||||||
status.value = FavoritesSyncStatus.CompleteWithErrors(errorList)
|
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 url = "${exh.baseUrl}/gallerypopups.php?gid=${gallery.gid}&t=${gallery.token}&act=addfav"
|
||||||
|
|
||||||
val request = POST(
|
val request = POST(
|
||||||
@ -263,13 +251,16 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!explicitlyRetryExhRequest(10, request)) {
|
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()) {
|
if (prefs.exhLenientSync().get()) {
|
||||||
errorList += errorString
|
errorList += error
|
||||||
} else {
|
} else {
|
||||||
status.value = FavoritesSyncStatus.Error(errorString)
|
status.value = error
|
||||||
throw IgnoredException(errorString)
|
throw IgnoredException(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,12 +284,13 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun applyChangeSetToRemote(errorList: MutableList<String>, changeSet: ChangeSet) {
|
private suspend fun applyChangeSetToRemote(
|
||||||
|
errorList: MutableList<FavoritesSyncStatus.SyncError.GallerySyncError>,
|
||||||
|
changeSet: ChangeSet,
|
||||||
|
) {
|
||||||
// Apply removals
|
// Apply removals
|
||||||
if (changeSet.removed.isNotEmpty()) {
|
if (changeSet.removed.isNotEmpty()) {
|
||||||
status.value = FavoritesSyncStatus.Processing(
|
status.value = FavoritesSyncStatus.Processing.RemovingRemoteGalleries(changeSet.removed.size)
|
||||||
context.stringResource(SYMR.strings.favorites_sync_removing_galleries, changeSet.removed.size),
|
|
||||||
)
|
|
||||||
|
|
||||||
val formBody = FormBody.Builder()
|
val formBody = FormBody.Builder()
|
||||||
.add("ddact", "delete")
|
.add("ddact", "delete")
|
||||||
@ -315,13 +307,11 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!explicitlyRetryExhRequest(10, request)) {
|
if (!explicitlyRetryExhRequest(10, request)) {
|
||||||
val errorString = context.stringResource(SYMR.strings.favorites_sync_unable_to_delete)
|
|
||||||
|
|
||||||
if (prefs.exhLenientSync().get()) {
|
if (prefs.exhLenientSync().get()) {
|
||||||
errorList += errorString
|
errorList += FavoritesSyncStatus.SyncError.GallerySyncError.UnableToDeleteFromRemote
|
||||||
} else {
|
} else {
|
||||||
status.value = FavoritesSyncStatus.Error(errorString)
|
status.value = FavoritesSyncStatus.SyncError.GallerySyncError.UnableToDeleteFromRemote
|
||||||
throw IgnoredException(errorString)
|
throw IgnoredException(FavoritesSyncStatus.SyncError.GallerySyncError.UnableToDeleteFromRemote)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -329,10 +319,10 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
// Apply additions
|
// Apply additions
|
||||||
throttleManager.resetThrottle()
|
throttleManager.resetThrottle()
|
||||||
changeSet.added.forEachIndexed { index, it ->
|
changeSet.added.forEachIndexed { index, it ->
|
||||||
status.value = FavoritesSyncStatus.Processing(
|
status.value = FavoritesSyncStatus.Processing.AddingGalleryToRemote(
|
||||||
message = context.stringResource(SYMR.strings.favorites_sync_adding_to_remote, index + 1, changeSet.added.size),
|
index = index + 1,
|
||||||
isThrottle = needWarnThrottle(),
|
total = changeSet.added.size,
|
||||||
context = context,
|
isThrottling = needWarnThrottle(),
|
||||||
title = it.title,
|
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>()
|
val removedManga = mutableListOf<Manga>()
|
||||||
|
|
||||||
// Apply removals
|
// Apply removals
|
||||||
changeSet.removed.forEachIndexed { index, it ->
|
changeSet.removed.forEachIndexed { index, it ->
|
||||||
status.value = FavoritesSyncStatus.Processing(
|
status.value = FavoritesSyncStatus.Processing.RemovingGalleryFromLocal(
|
||||||
context.stringResource(SYMR.strings.favorites_sync_remove_from_local, index + 1, changeSet.removed.size),
|
index = index + 1,
|
||||||
title = it.title,
|
total = changeSet.removed.size,
|
||||||
)
|
)
|
||||||
val url = it.getUrl()
|
val url = it.getUrl()
|
||||||
|
|
||||||
@ -379,10 +372,10 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
// Apply additions
|
// Apply additions
|
||||||
throttleManager.resetThrottle()
|
throttleManager.resetThrottle()
|
||||||
changeSet.added.forEachIndexed { index, it ->
|
changeSet.added.forEachIndexed { index, it ->
|
||||||
status.value = FavoritesSyncStatus.Processing(
|
status.value = FavoritesSyncStatus.Processing.AddingGalleryToLocal(
|
||||||
message = context.stringResource(SYMR.strings.favorites_sync_add_to_local, index + 1, changeSet.added.size),
|
index = index + 1,
|
||||||
isThrottle = needWarnThrottle(),
|
total = changeSet.added.size,
|
||||||
context = context,
|
isThrottling = needWarnThrottle(),
|
||||||
title = it.title,
|
title = it.title,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -405,24 +398,23 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
return@forEachIndexed
|
return@forEachIndexed
|
||||||
}
|
}
|
||||||
|
|
||||||
val errorString = context.stringResource(SYMR.strings.favorites_sync_failed_to_add_to_local) +
|
val error = when (result) {
|
||||||
when (result) {
|
is GalleryAddEvent.Fail.Error -> FavoritesSyncStatus.SyncError.GallerySyncError.GalleryAddFail(
|
||||||
is GalleryAddEvent.Fail.Error -> context.stringResource(
|
it.title, result.logMessage,
|
||||||
SYMR.strings.favorites_sync_failed_to_add_to_local_error, it.title, result.logMessage,
|
)
|
||||||
)
|
is GalleryAddEvent.Fail.UnknownType -> FavoritesSyncStatus.SyncError.GallerySyncError.InvalidGalleryFail(
|
||||||
is GalleryAddEvent.Fail.UnknownType -> context.stringResource(
|
it.title, result.galleryUrl,
|
||||||
SYMR.strings.favorites_sync_failed_to_add_to_local_unknown_type, it.title, result.galleryUrl,
|
)
|
||||||
)
|
is GalleryAddEvent.Fail.UnknownSource -> FavoritesSyncStatus.SyncError.GallerySyncError.InvalidGalleryFail(
|
||||||
is GalleryAddEvent.Fail.UnknownSource -> context.stringResource(
|
it.title, result.galleryUrl,
|
||||||
SYMR.strings.favorites_sync_failed_to_add_to_local_unknown_type, it.title, result.galleryUrl,
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (prefs.exhLenientSync().get()) {
|
if (prefs.exhLenientSync().get()) {
|
||||||
errorList += errorString
|
errorList += error
|
||||||
} else {
|
} else {
|
||||||
status.value = FavoritesSyncStatus.Error(errorString)
|
status.value = error
|
||||||
throw IgnoredException(errorString)
|
throw IgnoredException(error)
|
||||||
}
|
}
|
||||||
} else if (result is GalleryAddEvent.Success) {
|
} else if (result is GalleryAddEvent.Success) {
|
||||||
insertedMangaCategories += categories[it.category].id to result.manga
|
insertedMangaCategories += categories[it.category].id to result.manga
|
||||||
@ -438,59 +430,74 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
private fun needWarnThrottle() =
|
private fun needWarnThrottle() =
|
||||||
throttleManager.throttleTime >= THROTTLE_WARN
|
throttleManager.throttleTime >= THROTTLE_WARN
|
||||||
|
|
||||||
class IgnoredException(message: String) : RuntimeException(message)
|
class IgnoredException(message: FavoritesSyncStatus.SyncError.GallerySyncError) : RuntimeException(message.toString())
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val THROTTLE_WARN = 1.seconds
|
private val THROTTLE_WARN = 1.seconds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
sealed class FavoritesSyncStatus {
|
sealed class FavoritesSyncStatus {
|
||||||
abstract val message: String
|
@Serializable
|
||||||
|
sealed class SyncError : FavoritesSyncStatus() {
|
||||||
data class Error(override val message: String) : FavoritesSyncStatus()
|
@Serializable
|
||||||
data class Idle(override val message: String) : FavoritesSyncStatus() {
|
data object NotLoggedInSyncError : SyncError()
|
||||||
constructor(context: Context) : this(context.stringResource(SYMR.strings.favorites_sync_waiting_for_start))
|
@Serializable
|
||||||
}
|
data object FailedToFetchFavorites : SyncError()
|
||||||
sealed class BadLibraryState : FavoritesSyncStatus() {
|
@Serializable
|
||||||
data class MangaInMultipleCategories(
|
data class UnknownSyncError(val message: String) : SyncError()
|
||||||
val manga: Manga,
|
@Serializable
|
||||||
val categories: List<Category>,
|
sealed class GallerySyncError : SyncError() {
|
||||||
override val message: String,
|
@Serializable
|
||||||
) : BadLibraryState() {
|
data class UnableToAddGalleryToRemote(val title: String, val gid: String): GallerySyncError()
|
||||||
constructor(manga: Manga, categories: List<Category>, context: Context) :
|
@Serializable
|
||||||
this(
|
data object UnableToDeleteFromRemote : GallerySyncError()
|
||||||
manga = manga,
|
@Serializable
|
||||||
categories = categories,
|
data class GalleryAddFail(val title: String, val reason: String): GallerySyncError()
|
||||||
message = context.stringResource(
|
@Serializable
|
||||||
SYMR.strings.favorites_sync_gallery_in_multiple_categories, manga.title,
|
data class InvalidGalleryFail(val title: String, val url: String): GallerySyncError()
|
||||||
categories.joinToString {
|
|
||||||
it.name
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data class Initializing(override val message: String) : FavoritesSyncStatus() {
|
@Serializable
|
||||||
constructor(context: Context) : this(context.stringResource(SYMR.strings.favorites_sync_initializing))
|
data object Idle : FavoritesSyncStatus()
|
||||||
|
@Serializable
|
||||||
|
sealed class BadLibraryState : FavoritesSyncStatus() {
|
||||||
|
@Serializable
|
||||||
|
data class MangaInMultipleCategories(
|
||||||
|
val mangaId: Long,
|
||||||
|
val mangaTitle: String,
|
||||||
|
val categories: List<String>,
|
||||||
|
) : BadLibraryState()
|
||||||
}
|
}
|
||||||
data class Processing(
|
@Serializable
|
||||||
override val message: String,
|
data object Initializing : FavoritesSyncStatus()
|
||||||
val title: String? = null,
|
@Serializable
|
||||||
) : FavoritesSyncStatus() {
|
sealed class Processing : FavoritesSyncStatus() {
|
||||||
constructor(message: String, isThrottle: Boolean, context: Context, title: String?) :
|
data object VerifyingLibrary : Processing()
|
||||||
this(
|
data object DownloadingFavorites : Processing()
|
||||||
if (isThrottle) {
|
data object CalculatingRemoteChanges : Processing()
|
||||||
context.stringResource(SYMR.strings.favorites_sync_processing_throttle, message)
|
data object CalculatingLocalChanges : Processing()
|
||||||
} else {
|
data object SyncingCategoryNames : Processing()
|
||||||
message
|
data class RemovingRemoteGalleries(val galleryCount: Int) : Processing()
|
||||||
},
|
data class AddingGalleryToRemote(
|
||||||
title,
|
val index: Int,
|
||||||
)
|
val total: Int,
|
||||||
|
val isThrottling: Boolean,
|
||||||
val delayedMessage get() = if (title != null) this.message + "\n\n" + title else null
|
val title: String,
|
||||||
}
|
) : Processing()
|
||||||
data class CompleteWithErrors(val messages: List<String>) : FavoritesSyncStatus() {
|
data class RemovingGalleryFromLocal(
|
||||||
override val message: String = messages.joinToString("\n")
|
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()
|
||||||
}
|
}
|
||||||
|
@Serializable
|
||||||
|
data class CompleteWithErrors(val messages: List<SyncError.GallerySyncError>) : FavoritesSyncStatus()
|
||||||
}
|
}
|
||||||
|
@ -515,6 +515,7 @@
|
|||||||
<string name="favorites_sync_removing_galleries">Removing %1$d galleries from remote server</string>
|
<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_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_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_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_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>
|
<string name="favorites_sync_remote_not_exist">Remote gallery does not exist, skipping: %1$s!</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user