Compare commits

...

133 Commits

Author SHA1 Message Date
c615f4d458 Release v0.14.6 2023-04-16 11:00:14 -04:00
9e09a20e65 Avoid uncaught exceptions from OkHttp interceptors crashing entire app
(cherry picked from commit 26d422b0ae)
2023-04-16 10:57:40 -04:00
7115a9b9fe Update track domain shikimori.me (#9333)
shikimori.me

(cherry picked from commit 564a0980b9)
2023-04-16 10:53:01 -04:00
fd8b97fc87 Better handle overflowing content in MigrateDialog actions
Fixes #9207

(cherry picked from commit b7cd7b8b4e)
2023-04-16 10:52:53 -04:00
4dd67e4348 Save current chapter progress when navigating to adjacent chapters
Fixes #9295

(cherry picked from commit 776d36caf1)
2023-04-16 10:52:42 -04:00
10973bf3cd Fix Spanish (Latin America) being missing from in-app language selection
(cherry picked from commit 290efb0283)
2023-04-16 10:51:29 -04:00
934ed0551a Bump subsampling-scale-image-view
(cherry picked from commit e5e18c2030)
2023-04-16 10:51:16 -04:00
38428c6ebe Show proper string in manga detail screen for SourceNotInstalledException
(cherry picked from commit 14d1bcacc9)
2023-04-16 10:51:05 -04:00
bf85e147e7 Set default automatic library updates to off
(cherry picked from commit abd23b6826)
2023-04-16 10:50:55 -04:00
d2dd34c2e5 Use queued last chapter read number when performing delayed tracker update
Fixes #8876

(cherry picked from commit f7f2072621)
2023-04-16 10:50:24 -04:00
c4ab2b4675 Bump default user agent string and minimum WebView version
(cherry picked from commit c6e5f8abd9)
2023-04-16 10:49:28 -04:00
aa2ec5940f Avoid crashing in SourcePreferencesScreen if source can't be loaded
(cherry picked from commit 4efca04765)
2023-04-16 10:49:11 -04:00
79323de326 Avoid crash in DeleteLibraryMangaDialog
No clue why it ever gets a -1 index though.

(cherry picked from commit b12c7cf963)
2023-04-16 10:49:05 -04:00
08e6487a9a Fix download queue page count display bug (#9126)
When restarting a download, the page count would display as 0 until
the first page download completion, after all the existing pages were
rechecked.

To fix, calculate downloadedImages from pages instead of relying on
the downloader to reset and increment the count.

(cherry picked from commit 779df32e98)
2023-04-16 10:48:16 -04:00
4498b10a10 Fix occasional crash when opening library settings sheet
See https://stackoverflow.com/questions/47648689/sealed-classs-objects-mysteriously-becoming-null-when-referenced-by-other-compa

(cherry picked from commit c0e2eb211d)
2023-04-16 10:48:05 -04:00
6f2bb18d72 Avoid crash when loading invalid extension package
(cherry picked from commit 3d7c136320)
2023-04-16 10:47:58 -04:00
b690de55e5 Release v0.14.5 2023-02-19 15:25:35 -05:00
83fda20078 Avoid crashes if headers can't be built for usage in WebView
(cherry picked from commit ec49411bee)
2023-02-19 11:52:09 -05:00
f656a37045 Avoid crashing if getChapterUrl is not implemented
Fixes #9105

(cherry picked from commit ceaf579cb0)
2023-02-19 11:51:53 -05:00
c58b495433 MainActivity: Avoid navigator-related crash when handling onNewIntent (#9104)
(cherry picked from commit d3dadf71e8)
2023-02-19 11:51:44 -05:00
242aeb6a68 Avoid crashing if opening browse with unavailable source
(cherry picked from commit 0ef7650c1a)
2023-02-19 11:50:33 -05:00
d9969cea8a Fix ID type mismatch in MigrateSearchScreenModel (#9090)
`it.id` is the source ID of the source being sorted.
`state.value.manga!!.id` is the manga ID of the selected manga.
`state.value.manga!!.source` is the source ID of the selected manga.

(cherry picked from commit dc2eaf0788)
2023-02-19 11:50:26 -05:00
d61db5931e Move reader preloading to IO scope
Maybe fixes #8440

(cherry picked from commit e052bdef96)
2023-02-19 11:50:05 -05:00
0ea3ac9807 Avoid preload download check if chapter is already loaded or loading
Maybe fixes #8953, #9060

(cherry picked from commit d522d6d545)
2023-02-19 11:49:58 -05:00
f9e43f574f MangaCoverDialog: Disable memory cache (#9066)
(cherry picked from commit 1671a56f42)
2023-02-19 11:49:51 -05:00
5ef11e61d0 Prioritize finding selected chapter when deduping reader chapters
Fixes #9054

(cherry picked from commit 23432e4405)
2023-02-19 11:49:44 -05:00
48546c3db4 Scaffold: Fix snackbar bottom inset (#9052)
(cherry picked from commit 34a586ce48)
2023-02-19 11:49:38 -05:00
4d87ed496c Remove FAB extra padding in DownloadQueueScreen (#9053)
(cherry picked from commit ad762f8303)
2023-02-19 11:49:32 -05:00
06d12e6562 Fix crash in library when selected category is deleted (#9044)
(cherry picked from commit 13bb45b4be)
2023-02-19 11:49:24 -05:00
477e3d9b94 Release v0.14.4 2023-02-05 10:35:15 -05:00
3c16082636 Don't show SourceNotInstalledException name in error snackbar 2023-02-05 10:23:30 -05:00
29aee68ec7 Revert "Show no pinned sources message when attempting to migrate/search"
This reverts commit 6bb3070c57.

This doesn't quite work correctly, so reverting for now.
We'll have to have more robust states or something to deal with this in the
future.
2023-02-05 10:20:19 -05:00
75e23299b4 Bump desugaring libs 2023-02-05 10:02:20 -05:00
935ff1ee98 Translations update from Hosted Weblate (#8960)
Weblate translations



















Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ar/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/de/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/es/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fa/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/gl/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/id/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ko/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/nl/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pl/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pt/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/th/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/uk/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hans/
Translation: Tachiyomi/Tachiyomi 0.x

Co-authored-by: Abou <aboozar.gh.r@gmail.com>
Co-authored-by: Alba Paz <albapazpi@gmail.com>
Co-authored-by: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: Blue <bluestuffish@gmail.com>
Co-authored-by: Dan <denqwerta@gmail.com>
Co-authored-by: FTDaily <farrell05june2005@gmail.com>
Co-authored-by: Gabriel Lebis <gableb@hotmail.fr>
Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Shippo <Shipox@users.noreply.hosted.weblate.org>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: The Ghost <marcc2018@gmail.com>
Co-authored-by: ZiomaleQ <r.partyka30@gmail.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: jinu147 <nesqea20@gmail.com>
Co-authored-by: stevenlele <stevenlele@outlook.com>
2023-02-05 10:00:14 -05:00
c672cb81ec Update dependency com.android.tools.build:gradle to v7.4.1 (#9024)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-05 09:56:17 -05:00
7559c133c0 Call.await(): copy exception message when preserving error stack (#9013) 2023-02-01 11:09:35 -05:00
589bdba0b1 Show exception class in snackbar message (#9006)
* Show exception class in snackbar message

* omit IOException too
2023-01-31 22:36:53 -05:00
aca65f13bb Misc Service cleanup (#9005)
* Simplify DownloadService wake lock handling

_isRunning is only modified in onCreate/onDestroy, so the listener
job is redundant.

* Drop superclass calls to Service.onCreate/onDestroy

From https://developer.android.com/guide/components/services
> Note: Unlike the activity lifecycle callback methods, you are not
> required to call the superclass implementation of these callback
> methods.
2023-01-30 17:25:54 -05:00
7bf30a094a Update dependency androidx.compose.material:material to v1.4.0-alpha05 (#8997)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-29 10:35:19 -05:00
5454279a8e Update dependency com.google.android.material:material to v1.8.0 (#8999)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-29 10:31:41 -05:00
006bcdf934 Update dependency androidx.core:core-ktx to v1.10.0-alpha02 (#8998)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-29 10:27:48 -05:00
b00f00730d Set InsertPage status to Ready (#9001)
Fixes insert page just loading
2023-01-29 09:03:12 -05:00
f2c48480b6 Move some interactors to domain module 2023-01-27 22:37:17 -05:00
1730dd6af1 Move more things around 2023-01-27 22:31:12 -05:00
2501fef9e4 Split UpdatesGridGlanceWidget into smaller bits (#8991)
- Renamed Composables
- Moved Constants to core module
2023-01-27 14:49:57 -05:00
12e41b6e6f Move Glance Widget to seperate module (#8989)
Move Widget to seperate module

- Create a core module for presentation. Widget and App will share some resources and hopefully composables
2023-01-26 17:53:24 -05:00
c892c793a8 [BackupRestorer] Handle uncompressed backups (#8988)
[Backups] Handle uncompressed backups
2023-01-26 09:14:18 -05:00
3a82b4d924 Don't crash on timeout in renewCache() (#8986)
Fixes #8962.

withTimeout throws a TimeoutCancellationException if the timeout
expires. To avoid crashing renewalJob when there are no extensions,
use withTimeoutOrNull which does not throw on timeout.
2023-01-25 18:18:17 -05:00
b4b3a4d286 Fixup HttpPageLoader _loadPage (#8984)
Fixup for e4bc8990 (#8955)

HttpSource.fetchImage() uses Call.asObservableSuccess(), which
cancels the call on unsubscribe. This causes the call to be cancelled
before it is used, leading to a "java.net.SocketException: Socket is
closed" when trying to use the response in putImageToCache().

To fix this, use Call.awaitSuccess() via a new HttpSource.getImage()
suspending function. This addition to source-api is only intended for
app use, so it will not be added to the extensions-api stubs.
2023-01-25 18:18:12 -05:00
448702e5be OkHttp Call: split await() and awaitSuccess() (#8980) 2023-01-24 22:34:31 -05:00
2ef1f07aae Replace PageLoader.getPage() with PageLoader.loadPage() (#8976)
* Follow page status via StateFlow

Keep getPage subscription since it's needed to load the pages

* Replace PageLoader.getPage with PageLoader.loadPage
2023-01-23 17:10:44 -05:00
1a319601de Fix extension search query cursor and debounce (#8972)
* Fix extension search query cursor

* debounce

* extract debounce constant
2023-01-22 16:19:46 -05:00
cdf242e8c8 Move more to data and domain modules (#8973) 2023-01-22 16:19:22 -05:00
aee785a8bb Move more implementation to data module (#8971) 2023-01-22 11:44:39 -05:00
d45fc1e245 Move more models to domain module 2023-01-22 11:04:50 -05:00
14500ba4f8 Move more repositories to domain module 2023-01-22 10:59:52 -05:00
345e9c2a9a Move more models to domain module 2023-01-22 10:54:28 -05:00
b53e24e0db Move more models to domain module 2023-01-22 10:37:13 -05:00
d3a73fc228 Move Category model and repository to domain and data layer (#8967)
To keep the commit from being 100+ files the interactors wasn't moved.

The domain module like the data module uses `tachiyomi` instead of `eu.kanade.tachiyomi` for package names
2023-01-22 10:12:29 -05:00
c2812fca24 Update sqldelight to v1.5.5 (#8966)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-22 00:07:49 -05:00
856847a60a Update dependency io.github.fornewid:material-motion-compose-core to v0.10.4 (#8964)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-22 00:07:43 -05:00
748e2480d3 Update dependency com.google.gms:google-services to v4.3.15 (#8963)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-22 00:07:37 -05:00
2ebc8d9ae5 Save current page state on configuration change
Fixes #8881

The actual issue is that the ViewModel migration actually differs between what the current `init` block
and previous `onSave` methods did; where the `init` block does not get triggered on saving the
instance on config changes.

Not entirely sure why onSaveInstanceState was explicitly avoided for config changes before, but we
just do it all the time now and end up updating the requestedPage with the current page.
2023-01-21 20:18:12 -05:00
e28b015580 MangaScreenModel: Make download function follow reader preference (#8920)
* Make download function more clearer in manga screen

Maybe resolves #8879

* Minor cleanup

* Minor cleanup 2
2023-01-21 16:47:22 -05:00
e4bc8990fb Replace RxJava in HttpPageLoader downloader (#8955)
* Convert downloader Observable to flow

Uses `runInterruptible` to turn the blocking call to `queue.take()`
into a cancellable call.

Flow collection is ended by cancelling the scope in `recycle`. This
means the `HttpPageLoader` can't be reused after calling `recycle`,
but this was true with the `Observable` as well.)

* Convert load Observables to suspending function

Inlining the Observables allows for some simplification of the error
handling. Behavior should be otherwise identical.

* Convert cleanup Completable to coroutine

Uses global `launchIO`, not ideal but similar to previous behavior.
Can't be scheduled on the local `scope` as this runs after `scope` is
cancelled.
2023-01-21 16:46:16 -05:00
a179327d9d Translations update from Hosted Weblate (#8855)
Weblate translations

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Co-authored-by: Alessandro Jean <alessandrojean@gmail.com>
Co-authored-by: Dan <denqwerta@gmail.com>
Co-authored-by: DarKCroX <DarKCroX@users.noreply.hosted.weblate.org>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: FTDaily <farrell05june2005@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: Kornelijus Tvarijanavičius <kornelijus@tvaria.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Madddog1997 <madddog1997@gmail.com>
Co-authored-by: Marvash Magalli <antorunese96@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: M͜͡edRAM <mohammad7ram@gmail.com>
Co-authored-by: Nepx <anandabaskara@outlook.com>
Co-authored-by: Osyx <ofalkman@gmail.com>
Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Ricardo <contatorms7@tutamail.com>
Co-authored-by: Rostyslav Haitkulov <info@ubilling.net.ua>
Co-authored-by: Shippo <Shipox@users.noreply.hosted.weblate.org>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: Tahsin Gökalp <tahsinsaan@gmail.com>
Co-authored-by: TheKingTermux <achmadmaulana0233@gmail.com>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: VespreSky <mp.draw.1@googlemail.com>
Co-authored-by: Vetle Ledaal <vetle.ledaal@gmail.com>
Co-authored-by: Yurical <yurical1@outlook.com>
Co-authored-by: adkxamov <adxoff@gmail.com>
Co-authored-by: ayaao <myrgdream@gmail.com>
Co-authored-by: jinu147 <nesqea20@gmail.com>
Co-authored-by: slundi <slundi@gmail.com>
Co-authored-by: ssantos <ssantos@web.de>
Co-authored-by: stevenlele <stevenlele@outlook.com>
Co-authored-by: torchlight <sima142222@gmail.com>
Co-authored-by: Олександр Котецький <saymon4145@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ar/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ca/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/cs/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/de/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/el/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/es/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/id/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/it/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ja/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/kk/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ko/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/lt/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ms/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pt/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ru/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/sc/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/sk/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/sr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/sv/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/tr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/uk/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/uz/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hant/
Translation: Tachiyomi/Tachiyomi 0.x

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Co-authored-by: Alessandro Jean <alessandrojean@gmail.com>
Co-authored-by: Dan <denqwerta@gmail.com>
Co-authored-by: DarKCroX <DarKCroX@users.noreply.hosted.weblate.org>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: FTDaily <farrell05june2005@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: Kornelijus Tvarijanavičius <kornelijus@tvaria.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Madddog1997 <madddog1997@gmail.com>
Co-authored-by: Marvash Magalli <antorunese96@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: M͜͡edRAM <mohammad7ram@gmail.com>
Co-authored-by: Nepx <anandabaskara@outlook.com>
Co-authored-by: Osyx <ofalkman@gmail.com>
Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Ricardo <contatorms7@tutamail.com>
Co-authored-by: Rostyslav Haitkulov <info@ubilling.net.ua>
Co-authored-by: Shippo <Shipox@users.noreply.hosted.weblate.org>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: Tahsin Gökalp <tahsinsaan@gmail.com>
Co-authored-by: TheKingTermux <achmadmaulana0233@gmail.com>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: VespreSky <mp.draw.1@googlemail.com>
Co-authored-by: Vetle Ledaal <vetle.ledaal@gmail.com>
Co-authored-by: Yurical <yurical1@outlook.com>
Co-authored-by: adkxamov <adxoff@gmail.com>
Co-authored-by: ayaao <myrgdream@gmail.com>
Co-authored-by: jinu147 <nesqea20@gmail.com>
Co-authored-by: slundi <slundi@gmail.com>
Co-authored-by: ssantos <ssantos@web.de>
Co-authored-by: stevenlele <stevenlele@outlook.com>
Co-authored-by: torchlight <sima142222@gmail.com>
Co-authored-by: Олександр Котецький <saymon4145@gmail.com>
2023-01-21 10:38:10 -05:00
823749fc1e Move SQLDelight to data module (#8954)
And use tachiyomi instead of eu.kanade.tachiyomi for package names in the module
2023-01-21 10:37:07 -05:00
2b5d9fd76b Move shared configuration to subprojects in root Gradle file (#8951)
* Move shared configuration to subprojects in root Gradle file

* Missed but not forgotten

* Review changes
2023-01-20 23:04:22 -05:00
7a972dfdb7 Don't use platform attributes for white/black reader backgrounds
Probably fixes #8946
2023-01-18 22:49:28 -05:00
c31e75f02f Create plugin for linting (#8942) 2023-01-18 22:33:56 -05:00
b56b8b55b4 Upgrade to Kotlin 1.8.0 2023-01-18 17:24:58 -05:00
2695a4d8c7 Update local source icon and differentiate from fallback source icon
Closes #8934
2023-01-16 22:54:45 -05:00
1a4dad72a9 Hide WebView menu item in reader if local
Closes #8932
2023-01-16 22:40:36 -05:00
b7e6b4c28a [MyAnimeList] Handle cases where my_list_status.status is not present (#8931) 2023-01-16 21:59:07 -05:00
dc2d470413 Revert "Update dependency androidx.compose.material:material to v1.4.0-alpha04 (#8918)"
This reverts commit c637172ee0.

Too lazy to fix the crashes related to missing classes at runtime for now.
2023-01-15 10:43:40 -05:00
293b967858 Fix installing extensions on MIUI (#8916)
* Fix installing extensions on MIUI

* isShizukuReady -> isShizukuInstalled
2023-01-15 10:32:27 -05:00
c637172ee0 Update dependency androidx.compose.material:material to v1.4.0-alpha04 (#8918)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-15 10:18:13 -05:00
e468554fd9 Assign keys for global search rows based on source
Maybe fixes #8924
2023-01-15 10:14:28 -05:00
5b5eb92184 Fix checking downloads banner showing up incorrectly 2023-01-14 20:04:36 -05:00
58ebf14691 Convert PageLoader.getPages to suspending function (#8917) 2023-01-14 19:45:15 -05:00
992bab4f79 Prevent scrolling outside bounds in webtoon/vertical reading mode (#8821) 2023-01-14 18:38:52 -05:00
6fe650319d Bump versionCode to prevent downgrades
Since the database schema was changed in f301dc64
2023-01-14 18:27:36 -05:00
f301dc64f0 Allow partially read chapters to be marked as unread in updates screen (#8884)
* Allow partially read chapters to be marked as unread in updates screen

* Review changes

* Review changes 2
2023-01-14 18:26:40 -05:00
33a2219716 Enable confirmButton only when needed to respond to user input (#8848)
* Enable `confirmButton` when appropriate

* Show error in dialog instead

* Follow M3 guidelines
2023-01-14 18:24:57 -05:00
62480f090b Replace RxJava in ChapterLoader and ReaderViewModel (#8915)
* Replace RxJava in ChapterLoader

* Don't swallow CancellationException

* Simplify loadChapter behavior

* Add error handling to loadAdjacent
2023-01-14 18:22:27 -05:00
e7937fe562 Make androidx.preference dialog match M3 dialog (#8909) 2023-01-14 17:00:19 -05:00
287489d7d0 Show chapter scanlator on reader transition (#8910)
Closes #7131
2023-01-14 17:00:04 -05:00
2df0236669 Show loading indicator during migration
Closes #8862
2023-01-13 23:01:52 -05:00
c54d77333f Suwayomi Tracker: sync changes with Tachidesk v0.6.6 (#8902)
* Suwayomi Tracker: sync changes with Tachidesk v0.6.6

* replace var with val
2023-01-13 22:31:04 -05:00
8c494f314c Fix DownloadPageLoader resource leak (#8905)
The underlying ZipFile is leaking. To fix, store a reference to the
ZipPageLoader and recycle it on recycle.
2023-01-13 22:30:47 -05:00
8cea78de83 Fix ChapterCache.isImageInCache() resource leak (#8907)
diskCache.get() returns a DiskLruCache.Snapshot which must be closed.
2023-01-13 22:30:26 -05:00
b6468c7e31 Only how indexing downloads banner the first time
Closes #8903
2023-01-13 18:40:59 -05:00
1967923a94 Disable Gradle configuration cache
This might be breaking the Actions runs...
2023-01-13 17:59:57 -05:00
91004ad514 Parallelize global search properly
Fixes #8906
2023-01-13 17:58:00 -05:00
a2ee4e63ae Minor cleanup 2023-01-12 22:53:28 -05:00
4d8289cd36 Bump to latest Compose stable BOM 2023-01-12 22:47:11 -05:00
289264878e Bump AGP
Also enable configuration cache that Build Analyzer suggested
2023-01-12 22:44:37 -05:00
768bb7b503 Fix downloaded filter unmatched state in manga screen (#8897) 2023-01-12 22:26:04 -05:00
db4ae134aa Tweak TriStateItem view to match in earlier app version (#8898)
* Tweak `TriStateItem` view to match in earlier app version

* Apply to disabled state too
2023-01-12 17:46:24 -05:00
7329f03bc5 Show proper Exception message in MangaScreen (#8900)
Show proper Exception message in MangaScreen.
2023-01-12 17:45:38 -05:00
82ea643c7d Don't prompt to add to library multiple times
Fixes #8842
2023-01-11 20:00:50 -05:00
741c10e0b9 Reword set category dialog confirmation to "OK"
Closes #8878
2023-01-11 19:31:40 -05:00
34bb90f3c2 Update library sheet filter tab on open
Fixes #8885
2023-01-11 19:14:37 -05:00
f04cf72c0c Bump core-ktx dependency 2023-01-11 19:01:13 -05:00
157438e0c1 Minor dependency updates 2023-01-11 18:51:26 -05:00
75b23c99ec Refactor how extensions list is modelled
To better enable changing the UI in the future based on sections.
2023-01-10 23:18:34 -05:00
6bb3070c57 Show no pinned sources message when attempting to migrate/search 2023-01-10 22:39:19 -05:00
7df10b076c Show the tracker name when showing error toast 2023-01-09 23:27:11 -05:00
2245658363 Replace RxJava in DownloadQueueScreenModel (#8872) 2023-01-09 23:08:04 -05:00
46774771ec Fix double tapping History not working consistently
Fixes #8875
2023-01-09 22:50:11 -05:00
6263817bb4 Avoid crash if multiple instances of ClearDatabaseScreen opened
Fixes #8851

I guess we might want to do this for all screens? Maybe?
2023-01-08 22:16:40 -05:00
60456fe0e9 Fix crash in categories screen on config change
Fixes #8861
2023-01-08 22:12:53 -05:00
a0f47d3f1b Don't exclude same source when checking for duplicate entries
Closes #8870
2023-01-08 22:06:42 -05:00
6efcb8ccfa Use Voyager for WebView in non-reader places 2023-01-08 16:37:43 -05:00
0d128b75e2 Make MIUI extensions warning clearer that it's only a suggestion 2023-01-08 16:05:26 -05:00
0067d474c8 Use theme padding values in more places 2023-01-08 15:41:06 -05:00
cf393b217b Add Reader Setting to Skip Dupe Chapters (#8831)
Add reader setting to filter dupe chapters with same scanlator priority.
2023-01-08 15:40:23 -05:00
e265b929a1 Avoid crashes when fetching assist content URL in ReaderActivity 2023-01-08 15:23:06 -05:00
4cd01428ed Only show MIUI extension warning on MIUI >= 13
Related to #8834
2023-01-08 15:04:06 -05:00
3be05fbf9b Make global search results more compact 2023-01-08 10:48:35 -05:00
5d90ba8aa0 Only show library continue reading button if there's unread chapters
Closes #8865
2023-01-08 10:43:01 -05:00
48cab708ce Show available but not installed enhanced trackers
Closes #8859
2023-01-08 10:37:30 -05:00
5d9753d6a7 Bump minimum ext-lib to 1.3 2023-01-08 10:32:35 -05:00
425e48bec6 Avoid crashes when opening WebView from reader
Also ensure WebViewActivity has an Assistant URL when it first opens with a URL.
2023-01-08 10:17:54 -05:00
a42be4a833 Update dependency com.squareup.okio:okio to v3.3.0 (#8860)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-08 10:17:00 -05:00
30e030bb8e Bump dependencies 2023-01-07 15:34:33 -05:00
2a3c3d8d6a Fix reader settings sheet's mode section not updated (#8857) 2023-01-07 15:13:08 -05:00
7b026cec8d Fix floating-point error in navigate pan (#8856) 2023-01-07 15:09:10 -05:00
d8b528a4e0 Remove repetition in enhanced tracker preference declarations
Also hides entries that aren't relevant (i.e. if you don't have the source installed).
2023-01-07 14:41:27 -05:00
0f45907144 Adjust bookmarked chapter styling in Updates
To match updated styling in manga screen.
2023-01-07 14:32:29 -05:00
c4c9931ae2 add Suwayomi tracker (#8489)
* add Suwayomi Tracker

* fix compile
2023-01-07 14:27:44 -05:00
68345e636e Remove ability to hide unread chapter badges in library 2023-01-07 14:25:35 -05:00
0861c5618c Fix reader settings sheet not updated (#8854)
* Revert "Recreate reader settings when opening sheet (#8054)"

This reverts commit acb8ab15b2.

* Revert "Fix stacking of Settings menu in the reader on multiple taps (#8002)"

This reverts commit 30ac94181b.

* Fix reader settings sheet not updated
2023-01-07 14:25:30 -05:00
543 changed files with 6020 additions and 4020 deletions

View File

@ -3,7 +3,7 @@
I acknowledge that: I acknowledge that:
- I have updated: - I have updated:
- To the latest version of the app (stable is v0.14.3) - To the latest version of the app (stable is v0.14.6)
- All extensions - All extensions
- I have tried the troubleshooting guide: https://tachiyomi.org/help/guides/troubleshooting-problems/ - I have tried the troubleshooting guide: https://tachiyomi.org/help/guides/troubleshooting-problems/
- If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions - If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions

View File

@ -53,7 +53,7 @@ body:
label: Tachiyomi version label: Tachiyomi version
description: You can find your Tachiyomi version in **More → About**. description: You can find your Tachiyomi version in **More → About**.
placeholder: | placeholder: |
Example: "0.14.3" Example: "0.14.6"
validations: validations:
required: true required: true
@ -98,7 +98,7 @@ body:
required: true required: true
- label: I have tried the [troubleshooting guide](https://tachiyomi.org/help/guides/troubleshooting/). - label: I have tried the [troubleshooting guide](https://tachiyomi.org/help/guides/troubleshooting/).
required: true required: true
- label: I have updated the app to version **[0.14.3](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**. - label: I have updated the app to version **[0.14.6](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
required: true required: true
- label: I have updated all installed extensions. - label: I have updated all installed extensions.
required: true required: true

View File

@ -33,7 +33,7 @@ body:
required: true required: true
- label: If this is an issue with an extension, I should be opening an issue in the [extensions repository](https://github.com/tachiyomiorg/tachiyomi-extensions/issues/new/choose). - label: If this is an issue with an extension, I should be opening an issue in the [extensions repository](https://github.com/tachiyomiorg/tachiyomi-extensions/issues/new/choose).
required: true required: true
- label: I have updated the app to version **[0.14.3](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**. - label: I have updated the app to version **[0.14.6](https://github.com/tachiyomiorg/tachiyomi/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

View File

@ -36,4 +36,4 @@ jobs:
- name: Build app and run unit tests - name: Build app and run unit tests
uses: gradle/gradle-command-action@v2 uses: gradle/gradle-command-action@v2
with: with:
arguments: assembleStandardRelease testStandardReleaseUnitTest arguments: lintKotlin assembleStandardRelease testStandardReleaseUnitTest

View File

@ -31,7 +31,7 @@ jobs:
- name: Build app and run unit tests - name: Build app and run unit tests
uses: gradle/gradle-command-action@v2 uses: gradle/gradle-command-action@v2
with: with:
arguments: assembleStandardRelease testStandardReleaseUnitTest arguments: lintKotlin assembleStandardRelease testStandardReleaseUnitTest
# Sign APK and create release for tags # Sign APK and create release for tags

View File

@ -1,4 +1,3 @@
import org.gradle.api.tasks.testing.logging.TestLogEvent
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jmailen.gradle.kotlinter.tasks.LintTask import org.jmailen.gradle.kotlinter.tasks.LintTask
@ -8,7 +7,6 @@ plugins {
kotlin("android") kotlin("android")
kotlin("plugin.serialization") kotlin("plugin.serialization")
id("com.github.zellius.shortcut-helper") id("com.github.zellius.shortcut-helper")
id("com.squareup.sqldelight")
} }
if (gradle.startParameter.taskRequests.toString().contains("Standard")) { if (gradle.startParameter.taskRequests.toString().contains("Standard")) {
@ -21,15 +19,11 @@ val SUPPORTED_ABIS = setOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
android { android {
namespace = "eu.kanade.tachiyomi" namespace = "eu.kanade.tachiyomi"
compileSdk = AndroidConfig.compileSdk
ndkVersion = AndroidConfig.ndk
defaultConfig { defaultConfig {
applicationId = "eu.kanade.tachiyomi" applicationId = "eu.kanade.tachiyomi"
minSdk = AndroidConfig.minSdk versionCode = 101
targetSdk = AndroidConfig.targetSdk versionName = "0.14.6"
versionCode = 93
versionName = "0.14.3"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"") buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
@ -141,32 +135,16 @@ android {
composeOptions { composeOptions {
kotlinCompilerExtensionVersion = compose.versions.compiler.get() kotlinCompilerExtensionVersion = compose.versions.compiler.get()
} }
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
isCoreLibraryDesugaringEnabled = true
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
sqldelight {
database("Database") {
packageName = "eu.kanade.tachiyomi"
dialect = "sqlite:3.24"
}
}
} }
dependencies { dependencies {
implementation(project(":i18n")) implementation(project(":i18n"))
implementation(project(":core")) implementation(project(":core"))
implementation(project(":source-api")) implementation(project(":source-api"))
implementation(project(":data"))
coreLibraryDesugaring(libs.desugar) implementation(project(":domain"))
implementation(project(":presentation-core"))
implementation(project(":presentation-widget"))
// Compose // Compose
implementation(platform(compose.bom)) implementation(platform(compose.bom))
@ -189,9 +167,6 @@ dependencies {
implementation(androidx.paging.compose) implementation(androidx.paging.compose)
implementation(libs.bundles.sqlite) implementation(libs.bundles.sqlite)
implementation(libs.sqldelight.android.driver)
implementation(libs.sqldelight.coroutines)
implementation(libs.sqldelight.android.paging)
implementation(kotlinx.reflect) implementation(kotlinx.reflect)
@ -208,7 +183,6 @@ dependencies {
implementation(androidx.splashscreen) implementation(androidx.splashscreen)
implementation(androidx.recyclerview) implementation(androidx.recyclerview)
implementation(androidx.viewpager) implementation(androidx.viewpager)
implementation(androidx.glance)
implementation(androidx.profileinstaller) implementation(androidx.profileinstaller)
implementation(androidx.bundles.lifecycle) implementation(androidx.bundles.lifecycle)
@ -303,12 +277,6 @@ androidComponents {
} }
tasks { tasks {
withType<Test> {
useJUnitPlatform()
testLogging {
events(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED)
}
}
withType<LintTask>().configureEach { withType<LintTask>().configureEach {
exclude { it.file.path.contains("generated[\\\\/]".toRegex()) } exclude { it.file.path.contains("generated[\\\\/]".toRegex()) }
@ -346,11 +314,6 @@ tasks {
) )
} }
} }
preBuild {
val ktlintTask = if (System.getenv("GITHUB_BASE_REF") == null) formatKotlin else lintKotlin
dependsOn(ktlintTask)
}
} }
buildscript { buildscript {

View File

@ -188,7 +188,7 @@
android:exported="false" /> android:exported="false" />
<receiver <receiver
android:name=".glance.UpdatesGridGlanceReceiver" android:name="tachiyomi.presentation.widget.UpdatesGridGlanceReceiver"
android:enabled="@bool/glance_appwidget_available" android:enabled="@bool/glance_appwidget_available"
android:exported="false" android:exported="false"
android:label="@string/label_recent_updates"> android:label="@string/label_recent_updates">

View File

@ -2,10 +2,10 @@ package eu.kanade.core.prefs
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import eu.kanade.tachiyomi.core.preference.Preference
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import tachiyomi.core.preference.Preference
class PreferenceMutableState<T>( class PreferenceMutableState<T>(
private val preference: Preference<T>, private val preference: Preference<T>,

View File

@ -1,3 +0,0 @@
package eu.kanade.data.source
class NoResultsException : Exception()

View File

@ -1,9 +1,8 @@
package eu.kanade.data.source package eu.kanade.data.source
import eu.kanade.domain.source.model.Source
import eu.kanade.domain.source.model.SourceData
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import tachiyomi.domain.source.model.Source
val sourceMapper: (eu.kanade.tachiyomi.source.Source) -> Source = { source -> val sourceMapper: (eu.kanade.tachiyomi.source.Source) -> Source = { source ->
Source( Source(
@ -18,7 +17,3 @@ val sourceMapper: (eu.kanade.tachiyomi.source.Source) -> Source = { source ->
val catalogueSourceMapper: (CatalogueSource) -> Source = { source -> val catalogueSourceMapper: (CatalogueSource) -> Source = { source ->
sourceMapper(source).copy(supportsLatest = source.supportsLatest) sourceMapper(source).copy(supportsLatest = source.supportsLatest)
} }
val sourceDataMapper: (Long, String, String) -> SourceData = { id, lang, name ->
SourceData(id, lang, name)
}

View File

@ -6,8 +6,8 @@ import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.lang.awaitSingle import tachiyomi.core.util.lang.awaitSingle
import eu.kanade.tachiyomi.util.lang.withIOContext import tachiyomi.core.util.lang.withIOContext
abstract class SourcePagingSource( abstract class SourcePagingSource(
protected val source: CatalogueSource, protected val source: CatalogueSource,
@ -60,3 +60,5 @@ class SourceLatestPagingSource(source: CatalogueSource) : SourcePagingSource(sou
return source.fetchLatestUpdates(currentPage).awaitSingle() return source.fetchLatestUpdates(currentPage).awaitSingle()
} }
} }
class NoResultsException : Exception()

View File

@ -1,9 +1,6 @@
package eu.kanade.data.source package eu.kanade.data.source
import eu.kanade.data.DatabaseHandler
import eu.kanade.domain.source.model.Source
import eu.kanade.domain.source.model.SourcePagingSourceType import eu.kanade.domain.source.model.SourcePagingSourceType
import eu.kanade.domain.source.model.SourceWithCount
import eu.kanade.domain.source.repository.SourceRepository import eu.kanade.domain.source.repository.SourceRepository
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
@ -11,6 +8,9 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import tachiyomi.data.DatabaseHandler
import tachiyomi.domain.source.model.Source
import tachiyomi.domain.source.model.SourceWithCount
class SourceRepositoryImpl( class SourceRepositoryImpl(
private val sourceManager: SourceManager, private val sourceManager: SourceManager,

View File

@ -1,16 +1,8 @@
package eu.kanade.domain package eu.kanade.domain
import eu.kanade.data.category.CategoryRepositoryImpl
import eu.kanade.data.chapter.ChapterRepositoryImpl
import eu.kanade.data.history.HistoryRepositoryImpl
import eu.kanade.data.manga.MangaRepositoryImpl
import eu.kanade.data.source.SourceDataRepositoryImpl
import eu.kanade.data.source.SourceRepositoryImpl import eu.kanade.data.source.SourceRepositoryImpl
import eu.kanade.data.track.TrackRepositoryImpl
import eu.kanade.data.updates.UpdatesRepositoryImpl
import eu.kanade.domain.category.interactor.CreateCategoryWithName import eu.kanade.domain.category.interactor.CreateCategoryWithName
import eu.kanade.domain.category.interactor.DeleteCategory import eu.kanade.domain.category.interactor.DeleteCategory
import eu.kanade.domain.category.interactor.GetCategories
import eu.kanade.domain.category.interactor.RenameCategory import eu.kanade.domain.category.interactor.RenameCategory
import eu.kanade.domain.category.interactor.ReorderCategory import eu.kanade.domain.category.interactor.ReorderCategory
import eu.kanade.domain.category.interactor.ResetCategoryFlags import eu.kanade.domain.category.interactor.ResetCategoryFlags
@ -18,26 +10,18 @@ import eu.kanade.domain.category.interactor.SetDisplayModeForCategory
import eu.kanade.domain.category.interactor.SetMangaCategories import eu.kanade.domain.category.interactor.SetMangaCategories
import eu.kanade.domain.category.interactor.SetSortModeForCategory import eu.kanade.domain.category.interactor.SetSortModeForCategory
import eu.kanade.domain.category.interactor.UpdateCategory import eu.kanade.domain.category.interactor.UpdateCategory
import eu.kanade.domain.category.repository.CategoryRepository
import eu.kanade.domain.chapter.interactor.GetChapter import eu.kanade.domain.chapter.interactor.GetChapter
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
import eu.kanade.domain.chapter.interactor.SetMangaDefaultChapterFlags import eu.kanade.domain.chapter.interactor.SetMangaDefaultChapterFlags
import eu.kanade.domain.chapter.interactor.SetReadStatus import eu.kanade.domain.chapter.interactor.SetReadStatus
import eu.kanade.domain.chapter.interactor.ShouldUpdateDbChapter
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
import eu.kanade.domain.chapter.interactor.UpdateChapter import eu.kanade.domain.chapter.interactor.UpdateChapter
import eu.kanade.domain.chapter.repository.ChapterRepository
import eu.kanade.domain.download.interactor.DeleteDownload import eu.kanade.domain.download.interactor.DeleteDownload
import eu.kanade.domain.extension.interactor.GetExtensionLanguages import eu.kanade.domain.extension.interactor.GetExtensionLanguages
import eu.kanade.domain.extension.interactor.GetExtensionSources import eu.kanade.domain.extension.interactor.GetExtensionSources
import eu.kanade.domain.extension.interactor.GetExtensionsByType import eu.kanade.domain.extension.interactor.GetExtensionsByType
import eu.kanade.domain.history.interactor.GetHistory
import eu.kanade.domain.history.interactor.GetNextChapters import eu.kanade.domain.history.interactor.GetNextChapters
import eu.kanade.domain.history.interactor.GetTotalReadDuration
import eu.kanade.domain.history.interactor.RemoveHistory
import eu.kanade.domain.history.interactor.UpsertHistory
import eu.kanade.domain.history.repository.HistoryRepository
import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga
import eu.kanade.domain.manga.interactor.GetFavorites import eu.kanade.domain.manga.interactor.GetFavorites
import eu.kanade.domain.manga.interactor.GetLibraryManga import eu.kanade.domain.manga.interactor.GetLibraryManga
@ -48,7 +32,6 @@ import eu.kanade.domain.manga.interactor.ResetViewerFlags
import eu.kanade.domain.manga.interactor.SetMangaChapterFlags import eu.kanade.domain.manga.interactor.SetMangaChapterFlags
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.repository.MangaRepository
import eu.kanade.domain.source.interactor.GetEnabledSources import eu.kanade.domain.source.interactor.GetEnabledSources
import eu.kanade.domain.source.interactor.GetLanguagesWithSources import eu.kanade.domain.source.interactor.GetLanguagesWithSources
import eu.kanade.domain.source.interactor.GetRemoteManga import eu.kanade.domain.source.interactor.GetRemoteManga
@ -58,15 +41,32 @@ import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.domain.source.interactor.ToggleLanguage import eu.kanade.domain.source.interactor.ToggleLanguage
import eu.kanade.domain.source.interactor.ToggleSource import eu.kanade.domain.source.interactor.ToggleSource
import eu.kanade.domain.source.interactor.ToggleSourcePin import eu.kanade.domain.source.interactor.ToggleSourcePin
import eu.kanade.domain.source.repository.SourceDataRepository
import eu.kanade.domain.source.repository.SourceRepository import eu.kanade.domain.source.repository.SourceRepository
import eu.kanade.domain.track.interactor.DeleteTrack import eu.kanade.domain.track.interactor.DeleteTrack
import eu.kanade.domain.track.interactor.GetTracks import eu.kanade.domain.track.interactor.GetTracks
import eu.kanade.domain.track.interactor.GetTracksPerManga import eu.kanade.domain.track.interactor.GetTracksPerManga
import eu.kanade.domain.track.interactor.InsertTrack import eu.kanade.domain.track.interactor.InsertTrack
import eu.kanade.domain.track.repository.TrackRepository import tachiyomi.data.category.CategoryRepositoryImpl
import eu.kanade.domain.updates.interactor.GetUpdates import tachiyomi.data.chapter.ChapterRepositoryImpl
import eu.kanade.domain.updates.repository.UpdatesRepository import tachiyomi.data.history.HistoryRepositoryImpl
import tachiyomi.data.manga.MangaRepositoryImpl
import tachiyomi.data.source.SourceDataRepositoryImpl
import tachiyomi.data.track.TrackRepositoryImpl
import tachiyomi.data.updates.UpdatesRepositoryImpl
import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.category.repository.CategoryRepository
import tachiyomi.domain.chapter.interactor.ShouldUpdateDbChapter
import tachiyomi.domain.chapter.repository.ChapterRepository
import tachiyomi.domain.history.interactor.GetHistory
import tachiyomi.domain.history.interactor.GetTotalReadDuration
import tachiyomi.domain.history.interactor.RemoveHistory
import tachiyomi.domain.history.interactor.UpsertHistory
import tachiyomi.domain.history.repository.HistoryRepository
import tachiyomi.domain.manga.repository.MangaRepository
import tachiyomi.domain.source.repository.SourceDataRepository
import tachiyomi.domain.track.repository.TrackRepository
import tachiyomi.domain.updates.interactor.GetUpdates
import tachiyomi.domain.updates.repository.UpdatesRepository
import uy.kohesive.injekt.api.InjektModule import uy.kohesive.injekt.api.InjektModule
import uy.kohesive.injekt.api.InjektRegistrar import uy.kohesive.injekt.api.InjektRegistrar
import uy.kohesive.injekt.api.addFactory import uy.kohesive.injekt.api.addFactory

View File

@ -1,7 +1,7 @@
package eu.kanade.domain.backup.service package eu.kanade.domain.backup.service
import eu.kanade.tachiyomi.core.preference.PreferenceStore import tachiyomi.core.preference.PreferenceStore
import eu.kanade.tachiyomi.core.provider.FolderProvider import tachiyomi.core.provider.FolderProvider
class BackupPreferences( class BackupPreferences(
private val folderProvider: FolderProvider, private val folderProvider: FolderProvider,

View File

@ -1,12 +1,9 @@
package eu.kanade.domain.base package eu.kanade.domain.base
import android.content.Context import android.content.Context
import eu.kanade.tachiyomi.core.preference.PreferenceStore
import eu.kanade.tachiyomi.core.preference.getEnum
import eu.kanade.tachiyomi.data.preference.PreferenceValues
import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.isPreviewBuildType import eu.kanade.tachiyomi.util.system.isPreviewBuildType
import eu.kanade.tachiyomi.util.system.isReleaseBuildType import eu.kanade.tachiyomi.util.system.isReleaseBuildType
import tachiyomi.core.preference.PreferenceStore
class BasePreferences( class BasePreferences(
val context: Context, val context: Context,
@ -21,10 +18,7 @@ class BasePreferences(
fun automaticExtUpdates() = preferenceStore.getBoolean("automatic_ext_updates", true) fun automaticExtUpdates() = preferenceStore.getBoolean("automatic_ext_updates", true)
fun extensionInstaller() = preferenceStore.getEnum( fun extensionInstaller() = ExtensionInstallerPreference(context, preferenceStore)
"extension_installer",
if (DeviceUtil.isMiui) PreferenceValues.ExtensionInstaller.LEGACY else PreferenceValues.ExtensionInstaller.PACKAGEINSTALLER,
)
fun acraEnabled() = preferenceStore.getBoolean("acra.enable", isPreviewBuildType || isReleaseBuildType) fun acraEnabled() = preferenceStore.getBoolean("acra.enable", isPreviewBuildType || isReleaseBuildType)
} }

View File

@ -0,0 +1,68 @@
package eu.kanade.domain.base
import android.content.Context
import eu.kanade.tachiyomi.data.preference.PreferenceValues.ExtensionInstaller
import eu.kanade.tachiyomi.util.system.hasMiuiPackageInstaller
import eu.kanade.tachiyomi.util.system.isShizukuInstalled
import kotlinx.coroutines.CoroutineScope
import tachiyomi.core.preference.Preference
import tachiyomi.core.preference.PreferenceStore
import tachiyomi.core.preference.getEnum
class ExtensionInstallerPreference(
private val context: Context,
preferenceStore: PreferenceStore,
) : Preference<ExtensionInstaller> {
private val basePref = preferenceStore.getEnum(key(), defaultValue())
override fun key() = "extension_installer"
val entries get() = ExtensionInstaller.values().run {
if (context.hasMiuiPackageInstaller) {
filter { it != ExtensionInstaller.PACKAGEINSTALLER }
} else {
toList()
}
}
override fun defaultValue() = if (context.hasMiuiPackageInstaller) {
ExtensionInstaller.LEGACY
} else {
ExtensionInstaller.PACKAGEINSTALLER
}
private fun check(value: ExtensionInstaller): ExtensionInstaller {
when (value) {
ExtensionInstaller.PACKAGEINSTALLER -> {
if (context.hasMiuiPackageInstaller) return ExtensionInstaller.LEGACY
}
ExtensionInstaller.SHIZUKU -> {
if (!context.isShizukuInstalled) return defaultValue()
}
else -> {}
}
return value
}
override fun get(): ExtensionInstaller {
val value = basePref.get()
val checkedValue = check(value)
if (value != checkedValue) {
basePref.set(checkedValue)
}
return checkedValue
}
override fun set(value: ExtensionInstaller) {
basePref.set(check(value))
}
override fun isSet() = basePref.isSet()
override fun delete() = basePref.delete()
override fun changes() = basePref.changes()
override fun stateIn(scope: CoroutineScope) = basePref.stateIn(scope)
}

View File

@ -1,12 +1,11 @@
package eu.kanade.domain.category.interactor package eu.kanade.domain.category.interactor
import eu.kanade.domain.category.model.Category
import eu.kanade.domain.category.model.anyWithName
import eu.kanade.domain.category.repository.CategoryRepository
import eu.kanade.domain.library.service.LibraryPreferences import eu.kanade.domain.library.service.LibraryPreferences
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
import eu.kanade.tachiyomi.util.system.logcat
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.lang.withNonCancellableContext
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.category.model.Category
import tachiyomi.domain.category.repository.CategoryRepository
class CreateCategoryWithName( class CreateCategoryWithName(
private val categoryRepository: CategoryRepository, private val categoryRepository: CategoryRepository,
@ -23,10 +22,6 @@ class CreateCategoryWithName(
suspend fun await(name: String): Result = withNonCancellableContext { suspend fun await(name: String): Result = withNonCancellableContext {
val categories = categoryRepository.getAll() val categories = categoryRepository.getAll()
if (categories.anyWithName(name)) {
return@withNonCancellableContext Result.NameAlreadyExistsError
}
val nextOrder = categories.maxOfOrNull { it.order }?.plus(1) ?: 0 val nextOrder = categories.maxOfOrNull { it.order }?.plus(1) ?: 0
val newCategory = Category( val newCategory = Category(
id = 0, id = 0,
@ -46,7 +41,6 @@ class CreateCategoryWithName(
sealed class Result { sealed class Result {
object Success : Result() object Success : Result()
object NameAlreadyExistsError : Result()
data class InternalError(val error: Throwable) : Result() data class InternalError(val error: Throwable) : Result()
} }
} }

View File

@ -1,10 +1,10 @@
package eu.kanade.domain.category.interactor package eu.kanade.domain.category.interactor
import eu.kanade.domain.category.model.CategoryUpdate
import eu.kanade.domain.category.repository.CategoryRepository
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
import eu.kanade.tachiyomi.util.system.logcat
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.lang.withNonCancellableContext
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.category.model.CategoryUpdate
import tachiyomi.domain.category.repository.CategoryRepository
class DeleteCategory( class DeleteCategory(
private val categoryRepository: CategoryRepository, private val categoryRepository: CategoryRepository,

View File

@ -1,23 +1,17 @@
package eu.kanade.domain.category.interactor package eu.kanade.domain.category.interactor
import eu.kanade.domain.category.model.Category
import eu.kanade.domain.category.model.CategoryUpdate
import eu.kanade.domain.category.model.anyWithName
import eu.kanade.domain.category.repository.CategoryRepository
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
import eu.kanade.tachiyomi.util.system.logcat
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.lang.withNonCancellableContext
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.category.model.Category
import tachiyomi.domain.category.model.CategoryUpdate
import tachiyomi.domain.category.repository.CategoryRepository
class RenameCategory( class RenameCategory(
private val categoryRepository: CategoryRepository, private val categoryRepository: CategoryRepository,
) { ) {
suspend fun await(categoryId: Long, name: String) = withNonCancellableContext { suspend fun await(categoryId: Long, name: String) = withNonCancellableContext {
val categories = categoryRepository.getAll()
if (categories.anyWithName(name)) {
return@withNonCancellableContext Result.NameAlreadyExistsError
}
val update = CategoryUpdate( val update = CategoryUpdate(
id = categoryId, id = categoryId,
name = name, name = name,
@ -36,7 +30,6 @@ class RenameCategory(
sealed class Result { sealed class Result {
object Success : Result() object Success : Result()
object NameAlreadyExistsError : Result()
data class InternalError(val error: Throwable) : Result() data class InternalError(val error: Throwable) : Result()
} }
} }

View File

@ -1,13 +1,13 @@
package eu.kanade.domain.category.interactor package eu.kanade.domain.category.interactor
import eu.kanade.domain.category.model.Category
import eu.kanade.domain.category.model.CategoryUpdate
import eu.kanade.domain.category.repository.CategoryRepository
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
import eu.kanade.tachiyomi.util.system.logcat
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.lang.withNonCancellableContext
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.category.model.Category
import tachiyomi.domain.category.model.CategoryUpdate
import tachiyomi.domain.category.repository.CategoryRepository
import java.util.Collections import java.util.Collections
class ReorderCategory( class ReorderCategory(

View File

@ -1,8 +1,8 @@
package eu.kanade.domain.category.interactor package eu.kanade.domain.category.interactor
import eu.kanade.domain.category.repository.CategoryRepository
import eu.kanade.domain.library.model.plus
import eu.kanade.domain.library.service.LibraryPreferences import eu.kanade.domain.library.service.LibraryPreferences
import tachiyomi.domain.category.repository.CategoryRepository
import tachiyomi.domain.library.model.plus
class ResetCategoryFlags( class ResetCategoryFlags(
private val preferences: LibraryPreferences, private val preferences: LibraryPreferences,

View File

@ -1,11 +1,11 @@
package eu.kanade.domain.category.interactor package eu.kanade.domain.category.interactor
import eu.kanade.domain.category.model.Category
import eu.kanade.domain.category.model.CategoryUpdate
import eu.kanade.domain.category.repository.CategoryRepository
import eu.kanade.domain.library.model.LibraryDisplayMode
import eu.kanade.domain.library.model.plus
import eu.kanade.domain.library.service.LibraryPreferences import eu.kanade.domain.library.service.LibraryPreferences
import tachiyomi.domain.category.model.Category
import tachiyomi.domain.category.model.CategoryUpdate
import tachiyomi.domain.category.repository.CategoryRepository
import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.model.plus
class SetDisplayModeForCategory( class SetDisplayModeForCategory(
private val preferences: LibraryPreferences, private val preferences: LibraryPreferences,

View File

@ -1,8 +1,8 @@
package eu.kanade.domain.category.interactor package eu.kanade.domain.category.interactor
import eu.kanade.domain.manga.repository.MangaRepository
import eu.kanade.tachiyomi.util.system.logcat
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.manga.repository.MangaRepository
class SetMangaCategories( class SetMangaCategories(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,

View File

@ -1,11 +1,11 @@
package eu.kanade.domain.category.interactor package eu.kanade.domain.category.interactor
import eu.kanade.domain.category.model.Category
import eu.kanade.domain.category.model.CategoryUpdate
import eu.kanade.domain.category.repository.CategoryRepository
import eu.kanade.domain.library.model.LibrarySort
import eu.kanade.domain.library.model.plus
import eu.kanade.domain.library.service.LibraryPreferences import eu.kanade.domain.library.service.LibraryPreferences
import tachiyomi.domain.category.model.Category
import tachiyomi.domain.category.model.CategoryUpdate
import tachiyomi.domain.category.repository.CategoryRepository
import tachiyomi.domain.library.model.LibrarySort
import tachiyomi.domain.library.model.plus
class SetSortModeForCategory( class SetSortModeForCategory(
private val preferences: LibraryPreferences, private val preferences: LibraryPreferences,

View File

@ -1,8 +1,8 @@
package eu.kanade.domain.category.interactor package eu.kanade.domain.category.interactor
import eu.kanade.domain.category.model.CategoryUpdate import tachiyomi.core.util.lang.withNonCancellableContext
import eu.kanade.domain.category.repository.CategoryRepository import tachiyomi.domain.category.model.CategoryUpdate
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext import tachiyomi.domain.category.repository.CategoryRepository
class UpdateCategory( class UpdateCategory(
private val categoryRepository: CategoryRepository, private val categoryRepository: CategoryRepository,

View File

@ -1,9 +1,9 @@
package eu.kanade.domain.chapter.interactor package eu.kanade.domain.chapter.interactor
import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.chapter.repository.ChapterRepository
import eu.kanade.tachiyomi.util.system.logcat
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.chapter.repository.ChapterRepository
class GetChapter( class GetChapter(
private val chapterRepository: ChapterRepository, private val chapterRepository: ChapterRepository,

View File

@ -1,9 +1,9 @@
package eu.kanade.domain.chapter.interactor package eu.kanade.domain.chapter.interactor
import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.chapter.repository.ChapterRepository
import eu.kanade.tachiyomi.util.system.logcat
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.chapter.repository.ChapterRepository
class GetChapterByMangaId( class GetChapterByMangaId(
private val chapterRepository: ChapterRepository, private val chapterRepository: ChapterRepository,

View File

@ -3,8 +3,8 @@ package eu.kanade.domain.chapter.interactor
import eu.kanade.domain.library.service.LibraryPreferences import eu.kanade.domain.library.service.LibraryPreferences
import eu.kanade.domain.manga.interactor.GetFavorites import eu.kanade.domain.manga.interactor.GetFavorites
import eu.kanade.domain.manga.interactor.SetMangaChapterFlags import eu.kanade.domain.manga.interactor.SetMangaChapterFlags
import eu.kanade.domain.manga.model.Manga import tachiyomi.core.util.lang.withNonCancellableContext
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext import tachiyomi.domain.manga.model.Manga
class SetMangaDefaultChapterFlags( class SetMangaDefaultChapterFlags(
private val libraryPreferences: LibraryPreferences, private val libraryPreferences: LibraryPreferences,

View File

@ -1,15 +1,15 @@
package eu.kanade.domain.chapter.interactor package eu.kanade.domain.chapter.interactor
import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.chapter.model.ChapterUpdate
import eu.kanade.domain.chapter.repository.ChapterRepository
import eu.kanade.domain.download.interactor.DeleteDownload import eu.kanade.domain.download.interactor.DeleteDownload
import eu.kanade.domain.download.service.DownloadPreferences import eu.kanade.domain.download.service.DownloadPreferences
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.repository.MangaRepository
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
import eu.kanade.tachiyomi.util.system.logcat
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.lang.withNonCancellableContext
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.chapter.model.ChapterUpdate
import tachiyomi.domain.chapter.repository.ChapterRepository
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.repository.MangaRepository
class SetReadStatus( class SetReadStatus(
private val downloadPreferences: DownloadPreferences, private val downloadPreferences: DownloadPreferences,

View File

@ -1,12 +1,9 @@
package eu.kanade.domain.chapter.interactor package eu.kanade.domain.chapter.interactor
import eu.kanade.data.chapter.CleanupChapterName import eu.kanade.domain.chapter.model.copyFromSChapter
import eu.kanade.data.chapter.NoChaptersException import eu.kanade.domain.chapter.model.toSChapter
import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.chapter.model.toChapterUpdate
import eu.kanade.domain.chapter.repository.ChapterRepository
import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.toSManga
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.DownloadProvider import eu.kanade.tachiyomi.data.download.DownloadProvider
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
@ -14,6 +11,13 @@ import eu.kanade.tachiyomi.source.isLocal
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.chapter.ChapterRecognition import eu.kanade.tachiyomi.util.chapter.ChapterRecognition
import tachiyomi.data.chapter.ChapterSanitizer
import tachiyomi.domain.chapter.interactor.ShouldUpdateDbChapter
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.chapter.model.NoChaptersException
import tachiyomi.domain.chapter.model.toChapterUpdate
import tachiyomi.domain.chapter.repository.ChapterRepository
import tachiyomi.domain.manga.model.Manga
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.lang.Long.max import java.lang.Long.max
@ -52,7 +56,7 @@ class SyncChaptersWithSource(
.mapIndexed { i, sChapter -> .mapIndexed { i, sChapter ->
Chapter.create() Chapter.create()
.copyFromSChapter(sChapter) .copyFromSChapter(sChapter)
.copy(name = CleanupChapterName.await(sChapter.name, manga.title)) .copy(name = with(ChapterSanitizer) { sChapter.name.sanitize(manga.title) })
.copy(mangaId = manga.id, sourceOrder = i.toLong()) .copy(mangaId = manga.id, sourceOrder = i.toLong())
} }

View File

@ -1,13 +1,13 @@
package eu.kanade.domain.chapter.interactor package eu.kanade.domain.chapter.interactor
import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.chapter.model.toChapterUpdate
import eu.kanade.domain.track.interactor.InsertTrack import eu.kanade.domain.track.interactor.InsertTrack
import eu.kanade.domain.track.model.Track
import eu.kanade.domain.track.model.toDbTrack import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.util.system.logcat
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.chapter.model.toChapterUpdate
import tachiyomi.domain.track.model.Track
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get

View File

@ -1,9 +1,9 @@
package eu.kanade.domain.chapter.interactor package eu.kanade.domain.chapter.interactor
import eu.kanade.domain.chapter.model.ChapterUpdate
import eu.kanade.domain.chapter.repository.ChapterRepository
import eu.kanade.tachiyomi.util.system.logcat
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.chapter.model.ChapterUpdate
import tachiyomi.domain.chapter.repository.ChapterRepository
class UpdateChapter( class UpdateChapter(
private val chapterRepository: ChapterRepository, private val chapterRepository: ChapterRepository,

View File

@ -2,26 +2,11 @@ package eu.kanade.domain.chapter.model
import eu.kanade.tachiyomi.data.database.models.ChapterImpl import eu.kanade.tachiyomi.data.database.models.ChapterImpl
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import tachiyomi.domain.chapter.model.Chapter
import eu.kanade.tachiyomi.data.database.models.Chapter as DbChapter import eu.kanade.tachiyomi.data.database.models.Chapter as DbChapter
data class Chapter( // TODO: Remove when all deps are migrated
val id: Long, fun Chapter.toSChapter(): SChapter {
val mangaId: Long,
val read: Boolean,
val bookmark: Boolean,
val lastPageRead: Long,
val dateFetch: Long,
val sourceOrder: Long,
val url: String,
val name: String,
val dateUpload: Long,
val chapterNumber: Float,
val scanlator: String?,
) {
val isRecognizedNumber: Boolean
get() = chapterNumber >= 0f
fun toSChapter(): SChapter {
return SChapter.create().also { return SChapter.create().also {
it.url = url it.url = url
it.name = name it.name = name
@ -29,9 +14,9 @@ data class Chapter(
it.chapter_number = chapterNumber it.chapter_number = chapterNumber
it.scanlator = scanlator it.scanlator = scanlator
} }
} }
fun copyFromSChapter(sChapter: SChapter): Chapter { fun Chapter.copyFromSChapter(sChapter: SChapter): Chapter {
return this.copy( return this.copy(
name = sChapter.name, name = sChapter.name,
url = sChapter.url, url = sChapter.url,
@ -39,27 +24,8 @@ data class Chapter(
chapterNumber = sChapter.chapter_number, chapterNumber = sChapter.chapter_number,
scanlator = sChapter.scanlator?.ifBlank { null }, scanlator = sChapter.scanlator?.ifBlank { null },
) )
}
companion object {
fun create() = Chapter(
id = -1,
mangaId = -1,
read = false,
bookmark = false,
lastPageRead = 0,
dateFetch = 0,
sourceOrder = 0,
url = "",
name = "",
dateUpload = -1,
chapterNumber = -1f,
scanlator = null,
)
}
} }
// TODO: Remove when all deps are migrated
fun Chapter.toDbChapter(): DbChapter = ChapterImpl().also { fun Chapter.toDbChapter(): DbChapter = ChapterImpl().also {
it.id = id it.id = id
it.manga_id = mangaId it.manga_id = mangaId

View File

@ -1,12 +1,14 @@
package eu.kanade.domain.chapter.model package eu.kanade.domain.chapter.model
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.downloadedFilter
import eu.kanade.domain.manga.model.TriStateFilter
import eu.kanade.domain.manga.model.isLocal import eu.kanade.domain.manga.model.isLocal
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.manga.ChapterItem import eu.kanade.tachiyomi.ui.manga.ChapterItem
import eu.kanade.tachiyomi.util.chapter.getChapterSort import eu.kanade.tachiyomi.util.chapter.getChapterSort
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.TriStateFilter
/** /**
* Applies the view filters to the list of chapters obtained from the database. * Applies the view filters to the list of chapters obtained from the database.

View File

@ -1,10 +1,10 @@
package eu.kanade.domain.download.interactor package eu.kanade.domain.download.interactor
import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext import tachiyomi.core.util.lang.withNonCancellableContext
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.manga.model.Manga
class DeleteDownload( class DeleteDownload(
private val sourceManager: SourceManager, private val sourceManager: SourceManager,

View File

@ -1,7 +1,7 @@
package eu.kanade.domain.download.service package eu.kanade.domain.download.service
import eu.kanade.tachiyomi.core.preference.PreferenceStore import tachiyomi.core.preference.PreferenceStore
import eu.kanade.tachiyomi.core.provider.FolderProvider import tachiyomi.core.provider.FolderProvider
class DownloadPreferences( class DownloadPreferences(
private val folderProvider: FolderProvider, private val folderProvider: FolderProvider,

View File

@ -1,10 +1,10 @@
package eu.kanade.domain.history.interactor package eu.kanade.domain.history.interactor
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.history.repository.HistoryRepository
import eu.kanade.domain.manga.interactor.GetManga import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.tachiyomi.util.chapter.getChapterSort import eu.kanade.tachiyomi.util.chapter.getChapterSort
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.history.repository.HistoryRepository
import kotlin.math.max import kotlin.math.max
class GetNextChapters( class GetNextChapters(

View File

@ -1,14 +1,14 @@
package eu.kanade.domain.library.service package eu.kanade.domain.library.service
import eu.kanade.domain.library.model.LibraryDisplayMode
import eu.kanade.domain.library.model.LibrarySort
import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.core.preference.PreferenceStore
import eu.kanade.tachiyomi.data.preference.DEVICE_ONLY_ON_WIFI import eu.kanade.tachiyomi.data.preference.DEVICE_ONLY_ON_WIFI
import eu.kanade.tachiyomi.data.preference.MANGA_HAS_UNREAD import eu.kanade.tachiyomi.data.preference.MANGA_HAS_UNREAD
import eu.kanade.tachiyomi.data.preference.MANGA_NON_COMPLETED import eu.kanade.tachiyomi.data.preference.MANGA_NON_COMPLETED
import eu.kanade.tachiyomi.data.preference.MANGA_NON_READ import eu.kanade.tachiyomi.data.preference.MANGA_NON_READ
import eu.kanade.tachiyomi.widget.ExtendedNavigationView import eu.kanade.tachiyomi.widget.ExtendedNavigationView
import tachiyomi.core.preference.PreferenceStore
import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.model.LibrarySort
import tachiyomi.domain.manga.model.Manga
class LibraryPreferences( class LibraryPreferences(
private val preferenceStore: PreferenceStore, private val preferenceStore: PreferenceStore,
@ -22,7 +22,7 @@ class LibraryPreferences(
fun landscapeColumns() = preferenceStore.getInt("pref_library_columns_landscape_key", 0) fun landscapeColumns() = preferenceStore.getInt("pref_library_columns_landscape_key", 0)
fun libraryUpdateInterval() = preferenceStore.getInt("pref_library_update_interval_key", 24) fun libraryUpdateInterval() = preferenceStore.getInt("pref_library_update_interval_key", 0)
fun libraryUpdateLastTimestamp() = preferenceStore.getLong("library_update_last_timestamp", 0L) fun libraryUpdateLastTimestamp() = preferenceStore.getLong("library_update_last_timestamp", 0L)
fun libraryUpdateDeviceRestriction() = preferenceStore.getStringSet("library_update_restriction", setOf(DEVICE_ONLY_ON_WIFI)) fun libraryUpdateDeviceRestriction() = preferenceStore.getStringSet("library_update_restriction", setOf(DEVICE_ONLY_ON_WIFI))
@ -56,8 +56,6 @@ class LibraryPreferences(
fun localBadge() = preferenceStore.getBoolean("display_local_badge", true) fun localBadge() = preferenceStore.getBoolean("display_local_badge", true)
fun unreadBadge() = preferenceStore.getBoolean("display_unread_badge", true)
fun languageBadge() = preferenceStore.getBoolean("display_language_badge", false) fun languageBadge() = preferenceStore.getBoolean("display_language_badge", false)
fun newShowUpdatesCount() = preferenceStore.getBoolean("library_show_updates_count", true) fun newShowUpdatesCount() = preferenceStore.getBoolean("library_show_updates_count", true)

View File

@ -1,13 +1,13 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import eu.kanade.domain.manga.repository.MangaRepository import tachiyomi.domain.manga.repository.MangaRepository
class GetDuplicateLibraryManga( class GetDuplicateLibraryManga(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,
) { ) {
suspend fun await(title: String, sourceId: Long): Manga? { suspend fun await(title: String): Manga? {
return mangaRepository.getDuplicateLibraryManga(title.lowercase(), sourceId) return mangaRepository.getDuplicateLibraryManga(title.lowercase())
} }
} }

View File

@ -1,8 +1,8 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.repository.MangaRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.repository.MangaRepository
class GetFavorites( class GetFavorites(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,

View File

@ -1,8 +1,8 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import eu.kanade.domain.library.model.LibraryManga
import eu.kanade.domain.manga.repository.MangaRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import tachiyomi.domain.library.model.LibraryManga
import tachiyomi.domain.manga.repository.MangaRepository
class GetLibraryManga( class GetLibraryManga(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,

View File

@ -1,10 +1,10 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.repository.MangaRepository
import eu.kanade.tachiyomi.util.system.logcat
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.repository.MangaRepository
class GetManga( class GetManga(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,

View File

@ -1,11 +1,11 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.chapter.repository.ChapterRepository
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.repository.MangaRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.chapter.repository.ChapterRepository
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.repository.MangaRepository
class GetMangaWithChapters( class GetMangaWithChapters(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,

View File

@ -1,7 +1,7 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import eu.kanade.domain.manga.repository.MangaRepository import tachiyomi.domain.manga.repository.MangaRepository
class NetworkToLocalManga( class NetworkToLocalManga(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,

View File

@ -1,6 +1,6 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.repository.MangaRepository import tachiyomi.domain.manga.repository.MangaRepository
class ResetViewerFlags( class ResetViewerFlags(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,

View File

@ -1,8 +1,8 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import eu.kanade.domain.manga.model.MangaUpdate import tachiyomi.domain.manga.model.MangaUpdate
import eu.kanade.domain.manga.repository.MangaRepository import tachiyomi.domain.manga.repository.MangaRepository
class SetMangaChapterFlags( class SetMangaChapterFlags(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,

View File

@ -1,9 +1,9 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.model.MangaUpdate
import eu.kanade.domain.manga.repository.MangaRepository
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import tachiyomi.domain.manga.model.MangaUpdate
import tachiyomi.domain.manga.repository.MangaRepository
class SetMangaViewerFlags( class SetMangaViewerFlags(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,

View File

@ -1,12 +1,12 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.MangaUpdate
import eu.kanade.domain.manga.model.hasCustomCover import eu.kanade.domain.manga.model.hasCustomCover
import eu.kanade.domain.manga.model.isLocal import eu.kanade.domain.manga.model.isLocal
import eu.kanade.domain.manga.repository.MangaRepository
import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaUpdate
import tachiyomi.domain.manga.repository.MangaRepository
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.Date import java.util.Date

View File

@ -1,11 +1,12 @@
package eu.kanade.domain.manga.model package eu.kanade.domain.manga.model
import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import nl.adaptivity.xmlutil.serialization.XmlElement import nl.adaptivity.xmlutil.serialization.XmlElement
import nl.adaptivity.xmlutil.serialization.XmlSerialName import nl.adaptivity.xmlutil.serialization.XmlSerialName
import nl.adaptivity.xmlutil.serialization.XmlValue import nl.adaptivity.xmlutil.serialization.XmlValue
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.manga.model.Manga
const val COMIC_INFO_FILE = "ComicInfo.xml" const val COMIC_INFO_FILE = "ComicInfo.xml"

View File

@ -4,95 +4,39 @@ import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.UpdateStrategy
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.widget.ExtendedNavigationView import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.TriStateFilter
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.Serializable
data class Manga( // TODO: move these into the domain model
val id: Long, val Manga.readingModeType: Long
val source: Long,
val favorite: Boolean,
val lastUpdate: Long,
val dateAdded: Long,
val viewerFlags: Long,
val chapterFlags: Long,
val coverLastModified: Long,
val url: String,
val title: String,
val artist: String?,
val author: String?,
val description: String?,
val genre: List<String>?,
val status: Long,
val thumbnailUrl: String?,
val updateStrategy: UpdateStrategy,
val initialized: Boolean,
) : Serializable {
val sorting: Long
get() = chapterFlags and CHAPTER_SORTING_MASK
val displayMode: Long
get() = chapterFlags and CHAPTER_DISPLAY_MASK
val unreadFilterRaw: Long
get() = chapterFlags and CHAPTER_UNREAD_MASK
val downloadedFilterRaw: Long
get() = chapterFlags and CHAPTER_DOWNLOADED_MASK
val bookmarkedFilterRaw: Long
get() = chapterFlags and CHAPTER_BOOKMARKED_MASK
val readingModeType: Long
get() = viewerFlags and ReadingModeType.MASK.toLong() get() = viewerFlags and ReadingModeType.MASK.toLong()
val orientationType: Long val Manga.orientationType: Long
get() = viewerFlags and OrientationType.MASK.toLong() get() = viewerFlags and OrientationType.MASK.toLong()
val unreadFilter: TriStateFilter val Manga.downloadedFilter: TriStateFilter
get() = when (unreadFilterRaw) {
CHAPTER_SHOW_UNREAD -> TriStateFilter.ENABLED_IS
CHAPTER_SHOW_READ -> TriStateFilter.ENABLED_NOT
else -> TriStateFilter.DISABLED
}
val downloadedFilter: TriStateFilter
get() { get() {
if (forceDownloaded()) return TriStateFilter.ENABLED_IS if (forceDownloaded()) return TriStateFilter.ENABLED_IS
return when (downloadedFilterRaw) { return when (downloadedFilterRaw) {
CHAPTER_SHOW_DOWNLOADED -> TriStateFilter.ENABLED_IS Manga.CHAPTER_SHOW_DOWNLOADED -> TriStateFilter.ENABLED_IS
CHAPTER_SHOW_NOT_DOWNLOADED -> TriStateFilter.ENABLED_NOT Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriStateFilter.ENABLED_NOT
else -> TriStateFilter.DISABLED else -> TriStateFilter.DISABLED
} }
} }
fun Manga.chaptersFiltered(): Boolean {
val bookmarkedFilter: TriStateFilter
get() = when (bookmarkedFilterRaw) {
CHAPTER_SHOW_BOOKMARKED -> TriStateFilter.ENABLED_IS
CHAPTER_SHOW_NOT_BOOKMARKED -> TriStateFilter.ENABLED_NOT
else -> TriStateFilter.DISABLED
}
fun chaptersFiltered(): Boolean {
return unreadFilter != TriStateFilter.DISABLED || return unreadFilter != TriStateFilter.DISABLED ||
downloadedFilter != TriStateFilter.DISABLED || downloadedFilter != TriStateFilter.DISABLED ||
bookmarkedFilter != TriStateFilter.DISABLED bookmarkedFilter != TriStateFilter.DISABLED
} }
fun Manga.forceDownloaded(): Boolean {
fun forceDownloaded(): Boolean {
return favorite && Injekt.get<BasePreferences>().downloadedOnly().get() return favorite && Injekt.get<BasePreferences>().downloadedOnly().get()
} }
fun sortDescending(): Boolean { fun Manga.toSManga(): SManga = SManga.create().also {
return chapterFlags and CHAPTER_SORT_DIR_MASK == CHAPTER_SORT_DESC
}
fun toSManga(): SManga = SManga.create().also {
it.url = url it.url = url
it.title = title it.title = title
it.artist = artist it.artist = artist
@ -102,9 +46,9 @@ data class Manga(
it.status = status.toInt() it.status = status.toInt()
it.thumbnail_url = thumbnailUrl it.thumbnail_url = thumbnailUrl
it.initialized = initialized it.initialized = initialized
} }
fun copyFrom(other: SManga): Manga { fun Manga.copyFrom(other: SManga): Manga {
val author = other.author ?: author val author = other.author ?: author
val artist = other.artist ?: artist val artist = other.artist ?: artist
val description = other.description ?: description val description = other.description ?: description
@ -124,95 +68,6 @@ data class Manga(
updateStrategy = other.update_strategy, updateStrategy = other.update_strategy,
initialized = other.initialized && initialized, initialized = other.initialized && initialized,
) )
}
companion object {
// Generic filter that does not filter anything
const val SHOW_ALL = 0x00000000L
const val CHAPTER_SORT_DESC = 0x00000000L
const val CHAPTER_SORT_ASC = 0x00000001L
const val CHAPTER_SORT_DIR_MASK = 0x00000001L
const val CHAPTER_SHOW_UNREAD = 0x00000002L
const val CHAPTER_SHOW_READ = 0x00000004L
const val CHAPTER_UNREAD_MASK = 0x00000006L
const val CHAPTER_SHOW_DOWNLOADED = 0x00000008L
const val CHAPTER_SHOW_NOT_DOWNLOADED = 0x00000010L
const val CHAPTER_DOWNLOADED_MASK = 0x00000018L
const val CHAPTER_SHOW_BOOKMARKED = 0x00000020L
const val CHAPTER_SHOW_NOT_BOOKMARKED = 0x00000040L
const val CHAPTER_BOOKMARKED_MASK = 0x00000060L
const val CHAPTER_SORTING_SOURCE = 0x00000000L
const val CHAPTER_SORTING_NUMBER = 0x00000100L
const val CHAPTER_SORTING_UPLOAD_DATE = 0x00000200L
const val CHAPTER_SORTING_MASK = 0x00000300L
const val CHAPTER_DISPLAY_NAME = 0x00000000L
const val CHAPTER_DISPLAY_NUMBER = 0x00100000L
const val CHAPTER_DISPLAY_MASK = 0x00100000L
fun create() = Manga(
id = -1L,
url = "",
title = "",
source = -1L,
favorite = false,
lastUpdate = 0L,
dateAdded = 0L,
viewerFlags = 0L,
chapterFlags = 0L,
coverLastModified = 0L,
artist = null,
author = null,
description = null,
genre = null,
status = 0L,
thumbnailUrl = null,
updateStrategy = UpdateStrategy.ALWAYS_UPDATE,
initialized = false,
)
}
}
enum class TriStateFilter {
DISABLED, // Disable filter
ENABLED_IS, // Enabled with "is" filter
ENABLED_NOT, // Enabled with "not" filter
}
fun TriStateFilter.toTriStateGroupState(): ExtendedNavigationView.Item.TriStateGroup.State {
return when (this) {
TriStateFilter.DISABLED -> ExtendedNavigationView.Item.TriStateGroup.State.IGNORE
TriStateFilter.ENABLED_IS -> ExtendedNavigationView.Item.TriStateGroup.State.INCLUDE
TriStateFilter.ENABLED_NOT -> ExtendedNavigationView.Item.TriStateGroup.State.EXCLUDE
}
}
fun Manga.toMangaUpdate(): MangaUpdate {
return MangaUpdate(
id = id,
source = source,
favorite = favorite,
lastUpdate = lastUpdate,
dateAdded = dateAdded,
viewerFlags = viewerFlags,
chapterFlags = chapterFlags,
coverLastModified = coverLastModified,
url = url,
title = title,
artist = artist,
author = author,
description = description,
genre = genre,
status = status,
thumbnailUrl = thumbnailUrl,
updateStrategy = updateStrategy,
initialized = initialized,
)
} }
fun SManga.toDomainManga(sourceId: Long): Manga { fun SManga.toDomainManga(sourceId: Long): Manga {

View File

@ -1,14 +1,14 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.model.Pin
import eu.kanade.domain.source.model.Pins
import eu.kanade.domain.source.model.Source
import eu.kanade.domain.source.repository.SourceRepository import eu.kanade.domain.source.repository.SourceRepository
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import tachiyomi.domain.source.model.Pin
import tachiyomi.domain.source.model.Pins
import tachiyomi.domain.source.model.Source
class GetEnabledSources( class GetEnabledSources(
private val repository: SourceRepository, private val repository: SourceRepository,

View File

@ -1,11 +1,11 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.model.Source
import eu.kanade.domain.source.repository.SourceRepository import eu.kanade.domain.source.repository.SourceRepository
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import tachiyomi.domain.source.model.Source
class GetLanguagesWithSources( class GetLanguagesWithSources(
private val repository: SourceRepository, private val repository: SourceRepository,

View File

@ -1,10 +1,10 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.model.Source
import eu.kanade.domain.source.repository.SourceRepository import eu.kanade.domain.source.repository.SourceRepository
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import tachiyomi.domain.source.model.Source
import java.text.Collator import java.text.Collator
import java.util.Collections import java.util.Collections
import java.util.Locale import java.util.Locale

View File

@ -1,8 +1,8 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.model.SourceWithCount
import eu.kanade.domain.source.repository.SourceRepository import eu.kanade.domain.source.repository.SourceRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import tachiyomi.domain.source.model.SourceWithCount
class GetSourcesWithNonLibraryManga( class GetSourcesWithNonLibraryManga(
private val repository: SourceRepository, private val repository: SourceRepository,

View File

@ -1,7 +1,7 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.core.preference.getAndSet import tachiyomi.core.preference.getAndSet
class ToggleLanguage( class ToggleLanguage(
val preferences: SourcePreferences, val preferences: SourcePreferences,

View File

@ -1,8 +1,8 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.model.Source
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.core.preference.getAndSet import tachiyomi.core.preference.getAndSet
import tachiyomi.domain.source.model.Source
class ToggleSource( class ToggleSource(
private val preferences: SourcePreferences, private val preferences: SourcePreferences,

View File

@ -1,8 +1,8 @@
package eu.kanade.domain.source.interactor package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.model.Source
import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.core.preference.getAndSet import tachiyomi.core.preference.getAndSet
import tachiyomi.domain.source.model.Source
class ToggleSourcePin( class ToggleSourcePin(
private val preferences: SourcePreferences, private val preferences: SourcePreferences,

View File

@ -4,77 +4,13 @@ import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.asImageBitmap
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.extension.ExtensionManager
import tachiyomi.domain.source.model.Source
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
data class Source( val Source.icon: ImageBitmap?
val id: Long,
val lang: String,
val name: String,
val supportsLatest: Boolean,
val isStub: Boolean,
val pin: Pins = Pins.unpinned,
val isUsedLast: Boolean = false,
) {
val visualName: String
get() = when {
lang.isEmpty() -> name
else -> "$name (${lang.uppercase()})"
}
val icon: ImageBitmap?
get() { get() {
return Injekt.get<ExtensionManager>().getAppIconForSource(id) return Injekt.get<ExtensionManager>().getAppIconForSource(id)
?.toBitmap() ?.toBitmap()
?.asImageBitmap() ?.asImageBitmap()
} }
val key: () -> String = {
when {
isUsedLast -> "$id-lastused"
else -> "$id"
}
}
}
sealed class Pin(val code: Int) {
object Unpinned : Pin(0b00)
object Pinned : Pin(0b01)
object Actual : Pin(0b10)
}
inline fun Pins(builder: Pins.PinsBuilder.() -> Unit = {}): Pins {
return Pins.PinsBuilder().apply(builder).flags()
}
fun Pins(vararg pins: Pin) = Pins {
pins.forEach { +it }
}
data class Pins(val code: Int = Pin.Unpinned.code) {
operator fun contains(pin: Pin): Boolean = pin.code and code == pin.code
operator fun plus(pin: Pin): Pins = Pins(code or pin.code)
operator fun minus(pin: Pin): Pins = Pins(code xor pin.code)
companion object {
val unpinned = Pins(Pin.Unpinned)
val pinned = Pins(Pin.Pinned, Pin.Actual)
}
class PinsBuilder(var code: Int = 0) {
operator fun Pin.unaryPlus() {
this@PinsBuilder.code = code or this@PinsBuilder.code
}
operator fun Pin.unaryMinus() {
this@PinsBuilder.code = code or this@PinsBuilder.code
}
fun flags(): Pins = Pins(code)
}
}

View File

@ -1,10 +1,10 @@
package eu.kanade.domain.source.repository package eu.kanade.domain.source.repository
import eu.kanade.domain.source.model.Source
import eu.kanade.domain.source.model.SourcePagingSourceType import eu.kanade.domain.source.model.SourcePagingSourceType
import eu.kanade.domain.source.model.SourceWithCount
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import tachiyomi.domain.source.model.Source
import tachiyomi.domain.source.model.SourceWithCount
interface SourceRepository { interface SourceRepository {

View File

@ -1,10 +1,10 @@
package eu.kanade.domain.source.service package eu.kanade.domain.source.service
import eu.kanade.domain.library.model.LibraryDisplayMode
import eu.kanade.domain.source.interactor.SetMigrateSorting import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.tachiyomi.core.preference.PreferenceStore
import eu.kanade.tachiyomi.core.preference.getEnum
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.core.preference.PreferenceStore
import tachiyomi.core.preference.getEnum
import tachiyomi.domain.library.model.LibraryDisplayMode
class SourcePreferences( class SourcePreferences(
private val preferenceStore: PreferenceStore, private val preferenceStore: PreferenceStore,
@ -18,8 +18,6 @@ class SourcePreferences(
fun pinnedSources() = preferenceStore.getStringSet("pinned_catalogues", emptySet()) fun pinnedSources() = preferenceStore.getStringSet("pinned_catalogues", emptySet())
fun duplicatePinnedSources() = preferenceStore.getBoolean("duplicate_pinned_sources", false)
fun lastUsedSource() = preferenceStore.getLong("last_catalogue_source", -1) fun lastUsedSource() = preferenceStore.getLong("last_catalogue_source", -1)
fun showNsfwSource() = preferenceStore.getBoolean("show_nsfw_source", true) fun showNsfwSource() = preferenceStore.getBoolean("show_nsfw_source", true)

View File

@ -1,8 +1,8 @@
package eu.kanade.domain.track.interactor package eu.kanade.domain.track.interactor
import eu.kanade.domain.track.repository.TrackRepository
import eu.kanade.tachiyomi.util.system.logcat
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.track.repository.TrackRepository
class DeleteTrack( class DeleteTrack(
private val trackRepository: TrackRepository, private val trackRepository: TrackRepository,

View File

@ -1,10 +1,10 @@
package eu.kanade.domain.track.interactor package eu.kanade.domain.track.interactor
import eu.kanade.domain.track.model.Track
import eu.kanade.domain.track.repository.TrackRepository
import eu.kanade.tachiyomi.util.system.logcat
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.track.model.Track
import tachiyomi.domain.track.repository.TrackRepository
class GetTracks( class GetTracks(
private val trackRepository: TrackRepository, private val trackRepository: TrackRepository,

View File

@ -1,8 +1,8 @@
package eu.kanade.domain.track.interactor package eu.kanade.domain.track.interactor
import eu.kanade.domain.track.repository.TrackRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import tachiyomi.domain.track.repository.TrackRepository
class GetTracksPerManga( class GetTracksPerManga(
private val trackRepository: TrackRepository, private val trackRepository: TrackRepository,

View File

@ -1,9 +1,9 @@
package eu.kanade.domain.track.interactor package eu.kanade.domain.track.interactor
import eu.kanade.domain.track.model.Track
import eu.kanade.domain.track.repository.TrackRepository
import eu.kanade.tachiyomi.util.system.logcat
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.track.model.Track
import tachiyomi.domain.track.repository.TrackRepository
class InsertTrack( class InsertTrack(
private val trackRepository: TrackRepository, private val trackRepository: TrackRepository,

View File

@ -1,23 +1,9 @@
package eu.kanade.domain.track.model package eu.kanade.domain.track.model
import tachiyomi.domain.track.model.Track
import eu.kanade.tachiyomi.data.database.models.Track as DbTrack import eu.kanade.tachiyomi.data.database.models.Track as DbTrack
data class Track( fun Track.copyPersonalFrom(other: Track): Track {
val id: Long,
val mangaId: Long,
val syncId: Long,
val remoteId: Long,
val libraryId: Long?,
val title: String,
val lastChapterRead: Double,
val totalChapters: Long,
val status: Long,
val score: Float,
val remoteUrl: String,
val startDate: Long,
val finishDate: Long,
) {
fun copyPersonalFrom(other: Track): Track {
return this.copy( return this.copy(
lastChapterRead = other.lastChapterRead, lastChapterRead = other.lastChapterRead,
score = other.score, score = other.score,
@ -25,7 +11,6 @@ data class Track(
startDate = other.startDate, startDate = other.startDate,
finishDate = other.finishDate, finishDate = other.finishDate,
) )
}
} }
fun Track.toDbTrack(): DbTrack = DbTrack.create(syncId).also { fun Track.toDbTrack(): DbTrack = DbTrack.create(syncId).also {

View File

@ -14,9 +14,9 @@ import eu.kanade.domain.track.interactor.InsertTrack
import eu.kanade.domain.track.model.toDbTrack import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.domain.track.store.DelayedTrackingStore import eu.kanade.domain.track.store.DelayedTrackingStore
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.util.lang.withIOContext
import eu.kanade.tachiyomi.util.system.logcat
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.system.logcat
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -31,30 +31,33 @@ class DelayedTrackingUpdateJob(context: Context, workerParams: WorkerParameters)
val trackManager = Injekt.get<TrackManager>() val trackManager = Injekt.get<TrackManager>()
val delayedTrackingStore = Injekt.get<DelayedTrackingStore>() val delayedTrackingStore = Injekt.get<DelayedTrackingStore>()
withIOContext { val results = withIOContext {
val tracks = delayedTrackingStore.getItems().mapNotNull { delayedTrackingStore.getItems()
.mapNotNull {
val track = getTracks.awaitOne(it.trackId) val track = getTracks.awaitOne(it.trackId)
if (track == null) { if (track == null) {
delayedTrackingStore.remove(it.trackId) delayedTrackingStore.remove(it.trackId)
} }
track track?.copy(lastChapterRead = it.lastChapterRead.toDouble())
} }
.mapNotNull { track ->
tracks.forEach { track ->
try { try {
val service = trackManager.getService(track.syncId) val service = trackManager.getService(track.syncId)
if (service != null && service.isLogged) { if (service != null && service.isLogged) {
logcat(LogPriority.DEBUG) { "Updating delayed track item: ${track.id}, last chapter read: ${track.lastChapterRead}" }
service.update(track.toDbTrack(), true) service.update(track.toDbTrack(), true)
insertTrack.await(track) insertTrack.await(track)
} }
delayedTrackingStore.remove(track.id) delayedTrackingStore.remove(track.id)
null
} catch (e: Exception) { } catch (e: Exception) {
logcat(LogPriority.ERROR, e) logcat(LogPriority.ERROR, e)
false
} }
} }
} }
return Result.success() return if (results.isNotEmpty()) Result.failure() else Result.success()
} }
companion object { companion object {

View File

@ -1,8 +1,8 @@
package eu.kanade.domain.track.service package eu.kanade.domain.track.service
import eu.kanade.tachiyomi.core.preference.PreferenceStore
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.anilist.Anilist import eu.kanade.tachiyomi.data.track.anilist.Anilist
import tachiyomi.core.preference.PreferenceStore
class TrackPreferences( class TrackPreferences(
private val preferenceStore: PreferenceStore, private val preferenceStore: PreferenceStore,

View File

@ -2,9 +2,9 @@ package eu.kanade.domain.track.store
import android.content.Context import android.content.Context
import androidx.core.content.edit import androidx.core.content.edit
import eu.kanade.domain.track.model.Track
import eu.kanade.tachiyomi.util.system.logcat
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.track.model.Track
class DelayedTrackingStore(context: Context) { class DelayedTrackingStore(context: Context) {

View File

@ -4,10 +4,10 @@ import android.os.Build
import eu.kanade.domain.ui.model.AppTheme import eu.kanade.domain.ui.model.AppTheme
import eu.kanade.domain.ui.model.TabletUiMode 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.core.preference.PreferenceStore
import eu.kanade.tachiyomi.core.preference.getEnum
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.preference.PreferenceStore
import tachiyomi.core.preference.getEnum
import java.text.DateFormat import java.text.DateFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale

View File

@ -1,6 +1,7 @@
package eu.kanade.presentation.browse package eu.kanade.presentation.browse
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.HelpOutline import androidx.compose.material.icons.outlined.HelpOutline
@ -11,26 +12,30 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult import androidx.compose.material3.SnackbarResult
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.paging.LoadState import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.LazyPagingItems
import eu.kanade.data.source.NoResultsException import eu.kanade.data.source.NoResultsException
import eu.kanade.domain.library.model.LibraryDisplayMode
import eu.kanade.domain.manga.model.Manga
import eu.kanade.presentation.browse.components.BrowseSourceComfortableGrid import eu.kanade.presentation.browse.components.BrowseSourceComfortableGrid
import eu.kanade.presentation.browse.components.BrowseSourceCompactGrid import eu.kanade.presentation.browse.components.BrowseSourceCompactGrid
import eu.kanade.presentation.browse.components.BrowseSourceList import eu.kanade.presentation.browse.components.BrowseSourceList
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.EmptyScreenAction import eu.kanade.presentation.components.EmptyScreenAction
import eu.kanade.presentation.components.LoadingScreen import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.components.Scaffold
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.manga.model.Manga
@Composable @Composable
fun BrowseSourceContent( fun BrowseSourceContent(
source: CatalogueSource?, source: Source?,
mangaList: LazyPagingItems<StateFlow<Manga>>, mangaList: LazyPagingItems<StateFlow<Manga>>,
columns: GridCells, columns: GridCells,
displayMode: LibraryDisplayMode, displayMode: LibraryDisplayMode,
@ -139,3 +144,24 @@ fun BrowseSourceContent(
} }
} }
} }
@Composable
fun MissingSourceScreen(
source: SourceManager.StubSource,
navigateUp: () -> Unit,
) {
Scaffold(
topBar = { scrollBehavior ->
AppBar(
title = source.name,
navigateUp = navigateUp,
scrollBehavior = scrollBehavior,
)
},
) { paddingValues ->
EmptyScreen(
message = source.getSourceNotInstalledException().message!!,
modifier = Modifier.padding(paddingValues),
)
}
}

View File

@ -2,7 +2,6 @@ package eu.kanade.presentation.browse
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -32,7 +31,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -43,7 +41,6 @@ import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.FastScrollLazyColumn import eu.kanade.presentation.components.FastScrollLazyColumn
import eu.kanade.presentation.components.LoadingScreen import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.components.PullRefresh import eu.kanade.presentation.components.PullRefresh
import eu.kanade.presentation.components.WarningBanner
import eu.kanade.presentation.manga.components.DotSeparatorNoSpaceText import eu.kanade.presentation.manga.components.DotSeparatorNoSpaceText
import eu.kanade.presentation.theme.header import eu.kanade.presentation.theme.header
import eu.kanade.presentation.util.padding import eu.kanade.presentation.util.padding
@ -55,14 +52,13 @@ import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.extension.model.InstallStep import eu.kanade.tachiyomi.extension.model.InstallStep
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionUiModel import eu.kanade.tachiyomi.ui.browse.extension.ExtensionUiModel
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsState import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsState
import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
@Composable @Composable
fun ExtensionScreen( fun ExtensionScreen(
state: ExtensionsState, state: ExtensionsState,
contentPadding: PaddingValues, contentPadding: PaddingValues,
searchQuery: String? = null, searchQuery: String?,
onLongClickItem: (Extension) -> Unit, onLongClickItem: (Extension) -> Unit,
onClickItemCancel: (Extension) -> Unit, onClickItemCancel: (Extension) -> Unit,
onInstallExtension: (Extension.Available) -> Unit, onInstallExtension: (Extension.Available) -> Unit,
@ -123,48 +119,19 @@ private fun ExtensionContent(
onClickUpdateAll: () -> Unit, onClickUpdateAll: () -> Unit,
) { ) {
var trustState by remember { mutableStateOf<Extension.Untrusted?>(null) } var trustState by remember { mutableStateOf<Extension.Untrusted?>(null) }
val showMiuiWarning = DeviceUtil.isMiui && !DeviceUtil.isMiuiOptimizationDisabled()
val uriHandler = LocalUriHandler.current
FastScrollLazyColumn( FastScrollLazyColumn(
contentPadding = if (showMiuiWarning) { contentPadding = contentPadding + topSmallPaddingValues,
contentPadding
} else {
contentPadding + topSmallPaddingValues
},
) { ) {
if (showMiuiWarning) { state.items.forEach { (header, items) ->
item { item(
WarningBanner( contentType = "header",
textRes = R.string.ext_miui_warning, key = "extensionHeader-${header.hashCode()}",
modifier = Modifier ) {
.padding(bottom = MaterialTheme.padding.small) when (header) {
.clickable {
uriHandler.openUri("https://tachiyomi.org/extensions")
},
)
}
}
items(
items = state.items,
contentType = {
when (it) {
is ExtensionUiModel.Header -> "header"
is ExtensionUiModel.Item -> "item"
}
},
key = {
when (it) {
is ExtensionUiModel.Header -> "extensionHeader-${it.hashCode()}"
is ExtensionUiModel.Item -> "extension-${it.hashCode()}"
}
},
) { item ->
when (item) {
is ExtensionUiModel.Header.Resource -> { is ExtensionUiModel.Header.Resource -> {
val action: @Composable RowScope.() -> Unit = val action: @Composable RowScope.() -> Unit =
if (item.textRes == R.string.ext_updates_pending) { if (header.textRes == R.string.ext_updates_pending) {
{ {
Button(onClick = { onClickUpdateAll() }) { Button(onClick = { onClickUpdateAll() }) {
Text( Text(
@ -179,18 +146,25 @@ private fun ExtensionContent(
{} {}
} }
ExtensionHeader( ExtensionHeader(
textRes = item.textRes, textRes = header.textRes,
modifier = Modifier.animateItemPlacement(), modifier = Modifier.animateItemPlacement(),
action = action, action = action,
) )
} }
is ExtensionUiModel.Header.Text -> { is ExtensionUiModel.Header.Text -> {
ExtensionHeader( ExtensionHeader(
text = item.text, text = header.text,
modifier = Modifier.animateItemPlacement(), modifier = Modifier.animateItemPlacement(),
) )
} }
is ExtensionUiModel.Item -> { }
}
items(
items = items,
contentType = { "item" },
key = { "extension-${it.hashCode()}" },
) { item ->
ExtensionItem( ExtensionItem(
modifier = Modifier.animateItemPlacement(), modifier = Modifier.animateItemPlacement(),
item = item, item = item,
@ -220,7 +194,6 @@ private fun ExtensionContent(
} }
} }
} }
}
if (trustState != null) { if (trustState != null) {
ExtensionTrustDialog( ExtensionTrustDialog(
onClickConfirm = { onClickConfirm = {

View File

@ -8,7 +8,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import eu.kanade.domain.manga.model.Manga
import eu.kanade.presentation.browse.components.GlobalSearchCardRow import eu.kanade.presentation.browse.components.GlobalSearchCardRow
import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem
import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
@ -22,6 +21,7 @@ import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchState import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchState
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.manga.model.Manga
@Composable @Composable
fun GlobalSearchScreen( fun GlobalSearchScreen(
@ -71,16 +71,13 @@ fun GlobalSearchContent(
contentPadding = contentPadding, contentPadding = contentPadding,
) { ) {
items.forEach { (source, result) -> items.forEach { (source, result) ->
item { item(key = source.id) {
GlobalSearchResultItem( GlobalSearchResultItem(
title = source.name, title = source.name,
subtitle = LocaleHelper.getDisplayName(source.lang), subtitle = LocaleHelper.getDisplayName(source.lang),
onClick = { onClickSource(source) }, onClick = { onClickSource(source) },
) { ) {
when (result) { when (result) {
is SearchItemResult.Error -> {
GlobalSearchErrorResultItem(message = result.throwable.message)
}
SearchItemResult.Loading -> { SearchItemResult.Loading -> {
GlobalSearchLoadingResultItem() GlobalSearchLoadingResultItem()
} }
@ -104,6 +101,9 @@ fun GlobalSearchContent(
onLongClick = onLongClickItem, onLongClick = onLongClickItem,
) )
} }
is SearchItemResult.Error -> {
GlobalSearchErrorResultItem(message = result.throwable.message)
}
} }
} }
} }

View File

@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.items 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 eu.kanade.domain.manga.model.Manga
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.FastScrollLazyColumn import eu.kanade.presentation.components.FastScrollLazyColumn
@ -13,6 +12,7 @@ import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.manga.components.BaseMangaListItem import eu.kanade.presentation.manga.components.BaseMangaListItem
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaState import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaState
import tachiyomi.domain.manga.model.Manga
@Composable @Composable
fun MigrateMangaScreen( fun MigrateMangaScreen(

View File

@ -3,7 +3,6 @@ package eu.kanade.presentation.browse
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import eu.kanade.domain.manga.model.Manga
import eu.kanade.presentation.browse.components.GlobalSearchCardRow import eu.kanade.presentation.browse.components.GlobalSearchCardRow
import eu.kanade.presentation.browse.components.GlobalSearchEmptyResultItem import eu.kanade.presentation.browse.components.GlobalSearchEmptyResultItem
import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem
@ -16,6 +15,7 @@ import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchState import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchState
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.manga.model.Manga
@Composable @Composable
fun MigrateSearchScreen( fun MigrateSearchScreen(
@ -67,16 +67,13 @@ fun MigrateSearchContent(
contentPadding = contentPadding, contentPadding = contentPadding,
) { ) {
items.forEach { (source, result) -> items.forEach { (source, result) ->
item { item(key = source.id) {
GlobalSearchResultItem( GlobalSearchResultItem(
title = if (source.id == sourceId) "${source.name}" else source.name, title = if (source.id == sourceId) "${source.name}" else source.name,
subtitle = LocaleHelper.getDisplayName(source.lang), subtitle = LocaleHelper.getDisplayName(source.lang),
onClick = { onClickSource(source) }, onClick = { onClickSource(source) },
) { ) {
when (result) { when (result) {
is SearchItemResult.Error -> {
GlobalSearchErrorResultItem(message = result.throwable.message)
}
SearchItemResult.Loading -> { SearchItemResult.Loading -> {
GlobalSearchLoadingResultItem() GlobalSearchLoadingResultItem()
} }
@ -93,6 +90,9 @@ fun MigrateSearchContent(
onLongClick = onLongClickItem, onLongClick = onLongClickItem,
) )
} }
is SearchItemResult.Error -> {
GlobalSearchErrorResultItem(message = result.throwable.message)
}
} }
} }
} }

View File

@ -22,9 +22,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import eu.kanade.domain.source.interactor.SetMigrateSorting import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.domain.source.model.Source
import eu.kanade.presentation.browse.components.BaseSourceItem import eu.kanade.presentation.browse.components.BaseSourceItem
import eu.kanade.presentation.browse.components.SourceIcon import eu.kanade.presentation.browse.components.SourceIcon
import eu.kanade.presentation.components.Badge import eu.kanade.presentation.components.Badge
@ -41,6 +39,7 @@ import eu.kanade.presentation.util.topSmallPaddingValues
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrateSourceState import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrateSourceState
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.domain.source.model.Source
@Composable @Composable
fun MigrateSourceScreen( fun MigrateSourceScreen(
@ -164,7 +163,7 @@ private fun MigrateSourceItem(
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
) )
Row( Row(
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
if (sourceLangString != null) { if (sourceLangString != null) {

View File

@ -8,7 +8,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import eu.kanade.domain.source.model.Source
import eu.kanade.presentation.browse.components.BaseSourceItem import eu.kanade.presentation.browse.components.BaseSourceItem
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.EmptyScreen
@ -18,6 +17,7 @@ import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterState import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterState
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.source.model.Source
@Composable @Composable
fun SourcesFilterScreen( fun SourcesFilterScreen(

View File

@ -21,8 +21,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.domain.source.model.Pin
import eu.kanade.domain.source.model.Source
import eu.kanade.presentation.browse.components.BaseSourceItem import eu.kanade.presentation.browse.components.BaseSourceItem
import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.LoadingScreen import eu.kanade.presentation.components.LoadingScreen
@ -36,6 +34,8 @@ import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.ui.browse.source.SourcesState import eu.kanade.tachiyomi.ui.browse.source.SourcesState
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.source.model.Pin
import tachiyomi.domain.source.model.Source
@Composable @Composable
fun SourcesScreen( fun SourcesScreen(

View File

@ -9,10 +9,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import eu.kanade.domain.source.model.Source
import eu.kanade.presentation.util.padding import eu.kanade.presentation.util.padding
import eu.kanade.presentation.util.secondaryItemAlpha import eu.kanade.presentation.util.secondaryItemAlpha
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.source.model.Source
@Composable @Composable
fun BaseSourceItem( fun BaseSourceItem(

View File

@ -27,11 +27,13 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import coil.compose.AsyncImage import coil.compose.AsyncImage
import eu.kanade.domain.source.model.Source import eu.kanade.domain.source.model.icon
import eu.kanade.presentation.util.rememberResourceBitmapPainter import eu.kanade.presentation.util.rememberResourceBitmapPainter
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.util.lang.withIOContext import eu.kanade.tachiyomi.source.LocalSource
import tachiyomi.core.util.lang.withIOContext
import tachiyomi.domain.source.model.Source
private val defaultModifier = Modifier private val defaultModifier = Modifier
.height(40.dp) .height(40.dp)
@ -60,13 +62,20 @@ fun SourceIcon(
modifier = modifier.then(defaultModifier), modifier = modifier.then(defaultModifier),
) )
} }
else -> { source.id == LocalSource.ID -> {
Image( Image(
painter = painterResource(R.mipmap.ic_local_source), painter = painterResource(R.mipmap.ic_local_source),
contentDescription = null, contentDescription = null,
modifier = modifier.then(defaultModifier), modifier = modifier.then(defaultModifier),
) )
} }
else -> {
Image(
painter = painterResource(R.mipmap.ic_default_source),
contentDescription = null,
modifier = modifier.then(defaultModifier),
)
}
} }
} }
@ -90,17 +99,17 @@ fun ExtensionIcon(
is Extension.Installed -> { is Extension.Installed -> {
val icon by extension.getIcon(density) val icon by extension.getIcon(density)
when (icon) { when (icon) {
Result.Error -> Image(
bitmap = ImageBitmap.imageResource(id = R.mipmap.ic_local_source),
contentDescription = null,
modifier = modifier,
)
Result.Loading -> Box(modifier = modifier) Result.Loading -> Box(modifier = modifier)
is Result.Success -> Image( is Result.Success -> Image(
bitmap = (icon as Result.Success<ImageBitmap>).value, bitmap = (icon as Result.Success<ImageBitmap>).value,
contentDescription = null, contentDescription = null,
modifier = modifier, modifier = modifier,
) )
Result.Error -> Image(
bitmap = ImageBitmap.imageResource(id = R.mipmap.ic_default_source),
contentDescription = null,
modifier = modifier,
)
} }
} }
is Extension.Untrusted -> Image( is Extension.Untrusted -> Image(

View File

@ -11,13 +11,13 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.paging.LoadState import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.LazyPagingItems
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.MangaCover
import eu.kanade.presentation.browse.InLibraryBadge import eu.kanade.presentation.browse.InLibraryBadge
import eu.kanade.presentation.components.CommonMangaItemDefaults import eu.kanade.presentation.components.CommonMangaItemDefaults
import eu.kanade.presentation.components.MangaComfortableGridItem import eu.kanade.presentation.components.MangaComfortableGridItem
import eu.kanade.presentation.util.plus import eu.kanade.presentation.util.plus
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaCover
@Composable @Composable
fun BrowseSourceComfortableGrid( fun BrowseSourceComfortableGrid(

View File

@ -11,13 +11,13 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.paging.LoadState import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.LazyPagingItems
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.MangaCover
import eu.kanade.presentation.browse.InLibraryBadge import eu.kanade.presentation.browse.InLibraryBadge
import eu.kanade.presentation.components.CommonMangaItemDefaults import eu.kanade.presentation.components.CommonMangaItemDefaults
import eu.kanade.presentation.components.MangaCompactGridItem import eu.kanade.presentation.components.MangaCompactGridItem
import eu.kanade.presentation.util.plus import eu.kanade.presentation.util.plus
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaCover
@Composable @Composable
fun BrowseSourceCompactGrid( fun BrowseSourceCompactGrid(

View File

@ -7,8 +7,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import tachiyomi.domain.manga.model.Manga
@Composable @Composable
fun RemoveMangaDialog( fun RemoveMangaDialog(

View File

@ -8,14 +8,14 @@ import androidx.compose.ui.unit.dp
import androidx.paging.LoadState import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.items import androidx.paging.compose.items
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.MangaCover
import eu.kanade.presentation.browse.InLibraryBadge import eu.kanade.presentation.browse.InLibraryBadge
import eu.kanade.presentation.components.CommonMangaItemDefaults import eu.kanade.presentation.components.CommonMangaItemDefaults
import eu.kanade.presentation.components.LazyColumn import eu.kanade.presentation.components.LazyColumn
import eu.kanade.presentation.components.MangaListItem import eu.kanade.presentation.components.MangaListItem
import eu.kanade.presentation.util.plus import eu.kanade.presentation.util.plus
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaCover
@Composable @Composable
fun BrowseSourceList( fun BrowseSourceList(

View File

@ -13,7 +13,6 @@ 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.res.stringResource import androidx.compose.ui.res.stringResource
import eu.kanade.domain.library.model.LibraryDisplayMode
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.components.AppBarTitle import eu.kanade.presentation.components.AppBarTitle
@ -21,14 +20,15 @@ import eu.kanade.presentation.components.DropdownMenu
import eu.kanade.presentation.components.RadioMenuItem import eu.kanade.presentation.components.RadioMenuItem
import eu.kanade.presentation.components.SearchToolbar import eu.kanade.presentation.components.SearchToolbar
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
import tachiyomi.domain.library.model.LibraryDisplayMode
@Composable @Composable
fun BrowseSourceToolbar( fun BrowseSourceToolbar(
searchQuery: String?, searchQuery: String?,
onSearchQueryChange: (String?) -> Unit, onSearchQueryChange: (String?) -> Unit,
source: CatalogueSource?, source: Source?,
displayMode: LibraryDisplayMode, displayMode: LibraryDisplayMode,
onDisplayModeChange: (LibraryDisplayMode) -> Unit, onDisplayModeChange: (LibraryDisplayMode) -> Unit,
navigateUp: () -> Unit, navigateUp: () -> Unit,

View File

@ -5,10 +5,10 @@ import androidx.compose.foundation.layout.width
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 eu.kanade.domain.manga.model.MangaCover
import eu.kanade.presentation.browse.InLibraryBadge import eu.kanade.presentation.browse.InLibraryBadge
import eu.kanade.presentation.components.CommonMangaItemDefaults import eu.kanade.presentation.components.CommonMangaItemDefaults
import eu.kanade.presentation.components.MangaComfortableGridItem import eu.kanade.presentation.components.MangaComfortableGridItem
import tachiyomi.domain.manga.model.MangaCover
@Composable @Composable
fun GlobalSearchCard( fun GlobalSearchCard(
@ -18,7 +18,7 @@ fun GlobalSearchCard(
onClick: () -> Unit, onClick: () -> Unit,
onLongClick: () -> Unit, onLongClick: () -> Unit,
) { ) {
Box(modifier = Modifier.width(128.dp)) { Box(modifier = Modifier.width(96.dp)) {
MangaComfortableGridItem( MangaComfortableGridItem(
title = title, title = title,
coverData = cover, coverData = cover,

View File

@ -8,9 +8,9 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.asMangaCover
import eu.kanade.presentation.util.padding import eu.kanade.presentation.util.padding
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.asMangaCover
@Composable @Composable
fun GlobalSearchCardRow( fun GlobalSearchCardRow(
@ -20,14 +20,11 @@ fun GlobalSearchCardRow(
onLongClick: (Manga) -> Unit, onLongClick: (Manga) -> Unit,
) { ) {
LazyRow( LazyRow(
contentPadding = PaddingValues( contentPadding = PaddingValues(MaterialTheme.padding.small),
horizontal = MaterialTheme.padding.medium, horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.tiny),
vertical = MaterialTheme.padding.small,
),
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
) { ) {
items(titles) { title -> items(titles) {
val title by getManga(title) val title by getManga(it)
GlobalSearchCard( GlobalSearchCard(
title = title.title, title = title.title,
cover = title.asMangaCover(), cover = title.asMangaCover(),

View File

@ -3,8 +3,8 @@ package eu.kanade.presentation.category
import android.content.Context import android.content.Context
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import eu.kanade.domain.category.model.Category
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import tachiyomi.domain.category.model.Category
val Category.visualName: String val Category.visualName: String
@Composable @Composable

View File

@ -7,7 +7,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import eu.kanade.domain.category.model.Category
import eu.kanade.presentation.category.components.CategoryContent import eu.kanade.presentation.category.components.CategoryContent
import eu.kanade.presentation.category.components.CategoryFloatingActionButton import eu.kanade.presentation.category.components.CategoryFloatingActionButton
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
@ -18,6 +17,7 @@ import eu.kanade.presentation.util.plus
import eu.kanade.presentation.util.topSmallPaddingValues import eu.kanade.presentation.util.topSmallPaddingValues
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.category.CategoryScreenState import eu.kanade.tachiyomi.ui.category.CategoryScreenState
import tachiyomi.domain.category.model.Category
@Composable @Composable
fun CategoryScreen( fun CategoryScreen(

View File

@ -4,11 +4,12 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.MaterialTheme
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 eu.kanade.domain.category.model.Category
import eu.kanade.presentation.components.LazyColumn import eu.kanade.presentation.components.LazyColumn
import eu.kanade.presentation.util.padding
import tachiyomi.domain.category.model.Category
@Composable @Composable
fun CategoryContent( fun CategoryContent(
@ -23,7 +24,7 @@ fun CategoryContent(
LazyColumn( LazyColumn(
state = lazyListState, state = lazyListState,
contentPadding = paddingValues, contentPadding = paddingValues,
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
) { ) {
itemsIndexed( itemsIndexed(
items = categories, items = categories,

View File

@ -14,26 +14,32 @@ import androidx.compose.ui.Modifier
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.res.stringResource import androidx.compose.ui.res.stringResource
import eu.kanade.domain.category.model.Category
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import tachiyomi.domain.category.model.Category
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@Composable @Composable
fun CategoryCreateDialog( fun CategoryCreateDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
onCreate: (String) -> Unit, onCreate: (String) -> Unit,
categories: List<Category>,
) { ) {
var name by remember { mutableStateOf("") } var name by remember { mutableStateOf("") }
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
val nameAlreadyExists = remember(name) { categories.anyWithName(name) }
AlertDialog( AlertDialog(
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
confirmButton = { confirmButton = {
TextButton(onClick = { TextButton(
enabled = name.isNotEmpty() && !nameAlreadyExists,
onClick = {
onCreate(name) onCreate(name)
onDismissRequest() onDismissRequest()
},) { },
) {
Text(text = stringResource(R.string.action_add)) Text(text = stringResource(R.string.action_add))
} }
}, },
@ -47,13 +53,15 @@ fun CategoryCreateDialog(
}, },
text = { text = {
OutlinedTextField( OutlinedTextField(
modifier = Modifier modifier = Modifier.focusRequester(focusRequester),
.focusRequester(focusRequester),
value = name, value = name,
onValueChange = { name = it }, onValueChange = { name = it },
label = { label = { Text(text = stringResource(R.string.name)) },
Text(text = stringResource(R.string.name)) supportingText = {
val msgRes = if (name.isNotEmpty() && nameAlreadyExists) R.string.error_category_exists else R.string.information_required_plain
Text(text = stringResource(msgRes))
}, },
isError = name.isNotEmpty() && nameAlreadyExists,
singleLine = true, singleLine = true,
) )
}, },
@ -70,18 +78,25 @@ fun CategoryCreateDialog(
fun CategoryRenameDialog( fun CategoryRenameDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
onRename: (String) -> Unit, onRename: (String) -> Unit,
categories: List<Category>,
category: Category, category: Category,
) { ) {
var name by remember { mutableStateOf(category.name) } var name by remember { mutableStateOf(category.name) }
var valueHasChanged by remember { mutableStateOf(false) }
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
val nameAlreadyExists = remember(name) { categories.anyWithName(name) }
AlertDialog( AlertDialog(
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
confirmButton = { confirmButton = {
TextButton(onClick = { TextButton(
enabled = valueHasChanged && !nameAlreadyExists,
onClick = {
onRename(name) onRename(name)
onDismissRequest() onDismissRequest()
},) { },
) {
Text(text = stringResource(android.R.string.ok)) Text(text = stringResource(android.R.string.ok))
} }
}, },
@ -95,13 +110,18 @@ fun CategoryRenameDialog(
}, },
text = { text = {
OutlinedTextField( OutlinedTextField(
modifier = Modifier modifier = Modifier.focusRequester(focusRequester),
.focusRequester(focusRequester),
value = name, value = name,
onValueChange = { name = it }, onValueChange = {
label = { valueHasChanged = name != it
Text(text = stringResource(R.string.name)) name = it
}, },
label = { Text(text = stringResource(R.string.name)) },
supportingText = {
val msgRes = if (valueHasChanged && nameAlreadyExists) R.string.error_category_exists else R.string.information_required_plain
Text(text = stringResource(msgRes))
},
isError = valueHasChanged && nameAlreadyExists,
singleLine = true, singleLine = true,
) )
}, },
@ -143,3 +163,7 @@ fun CategoryDeleteDialog(
}, },
) )
} }
internal fun List<Category>.anyWithName(name: String): Boolean {
return any { name == it.name }
}

View File

@ -20,9 +20,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import eu.kanade.domain.category.model.Category
import eu.kanade.presentation.util.padding import eu.kanade.presentation.util.padding
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import tachiyomi.domain.category.model.Category
@Composable @Composable
fun CategoryListItem( fun CategoryListItem(

View File

@ -48,6 +48,8 @@ import eu.kanade.presentation.util.runOnEnterKeyPressed
import eu.kanade.presentation.util.secondaryItemAlpha import eu.kanade.presentation.util.secondaryItemAlpha
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
const val SEARCH_DEBOUNCE_MILLIS = 250L
@Composable @Composable
fun AppBar( fun AppBar(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,

View File

@ -22,10 +22,10 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import eu.kanade.core.prefs.CheckboxState import eu.kanade.core.prefs.CheckboxState
import eu.kanade.domain.category.model.Category
import eu.kanade.presentation.category.visualName import eu.kanade.presentation.category.visualName
import eu.kanade.presentation.util.padding import eu.kanade.presentation.util.padding
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import tachiyomi.domain.category.model.Category
@Composable @Composable
fun ChangeCategoryDialog( fun ChangeCategoryDialog(
@ -80,7 +80,7 @@ fun ChangeCategoryDialog(
) )
}, },
) { ) {
Text(text = stringResource(R.string.action_add)) Text(text = stringResource(android.R.string.ok))
} }
} }
}, },

View File

@ -62,7 +62,7 @@ private const val GridSelectedCoverAlpha = 0.76f
fun MangaCompactGridItem( fun MangaCompactGridItem(
isSelected: Boolean = false, isSelected: Boolean = false,
title: String? = null, title: String? = null,
coverData: eu.kanade.domain.manga.model.MangaCover, coverData: tachiyomi.domain.manga.model.MangaCover,
coverAlpha: Float = 1f, coverAlpha: Float = 1f,
coverBadgeStart: @Composable (RowScope.() -> Unit)? = null, coverBadgeStart: @Composable (RowScope.() -> Unit)? = null,
coverBadgeEnd: @Composable (RowScope.() -> Unit)? = null, coverBadgeEnd: @Composable (RowScope.() -> Unit)? = null,
@ -162,7 +162,7 @@ private fun BoxScope.CoverTextOverlay(
fun MangaComfortableGridItem( fun MangaComfortableGridItem(
isSelected: Boolean = false, isSelected: Boolean = false,
title: String, title: String,
coverData: eu.kanade.domain.manga.model.MangaCover, coverData: tachiyomi.domain.manga.model.MangaCover,
coverAlpha: Float = 1f, coverAlpha: Float = 1f,
coverBadgeStart: (@Composable RowScope.() -> Unit)? = null, coverBadgeStart: (@Composable RowScope.() -> Unit)? = null,
coverBadgeEnd: (@Composable RowScope.() -> Unit)? = null, coverBadgeEnd: (@Composable RowScope.() -> Unit)? = null,
@ -330,7 +330,7 @@ private fun Modifier.selectedOutline(
fun MangaListItem( fun MangaListItem(
isSelected: Boolean = false, isSelected: Boolean = false,
title: String, title: String,
coverData: eu.kanade.domain.manga.model.MangaCover, coverData: tachiyomi.domain.manga.model.MangaCover,
coverAlpha: Float = 1f, coverAlpha: Float = 1f,
badge: @Composable (RowScope.() -> Unit), badge: @Composable (RowScope.() -> Unit),
onLongClick: () -> Unit, onLongClick: () -> Unit,

View File

@ -44,6 +44,7 @@ fun DeleteLibraryMangaDialog(
}, },
confirmButton = { confirmButton = {
TextButton( TextButton(
enabled = list.any { it.isChecked },
onClick = { onClick = {
onDismissRequest() onDismissRequest()
onConfirm( onConfirm(
@ -63,10 +64,12 @@ fun DeleteLibraryMangaDialog(
list.forEach { state -> list.forEach { state ->
val onCheck = { val onCheck = {
val index = list.indexOf(state) val index = list.indexOf(state)
if (index != -1) {
val mutableList = list.toMutableList() val mutableList = list.toMutableList()
mutableList[index] = state.next() as CheckboxState.State<Int> mutableList[index] = state.next() as CheckboxState.State<Int>
list = mutableList.toList() list = mutableList.toList()
} }
}
Row( Row(
modifier = Modifier modifier = Modifier

View File

@ -9,14 +9,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.Source
@Composable @Composable
fun DuplicateMangaDialog( fun DuplicateMangaDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
onConfirm: () -> Unit, onConfirm: () -> Unit,
onOpenManga: () -> Unit, onOpenManga: () -> Unit,
duplicateFrom: Source,
) { ) {
AlertDialog( AlertDialog(
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
@ -46,12 +44,7 @@ fun DuplicateMangaDialog(
Text(text = stringResource(R.string.are_you_sure)) Text(text = stringResource(R.string.are_you_sure))
}, },
text = { text = {
Text( Text(text = stringResource(R.string.confirm_add_duplicate_manga))
text = stringResource(
id = R.string.confirm_manga_add_duplicate,
duplicateFrom.name,
),
)
}, },
) )
} }

View File

@ -27,6 +27,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.presentation.theme.TachiyomiTheme import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.presentation.util.ThemePreviews import eu.kanade.presentation.util.ThemePreviews
import eu.kanade.presentation.util.padding
import eu.kanade.presentation.util.secondaryItemAlpha import eu.kanade.presentation.util.secondaryItemAlpha
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import kotlin.random.Random import kotlin.random.Random
@ -79,7 +80,7 @@ fun EmptyScreen(
start = 24.dp, start = 24.dp,
end = 24.dp, end = 24.dp,
), ),
horizontalArrangement = Arrangement.spacedBy(space = 8.dp), horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
) { ) {
actions.forEach { actions.forEach {
ActionButton( ActionButton(

View File

@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.selection.selectableGroup import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationRailDefaults import androidx.compose.material3.NavigationRailDefaults
import androidx.compose.material3.contentColorFor import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -18,6 +19,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.presentation.util.padding
/** /**
* Center-aligned M3 Navigation rail * Center-aligned M3 Navigation rail
@ -44,14 +46,14 @@ fun NavigationRail(
.fillMaxHeight() .fillMaxHeight()
.windowInsetsPadding(windowInsets) .windowInsetsPadding(windowInsets)
.widthIn(min = 80.dp) .widthIn(min = 80.dp)
.padding(vertical = 4.dp) .padding(vertical = MaterialTheme.padding.tiny)
.selectableGroup(), .selectableGroup(),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(space = 4.dp, alignment = Alignment.CenterVertically), verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.tiny, alignment = Alignment.CenterVertically),
) { ) {
if (header != null) { if (header != null) {
header() header()
Spacer(Modifier.height(8.dp)) Spacer(Modifier.height(MaterialTheme.padding.small))
} }
content() content()
} }

View File

@ -240,13 +240,16 @@ private fun ScaffoldLayout(
) )
}.fastMap { it.measure(looseConstraints) } }.fastMap { it.measure(looseConstraints) }
val bottomBarHeight = bottomBarPlaceables.fastMaxBy { it.height }?.height val bottomBarHeight = bottomBarPlaceables
.fastMaxBy { it.height }
?.height
?.takeIf { it != 0 }
val fabOffsetFromBottom = fabPlacement?.let { val fabOffsetFromBottom = fabPlacement?.let {
max(bottomBarHeight ?: 0, bottomInset) + it.height + FabSpacing.roundToPx() max(bottomBarHeight ?: 0, bottomInset) + it.height + FabSpacing.roundToPx()
} }
val snackbarOffsetFromBottom = if (snackbarHeight != 0) { val snackbarOffsetFromBottom = if (snackbarHeight != 0) {
snackbarHeight + (fabOffsetFromBottom ?: bottomBarHeight ?: bottomInset) snackbarHeight + (fabOffsetFromBottom ?: max(bottomBarHeight ?: 0, bottomInset))
} else { } else {
0 0
} }

View File

@ -7,6 +7,7 @@ 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
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material.ContentAlpha
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDownward import androidx.compose.material.icons.filled.ArrowDownward
import androidx.compose.material.icons.filled.ArrowUpward import androidx.compose.material.icons.filled.ArrowUpward
@ -21,7 +22,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.domain.manga.model.TriStateFilter import tachiyomi.domain.manga.model.TriStateFilter
@Composable @Composable
fun TriStateItem( fun TriStateItem(
@ -46,6 +47,8 @@ fun TriStateItem(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(24.dp), horizontalArrangement = Arrangement.spacedBy(24.dp),
) { ) {
val stateAlpha = if (onClick != null) 1f else ContentAlpha.disabled
Icon( Icon(
imageVector = when (state) { imageVector = when (state) {
TriStateFilter.DISABLED -> Icons.Rounded.CheckBoxOutlineBlank TriStateFilter.DISABLED -> Icons.Rounded.CheckBoxOutlineBlank
@ -54,13 +57,17 @@ fun TriStateItem(
}, },
contentDescription = null, contentDescription = null,
tint = if (state == TriStateFilter.DISABLED) { tint = if (state == TriStateFilter.DISABLED) {
MaterialTheme.colorScheme.onSurfaceVariant MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = stateAlpha)
} else { } else {
MaterialTheme.colorScheme.primary when (onClick) {
null -> MaterialTheme.colorScheme.onSurface.copy(alpha = ContentAlpha.disabled)
else -> MaterialTheme.colorScheme.primary
}
}, },
) )
Text( Text(
text = label, text = label,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = stateAlpha),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
) )
} }

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