Compare commits

..

519 Commits

Author SHA1 Message Date
Jobobby04 509ace1a38 Revert "Reapply "Fix thread starvation caused by not yielding or using an inappropriate thread pool (#2955)""
This reverts commit 9c01119d24.

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
2026-04-08 15:02:37 -04:00
Jobobby04 170358f88e Revert "Reapply "Fix cache invalidation isn't done at startup (#2970)""
This reverts commit c17e9573b7.
2026-04-08 14:52:15 -04:00
Jobobby04 5c14879c12 Fix build 2026-04-06 16:25:25 -04:00
ArthurKun 1682962703 Add gradle.properties to build-logic synced with root gradle.properties (#3159)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
2026-04-06 16:22:14 -04:00
Mend Renovate a1daf53188 Update dependency com.gradleup.tapmoc:tapmoc-gradle-plugin to v0.4.1 (#3173) 2026-04-06 16:03:22 -04:00
Mend Renovate b0bdee8c72 Update dependency io.kotest:kotest-assertions-core to v6.1.10 (#3172) 2026-04-06 16:03:16 -04:00
MajorTanya 72c091d317 Add informative error for unapproved MAL titles (#3155)
MAL has a concept of "titles waiting for approval". These titles
cannot be added to user lists, but they do show up on the website and
crucially, in search results.

However, trying to add such an "unapproved" title will return a 400
error response with the error "invalid_content".

Previously, the awaitSuccess() call would mean the generic "HTTP 400"
toast would be shown. Now, a dedicated informative error message is
shown instead.
# Conflicts:
#	CHANGELOG.md
2026-04-06 16:02:51 -04:00
Mend Renovate 1cc796a62e Update dependency androidx.compose:compose-bom-beta to v2026.03.01 (#3147) 2026-04-06 16:02:30 -04:00
Mend Renovate cd40c0fb32 Update dependency androidx.work:work-runtime to v2.11.2 (#3146) 2026-04-06 16:02:15 -04:00
Christos 88edd18d02 Use plural forms for update error notification (#3090)
* Use plural forms for update error notification

Replace the hardcoded 'X update(s) failed' string with Android
plural resources so the notification correctly shows '1 update failed'
vs '2 updates failed', enabling proper localization.

Fixes #3051

* Address review: rename plural key and remove comment

Rename plural key to notification_update_errors to avoid potential
conflicts with existing translated string keys. Remove leftover
comment from strings.xml.

* Revert key rename per review
2026-04-06 16:02:10 -04:00
Mend Renovate ba9d010f78 Update dependency io.kotest:kotest-assertions-core to v6.1.9 (#3137) 2026-04-06 16:02:02 -04:00
Mend Renovate 1ed375968e Update dependency io.kotest:kotest-assertions-core to v6.1.8 (#3132) 2026-04-06 16:01:55 -04:00
MajorTanya 9e525515ea Address Gradle warning about Task.project (#3118)
Apparently, this is all that's needed to replace the forbidden
`Task.project` accessor, which would be an error in Gradle v10 (it's
been deprecated for a while)

This will also allow us to use the Configuration Cache if we wanted
to.
2026-04-06 16:01:47 -04:00
AntsyLich bd7201cfb9 Fix app crash on startup on some Android TV
Why do you guys even exist

# Conflicts:
#	CHANGELOG.md
2026-04-06 16:01:40 -04:00
AntsyLich d9ca2b69e8 Fix occasional crash when mass installing/uninstalling extension using PackageManager
# Conflicts:
#	CHANGELOG.md
2026-04-06 16:01:20 -04:00
AntsyLich 925fb118af Potentially fix 'database is locked' crash
# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt
2026-04-06 15:59:05 -04:00
AntsyLich 4552221020 Fix wrong exception being caught after 8c480c6355 migration
`android.database.sqlite.SQLiteException` instead of `android.database.SQLException`

# Conflicts:
#	CHANGELOG.md
2026-04-06 15:54:09 -04:00
MajorTanya 24b66b7030 Address CancellableContinuation.resume deprecation (#3115)
According to the source docs, this has been a warning since
kotlinx.coroutines 1.9.0.
2026-04-06 15:52:45 -04:00
Mend Renovate 58decd076c Update Gradle to v9 (#2333) 2026-04-06 15:50:18 -04:00
AntsyLich a7d93ae751 Cleanup and rework build logic (#3113)
# Conflicts:
#	app/build.gradle.kts
#	app/shortcuts.xml
#	app/src/main/res/xml/shortcuts.xml
#	app/src/main/shortcuts.xml
#	build.gradle.kts
#	buildSrc/src/main/kotlin/mihon/buildlogic/AndroidConfig.kt
#	core/archive/build.gradle.kts
#	core/common/build.gradle.kts
#	gradle/build-logic/src/main/kotlin/mihon/gradle/BuildConfig.kt
#	settings.gradle.kts
#	source-api/build.gradle.kts
#	source-local/build.gradle.kts
#	telemetry/build.gradle.kts
2026-04-06 15:50:09 -04:00
Weblate (bot) d225b7c586 Translations update from Hosted Weblate (#3003)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/be/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/fi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/be/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ko/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/lv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ta/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

Co-authored-by: A <ogloppi@mailbox.org>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: Kehribar <103407696+dpentx@users.noreply.github.com>
Co-authored-by: Lucas Correia <anicetoclucas@gmail.com>
Co-authored-by: ScratchBuild <foobarbuzz@gmail.com>
Co-authored-by: Siebrenvde <siebren@siebrenvde.dev>
Co-authored-by: lilp <felipegabriel.avila6@gmail.com>
Co-authored-by: nadevko <ormak@protonmail.com>
Co-authored-by: தமிழ்நேரம் <tamilneram247@gmail.com>
Co-authored-by: ℂ𝕠𝕠𝕠𝕝 (𝕘𝕚𝕥𝕙𝕦𝕓.𝕔𝕠𝕞/ℂ𝕠𝕠𝕠𝕝) <coool@mail.lv>
Co-authored-by: 안세훈 <on9686@gmail.com>
# Conflicts:
#	i18n/src/commonMain/moko-resources/be/strings.xml
2026-04-06 15:27:10 -04:00
AntsyLich 4c51d01236 Merge and cleanup version catalogs (#3103)
# Conflicts:
#	app/build.gradle.kts
#	core/archive/build.gradle.kts
#	gradle/libs.versions.toml
#	i18n/build.gradle.kts
#	settings.gradle.kts
#	source-api/build.gradle.kts
2026-04-06 15:26:05 -04:00
Mend Renovate 4d46e84f54 Update dependency com.google.firebase:firebase-bom to v34.11.0 (#3094) 2026-04-06 15:06:15 -04:00
AntsyLich af51607053 Replace preference getter functions with properties (#3091)
# Conflicts:
#	app/src/main/java/eu/kanade/domain/source/interactor/GetEnabledSources.kt
#	app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt
#	app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt
#	app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt
#	app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
#	app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt
#	app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt
#	app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt
#	app/src/main/java/eu/kanade/tachiyomi/App.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt
#	app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt
#	app/src/main/java/eu/kanade/tachiyomi/source/SourceExtensions.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/SecureActivityDelegate.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerConfig.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt
#	app/src/main/java/mihon/feature/migration/list/MigrationListScreenModel.kt
#	core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt
#	domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt
2026-04-06 15:06:05 -04:00
Leodyver Semilla 1c8e6dcd6f Fix extension actions disappearing after installing and uninstalling in same session (#3049)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
# Conflicts:
#	CHANGELOG.md
2026-04-06 14:20:49 -04:00
AntsyLich 7a398dabba Make retry in reader redownload image (#3089)
# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt
2026-04-06 14:18:57 -04:00
Leodyver Semilla b7fcf7ccda Fix WebView JavaScript dialogs popup after screen is closed (#3041)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
# Conflicts:
#	CHANGELOG.md
2026-04-06 14:13:39 -04:00
Leodyver Semilla acbda604cc MangaUpdates API content-type heade (#3021)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
# Conflicts:
#	CHANGELOG.md
2026-04-06 14:13:18 -04:00
Leodyver Semilla 97f3dd3b25 Fix tracker-induced duplicate key crash in duplicate detection (#3040)
# Conflicts:
#	CHANGELOG.md
2026-04-06 14:12:59 -04:00
Mend Renovate 60758f89ed Update dependency com.diffplug.spotless:spotless-plugin-gradle to v8.4.0 (#3086) 2026-04-06 14:12:41 -04:00
KaiserBh e96895345e feat(sync): prevent deleted "ghost chapters" from reappearing during sync. (#1575)
* feat(sync): prevent deleted "ghost chapters" from reappearing during sync.

- Pass lastSyncTime down to mergeChapters in SyncService.kt.
- Apply timestamp-based tombstoning logic to chapter merging. When a chapter is missing from either the local or remote backup, its `lastModifiedAt` timestamp is checked against the device's last sync time.
- Ensure that chapters deleted on one device (or removed by a source) are recognized as deletions and dropped from the merged backup, rather than being erroneously restored as "new" chapters on subsequent syncs.

* chore: change timestamp to use duration-based calculations

* chore: spotless
2026-04-06 13:08:30 -04:00
MediocreLegion eec1236b8b fix(delegate): migrate NH to the v2 api (#1581)
* fix(delegate): migrate NH to the v2 api

* remove extra comment

* remove redundant data

* linting

* Code cleanup

---------

Co-authored-by: Jobobby04 <jobobby04@users.noreply.github.com>
2026-04-03 12:59:13 -04:00
Weblate (bot) ee1e783126 Translations update from Hosted Weblate (#1577)
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ja/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ko/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ru/
Translation: Mihon/TachiyomiSY

Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: ScratchBuild <foobarbuzz@gmail.com>
Co-authored-by: ZenVinny <atdenada@gmail.com>
Co-authored-by: 안세훈 <on9686@gmail.com>
2026-04-03 12:50:27 -04:00
renovate[bot] f3ab39cb1f Update dependency net.zetetic:sqlcipher-android to v4.14.1 (#1583)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-03 12:50:00 -04:00
KaiserBh ba75395648 refactor: improve sync merging categories (#1559)
* feat: Add versioning to categories

* feat: use random UID for categories.

For legacy and migration we should assign uid on insert, and modify existing one as well in the migration.

* feat: sync category metadata

Add version, uid and lastModifiedAt fields to Category model to allow syncing.

* chore: fix category merging logic

Improve the category merging logic by matching using UIDs first, with a fallback to matching by name for legacy remote categories.

Previously, categories were only matched by name, which could lead to incorrect merges if names were changed. This change ensures more accurate synchronization by prioritizing the unique identifier. Conflict resolution is now based on the `version` field, and logging has been added for better visibility into the merging process.

* refactor: prioritize UID when restoring categories

If a category with the same UID exists, update it instead of creating a new one. Fallback to matching by name if no UID match is found.

* chore: add isSyncing flag like before.

This make sure the version is consistent, and it's not accidentally appended by the trigger, if it does then one device will always be ahead, than previous, and they need to make multiple changes to increase the version.

* Apply suggestion from @jobobby04

Use SY specific numbers(601, 602 for now)

Co-authored-by: jobobby04 <jobobby04@users.noreply.github.com>

* chore: commit review, re-order.

* chore: surround changes in // SY --> // SY <--

* refactor: fallback to existing category UID if backup UID is 0 during restore.

when dealing with old backups (backups created before we added UIDs). In those old backups, backupCategory.uid defaults to 0.
If a user restored an old backup, it would match by name, and then overwrite the newly generated local UID with 0. This would break the synchronization.

* refactor: change to 6xx

* feat: improve sync reliability for categories and settings

- Refactor `mergeCategoriesLists` to correctly match categories by name when UID matching fails, ensuring better reconciliation across devices.
- Fix a bug in category merging where multiple categories with UID 0 (common for non-synced items) caused data loss.
- Update `SyncManager` to detect changes in categories, sources, preferences, saved searches, and extension repos, ensuring they synchronize even when the library favorites haven't changed.
- Convert `BackupCategory` and `BackupExtensionRepos` to data classes to support robust content-aware comparison during the sync process.
- Fix data loss in `mergeSourcesLists`, `mergePreferencesLists`, and `mergeSavedSearchesLists` by retaining local versions when conflicting with remote data.

* fix(sync): properly sync category deletions across devices

Previously, the sync system could not distinguish between a category that was deleted locally and a new category created on another device, causing deleted categories to be restored from the remote backup.

- Update `SyncService` to use `lastSyncTimestamp` to deduce if a missing local category was deleted (if modified before last sync) or newly created remotely (if modified after).
- Update `SyncManager` to explicitly delete local categories that are absent from the merged remote backup, propagating deletions to other devices.
- Fix `RestoreOptions` in `SyncManager` to respect the user's sync preferences instead of hardcoding `categories = true`.

* chore: change it to 6xx and not 600.

* chore: don't need to change this.

* chore: use kotlin time duration units

---------

Co-authored-by: jobobby04 <jobobby04@users.noreply.github.com>
2026-04-03 12:49:37 -04:00
Weblate (bot) fe0b14ab97 Translations update from Hosted Weblate (#1561)
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/be/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/ta/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/be/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ko/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ta/
Translation: Mihon/TachiyomiSY
Translation: Mihon/TachiyomiSY Plurals

Co-authored-by: Kehribar <103407696+dpentx@users.noreply.github.com>
Co-authored-by: Lucas Correia <anicetoclucas@gmail.com>
Co-authored-by: lilp <felipegabriel.avila6@gmail.com>
Co-authored-by: nadevko <ormak@protonmail.com>
Co-authored-by: தமிழ்நேரம் <tamilneram247@gmail.com>
Co-authored-by: 안세훈 <on9686@gmail.com>
2026-03-18 19:55:58 -04:00
renovate[bot] 91d2140288 Update koin to v4.2.0 (#1569)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-18 19:47:20 -04:00
renovate[bot] 0417969dd6 Update dependency net.zetetic:sqlcipher-android to v4.14.0 (#1567)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-18 19:47:08 -04:00
AntsyLich 5d8d2ce48a Switch to AndroidX bundled sqlite driver (#3082)
# Conflicts:
#	app/build.gradle.kts
#	app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt
2026-03-18 19:45:20 -04:00
Mend Renovate b15277f134 Update paging.version to v3.4.2 (#3063) 2026-03-18 19:21:01 -04:00
Mend Renovate 76ca27f681 Update kotlin monorepo to v2.3.20 (#3074) 2026-03-18 19:20:57 -04:00
Mend Renovate 56923c76d4 Update sqldelight to v2.3.2 (#3077) 2026-03-18 19:20:53 -04:00
MajorTanya 32e19736b9 Address bundleOf deprecation (#3073) 2026-03-18 19:20:48 -04:00
Mend Renovate 11b01b2771 Update sqldelight to v2.3.1 (#3071)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
2026-03-18 19:20:45 -04:00
Mend Renovate 460ff13e54 Update dependency io.kotest:kotest-assertions-core to v6.1.7 (#3062) 2026-03-18 19:20:42 -04:00
Mend Renovate 57f77c8105 Update moko to v0.26.1 (#3068) 2026-03-18 19:20:39 -04:00
Mend Renovate a2eb22964a Update dependency com.squareup.okio:okio to v3.17.0 (#3070) 2026-03-18 19:20:36 -04:00
Mend Renovate 7158bae26a Update dependency androidx.activity:activity-compose to v1.13.0 (#3065) 2026-03-18 19:20:32 -04:00
Mend Renovate 807ce846d5 Update dependency androidx.core:core-ktx to v1.18.0 (#3067) 2026-03-18 19:20:29 -04:00
Mend Renovate 0b68f2c62a Update dependency androidx.compose:compose-bom to v2026.03.00 (#3066) 2026-03-18 19:20:26 -04:00
AntsyLich b7d6cc8dd0 Add installation id for feature flags (#3052)
# Conflicts:
#	app/build.gradle.kts
#	app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt
#	app/src/main/java/mihon/core/migration/migrations/Migrations.kt
2026-03-18 19:20:17 -04:00
Mend Renovate 8b1fd30902 Update dependency androidx.compose:compose-bom to v2026.02.01 (#3009) 2026-03-18 19:09:37 -04:00
Mend Renovate aff43f3aeb Update dependency com.google.firebase:firebase-bom to v34.10.0 (#3006) 2026-03-18 19:09:29 -04:00
Mend Renovate 0047d2e5d8 Update dependency com.diffplug.spotless:spotless-plugin-gradle to v8.3.0 (#3029) 2026-03-18 19:09:22 -04:00
Mend Renovate d87385f5b3 Update dependency com.materialkolor:material-kolor to v5.0.0-alpha07 (#3024) 2026-03-18 19:09:15 -04:00
AntsyLich c17e9573b7 Reapply "Fix cache invalidation isn't done at startup (#2970)"
This reverts commit d219c5e3bbcfb24c40fa69e40bff11b6fd81fd7f.

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt
2026-03-18 19:08:43 -04:00
AntsyLich 9c01119d24 Reapply "Fix thread starvation caused by not yielding or using an inappropriate thread pool (#2955)"
This reverts commit 1d7c838ae64e624d9dd0884722f0c6ae5d18e386.

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt
#	app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
2026-03-18 19:06:16 -04:00
Jobobby04 bbc839e234 Lint 2026-02-27 22:44:01 -05:00
Jobobby04 917f20894b Bump version code 2026-02-27 22:08:28 -05:00
Jobobby04 3a3b719b8b Copy last page read in migrate 2026-02-27 22:07:59 -05:00
Jobobby04 1903437ecf Cleanup 2026-02-27 22:07:42 -05:00
Jobobby04 5c26bb3a52 Add recommended proguard rules 2026-02-27 22:07:31 -05:00
Jobobby04 07599ade3a Fix blank page on cloudflare guard 2026-02-27 13:49:26 -05:00
Jobobby04 0a9f36402b Remove migrate button from merged manga 2026-02-27 13:38:28 -05:00
Weblate (bot) d2b325cd02 Translations update from Hosted Weblate (#2997)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cs/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/vi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/
Translation: Mihon/Mihon

Co-authored-by: Filip Jaruška <filip.jaruska@gmail.com>
Co-authored-by: Nguyễn Trung Đức <vaicato16@gmail.com>
Co-authored-by: ɴᴇᴋᴏ <s99095lkjjim@gmail.com>
(cherry picked from commit cc3f640faa7df9f2a64976d2639e6dfd60ebe213)
2026-02-27 13:33:34 -05:00
AntsyLich cdc64aceb7 Fix extension install/update stuck at pending (#3000)
Co-authored-by: p
(cherry picked from commit 84265febf3ce24d71994ced2b81215f858430d4e)

# Conflicts:
#	CHANGELOG.md
2026-02-27 13:33:28 -05:00
Mend Renovate 4bfd6e4026 Update dependency io.kotest:kotest-assertions-core to v6.1.4 (#2998)
(cherry picked from commit 171a06a8baccae7fb21d2c60150639941747d90c)
2026-02-27 13:33:01 -05:00
Weblate (bot) 50eebdf7d3 Translations update from Hosted Weblate (#2980)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eo/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sk/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/
Translation: Mihon/Mihon

Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Frosted <frosted@users.noreply.hosted.weblate.org>
Co-authored-by: Jakub Szafranek13 Fabijan <jakubfabijan@tuta.io>
Co-authored-by: KraXen <dpelech1@gmail.com>
Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: ZerOriSama <godarms2010@live.com>
(cherry picked from commit 565379779f82ee7dfdad45e251d82ff73dc3b7b2)
2026-02-27 13:32:56 -05:00
AntsyLich f843de28d7 Run automatic library updates even when connected to a VPN (#2996)
Co-authored-by: jeremiejig <3978761+jeremiejig@users.noreply.github.com>
(cherry picked from commit 039471427448347a1c12c39a488a1127a3ea1497)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt
2026-02-27 13:32:48 -05:00
Cuong-Tran d250a9a680 Going back now first clears search query on browse extension tab (#2906)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 75b445fa8fd42b882266e27d9b979b22ca37d42a)

# Conflicts:
#	CHANGELOG.md
2026-02-27 13:30:07 -05:00
AntsyLich 4130db3920 Add all pages of adjacent chapters in the UI instead of only the first or last three (#2995)
(cherry picked from commit 0cc724108b4f29a3d1d33ac4666a14873460a657)

# Conflicts:
#	CHANGELOG.md
2026-02-27 13:29:44 -05:00
Mend Renovate f2cbff04ab Update dependency io.coil-kt.coil3:coil-bom to v3.4.0 (#2992)
(cherry picked from commit 5b85084d0a90b241fdf1d3ad018a27b49db15f12)
2026-02-27 13:29:19 -05:00
AntsyLich 061e9359e8 Fix migration dialog not showing for consecutive prompts from the same screen (#2994)
(cherry picked from commit 8b2d35f3068a3f9c5e1bba77ed825ae51531c91c)

# Conflicts:
#	CHANGELOG.md
2026-02-27 13:29:16 -05:00
AntsyLich 73258e9e05 Fix migration's selected sources order not preserved (#2993)
(cherry picked from commit 47816d4b218e6a62ef9fcd3097b6b0b8f2f95b17)

# Conflicts:
#	CHANGELOG.md
2026-02-27 13:28:55 -05:00
NarwhalHorns 73e4982ffb Utilize tracker for library duplicate detection (#2978)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 89bbdb17fb4ed1cbe99c14f389940e0f91093a10)

# Conflicts:
#	CHANGELOG.md
2026-02-27 13:28:38 -05:00
NarwhalHorns 185cd923c0 Add option for bookmarked chapters to download dropdown (#2891)
(cherry picked from commit 3c6f0f1697ccab055ee7af47da84b2161d406f0c)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt
2026-02-27 13:28:16 -05:00
NarwhalHorns 3cfc53bf11 Optimize tracked library filter (#2977)
Co-authored-by: NarwhalHorns <onefailedgamer@gamil.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit ab214526c6f24466a0432b5c5c7d254a244cd958)

# Conflicts:
#	CHANGELOG.md
2026-02-27 13:21:29 -05:00
Weblate (bot) 1301acfdb7 Translations update from Hosted Weblate (#2843)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ca/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eo/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ko/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/my/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/vi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/
Translation: Mihon/Mihon

Co-authored-by: Ai Ai Ecchi <aiaiecchi9og@gmail.com>
Co-authored-by: Alexandre Dhooge <alexandre.dhooge@zaclys.net>
Co-authored-by: Cuong Tran <cuongtran.tm@gmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: Frosted <frosted@users.noreply.hosted.weblate.org>
Co-authored-by: Hualiang <642615676@qq.com>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: Jakub Szafranek13 Fabijan <jakubfabijan@tuta.io>
Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: MuhamadSyabitHidayattulloh <tebepc@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: Throw Away <throwawayacc4gulshan@gmail.com>
Co-authored-by: ZerOriSama <godarms2010@live.com>
Co-authored-by: akir45 <akkn0708@gmail.com>
Co-authored-by: clukki <nguyenhuuminh16911@gmail.com>
Co-authored-by: momoehab <momoehab@gmail.com>
Co-authored-by: yumekon <konrad.nowicki91@gmail.com>
Co-authored-by: ɴᴇᴋᴏ <s99095lkjjim@gmail.com>
Co-authored-by: 안세훈 <on9686@gmail.com>
(cherry picked from commit 7c7d35e2c64d4f85b0db431fbe18408698af9c6e)
2026-02-27 13:21:06 -05:00
Cuong-Tran 9d9dbea48d Remove redundant userSelected from selection methods (#2976)
(cherry picked from commit 9867c160f781b52cd297f7b1202cc6963b375df0)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt
2026-02-27 13:19:34 -05:00
Cuong-Tran c1df3eb1d0 Don't wrap an intent-chooser inside another intent-chooser (#2921)
(cherry picked from commit 20c899f2cd83fdd37dfc14d58ded6ae7581d3d65)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt
2026-02-27 13:19:12 -05:00
Jobobby04 3154c97aee Fix some build warnings 2026-02-27 13:17:48 -05:00
Mend Renovate ffe1b160de Update moko to v0.26.0 (#2967)
(cherry picked from commit 27749fc583527ab1360594a69ef586a4311c93f5)
2026-02-27 13:10:37 -05:00
Mend Renovate 23272375b7 Update dependency com.google.firebase:firebase-bom to v34.9.0 (#2964)
(cherry picked from commit 2248bdac546914492b0adf75425915b899749900)
2026-02-27 13:10:30 -05:00
Mend Renovate 863b6ee784 Update dependency org.junit.jupiter:junit-jupiter to v6.0.3 (#2963)
(cherry picked from commit 42c7329a9e17ab15c0dc1c1f33eabf54286f9339)
2026-02-27 13:10:25 -05:00
Mend Renovate c4c8d4b9c3 Update dependency androidx.compose:compose-bom to v2026.02.00 (#2962)
(cherry picked from commit 5be5a4e81978954713da6f7faa24cc9956c823e9)
2026-02-27 13:10:18 -05:00
Mend Renovate b2bbbca585 Update dependency androidx.activity:activity-compose to v1.12.4 (#2959)
(cherry picked from commit b924d582f459835f533fa54badbe129eede4388f)
2026-02-27 13:10:12 -05:00
Mend Renovate df3b879cf6 Update kotlin monorepo to v2.3.10 (#2960)
(cherry picked from commit 6f601a18c5beb78a70a94471abc2960fb37199b4)
2026-02-27 13:10:05 -05:00
Mend Renovate 47c4f2cc8c Update paging.version to v3.4.1 (#2961)
(cherry picked from commit 1ff9a5625c442be1c9504df0f0eabdaf24aa2fda)
2026-02-27 13:10:00 -05:00
Luca Auer 905a1c1230 Add missing indexes to improve database query performance (#2950)
(cherry picked from commit cae9fbf3213987e7d263845431dfac10a2ecb3b0)

# Conflicts:
#	data/src/main/sqldelight/tachiyomi/migrations/10.sqm
2026-02-27 13:09:55 -05:00
Mend Renovate bcaf7f6415 Update dependency io.kotest:kotest-assertions-core to v6.1.3 (#2939)
(cherry picked from commit d17976c91068944be1a5cba9a959c3763f589590)
2026-02-27 13:02:50 -05:00
Mend Renovate 4639b3ecc3 Update dependency com.materialkolor:material-kolor to v5.0.0-alpha06 (#2938)
(cherry picked from commit b609166702e0788160a557fa1470c6dd8e585169)
2026-02-27 13:02:44 -05:00
MajorTanya 2034971cc0 Clean up some build warnings (#2929)
* Replace deprecated rememberPlainTooltipPositionProvider

* Remove superfluous when branch

This when is marked as exhaustive.

* Replace deprecated LibrariesContainer call

AboutLibraries now wants us to produce the libraries ourselves.

* Replace deprecated ClipboardManager with Clipboard

Clipboard uses suspend functions, hence the coroutine scope addition.

* Use multi-dollar strs to simplify GraphQL queries

These have been available since Kotlin 2.1.

* Remove various redundant casts & conversions

- WebViewScreenContent: loadingState is in the LoadingState.Loading
  branch, no need to cast at all
- Bangumi: username is not modified, make val
- Kavita: token is already a String
- PagerViewerAdapter: insertPageLastPage is already null-checked
- PagerViewerAdapter: use reified filterIsInstance
- ReaderViewModel: chapter IDs are already Longs
- CloudflareInterceptor: webview is smart-cast to non-null here

* Replace deprecated MenuAnchorType

Literally just a typealias for ExposedDropdownMenuAnchorType anyway.

* OptimizeNonSkippingGroups is enabled by default

* Suppress shadowing warning

This is explicitly intentional according to the KDocs.

* Migrate Context Receivers to Context Parameters

Requires changing the compiler arg, but that is part of the migration:

https://blog.jetbrains.com/kotlin/2025/04/update-on-context-parameters

Apparently, the only visible change is that names are required now.
"_" can be used for anonymous context parameters.

* Fix expression bodies with explicit return

Naming conflict resolved by aliasing.

From 2.4/2.5 onward, these will only be allowed with explicit return
types, or have to be turned into a block body. I opted for the latter
since the function is reasonably dense already.

see: https://youtrack.jetbrains.com/issue/KTLC-288

* Suppress deprecation of non-AutoMirrored icons

We use these arrows for navigation in the Upcoming screen.
I strongly doubt the AutoMirrored versions would make sense for our
use-case.

* Explicitly opt-in to new annotation default rules

affects the following annotated value-parameters:
- Preference.SliderPreference.steps (`@IntRange`)
- ReaderViewModel.State.brightnessOverlayValue (`@IntRange`)
- ReadingMode.iconRes (`@DrawableRes`)
- MigrationListScreenModel.Dialog.Progress.progress (`@FloatRange`)

see: https://youtrack.jetbrains.com/issue/KT-73255
see: https://github.com/Kotlin/KEEP/blob/change-defaulting-rule/proposals/annotation-target-in-properties.md

Warning message was the following:

    This annotation is currently applied to the value parameter only, but in the future it will also be applied to field.
    - To opt in to applying to both value parameter and field, add '-Xannotation-default-target=param-property' to your compiler arguments.
    - To keep applying to the value parameter only, use the '@param:' annotation target.

(cherry picked from commit b543bc089a442c5e93b0fb6c83bc4037740b1eb5)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewerAdapter.kt
#	core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt
#	core/common/src/main/kotlin/mihon/core/common/archive/ArchiveInputStream.kt
2026-02-27 13:02:31 -05:00
Mend Renovate bb8698b2a6 Update dependency androidx.compose:compose-bom to v2026.01.01 (#2913)
(cherry picked from commit 100cea0757c930b153e50ca2ba52e4c06f14800d)
2026-02-27 12:52:37 -05:00
Mend Renovate cd69b09dd0 Update dependency androidx.work:work-runtime to v2.11.1 (#2914)
(cherry picked from commit e092b4208a0f8476c81ed58a2b0efd3a7c5996cf)
2026-02-27 12:52:29 -05:00
Mend Renovate 462b2164e8 Update paging.version to v3.4.0 (#2916)
(cherry picked from commit 1bf4eff931781eff11e3c1d59ca3747ffb069203)
2026-02-27 12:52:23 -05:00
Mend Renovate fb1a4ad828 Update dependency androidx.activity:activity-compose to v1.12.3 (#2917)
(cherry picked from commit b70edfac58e8e5722a51be398212ac025b818258)
2026-02-27 12:52:17 -05:00
Mend Renovate 3bd89cee26 Update markdown to v0.39.2 (#2923)
(cherry picked from commit 9c0eebb55ceb6a7edc50e414b4be19faf256bcda)
2026-02-27 12:52:11 -05:00
Mend Renovate 6f43e98fff Update dependency io.kotest:kotest-assertions-core to v6.1.2 (#2908)
(cherry picked from commit f6f1d13addea504ff7ed6ec87276d1cfdfcf2013)
2026-02-27 12:52:06 -05:00
Mend Renovate 6feeb4b1ee Update dependency com.diffplug.spotless:spotless-plugin-gradle to v8.2.1 (#2909)
(cherry picked from commit 31a9ff261e5e195fa2e27615bae0a86956a3c90a)
2026-02-27 12:52:00 -05:00
Mend Renovate fcfe750fcf Update dependency io.mockk:mockk to v1.14.9 (#2904)
(cherry picked from commit c0a1203541931a9090e6769acdb596c9ea4a011b)
2026-02-27 12:51:55 -05:00
Cuong-Tran 6e314e3643 Fix Add Repo input not taking up the full dialog width (#2816)
(cherry picked from commit 82ffc8efa69cb86229fe8b677f12942c80b88a15)

# Conflicts:
#	CHANGELOG.md
2026-02-27 12:51:48 -05:00
Mend Renovate 487ca49c11 Update serialization.version to v1.10.0 (#2879)
(cherry picked from commit d6e17b04c39c9d800c57d75e29714a51fbf902f8)
2026-02-27 12:51:27 -05:00
Mend Renovate 698abe8667 Update dependency com.diffplug.spotless:spotless-plugin-gradle to v8.2.0 (#2892)
(cherry picked from commit e6c67003511b601d49bc175b83596568ddfe09d9)
2026-02-27 12:51:20 -05:00
Mend Renovate 13c9daf9a9 Update dependency io.kotest:kotest-assertions-core to v6.1.1 (#2893)
(cherry picked from commit ede2f56bc6531f69553d0f95cf422b7f6d15b90b)
2026-02-27 12:51:14 -05:00
Mend Renovate eb21454d6d Update Gradle to v8.14.4 (#2894)
(cherry picked from commit 2037ec45006b54da0e64b3e07bae4c87e261ee59)
2026-02-27 12:51:07 -05:00
Cuong-Tran 56347e6d52 Fix memoization in manga bottom action menus (#2886)
(cherry picked from commit 533a578bdb438fe4a23e069386a6616c7daedcc4)
2026-02-27 12:50:46 -05:00
MajorTanya 5c085a36e8 Reword download index message (#2874)
I'm tired of people thinking the current wording is an error. Improved wordings welcome, this was just my first decent guess.

(cherry picked from commit 05d90ea4d652cd7ead385ec954e0ae2dc332a012)

# Conflicts:
#	CHANGELOG.md
2026-02-27 12:50:38 -05:00
Mend Renovate 65ab676946 Update dependency io.kotest:kotest-assertions-core to v6.1.0 (#2870)
(cherry picked from commit 49c4d08b22b6a440b93b58bd78280807550d6198)
2026-02-27 12:50:19 -05:00
MajorTanya 1f51569a35 Add Filters to Updates screen (#2851)
* Add Filters to Updates screen

Behaves basically like the filters in the library:

- Unread: Show/Don't show unread chapters
- Downloaded: Show/Don't show downloaded chapters
- Started: Show/Don't show chapters that have some progress but aren't
  fully Read
- Bookmarked: Show/Don't show chapters that have been bookmarked

Started behaves differently from its Library counterpart because the
actual manga data is not available at this point in time and I thought
calling getManga for each entry without caching would be a pretty bad
idea.

I have modelled this closely on the filter control flow in the
Library, but I'm sure this can be simplified/adjusted in some way.

* Move most filtering logic to SQL

Unread, Started, and Bookmarked filters are now part of the SQL query.

Download state cannot be filtered in the database so it remains in
Kotlin.

Because the Downloaded filter has to be run in Kotlin, the combine
flow uses the preferences flow twice, once to get the SQL query params
and once for the Kotlin filters (only Downloaded at this time).

* Add "Hide excluded scanlators" to update filters

Based on the work done in #1623 but integrated with the other filters
in this PR. Added the user as a co-author for credit.

Co-authored-by: Dani <17619547+shabnix@users.noreply.github.com>

---------

Co-authored-by: Dani <17619547+shabnix@users.noreply.github.com>
(cherry picked from commit bbe9aa8561360f030072fbc49f79748e71c6535e)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt
#	data/src/main/java/tachiyomi/data/updates/UpdatesRepositoryImpl.kt
#	data/src/main/sqldelight/tachiyomi/migrations/9.sqm
#	domain/src/main/java/tachiyomi/domain/updates/interactor/GetUpdates.kt
2026-02-27 12:35:44 -05:00
Jobobby04 b0d6e16ca3 Fix build 2026-02-27 11:51:08 -05:00
Mend Renovate 85cf54ccc8 Update dependency com.google.firebase:firebase-bom to v34.8.0 (#2856)
(cherry picked from commit 13975d6f7eea21b3ba4be31736038ef2f855bfea)
2026-02-27 11:46:14 -05:00
Mend Renovate 602df5a729 Update dependency androidx.compose:compose-bom to v2026 (#2853)
(cherry picked from commit e659e90c26814c9a29bf668545b4f10294f3c6b9)
2026-02-27 11:46:08 -05:00
Mend Renovate c8102836ce Update dependency com.materialkolor:material-kolor to v5.0.0-alpha05 (#2849)
(cherry picked from commit 99e6fa3c06e1dc24255a73aa5741a72c82d94d91)
2026-02-27 11:46:02 -05:00
Mend Renovate e641575941 Update markdown to v0.39.1 (#2850)
(cherry picked from commit cf13012629571e2097796b50df238f69e62843b3)
2026-02-27 11:45:49 -05:00
NGB-Was-Taken 83afcee4d1 Fix crash when trying to install/update extensions while shizuku isn't running (#2837)
(cherry picked from commit 4ce249c1a0ee9f3a20d91214fd09145e0924b2e4)

# Conflicts:
#	CHANGELOG.md
2026-02-27 11:45:04 -05:00
MajorTanya 2102e0594e Fix nullability of MAL authors breaking search (#2834)
One of these days I'll get through a tracker change without
nullability problems...

(cherry picked from commit edcf84d9022e7436606a0b8c493c1035888ac60a)
2026-02-27 11:44:38 -05:00
Constantin Piber 14c91da6b3 Add a small increment to chapter number before comparison to fix progress sync issues for Suwayomi (#2675)
Due to a `Float->Double->Float` conversion somewhere inside Mihon, the
tracker sees 2.1 as 2.0999999046325684, which means this filter ignores
the 2.1 chapter (which we just tried to mark as read). This small
epsilon is small enough to never bother any serious usage, but large
enough to ignore any such conversion errors.

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit bd5c4d48f980d2d3dcc1112fe499dba17ef8e507)

# Conflicts:
#	CHANGELOG.md
2026-02-27 11:44:26 -05:00
MajorTanya 46c1c6463a Add authors/artists to MAL search results (#2833)
(cherry picked from commit 51b3ab3fd19bdf6a7c3bd2085104392a9c412622)

# Conflicts:
#	CHANGELOG.md
2026-02-27 11:44:10 -05:00
Weblate (bot) 89a521b836 Translations update from Hosted Weblate (#2806)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ca/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ceb/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/it/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/bn/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ca/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ceb/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ko/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt_BR/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

Co-authored-by: Ahmed TOUCHANE <ahmedtouchane0@gmail.com>
Co-authored-by: Anderhale <anderhale@users.noreply.hosted.weblate.org>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: Eugene <e.shlyapkin99@gmail.com>
Co-authored-by: Gino Cicatiello <ginocic@gmail.com>
Co-authored-by: Hiroshi <borlonjhayron1119@gmail.com>
Co-authored-by: Luis Antonio <getcyonic+zaorinu@gmail.com>
Co-authored-by: NormalRandomPeople <normal.scribe833@silomails.com>
(cherry picked from commit e6f72000ba62302c3e4817a5f17057e5a8d7eafc)
2026-02-27 11:43:50 -05:00
MajorTanya 65c6ed21ab Optimise MAL search queries by ~11x (#2832)
Previously, the app made one request for the search, and then fired
off 1 request per search result to obtain additional data, such as
each title's synopsis, etc.

However, MAL's search allows field selection during the initial query,
which will return all the data in that first response, avoiding the
massive bunch of requests (and alleviating some pressure on MAL from
our userbase).

By combining the selected fields into one constant, I was able to also
get rid of the MALUserListSearch entirely because it was redundant.
This allows for a unified MALManga->TrackSearch helper, further
reducing complexity.

I got to my "11x" improvement because on page of search results has 10
elements, and this change turns 11 (1+10 for results) requests into 1.

(cherry picked from commit 9bf2d78a421213b1885456f5b54c3286edc539e1)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt
2026-02-27 11:42:54 -05:00
Mend Renovate 1b911e7e15 Update dependency org.junit.jupiter:junit-jupiter to v6.0.2 (#2830)
(cherry picked from commit 89c4e3bb39b7450a9aedcf9d720c74c04cd560ff)
2026-02-27 11:41:41 -05:00
Mend Renovate 0535e41051 Update dependency org.jsoup:jsoup to v1.22.1 (#2826)
(cherry picked from commit 47fe792ddc5a342ecc4685d57143cfb40f0682f7)
2026-02-27 11:41:32 -05:00
AntsyLich 3fc802f837 Remember descriptionAnnotator across composition
Closes #2510

Co-authored-by: Cuong-Tran <16017808+cuong-tran@users.noreply.github.com>
(cherry picked from commit 906d6f3cdbd6f7168d9cdea0c3eb8f9c663a09c2)
2026-02-27 11:41:17 -05:00
AntsyLich 976b5cc03e Cleanup extension screen search query predicate
(cherry picked from commit e059190fabe3cbe8498fc3cec7e39b0350f3c289)
2026-02-27 11:41:10 -05:00
AntsyLich a9fe971337 Switch to M3E ExtendedFloatingActionButton
(cherry picked from commit a39b5a56e853a9c11c0984b0385a2e60b0addc3a)
2026-02-27 11:40:06 -05:00
AntsyLich 5d1dbcb390 Switch to MaterialExpressiveTheme
(cherry picked from commit 3e6afee13b9fd5716f7f2d547b3edcd3e17915db)
2026-02-27 11:28:24 -05:00
AntsyLich 8d11ef3244 Use materilalKolor for monet compat color scheme
(cherry picked from commit 9a11ec8ead41cb7199e10f3c7464790a0bd3b1ad)
2026-02-27 11:28:18 -05:00
Weblate (bot) 724a61f513 Translations update from Hosted Weblate (#1536)
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/ko/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/fr/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/hr/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/it/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ko/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/zh_Hant/
Translation: Mihon/TachiyomiSY
Translation: Mihon/TachiyomiSY Plurals

Co-authored-by: Champ0999 <champ0999@users.noreply.hosted.weblate.org>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Erick Alejandro <erickux@gmail.com>
Co-authored-by: Frosted <frosted@users.noreply.hosted.weblate.org>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: Luis Antonio <getcyonic+zaorinu@gmail.com>
Co-authored-by: Metin <durmus38metin@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: NormalRandomPeople <normal.scribe833@silomails.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: ZerOriSama <godarms2010@live.com>
Co-authored-by: momoehab <momoehab@gmail.com>
Co-authored-by: 안세훈 <on9686@gmail.com>
2026-02-27 11:25:48 -05:00
renovate[bot] 724c774dc9 Update dependency net.zetetic:sqlcipher-android to v4.13.0 (#1544)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-27 11:21:21 -05:00
renovate[bot] 29e0b2e4a5 Update actions/upload-artifact action to v7 (#1557)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-27 11:21:12 -05:00
KaiserBh 2776e41127 feat: Add sync events to SyncYomi (#1558)
* feat: Add sync events to SyncYomi

Now it will send the events back to `SyncYomi` server and then forward those to the notifications services that are enabled, such as discord, telegram, and etc.

* chore: fix build error.
2026-02-27 11:20:59 -05:00
Jobobby04 af1f77418f This is SY not Mihon Crashing 2026-02-13 22:41:04 -05:00
MajorTanya c1df5da062 This is SY not Mihon (#1549) 2026-01-31 11:24:37 -05:00
Jobobby04 f8f645772d Crashfix
Co-authored-by: name <arkon@users.noreply.github.com>
2026-01-01 12:26:13 -05:00
Jobobby04 b1e6fa65d6 Or 2025-12-26 23:31:31 -05:00
Jobobby04 01e8c6cc12 Use ComposeStars from RatingBar library. 2025-12-26 14:56:38 -05:00
Jobobby04 b4668c6829 Lint 2025-12-25 17:43:46 -05:00
Jobobby04 08d6c604bc Cleanup 2025-12-25 17:42:40 -05:00
Constantin Piber 02cec06535 Implement automatic removal of downloads on Suwayomi after reading, configurable via extension settings (#2673)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 1263df9d4111511e49a43463c9808060433ce76d)

# Conflicts:
#	CHANGELOG.md
2025-12-25 17:26:02 -05:00
Weblate (bot) ebdb3f7478 Translations update from Hosted Weblate (#2711)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ka/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/bn/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eo/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ka/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ko/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/vi/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

Co-authored-by: Anderhale <anderhale@users.noreply.hosted.weblate.org>
Co-authored-by: Frosted <frosted@users.noreply.hosted.weblate.org>
Co-authored-by: Hasanur Rahman Biplob <hrbiplob10@gmail.com>
Co-authored-by: Jakub Szafranek13 Fabijan <jakubfabijan@tuta.io>
Co-authored-by: Nguyễn Trung Đức <vaicato16@gmail.com>
Co-authored-by: Temuri Doghonadze <temuri.doghonadze@gmail.com>
Co-authored-by: ابْنُ السَدِيمِ <amarlubs2@gmail.com>
Co-authored-by: 안세훈 <on9686@gmail.com>
(cherry picked from commit c96b6ae562cee1220b9fec74708d447413ab8c35)
2025-12-25 17:25:49 -05:00
Mend Renovate 3724d79825 Update dependency androidx.compose:compose-bom to v2025.12.01 (#2651)
(cherry picked from commit 23c427cf60701e46e470fee8b0a7564804ec599e)
2025-12-25 17:25:46 -05:00
Mend Renovate c3e2eb6672 Update markdown to v0.39.0 (#2804)
(cherry picked from commit e3260d56f713d4f5411ae00dfd3da2aba87f4cf2)
2025-12-25 17:25:39 -05:00
Mend Renovate fa91695add Update aboutlib.version to v13.2.1 (#2803)
(cherry picked from commit f37afbcec9f21823cd894036b6ca7f464eb34481)
2025-12-25 17:25:35 -05:00
MajorTanya e7786bd16f Fix pre-1970 upload date display in chapter list (#2779)
A user in #2777 was using the ComicInfo.xml Year/Month/Day fields to
indicate date of publication for some American comics, which often
predate the UNIX Epoch of 1970.

They were seeing "N/A" displays because this line of code discarded
date information for any time before Jan 1st, 1970.

The `toRelativeString` extension function used in the other
`relativeDateText` function already accounts for very distant dates
(anything >7 days away turns into full date, not relative, regardless
of setting, though disabling the relative timestamp setting
circumvents this with the same result). Removing this line should not
cause any issues as it is purely a display difference and the use case
of backdating comics to pre-1970 is worth it in my opinion.

(cherry picked from commit 7a1c8a1b61e07d2e1a402b5daf0e7c04c232f655)

# Conflicts:
#	CHANGELOG.md
2025-12-25 17:25:31 -05:00
Mend Renovate 3d70476b9f Update dependency androidx.activity:activity-compose to v1.12.2 (#2797)
(cherry picked from commit 532b5cf290b448814ce5370bde461d9d5c8f086a)
2025-12-25 17:25:07 -05:00
Mend Renovate e74e0de8f5 Update kotlin monorepo to v2.3.0 (#2794)
(cherry picked from commit 3cb1b2e17a5f77d6133e6bf244304c2d6719aa04)
2025-12-25 17:24:59 -05:00
Luca Auer a2f552d6d2 Minimize memory usage by reducing in-memory cover cache size (#2266)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 4c9cfd8da5f9c67daa4b6401a910f979fd79179f)

# Conflicts:
#	CHANGELOG.md
2025-12-25 17:24:53 -05:00
AntsyLich a6bd0bbd2a Fix reader not saving read duration when changing chapter (#2784)
(cherry picked from commit 2e0786f699cc6d4863eb20331739c8325a451e63)

# Conflicts:
#	CHANGELOG.md
2025-12-25 17:24:36 -05:00
Mend Renovate fd42bba188 Update dependency com.google.firebase:firebase-bom to v34.7.0 (#2782)
(cherry picked from commit e7e4d9b6b35033a0568f40e32e94aaf336c96c39)
2025-12-25 17:24:06 -05:00
AntsyLich a0ec735066 Use AGP provided NDK and Build Tools version
(cherry picked from commit 5fe7dd9f0612412afabd7646f89ba38b230fb7e7)

# Conflicts:
#	buildSrc/src/main/kotlin/mihon/buildlogic/AndroidConfig.kt
2025-12-25 17:23:59 -05:00
Mend Renovate 89f5fce19d Update dependency com.android.tools.build:gradle to v8.13.2 (#2780)
(cherry picked from commit 4cb05cc738862be52c5f53e43c943f20712c6153)
2025-12-25 17:23:18 -05:00
Jobobby04 bf711a995c Fix build 2025-12-25 17:23:01 -05:00
AntsyLich d977614b7a Update tracker icons (#2773)
(cherry picked from commit 876c3f951b7e9782054d8f788ab39772ae6cf440)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt
2025-12-25 17:22:53 -05:00
Mend Renovate d282df6973 Update dependency androidx.activity:activity-compose to v1.12.1 (#2760)
(cherry picked from commit 08a61a42e9b01f5591615e298c2fecc9f59762ac)
2025-12-25 16:55:40 -05:00
Mend Renovate db5b3a69cc Update dependency io.mockk:mockk to v1.14.7 (#2771)
(cherry picked from commit cadd36ad9a5721d19ea51b68910b8b3276965b3d)
2025-12-25 16:55:35 -05:00
Mend Renovate c70c5dff25 Update dependency io.kotest:kotest-assertions-core to v6.0.7 (#2749)
(cherry picked from commit 556371e1c89bfb0961b63629e79d3fed6a8e7999)
2025-12-25 16:55:24 -05:00
AntsyLich 25ace80419 Cleanup BaseOAuthLoginActivity and TrackLoginActivity (#2748)
(cherry picked from commit c222a28bd14c989b6fa0d53d7497bf063887c9ec)
2025-12-25 16:55:13 -05:00
Jobobby04 b8b468cea7 Minor fixes 2025-12-25 16:49:01 -05:00
NGB-Was-Taken 0ffc798e9a Add preference to toggle chapter URL hash for downloads (#1533) 2025-12-25 16:47:56 -05:00
renovate[bot] ad5a76741a Update actions/upload-artifact action to v6 (#1530)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-25 16:46:49 -05:00
Weblate (bot) 003c5ad39a Translations update from Hosted Weblate (#1529)
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/id/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/it/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/zh_Hant/
Translation: Mihon/TachiyomiSY

Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Frosted <frosted@users.noreply.hosted.weblate.org>
Co-authored-by: Gino Cicatiello <ginocic@gmail.com>
Co-authored-by: Hiroshi <borlonjhayron1119@gmail.com>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: MuhamadSyabitHidayattulloh <tebepc@gmail.com>
Co-authored-by: Nataniel Dika Kurniawan <hikawaart2@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: ZerOriSama <godarms2010@live.com>
2025-12-25 16:46:06 -05:00
NGB-Was-Taken 582d0ef121 Add handling for previously unhandled preferences (delegated MD) (#1524)
* Include romanized titles of the original language in description

* Implement handling for `finalChapterInDesc` preference.

* Handle `preferExtensionLangTitle` preference when fetching manga details.

* Address some warnings, clean up unused code and spotless apply.
2025-12-11 13:58:56 -05:00
NGB-Was-Taken 5566db160b fix deletion of duplicate downloaded chapters when automatically marked as read (#1500) 2025-12-11 13:56:57 -05:00
renovate[bot] 6fb6838656 Update actions/checkout action to v6 (#1522)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-11 13:48:09 -05:00
renovate[bot] 1e5d490c22 Update actions/upload-artifact action to v5 (#1513)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-11 13:48:02 -05:00
renovate[bot] 276aeb0f59 Update gradle/actions action to v5 (#1508)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-11 13:47:56 -05:00
renovate[bot] c62d9d1446 Update actions/github-script action to v8 (#1497)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-11 13:47:44 -05:00
renovate[bot] 4ff18364d9 Update actions/setup-java action to v5 (#1493)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-11 13:47:36 -05:00
renovate[bot] 6c8e4e951a Update dependency net.zetetic:sqlcipher-android to v4.12.0 (#1485)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-11 13:47:28 -05:00
renovate[bot] dc1fde628d Update koin to v4.1.1 (#1466)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-11 13:47:09 -05:00
Weblate (bot) 241b70e5ce Translations update from Hosted Weblate (#1465)
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/fr/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/ne/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/vi/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/de/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/eo/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/fr/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/hr/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/hu/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/id/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/it/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ja/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ne/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/pt/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ta/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/uk/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/vi/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/zh_Hant/
Translation: Mihon/TachiyomiSY
Translation: Mihon/TachiyomiSY Plurals

Co-authored-by: Acelith <joel.jon@moix.me>
Co-authored-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
Co-authored-by: Anderhale <anderhale@users.noreply.hosted.weblate.org>
Co-authored-by: Champ0999 <champ0999@users.noreply.hosted.weblate.org>
Co-authored-by: Conrad Mateman <conradmateme001@gmail.com>
Co-authored-by: Crazyom <naxom@laposte.net>
Co-authored-by: Deleted User <noreply+48943@weblate.org>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Dika <hikawaart2@gmail.com>
Co-authored-by: FateXBlood <fatexblood@gmail.com>
Co-authored-by: Frosted <frosted@users.noreply.hosted.weblate.org>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: Jakub Fabijan <jakubfabijan@tuta.io>
Co-authored-by: João Sousa <joaopsousa99@gmail.com>
Co-authored-by: Karley <siegitsi@gmail.com>
Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com>
Co-authored-by: MajorTanya <github@majortanya.eu>
Co-authored-by: Manjul Tamrakar <manjultamrakar4@gmail.com>
Co-authored-by: Manuela Silva <mmsrs@sky.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Mohamed kh <mohamedkhamekhami@gmail.com>
Co-authored-by: Nguyễn Trung Đức <vaicato16@gmail.com>
Co-authored-by: Omgeta <anooptiger@hotmail.com>
Co-authored-by: Rahim Kansous <rahimkansous18@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: TheKingTermux <50316075+TheKingTermux@users.noreply.github.com>
Co-authored-by: WarriorDan <Danpgl@live.it>
Co-authored-by: ZerOriSama <godarms2010@live.com>
Co-authored-by: abc0922001 <abc0922001@hotmail.com>
Co-authored-by: dianisaac <muhandreop@gmail.com>
Co-authored-by: f_pluz <pedroh.lobo20@gmail.com>
Co-authored-by: ssantos <ssantos@web.de>
Co-authored-by: Đào Ngọc Đang Khoa <daongocdangkhoa2510@gmail.com>
Co-authored-by: ابْنُ السَدِيمِ <amarlubs2@gmail.com>
Co-authored-by: தமிழ்நேரம் <anishprabu.t@gmail.com>
2025-12-11 13:46:18 -05:00
Jobobby04 64c755ddf3 Lint 2025-12-05 13:45:55 -05:00
Jobobby04 3ae6c0131b Lanraragi delegation 2025-12-05 13:44:58 -05:00
NGB-Was-Taken e3b43de298 Spotless apply 2025-11-26 18:41:48 +05:45
AntsyLich 02ff6b4e2f Fix mass migration not using the same search queries as individual migration (#2736)
(cherry picked from commit 7161bc2e825bdfd66a1829f7dce42bd0570b1008)
2025-11-26 15:06:06 +05:45
NGB-Was-Taken ee8379b12a Fix shizuku installer not updating installed extensions (#2697)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

(cherry picked from commit c3d858a5613a133352f6b140916d05de7f752771)
2025-11-26 15:05:26 +05:45
Mend Renovate 20e1cc0a7d Update dependency com.pinterest.ktlint:ktlint-cli to v1.8.0 (#2708)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

(cherry picked from commit 13552c5ffa796aa9bf5db5e12c758a4b3763bf2b)
2025-11-26 15:02:17 +05:45
Mend Renovate 78b434e794 Update dependency androidx.activity:activity-compose to v1.12.0 (#2725)
(cherry picked from commit e6ca458e1f4cad3786d685ce8412ad40aed5155d)
2025-11-26 14:59:51 +05:45
Mend Renovate 52c8b260e0 Update moko to v0.25.2 (#2723)
(cherry picked from commit 05c7df2ed32b429f1ed3f1425e854c1867813a21)
2025-11-26 14:59:51 +05:45
Mend Renovate cccdc99977 Update sqlite to v2.6.2 (#2724)
(cherry picked from commit 4a3339a21f479bd57a7765cbeb092674aa2e8507)
2025-11-26 14:59:51 +05:45
Mend Renovate ca15d2ccc5 Update lifecycle.version to v2.10.0 (#2726)
(cherry picked from commit 6f497ed03181b15cb79899c689ffe70353656f0b)
2025-11-26 14:59:51 +05:45
Jobobby04 b7b0ffc885 Remove old SmartSearch 2025-11-20 13:20:53 -05:00
Mend Renovate f1a57749c4 Update dependency com.squareup.okio:okio to v3.16.4 (#2716)
(cherry picked from commit c17fc6792a3b8a5b6f31c107f165bfcda44617ae)
2025-11-20 12:53:57 -05:00
Mend Renovate 26d6c09d21 Update dependency io.kotest:kotest-assertions-core to v6.0.5 (#2717)
(cherry picked from commit 9e27ae3d2b63210714d6b16add687d2b37a34736)
2025-11-20 12:53:51 -05:00
Mend Renovate 088e3b6800 Update okhttp monorepo to v5.3.2 (#2720)
(cherry picked from commit 8b5dd39f1cbfd2eb8dd09cf42757206eeba631f7)
2025-11-20 12:53:45 -05:00
Mend Renovate c8ee2dbff4 Update dependency com.diffplug.spotless:spotless-plugin-gradle to v8.1.0 (#2721)
(cherry picked from commit 8d81c94679e4ecc9e452b03feb7c08f524a4969d)
2025-11-20 12:53:39 -05:00
Mend Renovate 835a21b426 Update dependency com.google.firebase:firebase-bom to v34.6.0 (#2707)
(cherry picked from commit e5a693f224190a662f2192a06fd12ae2a03e7dc3)
2025-11-20 12:53:32 -05:00
Mend Renovate 8d67c87639 Update okhttp monorepo to v5.3.1 (#2712)
(cherry picked from commit 7947e1bd6a7bdd99fe6badde8b665da24f92ab7c)
2025-11-20 12:53:24 -05:00
Jobobby04 73cd25e8ba Remove unused migration preferences 2025-11-20 12:50:33 -05:00
Jobobby04 e9ed861f00 Minor cleanup 2025-11-20 12:49:42 -05:00
Jobobby04 e7c1d4deef Minor cleanup 2025-11-20 12:38:09 -05:00
Jobobby04 b8eb75fc68 Throttle E-Hentai requests 2025-11-20 12:37:37 -05:00
NGB-Was-Taken 3d34f3dd2f Exclude MergedSource from MigrationConfigScreen 2025-11-17 08:49:15 +05:45
NGB-Was-Taken 01dc277877 Replace topbar in ReaderAppBars with ReaderTopBar 2025-11-16 20:35:50 +05:45
NGB-Was-Taken b809ae5c6f Optimize imports 2025-11-16 18:48:51 +05:45
NGB-Was-Taken 4c563122f8 Spotless apply 2025-11-16 18:14:20 +05:45
NGB-Was-Taken 4c1124fdb0 Fix compile time errors and make it build 2025-11-16 18:07:44 +05:45
NGB-Was-Taken bdbaecd975 Fix queries and mappers to work with updated views 2025-11-16 18:07:44 +05:45
Mend Renovate 2b8641c1dd Update dependency com.squareup.okio:okio to v3.16.3 (#2709)
(cherry picked from commit cab729c9396d54c98b1e8c4716b93814016deab4)
2025-11-16 18:07:44 +05:45
Mend Renovate f1a90000b2 Update dependency app.cash.sqldelight:sqlite-3-38-dialect to v2.2.1 (#2703)
(cherry picked from commit f6a95a741667871df34288bdb6b67cafe2f79d54)
2025-11-16 18:07:44 +05:45
Weblate (bot) 3bd0f0b447 Translations update from Hosted Weblate (#2687)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/am/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/bn/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/as/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/bn/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ms/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/my/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

Co-authored-by: Anderhale <anderhale@users.noreply.hosted.weblate.org>
Co-authored-by: Anderhale <safwanistaken@gmail.com>
Co-authored-by: Ardiansyahset <ardiantozep@users.noreply.hosted.weblate.org>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: MajorTanya <github@majortanya.eu>
Co-authored-by: NGB-Was-Taken <76197326+NGB-Was-Taken@users.noreply.github.com>
Co-authored-by: amigo browser <juniperforest1@proton.me>
(cherry picked from commit 32db0a5f4c261062883a11f86b7091f654bff568)
2025-11-16 18:07:44 +05:45
NGB-Was-Taken cb5b618266 Stop tap zones from triggering when scrolling is stopped by tapping (#2680)
(cherry picked from commit 2ec67ac0c1831a68d8d73e2679f8c98a5a48acf5)
2025-11-16 18:07:44 +05:45
Mend Renovate c71c07690a Update sqldelight to v2.2.1 (#2704)
(cherry picked from commit b1b79a63f0ec570ba2eac41f8faa50ecf7301e5d)
2025-11-16 18:07:44 +05:45
Mend Renovate d85d77da01 Update dependency com.android.tools.build:gradle to v8.13.1 (#2685)
(cherry picked from commit ec99ab3aef734e8b9ce156fe7493f72e7a8cc03a)
2025-11-16 18:07:44 +05:45
Weblate (bot) 164d7bc70f Translations update from Hosted Weblate (#2676)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/it/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/am/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/as/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/bg/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/bn/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ca/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ceb/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/da/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eu/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fa/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/gl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/he/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/kk/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/kn/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/lt/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/lv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ms/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nn/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sa/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sah/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sc/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sk/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sq/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/th/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/uk/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

Co-authored-by: FateXBlood <fatexblood@gmail.com>
Co-authored-by: Frosted <frosted@users.noreply.hosted.weblate.org>
Co-authored-by: Gino Cicatiello <ginocic@gmail.com>
Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com>
Co-authored-by: MajorTanya <github@majortanya.eu>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: NGB-Was-Taken <76197326+NGB-Was-Taken@users.noreply.github.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: ZerOriSama <godarms2010@live.com>

(cherry picked from commit 84d620978bbc5a4698787f5ab81686e7e60767c9)
2025-11-16 18:07:44 +05:45
AntsyLich db51b09f80 Revert "Fix reader tap zones triggering after scrolling was stopped by the user" (#2670)
(cherry picked from commit 412815af067cdf343a6b5c7b5cd38eeb8190d543)
2025-11-16 18:07:44 +05:45
Weblate (bot) 5f8d03ba9b Translations update from Hosted Weblate (#2656)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/cs/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cs/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/jv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

Co-authored-by: Doministo <doministo@seznam.cz>
Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com>
Co-authored-by: Nataniel Dika Kurniawan <hikawaart2@gmail.com>
Co-authored-by: TheKingTermux <50316075+TheKingTermux@users.noreply.github.com>
Co-authored-by: ZerOriSama <godarms2010@live.com>
Co-authored-by: amigo browser <juniperforest1@proton.me>

(cherry picked from commit f7fb68692a7c41f9f09721dde8db574df1fde1ce)
2025-11-16 18:07:44 +05:45
AntsyLich 4cae7b27a6 Fix extra padding appearing in reader after user interactions (#2669)
(cherry picked from commit aa300cb53ea3a02b63c3b3f3fca60d5e7533a8f1)
2025-11-16 18:07:44 +05:45
Trevor Paley edcf939611 Improve WebView multi-window UX (#2662)
- Navigation history for lower windows is preserved when a popup is opened
- Back gesture will close a popup window rather than the entire WebView activity when there is no previous page
- The leftmost close button closes the entire activity as before
- When a popup window is shown, a new button appears to close just that window

(cherry picked from commit 855eea2ada8c09404dee99a965325913b246618f)
2025-11-16 18:07:44 +05:45
Mend Renovate 208d291b3c Update dependency androidx.core:core-splashscreen to v1.2.0 (#2661)
(cherry picked from commit f4703ed83a32afe1d39669f4988233c1fe7c3f32)
2025-11-16 18:07:44 +05:45
NGB-Was-Taken 19f049189a Fix flaky migration tests (#2663)
(cherry picked from commit 506d51a007e730594e4e5f05f00b4f94c4f24e05)
2025-11-16 18:07:44 +05:45
AntsyLich c855276555 Revert "Update dependency androidx.compose:compose-bom to v2025.10.01 (#2522)"
This reverts commit e8bdf58530cdfd6d530ea9a282785bd313e69be4.

(cherry picked from commit ace387f8bf8f451887b8055213be78312a5b4ea7)
2025-11-16 18:07:43 +05:45
Weblate (bot) 9e244e0889 Translations update from Hosted Weblate (#2646)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/nl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/jv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ms/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

Co-authored-by: Frosted <frosted@users.noreply.hosted.weblate.org>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Nataniel Dika Kurniawan <hikawaart2@gmail.com>
Co-authored-by: Siebrenvde <siebren@siebrenvde.dev>
(cherry picked from commit 5e428071c9a12bc637cbfcf235a33dc19cdef197)
2025-11-16 18:07:43 +05:45
AntsyLich bf7a067908 Fix long strip reader not scrolling on consecutive taps (#2650)
(cherry picked from commit 0acd80dd95094a837c8dc05f3fc29ef3e69bdc21)
2025-11-16 18:07:43 +05:45
bapeey 67c4b71b88 Fix WebView crash introduced in v0.19.2 (#2649)
(cherry picked from commit bdb0ce4779d565fc528e97bd38133a72ff3c1724)
2025-11-16 18:07:43 +05:45
Mend Renovate 1fa8a86cce Update dependency androidx.compose:compose-bom to v2025.10.01 (#2522)
(cherry picked from commit e8bdf58530cdfd6d530ea9a282785bd313e69be4)
2025-11-16 18:07:43 +05:45
Weblate (bot) 73eb98960f Translations update from Hosted Weblate (#2639)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/vi/
Translation: Mihon/Mihon

Co-authored-by: Frosted <frosted@users.noreply.hosted.weblate.org>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: Nguyễn Trung Đức <vaicato16@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
(cherry picked from commit e36b4ce60b2b7141c5b6a4205e98853a0d07438b)
2025-11-16 18:07:43 +05:45
AntsyLich 59704221b7 Migrated to the Android specific about libraries gradle plugin
(cherry picked from commit 6d543024a32bba3136841a19942bed4ea8f0736b)
2025-11-16 18:07:43 +05:45
AntsyLich 19c23943ec Handle reader cutout setting with Insets to support Android 15+ (#2640)
(cherry picked from commit 0e0b6d92833f8e4f3aebdcc1f7c8c175084175d6)
2025-11-16 18:07:43 +05:45
Weblate (bot) d068559dee Translations update from Hosted Weblate (#2373)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/hi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/jv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ko/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ms/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/pt/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/sk/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eo/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/jv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ko/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ms/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ro/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sk/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/th/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/vi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Efe Akın <efeakin1122@gmail.com>
Co-authored-by: Evan Jones (原文轩) <evanjones1883@gmail.com>
Co-authored-by: Farith <mail2@farithadnan.net>
Co-authored-by: Frosted <frosted@users.noreply.hosted.weblate.org>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: Itsmechinmoy <167056923+itsmechinmoy@users.noreply.github.com>
Co-authored-by: Itsmechinmoy <itsmechinmoy@users.noreply.hosted.weblate.org>
Co-authored-by: Jakub Fabijan <jakubfabijan@tuta.io>
Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com>
Co-authored-by: Madddog1997 <madddog1997@gmail.com>
Co-authored-by: Manjul Tamrakar <manjultamrakar4@gmail.com>
Co-authored-by: Manuela Silva <mmsrs@sky.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Mohamed kh <mohamedkhamekhami@gmail.com>
Co-authored-by: MuhamadSyabitHidayattulloh <tebepc@gmail.com>
Co-authored-by: Nataniel Dika Kurniawan <hikawaart2@gmail.com>
Co-authored-by: Omgeta <anooptiger@hotmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Saft Octavian <saftoctavian@gmail.com>
Co-authored-by: Siebrenvde <siebren@siebrenvde.dev>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: TheKingTermux <50316075+TheKingTermux@users.noreply.github.com>
Co-authored-by: Throw Away <throwawayacc4gulshan@gmail.com>
Co-authored-by: ZerOriSama <godarms2010@live.com>
Co-authored-by: keegang 6705 <darunphobwi@gmail.com>
Co-authored-by: ssantos <ssantos@web.de>
Co-authored-by: ɴᴇᴋᴏ <s99095lkjjim@gmail.com>
Co-authored-by: 안세훈 <on9686@gmail.com>

(cherry picked from commit 860955389661ce35d37f499efdd27259b93d8e56)
2025-11-16 18:07:43 +05:45
AntsyLich 8e6b5b8bee Make reader edge-to-edge (#1908)
(cherry picked from commit 5f0c4606681cd59b38ae0855c7827e149fa5488c)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt
#	app/src/main/java/eu/kanade/presentation/reader/ReaderPageIndicator.kt
#	app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderAppBars.kt
#	app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderBottomBar.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
2025-11-16 18:07:43 +05:45
Naputt1 c99ddbe10f Fix reader tap zones triggering after scrolling was stopped by the user (#2518)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

(cherry picked from commit ac28b6c80cee7605052299d6f5f43bc588f701d8)
2025-11-16 18:07:43 +05:45
Constantin Piber 23925c4ba6 Update Suwayomi tracker to use GraphQL API instead of REST API (#2585)
(cherry picked from commit cc2877673539db779af00fbefab3802ac52a7719)
2025-11-16 18:07:43 +05:45
Trevor Paley e71f0afd99 Added proper multi window support in WebView instead of treating everything as a redirect (#2584)
(cherry picked from commit 6ab87c793122165f98c36b4b7d9158069ea40f5a)
2025-11-16 18:07:43 +05:45
Kashish Aggarwal 014bf97248 Fix date picker not allowing the same start and finish date in negative time zones (#2617)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

(cherry picked from commit 8662f80fbf6b7c3aee4945bf656def8341cfdfd3)
2025-11-16 18:07:43 +05:45
anirudhn fc0d666366 Fix scrollbar not showing when animator duration scale animation is turned off (#2398)
(cherry picked from commit 09ec9fc8c54e126692ae68ff260058f3be46a5dd)
2025-11-16 18:07:43 +05:45
c2y5 eb7465e6f9 Fix extension download stuck at pending state in some cases (#2483)
Also auto update extension list whenever a repository is added or removed

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

(cherry picked from commit 87c6f34a558b009be4d316e396ec3eeb4cfbbbf8)
2025-11-16 18:07:43 +05:45
AntsyLich ff6ad20a77 Add option to customize concurrent downloads, increase page concurrency (#2637)
(cherry picked from commit 643762f91325a460c74398d472a555fb00ed9f63)
2025-11-16 18:07:43 +05:45
Mend Renovate 17c528a206 Update markdown to v0.38.1 (#2636)
(cherry picked from commit 7e880014b0f2c42bc430765a19068d93640ce603)
2025-11-16 18:07:43 +05:45
AntsyLich 63f4034a7f Add subtitle support to slider preference and general cleanup (#2635)
(cherry picked from commit f36c259c1faf2ee4a108fd98a5d27d93014ba34c)
2025-11-16 18:07:43 +05:45
AntsyLich 6c1bfc2177 Fix reader "Unable to edit key" error (#2634)
(cherry picked from commit aef3beb15fb2d4acbde9cb2ea4c5f639606d9fc8)
2025-11-16 18:07:43 +05:45
NGB-Was-Taken 45ff1f06ba Update shizuku.version to v13.1.5 (#2566)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

(cherry picked from commit e9469451acf6c7ad39ff08e344ad5013ddc39337)
2025-11-16 18:07:43 +05:45
AntsyLich 3e287a593a Bump app version code and default user agent
(cherry picked from commit f9793d33233ceb7729fa9cfe5ae55b45a372c54c)
2025-11-16 18:07:43 +05:45
AntsyLich 01420154be Fix migration "Attempt to invoke virtual method" crash (#2632)
(cherry picked from commit 93ba6acea56334573fc506d593affd5bea2a86b2)
2025-11-16 18:07:43 +05:45
AntsyLich 1e4c596d0e Fix migration dialog migrating to wrong entry (#2631)
(cherry picked from commit 5e7fecc2c11b4a175fe1c3f698f7daeb58fe311f)
2025-11-16 18:07:43 +05:45
Mend Renovate 26cb2bbbd1 Update dependency org.junit.jupiter:junit-jupiter to v6.0.1 (#2630)
(cherry picked from commit 343074da5f7b5b6b2bb8116cc65348684d4bf615)
2025-11-16 18:07:43 +05:45
AntsyLich 588db79a64 Fix mass migration advanced search query building (#2629)
(cherry picked from commit 7c08b75555a5444ede4912dc5e32607fac2b9678)
2025-11-16 18:07:43 +05:45
Constantin Piber e5aaf3b31f Migrate Kitsu to use library_id and remote_id properly (#2609)
(cherry picked from commit cbf72f4c60cb85f29f8446ba1adb1cfd29d38a59)

# Conflicts:
#	CHANGELOG.md
#	data/src/main/sqldelight/tachiyomi/migrations/8.sqm
2025-11-16 18:07:43 +05:45
Mend Renovate 9c222b128b Update okhttp monorepo to v5.3.0 (#2628)
(cherry picked from commit 0b6de39f2f878f1188a4fc1ea989517bad38e6ca)
2025-11-16 18:07:43 +05:45
Mend Renovate c66e08d43e Update plugin google-services to v4.4.4 (#2573)
(cherry picked from commit 72c4d1fdee981ca0bc94cd32d0a43bb739f43a61)
2025-11-16 18:07:43 +05:45
Mend Renovate 493d8fc45f Update dependency androidx.work:work-runtime to v2.11.0 (#2626)
(cherry picked from commit 3ff25bc984717052b57484859283c96583d152b2)
2025-11-16 18:07:43 +05:45
Mend Renovate 47993cb55d Update dependency com.squareup.okio:okio to v3.16.2 (#2576)
(cherry picked from commit e9224bc2ba19a5a78dd16c30615f2cae5b02ba5d)
2025-11-16 18:07:43 +05:45
Mend Renovate 4701cbea23 Update dependency com.google.firebase:firebase-bom to v34.5.0 (#2575)
(cherry picked from commit 5ac58d01b82daae809139ccb4245802816d23ff8)
2025-11-16 18:07:43 +05:45
Mend Renovate 70efd6f2bf Update xml.serialization.version to v0.91.3 (#2625)
(cherry picked from commit eefaf028ce7bca41d25f77c73e44a1f36af8703a)
2025-11-16 18:07:43 +05:45
Mend Renovate b85b6a713c Update kotlin monorepo to v2.2.21 (#2624)
(cherry picked from commit 582ccca1ab327864e72fff0fe05da6757584c280)
2025-11-16 18:07:43 +05:45
Mend Renovate 6291529a10 Update dependency io.kotest:kotest-assertions-core to v6.0.4 (#2594)
(cherry picked from commit 8f972115a82cda8619577e22465a047c2a863a1a)
2025-11-16 18:07:43 +05:45
Mend Renovate 160907ab52 Update aboutlib.version to v13 (major) (#2580)
Update aboutlib.version to v13

(cherry picked from commit 6f6c0338110e0fdd16cfc997981a21b6ed4859bc)
2025-11-16 18:07:43 +05:45
Mend Renovate bccd30a80f Update okhttp monorepo to v5.2.1 (#2577)
(cherry picked from commit 57a0ab67112d1966c00d7ec433a2cff70064495e)
2025-11-16 18:07:43 +05:45
Radon Rosborough ef4d3e6c4d Improve handling of downloads for chapters with same metadata and optionally for OSes that don't support Unicode in filename (#2305)
Co-authored-by: jkim <jhskim@hotmail.com>
Co-authored-by: fatotak <111342761+fatotak@users.noreply.github.com>
Co-authored-by: MajorTanya <39014446+MajorTanya@users.noreply.github.com>
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

(cherry picked from commit 58b25d697f7987e9888344e815d5646ec010a663)
2025-11-16 18:07:43 +05:45
Mend Renovate 4fe7a1375a Update okhttp monorepo to v5.2.0 (#2564)
(cherry picked from commit 1a31c7c7ee31eee19030875d11ec1f31eab895a4)
2025-11-16 18:07:43 +05:45
NGB-Was-Taken 02bc195068 Fix disabling incognito mode from notification (#2512)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

(cherry picked from commit 96e5131358110079587d262d37ec346e10941758)
2025-11-16 18:07:43 +05:45
Mend Renovate 1e20913237 Update dependency com.google.firebase:firebase-bom to v34.3.0 (#2508)
(cherry picked from commit 1d5bc8d2c2e3cb6df56561782e2e2842af3eaa50)
2025-11-16 18:07:43 +05:45
Mend Renovate 48b488fa59 Update dependency org.junit.jupiter:junit-jupiter to v6 (#2553)
(cherry picked from commit 9a45d248b1545ec192b4d5cff9b0756d0a7a12dd)
2025-11-16 18:07:43 +05:45
Mend Renovate 893f3b2e34 Update moko to v0.25.1 (#2550)
(cherry picked from commit 04168ecec84a3e2b470468002c05bbe9e5e538b5)
2025-11-16 18:07:43 +05:45
Mend Renovate 25be12852f Update dependency io.mockk:mockk to v1.14.6 (#2549)
(cherry picked from commit 607f0ea9cd08c070323c4c1a46efec53dc4b1358)
2025-11-16 18:07:43 +05:45
Secozzi 4f0292b000 Update markdown to 0.37.0 (#2516)
(cherry picked from commit 27a4f6f45c0ba639450428f5f58b1c938fe3032e)
2025-11-16 18:07:43 +05:45
Mend Renovate f669fd9205 Update kotlin monorepo to v2.2.20 (#2498)
(cherry picked from commit 5236d003d27d2aaa8a4baaa8aa4dfc0c8b299fea)
2025-11-16 18:07:43 +05:45
Mend Renovate c3e0646b61 Update dependency androidx.work:work-runtime to v2.10.5 (#2523)
(cherry picked from commit d61a41e8192786c8bb958652098a704d79ad5f5d)
2025-11-16 18:07:43 +05:45
Mend Renovate 0d2cad8693 Update sqlite to v2.6.1 (#2525)
(cherry picked from commit 5637860dd2ed637223014433e1e6005885af10a1)
2025-11-16 18:07:43 +05:45
Mend Renovate 237916a37b Update dependency com.diffplug.spotless:spotless-plugin-gradle to v8 (#2526)
(cherry picked from commit d4d18d0898d7fec07e6021e60481948a40b162ad)
2025-11-16 18:07:43 +05:45
Guzmazow 16653f9585 Improve spoofing of X-Requested-With header to support newer WebView versions (#2491)
(cherry picked from commit 065147472e8e683c47d11f3e00386aba2a7c1bac)
2025-11-16 18:07:43 +05:45
Constantin Piber 392b1009c9 Delegate Suwayomi tracker authentication to extension (#2476)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

(cherry picked from commit 6f635782c22cc0844caa4e4d88cf390005989b0b)
2025-11-16 18:07:43 +05:45
Mend Renovate 10c184e58a Update lifecycle.version to v2.9.4 (#2503)
Update dependency androidx.lifecycle:lifecycle-process to v2.9.4

(cherry picked from commit 86d85f74c097b8b52eb6a91887a055c92e1066f5)
2025-11-16 18:07:43 +05:45
Mend Renovate f6b8756dc0 Update sqlite to v2.6.0 (#2504)
(cherry picked from commit 29e6a2c4a6a32deae4c067c51b7e863b8f8128bb)
2025-11-16 18:07:43 +05:45
Mend Renovate f2654807a4 Update dependency androidx.core:core-ktx to v1.17.0 (#2402)
(cherry picked from commit 60c66bbd3a12e48bff52257c27d6b868e97da1d7)
2025-11-16 18:07:43 +05:45
Mend Renovate ab90b75d73 Update dependency androidx.activity:activity-compose to v1.11.0 (#2499)
(cherry picked from commit 060e5b2e2e8ad85886c4d3362ba1ae56fac52f79)
2025-11-16 18:07:43 +05:45
AntsyLich 0b58a081af Replace compose-stable-marker with compose-runtime-annotation
(cherry picked from commit 4ac9fcd4d3ff2eed59d9c8454d7463b7c9e6dda6)
2025-11-16 18:07:43 +05:45
AntsyLich ae98a5fe58 Bump compile and target sdk
(cherry picked from commit d3b7f7e55ff6609a2cddf1765205ba35c6c26b83)
2025-11-16 18:07:43 +05:45
Mend Renovate 74fa4e3503 Update dependency com.google.firebase:firebase-bom to v34.2.0 (#2376)
(cherry picked from commit 0d926626a1916783b67b3ae8cd2142aa90120e30)
2025-11-16 18:07:43 +05:45
Mend Renovate c20b0f67a8 Update dependency androidx.benchmark:benchmark-macro-junit4 to v1.4.1 (#2496)
(cherry picked from commit 6495a2ea430c82a8481b10ef345669d8ff915cac)
2025-11-16 18:07:43 +05:45
Mend Renovate 098c7196de Update dependency androidx.work:work-runtime to v2.10.4 (#2497)
(cherry picked from commit 94f711ba2a67d0da52ba26978f5c4a103abc7f69)
2025-11-16 18:07:43 +05:45
Mend Renovate 99a25560c1 Update dependency androidx.compose:compose-bom to v2025.09.00 (#2401)
(cherry picked from commit 9f5c4e03b23dab80051afa458361ce62133febbf)
2025-11-16 18:07:43 +05:45
Mend Renovate 79d19a2d8b Update lifecycle.version to v2.9.3 (#2447)
(cherry picked from commit 49562e1915108ac28fa3308d4eed319bb9ed61d5)
2025-11-16 18:07:43 +05:45
Mend Renovate 061d0809bd Update dependency org.jsoup:jsoup to v1.21.2 (#2438)
(cherry picked from commit 57c82b30bab20110832eecc4a825441708eaa02b)
2025-11-16 18:07:43 +05:45
Mend Renovate 0ce0c45cc7 Update dependency io.kotest:kotest-assertions-core to v6.0.3 (#2439)
(cherry picked from commit e573f72cfd5b6c66709ba948ce29c7246a9832e8)
2025-11-16 18:07:43 +05:45
Mend Renovate e910362b16 Update dependency com.android.tools.build:gradle to v8.13.0 (#2449)
(cherry picked from commit bd90307df9ca2f0d424483b29c0dd640eabc0f5f)
2025-11-16 18:07:43 +05:45
Secozzi 96c05bf113 Fix migration progress not updating and category flag mischeck (#2484)
- Fixed an issue where migration progress wasn't updated after a manual source search
- Fixed incorrect logic where the category migration flag was ignored due to checking the chapter flag instead

(cherry picked from commit 16b5317b90b3064d12aa38f687cc30110fd8cdb3)
2025-11-16 18:07:43 +05:45
Mend Renovate 6d2bda5c9d Update plugin firebase-crashlytics to v3.0.6 (#2374)
(cherry picked from commit 83f4b486296f6998efc8ef95fa9d58b75e5de130)
2025-11-16 18:07:43 +05:45
Mend Renovate 4a080fba3f Update dependency com.github.skydoves:compose-stable-marker to v1.0.7 (#2428)
(cherry picked from commit 85f5e5019e59a178f24c93c71644b1c4a08549f3)
2025-11-16 18:07:43 +05:45
AntsyLich ac5b3b164f Bump targetSdk to 35
(cherry picked from commit 4bc3b9f3b6fc87a9285ad3ca1aa8c81d8269d373)
2025-11-16 18:07:43 +05:45
Mend Renovate c43d7dbb31 Update dependency com.android.tools.build:gradle to v8.12.1 (#2417)
(cherry picked from commit feda4101525163c3978a2eb9ab934f9c4db1e873)
2025-11-16 18:07:43 +05:45
Mend Renovate d218fdfcf4 Update dependency sh.calvin.reorderable:reorderable to v3 (#2419)
(cherry picked from commit 200c2df5ba72a39c4116d14827d54a126b77cdc7)
2025-11-16 18:07:43 +05:45
Mend Renovate e59af2fd1f Update dependency io.kotest:kotest-assertions-core to v6 (#2416)
(cherry picked from commit be09cddde2f0799e7eff0003f9dacdb5a6e09947)
2025-11-16 18:07:43 +05:45
AntsyLich 3d761b5bf4 Switch to a fork of QuickJS Java
(cherry picked from commit 498317de52c119a08daaa8d38f3f7d859e7b4990)
2025-11-16 18:07:43 +05:45
Mend Renovate 1892101359 Update kotlin monorepo to v2.2.10 (#2404)
(cherry picked from commit 33b876edc6cd20c73682a165359bec1268aa1199)
2025-11-16 18:07:43 +05:45
Secozzi d76d25379e Don't hardcode app name in strings.xml (#2394)
(cherry picked from commit 3d3c36078a3b8cb741adffd46931aeab9cb38578)
2025-11-16 18:07:43 +05:45
Secozzi c96cf4b11a Fix height of description not being calculated correctly if images are present (#2382)
(cherry picked from commit c6a96b3970f0ba4643f438155653790973fa00f6)
2025-11-16 18:07:27 +05:45
krysanify 31601f523d Fix crash opening filter sheet with empty library and mark as read/unread for selected items (#2355)
(cherry picked from commit d88dbe6409b9c4dd7fbb37757e89c09af73f1fd3)
2025-11-16 17:48:12 +05:45
AntsyLich f9abe20b84 Remove gradle toolchains plugin
(cherry picked from commit d0bad9f0bd6b9d9dc641c81ee2428a0f859d01a8)
2025-11-16 17:48:12 +05:45
AntsyLich f7030ed800 Fix 'Default' category showing in library with no user-added categories (#2371)
(cherry picked from commit 7d717ee7fd9779b85fee29cadc946292cfdcee89)
2025-11-16 17:48:12 +05:45
AntsyLich f4173b3766 Fix title text color in light mode on mass migration list (#2370)
(cherry picked from commit a93f71b82be4489a7a2aefd8fa08bb114cae8db5)
2025-11-16 17:48:12 +05:45
AntsyLich 221a564644 Fix local source EPUB files not loading (#2369)
(cherry picked from commit 9bf3f15fff96b48e6847034c9fcd07f14675130b)
2025-11-16 17:48:12 +05:45
AntsyLich 7458eff2d8 Revert "Add full predictive back support (#2085)" (#2362)
This reverts commit c12bdbae8e7bc14da8966e45a3c450913e32129f.

(cherry picked from commit 1c3e96bf7f463949997c03e2119cfa55fd63dcd0)
2025-11-16 17:48:11 +05:45
Weblate (bot) 187245885a Translations update from Hosted Weblate (#1879)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/as/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/bg/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/bn/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/de/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/el/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/eo/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/fr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/hi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/hr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/id/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ja/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ne/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/pt/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ro/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/uk/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/vi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/bg/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cs/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eo/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/gl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/he/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ml/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/my/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ro/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sc/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ta/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/th/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/uk/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/vi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

Co-authored-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
Co-authored-by: Ahmed TOUCHANE <ahmedtouchane0@gmail.com>
Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Co-authored-by: Akhil Raj <akhilakae07@gmail.com>
Co-authored-by: Alex Maryson Jr <akamar87@gmail.com>
Co-authored-by: Aviv Ben Ami <avivbenami@gmail.com>
Co-authored-by: B4LiN7 <87660017+B4LiN7@users.noreply.github.com>
Co-authored-by: C201 <derasetad@gmail.com>
Co-authored-by: Conrad Mateman <conradmateme001@gmail.com>
Co-authored-by: Crazyom <naxom@laposte.net>
Co-authored-by: Danilo Issida Goncalves <danissida@hotmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Doministo <doministo@seznam.cz>
Co-authored-by: FateXBlood <fatexblood@gmail.com>
Co-authored-by: Frosted <frosted@users.noreply.hosted.weblate.org>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: Guillaume Lecocq <guillaume.taylor@gmail.com>
Co-authored-by: Homura Akemi <amber_c001@protonmail.com>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: Itsmechinmoy <itsmechinmoy@users.noreply.hosted.weblate.org>
Co-authored-by: Jakub Fabijan <jakubfabijan@tuta.io>
Co-authored-by: João Sousa <joaopsousa99@gmail.com>
Co-authored-by: Karley <siegitsi@gmail.com>
Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com>
Co-authored-by: Mario Kevin D. A <programas013@gmail.com>
Co-authored-by: Mehedi Talha <meheditalha007@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Mohamed kh <mohamedkhamekhami@gmail.com>
Co-authored-by: Nguyễn Trung Đức <vaicato16@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Prem Kumar <prem12321kumar@gmail.com>
Co-authored-by: Ryo Richie <ryorichie@gmail.com>
Co-authored-by: Saft Octavian <saftoctavian@gmail.com>
Co-authored-by: Siebrenvde <siebren@siebrenvde.dev>
Co-authored-by: Sky children of the Light <tu25261@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: Tahsin Gökalp <tahsinsaan@gmail.com>
Co-authored-by: TheKingTermux <50316075+TheKingTermux@users.noreply.github.com>
Co-authored-by: Yurt Page <yurtpage@gmail.com>
Co-authored-by: ZerOriSama <godarms2010@live.com>
Co-authored-by: abc0922001 <abc0922001@hotmail.com>
Co-authored-by: akir45 <akkn0708@gmail.com>
Co-authored-by: altinat <al@altqx.com>
Co-authored-by: amigo browser <juniperforest1@proton.me>
Co-authored-by: edgole <test.backache009@aleeas.com>
Co-authored-by: f_pluz <pedroh.lobo20@gmail.com>
Co-authored-by: fl0k1 <michele.carnova@gmail.com>
Co-authored-by: gekka <1778962971@qq.com>
Co-authored-by: gulse02 <gnoeoxgulse@gmail.com>
Co-authored-by: kevans <albapazpi@gmail.com>
Co-authored-by: naikhon <naikhon5@gmail.com>
Co-authored-by: pancake <ppzh0@users.noreply.hosted.weblate.org>
Co-authored-by: scb261 <65343233+scb261@users.noreply.github.com>
Co-authored-by: scb261 <scb261261@gmail.com>
Co-authored-by: Đào Ngọc Đang Khoa <daongocdangkhoa2510@gmail.com>
Co-authored-by: ɴᴇᴋᴏ <s99095lkjjim@gmail.com>
Co-authored-by: Артур Давлетов <ar.davletov2013@gmail.com>
Co-authored-by: Георгій Обушенков <heorhii.obushenkov@gmail.com>
Co-authored-by: Димитър Георгиев <dimitar13226@gmail.com>
Co-authored-by: ابومسلم <linuxmint1978@gmail.com>
Co-authored-by: ابْنُ السَدِيمِ <amarlubs2@gmail.com>
Co-authored-by: தமிழ்நேரம் <anishprabu.t@gmail.com>

(cherry picked from commit 45c1a314887732f691e349a9e7684952830341f0)
2025-11-16 17:48:11 +05:45
Radon Rosborough e5d8c2edbc Use ComicInfo.xml for chapter metadata in localSource (#2332)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

(cherry picked from commit 32257e438e2f0bea7e0bfe84dc72135795d620ad)
2025-11-16 17:48:11 +05:45
anirudhn be4aa39c8a Fixed scrollbar sometimes not showing during scroll or not reaching the bottom with few items (#2304)
(cherry picked from commit 095ef8e74b1c208be25b616ce01d5b198749ee2e)
2025-11-16 17:48:11 +05:45
MajorTanya 014c697773 Add label to privately installed extensions (#2349)
Just adds the same word as the install option ("Private" in English)
next to the extension version and 18+ label.

(cherry picked from commit 549d74a2c9aef0eb41ea18378cd29d4ab9eee2b4)
2025-11-16 17:48:11 +05:45
AntsyLich ea733de80e Fix same manga check logic in mass migration
(cherry picked from commit f1193866f4306384a2a466c6353446bfed2bd9aa)
2025-11-16 17:48:11 +05:45
AntsyLich 3ac5dcd66e Potentially fix library IndexOutOfBound crash (#2341)
(cherry picked from commit c4407eda0eed5a7faed47d4470d79e6b1512b5c2)
2025-11-16 17:48:11 +05:45
AntsyLich 054de1cc6f Remove checksum from release notes and improve download tip
(cherry picked from commit b93337cb3d53278e191cf1dceeaf6c4effdb141d)
2025-11-16 17:48:11 +05:45
AntsyLich b525c0988a Support mass migration in 'Browse -> Migrate' (#2338)
(cherry picked from commit 22f851173b1eca242645f328a46e6038c035d5ec)
2025-11-16 17:48:11 +05:45
AntsyLich 691efe0831 Support mass migration for selected library items (#2336)
(cherry picked from commit 982ebcf777215c90584ad28fae79e9ca8a22a951)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt
2025-11-16 17:48:11 +05:45
AntsyLich 8317a30d6e Optimize and cleanup library code (#2329)
(cherry picked from commit e62cd0e816402303fdf12513816894624f77e208)

# Conflicts:
#	.editorconfig
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt
#	data/src/main/sqldelight/tachiyomi/migrations/6.sqm
#	data/src/main/sqldelight/tachiyomi/view/libraryView.sq
2025-11-16 17:48:11 +05:45
Mend Renovate 633937b0bc Update dependency com.android.tools.build:gradle to v8.12.0 (#2331)
(cherry picked from commit 1365b28106c6fb01c920e997ad63c993e6703eef)
2025-11-16 17:48:11 +05:45
Mend Renovate 3152d2803a Update dependency androidx.test.ext:junit-ktx to v1.3.0 (#2327)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 7ec28eb3bd2d2ce409249e09b90189af21c2c2e6)
2025-11-16 17:48:11 +05:45
Mend Renovate 883c90adc8 Update dependency androidx.test.espresso:espresso-core to v3.7.0 (#2326)
(cherry picked from commit ff9dfe45ed4b9d4fa07cbc1b7bffbff86f596e5c)
2025-11-16 17:48:11 +05:45
Mend Renovate 8e658be7d7 Update dependency androidx.benchmark:benchmark-macro-junit4 to v1.4.0 (#2325)
(cherry picked from commit 967750ba5804f7ddf4ba230d8f3bb942096025b3)
2025-11-16 17:48:11 +05:45
Mend Renovate c27a4e2bf5 Update dependency androidx.work:work-runtime to v2.10.3 (#2324)
(cherry picked from commit 269af7fe2b5e1b444c1806bbeddfa89ec070ceab)
2025-11-16 17:48:11 +05:45
AwkwardPeak7 257f544a89 Include Manga initialized status in backup (#2285)
(cherry picked from commit 62eec15fe61be88e0ebc8be89a1e445dde55ba7e)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt
2025-11-16 17:48:11 +05:45
Secozzi f8cb08ce52 Add option for rendering images in description (#2076)
# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt
2025-11-16 17:48:03 +05:45
Mend Renovate d970eea7cf Update dependency androidx.compose:compose-bom to v2025.07.00 (#2284)
(cherry picked from commit 4f1faf49f3c1fee757b6a9ec58b15c2a010085c3)
2025-11-16 12:37:36 +05:45
Mend Renovate a45fad81c8 Update okhttp monorepo to v5.1.0 (#2257)
(cherry picked from commit 6d717ea88b7545b3c7edba2dd051ce01f8f8aa7f)
2025-11-16 12:37:36 +05:45
Mend Renovate 76d6cf129a Update kotlin monorepo to v2.2.0 (#2235)
(cherry picked from commit fbb5e6b92f48b5f11fa4413f3a48e475cd431c11)
2025-11-16 12:37:36 +05:45
Mend Renovate 879427446f Update dependency org.jsoup:jsoup to v1.21.1 (#2233)
(cherry picked from commit 8f5f29e7376f0ecd275ed2fb95d13b1b10e8c04c)
2025-11-16 12:37:36 +05:45
Mend Renovate 9edd7b0c04 Update dependency com.squareup.okio:okio to v3.16.0 (#2320)
(cherry picked from commit a4b9c704b65027cd2066802960aeb248dbdf1c1f)
2025-11-16 12:37:36 +05:45
Mend Renovate d97b83fe93 Update plugin firebase-crashlytics to v3.0.5 (#2307)
(cherry picked from commit 9352201b03fad147b16423cf33584ecd58548700)
2025-11-16 12:37:36 +05:45
Mend Renovate 027e6bbb05 Update dependency com.google.firebase:firebase-bom to v34 (#2310)
(cherry picked from commit c715e981bffe2ad896559369515c491d2a76946f)
2025-11-16 12:37:36 +05:45
AntsyLich 4cdead8006 Make local source default chapter sorting match file explorer behavior
Closes #2225

(cherry picked from commit 7f56555d632508379037eb0fba4411079c27ad5b)
2025-11-16 12:37:36 +05:45
Mend Renovate aaff472317 Update dependency com.squareup.logcat:logcat to v0.4 (#2319)
(cherry picked from commit 8636b7a68526e033ced6767666f730e6c50ac1b3)
2025-11-16 12:37:36 +05:45
Mend Renovate 9b2febcd6d Update xml.serialization.version to v0.91.2 (#2317)
(cherry picked from commit a49670bf0dc1bfa3250356be2883ebc2f0b603c3)
2025-11-16 12:37:36 +05:45
Mend Renovate 5de355238d Update dependency com.squareup.logcat:logcat to v0.3 (#2309)
(cherry picked from commit 61cee5c5e04550a9c1f16a40600d9e60e28f072c)
2025-11-16 12:37:36 +05:45
Mend Renovate 644c8ec491 Update dependency com.pinterest.ktlint:ktlint-cli to v1.7.1 (#2281)
(cherry picked from commit d805f0cd2a93720726cc2284a384de3fb1f6abbd)
2025-11-16 12:37:36 +05:45
Mend Renovate 4b516ae4c5 Update dependency io.coil-kt.coil3:coil-bom to v3.3.0 (#2308)
(cherry picked from commit ce07259e8e8fb602d1028013aa0c27ee0ce705c9)
2025-11-16 12:37:36 +05:45
Mend Renovate 223251f868 Update lifecycle.version to v2.9.2 (#2283)
Update dependency androidx.lifecycle:lifecycle-process to v2.9.2

(cherry picked from commit 2f10e7beaa11bdddcb409a2fed9d5f0b97b8ade8)
2025-11-16 12:37:36 +05:45
Mend Renovate 9b7d6bace1 Update dependency io.mockk:mockk to v1.14.5 (#2282)
(cherry picked from commit 4ef8fb958824c500f1a2155951d12490fb55c709)
2025-11-16 12:37:36 +05:45
Mend Renovate c15b8b65e5 Update dependency com.diffplug.spotless:spotless-plugin-gradle to v7.2.1 (#2293)
(cherry picked from commit 084e626669793cf637830827b5b03ae898c3814a)
2025-11-16 12:37:36 +05:45
Mend Renovate 4e9eaa5e81 Update dependency org.junit.jupiter:junit-jupiter to v5.13.4 (#2296)
(cherry picked from commit f93ccaaaa42fdf8091c89260803d9d518d06b9ac)
2025-11-16 12:37:36 +05:45
Mend Renovate 066e10246f Update dependency com.android.tools.build:gradle to v8.11.1 (#2277)
(cherry picked from commit 5585388e2d22879ac62e7fceaf26ae7f40cb8bde)
2025-11-16 12:37:36 +05:45
Matthias Ahouansou 3294dd6ca8 Use median to determine smart update interval (#2251)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

(cherry picked from commit d60241690b373e42215761b4108e52a7655daaeb)
2025-11-16 12:37:36 +05:45
Mend Renovate 2315295985 Update dependency com.squareup.okio:okio to v3.15.0 (#2256)
(cherry picked from commit 84aa07b7f0b15bbe9f400596007f6714005118bb)
2025-11-16 12:37:36 +05:45
Mend Renovate 56ddf21107 Update dependency com.diffplug.spotless:spotless-plugin-gradle to v7.1.0 (#2274)
(cherry picked from commit 0cc1224094069a321634cc3c1d5924b246ec7959)
2025-11-16 12:37:36 +05:45
Mend Renovate 3f47df21b8 Update serialization.version to v1.9.0 (#2252)
(cherry picked from commit a5a0d8330248174dc86d8f9a238a1c8489c291a6)
2025-11-16 12:37:36 +05:45
Mend Renovate 712407f524 Update dependency org.junit.jupiter:junit-jupiter to v5.13.3 (#2263)
(cherry picked from commit 1fde0275e313412af601e2ffdd79c7cf800678fb)
2025-11-16 12:37:36 +05:45
Mend Renovate 3a49a0a21a Update dependency gradle to v8.14.3 (#2264)
(cherry picked from commit a992f2d46758b741125c416c58a861a6f9ae9bc7)
2025-11-16 12:37:36 +05:45
Mend Renovate 04b3e13ba1 Update aboutlib.version to v12.2.4 (#2261)
(cherry picked from commit d8dd170d1b416cae793db10f9ee0568bfbdfadf9)
2025-11-16 12:37:36 +05:45
Mend Renovate 39bc2b49c0 Update moko to v0.25.0 (#2258)
(cherry picked from commit 6953090dabe3e90132c7c89a55e359a40257751e)
2025-11-16 12:37:36 +05:45
Mend Renovate 9765282640 Update plugin google-services to v4.4.3 (#2250)
(cherry picked from commit ab452a9945c6d34463e100ab63f614b1cf10b76c)
2025-11-16 12:37:36 +05:45
Mend Renovate 320a620afa Update dependency com.google.firebase:firebase-bom to v33.16.0 (#2248)
(cherry picked from commit 7dd595f16e2f484bd46f925c03b77675918208fc)
2025-11-16 12:37:36 +05:45
Mend Renovate ec2a720617 Update dependency com.android.tools.build:gradle to v8.11.0 (#2241)
(cherry picked from commit 6eb2a022f1e0b4a4da5714d69efe69bef584e179)
2025-11-16 12:37:36 +05:45
Mend Renovate 83d2ca7a54 Update dependency org.junit.jupiter:junit-jupiter to v5.13.2 (#2240)
(cherry picked from commit d61c66c286cf2c6d269809e2fbb5d601ed2d59e0)
2025-11-16 12:37:36 +05:45
Mend Renovate 891503c793 Update dependency io.mockk:mockk to v1.14.4 (#2232)
(cherry picked from commit 1e4ee1460893f7969a17f05b569f3673a42b77f0)
2025-11-16 12:37:36 +05:45
AntsyLich 43abc6c797 Fix background crash in mass migration screen
(cherry picked from commit 63943debc2fd4efa1a0418bbfefaea93a24b49fd)
2025-11-16 12:37:36 +05:45
Danny Wu f916e94a4c Add option to hide missing chapter count (#2108)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

(cherry picked from commit d2c1ff6adf5543e5a593949704d41f2358252593)
2025-11-16 12:37:36 +05:45
AntsyLich fc9b2b6e1e Update manga without chapters even if restricted by source (#2224)
(cherry picked from commit b9e02e92bedf66aa2c4f7ed42b2c6899ed7fd013)
2025-11-16 12:37:36 +05:45
Mend Renovate 66bdd70485 Update dependency com.mohamedrejeb.richeditor:richeditor-compose to v1.0.0-rc13 (#2213)
(cherry picked from commit 103218681a6d9eda2f264e8f09724814dc8d5a96)
2025-11-16 12:37:36 +05:45
Mend Renovate 29024ea03a Update dependency androidx.compose:compose-bom to v2025.06.01 (#2220)
(cherry picked from commit 07136d3969daee988ddc129d091a6923d50f6a5a)
2025-11-16 12:37:36 +05:45
Mend Renovate 6af33905be Update dependency androidx.work:work-runtime to v2.10.2 (#2221)
(cherry picked from commit 4962deeb0cb5766d22ec0d3fb6e88ac50be327de)
2025-11-16 12:37:36 +05:45
Mend Renovate f97d918728 Update dependency com.squareup.okio:okio to v3.13.0 (#2201)
(cherry picked from commit cecf4596f9580806cf3208a518e5acac30becb50)
2025-11-16 12:37:36 +05:45
Mend Renovate 6a38e501d9 Update aboutlib.version to v12.2.3 (#2205)
(cherry picked from commit 0c77afbe038c2ad3db3f09d221f2a03fc2cec237)
2025-11-16 12:37:36 +05:45
Mend Renovate 8310733c4c Update sqlite to v2.5.2 (#2210)
(cherry picked from commit d126b84f954d6a35dd3cc1a29849fb0822714552)
2025-11-16 12:37:36 +05:45
AwkwardPeak7 28f0946877 Ensure app waits for Cloudflare challenge to complete before continuing (#2200)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

(cherry picked from commit 2df3382148c76d287943fc36e2f55bc16df1cd83)
2025-11-16 12:37:36 +05:45
jobobby04 e135a0dc71 Mass migration implementation (#2110)
There is no way to trigger mass migration at the moment. The functionality will be added in a follow up PR.

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit ee19050cc00b0a787af65b641624d07bb194e155)
2025-11-16 12:37:36 +05:45
AntsyLich 05a65773f0 Further tweak migration config screen sheet
(cherry picked from commit 019fc08da2392725c36e064ffadc4e645edafb63)
2025-11-16 12:37:36 +05:45
AntsyLich 8d794560a0 Add more migration config options and remove skipping option (#2193)
(cherry picked from commit 288f577a45a6835c34ad41caab95794f164b7a0b)
2025-11-16 12:37:36 +05:45
Mend Renovate 4341a98413 Update aboutlib.version to v12.2.2 (#2190)
(cherry picked from commit a47d4ebbdd04ce439c25a564afdb813dc80615d4)
2025-11-16 12:37:36 +05:45
Mend Renovate 24f5a36350 Update dependency sh.calvin.reorderable:reorderable to v2.5.1 (#2183)
(cherry picked from commit 89954e68e3265936718b5dd0664102f9f37b554a)
2025-11-16 12:37:36 +05:45
Mend Renovate f125db6973 Update dependency sh.calvin.reorderable:reorderable to v2.5.0 (#2178)
(cherry picked from commit c3b590cd3dfe70a5736411c6533913f2dadb17bc)
2025-11-16 12:37:36 +05:45
Mend Renovate 4ec969657e Update dependency com.google.firebase:firebase-bom to v33.15.0 (#2177)
(cherry picked from commit cb3c5e9c9c8465d3b1228264964b19ecda8bfc98)
2025-11-16 12:37:36 +05:45
Mend Renovate cb7e790086 Update plugin firebase-crashlytics to v3.0.4 (#2174)
(cherry picked from commit 7fa2834009013b01b06e2d95a80a4b4e09f37d17)
2025-11-16 12:37:36 +05:45
Mend Renovate 9012dafed4 Update dependency gradle to v8.14.2 (#2168)
(cherry picked from commit 95e3c22429adc98fb3ad7b941b5a8aa45322bfdc)
2025-11-16 12:37:36 +05:45
Mend Renovate 8b24d7eded Update okhttp monorepo to v5.0.0-alpha.16 (#2149)
(cherry picked from commit 8bd70342fcd1355c53fef62e597e2aa20229e123)
2025-11-16 12:37:36 +05:45
Mend Renovate dcc537accb Update sqldelight to v2.1.0 (#2119)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 92ec6b17a315c12838bf94bd214d216435e0f8f9)
2025-11-16 12:37:36 +05:45
Mend Renovate ab67775f13 Update dependency org.junit.jupiter:junit-jupiter to v5.13.1 (#1754)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

(cherry picked from commit 591e9c13560f28d71598eb32648387d099ebc881)
2025-11-16 12:37:36 +05:45
Mend Renovate e105828e07 Update dependency androidx.compose:compose-bom to v2025.06.00 (#2175)
(cherry picked from commit 7fed9c2ccfed0179e50b4b8960fcd3eb6d229f53)
2025-11-16 12:37:36 +05:45
Mend Renovate 3c83edaad2 Update dependency com.squareup.logcat:logcat to v0.2.3 (#2126)
(cherry picked from commit ccb554c877e552433cc4fff9164207b02f0a17cf)
2025-11-16 12:37:36 +05:45
Mend Renovate b4f278e5fa Update dependency androidx.appcompat:appcompat to v1.7.1 (#2167)
(cherry picked from commit 5235713d83ab3643d30f2a7f087126d60dbb21f4)
2025-11-16 12:37:36 +05:45
Mend Renovate 29ca687a26 Update lifecycle.version to v2.9.1 (#2173)
(cherry picked from commit 4692010400d4fad2cc51901a355a0c15b865860c)
2025-11-16 12:37:36 +05:45
Mend Renovate ed975cae63 Update aboutlib.version to v12.2.1 (#2170)
(cherry picked from commit be528ba12b82d357170262d825b38033fbd83e91)
2025-11-16 12:37:36 +05:45
Mend Renovate 4fce0944b4 Update dependency me.zhanghai.android.libarchive:library to v1.1.6 (#2171)
(cherry picked from commit 405e536cbf4182e141d221795331c374236c9708)
2025-11-16 12:37:36 +05:45
AntsyLich 4a52898f08 Add option to skip migration config
(cherry picked from commit 8714653a2f3b1a1f536494bcefbe66e7a7bbb4f7)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt
2025-11-16 12:37:36 +05:45
AntsyLich 92b48319ed Cleanup migrate manga dialog and related code (#2156)
(cherry picked from commit 2b126f1ff56b63e470b48a04149e28c609f01148)

# Conflicts:
#	app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/MigrationFlags.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreenDialogScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSourceSearchScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt
2025-11-16 12:37:36 +05:45
AntsyLich 9e113d80f7 Fix no sources while migrating alongside UI and code cleanup (#2155)
(cherry picked from commit 5919f34fc96f254724bd3042ac2b91ac65912930)
2025-11-16 12:37:36 +05:45
Mend Renovate f960554cf8 Update dependency com.github.requery:sqlite-android to v3.49.0 (#2150)
(cherry picked from commit a4df33caf937a79ab1f70e33ad56e5b3899917e2)
2025-11-16 12:37:36 +05:45
Mend Renovate 0bdea705a5 Update aboutlib.version to v12.2.0 (#2152)
(cherry picked from commit 3580d2da6cc7f09eb3b6f1345c958a0ab0574856)
2025-11-16 12:37:36 +05:45
claymorwan 33361ea7f6 Add Catppuccin theme (#2117)
Mocha for dark and Latte for light, mauve accent

(cherry picked from commit 77eb55874278b7740d43b9144d9253a075cc593c)
2025-11-16 12:37:36 +05:45
Mend Renovate 717240f53c Update dependency com.android.tools.build:gradle to v8.10.1 (#2148)
(cherry picked from commit e1f6d143933ebf445adec5e1b87086551c2dca84)
2025-11-15 18:19:53 +05:45
AntsyLich 5156248a96 Add migration config screen to select and prioritize target sources (#2144)
(cherry picked from commit 2e180005a01f633ad7fafc5cfb3079f0bc858448)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt
2025-11-15 18:19:53 +05:45
Mend Renovate e074df469e Update markdown to v0.35.0 (#2143)
(cherry picked from commit 0f59fc1dd42f770a0e7a9359a3eb8baebc826246)
2025-11-15 18:19:53 +05:45
Mend Renovate d1277ecb02 Update dependency com.diffplug.spotless:spotless-plugin-gradle to v7.0.4 (#2146)
(cherry picked from commit d1055475e23a3e20ef41b228056611be82e62515)
2025-11-15 18:19:53 +05:45
Mend Renovate 8bcc235490 Update dependency com.squareup.okio:okio to v3.12.0 (#2147)
(cherry picked from commit 32470657ddcb6976bb4c8f852ec19fe1d4dfb542)
2025-11-15 18:19:53 +05:45
Mend Renovate aaa7171c10 Update dependency me.zhanghai.android.libarchive:library to v1.1.5 (#2142)
(cherry picked from commit 158896cfa9314b89848d4d45b3042b80cefb127e)
2025-11-15 18:19:53 +05:45
Jobobby04 887311b440 Fix notification settings under api 26 2025-06-16 14:44:30 -04:00
Jobobby04 1c90aac059 Add id to staff-edges 2025-06-16 11:28:31 -04:00
Jobobby04 3ad9765dcf Remove +1 in Page Layout reader settings 2025-06-10 14:40:28 -04:00
Jobobby04 cc934607c8 SpotlessApply 2025-05-24 21:07:29 -04:00
Weblate (bot) 5074e68b9c Translations update from Hosted Weblate (#1442)
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/my/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/vi/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/de/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/fr/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/id/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/it/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ja/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ne/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/uk/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/zh_Hant/
Translation: Mihon/TachiyomiSY
Translation: Mihon/TachiyomiSY Plurals

Co-authored-by: Alex Maryson Jr <akamar87@gmail.com>
Co-authored-by: B4LiN7 <87660017+B4LiN7@users.noreply.github.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: FateXBlood <fatexblood@gmail.com>
Co-authored-by: Frosted <frosted@users.noreply.hosted.weblate.org>
Co-authored-by: Hualiang <642615676@qq.com>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: Kosťantin Horovij <lg096066587039@gmail.com>
Co-authored-by: Lapis (Bas77) <sebastianramli77@gmail.com>
Co-authored-by: LordTenebrous <danielmorenoperez836@gmail.com>
Co-authored-by: Mohamed kh <mohamedkhamekhami@gmail.com>
Co-authored-by: Sky children of the Light <tu25261@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: ZerOriSama <godarms2010@live.com>
Co-authored-by: akir45 <akkn0708@gmail.com>
Co-authored-by: edgole <test.backache009@aleeas.com>
Co-authored-by: fl0k1 <michele.carnova@gmail.com>
Co-authored-by: naikhon <naikhon5@gmail.com>
Co-authored-by: qaugji <asteeky9@gmail.com>
Co-authored-by: ɴᴇᴋᴏ <s99095lkjjim@gmail.com>
Co-authored-by: Георгій Обушенков <heorhii.obushenkov@gmail.com>
Co-authored-by: ابومسلم <linuxmint1978@gmail.com>
2025-05-24 20:47:35 -04:00
多能豆 ade41f113d Fix E-Hentai Jump/Seek match for detailed date (#1450) 2025-05-24 20:44:55 -04:00
Jobobby04 95dc82594f More guards against edited data 2025-05-24 20:44:27 -04:00
NGB-Was-Taken 80e585fa91 Change log file extension to .txt (#1449) 2025-05-24 20:19:17 -04:00
Jobobby04 9f110f9db8 Bump version so migation actually runs 2025-05-24 20:18:14 -04:00
NGB-Was-Taken 71470b9e02 Remove the unused mark duplicate as read preference. (#1448)
* Remove the unused mark duplicate as read preference.

* Migrate the old preference to new preference
2025-05-24 20:16:44 -04:00
renovate[bot] 4fd24accac Update dependency net.zetetic:sqlcipher-android to v4.9.0 (#1447)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-24 20:15:17 -04:00
NGB-Was-Taken 31312fecac Fixes screen staying on in library tab. (#1451) 2025-05-24 20:13:43 -04:00
Mend Renovate b80d057922 Update dependency androidx.compose:compose-bom to v2025.05.01 (#2133)
(cherry picked from commit 92b376d9af93da988b695c36c4775d5e6947c048)
2025-05-24 20:12:57 -04:00
Mend Renovate f01d8bc835 Update dependency gradle to v8.14.1 (#2138)
(cherry picked from commit 1a2f09a622017dc5b201eadc6acc667487cf3d4d)
2025-05-24 20:12:48 -04:00
Jobobby04 ddffe71a22 SpotlessApply 2025-05-24 20:12:38 -04:00
AntsyLich 649a19ec57 Fix content cut off in home screen
Closes #2141

(cherry picked from commit 209e982fe4f1da5d1d49cfbfdd178625ee3c70f4)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt
2025-05-24 20:10:18 -04:00
Mend Renovate e82fd99a09 Update plugin org.gradle.toolchains.foojay-resolver-convention to v1 (#2130)
(cherry picked from commit 0109102901f942502807e2a7a9f1a58f951c763f)
2025-05-24 20:09:57 -04:00
Mend Renovate 67a9b8e2a0 Update dependency com.pinterest.ktlint:ktlint-cli to v1.6.0 (#2129)
(cherry picked from commit 4117a5167470ae36b4172721b4e4d40373f2a3c6)
2025-05-24 20:09:51 -04:00
Mend Renovate 2000f947c3 Update xml.serialization.version to v0.91.1 (#2112)
(cherry picked from commit 4090a61d08964a8e82f943ac4eb81a52c110005f)
2025-05-24 20:09:45 -04:00
Jobobby04 f992ada0a8 SpotlessApply 2025-05-16 21:16:09 -04:00
Jobobby04 f876cdb037 Use MediaServer in Json for NHentai
Co-authored-by: 4521 <18432684+az4521@users.noreply.github.com>
2025-05-16 20:34:03 -04:00
Jobobby04 f919d42370 Hello Discord 2025-05-16 12:02:37 -04:00
Mend Renovate ab5ff00c39 Update kotlin monorepo to v2.1.21 (#2102)
(cherry picked from commit 625c85cbd68086d605553b2d5ab9cc9cc9460688)
2025-05-15 13:50:17 -04:00
Mend Renovate 422738af56 Update dependency org.jetbrains.kotlinx:kotlinx-collections-immutable to v0.4.0 (#2104)
(cherry picked from commit 737ceeea576074cffcd2e96933aceffdbcc0a03a)
2025-05-15 13:50:08 -04:00
Mend Renovate 81751fc9ce Update dependency io.coil-kt.coil3:coil-bom to v3.2.0 (#2101)
(cherry picked from commit 7933c9eeb7b28ecc2ae31fa337654a80f2371b85)
2025-05-15 13:49:58 -04:00
Jobobby04 9b6c5effc9 Minor refactors 2025-05-15 13:38:03 -04:00
Jobobby04 129841d5c2 SpotlessApply and up version code due to database migration 2025-05-11 20:23:10 -04:00
AntsyLich 24d2460697 Disable reader's 'Keep screen on' setting by default (#2095)
(cherry picked from commit f0de8f973b331ebad6e1844aea7864f97f237941)

# Conflicts:
#	CHANGELOG.md
2025-05-11 20:18:36 -04:00
AntsyLich ef3d9626c1 Add full predictive back support (#2085)
Co-authored-by: p
(cherry picked from commit c12bdbae8e7bc14da8966e45a3c450913e32129f)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt
2025-05-11 20:18:11 -04:00
Mend Renovate 5c7b3c6c3b Update markdown to v0.34.0 (#2086)
(cherry picked from commit 33d407ee9c2196f25aaee0978b6808b9c393841e)
2025-05-11 20:10:50 -04:00
AntsyLich 6257888735 Update voyager to v1.1.0-beta03 (#2087)
(cherry picked from commit ef8c3ca119dfbdfcb0588bde080a7c6203133024)
2025-05-11 20:10:43 -04:00
FlaminSarge f5e714f794 Add advanced option to always update manga title from source (#1182)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 8b45ef0e5d5d368e0925df9816ae423defaed4d9)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/domain/manga/interactor/UpdateManga.kt
#	app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
2025-05-11 20:10:28 -04:00
AwkwardPeak7 3091f63504 Fix pressing Enter while searching also triggering navigation back on physical keyboards (#2077)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 86ebf5581598f28feab4090ac3bf627f54b511d7)

# Conflicts:
#	CHANGELOG.md
2025-05-11 20:00:59 -04:00
Mend Renovate 39755cccdc Update lifecycle.version to v2.9.0 (#2080)
(cherry picked from commit ddf282b10364fc71c5af4cfe77684a8ac0beaa5c)
2025-05-11 20:00:40 -04:00
Mend Renovate 9caf706ca3 Update sqlite to v2.5.1 (#2078)
(cherry picked from commit 744b809d458ba1bfee923f76493505768c261231)
2025-05-11 20:00:32 -04:00
Mend Renovate 6ba6a7c8d9 Update dependency androidx.compose:compose-bom to v2025.05.00 (#2079)
(cherry picked from commit cd2ce44efafbaf19e43ba342d417e4d553a8a0d7)
2025-05-11 20:00:26 -04:00
Mend Renovate 0a4a0e4c4c Update dependency com.android.tools.build:gradle to v8.10.0 (#2072)
(cherry picked from commit cae7c3dc588055dee5c9a99c331710e038b17794)
2025-05-11 20:00:19 -04:00
Mend Renovate b48d1e521a Update aboutlib.version to v12.1.2 (#2073)
(cherry picked from commit c0074402e7cc5917272459e7ebfb269e11af4c76)
2025-05-11 20:00:08 -04:00
AntsyLich 211d090a2d Add autofill support to tracker login dialog and update processing text (#2069)
(cherry picked from commit 7deeabe844d41d2b5e918ad747ddd548163c9fe3)

# Conflicts:
#	CHANGELOG.md
2025-05-11 19:59:44 -04:00
AntsyLich b6e5943e15 Fix downloader stopping after failing to create download directory of a manga (#2068)
(cherry picked from commit 536393a6d9941fac282f10b825aa611b91e1fcdb)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt
2025-05-11 19:59:22 -04:00
AntsyLich 78f6a34339 Fix Pill not following the local text style
Closes #2009

(cherry picked from commit f8cb506137a3619f828dac94557b5448b2a7fa24)

# Conflicts:
#	CHANGELOG.md
2025-05-11 19:48:31 -04:00
AntsyLich de967ae149 Cleanup MarkdownRender
Co-authored-by: p
(cherry picked from commit 98230ed30f04fe754fd4bd407356c8c03d8d8719)
2025-05-11 19:48:04 -04:00
Mend Renovate 4d075ff190 Update dependency androidx.compose:compose-bom to v2025.04.01 (#2040)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit d721a4321bdc6fafdd32e7bfd451b61b2bdd66b7)
2025-05-11 19:47:58 -04:00
Mend Renovate 076e2961c6 Update aboutlib.version to v12.1.0 (#2052)
(cherry picked from commit 1ac4b72cfe09be10cbde45633fbd84ffd703fc70)
2025-05-11 19:47:52 -04:00
Mend Renovate 7149de1bc3 Update dependency io.mockk:mockk to v1.14.2 (#2057)
(cherry picked from commit 9331f2b93f907bf3f7c95eb850b5befce90b68c6)
2025-05-11 19:47:42 -04:00
Mend Renovate 091f2f583a Update dependency org.jsoup:jsoup to v1.20.1 (#2058)
(cherry picked from commit 99c2a999735e04d026b937b953cdd5f19b2e7b1f)
2025-05-11 19:47:36 -04:00
Mend Renovate 1c0ef2ca98 Update aboutlib.version (#2046)
(cherry picked from commit 001716e34b0c533cfd0318be5048d32bc107931b)
2025-05-11 19:47:29 -04:00
NarwhalHorns 2a845bd7b5 Fix empty layout not appearing in browse source screen in some cases (#2043)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 37e19edf8a5f6a15a95f160390cbcf22d8133380)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt
2025-05-11 19:47:22 -04:00
AntsyLich afe326006f Switch default user agent to Android Chrome (#2048)
(cherry picked from commit 8b7f35598833917c89f8ae53cca10578fd880d67)

# Conflicts:
#	CHANGELOG.md
2025-05-11 19:44:45 -04:00
Mend Renovate 4b80154b09 Update dependency com.google.firebase:firebase-bom to v33.13.0 (#2047)
(cherry picked from commit 0b777336739b6f91ebfe45772c2f139d4e60c555)
2025-05-11 19:44:30 -04:00
Mend Renovate d6b230b8f1 Update dependency androidx.work:work-runtime to v2.10.1 (#2041)
(cherry picked from commit 9be558d6c0e0e81c1f37fc3d01b19872879f2daa)
2025-05-11 19:44:23 -04:00
Mend Renovate d02a2cbd29 Update dependency gradle to v8.14 (#2049)
(cherry picked from commit 0c8c5dbba6bf60fbc4e3e4dad912e2be8ce25a79)
2025-05-11 19:44:17 -04:00
AntsyLich 17d225b0d9 Fix crash when trying use source sort filter without a pre-selection (#2036)
(cherry picked from commit 1c982c2a01c1bba5ec4a955c9bf61cb346c752e7)

# Conflicts:
#	CHANGELOG.md
2025-05-11 19:44:10 -04:00
Mend Renovate 6cbbaccaf4 Update dependency com.android.tools.build:gradle to v8.9.2 (#2033)
(cherry picked from commit eeab61fc94e1a9486eba42fd79a8169473ab6fde)
2025-05-11 19:43:51 -04:00
Mend Renovate 94cc4027c2 Update aboutlib.version to v12 (major) (#2016)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit a036407c75d83ef0ba5350cb1825b615361316c3)
2025-05-11 19:43:44 -04:00
AntsyLich 03ae6ed2b0 Update dependency com.mohamedrejeb.richeditor:richeditor-compose to v1.0.0-rc11
(cherry picked from commit 615d93f780b415e505fb2159f4cbdf4694749f82)
2025-05-11 19:43:35 -04:00
AntsyLich fa8c232a69 Fix content under source browse screen top appbar is interactable (#2026)
(cherry picked from commit 9750c1e4bd6b931e71b7b348abbe2638a8cf317b)

# Conflicts:
#	CHANGELOG.md
2025-05-11 19:43:27 -04:00
Secozzi 0386ce998a Update markdown to 0.33.0 and tweak visuals (#2024)
- Update markdown to 0.33.0
- Use github flavour for github changelog
- Fix bullet list alignment

(cherry picked from commit e2915a1f69340cad515962de8a0b9d11ecff8d42)

# Conflicts:
#	CHANGELOG.md
2025-05-11 19:43:12 -04:00
AwkwardPeak7 273f73e9a2 Remove Okhttp networking from WebView Screen (#2020)
(cherry picked from commit df2b4c754bab9dd96fe2199b9f6df62d87b7175e)

# Conflicts:
#	CHANGELOG.md
2025-05-11 19:42:33 -04:00
KokaKiwi 5e20e54649 Fix reader not updating progress (#2007)
The condition for updating progress is wrong since fefa8f84982b537ca930438f7976087844d5bb9c

(cherry picked from commit 6632a122288cc9733844c8dce1ee51b520c0a32e)
2025-05-11 19:42:06 -04:00
Joseph Madamba b8c3f9dcce Update Facebook and Reddit icon (#1994)
(cherry picked from commit 0cb1925cf158155665f3173bccb93f39d84b71e0)

# Conflicts:
#	CHANGELOG.md
2025-05-11 19:41:59 -04:00
ArthurKun 802b6508fa Replace Modifier.composed with Composable Modifier (#1959)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit a31b3b7bbf2c5164baf76ac4b36f1d27c5d43135)
2025-05-11 19:41:36 -04:00
AwkwardPeak7 b6409b05e7 Include source headers when opening failed images from reader (#2004)
(cherry picked from commit fea85241afac5a849aa418d01710f5cdc0c25b54)

# Conflicts:
#	CHANGELOG.md
2025-05-11 19:41:27 -04:00
Secozzi 129f355b9c Use simpler markdown flavour in manga description (#2000)
(cherry picked from commit e273a26c9b7f0a9dd9f8847cfc65e69453fa5905)

# Conflicts:
#	CHANGELOG.md
2025-05-11 19:41:01 -04:00
AwkwardPeak7 9ffacb80e3 Fix duplicate requests in WebView due to empty reasonPhrase (#2003)
(cherry picked from commit 818e6931c6bc89e0bb111e77418542a88f8db37c)

# Conflicts:
#	CHANGELOG.md
2025-05-11 19:40:27 -04:00
AwkwardPeak7 85726db45d Add option to keep read manga when clearing database (#1979)
(cherry picked from commit ecc6ede0815a89b7f8288e47c607c57bacc47e71)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt
#	data/src/main/sqldelight/tachiyomi/data/mangas.sq
2025-05-11 19:40:06 -04:00
AwkwardPeak7 746b1b051c Surface image loading error in Reader (#1981)
(cherry picked from commit fefa8f84982b537ca930438f7976087844d5bb9c)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt
2025-05-11 19:36:53 -04:00
AwkwardPeak7 59887eed80 Change Page.State to sealed interface (#1988)
(cherry picked from commit f1e2efcb37e2c623b769e979fa1c7e9e5ad7117d)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ArchivePageLoader.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt
2025-05-11 19:35:07 -04:00
Mend Renovate b8267f1fef Update dependency androidx.core:core-ktx to v1.16.0 (#1990)
(cherry picked from commit 180318f57d82529c0040a2d310a679a493d2b9f3)
2025-05-11 19:12:53 -04:00
Mend Renovate 8c62bb6d6d Update plugin org.gradle.toolchains.foojay-resolver-convention to v0.10.0 (#1992)
(cherry picked from commit ed749de8066ac056bc0caa505739f1b7e45dea48)
2025-05-11 19:12:46 -04:00
Mend Renovate 751e04b87f Update markdown to v0.33.0-rc01 (#1999)
(cherry picked from commit bb33b0029ef630a609dd160ed278d5e8274f316d)
2025-05-11 19:12:40 -04:00
Mend Renovate 9f0161ed70 Update dependency androidx.compose:compose-bom to v2025.04.00 (#1989)
(cherry picked from commit 3a19e449b13c078d7c0b5762f0ba84a162cc7f71)
2025-05-11 19:12:33 -04:00
Mend Renovate 7b2c341386 Update dependency com.squareup.okio:okio to v3.11.0 (#1991)
(cherry picked from commit 818edf2776fd7706cd1a829a6bdc963a436f06d6)
2025-05-11 19:12:22 -04:00
Mend Renovate c8b29ecf1c Update dependency io.mockk:mockk to v1.14.0 (#1987)
(cherry picked from commit 47d2646751d40a155724f658ba6461e8a2d57aad)
2025-05-11 19:12:15 -04:00
Mend Renovate c30381c6ec Update dependency androidx.sqlite:sqlite-framework to v2.5.0 (#1986)
(cherry picked from commit a1a7d67afb16cc206f1d6c6d47da52b7705a417d)
2025-05-11 19:12:08 -04:00
Mend Renovate f489531543 Update dependency com.diffplug.spotless:spotless-plugin-gradle to v7.0.3 (#1977)
(cherry picked from commit 2090a380e0e9ab4f74fd2e5e74e6c2807e96f23d)
2025-05-11 19:12:01 -04:00
Mend Renovate 4bbe795626 Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-bom to v1.10.2 (#1978)
(cherry picked from commit 8e5cfe9d0acf854c9387ed3405f0fe3cc9140733)
2025-05-11 19:11:54 -04:00
Cuong-Tran 8aa3dca95f Fix navigation issue after migrating a duplicated entry from History tab
(cherry picked from commit d9c4b56336c21db96a835630a48c46ee7a480342)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt
2025-05-11 19:11:40 -04:00
NarwhalHorns 5e0f730159 Display total chapters on duplicates list items (#1963)
(cherry picked from commit 12abd9938b7c235d6a1c02391624703476c1f339)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt
#	data/src/main/java/tachiyomi/data/manga/MangaMapper.kt
2025-05-11 19:03:47 -04:00
Mend Renovate f1aed0d8b9 Update dependency androidx.compose:compose-bom to v2025.03.01 (#1927)
(cherry picked from commit c1225a5ef96f5f77bd337e0481935cbef75cc711)
2025-05-11 18:57:57 -04:00
AntsyLich a3465c31c9 Update non-library manga data when browsing (#1967)
(cherry picked from commit a594ad392d4793f3a5cb2b709d29b2feab6120d3)

# Conflicts:
#	CHANGELOG.md
2025-05-11 18:57:50 -04:00
Mend Renovate 053c48613b Update xml.serialization.version to v0.91.0 (#1956)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 80de0328190bd3adac8e034c420e6a91d3d7cfc9)
2025-05-11 18:55:30 -04:00
NarwhalHorns 615adc567b Display all similarly named duplicates in duplicate manga dialogue (#1861)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 0d35b6fdafbf5451a2743ea9bcfc735bf49374a7)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt
2025-05-11 18:14:26 -04:00
AntsyLich b0f645d906 Deduplicate entries when browsing (#1957)
(cherry picked from commit f81da3dcce9afba883b6a3accdd3bf4ea21cfa81)

# Conflicts:
#	CHANGELOG.md
#	data/src/main/java/tachiyomi/data/source/SourcePagingSource.kt
2025-05-11 18:05:21 -04:00
Mend Renovate 023c78d0e8 Update serialization.version to v1.8.1 (#1953)
(cherry picked from commit 2ce9fa0271c16449475adb07eb6338a497e11e3c)
2025-05-11 17:58:51 -04:00
AntsyLich 824550175a Remove feature flag from Nord theme (#1951)
(cherry picked from commit 5d2110f3fb1aa6b15f62af0dcd3378cfbe475b7a)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/domain/ui/model/AppTheme.kt
2025-05-11 17:32:37 -04:00
Secozzi ad53c0de83 Add markdown support for manga descriptions (#1948)
(cherry picked from commit 4e68339783b47b0780e1b9aee643404339d35ed1)

# Conflicts:
#	CHANGELOG.md
#	gradle/libs.versions.toml
2025-05-11 17:31:41 -04:00
AntsyLich c8039739d5 Significantly improve browsing speed (near instantaneous) (#1946)
(cherry picked from commit c8ffabc84a096207c1997ab69fc86176f3b53f00)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/domain/manga/model/Manga.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt
#	data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt
#	data/src/main/java/tachiyomi/data/source/SourcePagingSource.kt
#	data/src/main/sqldelight/tachiyomi/data/mangas.sq
#	domain/src/main/java/tachiyomi/domain/manga/interactor/NetworkToLocalManga.kt
#	domain/src/main/java/tachiyomi/domain/manga/repository/MangaRepository.kt
#	domain/src/main/java/tachiyomi/domain/source/repository/SourceRepository.kt
2025-05-11 17:24:33 -04:00
Bartu Özen 26674136e6 Fix app bar action tooltips blocking clicks (#1928)
(cherry picked from commit 77e79233ab054d16bb5dc04a040d0d86a326136f)

# Conflicts:
#	CHANGELOG.md
2025-05-11 16:50:49 -04:00
AntsyLich 9972fa1053 Fix mark existing duplicate read chapters as read option not working in some cases (#1944)
(cherry picked from commit 8a21148578af3c1538e9ab2b1fe5bdf05b4e35c9)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
2025-05-11 16:50:02 -04:00
AntsyLich ae3f974d8c Fix user notes not restoring when manga doesn't exist in DB (#1945)
(cherry picked from commit e91db86faef8d6b17961a1b73fbf07f0d2c8975d)

# Conflicts:
#	CHANGELOG.md
2025-05-11 16:44:09 -04:00
Mend Renovate 027f179a4b Update kotlin monorepo to v2.1.20 (#1883)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 556290f2d35f739bb4bddc012739acf10b92708d)
2025-05-11 16:43:30 -04:00
Mend Renovate e80cb1795e Update dependency com.android.tools.build:gradle to v8.9.1 (#1913)
(cherry picked from commit 8b947919acde9932808e666b8bb6a2df9613a67f)
2025-05-11 16:43:22 -04:00
Mend Renovate 66fe599498 Update dependency androidx.benchmark:benchmark-macro-junit4 to v1.3.4 (#1926)
(cherry picked from commit b62a9b40eb8ecb4c0c9c861d66c9afc427bc6bbe)
2025-05-11 16:43:15 -04:00
AntsyLich c9e6e321b3 Update editor config for 'sq' and 'sqm' file [skip ci]
(cherry picked from commit a6b532ee57d24e1ad83f1daea415cad1f313b49c)
2025-05-11 16:43:02 -04:00
kunet fb3c996904 Add user manga notes (#428)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 8fbe630308b962043c7b59422878c94f80156e9f)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt
#	app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt
#	app/src/main/java/eu/kanade/presentation/manga/components/MangaToolbar.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/MigrationFlags.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt
#	data/src/main/sqldelight/tachiyomi/migrations/5.sqm
#	domain/src/main/java/tachiyomi/domain/manga/model/MangaUpdate.kt
2025-05-11 16:42:33 -04:00
perokhe 70b25825ec Fix page number not appearing when opening chapter (#1936)
(cherry picked from commit 132d77aa9947f891f90f1afcdcb24e20ce515438)

# Conflicts:
#	CHANGELOG.md
2025-05-11 16:26:43 -04:00
Cuong-Tran 290e8f5a1e Fix benchmark build (#1938)
(cherry picked from commit b00bbe91beb942f2ac18765be6c78b6f318cc66d)
2025-05-11 16:25:36 -04:00
Jayman Rana f6b1440bf2 Fix backup sharing from notifications not working when app is in background (#1929)
(cherry picked from commit 3e5d3d099fed5feb6a6807196bea5fed72973fe9)

# Conflicts:
#	CHANGELOG.md
2025-05-11 16:25:27 -04:00
perokhe 77a4919656 Fix next chapter button occasionally jumping to the last page of the current chapter (#1920)
(cherry picked from commit 941dde341eb11703eadae543f351c9284617541c)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt
2025-05-11 16:25:14 -04:00
Ian Hunter 84d901b8a3 Add more Kaomoji for empty/error screens (#1909)
(cherry picked from commit d4aaf6521e86e8509d3971854c46b8520cef7f59)

# Conflicts:
#	CHANGELOG.md
2025-05-11 16:22:24 -04:00
Mend Renovate d27ed2580f Update dependency com.google.firebase:firebase-bom to v33.11.0 (#1890)
(cherry picked from commit f7046a503bea421a0310f8d2064888aea0a07d11)
2025-05-11 16:22:08 -04:00
MajorTanya 87ea971be0 Fix Bangumi search including novels (#1885)
(cherry picked from commit 953c4e7bc056ed8b9eebe1b111677a4616c4d694)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/dto/BGMSearch.kt
2025-05-11 16:22:01 -04:00
Weblate (bot) 91ea70b335 Translations update from Hosted Weblate (#1877)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/uk/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: scb261 <scb261261@gmail.com>
(cherry picked from commit 1d6dc1e8b0de08af1370c04eff480e4555095c55)
2025-05-11 16:21:27 -04:00
AntsyLich 2e94e152c2 Use current time as build time for preview builds (#1876)
(cherry picked from commit 935f1fcf3f8e4f9da4774d932b65ae77b44cc773)

# Conflicts:
#	app/build.gradle.kts
2025-05-11 16:21:22 -04:00
Weblate (bot) eece46fb0f Translations update from Hosted Weblate (#1550)
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/bn/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/fr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/gl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/he/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/hi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/hu/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/kk/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/pl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ro/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/am/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/as/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/be/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/bg/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/bn/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ca/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ceb/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cs/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eo/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/gl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ka/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/kk/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/km/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/kn/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/lt/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ml/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/mr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/my/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nn/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pl/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ro/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sa/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sah/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sc/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sk/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sq/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sv/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ta/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/th/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/uz/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/vi/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/
Translation: Mihon/Mihon
Translation: Mihon/Mihon Plurals

Co-authored-by: Abay Emes <abayemes@gmail.com>
Co-authored-by: Acelith <joel.jon@moix.me>
Co-authored-by: Ahmad Ansori Palembani <palembani@gmail.com>
Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Co-authored-by: Akhil Raj <akhilakae07@gmail.com>
Co-authored-by: C201 <derasetad@gmail.com>
Co-authored-by: Champ0999 <il.migliore0999@gmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Doministo <doministo@seznam.cz>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: Eji-san <ejierubani@gmail.com>
Co-authored-by: FateXBlood <fatexblood@gmail.com>
Co-authored-by: Frosted <frosted@users.noreply.hosted.weblate.org>
Co-authored-by: Harshit Prajapati <harshitprajapati7666@gmail.com>
Co-authored-by: Hasanur Rahman Biplob <hrbiplob10@gmail.com>
Co-authored-by: Horace Johnson <horacejohnson99@gmail.com>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: Itsmechinmoy <itsmechinmoy@users.noreply.hosted.weblate.org>
Co-authored-by: Jakub Fabijan <jakubfabijan@tuta.io>
Co-authored-by: Kerim Demirkaynak <aschannel111@gmail.com>
Co-authored-by: Koanrade <konrad.nowicki91@gmail.com>
Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com>
Co-authored-by: MD_Abdulla072 <md.abdullacse20@gmail.com>
Co-authored-by: Matyáš Caras <matyas@caras.wtf>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Mochammad Nopal Attasya <meleboy22@gmail.com>
Co-authored-by: Nguyễn Trung Đức <vaicato16@gmail.com>
Co-authored-by: NormalRandomPeople <normal.scribe833@silomails.com>
Co-authored-by: Pecs1 <mynameisnoname897@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Piyoka Smith <piyoka5697@ahaks.com>
Co-authored-by: Reza Almanda <rezaalmanda27@gmail.com>
Co-authored-by: Rom Savidor <romsavidor@gmail.com>
Co-authored-by: Saft Octavian <saftoctavian@gmail.com>
Co-authored-by: Shiratori <kuromaruhatake@gmail.com>
Co-authored-by: Siebrenvde <siebren@siebrenvde.dev>
Co-authored-by: Sixten Lund <arbitraryindices@users.noreply.hosted.weblate.org>
Co-authored-by: Sorawit Jannareubate <moszaduck007@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: Temuri Doghonadze <temuri.doghonadze@gmail.com>
Co-authored-by: TheKingTermux <50316075+TheKingTermux@users.noreply.github.com>
Co-authored-by: ZerOriSama <godarms2010@live.com>
Co-authored-by: abc0922001 <abc0922001@hotmail.com>
Co-authored-by: dianisaac <muhandreop@gmail.com>
Co-authored-by: f0roots <f0rootss@gmail.com>
Co-authored-by: kevans <albapazpi@gmail.com>
Co-authored-by: staxhinho <staxhinho@gmail.com>
Co-authored-by: ɴᴇᴋᴏ <s99095lkjjim@gmail.com>
Co-authored-by: தமிழ்நேரம் <anishprabu.t@gmail.com>
(cherry picked from commit b3726572381abfa1eaaf31cf7f3b685b390f60bf)

# Conflicts:
#	i18n/src/commonMain/moko-resources/de/strings.xml
#	i18n/src/commonMain/moko-resources/kk/strings.xml
#	i18n/src/commonMain/moko-resources/lt/strings.xml
#	i18n/src/commonMain/moko-resources/ru/strings.xml
2025-05-11 16:19:26 -04:00
AntsyLich 34736bc26e For release builds use last commit time as build time (#1873)
(cherry picked from commit dae7d179662ff6d6654e7c10e57f1aeeaf579de8)

# Conflicts:
#	app/build.gradle.kts
2025-05-11 16:17:52 -04:00
renovate[bot] 82cf385f9d Update dependency net.zetetic:sqlcipher-android to v4.8.0 (#1429)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-11 16:14:08 -04:00
renovate[bot] 682dea2fb1 Update koin to v4.0.4 (#1428)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-11 16:12:00 -04:00
renovate[bot] c10588d183 Update tachiyomiorg/issue-moderator-action action to v2.6.1 (#1388)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-11 16:11:44 -04:00
Jobobby04 6db1637770 Fix library flags test 2025-05-11 14:57:14 -04:00
Jobobby04 5742d2e3fe Release 1.12.0 2025-05-11 14:24:22 -04:00
BrutuZ c2d0308ac0 Populate Author field and clear Description on a couple of delegated (#1432) 2025-05-11 14:16:43 -04:00
Callum Wong 84c7da5a7d Add QR code scan button for sync API key (#1430)
* Add dependency com.journeyapps:zxing-android-embedded:4.3.0

* Add widget parameter to EditTextPreferenceWidget

* Add QR code scanner icon button to sync API key preference which launches a ScanContract

* Remove screenOrientation property from CaptureActivity manifest

* Allow scanning both normal and inverted codes

* store values and make code more concise

Co-authored-by: jobobby04 <jobobby04@users.noreply.github.com>

* Import local context

---------

Co-authored-by: jobobby04 <jobobby04@users.noreply.github.com>
2025-05-11 14:15:05 -04:00
cfouche 274350c118 Change for t1 for better hit rate (#1425) 2025-05-11 14:12:44 -04:00
Weblate (bot) 6bd978eef1 Translations update from Hosted Weblate (#1422)
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/vi/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ar/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/de/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/fr/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/it/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/uk/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/zh_Hant/
Translation: Mihon/TachiyomiSY
Translation: Mihon/TachiyomiSY Plurals

Co-authored-by: Alex Maryson Jr <akamar87@gmail.com>
Co-authored-by: B4LiN7 <87660017+B4LiN7@users.noreply.github.com>
Co-authored-by: Frosted <frosted@users.noreply.hosted.weblate.org>
Co-authored-by: Hualiang <642615676@qq.com>
Co-authored-by: Kosťantin Horovij <lg096066587039@gmail.com>
Co-authored-by: Sky children of the Light <tu25261@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: ZerOriSama <godarms2010@live.com>
Co-authored-by: edgole <test.backache009@aleeas.com>
Co-authored-by: fl0k1 <michele.carnova@gmail.com>
Co-authored-by: ɴᴇᴋᴏ <s99095lkjjim@gmail.com>
Co-authored-by: Георгій Обушенков <heorhii.obushenkov@gmail.com>
Co-authored-by: ابومسلم <linuxmint1978@gmail.com>
2025-05-11 13:49:55 -04:00
Weblate (bot) e0f40fad8c Translations update from Hosted Weblate (#1408)
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy-plurals/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/as/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/de/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/es/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/fil/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/id/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/it/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ja/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ne/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/ru/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/tr/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/vi/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/mihon/tachiyomisy/zh_Hant/
Translation: Mihon/TachiyomiSY
Translation: Mihon/TachiyomiSY Plurals

Co-authored-by: Champ0999 <il.migliore0999@gmail.com>
Co-authored-by: Corrado Belmonte <corrado.spam@gmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Eji-san <ejierubani@gmail.com>
Co-authored-by: FateXBlood <fatexblood@gmail.com>
Co-authored-by: Frosted <frosted@users.noreply.hosted.weblate.org>
Co-authored-by: Infy's Tagalog Translations <ced.paltep10@gmail.com>
Co-authored-by: Itsmechinmoy <itsmechinmoy@users.noreply.hosted.weblate.org>
Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com>
Co-authored-by: Nam Pai <namhg911@gmail.com>
Co-authored-by: Renan Sarto <app@renansg.com>
Co-authored-by: Sepultrex <sepultrex@gmail.com>
Co-authored-by: Shiratori <kuromaruhatake@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: Tim Schneeberger <tim.schneeberger@outlook.de>
Co-authored-by: ZerOriSama <godarms2010@live.com>
Co-authored-by: dianisaac <muhandreop@gmail.com>
Co-authored-by: quangpao <ddquangbao@gmail.com>
Co-authored-by: ɴᴇᴋᴏ <s99095lkjjim@gmail.com>
2025-03-18 18:17:56 -04:00
renovate[bot] 5647665782 Update dependency com.google.oauth-client:google-oauth-client to v1.39.0 (#1410)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-18 18:16:40 -04:00
Jobobby04 df99e7ee49 SpotlessApply 2025-03-18 18:04:23 -04:00
cfouche dbd4437474 Update base URL and host for Pururin to pururin.me (#1415) 2025-03-18 17:43:03 -04:00
AntsyLich 9c198d0c33 Seperate mark duplicate read chapters as read behaviors as options (#1870)
(cherry picked from commit 8a3b6107755c768924a31c2b58d705296133839c)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt
#	app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
2025-03-18 17:37:58 -04:00
AntsyLich d62a8a138c Add back option to hide unread chapter badge in library (#1871)
(cherry picked from commit ac432e2e941f4689caad246bab6aa7d303c83bfa)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt
2025-03-18 17:31:02 -04:00
Cuong-Tran f8a57ec98c Add back build tools version to sign-android-release (#1842)
(cherry picked from commit 7028b8673a6b78dc6ccc19f5b3242bf1b37ca908)
2025-03-18 17:29:07 -04:00
Mend Renovate aa6339df06 Update dependency org.jsoup:jsoup to v1.19.1 (#1822)
(cherry picked from commit 2dc8cf000b871b8ffe07016d76a4bc7114d6ea49)
2025-03-18 17:28:57 -04:00
Mend Renovate 3fbbfbd9cb Update dependency androidx.compose:compose-bom to v2025.03.00 (#1857)
(cherry picked from commit f76a3ad15ad3954c512c20d99337a207f2e2d37a)
2025-03-18 17:28:49 -04:00
AntsyLich 31d6bf1967 Remove closed issue/pr auto lock workflow [skip ci]
(cherry picked from commit f33aa1ac9223393d0921df2902e4b59589ab7d2d)

# Conflicts:
#	.github/workflows/lock.yml
2025-03-18 17:28:41 -04:00
MajorTanya 226b3f2ff4 Add app ID to debug info (#1847)
This will avoid the need to know which forks has which version numbers
and avoid confusion in support.

(cherry picked from commit eddf07f9ac31bab57d06515e42df9c854bc50eed)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt
2025-03-18 17:27:55 -04:00
AntsyLich ac8dab75fe Make option to mark duplicate chapter as read apply when reading (#1839)
(cherry picked from commit 22b5fb58ff8e89635d646f8fa29256b53c41ffbf)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
2025-03-18 17:27:12 -04:00
AntsyLich aad2bf4645 Make more sliders discrete and ensure they don't look out of place (#1840)
Also cleanup the underlying code

(cherry picked from commit 4f06c1cc09d15245b26b8a862738cb6a859fedcc)

# Conflicts:
#	CHANGELOG.md
2025-03-18 17:24:05 -04:00
AntsyLich 7f71296e1c Change label of setting to always use SSIV in long strip reader (#1834)
(cherry picked from commit 85d168ed5e201134558cc843aba896306617c9ca)

# Conflicts:
#	CHANGELOG.md
2025-03-18 16:57:58 -04:00
AntsyLich 9137170fb8 Bump default user agent (#1833)
(cherry picked from commit d3691cc2563815490683cc69cbc3260e4561906c)

# Conflicts:
#	CHANGELOG.md
2025-03-18 16:57:37 -04:00
FlaminSarge 0af667c9aa Attempt to fix crash when migrating or removing entries from library (#1828)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit 563bc02113a5ebc53650fdfdd13f408284a0cdc8)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt
#	domain/src/main/java/tachiyomi/domain/manga/interactor/GetLibraryManga.kt
2025-03-18 16:57:00 -04:00
NarwhalHorns 8dc6a95ce6 Display staff information on Anilist tracker search results (#1810)
Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit b702603965044cfe3ee852f8d0c970b6eb93b97a)

# Conflicts:
#	CHANGELOG.md
2025-03-18 16:55:40 -04:00
Roshan Varughese 1eb64d117e Fix an issue where tracker reading progress is changed to a lower value (#1795)
(cherry picked from commit 2e2f1ed82d63a93ebf87ee8494434c1bad2e268c)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt
2025-03-18 16:55:21 -04:00
Mend Renovate 8f48a80bc4 Update dependency com.android.tools.build:gradle to v8.9.0 (#1824)
(cherry picked from commit b2765a00d285040531619a287d5144718959dd49)
2025-03-18 16:54:37 -04:00
NarwhalHorns e76dd7fab0 Update track search preview (#1825)
(cherry picked from commit 0e6d6c087e5a4d889b9153b390d8335d7add1e87)
2025-03-18 16:54:29 -04:00
Smol Ame b53a9ce5ae Tweak and adjust issue template (#1817)
Co-authored-by: BrutuZ <brutuz@users.noreply.github.com>
(cherry picked from commit 4f7122d6f09f87930ccd7dae7c557f4b236bbc4b)
2025-03-18 16:54:22 -04:00
Mend Renovate 952f26929c Update dependency io.mockk:mockk to v1.13.17 (#1786)
(cherry picked from commit b763d3e2c24caac6898981395aece2984b3e03a3)
2025-03-18 16:54:12 -04:00
767 changed files with 24708 additions and 10766 deletions
+6 -3
View File
@@ -7,7 +7,7 @@ indent_style = space
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.xml] [*.{xml,sq,sqm,aidl}]
indent_size = 4 indent_size = 4
# noinspection EditorConfigKeyCorrectness # noinspection EditorConfigKeyCorrectness
@@ -23,9 +23,14 @@ ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
ktlint_code_style = intellij_idea ktlint_code_style = intellij_idea
ktlint_function_naming_ignore_when_annotated_with = Composable ktlint_function_naming_ignore_when_annotated_with = Composable
ktlint_standard_class-signature = disabled ktlint_standard_class-signature = disabled
ktlint_standard_comment-wrapping = disabled
ktlint_standard_discouraged-comment-location = disabled ktlint_standard_discouraged-comment-location = disabled
ktlint_standard_function-expression-body = disabled ktlint_standard_function-expression-body = disabled
ktlint_standard_function-signature = disabled ktlint_standard_function-signature = disabled
ktlint_standard_type-argument-comment = disabled
ktlint_standard_type-parameter-comment = disabled
ktlint_standard_blank-line-between-when-conditions = disabled
# SY # SY
ktlint_standard_filename = disabled ktlint_standard_filename = disabled
ktlint_standard_argument-list-wrapping = disabled ktlint_standard_argument-list-wrapping = disabled
@@ -33,8 +38,6 @@ ktlint_standard_function-naming = disabled
ktlint_standard_property-naming = disabled ktlint_standard_property-naming = disabled
ktlint_standard_multiline-expression-wrapping = disabled ktlint_standard_multiline-expression-wrapping = disabled
ktlint_standard_string-template-indent = disabled ktlint_standard_string-template-indent = disabled
ktlint_standard_comment-wrapping = disabled
ktlint_standard_max-line-length = disabled ktlint_standard_max-line-length = disabled
ktlint_standard_type-argument-comment = disabled
ktlint_standard_value-argument-comment = disabled ktlint_standard_value-argument-comment = disabled
ktlint_standard_value-parameter-comment = disabled ktlint_standard_value-parameter-comment = disabled
+3
View File
@@ -1,5 +1,8 @@
blank_issues_enabled: false blank_issues_enabled: false
contact_links: contact_links:
- name: ❌ Help with Extensions
url: https://mihon.app/docs/faq/browse/extensions
about: For extension-related questions/issues
- name: 🖥️ Mihon website - name: 🖥️ Mihon website
url: https://mihon.app/ url: https://mihon.app/
about: Guides, troubleshooting, and answers to common questions about: Guides, troubleshooting, and answers to common questions
+3 -3
View File
@@ -53,7 +53,7 @@ body:
label: TachiyomiSY version label: TachiyomiSY version
description: You can find your TachiyomiSY version in **More → About**. description: You can find your TachiyomiSY version in **More → About**.
placeholder: | placeholder: |
Example: "1.11.0" Example: "1.12.0"
validations: validations:
required: true required: true
@@ -96,9 +96,9 @@ body:
required: true required: true
- label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/). - label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/).
required: true required: true
- label: I have updated the app to version **[1.11.0](https://github.com/jobobby04/tachiyomisy/releases/latest)**. - label: I have updated the app to version **[1.12.0](https://github.com/jobobby04/tachiyomisy/releases/latest)**.
required: true required: true
- label: I have filled out all of the requested information in this form, including specific version numbers. - label: I have filled out all of the requested information in this form, including specific version numbers.
required: true required: true
- label: I understand that extensions are unaffiliated to Mihon, and will not receive any help for any source and/or extension-related issues. - label: I understand that **TachiyomiSY does not have or fix any extensions**, and I **will not receive help** for any issues related to sources or extensions.
required: true required: true
+1 -1
View File
@@ -31,7 +31,7 @@ body:
required: true required: true
- label: I have written a short but informative title. - label: I have written a short but informative title.
required: true required: true
- label: I have updated the app to version **[1.11.0](https://github.com/jobobby04/tachiyomisy/releases/latest)**. - label: I have updated the app to version **[1.12.0](https://github.com/jobobby04/tachiyomisy/releases/latest)**.
required: true required: true
- label: I will fill out all of the requested information in this form. - label: I will fill out all of the requested information in this form.
required: true required: true
+4 -4
View File
@@ -12,22 +12,22 @@ jobs:
steps: steps:
- name: Clone repo - name: Clone repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Set up JDK - name: Set up JDK
uses: actions/setup-java@v4 uses: actions/setup-java@v5
with: with:
java-version: 17 java-version: 17
distribution: temurin distribution: temurin
- name: Set up gradle - name: Set up gradle
uses: gradle/actions/setup-gradle@v4 uses: gradle/actions/setup-gradle@v5
- name: Build app - name: Build app
run: ./gradlew spotlessCheck assembleDevDebug run: ./gradlew spotlessCheck assembleDevDebug
- name: Upload APK - name: Upload APK
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: TachiyomiSY-${{ github.sha }}.apk name: TachiyomiSY-${{ github.sha }}.apk
path: app/build/outputs/apk/dev/debug/app-dev-debug.apk path: app/build/outputs/apk/dev/debug/app-dev-debug.apk
+9 -30
View File
@@ -15,20 +15,20 @@ jobs:
steps: steps:
- name: Clone repo - name: Clone repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Setup Android SDK - name: Setup Android SDK
run: | run: |
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3" ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"
- name: Set up JDK - name: Set up JDK
uses: actions/setup-java@v4 uses: actions/setup-java@v5
with: with:
java-version: 17 java-version: 17
distribution: temurin distribution: temurin
- name: Set up gradle - name: Set up gradle
uses: gradle/actions/setup-gradle@v4 uses: gradle/actions/setup-gradle@v5
# SY --> # SY -->
- name: Write google-services.json - name: Write google-services.json
@@ -63,30 +63,18 @@ jobs:
alias: ${{ secrets.ALIAS }} alias: ${{ secrets.ALIAS }}
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
keyPassword: ${{ secrets.KEY_PASSWORD }} keyPassword: ${{ secrets.KEY_PASSWORD }}
env:
BUILD_TOOLS_VERSION: '35.0.1'
- name: Clean up build artifacts - name: Clean up build artifacts
run: | run: |
set -e set -e
mv app/build/outputs/apk/standard/release/app-standard-universal-release-unsigned-signed.apk TachiyomiSY.apk mv app/build/outputs/apk/standard/release/app-standard-universal-release-unsigned-signed.apk TachiyomiSY.apk
sha=`sha256sum TachiyomiSY.apk | awk '{ print $1 }'`
echo "APK_UNIVERSAL_SHA=$sha" >> $GITHUB_ENV
mv app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release-unsigned-signed.apk TachiyomiSY-arm64-v8a.apk mv app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release-unsigned-signed.apk TachiyomiSY-arm64-v8a.apk
sha=`sha256sum TachiyomiSY-arm64-v8a.apk | awk '{ print $1 }'`
echo "APK_ARM64_V8A_SHA=$sha" >> $GITHUB_ENV
mv app/build/outputs/apk/standard/release/app-standard-armeabi-v7a-release-unsigned-signed.apk TachiyomiSY-armeabi-v7a.apk mv app/build/outputs/apk/standard/release/app-standard-armeabi-v7a-release-unsigned-signed.apk TachiyomiSY-armeabi-v7a.apk
sha=`sha256sum TachiyomiSY-armeabi-v7a.apk | awk '{ print $1 }'`
echo "APK_ARMEABI_V7A_SHA=$sha" >> $GITHUB_ENV
mv app/build/outputs/apk/standard/release/app-standard-x86-release-unsigned-signed.apk TachiyomiSY-x86.apk mv app/build/outputs/apk/standard/release/app-standard-x86-release-unsigned-signed.apk TachiyomiSY-x86.apk
sha=`sha256sum TachiyomiSY-x86.apk | awk '{ print $1 }'`
echo "APK_X86_SHA=$sha" >> $GITHUB_ENV
mv app/build/outputs/apk/standard/release/app-standard-x86_64-release-unsigned-signed.apk TachiyomiSY-x86_64.apk mv app/build/outputs/apk/standard/release/app-standard-x86_64-release-unsigned-signed.apk TachiyomiSY-x86_64.apk
sha=`sha256sum TachiyomiSY-x86_64.apk | awk '{ print $1 }'`
echo "APK_X86_64_SHA=$sha" >> $GITHUB_ENV
- name: Create release - name: Create release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
@@ -94,19 +82,10 @@ jobs:
tag_name: ${{ github.run_number }} tag_name: ${{ github.run_number }}
name: TachiyomiSY name: TachiyomiSY
body: | body: |
--- <!-->
> [!TIP]
### Checksums >
> ### If you are unsure which version to download then go with `TachiyomiSY.apk`
| Variant | SHA-256 |
| ------- | ------- |
| Universal | ${{ env.APK_UNIVERSAL_SHA }} |
| arm64-v8a | ${{ env.APK_ARM64_V8A_SHA }} |
| armeabi-v7a | ${{ env.APK_ARMEABI_V7A_SHA }} |
| x86 | ${{ env.APK_X86_SHA }} |
| x86_64 | ${{ env.APK_X86_64_SHA }} |
## If you are unsure which version to choose then go with TachiyomiSY.apk
files: | files: |
TachiyomiSY.apk TachiyomiSY.apk
TachiyomiSY-arm64-v8a.apk TachiyomiSY-arm64-v8a.apk
+12 -3
View File
@@ -12,10 +12,16 @@ jobs:
steps: steps:
- name: Clone repo - name: Clone repo
uses: actions/checkout@v4 uses: actions/checkout@v6
- name: Validate Gradle Wrapper - name: Set up JDK
uses: gradle/actions/wrapper-validation@v4 uses: actions/setup-java@v5
with:
java-version: 17
distribution: temurin
- name: Set up gradle
uses: gradle/actions/setup-gradle@v5
- name: Create Tag - name: Create Tag
run: | run: |
@@ -28,3 +34,6 @@ jobs:
-H 'Accept: application/vnd.github.everest-preview+json' \ -H 'Accept: application/vnd.github.everest-preview+json' \
-u ${{ secrets.ACCESS_TOKEN }} \ -u ${{ secrets.ACCESS_TOKEN }} \
--data '{"event_type": "ping", "client_payload": { "repository": "'"$GITHUB_REPOSITORY"'" }}' --data '{"event_type": "ping", "client_payload": { "repository": "'"$GITHUB_REPOSITORY"'" }}'
- name: Run unit tests
run: ./gradlew testDebugUnitTest testDevDebugUnitTest
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Moderate issues - name: Moderate issues
uses: tachiyomiorg/issue-moderator-action@v2.6.0 uses: tachiyomiorg/issue-moderator-action@v2.6.1
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
duplicate-label: Duplicate duplicate-label: Duplicate
-19
View File
@@ -1,19 +0,0 @@
name: Lock threads
on:
# Daily
schedule:
- cron: '0 0 * * *'
# Manual trigger
workflow_dispatch:
inputs:
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v5
with:
github-token: ${{ github.token }}
issue-inactive-days: '2'
pr-inactive-days: '2'
+1 -1
View File
@@ -10,7 +10,7 @@ jobs:
steps: steps:
- name: Check PR and Add Label - name: Check PR and Add Label
uses: actions/github-script@v7 uses: actions/github-script@v8
with: with:
script: | script: |
const prAuthor = context.payload.pull_request.user.login; const prAuthor = context.payload.pull_request.user.login;
+1 -1
View File
@@ -60,7 +60,7 @@ Additional features for some extensions, features include custom description, op
* Mangadex * Mangadex
* NHentai * NHentai
* Puruin * Puruin
* Tsumino * LANraragi
## Download ## Download
Get the app from our [releases page](https://github.com/jobobby04/tachiyomisy/releases/latest). Get the app from our [releases page](https://github.com/jobobby04/tachiyomisy/releases/latest).
+115 -96
View File
@@ -1,16 +1,19 @@
@file:Suppress("ChromeOsAbiSupport")
import mihon.buildlogic.getBuildTime import mihon.gradle.getBuildTime
import mihon.buildlogic.getCommitCount import mihon.gradle.getLatestCommitCount
import mihon.buildlogic.getGitSha import mihon.gradle.getLatestCommitSha
import mihon.gradle.tasks.ReplaceShortcutsPlaceholderTask
plugins { plugins {
id("mihon.android.application") alias(mihonx.plugins.android.application)
id("mihon.android.application.compose") alias(mihonx.plugins.compose)
alias(mihonx.plugins.spotless)
kotlin("plugin.parcelize") kotlin("plugin.parcelize")
kotlin("plugin.serialization")
// id("com.github.zellius.shortcut-helper")
alias(libs.plugins.aboutLibraries) alias(libs.plugins.aboutLibraries)
alias(libs.plugins.kotlin.serialization)
id("com.github.ben-manes.versions") id("com.github.ben-manes.versions")
} }
@@ -21,42 +24,26 @@ if (gradle.startParameter.taskRequests.toString().contains("Standard")) {
} }
} }
// shortcutHelper.setFilePath("./shortcuts.xml")
val supportedAbis = setOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
android { android {
namespace = "eu.kanade.tachiyomi" namespace = "eu.kanade.tachiyomi"
defaultConfig { defaultConfig {
applicationId = "eu.kanade.tachiyomi.sy" applicationId = "eu.kanade.tachiyomi.sy"
versionCode = 72 versionCode = 77
versionName = "1.11.0" versionName = "1.12.0"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") buildConfigField("String", "COMMIT_COUNT", "\"${getLatestCommitCount()}\"")
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"") buildConfigField("String", "COMMIT_SHA", "\"${getLatestCommitSha()}\"")
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime()}\"") buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLatestCommitTime = false)}\"")
buildConfigField("boolean", "INCLUDE_UPDATER", "false") buildConfigField("boolean", "INCLUDE_UPDATER", "false")
ndk {
abiFilters += supportedAbis
}
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
splits {
abi {
isEnable = true
reset()
include(*supportedAbis.toTypedArray())
isUniversalApk = true
}
}
buildTypes { buildTypes {
named("debug") { named("debug") {
versionNameSuffix = "-${getCommitCount()}" versionNameSuffix = "-${getLatestCommitCount()}"
applicationIdSuffix = ".debug" applicationIdSuffix = ".debug"
isPseudoLocalesEnabled = true isPseudoLocalesEnabled = true
} }
@@ -71,6 +58,8 @@ android {
isMinifyEnabled = true isMinifyEnabled = true
isShrinkResources = true isShrinkResources = true
setProguardFiles(listOf(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")) setProguardFiles(listOf(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"))
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLatestCommitTime = true)}\"")
} }
create("benchmark") { create("benchmark") {
initWith(getByName("release")) initWith(getByName("release"))
@@ -88,6 +77,15 @@ android {
getByName("benchmark").res.srcDirs("src/debug/res") getByName("benchmark").res.srcDirs("src/debug/res")
} }
splits {
abi {
isEnable = true
isUniversalApk = true
reset()
include("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
}
}
flavorDimensions.add("default") flavorDimensions.add("default")
productFlavors { productFlavors {
@@ -104,21 +102,32 @@ android {
} }
packaging { packaging {
resources.excludes.addAll( jniLibs {
listOf( keepDebugSymbols += listOf(
"libandroidx.graphics.path",
"libarchive-jni",
"libconscrypt_jni",
"libimagedecoder",
"libquickjs",
"libsqlite3x",
)
.map { "**/$it.so" }
}
resources {
excludes += setOf(
"kotlin-tooling-metadata.json", "kotlin-tooling-metadata.json",
"META-INF/DEPENDENCIES",
"LICENSE.txt", "LICENSE.txt",
"META-INF/LICENSE", "META-INF/**/*.properties",
"META-INF/**/LICENSE.txt", "META-INF/**/LICENSE.txt",
"META-INF/*.properties", "META-INF/*.properties",
"META-INF/**/*.properties",
"META-INF/README.md",
"META-INF/NOTICE",
"META-INF/*.version", "META-INF/*.version",
), "META-INF/DEPENDENCIES",
"META-INF/LICENSE",
"META-INF/NOTICE",
"META-INF/README.md",
) )
} }
}
dependenciesInfo { dependenciesInfo {
includeInApk = false includeInApk = false
@@ -127,9 +136,9 @@ android {
buildFeatures { buildFeatures {
viewBinding = true viewBinding = true
buildConfig = true buildConfig = true
aidl = true
// Disable some unused things // Disable some unused things
aidl = false
renderScript = false renderScript = false
shaders = false shaders = false
} }
@@ -148,12 +157,14 @@ kotlin {
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi", "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi", "-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api", "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
"-opt-in=androidx.compose.material3.ExperimentalMaterial3ExpressiveApi",
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi", "-opt-in=androidx.compose.ui.ExperimentalComposeUiApi",
"-opt-in=coil3.annotation.ExperimentalCoilApi", "-opt-in=coil3.annotation.ExperimentalCoilApi",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-opt-in=kotlinx.coroutines.FlowPreview", "-opt-in=kotlinx.coroutines.FlowPreview",
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi", "-opt-in=kotlinx.coroutines.InternalCoroutinesApi",
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi", "-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
"-Xannotation-default-target=param-property",
) )
} }
} }
@@ -173,96 +184,95 @@ dependencies {
implementation(projects.presentationWidget) implementation(projects.presentationWidget)
// Compose // Compose
implementation(compose.activity) implementation(libs.androidx.activity.compose)
implementation(compose.foundation) implementation(libs.androidx.compose.foundation)
implementation(compose.material3.core) implementation(libs.androidx.compose.material3)
implementation(compose.material.icons) implementation(libs.androidx.compose.materialIcons)
implementation(compose.animation) implementation(libs.androidx.compose.animation)
implementation(compose.animation.graphics) implementation(libs.androidx.compose.animationGraphics)
debugImplementation(compose.ui.tooling) debugImplementation(libs.androidx.compose.uiTooling)
implementation(compose.ui.tooling.preview) implementation(libs.androidx.compose.uiToolingPreview)
implementation(compose.ui.util) implementation(libs.androidx.compose.uiUtil)
implementation(androidx.interpolator) implementation(libs.androidx.interpolator)
implementation(androidx.paging.runtime) implementation(libs.androidx.paging.runtime)
implementation(androidx.paging.compose) implementation(libs.androidx.paging.compose)
implementation(libs.bundles.sqlite) implementation(libs.androidx.sqlite.bundled)
// SY --> // SY -->
implementation(sylibs.sqlcipher) implementation(sylibs.sqlcipher)
// SY <-- // SY <--
implementation(kotlinx.reflect) implementation(libs.kotlin.reflect)
implementation(kotlinx.immutables) implementation(libs.kotlinx.collections.immutable)
implementation(platform(kotlinx.coroutines.bom)) implementation(libs.bundles.kotlinx.coroutines)
implementation(kotlinx.bundles.coroutines)
// AndroidX libraries // AndroidX libraries
implementation(androidx.annotation) implementation(libs.androidx.annotation)
implementation(androidx.appcompat) implementation(libs.androidx.appCompat)
implementation(androidx.biometricktx) implementation(libs.androidx.biometric)
implementation(androidx.constraintlayout) implementation(libs.androidx.constraintLayout)
implementation(androidx.corektx) implementation(libs.androidx.core)
implementation(androidx.splashscreen) implementation(libs.androidx.coreSplashScreen)
implementation(androidx.recyclerview) implementation(libs.androidx.recyclerView)
implementation(androidx.viewpager) implementation(libs.androidx.viewPager)
implementation(androidx.profileinstaller) implementation(libs.androidx.profileInstaller)
implementation(androidx.bundles.lifecycle) implementation(libs.bundles.androidx.lifecycle)
// Job scheduling // Job scheduling
implementation(androidx.workmanager) implementation(libs.androidx.work)
// RxJava // RxJava
implementation(libs.rxjava) implementation(libs.rxJava)
// Networking // Networking
implementation(libs.bundles.okhttp) implementation(libs.bundles.okhttp)
implementation(libs.okio) implementation(libs.okio)
implementation(libs.conscrypt.android) // TLS 1.3 support for Android < 10 implementation(libs.conscrypt) // TLS 1.3 support for Android < 10
// Data serialization (JSON, protobuf, xml) // Data serialization (JSON, protobuf, xml)
implementation(kotlinx.bundles.serialization) implementation(libs.bundles.serialization)
// HTML parser // HTML parser
implementation(libs.jsoup) implementation(libs.jsoup)
// Disk // Disk
implementation(libs.disklrucache) implementation(libs.diskLruCache)
implementation(libs.unifile) implementation(libs.unifile)
// Preferences // Preferences
implementation(libs.preferencektx) implementation(libs.androidx.preference)
// Dependency injection // Dependency injection
implementation(libs.injekt.core) implementation(libs.injekt)
// Image loading // Image loading
implementation(platform(libs.coil.bom))
implementation(libs.bundles.coil) implementation(libs.bundles.coil)
implementation(libs.subsamplingscaleimageview) { implementation(libs.subsamplingScaleImageView) {
exclude(module = "image-decoder") exclude(module = "image-decoder")
} }
implementation(libs.image.decoder) implementation(libs.image.decoder)
// UI libraries // UI libraries
implementation(libs.material) implementation(libs.material)
implementation(libs.flexible.adapter.core) implementation(libs.flexibleAdapter)
implementation(libs.photoview) implementation(libs.photoView)
implementation(libs.directionalviewpager) { implementation(libs.directionalViewPager) {
exclude(group = "androidx.viewpager", module = "viewpager") exclude(group = "androidx.viewpager", module = "viewpager")
} }
implementation(libs.insetter) implementation(libs.composeRichEditor)
implementation(libs.bundles.richtext)
implementation(libs.aboutLibraries.compose) implementation(libs.aboutLibraries.compose)
implementation(libs.bundles.voyager) implementation(libs.bundles.voyager)
implementation(libs.compose.materialmotion) implementation(libs.composeMaterialMotion)
implementation(libs.swipe) implementation(libs.swipe)
implementation(libs.compose.webview) implementation(libs.composeWebview)
implementation(libs.compose.grid) implementation(libs.composeGrid)
implementation(libs.reorderable) implementation(libs.reorderable)
implementation(libs.bundles.markdown)
implementation(libs.materialKolor)
// Logging // Logging
implementation(libs.logcat) implementation(libs.logcat)
@@ -275,19 +285,20 @@ dependencies {
// Shizuku // Shizuku
implementation(libs.bundles.shizuku) implementation(libs.bundles.shizuku)
// String similarity
implementation(libs.stringSimilarity)
// Tests // Tests
testImplementation(libs.bundles.test) testImplementation(libs.bundles.test)
testRuntimeOnly(libs.junit.platform.launcher)
// For detecting memory leaks; see https://square.github.io/leakcanary/ // For detecting memory leaks; see https://square.github.io/leakcanary/
// debugImplementation(libs.leakcanary.android) // debugImplementation(libs.leakCanary.android)
implementation(libs.leakcanary.plumber) implementation(libs.leakCanary.plumber)
testImplementation(kotlinx.coroutines.test) testImplementation(libs.kotlinx.coroutines.test)
// SY --> // SY -->
// Text distance (EH)
implementation(sylibs.simularity)
// Firebase (EH) // Firebase (EH)
implementation(platform(libs.firebase.bom)) implementation(platform(libs.firebase.bom))
implementation(libs.firebase.analytics) implementation(libs.firebase.analytics)
@@ -307,17 +318,25 @@ dependencies {
// Koin // Koin
implementation(sylibs.koin.core) implementation(sylibs.koin.core)
implementation(sylibs.koin.android) implementation(sylibs.koin.android)
// ZXing Android Embedded
implementation(sylibs.zxing.android.embedded)
} }
androidComponents { androidComponents {
beforeVariants { variantBuilder -> onVariants { variant ->
// Disables standardBenchmark val resSource = variant.sources.res ?: return@onVariants
if (variantBuilder.buildType == "benchmark") {
variantBuilder.enable = variantBuilder.productFlavors.containsAll( val variantName = variant.name.replaceFirstChar { it.uppercase() }
listOf("default" to "dev"), val replaceShortcutsPlaceholderTask = tasks.register<ReplaceShortcutsPlaceholderTask>(
) "replace${variantName}ShortcutPlaceholder",
) {
applicationId.set(variant.applicationId)
shortcutsFile.set(projectDir.resolve("src/main/shortcuts.xml"))
} }
resSource.addGeneratedSourceDirectory(replaceShortcutsPlaceholderTask) { it.outputDir }
} }
onVariants(selector().withFlavor("default" to "standard")) { onVariants(selector().withFlavor("default" to "standard")) {
// Only excluding in standard flavor because this breaks // Only excluding in standard flavor because this breaks
// Layout Inspector's Compose tree // Layout Inspector's Compose tree
@@ -327,6 +346,6 @@ androidComponents {
buildscript { buildscript {
dependencies { dependencies {
classpath(kotlinx.gradle) classpath(libs.kotlin.gradle)
} }
} }
+2
View File
@@ -299,3 +299,5 @@
-dontwarn org.ietf.jgss.GSSManager -dontwarn org.ietf.jgss.GSSManager
-dontwarn org.ietf.jgss.GSSName -dontwarn org.ietf.jgss.GSSName
-dontwarn org.ietf.jgss.Oid -dontwarn org.ietf.jgss.Oid
-dontwarn com.google.re2j.Matcher
-dontwarn com.google.re2j.Pattern
+5 -1
View File
@@ -359,7 +359,7 @@
<data android:scheme="https" /> <data android:scheme="https" />
<data android:scheme="http" /> <data android:scheme="http" />
<data android:host="pururin.io" /> <data android:host="pururin.me" />
<data android:pathPattern="/gallery/..*" /> <data android:pathPattern="/gallery/..*" />
</intent-filter> </intent-filter>
@@ -413,6 +413,10 @@
android:scheme="tachiyomisy" /> android:scheme="tachiyomisy" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name="com.journeyapps.barcodescanner.CaptureActivity"
tools:remove="screenOrientation" />
</application> </application>
<uses-sdk tools:overrideLibrary="rikka.shizuku.api" <uses-sdk tools:overrideLibrary="rikka.shizuku.api"
@@ -0,0 +1,7 @@
package mihon.app.shizuku;
interface IShellInterface {
void install(in AssetFileDescriptor apk) = 1;
void destroy() = 16777114;
}
@@ -38,6 +38,7 @@ import mihon.domain.extensionrepo.interactor.ReplaceExtensionRepo
import mihon.domain.extensionrepo.interactor.UpdateExtensionRepo import mihon.domain.extensionrepo.interactor.UpdateExtensionRepo
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
import mihon.domain.extensionrepo.service.ExtensionRepoService import mihon.domain.extensionrepo.service.ExtensionRepoService
import mihon.domain.migration.usecases.MigrateMangaUseCase
import mihon.domain.upcoming.interactor.GetUpcomingManga import mihon.domain.upcoming.interactor.GetUpcomingManga
import tachiyomi.data.category.CategoryRepositoryImpl import tachiyomi.data.category.CategoryRepositoryImpl
import tachiyomi.data.chapter.ChapterRepositoryImpl import tachiyomi.data.chapter.ChapterRepositoryImpl
@@ -59,6 +60,7 @@ import tachiyomi.domain.category.interactor.SetMangaCategories
import tachiyomi.domain.category.interactor.SetSortModeForCategory import tachiyomi.domain.category.interactor.SetSortModeForCategory
import tachiyomi.domain.category.interactor.UpdateCategory import tachiyomi.domain.category.interactor.UpdateCategory
import tachiyomi.domain.category.repository.CategoryRepository import tachiyomi.domain.category.repository.CategoryRepository
import tachiyomi.domain.chapter.interactor.GetBookmarkedChaptersByMangaId
import tachiyomi.domain.chapter.interactor.GetChapter import tachiyomi.domain.chapter.interactor.GetChapter
import tachiyomi.domain.chapter.interactor.GetChapterByUrlAndMangaId import tachiyomi.domain.chapter.interactor.GetChapterByUrlAndMangaId
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
@@ -82,6 +84,7 @@ import tachiyomi.domain.manga.interactor.GetMangaWithChapters
import tachiyomi.domain.manga.interactor.NetworkToLocalManga import tachiyomi.domain.manga.interactor.NetworkToLocalManga
import tachiyomi.domain.manga.interactor.ResetViewerFlags import tachiyomi.domain.manga.interactor.ResetViewerFlags
import tachiyomi.domain.manga.interactor.SetMangaChapterFlags import tachiyomi.domain.manga.interactor.SetMangaChapterFlags
import tachiyomi.domain.manga.interactor.UpdateMangaNotes
import tachiyomi.domain.manga.repository.MangaRepository import tachiyomi.domain.manga.repository.MangaRepository
import tachiyomi.domain.release.interactor.GetApplicationRelease import tachiyomi.domain.release.interactor.GetApplicationRelease
import tachiyomi.domain.release.service.ReleaseService import tachiyomi.domain.release.service.ReleaseService
@@ -128,9 +131,15 @@ class DomainModule : InjektModule {
addFactory { SetMangaViewerFlags(get()) } addFactory { SetMangaViewerFlags(get()) }
addFactory { NetworkToLocalManga(get()) } addFactory { NetworkToLocalManga(get()) }
addFactory { UpdateManga(get(), get()) } addFactory { UpdateManga(get(), get()) }
addFactory { UpdateMangaNotes(get()) }
addFactory { SetMangaCategories(get()) } addFactory { SetMangaCategories(get()) }
addFactory { GetExcludedScanlators(get()) } addFactory { GetExcludedScanlators(get()) }
addFactory { SetExcludedScanlators(get()) } addFactory { SetExcludedScanlators(get()) }
addFactory {
MigrateMangaUseCase(
get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(),
)
}
addSingletonFactory<ReleaseService> { ReleaseServiceImpl(get(), get()) } addSingletonFactory<ReleaseService> { ReleaseServiceImpl(get(), get()) }
addFactory { GetApplicationRelease(get(), get()) } addFactory { GetApplicationRelease(get(), get()) }
@@ -148,6 +157,7 @@ class DomainModule : InjektModule {
addSingletonFactory<ChapterRepository> { ChapterRepositoryImpl(get()) } addSingletonFactory<ChapterRepository> { ChapterRepositoryImpl(get()) }
addFactory { GetChapter(get()) } addFactory { GetChapter(get()) }
addFactory { GetChaptersByMangaId(get()) } addFactory { GetChaptersByMangaId(get()) }
addFactory { GetBookmarkedChaptersByMangaId(get(), get(), get()) }
addFactory { GetChapterByUrlAndMangaId(get()) } addFactory { GetChapterByUrlAndMangaId(get()) }
addFactory { UpdateChapter(get()) } addFactory { UpdateChapter(get()) }
addFactory { SetReadStatus(get(), get(), get(), get(), get()) } addFactory { SetReadStatus(get(), get(), get(), get(), get()) }
@@ -28,7 +28,6 @@ import tachiyomi.data.source.SavedSearchRepositoryImpl
import tachiyomi.domain.chapter.interactor.DeleteChapters import tachiyomi.domain.chapter.interactor.DeleteChapters
import tachiyomi.domain.chapter.interactor.GetChapterByUrl import tachiyomi.domain.chapter.interactor.GetChapterByUrl
import tachiyomi.domain.chapter.interactor.GetMergedChaptersByMangaId import tachiyomi.domain.chapter.interactor.GetMergedChaptersByMangaId
import tachiyomi.domain.history.interactor.GetHistoryByMangaId
import tachiyomi.domain.manga.interactor.DeleteByMergeId import tachiyomi.domain.manga.interactor.DeleteByMergeId
import tachiyomi.domain.manga.interactor.DeleteFavoriteEntries import tachiyomi.domain.manga.interactor.DeleteFavoriteEntries
import tachiyomi.domain.manga.interactor.DeleteMangaById import tachiyomi.domain.manga.interactor.DeleteMangaById
@@ -88,7 +87,6 @@ class SYDomainModule : InjektModule {
addFactory { DeleteChapters(get()) } addFactory { DeleteChapters(get()) }
addFactory { DeleteMangaById(get()) } addFactory { DeleteMangaById(get()) }
addFactory { FilterSerializer() } addFactory { FilterSerializer() }
addFactory { GetHistoryByMangaId(get()) }
addFactory { GetChapterByUrl(get()) } addFactory { GetChapterByUrl(get()) }
addFactory { GetSourceCategories(get()) } addFactory { GetSourceCategories(get()) }
addFactory { CreateSourceCategory(get()) } addFactory { CreateSourceCategory(get()) }
@@ -9,19 +9,22 @@ import tachiyomi.i18n.MR
class BasePreferences( class BasePreferences(
val context: Context, val context: Context,
private val preferenceStore: PreferenceStore, preferenceStore: PreferenceStore,
) { ) {
fun downloadedOnly() = preferenceStore.getBoolean( val downloadedOnly: Preference<Boolean> = preferenceStore.getBoolean(
Preference.appStateKey("pref_downloaded_only"), Preference.appStateKey("pref_downloaded_only"),
false, false,
) )
fun incognitoMode() = preferenceStore.getBoolean(Preference.appStateKey("incognito_mode"), false) val incognitoMode: Preference<Boolean> = preferenceStore.getBoolean(Preference.appStateKey("incognito_mode"), false)
fun extensionInstaller() = ExtensionInstallerPreference(context, preferenceStore) val extensionInstaller: ExtensionInstallerPreference = ExtensionInstallerPreference(context, preferenceStore)
fun shownOnboardingFlow() = preferenceStore.getBoolean(Preference.appStateKey("onboarding_complete"), false) val shownOnboardingFlow: Preference<Boolean> = preferenceStore.getBoolean(
Preference.appStateKey("onboarding_complete"),
false,
)
enum class ExtensionInstaller(val titleRes: StringResource, val requiresSystemPermission: Boolean) { enum class ExtensionInstaller(val titleRes: StringResource, val requiresSystemPermission: Boolean) {
LEGACY(MR.strings.ext_installer_legacy, true), LEGACY(MR.strings.ext_installer_legacy, true),
@@ -30,9 +33,17 @@ class BasePreferences(
PRIVATE(MR.strings.ext_installer_private, false), PRIVATE(MR.strings.ext_installer_private, false),
} }
fun displayProfile() = preferenceStore.getString("pref_display_profile_key", "") val displayProfile: Preference<String> = preferenceStore.getString("pref_display_profile_key", "")
fun hardwareBitmapThreshold() = preferenceStore.getInt("pref_hardware_bitmap_threshold", GLUtil.SAFE_TEXTURE_LIMIT) val hardwareBitmapThreshold: Preference<Int> = preferenceStore.getInt(
"pref_hardware_bitmap_threshold",
GLUtil.SAFE_TEXTURE_LIMIT,
)
fun alwaysDecodeLongStripWithSSIV() = preferenceStore.getBoolean("pref_always_decode_long_strip_with_ssiv", false) val alwaysDecodeLongStripWithSSIV: Preference<Boolean> = preferenceStore.getBoolean(
"pref_always_decode_long_strip_with_ssiv",
false,
)
val installationId: Preference<String> = preferenceStore.getString(Preference.appStateKey("installation_id"), "")
} }
@@ -51,7 +51,7 @@ class SetReadStatus(
return@withNonCancellableContext Result.InternalError(e) return@withNonCancellableContext Result.InternalError(e)
} }
if (read && downloadPreferences.removeAfterMarkedAsRead().get()) { if (read && downloadPreferences.removeAfterMarkedAsRead.get()) {
chaptersToUpdate chaptersToUpdate
.groupBy { it.mangaId } .groupBy { it.mangaId }
.forEach { (mangaId, chapters) -> .forEach { (mangaId, chapters) ->
@@ -119,6 +119,7 @@ class SyncChaptersWithSource(
downloadManager.isChapterDownloaded( downloadManager.isChapterDownloaded(
dbChapter.name, dbChapter.name,
dbChapter.scanlator, dbChapter.scanlator,
dbChapter.url,
/* SY --> */ manga.ogTitle /* SY <-- */, /* SY --> */ manga.ogTitle /* SY <-- */,
manga.source, manga.source,
) )
@@ -126,12 +127,14 @@ class SyncChaptersWithSource(
if (shouldRenameChapter) { if (shouldRenameChapter) {
downloadManager.renameChapter(source, manga, dbChapter, chapter) downloadManager.renameChapter(source, manga, dbChapter, chapter)
} }
var toChangeChapter = dbChapter.copy( var toChangeChapter = dbChapter.copy(
name = chapter.name, name = chapter.name,
chapterNumber = chapter.chapterNumber, chapterNumber = chapter.chapterNumber,
scanlator = chapter.scanlator, scanlator = chapter.scanlator,
sourceOrder = chapter.sourceOrder, sourceOrder = chapter.sourceOrder,
) )
if (chapter.dateUpload != 0L) { if (chapter.dateUpload != 0L) {
toChangeChapter = toChangeChapter.copy(dateUpload = chapter.dateUpload) toChangeChapter = toChangeChapter.copy(dateUpload = chapter.dateUpload)
} }
@@ -173,7 +176,8 @@ class SyncChaptersWithSource(
val deletedChapterNumberDateFetchMap = removedChapters.sortedByDescending { it.dateFetch } val deletedChapterNumberDateFetchMap = removedChapters.sortedByDescending { it.dateFetch }
.associate { it.chapterNumber to it.dateFetch } .associate { it.chapterNumber to it.dateFetch }
val markDuplicateAsRead = libraryPreferences.markDuplicateChapterRead().get() val markDuplicateAsRead = libraryPreferences.markDuplicateReadChapterAsRead.get()
.contains(LibraryPreferences.MARK_DUPLICATE_CHAPTER_READ_NEW)
// Date fetch is set in such a way that the upper ones will have bigger value than the lower ones // Date fetch is set in such a way that the upper ones will have bigger value than the lower ones
// Sources MUST return the chapters from most to less recent, which is common. // Sources MUST return the chapters from most to less recent, which is common.
@@ -34,6 +34,7 @@ fun List<Chapter>.applyFilters(
val downloaded = downloadManager.isChapterDownloaded( val downloaded = downloadManager.isChapterDownloaded(
chapter.name, chapter.name,
chapter.scanlator, chapter.scanlator,
chapter.url,
/* SY --> */ manga.ogTitle /* SY <-- */, /* SY --> */ manga.ogTitle /* SY <-- */,
manga.source, manga.source,
) )
@@ -12,7 +12,7 @@ class GetExtensionLanguages(
) { ) {
fun subscribe(): Flow<List<String>> { fun subscribe(): Flow<List<String>> {
return combine( return combine(
preferences.enabledLanguages().changes(), preferences.enabledLanguages.changes(),
extensionManager.availableExtensionsFlow, extensionManager.availableExtensionsFlow,
) { enabledLanguage, availableExtensions -> ) { enabledLanguage, availableExtensions ->
availableExtensions availableExtensions
@@ -15,7 +15,7 @@ class GetExtensionSources(
val isMultiLangSingleSource = val isMultiLangSingleSource =
isMultiSource && extension.sources.map { it.name }.distinct().size == 1 isMultiSource && extension.sources.map { it.name }.distinct().size == 1
return preferences.disabledSources().changes().map { disabledSources -> return preferences.disabledSources.changes().map { disabledSources ->
fun Source.isEnabled() = id.toString() !in disabledSources fun Source.isEnabled() = id.toString() !in disabledSources
extension.sources extension.sources
@@ -13,10 +13,10 @@ class GetExtensionsByType(
) { ) {
fun subscribe(): Flow<Extensions> { fun subscribe(): Flow<Extensions> {
val showNsfwSources = preferences.showNsfwSource().get() val showNsfwSources = preferences.showNsfwSource.get()
return combine( return combine(
preferences.enabledLanguages().changes(), preferences.enabledLanguages.changes(),
extensionManager.installedExtensionsFlow, extensionManager.installedExtensionsFlow,
extensionManager.untrustedExtensionsFlow, extensionManager.untrustedExtensionsFlow,
extensionManager.availableExtensionsFlow, extensionManager.availableExtensionsFlow,
@@ -14,11 +14,11 @@ class TrustExtension(
suspend fun isTrusted(pkgInfo: PackageInfo, fingerprints: List<String>): Boolean { suspend fun isTrusted(pkgInfo: PackageInfo, fingerprints: List<String>): Boolean {
val trustedFingerprints = extensionRepoRepository.getAll().map { it.signingKeyFingerprint }.toHashSet() val trustedFingerprints = extensionRepoRepository.getAll().map { it.signingKeyFingerprint }.toHashSet()
val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:${fingerprints.last()}" val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:${fingerprints.last()}"
return trustedFingerprints.any { fingerprints.contains(it) } || key in preferences.trustedExtensions().get() return trustedFingerprints.any { fingerprints.contains(it) } || key in preferences.trustedExtensions.get()
} }
fun trust(pkgName: String, versionCode: Long, signatureHash: String) { fun trust(pkgName: String, versionCode: Long, signatureHash: String) {
preferences.trustedExtensions().getAndSet { exts -> preferences.trustedExtensions.getAndSet { exts ->
// Remove previously trusted versions // Remove previously trusted versions
val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet() val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet()
@@ -27,6 +27,6 @@ class TrustExtension(
} }
fun revokeAll() { fun revokeAll() {
preferences.trustedExtensions().delete() preferences.trustedExtensions.delete()
} }
} }
@@ -15,9 +15,9 @@ class CreateSortTag(
return Result.TagExists return Result.TagExists
} }
val size = preferences.sortTagsForLibrary().get().size val size = preferences.sortTagsForLibrary.get().size
preferences.sortTagsForLibrary() += encodeTag(size, tag) preferences.sortTagsForLibrary += encodeTag(size, tag)
return Result.Success return Result.Success
} }
@@ -8,7 +8,7 @@ class DeleteSortTag(
) { ) {
fun await(tag: String) { fun await(tag: String) {
preferences.sortTagsForLibrary().set( preferences.sortTagsForLibrary.set(
(getSortTag.await() - tag).mapIndexed { index, s -> (getSortTag.await() - tag).mapIndexed { index, s ->
CreateSortTag.encodeTag(index, s) CreateSortTag.encodeTag(index, s)
}.toSet(), }.toSet(),
@@ -23,7 +23,7 @@ class GetPagePreviews(
return try { return try {
val pagePreviews = try { val pagePreviews = try {
pagePreviewCache.getPageListFromCache(manga, chapterIds, page) pagePreviewCache.getPageListFromCache(manga, chapterIds, page)
} catch (e: Exception) { } catch (_: Exception) {
source.getPagePreviewList(manga.toSManga(), chapters.map { it.toSChapter() }, page).also { source.getPagePreviewList(manga.toSManga(), chapters.map { it.toSChapter() }, page).also {
pagePreviewCache.putPageListToCache(manga, chapterIds, it) pagePreviewCache.putPageListToCache(manga, chapterIds, it)
} }
@@ -7,14 +7,14 @@ import tachiyomi.domain.library.service.LibraryPreferences
class GetSortTag(private val preferences: LibraryPreferences) { class GetSortTag(private val preferences: LibraryPreferences) {
fun subscribe(): Flow<List<String>> { fun subscribe(): Flow<List<String>> {
return preferences.sortTagsForLibrary().changes() return preferences.sortTagsForLibrary.changes()
.map(::mapSortTags) .map(::mapSortTags)
} }
fun await() = getSortTags(preferences).let(::mapSortTags) fun await() = getSortTags(preferences).let(::mapSortTags)
companion object { companion object {
fun getSortTags(preferences: LibraryPreferences) = preferences.sortTagsForLibrary().get() fun getSortTags(preferences: LibraryPreferences) = preferences.sortTagsForLibrary.get()
fun mapSortTags(tags: Set<String>) = tags.mapNotNull { fun mapSortTags(tags: Set<String>) = tags.mapNotNull {
val index = it.indexOf('|') val index = it.indexOf('|')
@@ -23,7 +23,7 @@ class ReorderSortTag(
val reorderedTag = reorderedTags.removeAt(currentIndex) val reorderedTag = reorderedTags.removeAt(currentIndex)
reorderedTags.add(newPosition, reorderedTag) reorderedTags.add(newPosition, reorderedTag)
preferences.sortTagsForLibrary().set( preferences.sortTagsForLibrary.set(
reorderedTags.mapIndexed { index, s -> reorderedTags.mapIndexed { index, s ->
CreateSortTag.encodeTag(index, s) CreateSortTag.encodeTag(index, s)
}.toSet(), }.toSet(),
@@ -4,6 +4,7 @@ import eu.kanade.domain.manga.model.hasCustomCover
import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.manga.interactor.FetchInterval import tachiyomi.domain.manga.interactor.FetchInterval
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaUpdate import tachiyomi.domain.manga.model.MangaUpdate
@@ -32,9 +33,8 @@ class UpdateManga(
remoteManga: SManga, remoteManga: SManga,
manualFetch: Boolean, manualFetch: Boolean,
coverCache: CoverCache = Injekt.get(), coverCache: CoverCache = Injekt.get(),
// SY --> libraryPreferences: LibraryPreferences = Injekt.get(),
downloadManager: DownloadManager = Injekt.get(), downloadManager: DownloadManager = Injekt.get(),
// SY <--
): Boolean { ): Boolean {
val remoteTitle = try { val remoteTitle = try {
remoteManga.title remoteManga.title
@@ -42,14 +42,13 @@ class UpdateManga(
"" ""
} }
// SY --> // if the manga isn't a favorite (or 'update titles' preference is enabled), set its title from source and update in db
val title = if (remoteTitle.isNotBlank() && localManga.ogTitle != remoteTitle) { val title =
downloadManager.renameMangaDir(localManga.ogTitle, remoteTitle, localManga.source) if (remoteTitle.isNotEmpty() && (!localManga.favorite || libraryPreferences.updateMangaTitles.get())) {
remoteTitle remoteTitle
} else { } else {
null null
} }
// SY <--
val coverLastModified = val coverLastModified =
when { when {
@@ -69,7 +68,7 @@ class UpdateManga(
val thumbnailUrl = remoteManga.thumbnail_url?.takeIf { it.isNotEmpty() } val thumbnailUrl = remoteManga.thumbnail_url?.takeIf { it.isNotEmpty() }
return mangaRepository.update( val success = mangaRepository.update(
MangaUpdate( MangaUpdate(
id = localManga.id, id = localManga.id,
title = title, title = title,
@@ -84,6 +83,10 @@ class UpdateManga(
initialized = true, initialized = true,
), ),
) )
if (success && title != null) {
downloadManager.renameManga(localManga, title)
}
return success
} }
suspend fun awaitUpdateFetchInterval( suspend fun awaitUpdateFetchInterval(
@@ -23,7 +23,7 @@ val Manga.readerOrientation: Long
val Manga.downloadedFilter: TriState val Manga.downloadedFilter: TriState
get() { get() {
if (Injekt.get<BasePreferences>().downloadedOnly().get()) return TriState.ENABLED_IS if (Injekt.get<BasePreferences>().downloadedOnly.get()) return TriState.ENABLED_IS
return when (downloadedFilterRaw) { return when (downloadedFilterRaw) {
Manga.CHAPTER_SHOW_DOWNLOADED -> TriState.ENABLED_IS Manga.CHAPTER_SHOW_DOWNLOADED -> TriState.ENABLED_IS
Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriState.ENABLED_NOT Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriState.ENABLED_NOT
@@ -38,12 +38,14 @@ fun Manga.chaptersFiltered(): Boolean {
fun Manga.toSManga(): SManga = SManga.create().also { fun Manga.toSManga(): SManga = SManga.create().also {
it.url = url it.url = url
it.title = title // SY -->
it.artist = artist it.title = ogTitle
it.author = author it.artist = ogArtist
it.description = description it.author = ogAuthor
it.genre = genre.orEmpty().joinToString() it.description = ogDescription
it.status = status.toInt() it.genre = ogGenre.orEmpty().joinToString()
it.status = ogStatus.toInt()
// SY <--
it.thumbnail_url = thumbnailUrl it.thumbnail_url = thumbnailUrl
it.initialized = initialized it.initialized = initialized
} }
@@ -76,24 +78,6 @@ fun Manga.copyFrom(other: SManga): Manga {
) )
} }
fun SManga.toDomainManga(sourceId: Long): Manga {
return Manga.create().copy(
url = url,
// SY -->
ogTitle = title,
ogArtist = artist,
ogAuthor = author,
ogThumbnailUrl = thumbnail_url,
ogDescription = description,
ogGenre = getGenres(),
ogStatus = status.toLong(),
// SY <--
updateStrategy = update_strategy,
initialized = initialized,
source = sourceId,
)
}
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean { fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
return coverCache.getCustomCoverFile(id).exists() return coverCache.getCustomCoverFile(id).exists()
} }
@@ -11,7 +11,7 @@ class CreateSourceCategory(private val preferences: SourcePreferences) {
} }
// Create category. // Create category.
preferences.sourcesTabCategories() += category preferences.sourcesTabCategories += category
return Result.Success return Result.Success
} }
@@ -7,9 +7,9 @@ import tachiyomi.core.common.preference.minusAssign
class DeleteSourceCategory(private val preferences: SourcePreferences) { class DeleteSourceCategory(private val preferences: SourcePreferences) {
fun await(category: String) { fun await(category: String) {
preferences.sourcesTabSourcesInCategories().getAndSet { sourcesInCategories -> preferences.sourcesTabSourcesInCategories.getAndSet { sourcesInCategories ->
sourcesInCategories.filterNot { it.substringAfter("|") == category }.toSet() sourcesInCategories.filterNot { it.substringAfter("|") == category }.toSet()
} }
preferences.sourcesTabCategories() -= category preferences.sourcesTabCategories -= category
} }
} }
@@ -18,17 +18,17 @@ class GetEnabledSources(
fun subscribe(): Flow<List<Source>> { fun subscribe(): Flow<List<Source>> {
return combine( return combine(
preferences.pinnedSources().changes(), preferences.pinnedSources.changes(),
combine( combine(
preferences.enabledLanguages().changes(), preferences.enabledLanguages.changes(),
preferences.disabledSources().changes(), preferences.disabledSources.changes(),
preferences.lastUsedSource().changes(), preferences.lastUsedSource.changes(),
) { a, b, c -> Triple(a, b, c) }, ) { a, b, c -> Triple(a, b, c) },
// SY --> // SY -->
combine( combine(
preferences.dataSaverExcludedSources().changes(), preferences.dataSaverExcludedSources.changes(),
preferences.sourcesTabSourcesInCategories().changes(), preferences.sourcesTabSourcesInCategories.changes(),
preferences.sourcesTabCategoriesFilter().changes(), preferences.sourcesTabCategoriesFilter.changes(),
) { a, b, c -> Triple(a, b, c) }, ) { a, b, c -> Triple(a, b, c) },
// SY <-- // SY <--
repository.getSources(), repository.getSources(),
@@ -7,7 +7,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonArray
import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withIOContext
@@ -13,19 +13,19 @@ class GetIncognitoState(
private val extensionManager: ExtensionManager, private val extensionManager: ExtensionManager,
) { ) {
fun await(sourceId: Long?): Boolean { fun await(sourceId: Long?): Boolean {
if (basePreferences.incognitoMode().get()) return true if (basePreferences.incognitoMode.get()) return true
if (sourceId == null) return false if (sourceId == null) return false
val extensionPackage = extensionManager.getExtensionPackage(sourceId) ?: return false val extensionPackage = extensionManager.getExtensionPackage(sourceId) ?: return false
return extensionPackage in sourcePreferences.incognitoExtensions().get() return extensionPackage in sourcePreferences.incognitoExtensions.get()
} }
fun subscribe(sourceId: Long?): Flow<Boolean> { fun subscribe(sourceId: Long?): Flow<Boolean> {
if (sourceId == null) return basePreferences.incognitoMode().changes() if (sourceId == null) return basePreferences.incognitoMode.changes()
return combine( return combine(
basePreferences.incognitoMode().changes(), basePreferences.incognitoMode.changes(),
sourcePreferences.incognitoExtensions().changes(), sourcePreferences.incognitoExtensions.changes(),
extensionManager.getExtensionPackageAsFlow(sourceId), extensionManager.getExtensionPackageAsFlow(sourceId),
) { incognito, incognitoExtensions, extensionPackage -> ) { incognito, incognitoExtensions, extensionPackage ->
incognito || (extensionPackage in incognitoExtensions) incognito || (extensionPackage in incognitoExtensions)
@@ -16,8 +16,8 @@ class GetLanguagesWithSources(
fun subscribe(): Flow<SortedMap<String, List<Source>>> { fun subscribe(): Flow<SortedMap<String, List<Source>>> {
return combine( return combine(
preferences.enabledLanguages().changes(), preferences.enabledLanguages.changes(),
preferences.disabledSources().changes(), preferences.disabledSources.changes(),
repository.getOnlineSources(), repository.getOnlineSources(),
) { enabledLanguage, disabledSource, onlineSources -> ) { enabledLanguage, disabledSource, onlineSources ->
val sortedSources = onlineSources.filterNot { it.id in BlacklistedSources.HIDDEN_SOURCES }.sortedWith( val sortedSources = onlineSources.filterNot { it.id in BlacklistedSources.HIDDEN_SOURCES }.sortedWith(
@@ -9,7 +9,7 @@ class GetShowLatest(
) { ) {
fun subscribe(hasSmartSearchConfig: Boolean): Flow<Boolean> { fun subscribe(hasSmartSearchConfig: Boolean): Flow<Boolean> {
return preferences.useNewSourceNavigation().changes() return preferences.useNewSourceNavigation.changes()
.map { .map {
!hasSmartSearchConfig && !it !hasSmartSearchConfig && !it
} }
@@ -9,6 +9,6 @@ class GetSourceCategories(
) { ) {
fun subscribe(): Flow<List<String>> { fun subscribe(): Flow<List<String>> {
return preferences.sourcesTabCategories().changes().map { it.sortedWith(String.CASE_INSENSITIVE_ORDER) } return preferences.sourcesTabCategories.changes().map { it.sortedWith(String.CASE_INSENSITIVE_ORDER) }
} }
} }
@@ -16,8 +16,8 @@ class GetSourcesWithFavoriteCount(
fun subscribe(): Flow<List<Pair<Source, Long>>> { fun subscribe(): Flow<List<Pair<Source, Long>>> {
return combine( return combine(
preferences.migrationSortingDirection().changes(), preferences.migrationSortingDirection.changes(),
preferences.migrationSortingMode().changes(), preferences.migrationSortingMode.changes(),
repository.getSourcesWithFavoriteCount(), repository.getSourcesWithFavoriteCount(),
) { direction, mode, list -> ) { direction, mode, list ->
list list
@@ -14,7 +14,7 @@ class RenameSourceCategory(
CreateSourceCategory.Result.Success -> {} CreateSourceCategory.Result.Success -> {}
} }
preferences.sourcesTabSourcesInCategories().getAndSet { sourcesInCategories -> preferences.sourcesTabSourcesInCategories.getAndSet { sourcesInCategories ->
sourcesInCategories.map { sourcesInCategories.map {
val index = it.indexOf('|') val index = it.indexOf('|')
if (index != -1 && it.substring(index + 1) == categoryOld) { if (index != -1 && it.substring(index + 1) == categoryOld) {
@@ -24,7 +24,7 @@ class RenameSourceCategory(
} }
}.toSet() }.toSet()
} }
preferences.sourcesTabCategories().getAndSet { preferences.sourcesTabCategories.getAndSet {
it.minus(categoryOld).plus(categoryNew) it.minus(categoryOld).plus(categoryNew)
} }
@@ -7,8 +7,8 @@ class SetMigrateSorting(
) { ) {
fun await(mode: Mode, direction: Direction) { fun await(mode: Mode, direction: Direction) {
preferences.migrationSortingMode().set(mode) preferences.migrationSortingMode.set(mode)
preferences.migrationSortingDirection().set(direction) preferences.migrationSortingDirection.set(direction)
} }
enum class Mode { enum class Mode {
@@ -10,7 +10,7 @@ class SetSourceCategories(
fun await(source: Source, sourceCategories: List<String>) { fun await(source: Source, sourceCategories: List<String>) {
val sourceIdString = source.id.toString() val sourceIdString = source.id.toString()
preferences.sourcesTabSourcesInCategories().getAndSet { sourcesInCategories -> preferences.sourcesTabSourcesInCategories.getAndSet { sourcesInCategories ->
val currentSourceCategories = sourcesInCategories.filterNot { val currentSourceCategories = sourcesInCategories.filterNot {
it.substringBefore('|') == sourceIdString it.substringBefore('|') == sourceIdString
} }
@@ -9,7 +9,7 @@ class ToggleExcludeFromDataSaver(
) { ) {
fun await(source: Source) { fun await(source: Source) {
preferences.dataSaverExcludedSources().getAndSet { preferences.dataSaverExcludedSources.getAndSet {
if (source.id.toString() in it) { if (source.id.toString() in it) {
it - source.id.toString() it - source.id.toString()
} else { } else {
@@ -7,7 +7,7 @@ class ToggleIncognito(
private val preferences: SourcePreferences, private val preferences: SourcePreferences,
) { ) {
fun await(extensions: String, enable: Boolean) { fun await(extensions: String, enable: Boolean) {
preferences.incognitoExtensions().getAndSet { preferences.incognitoExtensions.getAndSet {
if (enable) it.plus(extensions) else it.minus(extensions) if (enable) it.plus(extensions) else it.minus(extensions)
} }
} }
@@ -8,8 +8,8 @@ class ToggleLanguage(
) { ) {
fun await(language: String) { fun await(language: String) {
val isEnabled = language in preferences.enabledLanguages().get() val isEnabled = language in preferences.enabledLanguages.get()
preferences.enabledLanguages().getAndSet { enabled -> preferences.enabledLanguages.getAndSet { enabled ->
if (isEnabled) enabled.minus(language) else enabled.plus(language) if (isEnabled) enabled.minus(language) else enabled.plus(language)
} }
} }
@@ -13,19 +13,19 @@ class ToggleSource(
} }
fun await(sourceId: Long, enable: Boolean = isEnabled(sourceId)) { fun await(sourceId: Long, enable: Boolean = isEnabled(sourceId)) {
preferences.disabledSources().getAndSet { disabled -> preferences.disabledSources.getAndSet { disabled ->
if (enable) disabled.minus("$sourceId") else disabled.plus("$sourceId") if (enable) disabled.minus("$sourceId") else disabled.plus("$sourceId")
} }
} }
fun await(sourceIds: List<Long>, enable: Boolean) { fun await(sourceIds: List<Long>, enable: Boolean) {
val transformedSourceIds = sourceIds.map { it.toString() } val transformedSourceIds = sourceIds.map { it.toString() }
preferences.disabledSources().getAndSet { disabled -> preferences.disabledSources.getAndSet { disabled ->
if (enable) disabled.minus(transformedSourceIds) else disabled.plus(transformedSourceIds) if (enable) disabled.minus(transformedSourceIds) else disabled.plus(transformedSourceIds)
} }
} }
private fun isEnabled(sourceId: Long): Boolean { private fun isEnabled(sourceId: Long): Boolean {
return sourceId.toString() in preferences.disabledSources().get() return sourceId.toString() in preferences.disabledSources.get()
} }
} }
@@ -9,8 +9,8 @@ class ToggleSourcePin(
) { ) {
fun await(source: Source) { fun await(source: Source) {
val isPinned = source.id.toString() in preferences.pinnedSources().get() val isPinned = source.id.toString() in preferences.pinnedSources.get()
preferences.pinnedSources().getAndSet { pinned -> preferences.pinnedSources.getAndSet { pinned ->
if (isPinned) pinned.minus("${source.id}") else pinned.plus("${source.id}") if (isPinned) pinned.minus("${source.id}") else pinned.plus("${source.id}")
} }
} }
@@ -2,91 +2,133 @@ package eu.kanade.domain.source.service
import eu.kanade.domain.source.interactor.SetMigrateSorting import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import mihon.domain.migration.models.MigrationFlag
import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.core.common.preference.getEnum import tachiyomi.core.common.preference.getEnum
import tachiyomi.core.common.preference.getLongArray
import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibraryDisplayMode
class SourcePreferences( class SourcePreferences(
private val preferenceStore: PreferenceStore, preferenceStore: PreferenceStore,
) { ) {
fun sourceDisplayMode() = preferenceStore.getObject( val sourceDisplayMode: Preference<LibraryDisplayMode> = preferenceStore.getObjectFromString(
"pref_display_mode_catalogue", "pref_display_mode_catalogue",
LibraryDisplayMode.default, LibraryDisplayMode.default,
LibraryDisplayMode.Serializer::serialize, LibraryDisplayMode.Serializer::serialize,
LibraryDisplayMode.Serializer::deserialize, LibraryDisplayMode.Serializer::deserialize,
) )
fun enabledLanguages() = preferenceStore.getStringSet("source_languages", LocaleHelper.getDefaultEnabledLanguages()) val enabledLanguages: Preference<Set<String>> = preferenceStore.getStringSet(
"source_languages",
LocaleHelper.getDefaultEnabledLanguages(),
)
fun disabledSources() = preferenceStore.getStringSet("hidden_catalogues", emptySet()) val disabledSources: Preference<Set<String>> = preferenceStore.getStringSet("hidden_catalogues", emptySet())
fun incognitoExtensions() = preferenceStore.getStringSet("incognito_extensions", emptySet()) val incognitoExtensions: Preference<Set<String>> = preferenceStore.getStringSet("incognito_extensions", emptySet())
fun pinnedSources() = preferenceStore.getStringSet("pinned_catalogues", emptySet()) val pinnedSources: Preference<Set<String>> = preferenceStore.getStringSet("pinned_catalogues", emptySet())
fun lastUsedSource() = preferenceStore.getLong( val lastUsedSource: Preference<Long> = preferenceStore.getLong(
Preference.appStateKey("last_catalogue_source"), Preference.appStateKey("last_catalogue_source"),
-1, -1,
) )
fun showNsfwSource() = preferenceStore.getBoolean("show_nsfw_source", true) val showNsfwSource: Preference<Boolean> = preferenceStore.getBoolean("show_nsfw_source", true)
fun migrationSortingMode() = preferenceStore.getEnum("pref_migration_sorting", SetMigrateSorting.Mode.ALPHABETICAL) val migrationSortingMode: Preference<SetMigrateSorting.Mode> = preferenceStore.getEnum(
"pref_migration_sorting",
SetMigrateSorting.Mode.ALPHABETICAL,
)
fun migrationSortingDirection() = preferenceStore.getEnum( val migrationSortingDirection: Preference<SetMigrateSorting.Direction> = preferenceStore.getEnum(
"pref_migration_direction", "pref_migration_direction",
SetMigrateSorting.Direction.ASCENDING, SetMigrateSorting.Direction.ASCENDING,
) )
fun hideInLibraryItems() = preferenceStore.getBoolean("browse_hide_in_library_items", false) val hideInLibraryItems: Preference<Boolean> = preferenceStore.getBoolean("browse_hide_in_library_items", false)
fun extensionRepos() = preferenceStore.getStringSet("extension_repos", emptySet()) val extensionRepos: Preference<Set<String>> = preferenceStore.getStringSet("extension_repos", emptySet())
fun extensionUpdatesCount() = preferenceStore.getInt("ext_updates_count", 0) val extensionUpdatesCount: Preference<Int> = preferenceStore.getInt("ext_updates_count", 0)
fun trustedExtensions() = preferenceStore.getStringSet( val trustedExtensions: Preference<Set<String>> = preferenceStore.getStringSet(
Preference.appStateKey("trusted_extensions"), Preference.appStateKey("trusted_extensions"),
emptySet(), emptySet(),
) )
fun globalSearchFilterState() = preferenceStore.getBoolean( val globalSearchFilterState: Preference<Boolean> = preferenceStore.getBoolean(
Preference.appStateKey("has_filters_toggle_state"), Preference.appStateKey("has_filters_toggle_state"),
false, false,
) )
// SY --> // SY -->
fun enableSourceBlacklist() = preferenceStore.getBoolean("eh_enable_source_blacklist", true) val enableSourceBlacklist: Preference<Boolean> = preferenceStore.getBoolean("eh_enable_source_blacklist", true)
fun sourcesTabCategories() = preferenceStore.getStringSet("sources_tab_categories", mutableSetOf()) val sourcesTabCategories: Preference<Set<String>> = preferenceStore.getStringSet("sources_tab_categories", mutableSetOf())
fun sourcesTabCategoriesFilter() = preferenceStore.getBoolean("sources_tab_categories_filter", false) val sourcesTabCategoriesFilter: Preference<Boolean> = preferenceStore.getBoolean("sources_tab_categories_filter", false)
fun sourcesTabSourcesInCategories() = preferenceStore.getStringSet("sources_tab_source_categories", mutableSetOf()) val sourcesTabSourcesInCategories: Preference<Set<String>> = preferenceStore.getStringSet("sources_tab_source_categories", mutableSetOf())
fun dataSaver() = preferenceStore.getEnum("data_saver", DataSaver.NONE) val dataSaver: Preference<DataSaver> = preferenceStore.getEnum("data_saver", DataSaver.NONE)
fun dataSaverIgnoreJpeg() = preferenceStore.getBoolean("ignore_jpeg", false) val dataSaverIgnoreJpeg: Preference<Boolean> = preferenceStore.getBoolean("ignore_jpeg", false)
fun dataSaverIgnoreGif() = preferenceStore.getBoolean("ignore_gif", true) val dataSaverIgnoreGif: Preference<Boolean> = preferenceStore.getBoolean("ignore_gif", true)
fun dataSaverImageQuality() = preferenceStore.getInt("data_saver_image_quality", 80) val dataSaverImageQuality: Preference<Int> = preferenceStore.getInt("data_saver_image_quality", 80)
fun dataSaverImageFormatJpeg() = preferenceStore.getBoolean("data_saver_image_format_jpeg", false) val dataSaverImageFormatJpeg: Preference<Boolean> = preferenceStore.getBoolean("data_saver_image_format_jpeg", false)
fun dataSaverServer() = preferenceStore.getString("data_saver_server", "") val dataSaverServer: Preference<String> = preferenceStore.getString("data_saver_server", "")
fun dataSaverColorBW() = preferenceStore.getBoolean("data_saver_color_bw", false) val dataSaverColorBW: Preference<Boolean> = preferenceStore.getBoolean("data_saver_color_bw", false)
fun dataSaverExcludedSources() = preferenceStore.getStringSet("data_saver_excluded", emptySet()) val dataSaverExcludedSources: Preference<Set<String>> = preferenceStore.getStringSet("data_saver_excluded", emptySet())
fun dataSaverDownloader() = preferenceStore.getBoolean("data_saver_downloader", true) val dataSaverDownloader: Preference<Boolean> = preferenceStore.getBoolean("data_saver_downloader", true)
enum class DataSaver { enum class DataSaver {
NONE, NONE,
BANDWIDTH_HERO, BANDWIDTH_HERO,
WSRV_NL, WSRV_NL,
} }
val allowLocalSourceHiddenFolders: Preference<Boolean> = preferenceStore.getBoolean("allow_local_source_hidden_folders", false)
val preferredMangaDexId: Preference<String> = preferenceStore.getString("preferred_mangaDex_id", "0")
val mangadexSyncToLibraryIndexes: Preference<Set<String>> = preferenceStore.getStringSet(
"pref_mangadex_sync_to_library_indexes",
emptySet(),
)
val recommendationSearchFlags: Preference<Int> = preferenceStore.getInt("rec_search_flags", Int.MAX_VALUE)
// SY <-- // SY <--
val migrationSources: Preference<List<Long>> = preferenceStore.getLongArray("migration_sources", emptyList())
val migrationFlags: Preference<Set<MigrationFlag>> = preferenceStore.getObjectFromInt(
key = "migration_flags",
defaultValue = MigrationFlag.entries.toSet(),
serializer = { MigrationFlag.toBit(it) },
deserializer = { value: Int -> MigrationFlag.fromBit(value) },
)
val migrationDeepSearchMode: Preference<Boolean> = preferenceStore.getBoolean("migration_deep_search", false)
val migrationPrioritizeByChapters: Preference<Boolean> = preferenceStore.getBoolean(
"migration_prioritize_by_chapters",
false,
)
val migrationHideUnmatched: Preference<Boolean> = preferenceStore.getBoolean("migration_hide_unmatched", false)
val migrationHideWithoutUpdates: Preference<Boolean> = preferenceStore.getBoolean(
"migration_hide_without_updates",
false,
)
} }
@@ -9,21 +9,21 @@ import java.util.UUID
class SyncPreferences( class SyncPreferences(
private val preferenceStore: PreferenceStore, private val preferenceStore: PreferenceStore,
) { ) {
fun clientHost() = preferenceStore.getString("sync_client_host", "https://sync.tachiyomi.org") val clientHost: Preference<String> = preferenceStore.getString("sync_client_host", "https://sync.tachiyomi.org")
fun clientAPIKey() = preferenceStore.getString("sync_client_api_key", "") val clientAPIKey: Preference<String> = preferenceStore.getString("sync_client_api_key", "")
fun lastSyncTimestamp() = preferenceStore.getLong(Preference.appStateKey("last_sync_timestamp"), 0L) val lastSyncTimestamp: Preference<Long> = preferenceStore.getLong(Preference.appStateKey("last_sync_timestamp"), 0L)
fun lastSyncEtag() = preferenceStore.getString("sync_etag", "") val lastSyncEtag: Preference<String> = preferenceStore.getString("sync_etag", "")
fun syncInterval() = preferenceStore.getInt("sync_interval", 0) val syncInterval: Preference<Int> = preferenceStore.getInt("sync_interval", 0)
fun syncService() = preferenceStore.getInt("sync_service", 0) val syncService: Preference<Int> = preferenceStore.getInt("sync_service", 0)
fun googleDriveAccessToken() = preferenceStore.getString( val googleDriveAccessToken: Preference<String> = preferenceStore.getString(
Preference.appStateKey("google_drive_access_token"), Preference.appStateKey("google_drive_access_token"),
"", "",
) )
fun googleDriveRefreshToken() = preferenceStore.getString( val googleDriveRefreshToken: Preference<String> = preferenceStore.getString(
Preference.appStateKey("google_drive_refresh_token"), Preference.appStateKey("google_drive_refresh_token"),
"", "",
) )
@@ -42,7 +42,7 @@ class SyncPreferences(
} }
fun isSyncEnabled(): Boolean { fun isSyncEnabled(): Boolean {
return syncService().get() != 0 return syncService.get() != 0
} }
fun getSyncSettings(): SyncSettings { fun getSyncSettings(): SyncSettings {
@@ -34,17 +34,17 @@ class TrackPreferences(
fun trackToken(tracker: Tracker) = preferenceStore.getString(Preference.privateKey("track_token_${tracker.id}"), "") fun trackToken(tracker: Tracker) = preferenceStore.getString(Preference.privateKey("track_token_${tracker.id}"), "")
fun anilistScoreType() = preferenceStore.getString("anilist_score_type", Anilist.POINT_10) val anilistScoreType: Preference<String> = preferenceStore.getString("anilist_score_type", Anilist.POINT_10)
fun autoUpdateTrack() = preferenceStore.getBoolean("pref_auto_update_manga_sync_key", true) val autoUpdateTrack: Preference<Boolean> = preferenceStore.getBoolean("pref_auto_update_manga_sync_key", true)
fun autoUpdateTrackOnMarkRead() = preferenceStore.getEnum( val autoUpdateTrackOnMarkRead: Preference<AutoTrackState> = preferenceStore.getEnum(
"pref_auto_update_manga_on_mark_read", "pref_auto_update_manga_on_mark_read",
AutoTrackState.ALWAYS, AutoTrackState.ALWAYS,
) )
// SY --> // SY -->
fun resolveUsingSourceMetadata() = preferenceStore.getBoolean( val resolveUsingSourceMetadata: Preference<Boolean> = preferenceStore.getBoolean(
"pref_resolve_using_source_metadata_key", "pref_resolve_using_source_metadata_key",
true, true,
) )
@@ -6,6 +6,7 @@ import eu.kanade.domain.ui.model.TabletUiMode
import eu.kanade.domain.ui.model.ThemeMode import eu.kanade.domain.ui.model.ThemeMode
import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable
import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.core.common.preference.getEnum import tachiyomi.core.common.preference.getEnum
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@@ -13,10 +14,10 @@ import java.time.format.FormatStyle
import java.util.Locale import java.util.Locale
class UiPreferences( class UiPreferences(
private val preferenceStore: PreferenceStore, preferenceStore: PreferenceStore,
) { ) {
fun themeMode() = preferenceStore.getEnum( val themeMode = preferenceStore.getEnum(
"pref_theme_mode_key", "pref_theme_mode_key",
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ThemeMode.SYSTEM ThemeMode.SYSTEM
@@ -25,7 +26,7 @@ class UiPreferences(
}, },
) )
fun appTheme() = preferenceStore.getEnum( val appTheme: Preference<AppTheme> = preferenceStore.getEnum(
"pref_app_theme", "pref_app_theme",
if (DeviceUtil.isDynamicColorAvailable) { if (DeviceUtil.isDynamicColorAvailable) {
AppTheme.MONET AppTheme.MONET
@@ -34,35 +35,37 @@ class UiPreferences(
}, },
) )
fun themeDarkAmoled() = preferenceStore.getBoolean("pref_theme_dark_amoled_key", false) val themeDarkAmoled: Preference<Boolean> = preferenceStore.getBoolean("pref_theme_dark_amoled_key", false)
fun relativeTime() = preferenceStore.getBoolean("relative_time_v2", true) val relativeTime: Preference<Boolean> = preferenceStore.getBoolean("relative_time_v2", true)
fun dateFormat() = preferenceStore.getString("app_date_format", "") val dateFormat: Preference<String> = preferenceStore.getString("app_date_format", "")
fun tabletUiMode() = preferenceStore.getEnum("tablet_ui_mode", TabletUiMode.AUTOMATIC) val tabletUiMode: Preference<TabletUiMode> = preferenceStore.getEnum("tablet_ui_mode", TabletUiMode.AUTOMATIC)
val imagesInDescription: Preference<Boolean> = preferenceStore.getBoolean("pref_render_images_description", true)
// SY --> // SY -->
fun expandFilters() = preferenceStore.getBoolean("eh_expand_filters", false) val expandFilters: Preference<Boolean> = preferenceStore.getBoolean("eh_expand_filters", false)
fun hideFeedTab() = preferenceStore.getBoolean("hide_latest_tab", false) val hideFeedTab: Preference<Boolean> = preferenceStore.getBoolean("hide_latest_tab", false)
fun feedTabInFront() = preferenceStore.getBoolean("latest_tab_position", false) val feedTabInFront: Preference<Boolean> = preferenceStore.getBoolean("latest_tab_position", false)
fun recommendsInOverflow() = preferenceStore.getBoolean("recommends_in_overflow", false) val recommendsInOverflow: Preference<Boolean> = preferenceStore.getBoolean("recommends_in_overflow", false)
fun mergeInOverflow() = preferenceStore.getBoolean("merge_in_overflow", true) val mergeInOverflow: Preference<Boolean> = preferenceStore.getBoolean("merge_in_overflow", true)
fun previewsRowCount() = preferenceStore.getInt("pref_previews_row_count", 4) val previewsRowCount: Preference<Int> = preferenceStore.getInt("pref_previews_row_count", 4)
fun useNewSourceNavigation() = preferenceStore.getBoolean("use_new_source_navigation", true) val useNewSourceNavigation: Preference<Boolean> = preferenceStore.getBoolean("use_new_source_navigation", true)
fun bottomBarLabels() = preferenceStore.getBoolean("pref_show_bottom_bar_labels", true) val bottomBarLabels: Preference<Boolean> = preferenceStore.getBoolean("pref_show_bottom_bar_labels", true)
fun showNavUpdates() = preferenceStore.getBoolean("pref_show_updates_button", true) val showNavUpdates: Preference<Boolean> = preferenceStore.getBoolean("pref_show_updates_button", true)
fun showNavHistory() = preferenceStore.getBoolean("pref_show_history_button", true) val showNavHistory: Preference<Boolean> = preferenceStore.getBoolean("pref_show_history_button", true)
// SY <-- // SY <--
@@ -1,19 +1,16 @@
package eu.kanade.domain.ui.model package eu.kanade.domain.ui.model
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import eu.kanade.tachiyomi.util.system.isDevFlavor
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
enum class AppTheme(val titleRes: StringResource?) { enum class AppTheme(val titleRes: StringResource?) {
DEFAULT(MR.strings.label_default), DEFAULT(MR.strings.label_default),
MONET(MR.strings.theme_monet), MONET(MR.strings.theme_monet),
CATPPUCCIN(MR.strings.theme_catppuccin),
GREEN_APPLE(MR.strings.theme_greenapple), GREEN_APPLE(MR.strings.theme_greenapple),
LAVENDER(MR.strings.theme_lavender), LAVENDER(MR.strings.theme_lavender),
MIDNIGHT_DUSK(MR.strings.theme_midnightdusk), MIDNIGHT_DUSK(MR.strings.theme_midnightdusk),
NORD(MR.strings.theme_nord),
// TODO: re-enable for preview
NORD(MR.strings.theme_nord.takeIf { isDevFlavor || isPreviewBuildType }),
STRAWBERRY_DAIQUIRI(MR.strings.theme_strawberrydaiquiri), STRAWBERRY_DAIQUIRI(MR.strings.theme_strawberrydaiquiri),
TAKO(MR.strings.theme_tako), TAKO(MR.strings.theme_tako),
TEALTURQUOISE(MR.strings.theme_tealturquoise), TEALTURQUOISE(MR.strings.theme_tealturquoise),
@@ -82,10 +82,18 @@ fun BrowseSourceContent(
} }
} }
if (mangaList.itemCount <= 0 && errorState != null && errorState is LoadState.Error) { if (mangaList.itemCount == 0 && mangaList.loadState.refresh is LoadState.Loading) {
LoadingScreen(Modifier.padding(contentPadding))
return
}
if (mangaList.itemCount == 0) {
EmptyScreen( EmptyScreen(
modifier = Modifier.padding(contentPadding), modifier = Modifier.padding(contentPadding),
message = getErrorMessage(errorState), message = when (errorState) {
is LoadState.Error -> getErrorMessage(errorState)
else -> stringResource(MR.strings.no_results_found)
},
actions = if (source is LocalSource /* SY --> */ && onLocalSourceHelpClick != null /* SY <-- */) { actions = if (source is LocalSource /* SY --> */ && onLocalSourceHelpClick != null /* SY <-- */) {
persistentListOf( persistentListOf(
EmptyScreenAction( EmptyScreenAction(
@@ -104,7 +112,7 @@ fun BrowseSourceContent(
// SY --> // SY -->
if (onWebViewClick != null) { if (onWebViewClick != null) {
EmptyScreenAction( EmptyScreenAction(
MR.strings.action_open_in_web_view, stringRes = MR.strings.action_open_in_web_view,
icon = Icons.Outlined.Public, icon = Icons.Outlined.Public,
onClick = onWebViewClick, onClick = onWebViewClick,
) )
@@ -113,7 +121,7 @@ fun BrowseSourceContent(
}, },
if (onHelpClick != null) { if (onHelpClick != null) {
EmptyScreenAction( EmptyScreenAction(
MR.strings.label_help, stringRes = MR.strings.label_help,
icon = Icons.AutoMirrored.Outlined.HelpOutline, icon = Icons.AutoMirrored.Outlined.HelpOutline,
onClick = onHelpClick, onClick = onHelpClick,
) )
@@ -128,13 +136,6 @@ fun BrowseSourceContent(
return return
} }
if (mangaList.itemCount == 0 && mangaList.loadState.refresh is LoadState.Loading) {
LoadingScreen(
modifier = Modifier.padding(contentPadding),
)
return
}
// SY --> // SY -->
if (source?.isEhBasedSource() == true && ehentaiBrowseDisplayMode) { if (source?.isEhBasedSource() == true && ehentaiBrowseDisplayMode) {
BrowseSourceEHentaiList( BrowseSourceEHentaiList(
@@ -351,13 +351,17 @@ private fun ExtensionItemContent(
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall), horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall),
) { ) {
ProvideTextStyle(value = MaterialTheme.typography.bodySmall) { ProvideTextStyle(value = MaterialTheme.typography.bodySmall) {
var hasAlreadyShownAnElement by remember { mutableStateOf(false) }
if (extension is Extension.Installed && extension.lang.isNotEmpty()) { if (extension is Extension.Installed && extension.lang.isNotEmpty()) {
hasAlreadyShownAnElement = true
Text( Text(
text = LocaleHelper.getSourceDisplayName(extension.lang, LocalContext.current), text = LocaleHelper.getSourceDisplayName(extension.lang, LocalContext.current),
) )
} }
if (extension.versionName.isNotEmpty()) { if (extension.versionName.isNotEmpty()) {
if (hasAlreadyShownAnElement) DotSeparatorNoSpaceText()
hasAlreadyShownAnElement = true
Text( Text(
text = extension.versionName, text = extension.versionName,
) )
@@ -373,6 +377,8 @@ private fun ExtensionItemContent(
else -> null else -> null
} }
if (warning != null) { if (warning != null) {
if (hasAlreadyShownAnElement) DotSeparatorNoSpaceText()
hasAlreadyShownAnElement = true
Text( Text(
text = stringResource(warning).uppercase(), text = stringResource(warning).uppercase(),
color = MaterialTheme.colorScheme.error, color = MaterialTheme.colorScheme.error,
@@ -380,6 +386,12 @@ private fun ExtensionItemContent(
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
) )
} }
if (extension is Extension.Installed && !extension.isShared) {
if (hasAlreadyShownAnElement) DotSeparatorNoSpaceText()
Text(
text = stringResource(MR.strings.ext_installer_private),
)
}
if (!installStep.isCompleted()) { if (!installStep.isCompleted()) {
DotSeparatorNoSpaceText() DotSeparatorNoSpaceText()
@@ -41,6 +41,7 @@ fun GlobalSearchScreen(
navigateUp = navigateUp, navigateUp = navigateUp,
onChangeSearchQuery = onChangeSearchQuery, onChangeSearchQuery = onChangeSearchQuery,
onSearch = onSearch, onSearch = onSearch,
hideSourceFilter = false,
sourceFilter = state.sourceFilter, sourceFilter = state.sourceFilter,
onChangeSearchFilter = onChangeSearchFilter, onChangeSearchFilter = onChangeSearchFilter,
onlyShowHasResults = state.onlyShowHasResults, onlyShowHasResults = state.onlyShowHasResults,
@@ -1,84 +0,0 @@
package eu.kanade.presentation.browse
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.manga.components.BaseMangaListItem
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaScreenModel
import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.EmptyScreen
@Composable
fun MigrateMangaScreen(
navigateUp: () -> Unit,
title: String?,
state: MigrateMangaScreenModel.State,
onClickItem: (Manga) -> Unit,
onClickCover: (Manga) -> Unit,
) {
Scaffold(
topBar = { scrollBehavior ->
AppBar(
title = title,
navigateUp = navigateUp,
scrollBehavior = scrollBehavior,
)
},
) { contentPadding ->
if (state.isEmpty) {
EmptyScreen(
stringRes = MR.strings.empty_screen,
modifier = Modifier.padding(contentPadding),
)
return@Scaffold
}
MigrateMangaContent(
contentPadding = contentPadding,
state = state,
onClickItem = onClickItem,
onClickCover = onClickCover,
)
}
}
@Composable
private fun MigrateMangaContent(
contentPadding: PaddingValues,
state: MigrateMangaScreenModel.State,
onClickItem: (Manga) -> Unit,
onClickCover: (Manga) -> Unit,
) {
FastScrollLazyColumn(
contentPadding = contentPadding,
) {
items(state.titles) { manga ->
MigrateMangaItem(
manga = manga,
onClickItem = onClickItem,
onClickCover = onClickCover,
)
}
}
}
@Composable
private fun MigrateMangaItem(
manga: Manga,
onClickItem: (Manga) -> Unit,
onClickCover: (Manga) -> Unit,
modifier: Modifier = Modifier,
) {
BaseMangaListItem(
modifier = modifier,
manga = manga,
onClickItem = { onClickItem(manga) },
onClickCover = { onClickCover(manga) },
)
}
@@ -32,6 +32,7 @@ fun MigrateSearchScreen(
navigateUp = navigateUp, navigateUp = navigateUp,
onChangeSearchQuery = onChangeSearchQuery, onChangeSearchQuery = onChangeSearchQuery,
onSearch = onSearch, onSearch = onSearch,
hideSourceFilter = true,
sourceFilter = state.sourceFilter, sourceFilter = state.sourceFilter,
onChangeSearchFilter = onChangeSearchFilter, onChangeSearchFilter = onChangeSearchFilter,
onlyShowHasResults = state.onlyShowHasResults, onlyShowHasResults = state.onlyShowHasResults,
@@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowForward import androidx.compose.material.icons.automirrored.outlined.ArrowForward
import androidx.compose.material.icons.outlined.ArrowForward
import androidx.compose.material.icons.outlined.ContentCopy import androidx.compose.material.icons.outlined.ContentCopy
import androidx.compose.material.icons.outlined.CopyAll import androidx.compose.material.icons.outlined.CopyAll
import androidx.compose.material.icons.outlined.Done import androidx.compose.material.icons.outlined.Done
@@ -30,8 +30,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.paging.LoadState import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.LazyPagingItems
import com.gowtham.ratingbar.RatingBar import com.gowtham.ratingbar.ComposeStars
import com.gowtham.ratingbar.RatingBarConfig import com.gowtham.ratingbar.RatingBarStyle
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import eu.kanade.presentation.manga.components.MangaCover import eu.kanade.presentation.manga.components.MangaCover
import exh.metadata.MetadataUtil import exh.metadata.MetadataUtil
@@ -222,17 +222,18 @@ fun BrowseSourceEHentaiListItem(
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
horizontalAlignment = Alignment.Start, horizontalAlignment = Alignment.Start,
) { ) {
RatingBar( ComposeStars(
value = rating, value = rating,
onValueChange = {}, numOfStars = 5,
onRatingChanged = {}, size = 18.dp,
config = RatingBarConfig().apply { spaceBetween = 2.dp,
isIndicator(true) hideInactiveStars = false,
numStars(5) style = RatingBarStyle.Fill(
size(18.dp) activeColor = Color(0xFF005ED7),
activeColor(Color(0xFF005ED7)) inActiveColor = Color(0xE1E2ECFF),
inactiveColor(Color(0xE1E2ECFF)) ),
}, painterEmpty = null,
painterFilled = null,
) )
val color = genre?.first?.color val color = genre?.first?.color
val res = genre?.second val res = genre?.second
@@ -3,12 +3,12 @@ package eu.kanade.presentation.browse.components
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.FilterList import androidx.compose.material.icons.outlined.FilterList
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.SmallExtendedFloatingActionButton
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR import tachiyomi.i18n.sy.SYMR
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
@Composable @Composable
@@ -17,7 +17,7 @@ fun BrowseSourceFloatingActionButton(
onFabClick: () -> Unit, onFabClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
ExtendedFloatingActionButton( SmallExtendedFloatingActionButton(
modifier = modifier, modifier = modifier,
text = { text = {
Text( Text(
@@ -4,7 +4,6 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ViewList import androidx.compose.material.icons.automirrored.filled.ViewList
import androidx.compose.material.icons.automirrored.outlined.Help import androidx.compose.material.icons.automirrored.outlined.Help
import androidx.compose.material.icons.filled.ViewModule import androidx.compose.material.icons.filled.ViewModule
import androidx.compose.material.icons.outlined.Help
import androidx.compose.material.icons.outlined.Public import androidx.compose.material.icons.outlined.Public
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.TopAppBarScrollBehavior
@@ -40,6 +40,7 @@ fun GlobalSearchToolbar(
navigateUp: () -> Unit, navigateUp: () -> Unit,
onChangeSearchQuery: (String?) -> Unit, onChangeSearchQuery: (String?) -> Unit,
onSearch: (String) -> Unit, onSearch: (String) -> Unit,
hideSourceFilter: Boolean,
sourceFilter: SourceFilter, sourceFilter: SourceFilter,
onChangeSearchFilter: (SourceFilter) -> Unit, onChangeSearchFilter: (SourceFilter) -> Unit,
onlyShowHasResults: Boolean, onlyShowHasResults: Boolean,
@@ -73,6 +74,7 @@ fun GlobalSearchToolbar(
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
) { ) {
// TODO: make this UX better; it only applies when triggering a new search // TODO: make this UX better; it only applies when triggering a new search
if (!hideSourceFilter) {
FilterChip( FilterChip(
selected = sourceFilter == SourceFilter.PinnedOnly, selected = sourceFilter == SourceFilter.PinnedOnly,
onClick = { onChangeSearchFilter(SourceFilter.PinnedOnly) }, onClick = { onChangeSearchFilter(SourceFilter.PinnedOnly) },
@@ -105,6 +107,7 @@ fun GlobalSearchToolbar(
) )
VerticalDivider() VerticalDivider()
}
FilterChip( FilterChip(
selected = onlyShowHasResults, selected = onlyShowHasResults,
@@ -4,11 +4,11 @@ import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Add import androidx.compose.material.icons.outlined.Add
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.SmallExtendedFloatingActionButton
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.shouldExpandFAB import tachiyomi.presentation.core.util.shouldExpandFAB
@@ -18,7 +18,7 @@ fun CategoryFloatingActionButton(
onCreate: () -> Unit, onCreate: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
ExtendedFloatingActionButton( SmallExtendedFloatingActionButton(
text = { Text(text = stringResource(MR.strings.action_add)) }, text = { Text(text = stringResource(MR.strings.action_add)) },
icon = { Icon(imageVector = Icons.Outlined.Add, contentDescription = null) }, icon = { Icon(imageVector = Icons.Outlined.Add, contentDescription = null) },
onClick = onCreate, onClick = onCreate,
@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Label import androidx.compose.material.icons.automirrored.outlined.Label
import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.Label
import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
@@ -9,7 +9,6 @@ import androidx.compose.material.icons.automirrored.outlined.Label
import androidx.compose.material.icons.outlined.ArrowDropDown import androidx.compose.material.icons.outlined.ArrowDropDown
import androidx.compose.material.icons.outlined.ArrowDropUp import androidx.compose.material.icons.outlined.ArrowDropUp
import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.Label
import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
@@ -9,7 +9,6 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.Label import androidx.compose.material.icons.automirrored.outlined.Label
import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.Edit import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material.icons.outlined.Label
import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
@@ -1,6 +1,5 @@
package eu.kanade.presentation.components package eu.kanade.presentation.components
import androidx.activity.compose.BackHandler
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
@@ -28,8 +27,8 @@ fun NavigatorAdaptiveSheet(
screen = screen, screen = screen,
content = { sheetNavigator -> content = { sheetNavigator ->
AdaptiveSheet( AdaptiveSheet(
enableSwipeDismiss = enableSwipeDismiss(sheetNavigator),
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
enableSwipeDismiss = enableSwipeDismiss(sheetNavigator),
) { ) {
ScreenTransition( ScreenTransition(
navigator = sheetNavigator, navigator = sheetNavigator,
@@ -38,11 +37,6 @@ fun NavigatorAdaptiveSheet(
fadeOut(animationSpec = tween(90)) fadeOut(animationSpec = tween(90))
}, },
) )
BackHandler(
enabled = sheetNavigator.size > 1,
onBack = sheetNavigator::pop,
)
} }
// Make sure screens are disposed no matter what // Make sure screens are disposed no matter what
@@ -79,10 +73,10 @@ fun AdaptiveSheet(
properties = dialogProperties, properties = dialogProperties,
) { ) {
AdaptiveSheetImpl( AdaptiveSheetImpl(
modifier = modifier,
isTabletUi = isTabletUi, isTabletUi = isTabletUi,
enableSwipeDismiss = enableSwipeDismiss, enableSwipeDismiss = enableSwipeDismiss,
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
modifier = modifier,
) { ) {
content() content()
} }
@@ -21,8 +21,9 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PlainTooltip import androidx.compose.material3.PlainTooltip
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TooltipAnchorPosition
import androidx.compose.material3.TooltipBox import androidx.compose.material3.TooltipBox
import androidx.compose.material3.TooltipDefaults import androidx.compose.material3.TooltipDefaults.rememberTooltipPositionProvider
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.TopAppBarScrollBehavior
@@ -36,6 +37,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@@ -194,13 +196,14 @@ fun AppBarActions(
actions.filterIsInstance<AppBar.Action>().map { actions.filterIsInstance<AppBar.Action>().map {
TooltipBox( TooltipBox(
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), positionProvider = rememberTooltipPositionProvider(TooltipAnchorPosition.Above),
tooltip = { tooltip = {
PlainTooltip { PlainTooltip {
Text(it.title) Text(it.title)
} }
}, },
state = rememberTooltipState(), state = rememberTooltipState(),
focusable = false,
) { ) {
IconButton( IconButton(
onClick = it.onClick, onClick = it.onClick,
@@ -218,13 +221,14 @@ fun AppBarActions(
val overflowActions = actions.filterIsInstance<AppBar.OverflowAction>() val overflowActions = actions.filterIsInstance<AppBar.OverflowAction>()
if (overflowActions.isNotEmpty()) { if (overflowActions.isNotEmpty()) {
TooltipBox( TooltipBox(
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), positionProvider = rememberTooltipPositionProvider(TooltipAnchorPosition.Above),
tooltip = { tooltip = {
PlainTooltip { PlainTooltip {
Text(stringResource(MR.strings.action_menu_overflow_description)) Text(stringResource(MR.strings.action_menu_overflow_description))
} }
}, },
state = rememberTooltipState(), state = rememberTooltipState(),
focusable = false,
) { ) {
IconButton( IconButton(
onClick = { showMenu = !showMenu }, onClick = { showMenu = !showMenu },
@@ -289,6 +293,7 @@ fun SearchToolbar(
onSearch(searchQuery) onSearch(searchQuery)
focusManager.clearFocus() focusManager.clearFocus()
keyboardController?.hide() keyboardController?.hide()
focusManager.moveFocus(FocusDirection.Next)
} }
BasicTextField( BasicTextField(
@@ -345,13 +350,14 @@ fun SearchToolbar(
// Don't show search action // Don't show search action
} else if (searchQuery == null) { } else if (searchQuery == null) {
TooltipBox( TooltipBox(
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), positionProvider = rememberTooltipPositionProvider(TooltipAnchorPosition.Above),
tooltip = { tooltip = {
PlainTooltip { PlainTooltip {
Text(stringResource(MR.strings.action_search)) Text(stringResource(MR.strings.action_search))
} }
}, },
state = rememberTooltipState(), state = rememberTooltipState(),
focusable = false,
) { ) {
IconButton( IconButton(
onClick = onClick, onClick = onClick,
@@ -364,13 +370,14 @@ fun SearchToolbar(
} }
} else if (searchQuery.isNotEmpty()) { } else if (searchQuery.isNotEmpty()) {
TooltipBox( TooltipBox(
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), positionProvider = rememberTooltipPositionProvider(TooltipAnchorPosition.Above),
tooltip = { tooltip = {
PlainTooltip { PlainTooltip {
Text(stringResource(MR.strings.action_reset)) Text(stringResource(MR.strings.action_reset))
} }
}, },
state = rememberTooltipState(), state = rememberTooltipState(),
focusable = false,
) { ) {
IconButton( IconButton(
onClick = { onClick = {
@@ -22,7 +22,7 @@ fun relativeDateText(
Instant.ofEpochMilli(dateEpochMillis), Instant.ofEpochMilli(dateEpochMillis),
ZoneId.systemDefault(), ZoneId.systemDefault(),
) )
.takeIf { dateEpochMillis > 0L }, .takeIf { dateEpochMillis != 0L },
) )
} }
@@ -33,8 +33,8 @@ fun relativeDateText(
val context = LocalContext.current val context = LocalContext.current
val preferences = remember { Injekt.get<UiPreferences>() } val preferences = remember { Injekt.get<UiPreferences>() }
val relativeTime = remember { preferences.relativeTime().get() } val relativeTime = remember { preferences.relativeTime.get() }
val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) } val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat.get()) }
return localDate?.toRelativeString( return localDate?.toRelativeString(
context = context, context = context,
@@ -4,6 +4,7 @@ import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.DpOffset
import eu.kanade.presentation.manga.DownloadAction import eu.kanade.presentation.manga.DownloadAction
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
@@ -12,10 +13,44 @@ import tachiyomi.presentation.core.i18n.stringResource
@Composable @Composable
fun DownloadDropdownMenu( fun DownloadDropdownMenu(
modifier: Modifier = Modifier,
expanded: Boolean, expanded: Boolean,
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
onDownloadClicked: (DownloadAction) -> Unit, onDownloadClicked: (DownloadAction) -> Unit,
modifier: Modifier = Modifier, offset: DpOffset? = null,
) {
if (offset != null) {
DropdownMenu(
expanded = expanded,
onDismissRequest = onDismissRequest,
modifier = modifier,
offset = offset,
content = {
DownloadDropdownMenuItems(
onDismissRequest = onDismissRequest,
onDownloadClicked = onDownloadClicked,
)
},
)
} else {
DropdownMenu(
expanded = expanded,
onDismissRequest = onDismissRequest,
modifier = modifier,
content = {
DownloadDropdownMenuItems(
onDismissRequest = onDismissRequest,
onDownloadClicked = onDownloadClicked,
)
},
)
}
}
@Composable
private fun DownloadDropdownMenuItems(
onDismissRequest: () -> Unit,
onDownloadClicked: (DownloadAction) -> Unit,
) { ) {
val options = persistentListOf( val options = persistentListOf(
DownloadAction.NEXT_1_CHAPTER to pluralStringResource(MR.plurals.download_amount, 1, 1), DownloadAction.NEXT_1_CHAPTER to pluralStringResource(MR.plurals.download_amount, 1, 1),
@@ -23,13 +58,9 @@ fun DownloadDropdownMenu(
DownloadAction.NEXT_10_CHAPTERS to pluralStringResource(MR.plurals.download_amount, 10, 10), DownloadAction.NEXT_10_CHAPTERS to pluralStringResource(MR.plurals.download_amount, 10, 10),
DownloadAction.NEXT_25_CHAPTERS to pluralStringResource(MR.plurals.download_amount, 25, 25), DownloadAction.NEXT_25_CHAPTERS to pluralStringResource(MR.plurals.download_amount, 25, 25),
DownloadAction.UNREAD_CHAPTERS to stringResource(MR.strings.download_unread), DownloadAction.UNREAD_CHAPTERS to stringResource(MR.strings.download_unread),
DownloadAction.BOOKMARKED_CHAPTERS to stringResource(MR.strings.download_bookmarked),
) )
DropdownMenu(
expanded = expanded,
onDismissRequest = onDismissRequest,
modifier = modifier,
) {
options.map { (downloadAction, string) -> options.map { (downloadAction, string) ->
DropdownMenuItem( DropdownMenuItem(
text = { Text(text = string) }, text = { Text(text = string) },
@@ -40,4 +71,3 @@ fun DownloadDropdownMenu(
) )
} }
} }
}
@@ -100,9 +100,9 @@ fun LibrarySettingsDialog(
private fun ColumnScope.FilterPage( private fun ColumnScope.FilterPage(
screenModel: LibrarySettingsScreenModel, screenModel: LibrarySettingsScreenModel,
) { ) {
val filterDownloaded by screenModel.libraryPreferences.filterDownloaded().collectAsState() val filterDownloaded by screenModel.libraryPreferences.filterDownloaded.collectAsState()
val downloadedOnly by screenModel.preferences.downloadedOnly().collectAsState() val downloadedOnly by screenModel.preferences.downloadedOnly.collectAsState()
val autoUpdateMangaRestrictions by screenModel.libraryPreferences.autoUpdateMangaRestrictions().collectAsState() val autoUpdateMangaRestrictions by screenModel.libraryPreferences.autoUpdateMangaRestrictions.collectAsState()
TriStateItem( TriStateItem(
label = stringResource(MR.strings.label_downloaded), label = stringResource(MR.strings.label_downloaded),
@@ -114,25 +114,25 @@ private fun ColumnScope.FilterPage(
enabled = !downloadedOnly, enabled = !downloadedOnly,
onClick = { screenModel.toggleFilter(LibraryPreferences::filterDownloaded) }, onClick = { screenModel.toggleFilter(LibraryPreferences::filterDownloaded) },
) )
val filterUnread by screenModel.libraryPreferences.filterUnread().collectAsState() val filterUnread by screenModel.libraryPreferences.filterUnread.collectAsState()
TriStateItem( TriStateItem(
label = stringResource(MR.strings.action_filter_unread), label = stringResource(MR.strings.action_filter_unread),
state = filterUnread, state = filterUnread,
onClick = { screenModel.toggleFilter(LibraryPreferences::filterUnread) }, onClick = { screenModel.toggleFilter(LibraryPreferences::filterUnread) },
) )
val filterStarted by screenModel.libraryPreferences.filterStarted().collectAsState() val filterStarted by screenModel.libraryPreferences.filterStarted.collectAsState()
TriStateItem( TriStateItem(
label = stringResource(MR.strings.label_started), label = stringResource(MR.strings.label_started),
state = filterStarted, state = filterStarted,
onClick = { screenModel.toggleFilter(LibraryPreferences::filterStarted) }, onClick = { screenModel.toggleFilter(LibraryPreferences::filterStarted) },
) )
val filterBookmarked by screenModel.libraryPreferences.filterBookmarked().collectAsState() val filterBookmarked by screenModel.libraryPreferences.filterBookmarked.collectAsState()
TriStateItem( TriStateItem(
label = stringResource(MR.strings.action_filter_bookmarked), label = stringResource(MR.strings.action_filter_bookmarked),
state = filterBookmarked, state = filterBookmarked,
onClick = { screenModel.toggleFilter(LibraryPreferences::filterBookmarked) }, onClick = { screenModel.toggleFilter(LibraryPreferences::filterBookmarked) },
) )
val filterCompleted by screenModel.libraryPreferences.filterCompleted().collectAsState() val filterCompleted by screenModel.libraryPreferences.filterCompleted.collectAsState()
TriStateItem( TriStateItem(
label = stringResource(MR.strings.completed), label = stringResource(MR.strings.completed),
state = filterCompleted, state = filterCompleted,
@@ -143,7 +143,7 @@ private fun ColumnScope.FilterPage(
(isDevFlavor || isPreviewBuildType) && (isDevFlavor || isPreviewBuildType) &&
LibraryPreferences.MANGA_OUTSIDE_RELEASE_PERIOD in autoUpdateMangaRestrictions LibraryPreferences.MANGA_OUTSIDE_RELEASE_PERIOD in autoUpdateMangaRestrictions
) { ) {
val filterIntervalCustom by screenModel.libraryPreferences.filterIntervalCustom().collectAsState() val filterIntervalCustom by screenModel.libraryPreferences.filterIntervalCustom.collectAsState()
TriStateItem( TriStateItem(
label = stringResource(MR.strings.action_filter_interval_custom), label = stringResource(MR.strings.action_filter_interval_custom),
state = filterIntervalCustom, state = filterIntervalCustom,
@@ -151,7 +151,7 @@ private fun ColumnScope.FilterPage(
) )
} }
// SY --> // SY -->
val filterLewd by screenModel.libraryPreferences.filterLewd().collectAsState() val filterLewd by screenModel.libraryPreferences.filterLewd.collectAsState()
TriStateItem( TriStateItem(
label = stringResource(SYMR.strings.lewd), label = stringResource(SYMR.strings.lewd),
state = filterLewd, state = filterLewd,
@@ -194,7 +194,7 @@ private fun ColumnScope.SortPage(
) { ) {
val trackers by screenModel.trackersFlow.collectAsState() val trackers by screenModel.trackersFlow.collectAsState()
// SY --> // SY -->
val globalSortMode by screenModel.libraryPreferences.sortingMode().collectAsState() val globalSortMode by screenModel.libraryPreferences.sortingMode.collectAsState()
val sortingMode = if (screenModel.grouping == LibraryGroup.BY_DEFAULT) { val sortingMode = if (screenModel.grouping == LibraryGroup.BY_DEFAULT) {
category.sort.type category.sort.type
} else { } else {
@@ -206,9 +206,9 @@ private fun ColumnScope.SortPage(
!globalSortMode.isAscending !globalSortMode.isAscending
} }
val hasSortTags by remember { val hasSortTags by remember {
screenModel.libraryPreferences.sortTagsForLibrary().changes() screenModel.libraryPreferences.sortTagsForLibrary.changes()
.map { it.isNotEmpty() } .map { it.isNotEmpty() }
}.collectAsState(initial = screenModel.libraryPreferences.sortTagsForLibrary().get().isNotEmpty()) }.collectAsState(initial = screenModel.libraryPreferences.sortTagsForLibrary.get().isNotEmpty())
// SY <-- // SY <--
val options = remember(trackers.isEmpty()/* SY --> */, hasSortTags/* SY <-- */) { val options = remember(trackers.isEmpty()/* SY --> */, hasSortTags/* SY <-- */) {
@@ -287,7 +287,7 @@ private val displayModes = listOf(
private fun ColumnScope.DisplayPage( private fun ColumnScope.DisplayPage(
screenModel: LibrarySettingsScreenModel, screenModel: LibrarySettingsScreenModel,
) { ) {
val displayMode by screenModel.libraryPreferences.displayMode().collectAsState() val displayMode by screenModel.libraryPreferences.displayMode.collectAsState()
SettingsChipRow(MR.strings.action_display_mode) { SettingsChipRow(MR.strings.action_display_mode) {
displayModes.map { (titleRes, mode) -> displayModes.map { (titleRes, mode) ->
FilterChip( FilterChip(
@@ -302,18 +302,18 @@ private fun ColumnScope.DisplayPage(
val configuration = LocalConfiguration.current val configuration = LocalConfiguration.current
val columnPreference = remember { val columnPreference = remember {
if (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { if (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
screenModel.libraryPreferences.landscapeColumns() screenModel.libraryPreferences.landscapeColumns
} else { } else {
screenModel.libraryPreferences.portraitColumns() screenModel.libraryPreferences.portraitColumns
} }
} }
val columns by columnPreference.collectAsState() val columns by columnPreference.collectAsState()
SliderItem( SliderItem(
label = stringResource(MR.strings.pref_library_columns),
max = 10,
value = columns, value = columns,
valueText = if (columns > 0) { valueRange = 0..10,
label = stringResource(MR.strings.pref_library_columns),
valueString = if (columns > 0) {
columns.toString() columns.toString()
} else { } else {
stringResource(MR.strings.label_auto) stringResource(MR.strings.label_auto)
@@ -326,29 +326,33 @@ private fun ColumnScope.DisplayPage(
HeadingItem(MR.strings.overlay_header) HeadingItem(MR.strings.overlay_header)
CheckboxItem( CheckboxItem(
label = stringResource(MR.strings.action_display_download_badge), label = stringResource(MR.strings.action_display_download_badge),
pref = screenModel.libraryPreferences.downloadBadge(), pref = screenModel.libraryPreferences.downloadBadge,
)
CheckboxItem(
label = stringResource(MR.strings.action_display_unread_badge),
pref = screenModel.libraryPreferences.unreadBadge,
) )
CheckboxItem( CheckboxItem(
label = stringResource(MR.strings.action_display_local_badge), label = stringResource(MR.strings.action_display_local_badge),
pref = screenModel.libraryPreferences.localBadge(), pref = screenModel.libraryPreferences.localBadge,
) )
CheckboxItem( CheckboxItem(
label = stringResource(MR.strings.action_display_language_badge), label = stringResource(MR.strings.action_display_language_badge),
pref = screenModel.libraryPreferences.languageBadge(), pref = screenModel.libraryPreferences.languageBadge,
) )
CheckboxItem( CheckboxItem(
label = stringResource(MR.strings.action_display_show_continue_reading_button), label = stringResource(MR.strings.action_display_show_continue_reading_button),
pref = screenModel.libraryPreferences.showContinueReadingButton(), pref = screenModel.libraryPreferences.showContinueReadingButton,
) )
HeadingItem(MR.strings.tabs_header) HeadingItem(MR.strings.tabs_header)
CheckboxItem( CheckboxItem(
label = stringResource(MR.strings.action_display_show_tabs), label = stringResource(MR.strings.action_display_show_tabs),
pref = screenModel.libraryPreferences.categoryTabs(), pref = screenModel.libraryPreferences.categoryTabs,
) )
CheckboxItem( CheckboxItem(
label = stringResource(MR.strings.action_display_show_number_of_items), label = stringResource(MR.strings.action_display_show_number_of_items),
pref = screenModel.libraryPreferences.categoryNumberOfItems(), pref = screenModel.libraryPreferences.categoryNumberOfItems,
) )
} }
@@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.util.fastAny
import eu.kanade.tachiyomi.ui.library.LibraryItem import eu.kanade.tachiyomi.ui.library.LibraryItem
import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.library.model.LibraryManga
import tachiyomi.domain.manga.model.MangaCover import tachiyomi.domain.manga.model.MangaCover
@@ -15,7 +14,7 @@ internal fun LibraryComfortableGrid(
items: List<LibraryItem>, items: List<LibraryItem>,
columns: Int, columns: Int,
contentPadding: PaddingValues, contentPadding: PaddingValues,
selection: List<LibraryManga>, selection: Set<Long>,
onClick: (LibraryManga) -> Unit, onClick: (LibraryManga) -> Unit,
onLongClick: (LibraryManga) -> Unit, onLongClick: (LibraryManga) -> Unit,
onClickContinueReading: ((LibraryManga) -> Unit)?, onClickContinueReading: ((LibraryManga) -> Unit)?,
@@ -35,7 +34,7 @@ internal fun LibraryComfortableGrid(
) { libraryItem -> ) { libraryItem ->
val manga = libraryItem.libraryManga.manga val manga = libraryItem.libraryManga.manga
MangaComfortableGridItem( MangaComfortableGridItem(
isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id }, isSelected = manga.id in selection,
title = manga.title, title = manga.title,
coverData = MangaCover( coverData = MangaCover(
mangaId = manga.id, mangaId = manga.id,
@@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.util.fastAny
import eu.kanade.tachiyomi.ui.library.LibraryItem import eu.kanade.tachiyomi.ui.library.LibraryItem
import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.library.model.LibraryManga
import tachiyomi.domain.manga.model.MangaCover import tachiyomi.domain.manga.model.MangaCover
@@ -16,7 +15,7 @@ internal fun LibraryCompactGrid(
showTitle: Boolean, showTitle: Boolean,
columns: Int, columns: Int,
contentPadding: PaddingValues, contentPadding: PaddingValues,
selection: List<LibraryManga>, selection: Set<Long>,
onClick: (LibraryManga) -> Unit, onClick: (LibraryManga) -> Unit,
onLongClick: (LibraryManga) -> Unit, onLongClick: (LibraryManga) -> Unit,
onClickContinueReading: ((LibraryManga) -> Unit)?, onClickContinueReading: ((LibraryManga) -> Unit)?,
@@ -36,7 +35,7 @@ internal fun LibraryCompactGrid(
) { libraryItem -> ) { libraryItem ->
val manga = libraryItem.libraryManga.manga val manga = libraryItem.libraryManga.manga
MangaCompactGridItem( MangaCompactGridItem(
isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id }, isSelected = manga.id in selection,
title = manga.title.takeIf { showTitle }, title = manga.title.takeIf { showTitle },
coverData = MangaCover( coverData = MangaCover(
mangaId = manga.id, mangaId = manga.id,
@@ -29,22 +29,22 @@ import kotlin.time.Duration.Companion.seconds
fun LibraryContent( fun LibraryContent(
categories: List<Category>, categories: List<Category>,
searchQuery: String?, searchQuery: String?,
selection: List<LibraryManga>, selection: Set<Long>,
contentPadding: PaddingValues, contentPadding: PaddingValues,
currentPage: () -> Int, currentPage: Int,
hasActiveFilters: Boolean, hasActiveFilters: Boolean,
showPageTabs: Boolean, showPageTabs: Boolean,
onChangeCurrentPage: (Int) -> Unit, onChangeCurrentPage: (Int) -> Unit,
onMangaClicked: (Long) -> Unit, onClickManga: (Long) -> Unit,
onContinueReadingClicked: ((LibraryManga) -> Unit)?, onContinueReadingClicked: ((LibraryManga) -> Unit)?,
onToggleSelection: (LibraryManga) -> Unit, onToggleSelection: (Category, LibraryManga) -> Unit,
onToggleRangeSelection: (LibraryManga) -> Unit, onToggleRangeSelection: (Category, LibraryManga) -> Unit,
onRefresh: (Category?) -> Boolean, onRefresh: () -> Boolean,
onGlobalSearchClicked: () -> Unit, onGlobalSearchClicked: () -> Unit,
getNumberOfMangaForCategory: (Category) -> Int?, getItemCountForCategory: (Category) -> Int?,
getDisplayMode: (Int) -> PreferenceMutableState<LibraryDisplayMode>, getDisplayMode: (Int) -> PreferenceMutableState<LibraryDisplayMode>,
getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>, getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>,
getLibraryForPage: (Int) -> List<LibraryItem>, getItemsForCategory: (Category) -> List<LibraryItem>,
) { ) {
Column( Column(
modifier = Modifier.padding( modifier = Modifier.padding(
@@ -53,15 +53,14 @@ fun LibraryContent(
end = contentPadding.calculateEndPadding(LocalLayoutDirection.current), end = contentPadding.calculateEndPadding(LocalLayoutDirection.current),
), ),
) { ) {
// SY --> val coercedCurrentPage = remember(categories, currentPage) { currentPage.coerceIn(0, categories.lastIndex) }
val coercedCurrentPage = remember(categories) { currentPage().coerceIn(0, categories.lastIndex) }
val pagerState = rememberPagerState(coercedCurrentPage) { categories.size }
// SY <-- // SY <--
val pagerState = rememberPagerState(coercedCurrentPage) { categories.size }
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
var isRefreshing by remember(pagerState.currentPage) { mutableStateOf(false) } var isRefreshing by remember(pagerState.currentPage) { mutableStateOf(false) }
if (showPageTabs && categories.size > 1) { if (showPageTabs && categories.isNotEmpty() && (categories.size > 1 || !categories.first().isSystemCategory)) {
LaunchedEffect(categories) { LaunchedEffect(categories) {
if (categories.size <= pagerState.currentPage) { if (categories.size <= pagerState.currentPage) {
pagerState.scrollToPage(categories.size - 1) pagerState.scrollToPage(categories.size - 1)
@@ -70,23 +69,20 @@ fun LibraryContent(
LibraryTabs( LibraryTabs(
categories = categories, categories = categories,
pagerState = pagerState, pagerState = pagerState,
getNumberOfMangaForCategory = getNumberOfMangaForCategory, getItemCountForCategory = getItemCountForCategory,
) { scope.launch { pagerState.animateScrollToPage(it) } } onTabItemClick = {
} scope.launch {
pagerState.animateScrollToPage(it)
val notSelectionMode = selection.isEmpty()
val onClickManga = { manga: LibraryManga ->
if (notSelectionMode) {
onMangaClicked(manga.manga.id)
} else {
onToggleSelection(manga)
} }
},
)
} }
PullRefresh( PullRefresh(
refreshing = isRefreshing, refreshing = isRefreshing,
enabled = selection.isEmpty(),
onRefresh = { onRefresh = {
val started = onRefresh(categories.getOrNull(currentPage()) ?: return@PullRefresh) val started = onRefresh()
if (!started) return@PullRefresh if (!started) return@PullRefresh
scope.launch { scope.launch {
// Fake refresh status but hide it after a second as it's a long running task // Fake refresh status but hide it after a second as it's a long running task
@@ -95,19 +91,25 @@ fun LibraryContent(
isRefreshing = false isRefreshing = false
} }
}, },
enabled = notSelectionMode,
) { ) {
LibraryPager( LibraryPager(
state = pagerState, state = pagerState,
contentPadding = PaddingValues(bottom = contentPadding.calculateBottomPadding()), contentPadding = PaddingValues(bottom = contentPadding.calculateBottomPadding()),
hasActiveFilters = hasActiveFilters, hasActiveFilters = hasActiveFilters,
selectedManga = selection, selection = selection,
searchQuery = searchQuery, searchQuery = searchQuery,
onGlobalSearchClicked = onGlobalSearchClicked, onGlobalSearchClicked = onGlobalSearchClicked,
getCategoryForPage = { page -> categories[page] },
getDisplayMode = getDisplayMode, getDisplayMode = getDisplayMode,
getColumnsForOrientation = getColumnsForOrientation, getColumnsForOrientation = getColumnsForOrientation,
getLibraryForPage = getLibraryForPage, getItemsForCategory = getItemsForCategory,
onClickManga = onClickManga, onClickManga = { category, manga ->
if (selection.isNotEmpty()) {
onToggleSelection(category, manga)
} else {
onClickManga(manga.manga.id)
}
},
onLongClickManga = onToggleRangeSelection, onLongClickManga = onToggleRangeSelection,
onClickContinueReading = onContinueReadingClicked, onClickContinueReading = onContinueReadingClicked,
) )
@@ -7,7 +7,6 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastAny
import eu.kanade.tachiyomi.ui.library.LibraryItem import eu.kanade.tachiyomi.ui.library.LibraryItem
import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.library.model.LibraryManga
import tachiyomi.domain.manga.model.MangaCover import tachiyomi.domain.manga.model.MangaCover
@@ -18,7 +17,7 @@ import tachiyomi.presentation.core.util.plus
internal fun LibraryList( internal fun LibraryList(
items: List<LibraryItem>, items: List<LibraryItem>,
contentPadding: PaddingValues, contentPadding: PaddingValues,
selection: List<LibraryManga>, selection: Set<Long>,
onClick: (LibraryManga) -> Unit, onClick: (LibraryManga) -> Unit,
onLongClick: (LibraryManga) -> Unit, onLongClick: (LibraryManga) -> Unit,
onClickContinueReading: ((LibraryManga) -> Unit)?, onClickContinueReading: ((LibraryManga) -> Unit)?,
@@ -45,7 +44,7 @@ internal fun LibraryList(
) { libraryItem -> ) { libraryItem ->
val manga = libraryItem.libraryManga.manga val manga = libraryItem.libraryManga.manga
MangaListItem( MangaListItem(
isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id }, isSelected = manga.id in selection,
title = manga.title, title = manga.title,
coverData = MangaCover( coverData = MangaCover(
mangaId = manga.id, mangaId = manga.id,
@@ -20,6 +20,7 @@ import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.core.preference.PreferenceMutableState import eu.kanade.core.preference.PreferenceMutableState
import eu.kanade.tachiyomi.ui.library.LibraryItem import eu.kanade.tachiyomi.ui.library.LibraryItem
import tachiyomi.domain.category.model.Category
import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.library.model.LibraryManga
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
@@ -31,14 +32,15 @@ fun LibraryPager(
state: PagerState, state: PagerState,
contentPadding: PaddingValues, contentPadding: PaddingValues,
hasActiveFilters: Boolean, hasActiveFilters: Boolean,
selectedManga: List<LibraryManga>, selection: Set<Long>,
searchQuery: String?, searchQuery: String?,
onGlobalSearchClicked: () -> Unit, onGlobalSearchClicked: () -> Unit,
getCategoryForPage: (Int) -> Category,
getDisplayMode: (Int) -> PreferenceMutableState<LibraryDisplayMode>, getDisplayMode: (Int) -> PreferenceMutableState<LibraryDisplayMode>,
getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>, getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>,
getLibraryForPage: (Int) -> List<LibraryItem>, getItemsForCategory: (Category) -> List<LibraryItem>,
onClickManga: (LibraryManga) -> Unit, onClickManga: (Category, LibraryManga) -> Unit,
onLongClickManga: (LibraryManga) -> Unit, onLongClickManga: (Category, LibraryManga) -> Unit,
onClickContinueReading: ((LibraryManga) -> Unit)?, onClickContinueReading: ((LibraryManga) -> Unit)?,
) { ) {
HorizontalPager( HorizontalPager(
@@ -50,9 +52,10 @@ fun LibraryPager(
// To make sure only one offscreen page is being composed // To make sure only one offscreen page is being composed
return@HorizontalPager return@HorizontalPager
} }
val library = getLibraryForPage(page) val category = getCategoryForPage(page)
val items = getItemsForCategory(category)
if (library.isEmpty()) { if (items.isEmpty()) {
LibraryPagerEmptyScreen( LibraryPagerEmptyScreen(
searchQuery = searchQuery, searchQuery = searchQuery,
hasActiveFilters = hasActiveFilters, hasActiveFilters = hasActiveFilters,
@@ -72,12 +75,15 @@ fun LibraryPager(
remember { mutableIntStateOf(0) } remember { mutableIntStateOf(0) }
} }
val onClickManga: (LibraryManga) -> Unit = { onClickManga(category, it) }
val onLongClickManga: (LibraryManga) -> Unit = { onLongClickManga(category, it) }
when (displayMode) { when (displayMode) {
LibraryDisplayMode.List -> { LibraryDisplayMode.List -> {
LibraryList( LibraryList(
items = library, items = items,
contentPadding = contentPadding, contentPadding = contentPadding,
selection = selectedManga, selection = selection,
onClick = onClickManga, onClick = onClickManga,
onLongClick = onLongClickManga, onLongClick = onLongClickManga,
onClickContinueReading = onClickContinueReading, onClickContinueReading = onClickContinueReading,
@@ -87,11 +93,11 @@ fun LibraryPager(
} }
LibraryDisplayMode.CompactGrid, LibraryDisplayMode.CoverOnlyGrid -> { LibraryDisplayMode.CompactGrid, LibraryDisplayMode.CoverOnlyGrid -> {
LibraryCompactGrid( LibraryCompactGrid(
items = library, items = items,
showTitle = displayMode is LibraryDisplayMode.CompactGrid, showTitle = displayMode is LibraryDisplayMode.CompactGrid,
columns = columns, columns = columns,
contentPadding = contentPadding, contentPadding = contentPadding,
selection = selectedManga, selection = selection,
onClick = onClickManga, onClick = onClickManga,
onLongClick = onLongClickManga, onLongClick = onLongClickManga,
onClickContinueReading = onClickContinueReading, onClickContinueReading = onClickContinueReading,
@@ -101,10 +107,10 @@ fun LibraryPager(
} }
LibraryDisplayMode.ComfortableGrid -> { LibraryDisplayMode.ComfortableGrid -> {
LibraryComfortableGrid( LibraryComfortableGrid(
items = library, items = items,
columns = columns, columns = columns,
contentPadding = contentPadding, contentPadding = contentPadding,
selection = selectedManga, selection = selection,
onClick = onClickManga, onClick = onClickManga,
onLongClick = onLongClickManga, onLongClick = onLongClickManga,
onClickContinueReading = onClickContinueReading, onClickContinueReading = onClickContinueReading,
@@ -18,13 +18,11 @@ import tachiyomi.presentation.core.components.material.TabText
internal fun LibraryTabs( internal fun LibraryTabs(
categories: List<Category>, categories: List<Category>,
pagerState: PagerState, pagerState: PagerState,
getNumberOfMangaForCategory: (Category) -> Int?, getItemCountForCategory: (Category) -> Int?,
onTabItemClick: (Int) -> Unit, onTabItemClick: (Int) -> Unit,
) { ) {
val currentPageIndex = pagerState.currentPage.coerceAtMost(categories.lastIndex) val currentPageIndex = pagerState.currentPage.coerceAtMost(categories.lastIndex)
Column( Column(modifier = Modifier.zIndex(2f)) {
modifier = Modifier.zIndex(1f),
) {
PrimaryScrollableTabRow( PrimaryScrollableTabRow(
selectedTabIndex = currentPageIndex, selectedTabIndex = currentPageIndex,
edgePadding = 0.dp, edgePadding = 0.dp,
@@ -39,7 +37,7 @@ internal fun LibraryTabs(
text = { text = {
TabText( TabText(
text = category.visualName, text = category.visualName,
badgeCount = getNumberOfMangaForCategory(category), badgeCount = getItemCountForCategory(category),
) )
}, },
unselectedContentColor = MaterialTheme.colorScheme.onSurface, unselectedContentColor = MaterialTheme.colorScheme.onSurface,
@@ -66,7 +66,7 @@ fun ChapterSettingsDialog(
) )
} }
val downloadedOnly = remember { Injekt.get<BasePreferences>().downloadedOnly().get() } val downloadedOnly = remember { Injekt.get<BasePreferences>().downloadedOnly.get() }
TabbedDialog( TabbedDialog(
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
@@ -1,44 +1,95 @@
package eu.kanade.presentation.manga package eu.kanade.presentation.manga
import androidx.compose.foundation.clickable import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Brush
import androidx.compose.material.icons.filled.PersonOutline
import androidx.compose.material.icons.filled.Warning
import androidx.compose.material.icons.outlined.Add import androidx.compose.material.icons.outlined.Add
import androidx.compose.material.icons.outlined.Book import androidx.compose.material.icons.outlined.AttachMoney
import androidx.compose.material.icons.outlined.SwapVert import androidx.compose.material.icons.outlined.Block
import androidx.compose.material.icons.outlined.Close
import androidx.compose.material.icons.outlined.Done
import androidx.compose.material.icons.outlined.DoneAll
import androidx.compose.material.icons.outlined.Pause
import androidx.compose.material.icons.outlined.Schedule
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.Typography
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextMeasurer
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.util.fastMaxOfOrNull
import coil3.request.ImageRequest
import coil3.request.crossfade
import eu.kanade.presentation.components.AdaptiveSheet import eu.kanade.presentation.components.AdaptiveSheet
import eu.kanade.presentation.components.TabbedDialogPaddings import eu.kanade.presentation.components.TabbedDialogPaddings
import eu.kanade.presentation.manga.components.MangaCover
import eu.kanade.presentation.more.settings.LocalPreferenceMinHeight import eu.kanade.presentation.more.settings.LocalPreferenceMinHeight
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.SManga
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaWithChapterCount
import tachiyomi.domain.source.model.StubSource
import tachiyomi.domain.source.service.SourceManager
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.Badge
import tachiyomi.presentation.core.components.BadgeGroup
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.secondaryItemAlpha
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@Composable @Composable
fun DuplicateMangaDialog( fun DuplicateMangaDialog(
duplicates: List<MangaWithChapterCount>,
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
onConfirm: () -> Unit, onConfirm: () -> Unit,
onOpenManga: () -> Unit, onOpenManga: (manga: Manga) -> Unit,
onMigrate: () -> Unit, onMigrate: (manga: Manga) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val sourceManager = remember { Injekt.get<SourceManager>() }
val minHeight = LocalPreferenceMinHeight.current val minHeight = LocalPreferenceMinHeight.current
val horizontalPadding = PaddingValues(horizontal = TabbedDialogPaddings.Horizontal)
val horizontalPaddingModifier = Modifier.padding(horizontalPadding)
AdaptiveSheet( AdaptiveSheet(
modifier = modifier, modifier = modifier,
@@ -46,45 +97,45 @@ fun DuplicateMangaDialog(
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.padding( .padding(vertical = TabbedDialogPaddings.Vertical)
vertical = TabbedDialogPaddings.Vertical, .verticalScroll(rememberScrollState())
horizontal = TabbedDialogPaddings.Horizontal,
)
.fillMaxWidth(), .fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.medium),
) { ) {
Text( Text(
modifier = Modifier.padding(TitlePadding), text = stringResource(MR.strings.possible_duplicates_title),
text = stringResource(MR.strings.are_you_sure),
style = MaterialTheme.typography.headlineMedium, style = MaterialTheme.typography.headlineMedium,
modifier = Modifier
.then(horizontalPaddingModifier)
.padding(top = MaterialTheme.padding.small),
) )
Text( Text(
text = stringResource(MR.strings.confirm_add_duplicate_manga), text = stringResource(MR.strings.possible_duplicates_summary),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.then(horizontalPaddingModifier),
) )
Spacer(Modifier.height(PaddingSize)) LazyRow(
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
TextPreferenceWidget( modifier = Modifier.height(getMaximumMangaCardHeight(duplicates)),
title = stringResource(MR.strings.action_show_manga), contentPadding = horizontalPadding,
icon = Icons.Outlined.Book, ) {
onPreferenceClick = { items(
onDismissRequest() items = duplicates,
onOpenManga() key = { it.manga.id },
}, ) {
) DuplicateMangaListItem(
duplicate = it,
HorizontalDivider() getSource = { sourceManager.getOrStub(it.manga.source) },
onMigrate = { onMigrate(it.manga) },
TextPreferenceWidget( onDismissRequest = onDismissRequest,
title = stringResource(MR.strings.action_migrate_duplicate), onOpenManga = { onOpenManga(it.manga) },
icon = Icons.Outlined.SwapVert,
onPreferenceClick = {
onDismissRequest()
onMigrate()
},
) )
}
}
Column(modifier = horizontalPaddingModifier) {
HorizontalDivider() HorizontalDivider()
TextPreferenceWidget( TextPreferenceWidget(
@@ -94,33 +145,262 @@ fun DuplicateMangaDialog(
onDismissRequest() onDismissRequest()
onConfirm() onConfirm()
}, },
modifier = Modifier.clip(CircleShape),
) )
}
Row( OutlinedButton(
onClick = onDismissRequest,
modifier = Modifier modifier = Modifier
.sizeIn(minHeight = minHeight) .then(horizontalPaddingModifier)
.clickable { onDismissRequest.invoke() } .padding(bottom = MaterialTheme.padding.medium)
.padding(ButtonPadding) .heightIn(min = minHeight)
.fillMaxWidth(), .fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
) { ) {
OutlinedButton(onClick = onDismissRequest, modifier = Modifier.fillMaxWidth()) {
Text( Text(
modifier = Modifier modifier = Modifier.padding(vertical = MaterialTheme.padding.extraSmall),
.padding(vertical = 8.dp),
text = stringResource(MR.strings.action_cancel), text = stringResource(MR.strings.action_cancel),
color = MaterialTheme.colorScheme.primary, color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.bodyLarge,
fontSize = 16.sp,
) )
} }
} }
} }
} }
@Composable
private fun DuplicateMangaListItem(
duplicate: MangaWithChapterCount,
getSource: () -> Source,
onDismissRequest: () -> Unit,
onOpenManga: () -> Unit,
onMigrate: () -> Unit,
) {
val source = getSource()
val manga = duplicate.manga
Column(
modifier = Modifier
.width(MangaCardWidth)
.clip(MaterialTheme.shapes.medium)
.background(MaterialTheme.colorScheme.surface)
.combinedClickable(
onLongClick = { onOpenManga() },
onClick = {
onDismissRequest()
onMigrate()
},
)
.padding(MaterialTheme.padding.small),
) {
Box {
MangaCover.Book(
data = ImageRequest.Builder(LocalContext.current)
.data(manga)
.crossfade(true)
.build(),
modifier = Modifier.fillMaxWidth(),
)
BadgeGroup(
modifier = Modifier
.padding(4.dp)
.align(Alignment.TopStart),
) {
Badge(
color = MaterialTheme.colorScheme.secondary,
textColor = MaterialTheme.colorScheme.onSecondary,
text = pluralStringResource(
MR.plurals.manga_num_chapters,
duplicate.chapterCount.toInt(),
duplicate.chapterCount,
),
)
}
} }
private val PaddingSize = 16.dp Spacer(modifier = Modifier.height(MaterialTheme.padding.extraSmall))
private val ButtonPadding = PaddingValues(top = 16.dp, bottom = 16.dp) Text(
private val TitlePadding = PaddingValues(bottom = 16.dp, top = 8.dp) text = manga.title,
style = MaterialTheme.typography.titleSmall,
overflow = TextOverflow.Ellipsis,
maxLines = 2,
)
if (!manga.author.isNullOrBlank()) {
MangaDetailRow(
text = manga.author!!,
iconImageVector = Icons.Filled.PersonOutline,
maxLines = 2,
)
}
if (!manga.artist.isNullOrBlank() && manga.author != manga.artist) {
MangaDetailRow(
text = manga.artist!!,
iconImageVector = Icons.Filled.Brush,
maxLines = 2,
)
}
MangaDetailRow(
text = when (manga.status) {
SManga.ONGOING.toLong() -> stringResource(MR.strings.ongoing)
SManga.COMPLETED.toLong() -> stringResource(MR.strings.completed)
SManga.LICENSED.toLong() -> stringResource(MR.strings.licensed)
SManga.PUBLISHING_FINISHED.toLong() -> stringResource(MR.strings.publishing_finished)
SManga.CANCELLED.toLong() -> stringResource(MR.strings.cancelled)
SManga.ON_HIATUS.toLong() -> stringResource(MR.strings.on_hiatus)
else -> stringResource(MR.strings.unknown)
},
iconImageVector = when (manga.status) {
SManga.ONGOING.toLong() -> Icons.Outlined.Schedule
SManga.COMPLETED.toLong() -> Icons.Outlined.DoneAll
SManga.LICENSED.toLong() -> Icons.Outlined.AttachMoney
SManga.PUBLISHING_FINISHED.toLong() -> Icons.Outlined.Done
SManga.CANCELLED.toLong() -> Icons.Outlined.Close
SManga.ON_HIATUS.toLong() -> Icons.Outlined.Pause
else -> Icons.Outlined.Block
},
)
Spacer(modifier = Modifier.weight(1f))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
) {
if (source is StubSource) {
Icon(
imageVector = Icons.Filled.Warning,
contentDescription = null,
modifier = Modifier.size(16.dp),
tint = MaterialTheme.colorScheme.error,
)
}
Text(
text = source.name,
style = MaterialTheme.typography.labelSmall,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
)
}
}
}
@Composable
private fun MangaDetailRow(
text: String,
iconImageVector: ImageVector,
maxLines: Int = 1,
) {
Row(
modifier = Modifier
.secondaryItemAlpha()
.padding(top = MaterialTheme.padding.extraSmall),
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
imageVector = iconImageVector,
contentDescription = null,
modifier = Modifier.size(MangaDetailsIconWidth),
)
Text(
text = text,
style = MaterialTheme.typography.bodySmall,
overflow = TextOverflow.Ellipsis,
maxLines = maxLines,
)
}
}
@Composable
private fun getMaximumMangaCardHeight(duplicates: List<MangaWithChapterCount>): Dp {
val density = LocalDensity.current
val typography = MaterialTheme.typography
val textMeasurer = rememberTextMeasurer()
val smallPadding = with(density) { MaterialTheme.padding.small.roundToPx() }
val extraSmallPadding = with(density) { MaterialTheme.padding.extraSmall.roundToPx() }
val width = with(density) { MangaCardWidth.roundToPx() - (2 * smallPadding) }
val iconWidth = with(density) { MangaDetailsIconWidth.roundToPx() }
val coverHeight = width / MangaCover.Book.ratio
val constraints = Constraints(maxWidth = width)
val detailsConstraints = Constraints(maxWidth = width - iconWidth - extraSmallPadding)
return remember(
duplicates,
density,
typography,
textMeasurer,
smallPadding,
extraSmallPadding,
coverHeight,
constraints,
detailsConstraints,
) {
duplicates.fastMaxOfOrNull {
calculateMangaCardHeight(
manga = it.manga,
density = density,
typography = typography,
textMeasurer = textMeasurer,
smallPadding = smallPadding,
extraSmallPadding = extraSmallPadding,
coverHeight = coverHeight,
constraints = constraints,
detailsConstraints = detailsConstraints,
)
}
?: 0.dp
}
}
private fun calculateMangaCardHeight(
manga: Manga,
density: Density,
typography: Typography,
textMeasurer: TextMeasurer,
smallPadding: Int,
extraSmallPadding: Int,
coverHeight: Float,
constraints: Constraints,
detailsConstraints: Constraints,
): Dp {
val titleHeight = textMeasurer.measureHeight(manga.title, typography.titleSmall, 2, constraints)
val authorHeight = if (!manga.author.isNullOrBlank()) {
textMeasurer.measureHeight(manga.author!!, typography.bodySmall, 2, detailsConstraints)
} else {
0
}
val artistHeight = if (!manga.artist.isNullOrBlank() && manga.author != manga.artist) {
textMeasurer.measureHeight(manga.artist!!, typography.bodySmall, 2, detailsConstraints)
} else {
0
}
val statusHeight = textMeasurer.measureHeight("", typography.bodySmall, 2, detailsConstraints)
val sourceHeight = textMeasurer.measureHeight("", typography.labelSmall, 1, constraints)
val totalHeight = coverHeight + titleHeight + authorHeight + artistHeight + statusHeight + sourceHeight
return with(density) { ((2 * smallPadding) + totalHeight + (5 * extraSmallPadding)).toDp() }
}
private fun TextMeasurer.measureHeight(
text: String,
style: TextStyle,
maxLines: Int,
constraints: Constraints,
): Int = measure(
text = text,
style = style,
overflow = TextOverflow.Ellipsis,
maxLines = maxLines,
constraints = constraints,
)
.size
.height
private val MangaCardWidth = 150.dp
private val MangaDetailsIconWidth = 16.dp
@@ -0,0 +1,45 @@
package eu.kanade.presentation.manga
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarTitle
import eu.kanade.presentation.manga.components.MangaNotesTextArea
import eu.kanade.tachiyomi.ui.manga.notes.MangaNotesScreen
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource
@Composable
fun MangaNotesScreen(
state: MangaNotesScreen.State,
navigateUp: () -> Unit,
onUpdate: (String) -> Unit,
) {
Scaffold(
topBar = { topBarScrollBehavior ->
AppBar(
titleContent = {
AppBarTitle(
title = stringResource(MR.strings.action_edit_notes),
subtitle = state.manga.title,
)
},
navigateUp = navigateUp,
scrollBehavior = topBarScrollBehavior,
)
},
) { contentPadding ->
MangaNotesTextArea(
state = state,
onUpdate = onUpdate,
modifier = Modifier
.padding(contentPadding)
.consumeWindowInsets(contentPadding)
.imePadding(),
)
}
}
@@ -1,10 +1,7 @@
package eu.kanade.presentation.manga package eu.kanade.presentation.manga
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
@@ -27,9 +24,11 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.SmallExtendedFloatingActionButton
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.animateFloatingActionButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -69,6 +68,7 @@ import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.getNameForMangaInfo import eu.kanade.tachiyomi.source.getNameForMangaInfo
import eu.kanade.tachiyomi.source.online.MetadataSource import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.all.EHentai import eu.kanade.tachiyomi.source.online.all.EHentai
import eu.kanade.tachiyomi.source.online.all.Lanraragi
import eu.kanade.tachiyomi.source.online.all.MangaDex import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.source.online.all.NHentai import eu.kanade.tachiyomi.source.online.all.NHentai
import eu.kanade.tachiyomi.source.online.english.EightMuses import eu.kanade.tachiyomi.source.online.english.EightMuses
@@ -87,6 +87,7 @@ import exh.source.isEhBasedManga
import exh.ui.metadata.adapters.EHentaiDescription import exh.ui.metadata.adapters.EHentaiDescription
import exh.ui.metadata.adapters.EightMusesDescription import exh.ui.metadata.adapters.EightMusesDescription
import exh.ui.metadata.adapters.HBrowseDescription import exh.ui.metadata.adapters.HBrowseDescription
import exh.ui.metadata.adapters.LanraragiDescription
import exh.ui.metadata.adapters.MangaDexDescription import exh.ui.metadata.adapters.MangaDexDescription
import exh.ui.metadata.adapters.NHentaiDescription import exh.ui.metadata.adapters.NHentaiDescription
import exh.ui.metadata.adapters.PururinDescription import exh.ui.metadata.adapters.PururinDescription
@@ -99,7 +100,6 @@ import tachiyomi.domain.source.model.StubSource
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.TwoPanelBox import tachiyomi.presentation.core.components.TwoPanelBox
import tachiyomi.presentation.core.components.VerticalFastScroller import tachiyomi.presentation.core.components.VerticalFastScroller
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
import tachiyomi.presentation.core.components.material.PullRefresh import tachiyomi.presentation.core.components.material.PullRefresh
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
@@ -142,6 +142,7 @@ fun MangaScreen(
onEditCategoryClicked: (() -> Unit)?, onEditCategoryClicked: (() -> Unit)?,
onEditFetchIntervalClicked: (() -> Unit)?, onEditFetchIntervalClicked: (() -> Unit)?,
onMigrateClicked: (() -> Unit)?, onMigrateClicked: (() -> Unit)?,
onEditNotesClicked: () -> Unit,
// SY --> // SY -->
onMetadataViewerClicked: () -> Unit, onMetadataViewerClicked: () -> Unit,
onEditInfoClicked: () -> Unit, onEditInfoClicked: () -> Unit,
@@ -164,7 +165,7 @@ fun MangaScreen(
onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit, onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit,
// Chapter selection // Chapter selection
onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onChapterSelected: (ChapterList.Item, Boolean, Boolean) -> Unit,
onAllChapterSelected: (Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit, onInvertSelection: () -> Unit,
) { ) {
@@ -201,6 +202,7 @@ fun MangaScreen(
onEditCategoryClicked = onEditCategoryClicked, onEditCategoryClicked = onEditCategoryClicked,
onEditIntervalClicked = onEditFetchIntervalClicked, onEditIntervalClicked = onEditFetchIntervalClicked,
onMigrateClicked = onMigrateClicked, onMigrateClicked = onMigrateClicked,
onEditNotesClicked = onEditNotesClicked,
// SY --> // SY -->
onMetadataViewerClicked = onMetadataViewerClicked, onMetadataViewerClicked = onMetadataViewerClicked,
onEditInfoClicked = onEditInfoClicked, onEditInfoClicked = onEditInfoClicked,
@@ -247,6 +249,7 @@ fun MangaScreen(
onEditCategoryClicked = onEditCategoryClicked, onEditCategoryClicked = onEditCategoryClicked,
onEditIntervalClicked = onEditFetchIntervalClicked, onEditIntervalClicked = onEditFetchIntervalClicked,
onMigrateClicked = onMigrateClicked, onMigrateClicked = onMigrateClicked,
onEditNotesClicked = onEditNotesClicked,
// SY --> // SY -->
onMetadataViewerClicked = onMetadataViewerClicked, onMetadataViewerClicked = onMetadataViewerClicked,
onEditInfoClicked = onEditInfoClicked, onEditInfoClicked = onEditInfoClicked,
@@ -303,6 +306,7 @@ private fun MangaScreenSmallImpl(
onEditCategoryClicked: (() -> Unit)?, onEditCategoryClicked: (() -> Unit)?,
onEditIntervalClicked: (() -> Unit)?, onEditIntervalClicked: (() -> Unit)?,
onMigrateClicked: (() -> Unit)?, onMigrateClicked: (() -> Unit)?,
onEditNotesClicked: () -> Unit,
// SY --> // SY -->
onMetadataViewerClicked: () -> Unit, onMetadataViewerClicked: () -> Unit,
onEditInfoClicked: () -> Unit, onEditInfoClicked: () -> Unit,
@@ -325,7 +329,7 @@ private fun MangaScreenSmallImpl(
onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit, onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit,
// Chapter selection // Chapter selection
onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onChapterSelected: (ChapterList.Item, Boolean, Boolean) -> Unit,
onAllChapterSelected: (Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit, onInvertSelection: () -> Unit,
) { ) {
@@ -345,13 +349,9 @@ private fun MangaScreenSmallImpl(
} }
// SY <-- // SY <--
BackHandler(onBack = { BackHandler(enabled = isAnySelected) {
if (isAnySelected) {
onAllChapterSelected(false) onAllChapterSelected(false)
} else {
navigateUp()
} }
})
Scaffold( Scaffold(
topBar = { topBar = {
@@ -382,6 +382,7 @@ private fun MangaScreenSmallImpl(
onClickEditCategory = onEditCategoryClicked, onClickEditCategory = onEditCategoryClicked,
onClickRefresh = onRefresh, onClickRefresh = onRefresh,
onClickMigrate = onMigrateClicked, onClickMigrate = onMigrateClicked,
onClickEditNotes = onEditNotesClicked,
// SY --> // SY -->
onClickEditInfo = onEditInfoClicked.takeIf { state.manga.favorite }, onClickEditInfo = onEditInfoClicked.takeIf { state.manga.favorite },
onClickRecommend = onRecommendClicked.takeIf { state.showRecommendationsInOverflow }, onClickRecommend = onRecommendClicked.takeIf { state.showRecommendationsInOverflow },
@@ -415,12 +416,7 @@ private fun MangaScreenSmallImpl(
val isFABVisible = remember(chapters) { val isFABVisible = remember(chapters) {
chapters.fastAny { !it.chapter.read } && !isAnySelected chapters.fastAny { !it.chapter.read } && !isAnySelected
} }
AnimatedVisibility( SmallExtendedFloatingActionButton(
visible = isFABVisible,
enter = fadeIn(),
exit = fadeOut(),
) {
ExtendedFloatingActionButton(
text = { text = {
val isReading = remember(state.chapters) { val isReading = remember(state.chapters) {
state.chapters.fastAny { it.chapter.read } state.chapters.fastAny { it.chapter.read }
@@ -432,8 +428,11 @@ private fun MangaScreenSmallImpl(
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) }, icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
onClick = onContinueReading, onClick = onContinueReading,
expanded = chapterListState.shouldExpandFAB(), expanded = chapterListState.shouldExpandFAB(),
modifier = Modifier.animateFloatingActionButton(
visible = isFABVisible,
alignment = Alignment.BottomEnd,
),
) )
}
}, },
) { contentPadding -> ) { contentPadding ->
val topPadding = contentPadding.calculateTopPadding() val topPadding = contentPadding.calculateTopPadding()
@@ -519,12 +518,14 @@ private fun MangaScreenSmallImpl(
defaultExpandState = state.isFromSource, defaultExpandState = state.isFromSource,
description = state.manga.description, description = state.manga.description,
tagsProvider = { state.manga.genre }, tagsProvider = { state.manga.genre },
notes = state.manga.notes,
onTagSearch = onTagSearch, onTagSearch = onTagSearch,
onCopyTagToClipboard = onCopyTagToClipboard, onCopyTagToClipboard = onCopyTagToClipboard,
onEditNotes = onEditNotesClicked,
// SY --> // SY -->
doSearch = onSearch, doSearch = onSearch,
searchMetadataChips = remember(state.meta, state.source.id, state.manga.genre) { searchMetadataChips = remember(state.meta, state.source.id, state.manga.genre) {
SearchMetadataChips(state.meta, state.source, state.manga.genre) SearchMetadataChips(state.meta, state.source.id, state.manga.genre)
}, },
// SY <-- // SY <--
) )
@@ -626,6 +627,7 @@ fun MangaScreenLargeImpl(
onEditCategoryClicked: (() -> Unit)?, onEditCategoryClicked: (() -> Unit)?,
onEditIntervalClicked: (() -> Unit)?, onEditIntervalClicked: (() -> Unit)?,
onMigrateClicked: (() -> Unit)?, onMigrateClicked: (() -> Unit)?,
onEditNotesClicked: () -> Unit,
// SY --> // SY -->
onMetadataViewerClicked: () -> Unit, onMetadataViewerClicked: () -> Unit,
onEditInfoClicked: () -> Unit, onEditInfoClicked: () -> Unit,
@@ -648,7 +650,7 @@ fun MangaScreenLargeImpl(
onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit, onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit,
// Chapter selection // Chapter selection
onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onChapterSelected: (ChapterList.Item, Boolean, Boolean) -> Unit,
onAllChapterSelected: (Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit, onInvertSelection: () -> Unit,
) { ) {
@@ -672,13 +674,9 @@ fun MangaScreenLargeImpl(
val chapterListState = rememberLazyListState() val chapterListState = rememberLazyListState()
BackHandler(onBack = { BackHandler(enabled = isAnySelected) {
if (isAnySelected) {
onAllChapterSelected(false) onAllChapterSelected(false)
} else {
navigateUp()
} }
})
Scaffold( Scaffold(
topBar = { topBar = {
@@ -696,6 +694,7 @@ fun MangaScreenLargeImpl(
onClickEditCategory = onEditCategoryClicked, onClickEditCategory = onEditCategoryClicked,
onClickRefresh = onRefresh, onClickRefresh = onRefresh,
onClickMigrate = onMigrateClicked, onClickMigrate = onMigrateClicked,
onClickEditNotes = onEditNotesClicked,
// SY --> // SY -->
onClickEditInfo = onEditInfoClicked.takeIf { state.manga.favorite }, onClickEditInfo = onEditInfoClicked.takeIf { state.manga.favorite },
onClickRecommend = onRecommendClicked.takeIf { state.showRecommendationsInOverflow }, onClickRecommend = onRecommendClicked.takeIf { state.showRecommendationsInOverflow },
@@ -734,12 +733,7 @@ fun MangaScreenLargeImpl(
val isFABVisible = remember(chapters) { val isFABVisible = remember(chapters) {
chapters.fastAny { !it.chapter.read } && !isAnySelected chapters.fastAny { !it.chapter.read } && !isAnySelected
} }
AnimatedVisibility( SmallExtendedFloatingActionButton(
visible = isFABVisible,
enter = fadeIn(),
exit = fadeOut(),
) {
ExtendedFloatingActionButton(
text = { text = {
val isReading = remember(state.chapters) { val isReading = remember(state.chapters) {
state.chapters.fastAny { it.chapter.read } state.chapters.fastAny { it.chapter.read }
@@ -753,8 +747,11 @@ fun MangaScreenLargeImpl(
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) }, icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
onClick = onContinueReading, onClick = onContinueReading,
expanded = chapterListState.shouldExpandFAB(), expanded = chapterListState.shouldExpandFAB(),
modifier = Modifier.animateFloatingActionButton(
visible = isFABVisible,
alignment = Alignment.BottomEnd,
),
) )
}
}, },
) { contentPadding -> ) { contentPadding ->
PullRefresh( PullRefresh(
@@ -814,12 +811,14 @@ fun MangaScreenLargeImpl(
defaultExpandState = true, defaultExpandState = true,
description = state.manga.description, description = state.manga.description,
tagsProvider = { state.manga.genre }, tagsProvider = { state.manga.genre },
notes = state.manga.notes,
onTagSearch = onTagSearch, onTagSearch = onTagSearch,
onCopyTagToClipboard = onCopyTagToClipboard, onCopyTagToClipboard = onCopyTagToClipboard,
onEditNotes = onEditNotesClicked,
// SY --> // SY -->
doSearch = onSearch, doSearch = onSearch,
searchMetadataChips = remember(state.meta, state.source.id, state.manga.genre) { searchMetadataChips = remember(state.meta, state.source.id, state.manga.genre) {
SearchMetadataChips(state.meta, state.source, state.manga.genre) SearchMetadataChips(state.meta, state.source.id, state.manga.genre)
}, },
// SY <-- // SY <--
) )
@@ -948,7 +947,7 @@ private fun LazyListScope.sharedChapterItems(
// SY <-- // SY <--
onChapterClicked: (Chapter) -> Unit, onChapterClicked: (Chapter) -> Unit,
onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?, onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?,
onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onChapterSelected: (ChapterList.Item, Boolean, Boolean) -> Unit,
onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit, onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit,
) { ) {
items( items(
@@ -1015,14 +1014,14 @@ private fun LazyListScope.sharedChapterItems(
chapterSwipeStartAction = chapterSwipeStartAction, chapterSwipeStartAction = chapterSwipeStartAction,
chapterSwipeEndAction = chapterSwipeEndAction, chapterSwipeEndAction = chapterSwipeEndAction,
onLongClick = { onLongClick = {
onChapterSelected(item, !item.selected, true, true) onChapterSelected(item, !item.selected, true)
haptic.performHapticFeedback(HapticFeedbackType.LongPress) haptic.performHapticFeedback(HapticFeedbackType.LongPress)
}, },
onClick = { onClick = {
onChapterItemClick( onChapterItemClick(
chapterItem = item, chapterItem = item,
isAnyChapterSelected = isAnyChapterSelected, isAnyChapterSelected = isAnyChapterSelected,
onToggleSelection = { onChapterSelected(item, !item.selected, true, false) }, onToggleSelection = { onChapterSelected(item, !item.selected, false) },
onChapterClicked = onChapterClicked, onChapterClicked = onChapterClicked,
) )
}, },
@@ -1086,6 +1085,9 @@ fun metadataDescription(source: Source): MetadataDescriptionComposable? {
is Tsumino -> { state, openMetadataViewer, _ -> is Tsumino -> { state, openMetadataViewer, _ ->
TsuminoDescription(state, openMetadataViewer) TsuminoDescription(state, openMetadataViewer)
} }
is Lanraragi -> { state, openMetadataViewer, _ ->
LanraragiDescription(state, openMetadataViewer)
}
else -> null else -> null
} }
} }
@@ -6,6 +6,7 @@ enum class DownloadAction {
NEXT_10_CHAPTERS, NEXT_10_CHAPTERS,
NEXT_25_CHAPTERS, NEXT_25_CHAPTERS,
UNREAD_CHAPTERS, UNREAD_CHAPTERS,
BOOKMARKED_CHAPTERS,
} }
enum class EditCoverAction { enum class EditCoverAction {
@@ -9,6 +9,7 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
@@ -30,7 +31,6 @@ import androidx.compose.material.icons.outlined.DoneAll
import androidx.compose.material.icons.outlined.Download import androidx.compose.material.icons.outlined.Download
import androidx.compose.material.icons.outlined.MoreVert import androidx.compose.material.icons.outlined.MoreVert
import androidx.compose.material.icons.outlined.RemoveDone import androidx.compose.material.icons.outlined.RemoveDone
import androidx.compose.material.icons.outlined.SwapCalls
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@@ -52,6 +52,7 @@ import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.vectorResource import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.presentation.components.DownloadDropdownMenu import eu.kanade.presentation.components.DownloadDropdownMenu
import eu.kanade.presentation.components.DropdownMenu import eu.kanade.presentation.components.DropdownMenu
@@ -92,10 +93,10 @@ fun MangaBottomActionMenu(
) { ) {
val haptic = LocalHapticFeedback.current val haptic = LocalHapticFeedback.current
val confirm = remember { mutableStateListOf(false, false, false, false, false, false, false) } val confirm = remember { mutableStateListOf(false, false, false, false, false, false, false) }
var resetJob: Job? = remember { null } var resetJob by remember { mutableStateOf<Job?>(null) }
val onLongClickItem: (Int) -> Unit = { toConfirmIndex -> val onLongClickItem: (Int) -> Unit = { toConfirmIndex ->
haptic.performHapticFeedback(HapticFeedbackType.LongPress) haptic.performHapticFeedback(HapticFeedbackType.LongPress)
(0..<7).forEach { i -> confirm[i] = i == toConfirmIndex } confirm.indices.forEach { i -> confirm[i] = i == toConfirmIndex }
resetJob?.cancel() resetJob?.cancel()
resetJob = scope.launch { resetJob = scope.launch {
delay(1.seconds) delay(1.seconds)
@@ -192,7 +193,7 @@ private fun RowScope.Button(
targetValue = if (toConfirm) 2f else 1f, targetValue = if (toConfirm) 2f else 1f,
label = "weight", label = "weight",
) )
Column( Box(
modifier = Modifier modifier = Modifier
.size(48.dp) .size(48.dp)
.weight(animatedWeight) .weight(animatedWeight)
@@ -202,6 +203,9 @@ private fun RowScope.Button(
onLongClick = onLongClick, onLongClick = onLongClick,
onClick = onClick, onClick = onClick,
), ),
contentAlignment = Alignment.Center,
) {
Column(
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
@@ -221,6 +225,7 @@ private fun RowScope.Button(
style = MaterialTheme.typography.labelSmall, style = MaterialTheme.typography.labelSmall,
) )
} }
}
content?.invoke() content?.invoke()
} }
} }
@@ -233,9 +238,9 @@ fun LibraryBottomActionMenu(
onMarkAsUnreadClicked: () -> Unit, onMarkAsUnreadClicked: () -> Unit,
onDownloadClicked: ((DownloadAction) -> Unit)?, onDownloadClicked: ((DownloadAction) -> Unit)?,
onDeleteClicked: () -> Unit, onDeleteClicked: () -> Unit,
onMigrateClicked: (() -> Unit)?,
// SY --> // SY -->
onClickCleanTitles: (() -> Unit)?, onClickCleanTitles: (() -> Unit)?,
onClickMigrate: (() -> Unit)?,
onClickCollectRecommendations: (() -> Unit)?, onClickCollectRecommendations: (() -> Unit)?,
onClickAddToMangaDex: (() -> Unit)?, onClickAddToMangaDex: (() -> Unit)?,
onClickResetInfo: (() -> Unit)?, onClickResetInfo: (() -> Unit)?,
@@ -254,12 +259,11 @@ fun LibraryBottomActionMenu(
color = MaterialTheme.colorScheme.surfaceContainerHigh, color = MaterialTheme.colorScheme.surfaceContainerHigh,
) { ) {
val haptic = LocalHapticFeedback.current val haptic = LocalHapticFeedback.current
val confirm = val confirm = remember { mutableStateListOf(false, false, false, false, false, false) }
remember { mutableStateListOf(false, false, false, false, false /* SY --> */, false /* SY <-- */) } var resetJob by remember { mutableStateOf<Job?>(null) }
var resetJob: Job? = remember { null }
val onLongClickItem: (Int) -> Unit = { toConfirmIndex -> val onLongClickItem: (Int) -> Unit = { toConfirmIndex ->
haptic.performHapticFeedback(HapticFeedbackType.LongPress) haptic.performHapticFeedback(HapticFeedbackType.LongPress)
(0..<5).forEach { i -> confirm[i] = i == toConfirmIndex } confirm.indices.forEach { i -> confirm[i] = i == toConfirmIndex }
resetJob?.cancel() resetJob?.cancel()
resetJob = scope.launch { resetJob = scope.launch {
delay(1.seconds) delay(1.seconds)
@@ -270,7 +274,8 @@ fun LibraryBottomActionMenu(
val showOverflow = onClickCleanTitles != null || val showOverflow = onClickCleanTitles != null ||
onClickAddToMangaDex != null || onClickAddToMangaDex != null ||
onClickResetInfo != null || onClickResetInfo != null ||
onClickCollectRecommendations != null onClickCollectRecommendations != null ||
onMigrateClicked != null
val configuration = LocalConfiguration.current val configuration = LocalConfiguration.current
val moveMarkPrev = remember { !configuration.isTabletUi() } val moveMarkPrev = remember { !configuration.isTabletUi() }
var overFlowOpen by remember { mutableStateOf(false) } var overFlowOpen by remember { mutableStateOf(false) }
@@ -299,11 +304,11 @@ fun LibraryBottomActionMenu(
onLongClick = { onLongClickItem(3) }, onLongClick = { onLongClickItem(3) },
onClick = { downloadExpanded = !downloadExpanded }, onClick = { downloadExpanded = !downloadExpanded },
) { ) {
val onDismissRequest = { downloadExpanded = false }
DownloadDropdownMenu( DownloadDropdownMenu(
expanded = downloadExpanded, expanded = downloadExpanded,
onDismissRequest = onDismissRequest, onDismissRequest = { downloadExpanded = false },
onDownloadClicked = onDownloadClicked, onDownloadClicked = onDownloadClicked,
offset = BottomBarMenuDpOffset,
) )
} }
} }
@@ -355,10 +360,10 @@ fun LibraryBottomActionMenu(
onClick = onClickCleanTitles, onClick = onClickCleanTitles,
) )
} }
if (onClickMigrate != null) { if (onMigrateClicked != null) {
DropdownMenuItem( DropdownMenuItem(
text = { Text(stringResource(MR.strings.migrate)) }, text = { Text(stringResource(MR.strings.migrate)) },
onClick = onClickMigrate, onClick = onMigrateClicked,
) )
} }
if (onClickCollectRecommendations != null) { if (onClickCollectRecommendations != null) {
@@ -388,18 +393,11 @@ fun LibraryBottomActionMenu(
onLongClick = { onLongClickItem(2) }, onLongClick = { onLongClickItem(2) },
onClick = onMarkAsUnreadClicked, onClick = onMarkAsUnreadClicked,
) )
if (onClickMigrate != null) {
Button(
title = stringResource(MR.strings.migrate),
icon = Icons.Outlined.SwapCalls,
toConfirm = confirm[5],
onLongClick = { onLongClickItem(5) },
onClick = onClickMigrate,
)
}
} }
// SY <-- // SY <--
} }
} }
} }
} }
private val BottomBarMenuDpOffset = DpOffset(0.dp, 0.dp)
@@ -37,6 +37,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import androidx.core.graphics.drawable.toDrawable
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import coil3.asDrawable import coil3.asDrawable
import coil3.imageLoader import coil3.imageLoader
@@ -172,20 +173,20 @@ fun MangaCoverDialog(
.memoryCachePolicy(CachePolicy.DISABLED) .memoryCachePolicy(CachePolicy.DISABLED)
.target { image -> .target { image ->
val drawable = image.asDrawable(view.context.resources) val drawable = image.asDrawable(view.context.resources)
// Copy bitmap in case it came from memory cache // Copy bitmap in case it came from memory cache
// Because SSIV needs to thoroughly read the image // Because SSIV needs to thoroughly read the image
val copy = (drawable as? BitmapDrawable)?.let { val copy = (drawable as? BitmapDrawable)
val config = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { ?.bitmap
?.copy(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Bitmap.Config.HARDWARE Bitmap.Config.HARDWARE
} else { } else {
Bitmap.Config.ARGB_8888 Bitmap.Config.ARGB_8888
} },
BitmapDrawable( false,
view.context.resources,
it.bitmap.copy(config, false),
) )
} ?: drawable ?.toDrawable(view.context.resources)
?: drawable
view.setImage(copy, ReaderPageImageView.Config(zoomDuration = 500)) view.setImage(copy, ReaderPageImageView.Config(zoomDuration = 500))
} }
.build() .build()
@@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.sizeIn import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.CallMerge import androidx.compose.material.icons.automirrored.outlined.CallMerge
@@ -33,7 +34,6 @@ import androidx.compose.material.icons.filled.PersonOutline
import androidx.compose.material.icons.filled.Warning import androidx.compose.material.icons.filled.Warning
import androidx.compose.material.icons.outlined.AttachMoney import androidx.compose.material.icons.outlined.AttachMoney
import androidx.compose.material.icons.outlined.Block import androidx.compose.material.icons.outlined.Block
import androidx.compose.material.icons.outlined.CallMerge
import androidx.compose.material.icons.outlined.Close import androidx.compose.material.icons.outlined.Close
import androidx.compose.material.icons.outlined.Done import androidx.compose.material.icons.outlined.Done
import androidx.compose.material.icons.outlined.DoneAll import androidx.compose.material.icons.outlined.DoneAll
@@ -52,6 +52,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
@@ -67,9 +68,13 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.LinkAnnotation
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.withLink
import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -77,10 +82,17 @@ import androidx.compose.ui.unit.sp
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
import coil3.request.ImageRequest import coil3.request.ImageRequest
import coil3.request.crossfade import coil3.request.crossfade
import com.mikepenz.markdown.model.markdownAnnotator
import com.mikepenz.markdown.model.markdownAnnotatorConfig
import com.mikepenz.markdown.utils.getUnescapedTextInNode
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.components.DropdownMenu import eu.kanade.presentation.components.DropdownMenu
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import org.intellij.markdown.MarkdownElementTypes
import org.intellij.markdown.MarkdownTokenTypes
import org.intellij.markdown.ast.findChildOfType
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR import tachiyomi.i18n.sy.SYMR
@@ -91,12 +103,12 @@ import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.clickableNoIndication import tachiyomi.presentation.core.util.clickableNoIndication
import tachiyomi.presentation.core.util.secondaryItemAlpha import tachiyomi.presentation.core.util.secondaryItemAlpha
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.time.Instant import java.time.Instant
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
import kotlin.math.roundToInt import kotlin.math.roundToInt
private val whitespaceLineRegex = Regex("[\\r\\n]{2,}", setOf(RegexOption.MULTILINE))
@Composable @Composable
fun MangaInfoBox( fun MangaInfoBox(
isTabletUi: Boolean, isTabletUi: Boolean,
@@ -250,8 +262,10 @@ fun ExpandableMangaDescription(
defaultExpandState: Boolean, defaultExpandState: Boolean,
description: String?, description: String?,
tagsProvider: () -> List<String>?, tagsProvider: () -> List<String>?,
notes: String,
onTagSearch: (String) -> Unit, onTagSearch: (String) -> Unit,
onCopyTagToClipboard: (tag: String) -> Unit, onCopyTagToClipboard: (tag: String) -> Unit,
onEditNotes: () -> Unit,
// SY --> // SY -->
searchMetadataChips: SearchMetadataChips?, searchMetadataChips: SearchMetadataChips?,
doSearch: (query: String, global: Boolean) -> Unit, doSearch: (query: String, global: Boolean) -> Unit,
@@ -264,15 +278,12 @@ fun ExpandableMangaDescription(
} }
val desc = val desc =
description.takeIf { !it.isNullOrBlank() } ?: stringResource(MR.strings.description_placeholder) description.takeIf { !it.isNullOrBlank() } ?: stringResource(MR.strings.description_placeholder)
val trimmedDescription = remember(desc) {
desc
.replace(whitespaceLineRegex, "\n")
.trimEnd()
}
MangaSummary( MangaSummary(
expandedDescription = desc, description = desc,
shrunkDescription = trimmedDescription,
expanded = expanded, expanded = expanded,
notes = notes,
onEditNotesClicked = onEditNotes,
modifier = Modifier modifier = Modifier
.padding(top = 8.dp) .padding(top = 8.dp)
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)
@@ -594,42 +605,97 @@ private fun ColumnScope.MangaContentInfo(
} }
} }
@Composable
private fun descriptionAnnotator(loadImages: Boolean, linkStyle: SpanStyle) = remember(loadImages, linkStyle) {
markdownAnnotator(
annotate = { content, child ->
if (!loadImages && child.type == MarkdownElementTypes.IMAGE) {
val inlineLink = child.findChildOfType(MarkdownElementTypes.INLINE_LINK)
val url = inlineLink?.findChildOfType(MarkdownElementTypes.LINK_DESTINATION)
?.getUnescapedTextInNode(content)
?: inlineLink?.findChildOfType(MarkdownElementTypes.AUTOLINK)
?.findChildOfType(MarkdownTokenTypes.AUTOLINK)
?.getUnescapedTextInNode(content)
?: return@markdownAnnotator false
val textNode = inlineLink?.findChildOfType(MarkdownElementTypes.LINK_TITLE)
?: inlineLink?.findChildOfType(MarkdownElementTypes.LINK_TEXT)
val altText = textNode?.findChildOfType(MarkdownTokenTypes.TEXT)
?.getUnescapedTextInNode(content).orEmpty()
withLink(LinkAnnotation.Url(url = url)) {
pushStyle(linkStyle)
appendInlineContent(MARKDOWN_INLINE_IMAGE_TAG)
append(altText)
pop()
}
return@markdownAnnotator true
}
if (child.type in DISALLOWED_MARKDOWN_TYPES) {
append(content.substring(child.startOffset, child.endOffset))
return@markdownAnnotator true
}
false
},
config = markdownAnnotatorConfig(
eolAsNewLine = true,
),
)
}
@Composable @Composable
private fun MangaSummary( private fun MangaSummary(
expandedDescription: String, description: String,
shrunkDescription: String, notes: String,
expanded: Boolean, expanded: Boolean,
onEditNotesClicked: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val preferences = remember { Injekt.get<UiPreferences>() }
val loadImages = remember { preferences.imagesInDescription.get() }
val animProgress by animateFloatAsState( val animProgress by animateFloatAsState(
targetValue = if (expanded) 1f else 0f, targetValue = if (expanded) 1f else 0f,
label = "summary", label = "summary",
) )
var infoHeight by remember { mutableIntStateOf(0) }
Layout( Layout(
modifier = modifier.clipToBounds(), modifier = modifier.clipToBounds(),
contents = listOf( contents = listOf(
{ {
Text( Text(
text = "\n\n", // Shows at least 3 lines // Shows at least 3 lines if no notes
// when there are notes show 6
text = if (notes.isBlank()) "\n\n" else "\n\n\n\n\n",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
) )
}, },
{ {
Text( Column(
text = expandedDescription, modifier = Modifier.onSizeChanged { size ->
style = MaterialTheme.typography.bodyMedium, infoHeight = size.height
)
}, },
{ ) {
MangaNotesSection(
content = notes,
expanded = expanded,
onEditNotes = onEditNotesClicked,
)
SelectionContainer { SelectionContainer {
Text( MarkdownRender(
text = if (expanded) expandedDescription else shrunkDescription, content = description,
maxLines = Int.MAX_VALUE,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.secondaryItemAlpha(), modifier = Modifier.secondaryItemAlpha(),
annotator = descriptionAnnotator(
loadImages = loadImages,
linkStyle = getMarkdownLinkStyle().toSpanStyle(),
),
loadImages = loadImages,
) )
} }
}
}, },
{ {
val colors = listOf(Color.Transparent, MaterialTheme.colorScheme.background) val colors = listOf(Color.Transparent, MaterialTheme.colorScheme.background)
@@ -649,14 +715,11 @@ private fun MangaSummary(
} }
}, },
), ),
) { (shrunk, expanded, actual, scrim), constraints -> ) { (shrunk, actual, scrim), constraints ->
val shrunkHeight = shrunk.single() val shrunkHeight = shrunk.single()
.measure(constraints) .measure(constraints)
.height .height
val expandedHeight = expanded.single() val heightDelta = infoHeight - shrunkHeight
.measure(constraints)
.height
val heightDelta = expandedHeight - shrunkHeight
val scrimHeight = 24.dp.roundToPx() val scrimHeight = 24.dp.roundToPx()
val actualPlaceable = actual.single() val actualPlaceable = actual.single()
@@ -0,0 +1,60 @@
package eu.kanade.presentation.manga.components
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.tween
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import com.mohamedrejeb.richeditor.model.rememberRichTextState
import com.mohamedrejeb.richeditor.ui.material3.RichText
private val FADE_TIME = tween<Float>(500)
@Composable
fun MangaNotesDisplay(
content: String,
modifier: Modifier,
) {
val alpha = remember { Animatable(1f) }
var contentUpdatedOnce by remember { mutableStateOf(false) }
val richTextState = rememberRichTextState()
val primaryColor = MaterialTheme.colorScheme.primary
LaunchedEffect(content) {
richTextState.setMarkdown(content)
if (!contentUpdatedOnce) {
contentUpdatedOnce = true
return@LaunchedEffect
}
alpha.snapTo(targetValue = 0f)
alpha.animateTo(targetValue = 1f, animationSpec = FADE_TIME)
}
LaunchedEffect(Unit) {
richTextState.config.unorderedListIndent = 4
richTextState.config.orderedListIndent = 20
}
LaunchedEffect(primaryColor) {
richTextState.config.linkColor = primaryColor
}
SelectionContainer {
RichText(
modifier = modifier
// Only animate size if the notes changes
.then(if (contentUpdatedOnce) Modifier.animateContentSize() else Modifier)
.alpha(alpha.value),
style = MaterialTheme.typography.bodyMedium,
state = richTextState,
)
}
}
@@ -0,0 +1,90 @@
package eu.kanade.presentation.manga.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.EditNote
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.Button
import tachiyomi.presentation.core.components.material.ButtonDefaults
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource
@Composable
fun MangaNotesSection(
content: String,
expanded: Boolean,
onEditNotes: () -> Unit,
modifier: Modifier = Modifier,
) {
if (content.isBlank()) return
Column(
modifier = modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
MangaNotesDisplay(
content = content,
modifier = modifier.fillMaxWidth(),
)
if (expanded) {
Button(
onClick = onEditNotes,
colors = ButtonDefaults.buttonColors(
containerColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.primary,
),
shape = RoundedCornerShape(8.dp),
modifier = Modifier
.padding(horizontal = 16.dp, vertical = 4.dp),
) {
Row(
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
imageVector = Icons.Filled.EditNote,
contentDescription = null,
modifier = Modifier
.size(16.dp),
)
Text(
stringResource(MR.strings.action_edit_notes),
)
}
}
}
HorizontalDivider(
modifier = Modifier
.padding(
top = if (expanded) 0.dp else 12.dp,
bottom = if (expanded) 16.dp else 12.dp,
),
)
}
}
@PreviewLightDark
@Composable
private fun MangaNotesSectionPreview() {
MangaNotesSection(
onEditNotes = {},
expanded = true,
content = "# Hello world\ntest1234 hi there!",
)
}
@@ -0,0 +1,224 @@
package eu.kanade.presentation.manga.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.FormatListBulleted
import androidx.compose.material.icons.outlined.FormatBold
import androidx.compose.material.icons.outlined.FormatItalic
import androidx.compose.material.icons.outlined.FormatListNumbered
import androidx.compose.material.icons.outlined.FormatUnderlined
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import com.mohamedrejeb.richeditor.model.rememberRichTextState
import com.mohamedrejeb.richeditor.ui.material3.RichTextEditor
import com.mohamedrejeb.richeditor.ui.material3.RichTextEditorDefaults.richTextEditorColors
import eu.kanade.tachiyomi.ui.manga.notes.MangaNotesScreen
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource
import kotlin.time.Duration.Companion.seconds
private const val MAX_LENGTH = 250
private const val MAX_LENGTH_WARN = MAX_LENGTH * 0.9
@Composable
fun MangaNotesTextArea(
state: MangaNotesScreen.State,
onUpdate: (String) -> Unit,
modifier: Modifier = Modifier,
) {
val scope = rememberCoroutineScope()
val richTextState = rememberRichTextState()
val primaryColor = MaterialTheme.colorScheme.primary
DisposableEffect(scope, richTextState) {
snapshotFlow { richTextState.annotatedString }
.debounce(0.25.seconds)
.distinctUntilChanged()
.map { richTextState.toMarkdown() }
.onEach { onUpdate(it) }
.launchIn(scope)
onDispose {
onUpdate(richTextState.toMarkdown())
}
}
LaunchedEffect(Unit) {
richTextState.setMarkdown(state.notes)
richTextState.config.unorderedListIndent = 4
richTextState.config.orderedListIndent = 20
}
LaunchedEffect(primaryColor) {
richTextState.config.linkColor = primaryColor
}
val focusRequester = remember { FocusRequester() }
LaunchedEffect(focusRequester) {
focusRequester.requestFocus()
}
val textLength = remember(richTextState.annotatedString) { richTextState.toText().length }
Column(
modifier = modifier
.padding(horizontal = MaterialTheme.padding.small)
.fillMaxSize(),
) {
RichTextEditor(
state = richTextState,
textStyle = MaterialTheme.typography.bodyLarge,
maxLength = MAX_LENGTH,
placeholder = {
Text(text = stringResource(MR.strings.notes_placeholder))
},
colors = richTextEditorColors(
containerColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
),
contentPadding = PaddingValues(
horizontal = MaterialTheme.padding.medium,
),
modifier = Modifier
.weight(1f)
.fillMaxWidth()
.focusRequester(focusRequester),
)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.padding(vertical = MaterialTheme.padding.small)
.fillMaxWidth(),
) {
LazyRow(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(2.dp),
) {
item {
MangaNotesTextAreaButton(
onClick = { richTextState.toggleSpanStyle(SpanStyle(fontWeight = FontWeight.Bold)) },
isSelected = richTextState.currentSpanStyle.fontWeight == FontWeight.Bold,
icon = Icons.Outlined.FormatBold,
)
}
item {
MangaNotesTextAreaButton(
onClick = { richTextState.toggleSpanStyle(SpanStyle(fontStyle = FontStyle.Italic)) },
isSelected = richTextState.currentSpanStyle.fontStyle == FontStyle.Italic,
icon = Icons.Outlined.FormatItalic,
)
}
item {
MangaNotesTextAreaButton(
onClick = {
richTextState.toggleSpanStyle(SpanStyle(textDecoration = TextDecoration.Underline))
},
isSelected = richTextState.currentSpanStyle.textDecoration
?.contains(TextDecoration.Underline)
?: false,
icon = Icons.Outlined.FormatUnderlined,
)
}
item {
VerticalDivider(
modifier = Modifier
.padding(horizontal = MaterialTheme.padding.extraSmall)
.height(MaterialTheme.padding.large),
)
}
item {
MangaNotesTextAreaButton(
onClick = { richTextState.toggleUnorderedList() },
isSelected = richTextState.isUnorderedList,
icon = Icons.AutoMirrored.Outlined.FormatListBulleted,
)
}
item {
MangaNotesTextAreaButton(
onClick = { richTextState.toggleOrderedList() },
isSelected = richTextState.isOrderedList,
icon = Icons.Outlined.FormatListNumbered,
)
}
}
Box(
contentAlignment = Alignment.Center,
) {
Text(
text = (MAX_LENGTH - textLength).toString(),
color = if (textLength > MAX_LENGTH_WARN) {
MaterialTheme.colorScheme.error
} else {
Color.Unspecified
},
modifier = Modifier.padding(MaterialTheme.padding.extraSmall),
)
}
}
}
}
@Composable
fun MangaNotesTextAreaButton(
onClick: () -> Unit,
icon: ImageVector,
isSelected: Boolean,
modifier: Modifier = Modifier,
) {
Box(
modifier = modifier
.clip(MaterialTheme.shapes.small)
.clickable(
onClick = onClick,
enabled = true,
role = Role.Button,
),
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = icon,
contentDescription = icon.name,
tint = if (isSelected) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.primary,
modifier = Modifier
.background(color = if (isSelected) MaterialTheme.colorScheme.onBackground else Color.Transparent)
.padding(MaterialTheme.padding.extraSmall),
)
}
}
@@ -38,6 +38,7 @@ fun MangaToolbar(
onClickEditCategory: (() -> Unit)?, onClickEditCategory: (() -> Unit)?,
onClickRefresh: () -> Unit, onClickRefresh: () -> Unit,
onClickMigrate: (() -> Unit)?, onClickMigrate: (() -> Unit)?,
onClickEditNotes: () -> Unit,
// SY --> // SY -->
onClickEditInfo: (() -> Unit)?, onClickEditInfo: (() -> Unit)?,
onClickRecommend: (() -> Unit)?, onClickRecommend: (() -> Unit)?,
@@ -147,6 +148,12 @@ fun MangaToolbar(
), ),
) )
} }
add(
AppBar.OverflowAction(
title = stringResource(MR.strings.action_notes),
onClick = onClickEditNotes,
),
)
// SY --> // SY -->
if (onClickMerge != null) { if (onClickMerge != null) {
add( add(
@@ -0,0 +1,286 @@
package eu.kanade.presentation.manga.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Image
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.FirstBaseline
import androidx.compose.ui.text.Placeholder
import androidx.compose.ui.text.PlaceholderVerticalAlign
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextLinkStyles
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.mikepenz.markdown.coil3.Coil3ImageTransformerImpl
import com.mikepenz.markdown.compose.LocalBulletListHandler
import com.mikepenz.markdown.compose.Markdown
import com.mikepenz.markdown.compose.components.markdownComponents
import com.mikepenz.markdown.compose.elements.MarkdownBulletList
import com.mikepenz.markdown.compose.elements.MarkdownDivider
import com.mikepenz.markdown.compose.elements.MarkdownOrderedList
import com.mikepenz.markdown.compose.elements.MarkdownTable
import com.mikepenz.markdown.compose.elements.MarkdownTableHeader
import com.mikepenz.markdown.compose.elements.MarkdownTableRow
import com.mikepenz.markdown.compose.elements.MarkdownText
import com.mikepenz.markdown.compose.elements.listDepth
import com.mikepenz.markdown.model.DefaultMarkdownColors
import com.mikepenz.markdown.model.DefaultMarkdownInlineContent
import com.mikepenz.markdown.model.DefaultMarkdownTypography
import com.mikepenz.markdown.model.MarkdownAnnotator
import com.mikepenz.markdown.model.MarkdownColors
import com.mikepenz.markdown.model.MarkdownPadding
import com.mikepenz.markdown.model.MarkdownTypography
import com.mikepenz.markdown.model.NoOpImageTransformerImpl
import com.mikepenz.markdown.model.markdownAnnotator
import com.mikepenz.markdown.model.rememberMarkdownState
import org.intellij.markdown.MarkdownTokenTypes.Companion.HTML_TAG
import org.intellij.markdown.flavours.MarkdownFlavourDescriptor
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
import org.intellij.markdown.flavours.commonmark.CommonMarkMarkerProcessor
import org.intellij.markdown.flavours.gfm.table.GitHubTableMarkerProvider
import org.intellij.markdown.parser.MarkerProcessor
import org.intellij.markdown.parser.MarkerProcessorFactory
import org.intellij.markdown.parser.ProductionHolder
import org.intellij.markdown.parser.constraints.CommonMarkdownConstraints
import org.intellij.markdown.parser.constraints.MarkdownConstraints
import org.intellij.markdown.parser.markerblocks.MarkerBlockProvider
import org.intellij.markdown.parser.markerblocks.providers.AtxHeaderProvider
import org.intellij.markdown.parser.markerblocks.providers.BlockQuoteProvider
import org.intellij.markdown.parser.markerblocks.providers.CodeBlockProvider
import org.intellij.markdown.parser.markerblocks.providers.CodeFenceProvider
import org.intellij.markdown.parser.markerblocks.providers.HorizontalRuleProvider
import org.intellij.markdown.parser.markerblocks.providers.ListMarkerProvider
import org.intellij.markdown.parser.markerblocks.providers.SetextHeaderProvider
import tachiyomi.presentation.core.components.material.padding
const val MARKDOWN_INLINE_IMAGE_TAG = "MARKDOWN_INLINE_IMAGE"
@Composable
fun MarkdownRender(
content: String,
modifier: Modifier = Modifier,
flavour: MarkdownFlavourDescriptor = SimpleMarkdownFlavourDescriptor,
annotator: MarkdownAnnotator = remember { markdownAnnotator() },
loadImages: Boolean = true,
) {
Markdown(
markdownState = rememberMarkdownState(
content = content,
flavour = flavour,
immediate = true,
),
annotator = annotator,
colors = getMarkdownColors(),
typography = getMarkdownTypography(),
padding = markdownPadding,
components = markdownComponents,
imageTransformer = remember(loadImages) {
if (loadImages) Coil3ImageTransformerImpl else NoOpImageTransformerImpl()
},
inlineContent = getMarkdownInlineContent(),
modifier = modifier,
)
}
@Composable
@ReadOnlyComposable
private fun getMarkdownColors(): MarkdownColors {
val codeBackground = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f)
return DefaultMarkdownColors(
text = MaterialTheme.colorScheme.onSurface,
codeBackground = codeBackground,
inlineCodeBackground = codeBackground,
dividerColor = MaterialTheme.colorScheme.outlineVariant,
tableBackground = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.05f),
)
}
@Composable
@ReadOnlyComposable
fun getMarkdownLinkStyle() = MaterialTheme.typography.bodyMedium.copy(
color = MaterialTheme.colorScheme.primary,
fontWeight = FontWeight.Bold,
)
@Composable
@ReadOnlyComposable
private fun getMarkdownTypography(): MarkdownTypography {
val link = getMarkdownLinkStyle()
return DefaultMarkdownTypography(
h1 = MaterialTheme.typography.headlineMedium,
h2 = MaterialTheme.typography.headlineSmall,
h3 = MaterialTheme.typography.titleLarge,
h4 = MaterialTheme.typography.titleMedium,
h5 = MaterialTheme.typography.titleSmall,
h6 = MaterialTheme.typography.bodyLarge,
text = MaterialTheme.typography.bodyMedium,
code = MaterialTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Monospace),
inlineCode = MaterialTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Monospace),
quote = MaterialTheme.typography.bodyMedium.plus(SpanStyle(fontStyle = FontStyle.Italic)),
paragraph = MaterialTheme.typography.bodyMedium,
ordered = MaterialTheme.typography.bodyMedium,
bullet = MaterialTheme.typography.bodyMedium,
list = MaterialTheme.typography.bodyMedium,
textLink = TextLinkStyles(style = link.toSpanStyle()),
table = MaterialTheme.typography.bodyMedium,
)
}
private val markdownPadding = object : MarkdownPadding {
override val block: Dp = 2.dp
override val blockQuote: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 0.dp)
override val blockQuoteBar: PaddingValues.Absolute = PaddingValues.Absolute(
left = 4.dp,
top = 2.dp,
right = 4.dp,
bottom = 2.dp,
)
override val blockQuoteText: PaddingValues = PaddingValues(vertical = 4.dp)
override val codeBlock: PaddingValues = PaddingValues(8.dp)
override val list: Dp = 0.dp
override val listIndent: Dp = 8.dp
override val listItemBottom: Dp = 0.dp
override val listItemTop: Dp = 0.dp
}
private val markdownComponents = markdownComponents(
horizontalRule = {
MarkdownDivider(
modifier = Modifier
.padding(vertical = MaterialTheme.padding.extraSmall)
.fillMaxWidth(),
)
},
orderedList = { ol ->
Column(modifier = Modifier.padding(start = MaterialTheme.padding.small)) {
MarkdownOrderedList(
content = ol.content,
node = ol.node,
style = ol.typography.ordered,
depth = ol.listDepth,
markerModifier = { Modifier.alignBy(FirstBaseline) },
listModifier = { Modifier.alignBy(FirstBaseline) },
)
}
},
unorderedList = { ul ->
val markers = listOf("", "", "", "")
CompositionLocalProvider(
LocalBulletListHandler provides { _, _, _, _, _ -> "${markers[ul.listDepth % markers.size]} " },
) {
Column(modifier = Modifier.padding(start = MaterialTheme.padding.small)) {
MarkdownBulletList(
content = ul.content,
node = ul.node,
style = ul.typography.bullet,
markerModifier = { Modifier.alignBy(FirstBaseline) },
listModifier = { Modifier.alignBy(FirstBaseline) },
)
}
}
},
table = { t ->
MarkdownTable(
content = t.content,
node = t.node,
style = t.typography.text,
headerBlock = { content, header, tableWidth, style ->
MarkdownTableHeader(
content = content,
header = header,
tableWidth = tableWidth,
style = style,
maxLines = Int.MAX_VALUE,
)
},
rowBlock = { content, header, tableWidth, style ->
MarkdownTableRow(
content = content,
header = header,
tableWidth = tableWidth,
style = style,
maxLines = Int.MAX_VALUE,
)
},
)
},
custom = { type, model ->
if (type in DISALLOWED_MARKDOWN_TYPES) {
MarkdownText(
content = model.content.substring(model.node.startOffset, model.node.endOffset),
style = model.typography.text,
)
}
},
)
@Composable
@ReadOnlyComposable
private fun getMarkdownInlineContent() = DefaultMarkdownInlineContent(
inlineContent = mapOf(
MARKDOWN_INLINE_IMAGE_TAG to InlineTextContent(
placeholder = Placeholder(
width = MaterialTheme.typography.bodyMedium.fontSize * 1.25,
height = MaterialTheme.typography.bodyMedium.fontSize * 1.25,
placeholderVerticalAlign = PlaceholderVerticalAlign.TextCenter,
),
children = {
Icon(
imageVector = Icons.Outlined.Image,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
)
},
),
),
)
private object SimpleMarkdownFlavourDescriptor : CommonMarkFlavourDescriptor() {
override val markerProcessorFactory: MarkerProcessorFactory = SimpleMarkdownProcessFactory
}
private object SimpleMarkdownProcessFactory : MarkerProcessorFactory {
override fun createMarkerProcessor(productionHolder: ProductionHolder): MarkerProcessor<*> {
return SimpleMarkdownMarkerProcessor(productionHolder, CommonMarkdownConstraints.BASE)
}
}
/**
* Like `CommonMarkFlavour`, but with html blocks and reference links removed and
* table support added
*/
private class SimpleMarkdownMarkerProcessor(
productionHolder: ProductionHolder,
constraints: MarkdownConstraints,
) : CommonMarkMarkerProcessor(productionHolder, constraints) {
private val markerBlockProviders = listOf(
CodeBlockProvider(),
HorizontalRuleProvider(),
CodeFenceProvider(),
SetextHeaderProvider(),
BlockQuoteProvider(),
ListMarkerProvider(),
AtxHeaderProvider(),
GitHubTableMarkerProvider(),
)
override fun getMarkerBlockProviders(): List<MarkerBlockProvider<StateInfo>> {
return markerBlockProviders
}
}
val DISALLOWED_MARKDOWN_TYPES = arrayOf(HTML_TAG)
@@ -17,7 +17,6 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -25,8 +24,6 @@ import eu.kanade.presentation.components.ChipBorder
import eu.kanade.presentation.components.SuggestionChip import eu.kanade.presentation.components.SuggestionChip
import eu.kanade.presentation.components.SuggestionChipDefaults import eu.kanade.presentation.components.SuggestionChipDefaults
import eu.kanade.presentation.theme.TachiyomiPreviewTheme import eu.kanade.presentation.theme.TachiyomiPreviewTheme
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.online.all.EHentai
import exh.metadata.metadata.EHentaiSearchMetadata import exh.metadata.metadata.EHentaiSearchMetadata
import exh.metadata.metadata.RaisedSearchMetadata import exh.metadata.metadata.RaisedSearchMetadata
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
@@ -49,7 +46,7 @@ value class SearchMetadataChips(
val tags: Map<String, List<DisplayTag>>, val tags: Map<String, List<DisplayTag>>,
) { ) {
companion object { companion object {
operator fun invoke(meta: RaisedSearchMetadata?, source: Source, tags: List<String>?): SearchMetadataChips? { operator fun invoke(meta: RaisedSearchMetadata?, sourceId: Long, tags: List<String>?): SearchMetadataChips? {
return if (meta != null) { return if (meta != null) {
SearchMetadataChips( SearchMetadataChips(
meta.tags meta.tags
@@ -59,11 +56,11 @@ value class SearchMetadataChips(
namespace = it.namespace, namespace = it.namespace,
text = it.name, text = it.name,
search = if (!it.namespace.isNullOrEmpty()) { search = if (!it.namespace.isNullOrEmpty()) {
SourceTagsUtil.getWrappedTag(source.id, namespace = it.namespace, tag = it.name) SourceTagsUtil.getWrappedTag(sourceId, namespace = it.namespace, tag = it.name)
} else { } else {
SourceTagsUtil.getWrappedTag(source.id, fullTag = it.name) SourceTagsUtil.getWrappedTag(sourceId, fullTag = it.name)
} ?: it.name, } ?: it.name,
border = if (source.id == EXH_SOURCE_ID || source.id == EH_SOURCE_ID) { border = if (sourceId == EXH_SOURCE_ID || sourceId == EH_SOURCE_ID) {
when (it.type) { when (it.type) {
EHentaiSearchMetadata.TAG_TYPE_NORMAL -> 2 EHentaiSearchMetadata.TAG_TYPE_NORMAL -> 2
EHentaiSearchMetadata.TAG_TYPE_LIGHT -> 1 EHentaiSearchMetadata.TAG_TYPE_LIGHT -> 1
@@ -178,7 +175,6 @@ fun TagsChip(
fun NamespaceTagsPreview() { fun NamespaceTagsPreview() {
TachiyomiPreviewTheme { TachiyomiPreviewTheme {
Surface { Surface {
val context = LocalContext.current
NamespaceTags( NamespaceTags(
tags = remember { tags = remember {
EHentaiSearchMetadata().apply { EHentaiSearchMetadata().apply {
@@ -216,7 +212,7 @@ fun NamespaceTagsPreview() {
), ),
), ),
) )
}.let { SearchMetadataChips(it, EHentai(EXH_SOURCE_ID, true, context), emptyList()) }!! }.let { SearchMetadataChips(it, EXH_SOURCE_ID, emptyList()) }!!
}, },
onClick = {}, onClick = {},
) )
@@ -7,12 +7,9 @@ import androidx.compose.material.icons.automirrored.outlined.Label
import androidx.compose.material.icons.automirrored.outlined.PlaylistAdd import androidx.compose.material.icons.automirrored.outlined.PlaylistAdd
import androidx.compose.material.icons.outlined.CloudOff import androidx.compose.material.icons.outlined.CloudOff
import androidx.compose.material.icons.outlined.GetApp import androidx.compose.material.icons.outlined.GetApp
import androidx.compose.material.icons.outlined.HelpOutline
import androidx.compose.material.icons.outlined.History import androidx.compose.material.icons.outlined.History
import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.outlined.Label
import androidx.compose.material.icons.outlined.NewReleases import androidx.compose.material.icons.outlined.NewReleases
import androidx.compose.material.icons.outlined.PlaylistAdd
import androidx.compose.material.icons.outlined.QueryStats import androidx.compose.material.icons.outlined.QueryStats
import androidx.compose.material.icons.outlined.Settings import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material.icons.outlined.Storage import androidx.compose.material.icons.outlined.Storage
@@ -1,5 +1,6 @@
package eu.kanade.presentation.more package eu.kanade.presentation.more
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -13,13 +14,10 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.tooling.preview.PreviewLightDark
import com.halilibo.richtext.markdown.Markdown import eu.kanade.presentation.manga.components.MarkdownRender
import com.halilibo.richtext.ui.RichTextStyle
import com.halilibo.richtext.ui.material3.RichText
import com.halilibo.richtext.ui.string.RichTextStringStyle
import eu.kanade.presentation.theme.TachiyomiPreviewTheme import eu.kanade.presentation.theme.TachiyomiPreviewTheme
import org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor
import tachiyomi.i18n.MR import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.i18n.stringResource
@@ -42,17 +40,15 @@ fun NewUpdateScreen(
rejectText = stringResource(MR.strings.action_not_now), rejectText = stringResource(MR.strings.action_not_now),
onRejectClick = onRejectUpdate, onRejectClick = onRejectUpdate,
) { ) {
RichText( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = MaterialTheme.padding.large), .padding(vertical = MaterialTheme.padding.large),
style = RichTextStyle(
stringStyle = RichTextStringStyle(
linkStyle = SpanStyle(color = MaterialTheme.colorScheme.primary),
),
),
) { ) {
Markdown(content = changelogInfo) MarkdownRender(
content = changelogInfo,
flavour = GFMFlavourDescriptor(),
)
TextButton( TextButton(
onClick = onOpenInBrowser, onClick = onOpenInBrowser,
@@ -42,7 +42,9 @@ fun OnboardingScreen(
} }
val isLastStep = currentStep == steps.lastIndex val isLastStep = currentStep == steps.lastIndex
BackHandler(enabled = currentStep != 0, onBack = { currentStep-- }) BackHandler(enabled = currentStep != 0) {
currentStep--
}
InfoScreen( InfoScreen(
icon = Icons.Outlined.RocketLaunch, icon = Icons.Outlined.RocketLaunch,
@@ -122,7 +122,7 @@ internal class PermissionStep : OnboardingStep {
color = MaterialTheme.colorScheme.onPrimaryContainer, color = MaterialTheme.colorScheme.onPrimaryContainer,
) )
val crashlyticsPref = privacyPreferences.crashlytics() val crashlyticsPref = privacyPreferences.crashlytics
val crashlytics by crashlyticsPref.collectAsState() val crashlytics by crashlyticsPref.collectAsState()
PermissionSwitch( PermissionSwitch(
title = stringResource(MR.strings.onboarding_permission_crashlytics), title = stringResource(MR.strings.onboarding_permission_crashlytics),
@@ -131,7 +131,7 @@ internal class PermissionStep : OnboardingStep {
onToggleChange = crashlyticsPref::set, onToggleChange = crashlyticsPref::set,
) )
val analyticsPref = privacyPreferences.analytics() val analyticsPref = privacyPreferences.analytics
val analytics by analyticsPref.collectAsState() val analytics by analyticsPref.collectAsState()
PermissionSwitch( PermissionSwitch(
title = stringResource(MR.strings.onboarding_permission_analytics), title = stringResource(MR.strings.onboarding_permission_analytics),
@@ -30,7 +30,7 @@ import uy.kohesive.injekt.api.get
internal class StorageStep : OnboardingStep { internal class StorageStep : OnboardingStep {
private val storagePref = Injekt.get<StoragePreferences>().baseStorageDirectory() private val storagePref = Injekt.get<StoragePreferences>().baseStorageDirectory
private var _isComplete by mutableStateOf(false) private var _isComplete by mutableStateOf(false)
@@ -19,13 +19,13 @@ internal class ThemeStep : OnboardingStep {
@Composable @Composable
override fun Content() { override fun Content() {
val themeModePref = uiPreferences.themeMode() val themeModePref = uiPreferences.themeMode
val themeMode by themeModePref.collectAsState() val themeMode by themeModePref.collectAsState()
val appThemePref = uiPreferences.appTheme() val appThemePref = uiPreferences.appTheme
val appTheme by appThemePref.collectAsState() val appTheme by appThemePref.collectAsState()
val amoledPref = uiPreferences.themeDarkAmoled() val amoledPref = uiPreferences.themeDarkAmoled
val amoled by amoledPref.collectAsState() val amoled by amoledPref.collectAsState()
Column { Column {
@@ -1,5 +1,6 @@
package eu.kanade.presentation.more.settings package eu.kanade.presentation.more.settings
import androidx.annotation.IntRange
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
@@ -14,13 +15,13 @@ sealed class Preference {
abstract val title: String abstract val title: String
abstract val enabled: Boolean abstract val enabled: Boolean
sealed class PreferenceItem<T> : Preference() { sealed class PreferenceItem<T, R> : Preference() {
// SY --> // SY -->
abstract val subtitle: CharSequence? abstract val subtitle: CharSequence?
// SY <-- // SY <--
abstract val icon: ImageVector? abstract val icon: ImageVector?
abstract val onValueChanged: suspend (value: T) -> Boolean abstract val onValueChanged: suspend (value: T) -> R
/** /**
* A basic [PreferenceItem] that only displays texts. * A basic [PreferenceItem] that only displays texts.
@@ -29,10 +30,11 @@ sealed class Preference {
override val title: String, override val title: String,
override val subtitle: CharSequence? = null, override val subtitle: CharSequence? = null,
override val enabled: Boolean = true, override val enabled: Boolean = true,
val widget: @Composable (() -> Unit)? = null,
val onClick: (() -> Unit)? = null, val onClick: (() -> Unit)? = null,
) : PreferenceItem<String>() { ) : PreferenceItem<String, Unit>() {
override val icon: ImageVector? = null override val icon: ImageVector? = null
override val onValueChanged: suspend (value: String) -> Boolean = { true } override val onValueChanged: suspend (value: String) -> Unit = {}
} }
/** /**
@@ -44,7 +46,7 @@ sealed class Preference {
override val subtitle: CharSequence? = null, override val subtitle: CharSequence? = null,
override val enabled: Boolean = true, override val enabled: Boolean = true,
override val onValueChanged: suspend (value: Boolean) -> Boolean = { true }, override val onValueChanged: suspend (value: Boolean) -> Boolean = { true },
) : PreferenceItem<Boolean>() { ) : PreferenceItem<Boolean, Boolean>() {
override val icon: ImageVector? = null override val icon: ImageVector? = null
} }
@@ -53,14 +55,14 @@ sealed class Preference {
*/ */
data class SliderPreference( data class SliderPreference(
val value: Int, val value: Int,
val max: Int,
val min: Int = 0,
val steps: Int = 0,
override val title: String, override val title: String,
override val subtitle: String? = null, override val subtitle: String? = null,
val valueString: String? = null,
val valueRange: IntProgression = 0..1,
@IntRange(from = 0) val steps: Int = with(valueRange) { (last - first) - 1 },
override val enabled: Boolean = true, override val enabled: Boolean = true,
override val onValueChanged: suspend (value: Int) -> Boolean = { true }, override val onValueChanged: suspend (value: Int) -> Unit = {},
) : PreferenceItem<Int>() { ) : PreferenceItem<Int, Unit>() {
override val icon: ImageVector? = null override val icon: ImageVector? = null
} }
@@ -78,7 +80,7 @@ sealed class Preference {
override val icon: ImageVector? = null, override val icon: ImageVector? = null,
override val enabled: Boolean = true, override val enabled: Boolean = true,
override val onValueChanged: suspend (value: T) -> Boolean = { true }, override val onValueChanged: suspend (value: T) -> Boolean = { true },
) : PreferenceItem<T>() { ) : PreferenceItem<T, Boolean>() {
internal fun internalSet(value: Any) = preference.set(value as T) internal fun internalSet(value: Any) = preference.set(value as T)
internal suspend fun internalOnValueChanged(value: Any) = onValueChanged(value as T) internal suspend fun internalOnValueChanged(value: Any) = onValueChanged(value as T)
@@ -99,8 +101,8 @@ sealed class Preference {
{ v, e -> subtitle?.format(e[v]) }, { v, e -> subtitle?.format(e[v]) },
override val icon: ImageVector? = null, override val icon: ImageVector? = null,
override val enabled: Boolean = true, override val enabled: Boolean = true,
override val onValueChanged: suspend (value: String) -> Boolean = { true }, override val onValueChanged: suspend (value: String) -> Unit = {},
) : PreferenceItem<String>() ) : PreferenceItem<String, Unit>()
/** /**
* A [PreferenceItem] that displays a list of entries as a dialog. * A [PreferenceItem] that displays a list of entries as a dialog.
@@ -124,7 +126,7 @@ sealed class Preference {
override val icon: ImageVector? = null, override val icon: ImageVector? = null,
override val enabled: Boolean = true, override val enabled: Boolean = true,
override val onValueChanged: suspend (value: Set<String>) -> Boolean = { true }, override val onValueChanged: suspend (value: Set<String>) -> Boolean = { true },
) : PreferenceItem<Set<String>>() ) : PreferenceItem<Set<String>, Boolean>()
/** /**
* A [PreferenceItem] that shows a EditText in the dialog. * A [PreferenceItem] that shows a EditText in the dialog.
@@ -135,7 +137,7 @@ sealed class Preference {
override val subtitle: String? = "%s", override val subtitle: String? = "%s",
override val enabled: Boolean = true, override val enabled: Boolean = true,
override val onValueChanged: suspend (value: String) -> Boolean = { true }, override val onValueChanged: suspend (value: String) -> Boolean = { true },
) : PreferenceItem<String>() { ) : PreferenceItem<String, Boolean>() {
override val icon: ImageVector? = null override val icon: ImageVector? = null
} }
@@ -146,31 +148,31 @@ sealed class Preference {
val tracker: Tracker, val tracker: Tracker,
val login: () -> Unit, val login: () -> Unit,
val logout: () -> Unit, val logout: () -> Unit,
) : PreferenceItem<String>() { ) : PreferenceItem<String, Unit>() {
override val title: String = "" override val title: String = ""
override val enabled: Boolean = true override val enabled: Boolean = true
override val subtitle: String? = null override val subtitle: String? = null
override val icon: ImageVector? = null override val icon: ImageVector? = null
override val onValueChanged: suspend (value: String) -> Boolean = { true } override val onValueChanged: suspend (value: String) -> Unit = {}
} }
data class InfoPreference( data class InfoPreference(
override val title: String, override val title: String,
) : PreferenceItem<String>() { ) : PreferenceItem<String, Unit>() {
override val enabled: Boolean = true override val enabled: Boolean = true
override val subtitle: String? = null override val subtitle: String? = null
override val icon: ImageVector? = null override val icon: ImageVector? = null
override val onValueChanged: suspend (value: String) -> Boolean = { true } override val onValueChanged: suspend (value: String) -> Unit = {}
} }
data class CustomPreference( data class CustomPreference(
override val title: String, override val title: String,
val content: @Composable () -> Unit, val content: @Composable () -> Unit,
) : PreferenceItem<Unit>() { ) : PreferenceItem<Unit, Unit>() {
override val enabled: Boolean = true override val enabled: Boolean = true
override val subtitle: String? = null override val subtitle: String? = null
override val icon: ImageVector? = null override val icon: ImageVector? = null
override val onValueChanged: suspend (value: Unit) -> Boolean = { true } override val onValueChanged: suspend (value: Unit) -> Unit = {}
} }
} }
@@ -178,6 +180,6 @@ sealed class Preference {
override val title: String, override val title: String,
override val enabled: Boolean = true, override val enabled: Boolean = true,
val preferenceItems: ImmutableList<PreferenceItem<out Any>>, val preferenceItems: ImmutableList<PreferenceItem<out Any, out Any>>,
) : Preference() ) : Preference()
} }

Some files were not shown because too many files have changed in this diff Show More