Compare commits

...

636 Commits

Author SHA1 Message Date
arkon
64c50c1283 Require Android 8+
Given that the next stable version of Chrome (120) will require Android 8+, it's
inevitable that the WebView functionality will gradually break. As always, newer
OS versions are recommended for better support with evolving Internet technologies.

According to https://apilevels.com/, Android 8+ still covers 93.7% of Android users.
2023-11-04 19:21:46 -04:00
arkon
4146c4c31d Ensure page indicator texts are centered
Maybe fixes #9976
2023-11-04 17:50:33 -04:00
arkon
69223df27c Move tracker binding logic to interactor 2023-11-04 17:05:38 -04:00
arkon
4b225a4ff1 Revert "Always save pages/covers in subfolders"
This reverts commit 8568d5d6c3.

Closes #10052
2023-11-04 16:46:02 -04:00
Weblate (bot)
4a2ee0b596 Translations update from Hosted Weblate (#10089)
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/fil/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hr/
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/ne/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/nl/
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/th/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/uk/
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: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
Co-authored-by: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: Blue <bluestuffish@gmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: Hiroshi <borlonjhayron1119@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: La prière <lapriere@users.noreply.hosted.weblate.org>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: Zero O <godarms2010@live.com>
Co-authored-by: abc0922001 <abc0922001@hotmail.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: winver <kirillstuzhuk@gmail.com>
2023-11-04 16:37:50 -04:00
arkon
8644d90bd4 Bump dependencies 2023-11-04 16:11:43 -04:00
Ota
f30ab56fd0 New alphabetical chapter sort (#10073)
* added alphabetical chapter sorting

* Deleted sort_by_alphabet and re-utilized action_sort_alpha

* Accidentally deleted wrong string. Now solved

* Accidentally deleted wrong string. Now solved

Deleted sort_by_source instead of sort_by_alphabet in strings.xml.
Now reverted.

* Alphabetical sorting now uses Collator

* Clean up repeated Collator instances

---------

Co-authored-by: arkon <eugcheung94@gmail.com>
2023-11-04 15:47:32 -04:00
Caleb Morris
5d91b77c93 Added library sort by mean Tracker score (#10005) 2023-11-04 15:31:59 -04:00
arkon
aca36f9625 Maybe fix foreign key error during some backup restores 2023-11-01 22:52:00 -04:00
arkon
d5e8c38075 Bump dependencies 2023-11-01 22:21:41 -04:00
AntsyLich
6d538db5f2 Show missing chapter count between two chapters in chapter list (#10096)
* Show missing chapter count between two chapters in chapter list

Closes #8460

* Fix crash

* Lint

* Review changes

* Lint
2023-11-01 22:18:19 -04:00
Eshlender
b3d7c92475 Text on tabs Overflow Ellipsis (#10095)
* Update TabbedDialog to TabbedScreen

* clean
2023-11-01 22:17:17 -04:00
Eshlender
d862d83511 Combining manga information into a function (#10093)
* Combining manga information into a function

* clean space

* indexes

* context

* clean

* textAlign for Tablet Mode
2023-11-01 09:01:38 -04:00
Howard Wu
8a1625ec79 buildDir deprecated, use layout.buildDirectory instead (#10097)
gradle/gradle#20210
gradle/gradle#24820
2023-10-31 22:14:31 -04:00
arkon
2ee895ee3c Use same icon as chapter list items to indicate downloaded chapter in reader transitions 2023-10-31 18:05:37 -04:00
arkon
7cf2ce2994 Handle Brotli-compressed responses 2023-10-31 18:03:07 -04:00
arkon
cb8ea5eab0 Add basic storage usage info to "Data and storage" settings screen 2023-10-29 18:18:56 -04:00
arkon
ce7bf396eb Don't include "app state" preferences in backups 2023-10-29 12:24:02 -04:00
arkon
1aa5222c99 Record time when last automatic backup was created
Closes #3474
2023-10-29 12:03:46 -04:00
arkon
298c49f3ab Abstract out library last updated timespan text
So we can reuse it for showing last automatic backup time.
2023-10-29 11:54:50 -04:00
arkon
ce5e10be95 Clean up chapter restoring logic a bit 2023-10-29 11:43:06 -04:00
arkon
64ad25d1b5 Make scrollbar slightly chonkier
Closes #9728
2023-10-28 22:41:35 -04:00
arkon
4868dd2d03 Try to ensure that reader page error message is removed if image is loaded
Maybe fixes #5687
2023-10-28 17:18:42 -04:00
arkon
443d56f69b Add option to flash white screen on page change in reader for e-ink displays
Closes #2123
2023-10-28 16:21:45 -04:00
Eshlender
7457a18aee Add icons for author and artist in MangaInfoHeader (#10079)
* Mark author and artist

* overall style

* Clean up spacing

---------

Co-authored-by: arkon <eugcheung94@gmail.com>
2023-10-28 15:46:10 -04:00
Weblate (bot)
d80ba2e807 Translations update from Hosted Weblate (#10043)
Weblate translations





















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/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/es_419/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fil/
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/ko/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ro/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ru/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/th/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/tr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hans/
Translation: Tachiyomi/Tachiyomi 0.x

Co-authored-by: Ahmed Sameh <as562384@gmail.com>
Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
Co-authored-by: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: Bicycle <evocatorediboscopietra@gmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: Jueon Park <bluegbgb@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Saft Octavian <saftoctavian@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: TheKingTermux <achmadmaulana0233@gmail.com>
Co-authored-by: Zero O <godarms2010@live.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: bapeey <luisrleccar@hotmail.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: orkan gökçe alaz aşina <examplehuman@outlook.com>
2023-10-28 15:35:23 -04:00
arkon
118d3b7fcc Add ability to reset chapter flags to defaults
Closes #10063
2023-10-28 15:28:39 -04:00
arkon
eed57b80be Replace AppUpdateService with a WorkManager job
Fixes #7773

Co-authored-by: Jays2Kings <Jays2Kings@users.noreply.github.com>
2023-10-27 15:45:18 -04:00
arkon
c46c39d4ae Rename "Backup and restore" settings screen to "Data and storage"
We can house more things in here in the future, like:
- A unified storage location setting (with scoped storage)
- Sync
- Disk usage info
2023-10-27 15:06:56 -04:00
arkon
d7d7a6d2fc Revert Compose update
Fixes #10069
2023-10-26 13:06:26 -04:00
AntsyLich
17b90d2491 Yeet app update download progress notification on complete (#10071) 2023-10-26 12:30:40 -04:00
arkon
9ecec5d468 Set saved image date modified value to current time
Fixes #10070
2023-10-26 08:35:51 -04:00
Seishirou101
0bdd3f79d4 Add info about problematic extensions to debug logs (#10059)
* add ext info to crashlog

* add unofficial to crashlog too

* update to have header include unofficial too

* after ktlintFormat

* Clean up debug info output

---------

Co-authored-by: arkon <eugcheung94@gmail.com>
2023-10-25 22:13:46 -04:00
arkon
7dccde0930 Merge branch 'patch' 2023-10-25 12:06:41 -04:00
arkon
8057f067b9 Handle reader app bar insets in Compose 2023-10-25 09:21:04 -04:00
arkon
548f7f415a Avoid opening blobs as webpages
Fixes #10060
2023-10-25 09:18:59 -04:00
arkon
d9c0b1ce7d Migrate reader low brightness overlay to Compose 2023-10-24 22:21:17 -04:00
arkon
0a0b686119 Add Compose previews for reading and orientation mode dialogs 2023-10-24 22:16:03 -04:00
arkon
092d930175 Update default user agent string 2023-10-24 21:58:53 -04:00
Caleb Morris
3b7ed9bc6d Detached permission request from DiskUtil (#10051)
Being an extension on the DiskUtil couples to a class at a different abstraction
 layer without really needing to. Created PermissionRequestHelper as a place to
 put similar requests if needed in the future.
2023-10-22 19:58:16 -04:00
arkon
012854dd1e Update Voyager 2023-10-22 15:54:31 -04:00
Caleb Morris
6d1e520c6c [dev QoL] Added AndroidStudio previews for [presentation.track] namespace (#10022)
* Created DummyTracker for use in tests and presentation previews

* Added previews for TrackerSearch

* Added previews for TrackLogoIcon

* Added preview for TrackInfoDialogSelector

* Added previews for TrackInfoDialogHome
2023-10-22 09:30:34 -04:00
Gabriel Donadel Dall'Agnol
dcc3141080 Fix README.md typo (#10047) 2023-10-21 22:00:05 -04:00
arkon
7326598475 Minor cleanup to ResolvableSource 2023-10-21 21:50:53 -04:00
renovate[bot]
fcba2306e9 Update dependency ch.acra:acra-http to v5.11.3 (#10046)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-21 21:44:54 -04:00
Joshua Owolabi
f84868a264 Allow extensions to open manga or chapter by URL (#9996)
* open manga and chapter using URL

* removing unnnecessary logs

* Resolving comments

* Resolving comments
2023-10-21 21:44:43 -04:00
Caleb Morris
15423bfc84 Changed data-mappers to use function format (#10045)
The lambda-format was really confusing to read and keep which anonymous data
 item was corresponding to which field. Now it's directly inspectable in the IDE
2023-10-21 21:42:09 -04:00
arkon
8e4cedf173 Update Compose 2023-10-21 18:09:30 -04:00
arkon
19965e0bdb Update jsoup 2023-10-21 18:09:21 -04:00
Caleb Morris
3a35c13575 Decoupled Tracker Interface (#10042)
Split out Tracker to interface and created simple dummy instance for previews
2023-10-21 17:10:34 -04:00
arkon
489d22720a Refresh tracks before updating progress
Closes #1652

Also removes the ability to trigger refreshes for the entire library or
as part of a library update as it should no longer be needed. Opening
the tracking sheet already refreshes the data too, so stale data is
irrelevant there.

Also closes #4775 since it would no longer be relevant.
2023-10-21 10:20:35 -04:00
Weblate (bot)
e1b3345b94 Translations update from Hosted Weblate (#9992)
Weblate translations




























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/cv/
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/es_419/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fil/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hr/
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/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ne/
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/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ru/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/sv/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/th/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/vi/
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: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
Co-authored-by: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: C201 <derasetad@gmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Diego D <papitas30gameryt@gmail.com>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: Esttven <m4ttesteban@gmail.com>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: ItsPoofy <tuanminh8688@gmail.com>
Co-authored-by: Jendrej <ejjendrej@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Maristella Kalil Victoriano Silva <maris.victoriano@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Shjosan <shjosan@kakmix.co>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: Vetle Ledaal <vetle.ledaal@gmail.com>
Co-authored-by: abc0922001 <abc0922001@hotmail.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: zhongfly <icesshadows@gmail.com>
2023-10-21 09:44:05 -04:00
arkon
c53172265b Consistent labeled checkbox composable 2023-10-21 09:42:12 -04:00
arkon
8626a55fe4 Make text clickable when removing item from tracker 2023-10-18 22:50:09 -04:00
arkon
1302461518 Bump dependencies 2023-10-18 22:49:52 -04:00
Vlasov Roman
8f3681d79f Change Shikimori domain from ".me" to ".one" (#10027) 2023-10-18 22:49:45 -04:00
arkon
c4ce3dd46f Update background job preferences once settings are restored
Co-authored-by: Jays2Kings <Jays2Kings@users.noreply.github.com>
2023-10-17 22:32:13 -04:00
arkon
22df12a680 Change crash log info to just have actual WebView version 2023-10-17 22:30:55 -04:00
arkon
e572abb041 Show an error if backup file URI isn't returned to app when attempting restore
Related to #10028
2023-10-17 22:25:05 -04:00
Ivan Iskandar
ea99d77fda ExtensionLoader: Fix incorrect ext file deletion (#10026)
Ref c492efcb31
2023-10-15 22:05:35 -04:00
Prasidh Gopal Anchan
2bf77f1d81 Fix checkboxes not working after scrolling in filter sheet (#10023)
Fixed an issue where CheckBox was not being checked after scrolling in the Filter tab
2023-10-15 17:10:01 -04:00
arkon
f79f0a7e97 Add haptics to SliderItem 2023-10-14 22:47:02 -04:00
arkon
82a9d36df7 Minor cleanup 2023-10-14 22:46:51 -04:00
Caleb Morris
447bcb28ef [dev QoL] Added AndroidStudio previews for [presentation.history] namespace (#10012)
* Added display preview for HistoryDialogs

* Added preview with provider for each branch for HistoryItem

* Added previews for HistoryScreen

Created in-memory preferences construct for when its needed at top-level injection

* Fixed ktlint violations
2023-10-14 22:23:11 -04:00
arkon
0be7ac5871 Bump dependencies 2023-10-14 22:22:46 -04:00
arkon
d18022c259 Migrate top reader app bar to Compose 2023-10-14 12:30:17 -04:00
arkon
5619a4c0d9 Remove remaining usages of platform-provided strings 2023-10-14 11:11:05 -04:00
arkon
8a7bbfddda Add info about formatting task in CONTRIBUTING.md [skip ci] 2023-10-14 11:09:11 -04:00
Ivan Iskandar
0026f96fad MangaSummary: Refactor to not use SubcomposeLayout (#10008) 2023-10-14 10:52:04 -04:00
Ivan Iskandar
c492efcb31 ExtensionLoader: Set read-only to private extension files (#10007) 2023-10-12 23:04:40 -04:00
arkon
c386d375de Tweak Cloudflare help message in WebView screen
Catches pages like what Shinigami is currently showing.
Also adjusts the banner to make it look more like part of the top AppBar so it
looks less like part of the webpage.
2023-10-12 22:56:33 -04:00
arkon
540fb1bb7c Use AppBar abstraction in more places 2023-10-12 22:49:21 -04:00
arkon
81448f5d01 Minor cleanup 2023-10-12 22:43:03 -04:00
arkon
7c01201055 Refactor reader bottom bar to presentation package 2023-10-12 22:42:49 -04:00
arkon
90d3dd2242 Use relative touch positions for reader tap events
Fixes #10004
2023-10-12 22:15:30 -04:00
arkon
97b4d1f13d Use Compose to animate bottom reader menu bars 2023-10-09 22:27:46 -04:00
arkon
79b37df647 Automatically convert details.json to ComicInfo.xml for local series
Originally contributed as #9603
I ended up coming back to this since it seems like a reasonable way to migrate
users in the short-medium term. We'll remove this in a later release.

Co-authored-by: Shamicen <Shamicen@users.noreply.github.com>
2023-10-08 22:27:06 -04:00
arkon
b7d282235d Remove duplicated logic for binding enhanced trackers 2023-10-08 19:19:04 -04:00
Pauline
77ebc362f6 Add button to reorder categories alphabetically (#9369)
Closes #6459

Co-authored-by: arkon <arkon@users.noreply.github.com>
2023-10-08 18:55:15 -04:00
arkon
8568d5d6c3 Always save pages/covers in subfolders
Ensures that pages and covers are grouped together.
2023-10-08 17:04:02 -04:00
arkon
7ed99fbbd6 Account for skipped entries when showing large updates warning
Closes #6159
2023-10-08 16:40:17 -04:00
arkon
94cba9324c Remove beta webtoon viewer split page
This had a bunch of issues around split pages not showing up properly so things
end up appearing to be missing while reading.
It'd be more worthwhile redoing the reader viewers than trying to get this to work
properly. It'd be better to just enable the split pages on download instead.

Closes #8433
2023-10-08 16:39:45 -04:00
arkon
6dab94a937 Move backup restoring functions from BackupManager to BackupRestorer 2023-10-08 16:11:45 -04:00
arkon
0f42b9f154 Add source preferences to backups
Closes #1857

Co-authored-by: jmir1 <jmir1@users.noreply.github.com>
2023-10-08 16:02:03 -04:00
arkon
730f3a6e52 Exclude tracker credentials in backups 2023-10-08 11:07:42 -04:00
arkon
72024aa44a Add app settings to backups
This should be compatible with Aniyomi's implementation.
Related to #1857

Co-authored-by: jmir1 <jmir1@users.noreply.github.com>
2023-10-08 10:41:20 -04:00
renovate[bot]
9c688b08c0 Update dependency com.google.android.material:material to v1.10.0 (#9991)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-07 23:29:14 -04:00
renovate[bot]
c66a4fa7a7 Update dependency androidx.benchmark:benchmark-macro-junit4 to v1.2.0-rc02 (#9990)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-07 23:25:11 -04:00
arkon
e47f4cc177 Specify broadcast receiver export flags
Really only useful once we target Android 14, but doesn't hurt to do it now.
2023-10-07 10:03:45 -04:00
Weblate (bot)
6462472d16 Translations update from Hosted Weblate (#9957)
Weblate translations























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/es_419/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fil/
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/pl/
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/th/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/uk/
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 <14254807+alessandrojean@users.noreply.github.com>
Co-authored-by: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: Dan <jonweblin2205@protonmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: Jendrej <ejjendrej@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Milan Šalka <salka.milan@googlemail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: Yesaya Kefin Irli <yesaya.kevin99@gmail.com>
Co-authored-by: abc0922001 <abc0922001@hotmail.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: bapeey <luisrleccar@hotmail.com>
2023-10-07 09:44:30 -04:00
LooKeR
78aa50bb35 Reduce recomposition of MangaHeader (#9985)
* Reduce recomposition of MangaHeader

* Reuse `Modifier` for `Tags`

Reference:
https://developer.android.com/jetpack/compose/modifiers#reusing-modifiers

* Don't recalculate Read State on recomposition

* Fix Linting issue

* Optimize chapter state calculations
2023-10-06 18:24:43 -04:00
arkon
7f0f67d752 Update social media icons 2023-10-05 09:33:34 -04:00
arkon
df332860b8 Bump dependencies 2023-10-04 22:28:50 -04:00
renovate[bot]
8a8afa46e9 Update aboutlib.version to v10.9.1 (#9971)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-01 10:07:24 -04:00
Ivan Iskandar
509bee0563 Add project icon (#9972) 2023-10-01 10:07:14 -04:00
arkon
afb1ee2200 Rename new method in ConfigurableSource to get preferences
Maybe fixes #9969
2023-09-30 14:36:06 -04:00
arkon
66a938779d Update SSIV and image-decoder, except with partially revert to non-broken HEIF/AVIF support 2023-09-27 22:54:03 -04:00
arkon
ed506f8495 Update SSIV and image-decoder
Includes updated libwebp for CVE-2023-5129
2023-09-27 22:22:04 -04:00
arkon
c8e226acb2 Tracker-related cleanup 2023-09-25 23:32:39 -04:00
arkon
86edce0d87 Bring back relative timestamp translations 2023-09-24 17:26:12 -04:00
Weblate (bot)
4e69bf993a Translations update from Hosted Weblate (#9919)
Weblate translations























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/cv/
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/fil/
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/it/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ja/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ko/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ne/
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/sv/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/th/
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/zh_Hant/
Translation: Tachiyomi/Tachiyomi 0.x

Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
Co-authored-by: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: C201 <derasetad@gmail.com>
Co-authored-by: Dan <jonweblin2205@protonmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Paavalen Lingachetti <p.lingachetti@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Shjosan <shjosan@kakmix.co>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: Vetle Ledaal <vetle.ledaal@gmail.com>
Co-authored-by: abc0922001 <abc0922001@hotmail.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: orkan gökçe alaz aşina <examplehuman@outlook.com>
Co-authored-by: sarami <ppp821203@gmail.com>
2023-09-24 17:22:45 -04:00
arkon
56d2464870 Bring back simplified relative timestamp setting
Except now it's just an on/off toggle for relative up to a week.
2023-09-24 17:18:10 -04:00
arkon
5de72b7d32 Bump dependencies 2023-09-23 12:15:28 -04:00
arkon
de92b1351f Add WebView-based user agent string to debug info
Could probably use this when choosing a user agent later on.
2023-09-22 16:42:04 -04:00
arkon
77a8a4229c Fix duplicate files being created when saving pages on Android 10+ with separate folders setting enabled
Fixes #9943
2023-09-22 16:16:23 -04:00
arkon
d4290f6f59 Fix hide entries in library setting causing browse to not load
Fixes #9924
2023-09-20 23:19:00 -04:00
arkon
b08d604d2a Consistently use absolute date strings everywhere
Closes #9781
2023-09-20 22:49:15 -04:00
Soitora
9e04f14a7b Run Netlify Build Hook after Release (#9937)
* Run Netlify Build Hook after Release

* Add if statement

* Move if statement to job level instead of step
2023-09-17 12:08:12 -04:00
arkon
6663abebaf Clean up fetch interval tests a bit
Also limit the dates we look at to most recent 10 distinct dates only. Closes #9930
2023-09-17 12:06:17 -04:00
arkon
e5f83d0c6e Fix track search item not filling width if content is too short 2023-09-17 11:03:39 -04:00
renovate[bot]
3ad7add3b5 Update dependency io.github.fornewid:material-motion-compose-core to v1.0.7 (#9938)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-16 23:06:10 -04:00
renovate[bot]
66aacade9a Update dependency com.google.gms:google-services to v4.4.0 (#9940)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-16 23:02:19 -04:00
renovate[bot]
fe3a710ed0 Update xml.serialization.version to v0.86.2 (#9939)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-16 23:01:03 -04:00
arkon
f9754f4f58 Fix cut off labels in reader sheet toggles 2023-09-11 18:39:36 -04:00
arkon
8824c7dbe3 Tweak reading mode and orientation sheet designs 2023-09-10 22:36:57 -04:00
arkon
ccc9a5a052 Update website links 2023-09-10 18:16:53 -04:00
Soitora
f5e0cee36c Change website URLs to reflect changes (#9916)
Change website URLs
2023-09-10 18:11:00 -04:00
Weblate (bot)
36f1e0e476 Translations update from Hosted Weblate (#9904)
Weblate translations





























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/cv/
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/fil/
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/lv/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ms/
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_BR/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ru/
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/th/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/tr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/vi/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hant/
Translation: Tachiyomi/Tachiyomi 0.x

Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
Co-authored-by: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: Astrid <github@astrid.exposed>
Co-authored-by: C201 <derasetad@gmail.com>
Co-authored-by: Clxff H3r4ld0 <123844876+clxf12@users.noreply.github.com>
Co-authored-by: DarKCroX <DarKCroX@users.noreply.hosted.weblate.org>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Druvvaldis <druvvaldisr@gmail.com>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: Jozef Hollý <j2.00ghz@gmail.com>
Co-authored-by: Karuto <nguyenthaison609@outlook.com>
Co-authored-by: Luna Jernberg <droidbittin@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Paavalen Lingachetti <p.lingachetti@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Stefan Rackov <stfnrckv@pm.me>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: ZiomaleQ <r.partyka30@gmail.com>
Co-authored-by: abc0922001 <abc0922001@hotmail.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: orkan gökçe alaz aşina <examplehuman@outlook.com>
Co-authored-by: sebastians17 <sebastians117.ss@gmail.com>
2023-09-09 14:51:54 -04:00
arkon
2dd2db7225 Update to Kotlin 1.9.10 2023-09-09 14:49:04 -04:00
Joshua
3d0e750519 [Download Queue] Move series to bottom (#9918)
Added item in download queue page to move series to bottom
2023-09-09 14:01:24 -04:00
arkon
26c5d761da Add more replacement suspend functions for source APIs
These are basically 1-to-1 replacements for the existing RxJava APIs.
This will make the initial migration off of RxJava simpler. We'll
revisit the actual call flows in followup versions of the API.
2023-09-08 22:58:46 -04:00
arkon
1668be8587 Remove old FastScroller
Not sure if this will return to the download queue screen, you really
shouldn't be downloading a ton of stuff at once anyway?
2023-09-08 22:30:13 -04:00
arkon
86a3fc77c6 Bump dependencies 2023-09-07 22:23:10 -04:00
arkon
cc018cee18 Change backup file names
We use the application ID now to ensure uniqueness if the same folder is selected
between different app versions/forks. This will make more sense once storage
settings are unified to a single location.

Also changes the file extension while we're at it so people stop accidentally
ungzipping it.
2023-09-07 22:15:50 -04:00
renovate[bot]
d9d143e6be Update dependency io.kotest:kotest-assertions-core to v5.7.1 (#9905)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-03 10:54:05 -04:00
arkon
3f0db60a99 Minor updates 2023-09-03 10:02:04 -04:00
arkon
87f3d4bd05 Use app name in biometric unlock dialog
Mostly for forks to show the right name.
2023-09-03 10:01:47 -04:00
renovate[bot]
5c3d655d9e Update dependency io.kotest:kotest-assertions-core to v5.7.0 (#9901)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-02 22:11:31 -04:00
renovate[bot]
66b175a3c8 Update dependency ch.acra:acra-http to v5.11.2 (#9900)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-02 22:11:25 -04:00
Weblate (bot)
3cd3f45c8a Translations update from Hosted Weblate (#9860)
Weblate translations






















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/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/es_419/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/he/
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/ms/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ne/
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/sv/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/vi/
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: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
Co-authored-by: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: Astrid <github@astrid.exposed>
Co-authored-by: DarKCroX <DarKCroX@users.noreply.hosted.weblate.org>
Co-authored-by: DatTran MLL <tranthanhdat1142003@gmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: Luna Jernberg <droidbittin@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: TheKingTermux <achmadmaulana0233@gmail.com>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: Vetle Ledaal <vetle.ledaal@gmail.com>
Co-authored-by: bapeey <luisrleccar@hotmail.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: rzvsrh <rzvsrh333@gmail.com>
Co-authored-by: stevenlele <stevenlele@outlook.com>
2023-09-02 09:39:12 -04:00
Ivan Iskandar
816d7815e9 "Updates" widget for Galaxy Z Flip5 cover screen (#9892) 2023-09-02 09:37:25 -04:00
arkon
dbc7fe4d54 Update linting task in action workflows 2023-09-01 23:09:40 -04:00
arkon
d29b7c4e57 Switch to different ktlint plugin
Should be better at incremental builds.
To format, run `./gradlew ktlintFormat`.
2023-09-01 23:02:18 -04:00
arkon
772db51593 Bump dependencies 2023-09-01 22:47:42 -04:00
arkon
87530f506e Limit amount of updates loaded for widget
Probably fixes #9868
2023-08-27 22:05:52 -04:00
arkon
98d6ce2eaf Refactor some tracking-related logic 2023-08-27 10:41:58 -04:00
arkon
7644d7c31e Update kotlinx.serialization 2023-08-27 10:11:31 -04:00
arkon
dde2f42138 Refactor some tracking-related logic 2023-08-26 18:30:17 -04:00
arkon
6922792ad1 Add more user-friendly network-related exception messages 2023-08-26 17:45:26 -04:00
arkon
f32243899d Use default non-final resource IDs behavior 2023-08-26 10:32:53 -04:00
arkon
13dc54df70 Remove unused rxandroid dependency 2023-08-26 10:30:26 -04:00
arkon
6d9a8a30e9 Add ResolvableSource interface for potentially opening entries directly based on some URI via a share intent
Implemented as an intermediate step in the existing Global Search share intent workflow.
If any source manages to resolve the URI (e.g., a URL, a slug, etc.), the resolved SManga entry
is directly opened. If nothing gets resolved, continue to a Global Search.
2023-08-25 22:25:00 -04:00
arkon
2bf263e301 Revert Compose upgrade
Caused weird rendering issues in MangaScreen
2023-08-25 21:57:22 -04:00
arkon
c06beac660 Better ignore irrelevant files when indexing downloads 2023-08-24 22:41:22 -04:00
arkon
74f74eef56 Don't run automatic backup or library update jobs if battery is low 2023-08-24 22:25:29 -04:00
arkon
3aafec482c Bump dependencies 2023-08-24 22:18:30 -04:00
Alessandro Jean
ed80ac3154 Replace mentions with links to profiles in the release information (#9859)
Replace mentions with links in the release information.
2023-08-24 22:04:26 -04:00
Alessandro Jean
eeeaae4570 Only set the dialog title if not already set in the extension (#9858)
Only set the dialog title if not already set in the extension.
2023-08-19 16:47:20 -04:00
arkon
d1c956401c Add documentation for HttpException
Corresponds with https://github.com/tachiyomiorg/extensions-lib/pull/12
2023-08-18 22:28:14 -04:00
arkon
1be7949275 Upgrade to Gradle 8.3 2023-08-18 22:27:47 -04:00
Weblate (bot)
5572b28d01 Translations update from Hosted Weblate (#9808)
Weblate translations






























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/cv/
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/eu/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fil/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hu/
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/ne/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/nl/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ro/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ru/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/sa/
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/th/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/uk/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/vi/
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: 2227975312 <2227975312@qq.com>
Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
Co-authored-by: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: Astrid <github@astrid.exposed>
Co-authored-by: Bashmak <mrwho.vz@gmail.com>
Co-authored-by: C201 <derasetad@gmail.com>
Co-authored-by: Dan <jonweblin2205@protonmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Garutmaan Garuda <garutmaangaruda@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: K. Sz. Bence <tudi20@protonmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Reza Almanda <rezaalmanda27@gmail.com>
Co-authored-by: Shjosan <shjosan@kakmix.co>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: Unai <uesandi@gmail.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: arkon <eugcheung94@gmail.com>
Co-authored-by: f0roots <f0rootss@gmail.com>
Co-authored-by: torchlight <sima142222@gmail.com>
Co-authored-by: xconkhi9x <bighih2@gmail.com>
2023-08-18 09:13:53 -04:00
arkon
4e68b62881 Minor cleanup 2023-08-16 23:10:28 -04:00
arkon
4e31e6a2fa Upgrade to AGP 8.1.0 2023-08-16 19:00:54 -04:00
arkon
bc692ebfc6 Bump dependencies 2023-08-15 22:19:07 -04:00
AntsyLich
96f6a5abc2 [skip ci] Add cloudflare autoclose to issue_moderator.yml (#9841) 2023-08-13 10:05:19 -04:00
Alessandro Jean
3411ac40c0 Make source ID generation function reusable to extensions (#9836)
* Make source ID generation function reusable to extensions.

* Add parameters and return documentation.
2023-08-11 22:29:56 -04:00
arkon
8a6a104987 Set tracker finished date when manually updating to last chapter
Closes #9834
Individual tracker implementations already handle setting it too on update.
2023-08-11 09:30:35 -04:00
arkon
efa7a3a167 Update Compose
Also remove workaround for preventing keyboard showing up for SelectItems.
2023-08-10 22:45:58 -04:00
arkon
67bc81ebde Bump dependencies 2023-08-10 22:28:28 -04:00
arkon
0a3ce8ebe4 Clean up SetFetchIntervalTest 2023-08-06 22:34:31 -04:00
arkon
3ebf39bd55 Minor reader cleanup 2023-08-06 22:27:45 -04:00
Mekanik
8f395d98e7 Make some error messages localizable (#9811)
* Make error message of 3 exceptions localizable.

* Revert unnecessary file handle exception change.
2023-08-06 09:50:43 -04:00
Mekanik
26b3eb696c Fix missing inversion in tap zones help overlay. (#9812) 2023-08-05 18:36:19 -04:00
Ivan Iskandar
627f07408e Add private extension install method (#9710)
* Add private extension install method

Private extensions are put inside private data directory of the running app, so
this kind of extensions can only be used by the running app and not shared with
other apps.

One limitation of private extension is the lack of deeplink handlers (if there's
any) since the extension APK is not installed to the system.

When both kinds of extensions are installed with a same package name, shared
extension (the one installed to the system) will be used unless the version
codes are different. In that case the one with higher version code will be used.

* update
2023-08-05 12:15:52 -04:00
arkon
7146913c71 Bump dependencies 2023-08-05 12:12:17 -04:00
arkon
39c6bcccd8 Consider local manga as downloaded when filtering in reader
Fixes #9801
2023-08-05 12:01:17 -04:00
arkon
6259bbaa5e Always include bound trackers when migrating/copying 2023-08-05 11:54:34 -04:00
Weblate (bot)
cb4b8ac0dc Translations update from Hosted Weblate (#9775)
Weblate translations























Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/
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/es/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fil/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hi/
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/ko/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ne/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pl/
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/sv/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/th/
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: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: Jendrej <ejjendrej@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Reza Almanda <rezaalmanda27@gmail.com>
Co-authored-by: Shjosan <shjosan@kakmix.co>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: THE_LEGEND <the.legend9285+weblate@gmail.com>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: Vetle Ledaal <vetle.ledaal@gmail.com>
Co-authored-by: abc0922001 <abc0922001@hotmail.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: jinu147 <nesqea20@gmail.com>
Co-authored-by: stevenlele <stevenlele@outlook.com>
2023-08-05 11:54:27 -04:00
Mekanik
4b7acdb022 Fix migration flags usage (incorrect defaults and copy mode) (#9805)
* Fix migration flags usage (incorect defaults and copy mode)

* Remove unused logcat import left from testing.
2023-08-05 11:49:22 -04:00
Ivan Iskandar
af0fdfa3b7 ReaderViewModel: Fix saved state (#9807)
Also save page index
2023-08-05 09:47:02 -04:00
Quang Kieu
d874f20362 [Hotfix] Fix bug of not fetch update if manual library refresh as fetch period have lower limit (#9806)
Fix bug of not fetch update if manual library refresh, no auto

If somehow manga missed check period, we would not give new next update cycle and it would forever left behind
2023-08-05 09:07:00 -04:00
arkon
8680accd8e Migrate bottom reader menu to Compose 2023-08-04 18:05:02 -04:00
arkon
7308090288 Migrate reader shortcut menus to Compose
Contents' UIs should probably be improved, but that can happen separately.
2023-08-04 17:34:08 -04:00
arkon
400ca48456 Remove unnecessary profileable flag in manifest 2023-08-04 17:12:11 -04:00
Alessandro Jean
9b6567f5e4 Add support to kotlin.time APIs in the rate limit interceptor (#9797)
* Add support to kotlin.time APIs in the rate limit interceptor.

* Add a missing line break in the doc.

* Move the specific host to the same file.

* Add kotlin.time rule to Proguard and remove specific host rule.

* Mark the old version as deprecated and address review.

* Remove unused import.

* Remove yet another unused import.
2023-08-04 17:11:43 -04:00
arkon
7798186c32 Drop support for extension-lib 1.3 2023-08-04 10:35:57 -04:00
arkon
9dc66c7c8d Combine tracking OAuth login activities 2023-08-04 10:35:47 -04:00
arkon
10b0ef9b6d Dismiss extension update notification if all updates installed 2023-08-02 18:00:06 -04:00
arkon
81cd765543 More refactoring of expected next update logic 2023-07-30 19:13:16 -04:00
arkon
c9a1bd86b5 Refactor some Screens to be classes
Not really much point in keeping these as singletons.
Hopefully allows for these to be GC-ed after closing them.
2023-07-30 19:13:16 -04:00
Alessandro Jean
dfbbbadfac Show ellipsis in longer OSS library names (#9780)
Show ellipsis in longer OSS library names.
2023-07-30 17:39:37 -04:00
arkon
0f21d16263 Minor cleanup 2023-07-30 10:08:51 -04:00
arkon
d65f9c2916 Revert to AGP 8.0.2
Related to #9774
2023-07-29 23:26:00 -04:00
arkon
5718983f41 Update benchmarking dependencies 2023-07-29 22:48:56 -04:00
arkon
f7b335e4fb Set useDefaultDebugSigningConfigForProfileableBuildtypes flag
Related to #9774
2023-07-29 19:47:54 -04:00
arkon
aa6937baf2 Disable profiling on preview builds
Related to #9774
2023-07-29 18:52:05 -04:00
arkon
59f7d2273f Fix unit tests 2023-07-29 16:26:51 -04:00
arkon
cd91ea9b77 Remove need for SQLDelight primitive adapters 2023-07-29 16:14:23 -04:00
arkon
6a558ad119 Upgrade to SQLDelight 2 2023-07-29 15:32:09 -04:00
Hosted Weblate
f5936e9456 Weblate translations
Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
Co-authored-by: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: Dan <jonweblin2205@protonmail.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: FateXBlood <zecrofelix@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: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Muhammad Abdul Aziz Al-Ghofari <muhammadabdulazizalghofari@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: TheKingTermux <achmadmaulana0233@gmail.com>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: Vetle Ledaal <vetle.ledaal@gmail.com>
Co-authored-by: abc0922001 <abc0922001@hotmail.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: bapeey <luisrleccar@hotmail.com>
Co-authored-by: expertjun <jtrobin@naver.com>
Co-authored-by: hankskyjames777 <iamjuanz30312@gmail.com>
Co-authored-by: jinu147 <nesqea20@gmail.com>
Co-authored-by: orkan gökçe alaz aşina <examplehuman@outlook.com>
Co-authored-by: stevenlele <stevenlele@outlook.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/ceb/
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/es_419/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fil/
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/ko/
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/ne/
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/th/
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/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hant/
Translation: Tachiyomi/Tachiyomi 0.x
2023-07-29 14:40:19 -04:00
Weblate (bot)
9df351da0a Translations update from Hosted Weblate (#9713)
Weblate translations





























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/ceb/
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/es_419/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fil/
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/ko/
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/ne/
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/th/
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/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hant/
Translation: Tachiyomi/Tachiyomi 0.x

Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
Co-authored-by: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: Dan <jonweblin2205@protonmail.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: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Muhammad Abdul Aziz Al-Ghofari <muhammadabdulazizalghofari@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: TheKingTermux <achmadmaulana0233@gmail.com>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: Vetle Ledaal <vetle.ledaal@gmail.com>
Co-authored-by: abc0922001 <abc0922001@hotmail.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: bapeey <luisrleccar@hotmail.com>
Co-authored-by: expertjun <jtrobin@naver.com>
Co-authored-by: hankskyjames777 <iamjuanz30312@gmail.com>
Co-authored-by: jinu147 <nesqea20@gmail.com>
Co-authored-by: orkan gökçe alaz aşina <examplehuman@outlook.com>
Co-authored-by: stevenlele <stevenlele@outlook.com>
2023-07-29 14:35:37 -04:00
arkon
90325d48aa Subscribe to download queue state changes in UpdatesScreen 2023-07-29 14:26:36 -04:00
stevenyomi
e2abf283fe Don't show future timestamps as Recently (#9773) 2023-07-29 14:09:08 -04:00
arkon
db788d519d Avoid badly wrapped buttons in DuplicateMangaDialog
Fixes #9767
2023-07-29 12:14:11 -04:00
arkon
f3e9d5f346 Show feedback message when downloads index manually invalidated
Closes #9768
2023-07-29 12:09:00 -04:00
arkon
fd30c0adcd Avoid showing duplicate entry dialog for same entry
Fixes #9772
2023-07-29 12:07:13 -04:00
arkon
3ad4f1114a Cleanup related to fetch interval display 2023-07-29 10:29:53 -04:00
arkon
fe90546821 Remove relative timestamps setting 2023-07-29 10:03:16 -04:00
arkon
3892c4caac Minor cleanup 2023-07-29 09:51:51 -04:00
Quang Kieu
cb639f4e90 Update Manga in Expected Period (#5734)
* Add Predict Interval Test

* Get mangas next update and interval in library update

* Get next update and interval in backup restore

* Display and set intervals, nextUpdate in Manga Info

* Move logic function to MangeScreen and InfoHeader

Update per suggestion

---------

Co-authored-by: arkon <arkon@users.noreply.github.com>
2023-07-28 23:10:02 -04:00
arkon
6d69caf59e Show help banner when Cloudflare captcha page is shown in WebView 2023-07-28 23:09:52 -04:00
arkon
cdc1c5efa3 Better handle saving animated drawables 2023-07-26 23:26:58 -04:00
arkon
77bfd0c099 Don't attempt to show non-bitmap image in notification when saving
Fixes #9758
2023-07-26 23:09:33 -04:00
arkon
8ff0c9d61a Allow more flexible custom preference composables 2023-07-26 22:57:15 -04:00
arkon
b6620434b3 Bump dependencies 2023-07-26 22:56:49 -04:00
arkon
abae9bf37d Minor cleanup 2023-07-23 20:03:37 -04:00
arkon
2556e9f08c Refactor duplicate chapter number formatters 2023-07-23 18:09:08 -04:00
renovate[bot]
7aa172c512 Update richtext to v0.17.0 (#9748)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-23 16:34:28 -04:00
renovate[bot]
81cf232bcb Update dependency org.junit.jupiter:junit-jupiter to v5.10.0 (#9749)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-23 16:34:14 -04:00
arkon
ee26d6dffd Remove some uses of color resources 2023-07-22 18:56:45 -04:00
KaiserBh
7b2764e8f7 Refactor backup and restore to support cross device sync. (#9699)
* refactor: backup and restore to support cross device sync.

* chore: Updated string resources

* refactor: change function name.

* refactor: Use URI SyncHolder.kt not needed anymore.
2023-07-22 18:39:56 -04:00
arkon
46e3b9e40d Use previously updated track item when binding start date
Fixes #9743
2023-07-22 09:32:17 -04:00
Jobobby04
8d00ff1b40 Fix fresh database installs
(cherry picked from commit 70bbede29e0f995436d86f50ab14ace837839a6c)
2023-07-19 22:21:13 -04:00
arkon
cf14831fbe Clean up preference extensions/items a bit 2023-07-19 21:57:22 -04:00
arkon
7a4680603d Avoid triggering new search for same query in global search 2023-07-19 20:31:46 -04:00
arkon
99f12b1fbf Bump dependencies 2023-07-19 20:04:49 -04:00
Ivan Iskandar
5c73045aa4 Don't require deeplink ext target to be pinned (#9740) 2023-07-19 19:54:06 -04:00
arkon
ac306547a0 Bump dependencies 2023-07-18 19:12:04 -04:00
arkon
3f868c0435 Use correct sources when triggering new global search
Actually fixes #9724
2023-07-18 18:09:31 -04:00
arkon
262ce3473f Increase max lines for title in global search to 3
Closes #9729
2023-07-16 22:24:14 -04:00
arkon
43b9b104f5 Remove fast scrollbar from some unnecessary places 2023-07-16 22:19:50 -04:00
arkon
c7f0a54a37 Trigger new search on source filter change
Fixes #9724
Could be cleaned more though.
2023-07-16 22:19:33 -04:00
arkon
ca789dca0e Dedupe SearchScreenModels 2023-07-16 19:44:32 -04:00
arkon
ef7b285151 Minor refactoring 2023-07-16 19:18:38 -04:00
arkon
dd3ca0c131 Replicate global search filters to migrate screen
Still needs better refactoring to dedupe all of this stuff though...
2023-07-16 17:09:59 -04:00
arkon
8b46e8edad Dedupe Global/MigrateSearchContent composables 2023-07-16 16:43:26 -04:00
arkon
30f845139d Use consistent extension icon URLs
Better caching between versions.
2023-07-16 15:44:36 -04:00
arkon
818471b7e1 Set start date when tracker is bound if any chapters are already read
Closes #6734
2023-07-16 15:01:04 -04:00
arkon
a3a3f44056 Constrain reader sheet to max 75% of height 2023-07-16 09:59:17 -04:00
arkon
22c6dbda3f Replace reader sheet dropdowns with chips 2023-07-16 09:58:52 -04:00
arkon
34f7caa0fc Use Material3 chips 2023-07-16 09:11:57 -04:00
arkon
01553b1ed8 Don't update chapter progress if current page is errored
Closes #5355
2023-07-16 09:11:57 -04:00
renovate[bot]
a24afa9a76 Update dependency gradle to v8.2.1 (#9723)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-15 21:23:42 -04:00
AntsyLich
ec08ba05fc Finish up reader reading mode settings compose migration (#9721) 2023-07-15 15:47:01 -04:00
arkon
30bea8b753 Replace library sheet display modes with FlowRow of Chips 2023-07-15 14:31:13 -04:00
arkon
09e4b5a9cd Replace some reader sheet settings with FlowRow of Chips 2023-07-15 14:27:30 -04:00
arkon
5467104b95 Fix window undimming when reader custom filter settings are open 2023-07-15 13:23:25 -04:00
arkon
e0733c1a4c Clean up NetworkHelper 2023-07-15 13:05:25 -04:00
arkon
1cf7f9be54 Use segmented buttons for reader background setting in sheet 2023-07-15 13:05:06 -04:00
arkon
fb99577836 Implement showing selected per-series reader settings 2023-07-15 12:39:36 -04:00
arkon
28131ac135 Remove legacy settings sheet
The per-series settings aren't quite functional yet, but they're also
accessible outside of the sheet.
2023-07-15 11:14:18 -04:00
arkon
e40b8d537c Move all pager/webtoon reader setting to Compose sheet 2023-07-15 10:47:56 -04:00
arkon
12e7ee9d0c Tweak global search source filtering
Pinned only setting is removed in favor of the UI in the global search screen itself, which defaults to pinned only.
This needs more UX improvements, but I'm not really sure what it should be like right now.
2023-07-15 10:09:46 -04:00
arkon
54733e6ceb Mark some state data classes as immutable 2023-07-15 09:54:29 -04:00
arkon
22e8050fff Handle Cloudflare in default network client and deprecate cloudflareClient 2023-07-15 09:34:20 -04:00
arkon
a629db2884 Address some build warnings 2023-07-14 23:08:45 -04:00
zaghdaneh
cbcec8c4d9 Add filters to Global search (#9691)
* add pinned and available filter chips to global search

* split filter predicate into seperate function

* change the global search available filter to has Results

* reordering of imports
2023-07-14 22:49:14 -04:00
arkon
2f05f7b91f Remove bad translations 2023-07-13 17:53:32 -04:00
Weblate (bot)
a3a9699e8a Translations update from Hosted Weblate (#9684)
Weblate translations























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/es_419/
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/ko/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/mr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ms/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/nl/
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/uk/
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: Aditya Kadam <akxyz911@gmail.com>
Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Co-authored-by: Alessandro Jean <alessandrojean@gmail.com>
Co-authored-by: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: Clxff H3r4ld0 <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Dan <jonweblin2205@protonmail.com>
Co-authored-by: DarKCroX <DarKCroX@users.noreply.hosted.weblate.org>
Co-authored-by: DevByte <yeiser192@gmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: Eric SHI <eric@ericshi.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: abc0922001 <abc0922001@hotmail.com>
Co-authored-by: bapeey <luisrleccar@hotmail.com>
Co-authored-by: jinu147 <nesqea20@gmail.com>
Co-authored-by: poonkje <aaron.knoop@live.nl>
Co-authored-by: stevenlele <stevenlele@outlook.com>
2023-07-13 17:53:03 -04:00
arkon
f01a312c23 Prevent keyboard when using SelectItem in filters
Closes #9703
2023-07-13 17:38:15 -04:00
zaghdaneh
0fffde50ff Fix to multiple chapter download incorrect state (#9707)
add subscription to download manager queue state flow
2023-07-12 23:09:15 -04:00
arkon
8775596a82 Use system APIs to localize percentage numbers 2023-07-11 22:32:20 -04:00
arkon
2f0133986a Move boolean reading mode prefs to Compose sheet
Also allow webtoon side padding to be any amount between 0 - 25%.
2023-07-11 22:24:16 -04:00
AntsyLich
3bd2cad45f Change the wording to "Retry" when page fails to load. (#9701) 2023-07-11 09:15:38 -04:00
AntsyLich
48f7a2de41 Actually retry when a page fails to load in Browse screen (#9700) 2023-07-11 08:08:25 -04:00
arkon
3aa6e7ae0e Fix swipe action preference labels 2023-07-10 22:23:05 -04:00
arkon
813d7e49cd Remove unused tabbed sheet layouts/classes 2023-07-10 18:55:20 -04:00
arkon
710ebfb7a5 Initial migration of general reader settings to Compose 2023-07-10 18:42:35 -04:00
arkon
87bdee5990 Move SettingsItems composables to presentation-core 2023-07-10 17:25:52 -04:00
arkon
efabe801be Refactor chapter tracking logic
Could probably call this if we ever make it update tracking on manually
marking chapters as read.
2023-07-10 17:13:58 -04:00
arkon
9a817e49be Set proper defaults for new table columns 2023-07-10 16:44:48 -04:00
KaiserBh
a577f5534f Database changes to support library syncing (#9683)
* feat: added migrations.

* feat: create triggers, account for new installs.

* feat: update mappers to include the new field.

* feat: update backupManga and backupChapter.

Include the new fields to be backed up as well.

* feat: add sql query to fetch all manga with `last_favorited_at` field.

* feat: version bump.

* chore: revert and refactor.

* chore: forgot to lower case the field name.

* chore: added getAllManga query as well renamed `fetchMangaWithLastFavorite` to `getMangasWithFavoriteTimestamp`

* chore: oops that's not meant to be there.

* feat: back fill and set last_modified_at to not null.

* chore: remove redundant triggers.

* fix: build error, accidentally removed insert.

* fix: build error, accidentally removed insert.

* refactor: review pointer, make fields not null.
2023-07-10 15:52:57 -04:00
renovate[bot]
d0f52ea93d Update aboutlib.version to v10.8.2 (#9689)
Update dependency com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin to v10.8.2

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-10 11:16:34 -04:00
arkon
6063efd101 Bump dependencies 2023-07-09 22:43:56 -04:00
Ivan Iskandar
0759936226 Remove scrollable animation workaround (#9690)
Reverts ba93060e59
Related https://android-review.googlesource.com/c/platform/frameworks/support/+/2239762
2023-07-09 14:08:58 -04:00
arkon
1e3d9a00f2 Handle chapter read status in correct order
Fixes #9687
2023-07-09 09:54:36 -04:00
renovate[bot]
7c62453280 Update aboutlib.version to v10.8.1 (#9685)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-08 22:08:37 -04:00
arkon
226272f686 Refactor reader progress/history logic 2023-07-08 18:05:00 -04:00
arkon
16cbcecd99 Fix download ahead
Fixes #9669
2023-07-08 17:56:15 -04:00
arkon
b008223661 Minor reorganization 2023-07-08 17:47:10 -04:00
arkon
f8cf3db4a4 Allow download ahead even if entry isn't favorited 2023-07-08 17:46:48 -04:00
arkon
a585d46e7a Renovate: group Compose compiler and Kotlin version upgrades 2023-07-08 16:02:57 -04:00
arkon
8cc42bce5a Tweak chapter swipe directions and icon color 2023-07-08 15:55:53 -04:00
Weblate (bot)
67c6dbea0d Translations update from Hosted Weblate (#9671)
Weblate translations



















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/cv/
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/fil/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hi/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/it/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ms/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ne/
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/tr/
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: C201 <derasetad@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: Efe Devirgen <efedevirgen@gmail.com>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Nick Mariño <nickaidan19@gmail.com>
Co-authored-by: Om Mishra <projectrexaofficial@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: stevenlele <stevenlele@outlook.com>
2023-07-08 15:55:47 -04:00
arkon
db33437577 Upgrade Okio 2023-07-08 10:06:55 -04:00
Ivan Iskandar
8287c9d193 MangaChapterListItem: Replace swipe action method (#9682)
Using swipe (the library) and added haptic feedback
2023-07-08 10:02:20 -04:00
arkon
d32409bd6e Fix up icon direction when RTL 2023-07-07 19:58:53 -04:00
arkon
cf3f2d0380 Adjust manga FAB to only say "Start" if there's no unread chapters in unfiltered list
Closes #9479
2023-07-07 17:57:29 -04:00
arkon
53c6230afe Change auto clear cache to occur on app launch instead
Fixes #9564

Avoids the issue of clearing the cache when the app is backgrounded despite being in the reader.
We could do a job on idle, but we'd still need to be careful around whether the reader is active,
so this is just simpler considering it's a separate activity.
2023-07-07 17:46:39 -04:00
Semen
4882896f4d Add function to delete downloaded chapters when migrating manga (#9621)
add function to delete downloaded chapters when migrating manga and getFlagsFromPositions fix
2023-07-07 09:57:02 -04:00
renovate[bot]
4d67066de3 Update xml.serialization.version to v0.86.1 (#9674)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-05 23:06:32 -04:00
arkon
6fe5e6e21b Save reader progress on every page change
Fixes #9668
Could probably refactor this a bit more, but the reader view model stuff is a mess in general anyway.
2023-07-05 18:57:57 -04:00
arkon
8c5496b53f Configure external-files-path for files provider
Maybe fixes #9660. Why do Chinese companies insist on breaking things?
2023-07-05 18:38:32 -04:00
arkon
235a587e42 Upgrade to Kotlin 1.8.22 2023-07-02 00:04:52 -04:00
arkon
3125d78706 Remove some dead code 2023-07-01 14:54:35 -04:00
arkon
bb8f3c63f1 Remove usage of savedInstanceState for storing reader menu visibility state 2023-07-01 12:47:20 -04:00
Weblate (bot)
20faaaa908 Translations update from Hosted Weblate (#9617)
Weblate translations
























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/eo/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/es/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fil/
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/ms/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ne/
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/th/
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/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hant/
Translation: Tachiyomi/Tachiyomi 0.x

Co-authored-by: Alessandro Jean <alessandrojean@gmail.com>
Co-authored-by: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: Clxff H3r4ld0 <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Dan <jonweblin2205@protonmail.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: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Flamm <robindevaux25@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Malê Mairu <kalog89639@anwarb.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: orkan gökçe alaz aşina <examplehuman@outlook.com>
Co-authored-by: stevenlele <stevenlele@outlook.com>
2023-07-01 12:31:20 -04:00
arkon
44cc6f11c7 Fix crash when tapping reader in long strip mode before RecyclerView is created 2023-07-01 10:36:46 -04:00
arkon
bae391c2c1 Replace deprecated ProgressDialog
Fixes #8223
2023-07-01 10:36:20 -04:00
arkon
0ac5f3b93c Accept third party cookies in WebView instances
May help with Cloudflare.
2023-07-01 09:40:58 -04:00
arkon
b79ef5dc79 Address some deprecation warnings 2023-06-30 22:14:17 -04:00
arkon
7d26ca046f Bump dependencies 2023-06-30 22:09:07 -04:00
LagradOst
d99f4697e8 Fixed scrolling on the background using long strip (#9654)
Update WebtoonFrame.kt
2023-06-30 21:56:35 -04:00
stevenyomi
bb3fdef40b Browse Source: clear search query when changing listing (#9652) 2023-06-29 22:39:43 -04:00
Alessandro Jean
2a7cca6ea4 Show a progress indicator while checking for updates in the about screen (#9641)
* Show a progress indicator while checking for updates.

* Remove a unused import.

* Remove the initial toast.
2023-06-27 22:14:31 -04:00
Alessandro Jean
6ed2748846 Show open source library license in a custom screen (#9645)
Show open source library license in a custom screen.
2023-06-26 22:28:14 -04:00
Ivan Iskandar
7c90fe0f7d AdaptiveSheet: Migrate deprecated swipeable (#9642) 2023-06-26 22:20:08 -04:00
Alessandro Jean
8a5e443ca5 Make source preferences' titles multiline (#9644)
Make source preferences' titles multiline.
2023-06-26 14:05:22 -04:00
arkon
a07e0df815 Use same icons as action bar in swipe actions 2023-06-25 14:50:48 -04:00
arkon
88e9fefa59 Retry LibraryUpdateJob later if Wi-Fi condition not met 2023-06-25 14:31:28 -04:00
arkon
c0fd47b066 Retry DelayedTrackingUpdateJob up to 3 times if all items not updated 2023-06-25 14:31:28 -04:00
Denny Rodrigues do Carmo
ee684cbef5 Fix download ahead while reading functionality (#9640)
Correct condition for downloading next chapter
2023-06-25 14:18:54 -04:00
renovate[bot]
1f618d6634 Update aboutlib.version to v10.8.0 (#9637)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-24 22:19:37 -04:00
arkon
7d4af1f8cc Don't affect reader toolbars/sheets when using custom brightness
Closes #8444
You should be able to tap to bring up the menus even if the brightness is too low.
2023-06-24 12:56:45 -04:00
arkon
fe82cdb9c8 Migrate ReaderColorFilterSettings to Compose
It'll eventually be a tab with the other settings again once the other tabs are also
migrated over so it's just a single Compose sheet.
2023-06-24 12:51:10 -04:00
arkon
b354e37cc3 Refactor grid size slider composable for reuse 2023-06-24 11:23:06 -04:00
arkon
f344831d58 Use our own translation for "OK"
I missed this in the PR that it came with, but I guess it'll be useful if we go multiplatform.
2023-06-24 10:38:34 -04:00
Semen
2eca8511cb Trackers empty scores start starts from middle (#9624)
* Trackers empty scores start starts from middle

* Trackers empty scores start starts from middle

* Trackers empty scores start starts from middle
2023-06-24 10:37:20 -04:00
arkon
f2b0d74b4c Migrate ReaderPageSheet to Compose 2023-06-23 23:17:47 -04:00
arkon
42bc2b07ce Minor cleanup 2023-06-23 22:39:59 -04:00
arkon
e2d6269a38 Bump default user agent string 2023-06-23 22:23:56 -04:00
Shamicen
fcfa62f220 Better handle decimal chapter numbers and add categories in ComicInfo.xml files (#9604)
* Serialize whole chapter numbers without decimal point and add library categories to genre

* added Tachiyomi specific ComicInfo Category field

* lint

* implemented requested changes
2023-06-23 17:56:01 -04:00
Ivan Iskandar
25b0458930 composed Modifier changes (#9631)
Referring at the examples and other internal usages, the
resulting stateful Modifier should be separated from
all the previous Modifier chain.
2023-06-23 17:54:01 -04:00
Ivan Iskandar
6808fbbb21 Use lite compose-ui-tooling module on release builds (#9630)
The size reduction is minuscule but it's there.
2023-06-23 08:26:35 -04:00
zaghdaneh
b36b3bfcab Remove manga from trackers (#9535)
* Dialog for service tracker removal added, anilist query prepared

* added API delete requests for Mal and Kitsu

* implement and fix tracker delete for anilist, shikimori, mangaupdates

* implement and test mal delete request

* Update to dialog text to reflect current tracker

* finish kitsu api request and block bangumi tracker removal

* Change delete flag into interface, localise strings, clean up logs

* Add shikimori delete compatibility for already existing entries

* update track delete dialog prompt to include checkbox, update strings

* Update i18n/src/main/res/values/strings.xml

Co-authored-by: stevenyomi <95685115+stevenyomi@users.noreply.github.com>

* Update i18n/src/main/res/values/strings.xml

---------

Co-authored-by: unknown <zaghdane@fireflow.de>
Co-authored-by: arkon <arkon@users.noreply.github.com>
Co-authored-by: stevenyomi <95685115+stevenyomi@users.noreply.github.com>
2023-06-22 22:06:43 -04:00
arkon
7f0ed58b54 Update Guava 2023-06-22 22:01:25 -04:00
Ivan Iskandar
b4393ff741 Update core-splashscreen (#9629) 2023-06-22 21:59:17 -04:00
Ivan Iskandar
b8af1621b5 Update Compose BOM v2023.06.00-alpha01 (#9628) 2023-06-22 21:50:18 -04:00
Ivan Iskandar
4a75f82a6f Update Paging and match version (#9626) 2023-06-22 14:20:33 -04:00
Ivan Iskandar
740e370465 Bump compile SDK version 34 (#9625) 2023-06-22 14:20:12 -04:00
renovate[bot]
245985bf42 Update dependency ch.acra:acra-http to v5.10.1 (#9614)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-19 22:16:01 -04:00
renovate[bot]
344f5afd50 Update dependency io.github.fornewid:material-motion-compose-core to v1.0.3 (#9613)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-17 10:06:42 -04:00
Weblate (bot)
1c6e5605f9 Translations update from Hosted Weblate (#9579)
Weblate translations


























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/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/fil/
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/ko/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ms/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ne/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pl/
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/th/
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/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hant/
Translation: Tachiyomi/Tachiyomi 0.x

Co-authored-by: Adhwa King Dota <stevetom506@gmail.com>
Co-authored-by: Alessandro Jean <alessandrojean@gmail.com>
Co-authored-by: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: Clxff H3r4ld0 <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Dan <jonweblin2205@protonmail.com>
Co-authored-by: DarKCroX <DarKCroX@users.noreply.hosted.weblate.org>
Co-authored-by: Deniz <denizgezgin365@gmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Efe Devirgen <efedevirgen@gmail.com>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Piotr <growgra@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: jinu147 <nesqea20@gmail.com>
Co-authored-by: sebastians17 <sebastians117.ss@gmail.com>
Co-authored-by: stevenlele <stevenlele@outlook.com>
Co-authored-by: sunfkny <1198355143@qq.com>
Co-authored-by: 朔夜月 <a03175ii0@gmail.com>
2023-06-17 10:06:32 -04:00
arkon
0871208023 Avoid crash when trying to open random entry but categories are still loading
Fixes #9610
2023-06-15 22:16:31 -04:00
arkon
ee95c1439f Blur manga info header image
Similar to J2K. This only applies on Android 12+.
2023-06-13 20:36:33 -04:00
arkon
e323f3c25a Don't update last page read/read state of chapter if Incognito Mode is enabled
Actually closes #7228, which I forgot was about history more than trackers.
2023-06-13 19:30:02 -04:00
arkon
9766399539 Don't update trackers after reading a chapter if Incognito Mode is enabled
Closes #7228
2023-06-13 18:51:07 -04:00
AntsyLich
fc4fd487f9 Increase update screen limit to 500 (#9599) 2023-06-10 15:38:56 -04:00
Simon
dddba7bb6f Filter out non-downloaded chapters in reader when Downloaded Only is enabled (#9568)
* FIxed Issue #5463 - DownloadedOnly Bug

* Changes according to Feedback

* Changes according to Feedback

---------

Co-authored-by: AlphiGhost <71730726+AlphiGhost@users.noreply.github.com>
2023-06-10 12:48:15 -04:00
Ivan Iskandar
9ec8d770ea MangaChapterListItem: Increase swipe action touch slop (#9598) 2023-06-10 12:48:03 -04:00
Two-Ai
cf777d9893 Cleanup MangaScreenModel successState usage (#9582)
* Refactor updateSuccessState

- Convert to inline function
- Use when for type safety if we add other MangaScreenState types

* Replace equivalent expressions with updateSuccessState

* Replace safe cast in MangaScreen
2023-06-09 23:11:02 -04:00
arkon
0d9f8e8743 Fix LibraryFlagsTest
Forgot to update these after LibraryDisplayMode was made to not be a flag.
2023-06-09 22:59:06 -04:00
arkon
841f80f935 Add share menu item in reader
Closes #9510
2023-06-09 22:52:49 -04:00
arkon
39a7356ed1 Directly track current WebView URL instead of relying on state
State approach doesn't work well for client-side routed apps like MangaDex.

Fixes #9576
2023-06-09 22:45:13 -04:00
arkon
438054a0ec Bump dependencies 2023-06-09 22:38:37 -04:00
arkon
34b9c82cd0 Remove General settings section 2023-06-04 17:07:29 -04:00
arkon
405a75438a Remove per-category display mode
There seems to be little value in this feature, and juggling flag masks is annoying.
Per-category sorting is still a thing, but could be refactored away from the flag in the feature.
2023-06-04 16:59:21 -04:00
Ivan Iskandar
39e4568460 ReaderProgressIndicator: Convert to Compose (#9574) 2023-06-03 13:11:41 -04:00
Weblate (bot)
0d96791a84 Translations update from Hosted Weblate (#9531)
Weblate translations




























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/fil/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hi/
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/jv/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ko/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ne/
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/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ru/
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/vi/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hans/
Translation: Tachiyomi/Tachiyomi 0.x

Co-authored-by: Alessandro Jean <alessandrojean@gmail.com>
Co-authored-by: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: AntonP <tony.pug.stark@gmail.com>
Co-authored-by: Christian Elbrianno <crse@protonmail.ch>
Co-authored-by: Clxff H3r4ld0 <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Dan <denqwerta@gmail.com>
Co-authored-by: Danel Dave Barbuco <barbucodanel@gmail.com>
Co-authored-by: DatTran MLL <tranthanhdat1142003@gmail.com>
Co-authored-by: Dexroneum <Rozhenkov69@gmail.com>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Ferran <ferrancette@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: Igor <zerrxs@gmail.com>
Co-authored-by: Izxmi <heltherrivas05@gmail.com>
Co-authored-by: Leonardo Falcoski <leonardo.falcoski@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Rostyslav Haitkulov <info@ubilling.net.ua>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: TheKingTermux <achmadmaulana0233@gmail.com>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: stevenlele <stevenlele@outlook.com>
2023-06-03 13:10:13 -04:00
arkon
531e1c62bb Hide release period update restriction in non-dev builds until ready 2023-06-02 18:23:31 -04:00
arkon
1a1f16f44a Bump dependencies 2023-05-31 22:48:13 -04:00
arkon
431f8772f8 Address minor build warnings 2023-05-31 22:47:31 -04:00
arkon
8a5382042c Fix misleading release grace period "Default" options 2023-05-31 18:52:36 -04:00
arkon
8f4bc71cf7 Remove confirm exit option
Redundant with predictive back, but also just sort of pointless since it doesn't help
with any sort of app state retention.
2023-05-31 18:51:01 -04:00
Two-Ai
0ac38297f4 Replace RxJava in extension installer (#9556)
* Replace RxJava in extension installer

Replace common downloadsRelay with Map of individual StateFlows

* Drop RxRelay dependency

* Simplify updateAllExtensions

* Simplify addDownloadState/removeDownloadState

Use immutable Map functions instead of converting to MutableMap
2023-05-30 10:25:20 -04:00
arkon
4c65c2311e Limit updates to 250 most recent chapters
Still limits to things within the past 3 months though.

Closes #9554
2023-05-28 16:48:22 -04:00
arkon
f48f212001 Minor cleanup 2023-05-27 23:27:02 -04:00
Quang Kieu
c90f344910 Add setting and calculate for update interval (#9399)
* Add Grace Period value and settings

* Add functions to calculate nextUpdate

* update per review

* Move more into SetMangaUpdateInterval, keep wrapper
2023-05-27 23:01:36 -04:00
Ivan Iskandar
a458bd9fdb Update Glance v1.0.0-beta01 (#9551) 2023-05-27 22:59:21 -04:00
arkon
ed5a56be60 Set reader chapter name to marquee if too long
Closes #7159
2023-05-27 19:15:11 -04:00
arkon
899fe57f15 Slightly tweak MangaScreen refresh indicator
Related to #7813. It still starts below the status bar, but it looks a bit less weird.
2023-05-27 19:06:04 -04:00
arkon
bac42edabb Add debug screen to copy backup file schema
Closes #8544
2023-05-27 18:53:11 -04:00
arkon
8735f3566f Fix bookmarked chapters being deleted after manually marked as read
Fixes #9520
2023-05-27 18:30:59 -04:00
arkon
46efd4c134 Fix some crashes 2023-05-27 09:53:01 -04:00
arkon
dfd38db7e3 Use primitive state holders 2023-05-27 09:22:31 -04:00
Ivan Iskandar
0189fc1f66 Bump Compose BOM version 2023.04.00-beta01.1 (#9548) 2023-05-27 09:17:31 -04:00
arkon
929a881943 Simplify chapter item composable a bit
Closes #9442 because I just removed the rounding entirely...
2023-05-26 23:02:16 -04:00
arkon
152fdec855 Minor cleanup and remove unused dependencies 2023-05-26 22:52:00 -04:00
Artemis-CtrlAltDel
9c07451d95 fix: grid size slider (#9542) 2023-05-26 22:41:22 -04:00
arkon
e3b2720924 Remove redundant inset handling in AdaptiveSheet
The Dialog is handling it anyway, so this doesn't really do anything useful.
We might need to add this back if Dialog actually handles edge-to-edge properly.
2023-05-24 18:10:17 -04:00
Two-Ai
3ae1e37c40 Replace RxJava in Downloader (#9256)
* Rename removeFromQueueByPredicate to removeFromQueueIf

Follow-up to PR comment in #9511

* Make Download hashCode stable

Mutating pages would previously change the Download hashCode, which
breaks HashMap lookups.

* Convert Donwloader subscription to coroutine

Replace downloadsRelay with activeDownloadsFlow. Instead of managing
a PublishRelay independent from the queue, derive a Flow of active
downloads directly from the queue StateFlow. (This will allow
updating the queue without pausing the downloader, to be done in a
follow-up PR.)

When a download completes successfully, the downloads is removed from
queueState. This updates activeDownloadsFlow and causes the
downloaderJob start the download job for the next active download.

When a download fails, the download is left in the queue, so
queueState is not modified. To make activeDownloadsFlow update
without a change to queueState, use transformLatest and use the
Download statusFlows to suspend until a download reaches the ERROR
state.

To avoid stopping and starting downloads every time
activeDownloadsFlow emits a new value, maintain a map of current
download Jobs and only start/stop jobs in the difference between
downloadJobs and activeDownloads. To make sure all child download
jobs are cancelled when the top-level downloader job is cancelled,
use supervisorScope.

* Remove obsolete main thread references in Downloader

Thread safety of the queue state used to be guaranteed by running all
queue mutation on the main thread, but this has not been true for
some time. Since the queue state is now backed by a StateFlow,
queueState can be safely updated by any thread.
2023-05-24 18:02:27 -04:00
arkon
d8998aacb4 Bump dependencies 2023-05-24 17:55:32 -04:00
arkon
efdff9a21a Update minimum WebView version 2023-05-24 17:55:27 -04:00
arkon
1824adb2ed Update default user agent string 2023-05-24 17:54:16 -04:00
renovate[bot]
38445673f3 Update dependency com.github.requery:sqlite-android to v3.42.0 (#9530)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-21 11:22:10 -04:00
arkon
5a9889b562 Upgrade Compose
Co-authored-by: ivaniskandar <ivaniskandar@users.noreply.github.com>
2023-05-21 11:21:32 -04:00
arkon
5ca7c39751 Replace Cascade with our own somewhat janky implementation 2023-05-21 11:02:56 -04:00
arkon
44609c494c Use AppBarActions in more places 2023-05-20 22:47:16 -04:00
renovate[bot]
0810d3db69 Update dependency com.github.requery:sqlite-android to v3.41.2 (#9526)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-20 22:47:09 -04:00
renovate[bot]
d4fb9995ef Update leakcanary to v2.11 (#9527)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-20 22:46:52 -04:00
Weblate (bot)
22a4372583 Translations update from Hosted Weblate (#9501)
Weblate translations









Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/de/
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/ne/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pt/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/sr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hant/
Translation: Tachiyomi/Tachiyomi 0.x

Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Filipe Mota (BlackSpirits) <blackspirits@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Pipixel_06 <nico.berard@outlook.com>
Co-authored-by: kevans <albapazpi@gmail.com>
Co-authored-by: torchlight <sima142222@gmail.com>
2023-05-20 16:18:23 -04:00
arkon
a4d86a2e1e Enable predictive back gesture for Android 13 (behind developer option)/14+ 2023-05-20 16:16:19 -04:00
Shamicen
b8716ff6fe Populate the ComicInfo Number field with chapter numbers (#9514)
* Populate the ComicInfo Number field

* added negative number check
2023-05-20 10:24:10 -04:00
Ivan Iskandar
73118d4af7 DownloadCache: Fix freezing on initial loading of cache file (#9523) 2023-05-19 22:06:06 -04:00
Two-Ai
c27bf4e866 Minor Downloader cleanup (#9511)
* Inline completeDownload

* Consolidate queueState updates in removeFromQueue

* Inline post-download steps into downloadChapter
2023-05-19 17:16:32 -04:00
Shamicen
f50f5c4b54 bump xmlutil (#9505)
fixes broken surrogate pairs
2023-05-14 19:19:22 -04:00
arkon
fb38d30775 Avoid attempts to renaming download dirs if name hasn't actually changed
Maybe fixes #9503
2023-05-14 12:24:40 -04:00
Weblate (bot)
a3a9c8ac8e Translations update from Hosted Weblate (#9463)
Weblate translations















Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/bn/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/de/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fil/
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/ne/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pl/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/tr/
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: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Gab Albatros <2lj.fzb@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: Johnathan Illian <johnathanillian77@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: NGB-Was-Taken <myalternate34@gmail.com>
Co-authored-by: Pipixel_06 <nico.berard@outlook.com>
Co-authored-by: Ramim Hasan <ramimhasan.dev@gmail.com>
Co-authored-by: kevans <albapazpi@gmail.com>
Co-authored-by: orkan gökçe alaz aşina <examplehuman@outlook.com>
Co-authored-by: sebastians17 <sebastians117.ss@gmail.com>
Co-authored-by: stevenlele <stevenlele@outlook.com>
2023-05-13 22:50:07 -04:00
arkon
b4bb855675 Revert "Bump to somewhat newer version of Compose BOM"
This reverts commit ce81b76150.
Fixes crashes when opening menus. Cascade doesn't have an update
to fix this yet.
2023-05-13 22:45:36 -04:00
Ivan Iskandar
6263a52777 Fix navigation backstack (#9497)
Partial revert of dbbf6c5de0
2023-05-13 14:22:02 -04:00
Ivan Iskandar
96defd6b05 Replace our custom Pager (#9494)
Turns out that changing the pagerSnapDistance
is enough to achieve the same result.
2023-05-13 12:06:00 -04:00
Ivan Iskandar
8df9bce1b4 Upgrade Kotlin 1.8.21 (#9495) 2023-05-13 12:05:19 -04:00
arkon
bcd90be525 Use AppBarActions in more places
Related to #8270
2023-05-13 10:04:22 -04:00
arkon
22afae4449 Add tooltips for AppBarActions
Partially addresses #8270. A bunch of Scaffolds aren't using this helper.
2023-05-12 22:56:13 -04:00
arkon
8fae92034e Fix missing appbar when statistics are loading 2023-05-12 18:13:07 -04:00
arkon
ce81b76150 Bump to somewhat newer version of Compose BOM 2023-05-12 18:10:07 -04:00
AntsyLich
f70d5ea976 Delay automatic backup when restoring (#9492) 2023-05-12 18:02:11 -04:00
arkon
dbbf6c5de0 Switch back to upstream version of Voyager 2023-05-12 18:01:48 -04:00
arkon
2379df7e60 Bump dependencies 2023-05-12 17:44:48 -04:00
renovate[bot]
e3ce3ff418 Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-bom to v1.7.0 (#9469)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-09 22:08:35 -04:00
FlaminSarge
4395202703 Fix StubSource param order to match previous SourceData order (#9485)
SourceData was replaced with direct StubSource usage but the param order was changed without changing the usage; fixing the param order as such.
2023-05-09 18:37:24 -04:00
Ivan Iskandar
84acae27b7 ChapterNavigator: Fix rounding error when changing page with slider (#9477)
ChapterNavigator: Fix rounding error when changing page from slider
2023-05-08 09:00:05 -04:00
arkon
71f6e07e71 Don't compute key for browse list based on manga ID, which may be repeated
Fixes #9473
2023-05-07 19:39:09 -04:00
arkon
6f59c6c6bb Revert attempts to read archives to cache first
Issues:
- Apache implementation relies on methods unavailable on lower Android API levels
- Using input stream implementation doesn't seem to read some files properly, but using
  ZipFile implementation still requires reading the entire thing into memory
2023-05-07 12:03:58 -04:00
Ivan Iskandar
d36cf5ce15 Chapter transition tweaks (#9470)
* Chapter transition tweaks

* Chapter transition cleanups
2023-05-07 10:08:33 -04:00
renovate[bot]
332d9ff61b Update dependency io.github.fornewid:material-motion-compose-core to v0.12.2 (#9467)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-07 10:04:53 -04:00
renovate[bot]
7bb1ccf6f7 Update dependency com.google.android.material:material to v1.9.0 (#9468)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-07 10:04:00 -04:00
arkon
05a7d5174a Fix unit tests not running in workflows 2023-05-06 23:12:12 -04:00
arkon
b051e37ab7 Address minor build warnings 2023-05-06 23:11:14 -04:00
arkon
44383ff950 Add R8 rule for org.apache.commons:commons-compress
Fixes #9465
2023-05-06 22:49:03 -04:00
arkon
1b25290d39 Fix filter FAB not working in migrate screen
I feel like this needs to be aligned with the browse screen/deduped somehow, but that can happen separately.

Fixes #9444
2023-05-06 12:27:44 -04:00
arkon
2f5eb73d29 Allow scrolling in restore confirmation dialog
Fixes #9460
2023-05-06 12:22:14 -04:00
Ivan Iskandar
f0dd33ee4c ChapterNavigator: Always show buttons and fix steps visual (#9461) 2023-05-06 11:15:39 -04:00
Weblate (bot)
e15b945e16 Translations update from Hosted Weblate (#9341)
Weblate translations













































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/bn/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/cs/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/cv/
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/fil/
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/hu/
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/km/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ko/
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/ne/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pl/
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/th/
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/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: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: BaHu <hunfire100@gmail.com>
Co-authored-by: C201 <derasetad@gmail.com>
Co-authored-by: Cedric Anders <anders.cedric@gmail.com>
Co-authored-by: Clxff Heraldo <123844876+clxf12@users.noreply.github.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: Evgeniy Khramov <thejenjagamertjg@gmail.com>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Gab Albatros <2lj.fzb@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: Hiroshi <borlonjhayron1119@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: Jen Kung-chih <Kaitul@outlook.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Qurban Aqayev <aqayev10.qa@gmail.com>
Co-authored-by: Sertinel <cankalenderr@yandex.com>
Co-authored-by: Shafkat Hasan <shafkathasan2@gmail.com>
Co-authored-by: Slyizs Áron <asdagf47@gmail.com>
Co-authored-by: Sup Kelelawar <apkfile007@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: The Kiwy <tttthekiwy@gmail.com>
Co-authored-by: TheKingTermux <achmadmaulana0233@gmail.com>
Co-authored-by: Tolorin <TolorinGT099@gmail.com>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: Vetle Ledaal <vetle.ledaal@gmail.com>
Co-authored-by: abc0922001 <abc0922001@hotmail.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: jinu147 <nesqea20@gmail.com>
Co-authored-by: john mactavish <Soap8750@gmail.com>
Co-authored-by: orkan gökçe alaz aşina <examplehuman@outlook.com>
Co-authored-by: sebastians17 <sebastians117.ss@gmail.com>
Co-authored-by: stevenlele <stevenlele@outlook.com>
Co-authored-by: Äljan Sayat <form7040@gmail.com>
2023-05-06 10:17:05 -04:00
renovate[bot]
5c7d88c2ed Update dependency org.jsoup:jsoup to v1.16.1 (#9427)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-05 22:47:23 -04:00
Two-Ai
bbe0ab1dd0 Fix delay between URL fetch and image download (#9452)
Fetch each source image URL immediately before downloading each image
instead of fetching all URLs and then downloading all images.

Source image URLs may change, so the downloader may fail if there is
too long a delay between fetching the image URL and downloading the
image.
2023-05-05 22:17:51 -04:00
Two-Ai
cb2d43c0d1 Ensure final download status is always set (#9453) 2023-05-05 22:17:05 -04:00
Ivan Iskandar
fce9cb820c ChapterNavigator: Fix haptic feedback (#9458) 2023-05-05 22:15:56 -04:00
Ivan Iskandar
08e4863d94 ChapterNavigator: Fix background color (#9450) 2023-05-04 13:55:53 -04:00
arkon
9a10656bf0 Migrate reader slider and next/prev buttons to Compose 2023-05-03 17:14:11 -04:00
arkon
3c79777e66 Migrate PageIndicatorTextView to Compose
Probably closes #7798
2023-05-03 16:18:25 -04:00
arkon
f5ad95d78a Fix language in source filter list jumping to top incorrectly
Fixes #9068
2023-05-03 15:07:41 -04:00
arkon
14c465d36f Get current track services when composing LibrarySettingsDialog
Fixes #9431
2023-05-03 14:41:08 -04:00
arkon
921a988c4a Bump AGP for Android Studio Flamingo 2022.2.1 Patch 1 2023-05-03 14:34:56 -04:00
arkon
99378ddf20 Bump dependencies 2023-05-03 14:09:52 -04:00
arkon
c623258e8c Try Apache implementation of ZipFile instead
Docs: https://commons.apache.org/proper/commons-compress/
Related StackOverflow post: https://stackoverflow.com/a/54236244/4421500

Related to #9438
2023-05-03 14:00:33 -04:00
Houssein Zaghdane
6ce42dc167 fix to multiple "add to library" toasts bug when adding a new series (#9433)
fix to multiple "add to library" toasts bug (#9177)

Co-authored-by: Houssein Zaghdane <zaghdane@fireflow.de>
2023-05-03 10:40:11 -04:00
Andreas
f63573f25f Remove SourceData and use StubSource directly for database (#9429) 2023-05-03 10:33:05 -04:00
renovate[bot]
b328f0e344 Update dependency io.github.fornewid:material-motion-compose-core to v0.12.1 (#9426)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-30 09:52:15 -04:00
Andreas
02864ebd60 Move GitHub Release/App Update logic to data (#9422)
* Move GitHub Release/App Update logic to data

* Add tests for GetApplicationRelease

* Review changes
2023-04-29 22:14:49 -04:00
renovate[bot]
eed91f6360 Update dependency org.junit.jupiter:junit-jupiter to v5.9.3 (#9424)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-29 22:07:42 -04:00
arkon
f317193bec Downgrade back down to Kotlin 1.8.10
Some people are having issues building the project in Android Studio. Invalidating/clearing the cache works for me,
but doesn't seem to work for others.

Potential tracking issue: https://youtrack.jetbrains.com/issue/KT-57605
2023-04-28 19:36:37 -04:00
arkon
f459515dd7 Fix manga chapter flags not working
The new column is appended to the end, so ordering does matter here.
2023-04-28 16:44:28 -04:00
arkon
9339ea4196 Process chapter duplicates after sorting
Closes #9255, sort of. The example is a bad edge case though, where chapter numbers are repeated across versions,
so realistically only the first 113 will appear but the later 113(s) won't despite being "different". Those realistically
should be in different manga entries, not all mixed together, so this is just a crappy source.
2023-04-28 16:01:14 -04:00
AntsyLich
6bdc1b676e Avoid potential crash when opening library settings sheet 2.0 (#9419)
Avoid potential crash when opening library settings sheet  2.0

Previous one had issues
2023-04-28 15:15:54 -04:00
Andreas
7451c13edd Add slider for changing columns (#9421)
- It changes the columns based on the current orientation
2023-04-28 15:13:41 -04:00
arkon
ccd4143d9d extension-lib 1.5: Add AppInfo#getSupportedImageMimeTypes() 2023-04-28 11:36:17 -04:00
arkon
c590f55030 Revert "Avoid potential crash when opening library settings sheet (#9415)"
This reverts commit 2cb08e6bb1.
2023-04-28 10:11:13 -04:00
AntsyLich
c21813a8b5 Add an icon to "Item Per Row" on library sheet (#9414)
Also cleanup `SettingsItems.kt`
2023-04-28 10:08:35 -04:00
AntsyLich
2cb08e6bb1 Avoid potential crash when opening library settings sheet (#9415) 2023-04-28 09:08:17 -04:00
stevenyomi
058ee4c86b Fix exception formatter's format (#9413) 2023-04-28 09:06:32 -04:00
arkon
ea6e5eebac Remove "when tapping" from "Pan wide images" setting
Closes #9343
2023-04-27 22:54:07 -04:00
arkon
9cc25ff345 Fix disable source option not appearing 2023-04-27 22:47:58 -04:00
arkon
c9805b8612 Consolidate exception message formatting
Closes #9408
2023-04-27 22:45:30 -04:00
Quang Kieu
41c89eb61d Add interval data layer (#9398)
* Update Manga classes for fetch interval data

* Update per review

bump version

---------

Co-authored-by: quangkieu <qkieu>
2023-04-27 22:27:12 -04:00
arkon
392c3492b3 Minor cleanup 2023-04-26 17:27:44 -04:00
arkon
f7cd3929a3 Reword chapter swipe action preference labels 2023-04-26 17:22:13 -04:00
arkon
20bec66a9d Handle archives with nested directories properly
Closes #9389
2023-04-25 22:07:34 -04:00
Ken Swenson
3ce9a9ff97 Double tap zoom toggle (#9384)
* Double tap zoom toggle

Implements a toggle that allows users to disable double tap zoom including QuickScaling for webtoons. Partially resolves #4145

* Update i18n/src/main/res/values/strings.xml

---------

Co-authored-by: arkon <arkon@users.noreply.github.com>
2023-04-25 18:06:49 -04:00
d-najd
a8f17a3fab Add swipe actions for chapters (#9304)
* added chapter swipe

* Rework corner animtion

* Update i18n/src/main/res/values/strings.xml

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

* Replace LTR/RTL with Start/End layout

* Added label to the animation so the warning will go away

* Getting rid of the swipe threshold setting

* adding disabled option, renaming stuff, other stuff?

* Getting rid of the snackbar

* Getting rid of unecessary strings

* changing enum names as requested

* Renaming Raio to Ratio (I need a better keyboard as well -__-)

* Replacing error with download icon and action

* backup

* minor cleanup

* fixing an nasty edge case

* fixing mistakes in the previous conflict

* space

* fixing bug

fixed bug where the user could dismiss already dismissed item leading to item getting stuck

* fixing lint errors

* fixing lints (hopefully)

* Added "swipe disabled" to the list of actions

* Replacing string value and moving value as requested

* replacing rest of the strings with generic ones

---------

Co-authored-by: arkon <arkon@users.noreply.github.com>
2023-04-25 17:29:39 -04:00
Tooster
ef3d2c14b4 Fix misused string key in library context menu (#9388)
The "update global" action used key which led to translation issues in Polish.
2023-04-24 22:32:28 -04:00
arkon
44619febd3 Load ZIP file contents to cache (#9381)
* Extract downloaded archives to tmp folder when loading for viewing

* Generate sequence of entries from ZipInputStream instead of loading entire ZipFile
2023-04-23 11:59:58 -04:00
arkon
c48accb357 Maybe fix Firebase crashes
See https://github.com/firebase/firebase-android-sdk/issues/3507
2023-04-23 11:59:44 -04:00
arkon
418e6a8b3a Make loader implementation classes internal 2023-04-23 10:11:26 -04:00
arkon
67b4e53a58 Minor cleanup 2023-04-23 10:11:26 -04:00
renovate[bot]
d62d94f587 Update dependency io.github.fornewid:material-motion-compose-core to v0.11.3 (#9379)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-23 10:11:06 -04:00
arkon
265934d77a Fix missing type info in release builds 2023-04-22 18:32:22 -04:00
arkon
2a218cca90 Fix missing type info in release builds 2023-04-22 18:22:24 -04:00
arkon
e23cc8f83a Hide beta split tall images setting from reader setting sheet for release builds
Closes #9358
2023-04-22 16:33:41 -04:00
arkon
0b125b7106 Use Compose for reader transition chapter info (#9373) 2023-04-22 16:33:36 -04:00
renovate[bot]
320587e36e Update dependency gradle to v8.1.1 (#9376)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-22 11:35:07 -04:00
arkon
26f3995595 Consolidate missing chapters functions to domain module and add tests 2023-04-22 11:34:51 -04:00
arkon
94c94b2d88 Minor JavaDoc updates 2023-04-22 11:15:45 -04:00
Pauline
41cc1fe723 Fallback chapter name if it ends up as blank (#9220)
* change the directory's name for a download when the chapter's name is only composed of numbers or is blank

* maj in case the chapter name is blank or empty

* clean code
2023-04-22 11:11:56 -04:00
arkon
03a344e9c1 Bump dependencies 2023-04-19 22:58:36 -04:00
arkon
add228407f Remove abstract TabeedBottomSheetDialog class 2023-04-19 22:43:36 -04:00
Ivan Iskandar
2c6e025063 Add more info to debug screen (#9357)
* App version
* WebView version
* ART profile status
* Device model
* Android version
2023-04-18 22:59:27 -04:00
arkon
ba30dfe7e2 Bump dependencies 2023-04-17 23:07:22 -04:00
arkon
97e6f1ea9a Upgrade to AGP 8.0.0 (#9351) 2023-04-16 18:02:24 -04:00
arkon
5c1a81d8ca Merge branch 'patch' 2023-04-16 11:09:22 -04:00
renovate[bot]
7e56cba060 Update dependency gradle to v8.1 (#9345)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-16 10:23:32 -04:00
renovate[bot]
dc569fb20a Update dependency com.google.firebase:firebase-analytics-ktx to v21.2.2 (#9344)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-16 10:23:25 -04:00
arkon
c6ac992798 Move library columns settings to library sheet
Closes #3969. Dialog-ception.
2023-04-15 19:36:49 -04:00
arkon
8a18e10cc2 Rename reading modes
Closes #9339
2023-04-15 10:03:15 -04:00
arkon
d6b9711e45 Use Kotest matchers in other test classes 2023-04-15 09:54:06 -04:00
arkon
8ab7e63293 Add tests for MissingChapters function 2023-04-15 09:51:52 -04:00
arkon
4bcd623829 Move worker info screen into debug info menu
No need to translate anything for debug info. Dunno what else will end up in that menu in the future.
2023-04-15 09:35:22 -04:00
Weblate (bot)
18acf66cb8 Translations update from Hosted Weblate (#9290)
Weblate translations

































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/ceb/
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/fa/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fil/
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/ka/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ko/
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/ne/
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/sv/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/th/
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/vi/
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: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: Clxff Heraldo <123844876+clxf12@users.noreply.github.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: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: Hiroshi <borlonjhayron1119@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: ItsPoofy <tuanminh8688@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Ric <rikku.debec@gmail.com>
Co-authored-by: Sertinel <cankalenderr@yandex.com>
Co-authored-by: Shjosan <shjosan@kakmix.co>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: Temuri Doghonadze <temuri.doghonadze@gmail.com>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: Vetle Ledaal <vetle.ledaal@gmail.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: edgolron <edgolron@tutanota.com>
Co-authored-by: jinu147 <nesqea20@gmail.com>
Co-authored-by: komeil Parseh <ahmdparsh129@gmail.com>
Co-authored-by: love CiCi <lll090407@gmail.com>
Co-authored-by: stevenlele <stevenlele@outlook.com>
2023-04-15 09:34:52 -04:00
Trace
4816b4b53a fix: skip duplicate chapters on download ahead if option to skip duplicates is enabled (#9334)
* fix: skip duplicate chapters on download ahead if option is enabled

* fix: Use a function to filter duplicates
2023-04-15 09:34:02 -04:00
Ivan Iskandar
60d8650860 WheelPicker: Add manual input (#9338) 2023-04-15 09:26:33 -04:00
renovate[bot]
bfb7b5afd5 Update dependency io.github.fornewid:material-motion-compose-core to v0.11.2 (#9310)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-14 21:59:22 -04:00
Ivan Iskandar
a2627d70af WheelPicker: Add haptic feedback (#9322) 2023-04-14 21:58:57 -04:00
0x7673
6662a97b2f Remove horizontal padding of actions row in empty screen (#9332) 2023-04-14 21:58:34 -04:00
Eshlender
564a0980b9 Update track domain shikimori.me (#9333)
shikimori.me
2023-04-14 21:57:05 -04:00
renovate[bot]
e3fbd26880 Update aboutlib_version to v10.6.2 (#9309)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-09 12:14:30 -04:00
Ivan Iskandar
c1e23ec18e GlobalSearchScreen: Skip result screen when using search intent (#9299) 2023-04-07 22:18:48 -04:00
arkon
b7cd7b8b4e Better handle overflowing content in MigrateDialog actions
Fixes #9207
2023-04-05 22:36:57 -04:00
arkon
776d36caf1 Save current chapter progress when navigating to adjacent chapters
Fixes #9295
2023-04-05 22:29:56 -04:00
Ivan Iskandar
182e642cfc SearchToolbar: Check initial search query to open keyboard by default (#9297) 2023-04-05 22:15:56 -04:00
arkon
88bf1a706b Bump dependencies 2023-04-05 22:15:37 -04:00
arkon
d25ba23079 Fix automatic backups not working
Apparently they die if you rename a worker class.
2023-04-05 22:13:54 -04:00
arkon
75460e01c8 Remove crash log notification in favor of sharing directly 2023-04-02 15:30:22 -04:00
renovate[bot]
c9bd3a5314 Update dependency com.android.tools:desugar_jdk_libs to v2.0.3 (#9287)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-02 15:20:17 -04:00
arkon
7c6a5dc43b [skip ci] Update issue-moderator-action 2023-04-02 14:28:22 -04:00
RS156
274218cf22 Make center zoom start vertically centered too (#8849)
Update ReaderPageImageView.kt

"true center" Zoom start position #8747.
Changed zoom position to get true center value.
2023-04-01 14:52:15 -04:00
Weblate (bot)
c7d6509565 Translations update from Hosted Weblate (#9237)
Weblate translations



































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/cv/
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/fil/
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/hi/
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/ko/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/lv/
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/ne/
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/th/
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/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: Ali Aljishi <ahj696@hotmail.com>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: C201 <derasetad@gmail.com>
Co-authored-by: Christian Elbrianno <crse@protonmail.ch>
Co-authored-by: Clxff Heraldo <123844876+clxf12@users.noreply.github.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: Druvvaldis <druvvaldisr@gmail.com>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: Erik Johannessen <erikjohannessen8@gmail.com>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Ric <rikku.debec@gmail.com>
Co-authored-by: Ricardo <contatorms7@tutamail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: Vetle Ledaal <vetle.ledaal@gmail.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: jinu147 <nesqea20@gmail.com>
Co-authored-by: kevans <albapazpi@gmail.com>
Co-authored-by: staxhinho <staxhinho@gmail.com>
Co-authored-by: stevenlele <stevenlele@outlook.com>
2023-04-01 14:44:45 -04:00
arkon
bc0b9e536a Sample updates to scrollbars 2023-03-31 09:25:35 -04:00
Ivan Iskandar
7a1b599462 Adjust SearchToolbar soft keyboard behavior (#9282)
* Show soft keyboard when the text field is composed (a redo)
* Clear focus on text field when soft keyboard is hidden
* Request focus on text field and show soft keyboard
when clear button is clicked
2023-03-31 09:24:44 -04:00
arkon
1dd62af188 Ensure EmptyScreen is scrollable
Also fix padding for loading/empty states in BrowseSourceScreen
2023-03-29 22:53:58 -04:00
Ivan Iskandar
6f1099b710 AdaptiveSheet: Wrap inside Dialog (#9279)
Because of Compose issue, the style of the sheet surface is adjusted as
a workaround

Ref https://issuetracker.google.com/issues/246909281
2023-03-29 22:34:32 -04:00
arkon
be8e2f119f Make pin source icon lighter
Closes #9274
2023-03-28 23:01:22 -04:00
arkon
18f9e5ba6b Use IO dispatcher for some screen model work
Not sure if this is an ideal approach. If it is, we could migrate more usages to this.
2023-03-28 22:52:30 -04:00
arkon
d1bf857079 Remove unnecessary withIOContext 2023-03-28 18:16:43 -04:00
arkon
1814b3b22c Don't unnecessarily wrap IOExceptions in UncaughtExceptionInterceptor 2023-03-28 18:16:26 -04:00
arkon
be54b8862e Refactor away some unnecessary lambda expressions 2023-03-26 13:27:31 -04:00
arkon
1a61130f0b Don't attempt to initialize manga details from BrowseSource or Search screens
This was effectively DDoSing sources as it does a request for every entry to get the details (primarily a cover image).
The expectation now is that users have to open individual entries to load the details/cover if needed.
This isn't necessary for most sources, which are able to provide covers as part of the listing normally.
2023-03-26 13:12:32 -04:00
arkon
1de4bc9586 Restore POST_NOTIFICATIONS permission check for SDK 33+
Although we don't even target it yet and don't prompt for it but whatever, less work in the future.
2023-03-26 12:56:34 -04:00
arkon
1986042277 Skip POST_NOTIFICATIONS permission check for now
Fixes #9265
2023-03-26 12:34:32 -04:00
arkon
e932983494 Subscribe to changes to manga in BrowseSourceScreen
Fixes #9235
2023-03-26 12:06:12 -04:00
Two-Ai
35d381144d Cleanup Preference.asHotFlow() (#9257)
* Drop duplicate initial call in Preference.asHotFlow

Preference.changes() always starts by returning the current value of
the preference, so asHotFlow calls block twice on the initial value.

Possible breaking change: As implemented, asHotFlow ran block(get())
before returning the flow. After this change, the first call to block
will run within the flow collection. This might cause concurrency
issues if the flow collection is late to execute.

* Inline Preference.asHotFlow

The Preference.changes().onEach().launchIn() pattern is used widely,
so the asHotFlow extension method is redundant.
2023-03-26 11:52:54 -04:00
stevenyomi
0bcc22822d Simplify code in missing chapters warning (#9263) 2023-03-26 11:50:29 -04:00
arkon
1ff78173f7 Adjust missing chapters UI 2023-03-26 10:26:58 -04:00
arkon
ee45f46193 Bump dependencies 2023-03-25 21:29:56 -04:00
arkon
290efb0283 Fix Spanish (Latin America) being missing from in-app language selection 2023-03-24 22:58:29 -04:00
arkon
8d7a7919a9 Add TODO to default to 32-bit color at some point
Originally proposed in #8959
2023-03-24 22:52:18 -04:00
arkon
953720472f Add "Rotate wide pages to fit" setting for paged reader
Originally authored in #7983

Co-authored-by: timothyng-164 <timothyng-164@users.noreply.github.com>
2023-03-24 22:49:35 -04:00
Felix Kaiser
f94d902bb6 Added missing chapters count in MangaInfoHeader (#9184)
* Added missing chapters count in MangaInfoHeader

* Added "Might be missing chapters"

* Added missing chapters to MangaAndSourceTitlesLarge function

* Removed comments

* Reworked getMissingChapters to countMissingChapters, moved -1 check

* Attempting detecting sub-chapters

* Moved MissingChapters to ChapterHeader; Adapted design to fit in

* Fixed block comment in one-line-element

* Fixed critical missing-chapter counting bug

* Undid unintentional & unnecessary changes

* Moved & refactored countMissingChapters

* Fixed import order; Mapping chapter object to chapterNumber

* Optimized "No (valid) chapters" detection

---------

Co-authored-by: arkon <arkon@users.noreply.github.com>
2023-03-24 22:44:58 -04:00
arkon
da25322572 Bump Compose dependencies 2023-03-23 22:19:43 -04:00
arkon
cb4699a5bb Bump dependencies 2023-03-22 22:58:42 -04:00
arkon
2e5efadf42 Rename Complications -> Overlay 2023-03-22 09:26:07 -04:00
arkon
e5e18c2030 Bump subsampling-scale-image-view 2023-03-21 22:59:42 -04:00
arkon
ac0596a53d Revert "Always attempt to split tall images when downloading"
This partially reverts commit 2769525b2c.

Keeps the change to silently ignore spliting errors since it falls back to
the original images in those cases.
2023-03-20 08:45:36 -04:00
arkon
7ec5a51eb8 Move isLocal extension functions to LocalSource 2023-03-20 08:41:44 -04:00
arkon
3cca460282 Misc cleanup 2023-03-19 22:38:14 -04:00
arkon
d703fb7946 Split up ContextExtensions into smaller files 2023-03-19 18:27:30 -04:00
arkon
859601a46e Clean up WorkManager usages a bit 2023-03-19 18:19:40 -04:00
arkon
cdc160afc2 Convert BackupRestoreService to a WorkManager job
Co-authored-by: Jays2Kings <Jays2Kings@users.noreply.github.com>
2023-03-19 17:28:59 -04:00
arkon
14d1bcacc9 Show proper string in manga detail screen for SourceNotInstalledException 2023-03-19 17:23:51 -04:00
arkon
abd23b6826 Set default automatic library updates to off 2023-03-19 17:14:51 -04:00
arkon
7d8a865cac Simplify some of the notification builders 2023-03-19 16:24:37 -04:00
Andreas
dfdb688b43 Migrate things to use newer data models (#9239)
* Remove old database models from Coil

* Remove old database models from TrackInfoDialogHome

* Remove old database models from Backup Manager
2023-03-19 13:11:58 -04:00
renovate[bot]
c955ac6a66 Update dependency com.github.requery:sqlite-android to v3.41.1 (#9233)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-19 13:09:58 -04:00
arkon
f3ca4e76a8 Re-enable ComicInfo.xml generation on download
Closes #8537
2023-03-19 13:02:38 -04:00
arkon
2769525b2c Always attempt to split tall images when downloading 2023-03-19 12:57:16 -04:00
arkon
843e748de3 Clean up library display settings tab a bit 2023-03-19 12:41:29 -04:00
Weblate (bot)
d160cfaa0e Translations update from Hosted Weblate (#9178)
Weblate translations



















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/el/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/es/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fil/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hi/
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/kk/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ne/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/nl/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pt/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/sc/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/sq/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/sv/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/th/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hant/
Translation: Tachiyomi/Tachiyomi 0.x

Co-authored-by: Abay Emes <abayemes@gmail.com>
Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Co-authored-by: Alex Georgiou <alexandrosgeorgiou35@gmail.com>
Co-authored-by: Clxff Heraldo <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Denis Çerri <deniscerri3@gmail.com>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Piny <weaamadel77@gmail.com>
Co-authored-by: Shjosan <shjosan@kakmix.co>
Co-authored-by: Timo <timovdvenne@gmail.com>
Co-authored-by: Xnethers <z44440000z@gmail.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: bertklaps <bert.klaps@intel.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: niisshhaanntt <nishant_bodkhe@yahoo.com>
Co-authored-by: ssantos <ssantos@web.de>
2023-03-18 09:52:07 -04:00
Alexandr Kozlinskiy
81af97df77 BrowseSource: do networkToLocal and initializeManga inside flow (#9217)
* do networkToLocal and initializeManga inside flow

* remove BrowseSourceScreenModel.GetManga
2023-03-18 09:42:44 -04:00
Ivan Iskandar
18e55aa25f Adjust tab indicator visual (#9219)
Now behaves like the non-compose indicator by showing the swipe progress too
2023-03-16 22:20:25 -04:00
Ivan Iskandar
4d3e13b0d1 Initialize download index disk cache (#9179) 2023-03-16 22:18:11 -04:00
arkon
a335b4ee9e Bump dependencies 2023-03-15 09:42:13 -04:00
arkon
47a2d06682 Refactor tracker status string mappings
Should fix #9195
2023-03-10 23:01:10 -05:00
stevenyomi
ce66ed0389 Update CI badge in README (#9187) 2023-03-09 22:11:11 -05:00
Ivan Iskandar
c0f94ae8af Revert "Fix banners-related issues" (#9186)
Revert "Fix banners-related issues (#9143)"

This reverts commit 63048d2f0b.
2023-03-09 22:10:49 -05:00
arkon
ed32a511e7 Bump dependencies 2023-03-08 22:52:19 -05:00
Ivan Iskandar
17ed4873e8 Bump compose-bom version 2023.02.00-rc02 (#9185) 2023-03-08 22:41:14 -05:00
arkon
09acc53483 Remove download all chapters menu item
Users can still select all the chapters (long press + select all) to download them.
2023-03-07 22:38:27 -05:00
arkon
bebd4be43d Move more things to domain/data modules 2023-03-07 22:38:02 -05:00
arkon
9b77759f24 Use stricter visibility for composables where possible 2023-03-05 18:41:08 -05:00
arkon
e458de5e9c Add dot beside unread chapter names
Closes #4261
Also includes changes that might help with #9043
2023-03-05 17:47:27 -05:00
arkon
737a303df7 Remove some app translations that have low completion rates 2023-03-05 15:45:32 -05:00
Weblate (bot)
477dd37981 Translations update from Hosted Weblate (#9148)
Weblate translations




























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/cv/
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/fil/
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/ko/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ml/
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/ne/
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/tr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/uk/
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: Alessandro Jean <alessandrojean@gmail.com>
Co-authored-by: C201 <derasetad@gmail.com>
Co-authored-by: DIO Brando <babhiram131@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: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Felipe Nogueira <contato.fnog@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Mamon Asad <mmamonasad@gmail.com>
Co-authored-by: Matteo Mercurio <mercurio.matteo27@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: TheKingTermux <achmadmaulana0233@gmail.com>
Co-authored-by: Vetle Ledaal <vetle.ledaal@gmail.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: jinu147 <nesqea20@gmail.com>
Co-authored-by: stevenlele <stevenlele@outlook.com>
2023-03-05 15:41:46 -05:00
arkon
e917349bb7 Use Compose icons instead of drawables
Using:
- https://github.com/DevSrSouza/compose-icons
- https://github.com/DevSrSouza/compose-icons/blob/master/simple-icons/DOCUMENTATION.md
2023-03-05 15:37:41 -05:00
arkon
ad4912803b Refactor SourceManager/StubSource to domain module 2023-03-05 12:38:31 -05:00
arkon
f96f0c5889 Move some preferences into domain module 2023-03-05 12:11:47 -05:00
arkon
2b9acadc5b Move sourceMapper to data module 2023-03-05 12:05:48 -05:00
arkon
9caa0d147b Show proper message when doing global update
Fixes #9170
2023-03-05 11:07:33 -05:00
arkon
c6e5f8abd9 Bump default user agent string and minimum WebView version 2023-03-05 10:17:22 -05:00
Andreas
1abf01c4a0 Convert source modules to Kotlin Multiplatform (#9172)
Use KMP in source modules 


Use KMP in source-api


Expect LocalSource
2023-03-05 10:16:19 -05:00
Two-Ai
b41565f879 Inline DownloadQueue into Downloader (#9159)
* Move statusFlow and progressFlow to DownloadManager

* Inline DownloadQueue into Downloader

* Move reorderQueue implementation to Downloader
2023-02-28 22:13:13 -05:00
arkon
f03a834136 Add explicit overflow menu options to refresh library category and manga chapters list
Jetpack Compose treats mouse input differently than just mimicking a touch input, so dragging doesn't actually
invoke the pull to refresh. If that changes in the future, we could consider removing these.

Doesn't seem too necessary for the extensions list, so I skipped that.

Closes #8455
2023-02-26 16:58:36 -05:00
arkon
f7f2072621 Use queued last chapter read number when performing delayed tracker update
Fixes #8876
2023-02-26 16:48:04 -05:00
arkon
5b2e937d5f Minor refactoring 2023-02-26 16:47:29 -05:00
Andreas
f27dc19b37 Move Local Source to separate module (#9152)
* Move Local Source to separate module

* Review changes
2023-02-26 16:16:49 -05:00
arkon
2368c50ebb Add menu shortcut to source settings in BrowseSourceScreen
Adapted from TachiyomiSY.

Co-authored-by: jobobby04 <jobobby04@users.noreply.github.com>
2023-02-26 10:23:07 -05:00
Two-Ai
0505906e7a Move all DownloadService.stop calls to Downloader (#9146)
Downloader.stop is now the sole responsible for stopping the
DownloadService. This will help cleanly removing
DownloadService.stop when migrating to coroutines.
2023-02-25 15:40:22 -05:00
arkon
4efca04765 Avoid crashing in SourcePreferencesScreen if source can't be loaded 2023-02-25 15:32:46 -05:00
arkon
b12c7cf963 Avoid crash in DeleteLibraryMangaDialog
No clue why it ever gets a -1 index though.
2023-02-25 15:29:00 -05:00
arkon
487622c592 Close source filter dialog when filtering 2023-02-25 15:16:48 -05:00
arkon
26d422b0ae Avoid uncaught exceptions from OkHttp interceptors crashing entire app 2023-02-25 15:13:59 -05:00
Weblate (bot)
79a7b68837 Translations update from Hosted Weblate (#9107)
Weblate translations















Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ar/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/es/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/es_419/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fil/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ko/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ne/
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/sq/
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: Amjad Ali <playeroid96@gmail.com>
Co-authored-by: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Geovani Amaral <geovani.af4@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: Juan <benitesjn@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: MedRAM <mohammad7ram@users.noreply.hosted.weblate.org>
Co-authored-by: gnu-ewm <gnu.ewm@protonmail.com>
Co-authored-by: jinu147 <nesqea20@gmail.com>
Co-authored-by: seew3l <luisrleccar@hotmail.com>
Co-authored-by: stevenlele <stevenlele@outlook.com>
Co-authored-by: whales <mololet277@aosod.com>
Co-authored-by: 朔夜月 <a03175ii0@gmail.com>
2023-02-25 14:45:15 -05:00
Ivan Iskandar
63048d2f0b Fix banners-related issues (#9143)
This is most likely Compose issue so these changes will
be reevaluated when new Compose ver is out.
2023-02-25 14:44:35 -05:00
Two-Ai
79662a5866 Misc Downloader state cleanup (#9145)
* Replace Downloader CompositeSubscription with nullable Subscription

* Derive Downloader.isRunning from subscription

Also simplify usages of isRunning

* Move DownloadNotifier.paused to Downloader.isPaused

* Remove unused DownloadNotifier.errorThrown
2023-02-25 14:43:00 -05:00
Two-Ai
ed6809fa28 Simplify filter logic (#9141)
* Remove unnecessary else branch

* Add TriStateFilter applyFilter

* Simplify filterFnTracking filter logic
2023-02-25 11:46:40 -05:00
Two-Ai
86b9262a7e Make DownloadManager the sole entry point for DownloadService (#9140)
* Rename functions for DownloadService internal use

* Call DownloadService.start via DownloadManager

* Inline DownloadService.stop into pauseDownloads

* Inline DownloadService.stop into clearQueue

NotificationReceiver will now also stop the DownloadService when
receiving ACTION_CLEAR_DOWNLOADS.

* Provide DownloadService.isRunning via DownloadManager
2023-02-24 22:07:30 -05:00
arkon
7ec87e76db Migrate TriState usages to TriStateFilter enum 2023-02-24 16:09:47 -05:00
arkon
a0e76d2fd9 Bump dependencies 2023-02-24 16:07:37 -05:00
Ivan Iskandar
ec3ce74af8 TrackDateSelectorScreen: Use M3 date picker (#9138) 2023-02-24 15:22:23 -05:00
Two-Ai
83a4e34095 Remove redundant Downloader isNotification argument (#9139)
DownloadQueue.clear() already sets QUEUE downloads to NOT_DOWNLOADED.
2023-02-24 15:11:51 -05:00
arkon
84a0044d51 Remove some unused resources 2023-02-23 22:42:02 -05:00
arkon
92132c59f5 Migrate source filter sheet to Compose (#9135) 2023-02-23 22:32:40 -05:00
Ivan Iskandar
36ae388332 Bump compose-bom version 2023.02.00-beta02 (#9137) 2023-02-23 22:29:38 -05:00
arkon
bd47eafeec Fix per-category sort/display affecting the wrong category 2023-02-23 13:54:08 -05:00
arkon
9432d2d06a Bump dependencies 2023-02-22 23:09:16 -05:00
Two-Ai
fa61c8fe6f Convert downloadChapter to suspend function (#9127)
1:1 translation from the RxJava implementation, should match the
previous behavior.

Dropped the return value from functions of the form
```
fun foo(t: T, ...): Observable<T>
```
where the Observable produced the original argument `t`.
The caller already has the result if necessary.

While this conversion is not flow-based overall, some sections use
flows to use the flatMapMerge and retryWhen operators.

Removed RetryWithDelay as it was only used here.

Inlined fetchAllImageUrlsFromPageList instead of converting it to a
suspending equivalent. fetchAllImageUrlsFromPageList is no longer
used in the app, but was not removed as it is part of source-api.
(However, it does not seem to be used exposed in extensions-lib or
used in tachiyomi-extensions.)

runBlocking is used as a temporary stop-gap.
2023-02-21 23:02:10 -05:00
arkon
92bd98e45f Consolidate all theme colors to presentation-core module 2023-02-21 22:52:36 -05:00
arkon
fd7c993b0b Move CheckboxState to core module 2023-02-21 22:52:36 -05:00
Two-Ai
779df32e98 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.
2023-02-21 18:21:00 -05:00
arkon
f4e843f114 Fix package names 2023-02-21 15:11:34 -05:00
arkon
c0e2eb211d 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
2023-02-21 12:25:46 -05:00
arkon
0bd56ab77c Fix height shift when scrolling through themes 2023-02-21 12:04:17 -05:00
Ivan Iskandar
6b03dca5f4 Use Compose Foundation's flow layout (#9123) 2023-02-21 12:04:11 -05:00
Ivan Iskandar
bd7b21337c Add minLines to comfortable grid item title (#9122) 2023-02-21 11:44:56 -05:00
Ivan Iskandar
60a3ba5a5c Use non-stable Compose BOM (#9120) 2023-02-21 10:41:56 -05:00
arkon
7c2eb0b881 [skip ci] add link to FAQ in issue templates 2023-02-21 10:31:31 -05:00
arkon
93523ef50b Remove dependency injection from core module and data module from presentation-widget module
Includes side effects:
- No longer need to restart app for user agent string change to take effect
- parseAs extension function requires a Json instance in the calling context, which doesn't necessarily need to be the default one provided by Injekt
2023-02-20 19:02:38 -05:00
arkon
10d7349506 Move more components to presentation-core module 2023-02-20 10:12:41 -05:00
arkon
3d7c136320 Avoid crash when loading invalid extension package 2023-02-19 16:44:58 -05:00
arkon
a6d6a5ed87 Merge branch 'patch'
# Conflicts:
#	app/build.gradle.kts
#	app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt
2023-02-19 15:31:35 -05:00
arkon
ec49411bee Avoid crashes if headers can't be built for usage in WebView 2023-02-19 11:48:26 -05:00
arkon
3f7911235c Use unique keys for all screens to avoid crashes
Fixes #9008
Fixes #9110
2023-02-19 11:09:41 -05:00
arkon
727399611d Migrate library settings sheet to Compose 2023-02-18 20:55:55 -05:00
renovate[bot]
94232a4937 Update dependency gradle to v8.0.1 (#9108)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-18 20:41:08 -05:00
arkon
07fdb74fbc Minor settings sheet cleanup 2023-02-18 19:00:19 -05:00
arkon
d400ac2a49 Remove unnecessary usages of NotificationManagerCompat to actually create notifications 2023-02-18 17:10:45 -05:00
arkon
dd71c76a8f Move more components 2023-02-18 17:04:32 -05:00
arkon
58a0add4f6 Move more components to presentation-core module 2023-02-18 16:33:03 -05:00
arkon
bfe143015a Move more components to presentation-core module 2023-02-18 16:03:01 -05:00
arkon
e3cf863230 Start moving some Compose components to presentation-core module 2023-02-18 15:52:52 -05:00
arkon
ee818bc7c5 Move chapter utils to domain module 2023-02-18 15:24:04 -05:00
arkon
f816196df2 Move more things to domain module 2023-02-18 15:14:04 -05:00
arkon
753bf7de5d Bump dependencies 2023-02-18 14:45:04 -05:00
arkon
3634b52e3a Only show unread entries in widget
Closes #9083
2023-02-18 10:51:06 -05:00
Weblate (bot)
ef863335e6 Translations update from Hosted Weblate (#9035)
Weblate translations













































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/bn/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ca/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ceb/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/cs/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/cv/
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/es_419/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fa/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fil/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hi/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hu/
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/km/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ko/
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/ne/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/nl/
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/ro/
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/te/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/th/
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/vi/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hans/
Translation: Tachiyomi/Tachiyomi 0.x

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Co-authored-by: Alessandro Jean <alessandrojean@gmail.com>
Co-authored-by: B4LiN7 <balint.k.furedi@gmail.com>
Co-authored-by: Blue <bluestuffish@gmail.com>
Co-authored-by: C201 <derasetad@gmail.com>
Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Dan <denqwerta@gmail.com>
Co-authored-by: Daniel JB <daniel.jb.1911@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: FateXBlood <zecrofelix@gmail.com>
Co-authored-by: Giorgio Sanna <sannagiorgio1997@gmail.com>
Co-authored-by: Hiroshi <borlonjhayron1119@gmail.com>
Co-authored-by: ID-86 <id86dev@gmail.com>
Co-authored-by: Iftikhar Mahmud <iftikhar.mahmud4@gmail.com>
Co-authored-by: InfinityDouki56 <ced.paltep10@gmail.com>
Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: Kanishka Parankusham <kanishka7878@gmail.com>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: NanoWarrior <jagadeeshvarma.b@gmail.com>
Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Co-authored-by: P6N7L <nichitapospai@gmail.com>
Co-authored-by: PSxUchiha <priyanshusharma1803@outlook.com>
Co-authored-by: PedroJLR <jacobnchrono@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Shippo <Shipox@users.noreply.hosted.weblate.org>
Co-authored-by: Soroush <skaveh1384@gmail.com>
Co-authored-by: Swyter <swyterzone@gmail.com>
Co-authored-by: Uzuki Shimamura <hzy980512@126.com>
Co-authored-by: Vetle Ledaal <vetle.ledaal@gmail.com>
Co-authored-by: Walter Alonso <waljoalbri@gmail.com>
Co-authored-by: altinat <altinat@duck.com>
Co-authored-by: aşina orkan göksel aşina <examplehuman@outlook.com>
Co-authored-by: beerpsi <lacvtg.a1.2023@gmail.com>
Co-authored-by: jinu147 <nesqea20@gmail.com>
Co-authored-by: saurus <sokphanun@gmail.com>
Co-authored-by: seew3l <luisrleccar@hotmail.com>
Co-authored-by: shadowzephyr <shadowzephyr88@gmail.com>
Co-authored-by: ssantos <ssantos@web.de>
Co-authored-by: stevenlele <stevenlele@outlook.com>
2023-02-18 10:20:33 -05:00
arkon
ceaf579cb0 Avoid crashing if getChapterUrl is not implemented
Fixes #9105
2023-02-18 10:16:17 -05:00
arkon
b49280e347 Remove unused Rx/Coroutines converters 2023-02-18 10:16:05 -05:00
Ivan Iskandar
d3dadf71e8 MainActivity: Avoid navigator-related crash when handling onNewIntent (#9104) 2023-02-18 10:08:37 -05:00
Two-Ai
ffa8c8fd07 Remove RxJava in PageHolder (#9103)
Inline readImageHeaderSubscription in PageHolder

Inline readImageHeaderSubscription in PagerPageHolder and
WebtoonPageHolder by converting setImage() into a suspend function.
The image processing runs in the loadPageAndProcessStatus
continuation.

Use suspendCancellableCoroutine as a substitute for doOnUnsubscribe
in WebtoonPageHolder.
Closing openStream after the frame.setImage but before the PageHolder
is recycled causes the page display to fail for reasons that are not
currently understood.

Remove subscription handling from WebtoonViewer/WebtoonBaseHolder as
it is no longer used.
2023-02-18 10:07:27 -05:00
arkon
0ef7650c1a Avoid crashing if opening browse with unavailable source 2023-02-15 22:47:47 -05:00
Two-Ai
4635e58405 Simplify PageHolder load Job (#9086)
Inline statusJob into loadJob, using supervisorScope to load the page
and track status changes in parallel.
- supervisorScope does not complete until both the child loadPage
  coroutine and statusFlow.collectLatest have completed.
- Cancelling supervisorScope cancels the child loadPage coroutine and
  statusFlow.collectLatest.
- Use supervisorScope instead of coroutineScope to let status
  collection continue if loadPage fails.

Inline progressJob into loadJob, using collectLatest's cancellation
to avoid cancelling the progressFlow collection explicitly.
- collectLatest cancels the previous action block when the flow
  emits a new value. This means the DOWNLOAD_IMAGE
  progressFlow.collectLatest gets automatically cancelled when
  statusFlow emits a new state.

Convert launchLoadJob to suspend function, move job launch to caller,
and rename as loadPageAndProcessStatus.
2023-02-15 22:24:55 -05:00
Two-Ai
dc2eaf0788 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.
2023-02-14 11:46:31 -05:00
0x7673
d02b0ca2db Add copy tags to clipboard feature (#9063) 2023-02-13 22:52:10 -05:00
arkon
4d607c4aed Don't apply Wi-Fi network restriction for manual library update jobs
Fixes #9074
2023-02-12 23:15:16 -05:00
Ivan Iskandar
be4072c86b Rework on the wheel picker (#8559)
* Rework the wheel picker

doesn't need for the animation to stop to change the value

* fix

---------

Co-authored-by: arkon <arkon@users.noreply.github.com>
2023-02-12 23:10:47 -05:00
arkon
2970eca9e4 Remove background extensions updates check
Same reasoning as removing app update check. It gets kicked off in the foreground now too.
2023-02-12 23:07:11 -05:00
arkon
42954609b9 Remove background app update check
We already check in the foreground. If the app isn't being foregrounded at all, then there isn't much
point in checking for an update.
2023-02-12 22:44:39 -05:00
arkon
6348cbaeb7 Add option to hide entries already in library when browsing sources
Closes #2941
2023-02-12 22:28:12 -05:00
arkon
a7cb33d8c9 Open global search when Browse is tapped twice
Closes #3925
2023-02-12 18:17:46 -05:00
stevenyomi
ec46b2281b Add note to migration copy string (#9075) 2023-02-12 18:17:39 -05:00
arkon
3a2dc46ff0 Replace Local and In Library badge text with icons
Fixes #5725
2023-02-12 17:22:34 -05:00
arkon
e052bdef96 Move reader preloading to IO scope
Maybe fixes #8440
2023-02-12 16:14:12 -05:00
arkon
d522d6d545 Avoid preload download check if chapter is already loaded or loading
Maybe fixes #8953, #9060
2023-02-12 16:03:24 -05:00
Two-Ai
7b118eba22 Clean up LibraryItem (#9072)
* Move LibraryItem vars to constructor vals

* Convert LibraryItem to data class

Remove redundant equals and hashCode

* Remove unused LibraryItem.displayMode

* Simplify LibraryItem.matches()

* Align types in LibraryItem and LibraryBadges

* fixup! Simplify LibraryItem.matches()
2023-02-12 15:25:27 -05:00
arkon
f6e6a7ddf1 Replace custom download amount with next 25
Simplifies things and maybe discourages whacky downloading behavior?
Users can still range select in the chapters list to download custom amounts.
2023-02-12 15:25:09 -05:00
arkon
5ce64ac7ff Update Cascade
Also clean up kotlinx.serialization versioning
2023-02-12 14:50:41 -05:00
Ivan Iskandar
1671a56f42 MangaCoverDialog: Disable memory cache (#9066) 2023-02-10 22:38:59 -05:00
arkon
ab6dfe9e25 Bump dependencies
Fixes #8168, I think.
2023-02-08 22:53:42 -05:00
arkon
bff98ca768 Clean up chapter item composables a bit
Might help with #9043?
2023-02-08 22:17:40 -05:00
arkon
32b9b261f0 Rename Security settings to Security and privacy
Closes #9049
2023-02-08 21:47:57 -05:00
arkon
23432e4405 Prioritize finding selected chapter when deduping reader chapters
Fixes #9054
2023-02-08 21:47:57 -05:00
Ivan Iskandar
34a586ce48 Scaffold: Fix snackbar bottom inset (#9052) 2023-02-08 09:37:12 -05:00
Ivan Iskandar
ad762f8303 Remove FAB extra padding in DownloadQueueScreen (#9053) 2023-02-08 09:37:04 -05:00
arkon
389b039679 Update version check for library update job migration
Forgot to bump as part of fixing merge conflict for last commit.
2023-02-07 23:22:56 -05:00
Ivan Iskandar
ef9dacde79 Fully utilize WorkManager for library updates (#9007)
No more trampolining, and stuff.

It's pretty much straight copy-paste from the service, with
some changes related to cancellation handling. Manual updates
will also runs with workman job so auto update work
scheduling need some adjustments too.

Bumped version code to re-enqueue auto update job with the
new spec.

Co-authored-by: arkon <arkon@users.noreply.github.com>
2023-02-07 22:37:20 -05:00
0x7673
13bb45b4be Fix crash in library when selected category is deleted (#9044) 2023-02-07 22:19:46 -05:00
Two-Ai
bd2cb97179 Replace RxJava in DownloadQueue (#9016)
* Misc cleanup

- Replace !List.isEmpty with List.isNotEmpty
- Remove redundant case in MoreScreenModel
- Drop no-op StateFlow.catch
  - From lint warning:
> SharedFlow never completes, so this operator typically has not
> effect, it can only catch exceptions from 'onSubscribe' operator

* Convert DownloadQueue queue to MutableStateFlow

Replace delegation to a MutableList with an internal
MutableStateFlow<List>.

In order to avoid modifying every usage of the queue as a list, add
passthrough functions for the currently used list functions. This
should be later refactored, possibly by inlining DownloadQueue
into Downloader.

DownloadQueue.updates was a SharedFlow which updated every time a
change was made to the queue. This is now equivalent to the queue
StateFlow.

Simultaneous assignments to _state.value could cause concurrency
issues. To avoid this, always modify the queue using _state.update.

* Add Download.statusFlow/progressFlow

progressFlow is based on the DownloadQueueScreenModel implementation
rather than the DownloadQueue implementation.

* Reimplement DownloadQueue.statusFlow/progressFlow

Use StateFlow<List<T>>.flatMapLatest() and List<Flow<T>>.merge() to
replicate the effect of PublishSubject.

Use drop(1) to avoid re-emitting the state of each download each time
the merged flow is recreated.

* fixup! Reimplement DownloadQueue.statusFlow/progressFlow
2023-02-07 22:13:19 -05:00
stevenyomi
0d8f1c8560 Tweak Response.parseAs() to utilize intrinsics (#9047) 2023-02-07 22:10:28 -05:00
932 changed files with 62471 additions and 45221 deletions

View File

@@ -3,5 +3,5 @@ indent_size=4
insert_final_newline=true
ij_kotlin_allow_trailing_comma=true
ij_kotlin_allow_trailing_comma_on_call_site=true
ij_kotlin_name_count_to_use_star_import = 2147483647
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
ij_kotlin_name_count_to_use_star_import=2147483647
ij_kotlin_name_count_to_use_star_import_for_members=2147483647

View File

@@ -5,7 +5,7 @@ I acknowledge that:
- I have updated:
- To the latest version of the app (stable is v0.14.7)
- All extensions
- I have tried the troubleshooting guide: https://tachiyomi.org/help/guides/troubleshooting-problems/
- I have gone through the FAQ (https://tachiyomi.org/docs/faq/general) and troubleshooting guide (https://tachiyomi.org/docs/guides/troubleshooting/)
- If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions
- I have searched the existing issues and this is new ticket **NOT** a duplicate or related to another open or closed issue
- I will fill out the title and the information in this template

View File

@@ -4,8 +4,8 @@ contact_links:
url: https://github.com/tachiyomiorg/tachiyomi-extensions/issues/new/choose
about: Issues and requests for extensions and sources should be opened in the tachiyomi-extensions repository instead
- name: 📦 Tachiyomi extensions
url: https://tachiyomi.org/extensions
url: https://tachiyomi.org/extensions/
about: List of all available extensions with download links
- name: 🖥️ Tachiyomi website
url: https://tachiyomi.org/help/
url: https://tachiyomi.org/
about: Guides, troubleshooting, and answers to common questions

View File

@@ -96,7 +96,7 @@ body:
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).
required: true
- label: I have tried the [troubleshooting guide](https://tachiyomi.org/help/guides/troubleshooting/).
- label: I have gone through the [FAQ](https://tachiyomi.org/docs/faq/general) and [troubleshooting guide](https://tachiyomi.org/docs/guides/troubleshooting/).
required: true
- label: I have updated the app to version **[0.14.7](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**.
required: true

12
.github/renovate.json vendored
View File

@@ -1,12 +0,0 @@
{
"extends": [
"config:base"
],
"schedule": ["every sunday"],
"ignoreDeps": [
"androidx.core:core-splashscreen",
"com.android.tools:r8",
"com.google.guava:guava",
"com.github.commandiron:WheelPickerCompose"
]
}

17
.github/renovate.json5 vendored Normal file
View File

@@ -0,0 +1,17 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
],
"schedule": ["every sunday"],
"packageRules": [
{
// Compiler plugins are tightly coupled to Kotlin version
"groupName": "Kotlin",
"matchPackagePrefixes": [
"androidx.compose.compiler",
"org.jetbrains.kotlin",
],
}
]
}

View File

@@ -19,7 +19,7 @@ jobs:
steps:
- name: Clone repo
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
@@ -27,13 +27,13 @@ jobs:
- name: Dependency Review
uses: actions/dependency-review-action@v3
- name: Set up JDK 11
- name: Set up JDK
uses: actions/setup-java@v3
with:
java-version: 11
java-version: 17
distribution: adopt
- name: Build app and run unit tests
uses: gradle/gradle-command-action@v2
with:
arguments: lintKotlin assembleStandardRelease testStandardReleaseUnitTest
arguments: ktlintCheck assembleStandardRelease testReleaseUnitTest

View File

@@ -17,21 +17,21 @@ jobs:
steps:
- name: Clone repo
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
- name: Set up JDK 11
- name: Set up JDK
uses: actions/setup-java@v3
with:
java-version: 11
java-version: 17
distribution: adopt
- name: Build app and run unit tests
uses: gradle/gradle-command-action@v2
with:
arguments: lintKotlin assembleStandardRelease testStandardReleaseUnitTest
arguments: ktlintCheck assembleStandardRelease testReleaseUnitTest
# Sign APK and create release for tags

View File

@@ -11,9 +11,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Moderate issues
uses: tachiyomiorg/issue-moderator-action@v1
uses: tachiyomiorg/issue-moderator-action@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
duplicate-label: Duplicate
auto-close-rules: |
[
{
@@ -31,5 +33,13 @@ jobs:
"regex": "^(?!.*myanimelist.*).*(aniyomi|anime).*$",
"ignoreCase": true,
"message": "Tachiyomi does not support anime, and has no plans to support anime. In addition Tachiyomi is not affiliated with Aniyomi https://github.com/jmir1/aniyomi"
},
{
"type": "both",
"regex": ".*(?:fail(?:ed|ure|s)?|can\\s*(?:no|')?t|(?:not|un).*able|(?<!n[o']?t )blocked by|error) (?:to )?(?:get past|by ?pass|penetrate)?.*cloud ?fl?are.*",
"ignoreCase": true,
"labels": ["Cloudflare protected"],
"message": "Refer to the **Solving Cloudflare issues** section at https://tachiyomi.org/docs/guides/troubleshooting/#cloudflare. If it doesn't work, migrate to other sources or wait until they lower their protection."
}
]
auto-close-ignore-label: do-not-autoclose

3
.gitignore vendored
View File

@@ -2,7 +2,8 @@
/local.properties
/.idea/workspace.xml
.DS_Store
.idea/
.idea/*
!.idea/icon.png
*iml
*.iml

BIN
.idea/icon.png generated Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -24,13 +24,17 @@ Before you start, please note that the ability to use following technologies is
- [Android Studio](https://developer.android.com/studio)
- Emulator or phone with developer options enabled to test changes.
## Linting
To auto-fix some linting errors, run the `ktlintFormat` Gradle task.
## Getting help
- Join [the Discord server](https://discord.gg/tachiyomi) for online help and to ask questions while developing.
# Translations
Translations are done externally via Weblate. See [our website](https://tachiyomi.org/help/contribution/#translation) for more details.
Translations are done externally via Weblate. See [our website](https://tachiyomi.org/docs/contribute#translation) for more details.
# Forks

View File

@@ -1,6 +1,6 @@
| Build | Stable | Weekly Preview | Contribute | Support Server |
|-------|----------|---------|------------|---------|
| ![CI](https://github.com/tachiyomiorg/tachiyomi/workflows/CI/badge.svg?branch=dev&event=push) | [![stable release](https://img.shields.io/github/release/tachiyomiorg/tachiyomi.svg?maxAge=3600&label=download)](https://github.com/tachiyomiorg/tachiyomi/releases) | [![latest preview build](https://img.shields.io/github/v/release/tachiyomiorg/tachiyomi-preview.svg?maxAge=3600&label=download)](https://github.com/tachiyomiorg/tachiyomi-preview/releases) | [![Translation status](https://hosted.weblate.org/widgets/tachiyomi/-/svg-badge.svg)](https://hosted.weblate.org/engage/tachiyomi/?utm_source=widget) | [![Discord](https://img.shields.io/discord/349436576037732353.svg?label=discord&labelColor=7289da&color=2c2f33&style=flat)](https://discord.gg/tachiyomi) |
| [![CI](https://github.com/tachiyomiorg/tachiyomi/actions/workflows/build_push.yml/badge.svg)](https://github.com/tachiyomiorg/tachiyomi/actions/workflows/build_push.yml) | [![stable release](https://img.shields.io/github/release/tachiyomiorg/tachiyomi.svg?maxAge=3600&label=download)](https://github.com/tachiyomiorg/tachiyomi/releases) | [![latest preview build](https://img.shields.io/github/v/release/tachiyomiorg/tachiyomi-preview.svg?maxAge=3600&label=download)](https://github.com/tachiyomiorg/tachiyomi-preview/releases) | [![Translation status](https://hosted.weblate.org/widgets/tachiyomi/-/svg-badge.svg)](https://hosted.weblate.org/engage/tachiyomi/?utm_source=widget) | [![Discord](https://img.shields.io/discord/349436576037732353.svg?label=discord&labelColor=7289da&color=2c2f33&style=flat)](https://discord.gg/tachiyomi) |
# ![app icon](./.github/readme-images/app-icon.png)Tachiyomi
@@ -29,7 +29,7 @@ Please make sure to read the full guidelines. Your issue may be closed without w
<details><summary>Issues</summary>
1. **Before reporting a new issue, take a look at the [FAQ](https://tachiyomi.org/help/faq/), the [changelog](https://github.com/tachiyomiorg/tachiyomi/releases) and the already opened [issues](https://github.com/tachiyomiorg/tachiyomi/issues).**
1. **Before reporting a new issue, take a look at the [FAQ](https://tachiyomi.org/docs/faq/general), the [changelog](https://tachiyomi.org/changelogs/) and the already opened [issues](https://github.com/tachiyomiorg/tachiyomi/issues).**
2. If you are unsure, ask here: [![Discord](https://img.shields.io/discord/349436576037732353.svg)](https://discord.gg/tachiyomi)
</details>
@@ -38,7 +38,7 @@ Please make sure to read the full guidelines. Your issue may be closed without w
* Include version (More → About → Version)
* If not latest, try updating, it may have already been solved
* Preview version is equal to the number of commits as seen in the main page
* Preview version is equal to the number of commits as seen on the main page
* Include steps to reproduce (if not obvious from description)
* Include screenshot (if needed)
* If it could be device-dependent, try reproducing on another device (if possible)

1
app/.gitignore vendored
View File

@@ -1,4 +1,3 @@
/build
*iml
*.iml
custom.gradle

View File

@@ -1,5 +1,4 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jmailen.gradle.kotlinter.tasks.LintTask
plugins {
id("com.android.application")
@@ -22,7 +21,8 @@ android {
defaultConfig {
applicationId = "eu.kanade.tachiyomi"
versionCode = 102
versionCode = 108
versionName = "0.14.7"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
@@ -65,11 +65,11 @@ android {
initWith(getByName("release"))
buildConfigField("boolean", "PREVIEW", "true")
signingConfig = signingConfigs.getByName("debug")
matchingFallbacks.add("release")
val debugType = getByName("debug")
signingConfig = debugType.signingConfig
versionNameSuffix = debugType.versionNameSuffix
applicationIdSuffix = debugType.applicationIdSuffix
matchingFallbacks.add("release")
}
create("benchmark") {
initWith(getByName("release"))
@@ -77,6 +77,7 @@ android {
signingConfig = signingConfigs.getByName("debug")
matchingFallbacks.add("release")
isDebuggable = false
isProfileable = true
versionNameSuffix = "-benchmark"
applicationIdSuffix = ".benchmark"
}
@@ -101,16 +102,18 @@ android {
}
}
packagingOptions {
resources.excludes.addAll(listOf(
"META-INF/DEPENDENCIES",
"LICENSE.txt",
"META-INF/LICENSE",
"META-INF/LICENSE.txt",
"META-INF/README.md",
"META-INF/NOTICE",
"META-INF/*.kotlin_module",
))
packaging {
resources.excludes.addAll(
listOf(
"META-INF/DEPENDENCIES",
"LICENSE.txt",
"META-INF/LICENSE",
"META-INF/LICENSE.txt",
"META-INF/README.md",
"META-INF/NOTICE",
"META-INF/*.kotlin_module",
),
)
}
dependenciesInfo {
@@ -140,7 +143,9 @@ android {
dependencies {
implementation(project(":i18n"))
implementation(project(":core"))
implementation(project(":core-metadata"))
implementation(project(":source-api"))
implementation(project(":source-local"))
implementation(project(":data"))
implementation(project(":domain"))
implementation(project(":presentation-core"))
@@ -155,10 +160,10 @@ dependencies {
implementation(compose.material.icons)
implementation(compose.animation)
implementation(compose.animation.graphics)
implementation(compose.ui.tooling)
debugImplementation(compose.ui.tooling)
implementation(compose.ui.tooling.preview)
implementation(compose.ui.util)
implementation(compose.accompanist.webview)
implementation(compose.accompanist.flowlayout)
implementation(compose.accompanist.permissions)
implementation(compose.accompanist.themeadapter)
implementation(compose.accompanist.systemuicontroller)
@@ -178,7 +183,6 @@ dependencies {
implementation(androidx.appcompat)
implementation(androidx.biometricktx)
implementation(androidx.constraintlayout)
implementation(androidx.coordinatorlayout)
implementation(androidx.corektx)
implementation(androidx.splashscreen)
implementation(androidx.recyclerview)
@@ -188,20 +192,18 @@ dependencies {
implementation(androidx.bundles.lifecycle)
// Job scheduling
implementation(androidx.bundles.workmanager)
implementation(androidx.workmanager)
// RX
implementation(libs.bundles.reactivex)
// RxJava
implementation(libs.rxjava)
implementation(libs.flowreactivenetwork)
// Network client
// Networking
implementation(libs.bundles.okhttp)
implementation(libs.okio)
implementation(libs.conscrypt.android) // TLS 1.3 support for Android < 10
// TLS 1.3 support for Android < 10
implementation(libs.conscrypt.android)
// Data serialization (JSON, protobuf)
// Data serialization (JSON, protobuf, xml)
implementation(kotlinx.bundles.serialization)
// HTML parser
@@ -219,19 +221,16 @@ dependencies {
implementation(libs.injekt.core)
// Image loading
implementation(platform(libs.coil.bom))
implementation(libs.bundles.coil)
implementation(libs.subsamplingscaleimageview) {
exclude(module = "image-decoder")
}
implementation(libs.image.decoder)
// Sort
implementation(libs.natural.comparator)
// UI libraries
implementation(libs.material)
implementation(libs.flexible.adapter.core)
implementation(libs.flexible.adapter.ui)
implementation(libs.photoview)
implementation(libs.directionalviewpager) {
exclude(group = "androidx.viewpager", module = "viewpager")
@@ -239,10 +238,9 @@ dependencies {
implementation(libs.insetter)
implementation(libs.bundles.richtext)
implementation(libs.aboutLibraries.compose)
implementation(libs.cascade)
implementation(libs.bundles.voyager)
implementation(libs.wheelpicker)
implementation(libs.materialmotion.core)
implementation(libs.compose.materialmotion)
implementation(libs.swipe)
// Logging
implementation(libs.logcat)
@@ -255,7 +253,7 @@ dependencies {
implementation(libs.bundles.shizuku)
// Tests
testImplementation(libs.junit)
testImplementation(libs.bundles.test)
// For detecting memory leaks; see https://square.github.io/leakcanary/
// debugImplementation(libs.leakcanary.android)
@@ -266,7 +264,9 @@ androidComponents {
beforeVariants { variantBuilder ->
// Disables standardBenchmark
if (variantBuilder.buildType == "benchmark") {
variantBuilder.enable = variantBuilder.productFlavors.containsAll(listOf("default" to "dev"))
variantBuilder.enable = variantBuilder.productFlavors.containsAll(
listOf("default" to "dev"),
)
}
}
onVariants(selector().withFlavor("default" to "standard")) {
@@ -277,16 +277,10 @@ androidComponents {
}
tasks {
withType<LintTask>().configureEach {
exclude { it.file.path.contains("generated[\\\\/]".toRegex()) }
}
// See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers)
withType<KotlinCompile> {
kotlinOptions.freeCompilerArgs += listOf(
"-opt-in=coil.annotation.ExperimentalCoilApi",
"-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi",
"-Xcontext-receivers",
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
"-opt-in=androidx.compose.material.ExperimentalMaterialApi",
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
@@ -295,6 +289,8 @@ tasks {
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
"-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi",
"-opt-in=coil.annotation.ExperimentalCoilApi",
"-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-opt-in=kotlinx.coroutines.FlowPreview",
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi",
@@ -305,12 +301,12 @@ tasks {
kotlinOptions.freeCompilerArgs += listOf(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
project.buildDir.absolutePath + "/compose_metrics"
project.layout.buildDirectory.dir("compose_metrics").get().asFile.absolutePath,
)
kotlinOptions.freeCompilerArgs += listOf(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
project.buildDir.absolutePath + "/compose_metrics"
project.layout.buildDirectory.dir("compose_metrics").get().asFile.absolutePath,
)
}
}

View File

@@ -1,4 +1,5 @@
-dontusemixedcaseclassnames
-ignorewarnings
-verbose
-keepattributes *Annotation*
@@ -13,7 +14,7 @@
}
-keepclassmembers class * implements android.os.Parcelable {
public static final ** CREATOR;
public static final ** CREATOR;
}
-keep class androidx.annotation.Keep

View File

@@ -1,14 +1,18 @@
-dontobfuscate
-keep,allowoptimization class eu.kanade.**
-keep,allowoptimization class tachiyomi.**
# Keep common dependencies used in extensions
-keep,allowoptimization class androidx.preference.** { public protected *; }
-keep,allowoptimization class kotlin.** { public protected *; }
-keep,allowoptimization class kotlinx.coroutines.** { public protected *; }
-keep,allowoptimization class kotlinx.serialization.** { public protected *; }
-keep,allowoptimization class kotlin.time.** { public protected *; }
-keep,allowoptimization class okhttp3.** { public protected *; }
-keep,allowoptimization class okio.** { public protected *; }
-keep,allowoptimization class rx.** { public protected *; }
-keep,allowoptimization class org.jsoup.** { public protected *; }
-keep,allowoptimization class rx.** { public protected *; }
-keep,allowoptimization class app.cash.quickjs.** { public protected *; }
-keep,allowoptimization class uy.kohesive.injekt.** { public protected *; }
@@ -66,4 +70,8 @@
##---------------End: proguard configuration for kotlinx.serialization ----------
# XmlUtil
-keep public enum nl.adaptivity.xmlutil.EventType { *; }
-keep public enum nl.adaptivity.xmlutil.EventType { *; }
# Firebase
-keep class com.google.firebase.installations.** { *; }
-keep interface com.google.firebase.installations.** { *; }

View File

@@ -1,5 +1,4 @@
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut
android:enabled="true"
android:icon="@drawable/sc_collections_bookmark_48dp"

View File

@@ -32,21 +32,17 @@
<application
android:name=".App"
android:allowBackup="false"
android:enableOnBackInvokedCallback="true"
android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:localeConfig="@xml/locales_config"
android:networkSecurityConfig="@xml/network_security_config"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/Theme.Tachiyomi"
android:supportsRtl="true"
android:networkSecurityConfig="@xml/network_security_config">
<!-- enable profiling by macrobenchmark -->
<profileable
android:shell="true"
tools:targetApi="q" />
android:theme="@style/Theme.Tachiyomi">
<activity
android:name=".ui.main.MainActivity"
@@ -69,10 +65,10 @@
android:exported="false" />
<activity
android:name=".ui.main.DeepLinkActivity"
android:name=".ui.deeplink.DeepLinkActivity"
android:launchMode="singleTask"
android:theme="@android:style/Theme.NoDisplay"
android:label="@string/action_global_search"
android:label="@string/action_search"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
@@ -123,8 +119,8 @@
android:exported="false" />
<activity
android:name=".ui.setting.track.AnilistLoginActivity"
android:label="Anilist"
android:name=".ui.setting.track.TrackLoginActivity"
android:label="@string/track_activity_name"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@@ -132,54 +128,12 @@
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="anilist-auth"
android:scheme="tachiyomi" />
</intent-filter>
</activity>
<activity
android:name=".ui.setting.track.MyAnimeListLoginActivity"
android:label="MyAnimeList"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:host="anilist-auth"/>
<data android:host="bangumi-auth"/>
<data android:host="myanimelist-auth"/>
<data android:host="shikimori-auth"/>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="myanimelist-auth"
android:scheme="tachiyomi" />
</intent-filter>
</activity>
<activity
android:name=".ui.setting.track.ShikimoriLoginActivity"
android:label="Shikimori"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="shikimori-auth"
android:scheme="tachiyomi" />
</intent-filter>
</activity>
<activity
android:name=".ui.setting.track.BangumiLoginActivity"
android:label="Bangumi"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="bangumi-auth"
android:scheme="tachiyomi" />
<data android:scheme="tachiyomi"/>
</intent-filter>
</activity>
@@ -187,37 +141,12 @@
android:name=".data.notification.NotificationReceiver"
android:exported="false" />
<receiver
android:name="tachiyomi.presentation.widget.UpdatesGridGlanceReceiver"
android:enabled="@bool/glance_appwidget_available"
android:exported="false"
android:label="@string/label_recent_updates">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/updates_grid_glance_widget_info" />
</receiver>
<service
android:name=".data.library.LibraryUpdateService"
android:exported="false" />
<service
android:name=".data.download.DownloadService"
android:exported="false" />
<service
android:name=".data.updater.AppUpdateService"
android:exported="false" />
<service
android:name=".data.backup.BackupRestoreService"
android:exported="false" />
<service android:name=".extension.util.ExtensionInstallService"
android:name=".extension.util.ExtensionInstallService"
android:exported="false" />
<service

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
package eu.kanade.core.preference
import androidx.compose.ui.state.ToggleableState
import tachiyomi.core.preference.CheckboxState
fun <T> CheckboxState.TriState<T>.asToggleableState() = when (this) {
is CheckboxState.TriState.Exclude -> ToggleableState.Indeterminate
is CheckboxState.TriState.Include -> ToggleableState.On
is CheckboxState.TriState.None -> ToggleableState.Off
}

View File

@@ -1,4 +1,4 @@
package eu.kanade.core.prefs
package eu.kanade.core.preference
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
@@ -31,7 +31,7 @@ class PreferenceMutableState<T>(
}
override fun component2(): (T) -> Unit {
return { preference.set(it) }
return preference::set
}
}

View File

@@ -1,7 +1,6 @@
package eu.kanade.core.util
import androidx.compose.ui.util.fastForEach
import java.util.concurrent.ConcurrentHashMap
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@@ -12,23 +11,14 @@ fun <T : R, R : Any> List<T>.insertSeparators(
val newList = mutableListOf<R>()
for (i in -1..lastIndex) {
val before = getOrNull(i)
before?.let { newList.add(it) }
before?.let(newList::add)
val after = getOrNull(i + 1)
val separator = generator.invoke(before, after)
separator?.let { newList.add(it) }
separator?.let(newList::add)
}
return newList
}
/**
* Returns a new map containing only the key entries of [transform] that are not null.
*/
inline fun <K, V, R> Map<out K, V>.mapNotNullKeys(transform: (Map.Entry<K?, V>) -> R?): ConcurrentHashMap<R, V> {
val mutableMap = ConcurrentHashMap<R, V>()
forEach { element -> transform(element)?.let { mutableMap[it] = element.value } }
return mutableMap
}
fun <E> HashSet<E>.addOrRemove(value: E, shouldAdd: Boolean) {
if (shouldAdd) {
add(value)
@@ -80,7 +70,7 @@ inline fun <T, R> List<T>.fastMapNotNull(transform: (T) -> R?): List<R> {
contract { callsInPlace(transform) }
val destination = ArrayList<R>()
fastForEach { element ->
transform(element)?.let { destination.add(it) }
transform(element)?.let(destination::add)
}
return destination
}

View File

@@ -1,16 +0,0 @@
package eu.kanade.core.util
import android.content.Context
import eu.kanade.tachiyomi.R
import kotlin.time.Duration
fun Duration.toDurationString(context: Context, fallback: String): String {
return toComponents { days, hours, minutes, seconds, _ ->
buildList(4) {
if (days != 0L) add(context.getString(R.string.day_short, days))
if (hours != 0) add(context.getString(R.string.hour_short, hours))
if (minutes != 0 && (days == 0L || hours == 0)) add(context.getString(R.string.minute_short, minutes))
if (seconds != 0 && days == 0L && hours == 0) add(context.getString(R.string.seconds_short, seconds))
}.joinToString(" ").ifBlank { fallback }
}
}

View File

@@ -1,61 +0,0 @@
package eu.kanade.core.util
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.launch
import rx.Emitter
import rx.Observable
import rx.Observer
import kotlin.coroutines.CoroutineContext
fun <T : Any> Observable<T>.asFlow(): Flow<T> = callbackFlow {
val observer = object : Observer<T> {
override fun onNext(t: T) {
trySend(t)
}
override fun onError(e: Throwable) {
close(e)
}
override fun onCompleted() {
close()
}
}
val subscription = subscribe(observer)
awaitClose { subscription.unsubscribe() }
}
fun <T : Any> Flow<T>.asObservable(
context: CoroutineContext = Dispatchers.Unconfined,
backpressureMode: Emitter.BackpressureMode = Emitter.BackpressureMode.NONE,
): Observable<T> {
return Observable.create(
{ emitter ->
/*
* ATOMIC is used here to provide stable behaviour of subscribe+dispose pair even if
* asObservable is already invoked from unconfined
*/
val job = GlobalScope.launch(context = context, start = CoroutineStart.ATOMIC) {
try {
collect { emitter.onNext(it) }
emitter.onCompleted()
} catch (e: Throwable) {
// Ignore `CancellationException` as error, since it indicates "normal cancellation"
if (e !is CancellationException) {
emitter.onError(e)
} else {
emitter.onCompleted()
}
}
}
emitter.setCancellation { job.cancel() }
},
backpressureMode,
)
}

View File

@@ -1,19 +0,0 @@
package eu.kanade.data.source
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.SourceManager
import tachiyomi.domain.source.model.Source
val sourceMapper: (eu.kanade.tachiyomi.source.Source) -> Source = { source ->
Source(
source.id,
source.lang,
source.name,
supportsLatest = false,
isStub = source is SourceManager.StubSource,
)
}
val catalogueSourceMapper: (CatalogueSource) -> Source = { source ->
sourceMapper(source).copy(supportsLatest = source.supportsLatest)
}

View File

@@ -1,74 +0,0 @@
package eu.kanade.data.source
import eu.kanade.domain.source.model.SourcePagingSourceType
import eu.kanade.domain.source.repository.SourceRepository
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.FilterList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import tachiyomi.data.DatabaseHandler
import tachiyomi.domain.source.model.Source
import tachiyomi.domain.source.model.SourceWithCount
class SourceRepositoryImpl(
private val sourceManager: SourceManager,
private val handler: DatabaseHandler,
) : SourceRepository {
override fun getSources(): Flow<List<Source>> {
return sourceManager.catalogueSources.map { sources ->
sources.map(catalogueSourceMapper)
}
}
override fun getOnlineSources(): Flow<List<Source>> {
return sourceManager.onlineSources.map { sources ->
sources.map(sourceMapper)
}
}
override fun getSourcesWithFavoriteCount(): Flow<List<Pair<Source, Long>>> {
val sourceIdWithFavoriteCount = handler.subscribeToList { mangasQueries.getSourceIdWithFavoriteCount() }
return sourceIdWithFavoriteCount.map { sourceIdsWithCount ->
sourceIdsWithCount
.filterNot { it.source == LocalSource.ID }
.map { (sourceId, count) ->
val source = sourceManager.getOrStub(sourceId).run {
sourceMapper(this)
}
source to count
}
}
}
override fun getSourcesWithNonLibraryManga(): Flow<List<SourceWithCount>> {
val sourceIdWithNonLibraryManga = handler.subscribeToList { mangasQueries.getSourceIdsWithNonLibraryManga() }
return sourceIdWithNonLibraryManga.map { sourceId ->
sourceId.map { (sourceId, count) ->
val source = sourceManager.getOrStub(sourceId)
SourceWithCount(sourceMapper(source), count)
}
}
}
override fun search(
sourceId: Long,
query: String,
filterList: FilterList,
): SourcePagingSourceType {
val source = sourceManager.get(sourceId) as CatalogueSource
return SourceSearchPagingSource(source, query, filterList)
}
override fun getPopular(sourceId: Long): SourcePagingSourceType {
val source = sourceManager.get(sourceId) as CatalogueSource
return SourcePopularPagingSource(source)
}
override fun getLatest(sourceId: Long): SourcePagingSourceType {
val source = sourceManager.get(sourceId) as CatalogueSource
return SourceLatestPagingSource(source)
}
}

View File

@@ -1,69 +1,78 @@
package eu.kanade.domain
import eu.kanade.data.source.SourceRepositoryImpl
import eu.kanade.domain.category.interactor.CreateCategoryWithName
import eu.kanade.domain.category.interactor.DeleteCategory
import eu.kanade.domain.category.interactor.RenameCategory
import eu.kanade.domain.category.interactor.ReorderCategory
import eu.kanade.domain.category.interactor.ResetCategoryFlags
import eu.kanade.domain.category.interactor.SetDisplayModeForCategory
import eu.kanade.domain.category.interactor.SetMangaCategories
import eu.kanade.domain.category.interactor.SetSortModeForCategory
import eu.kanade.domain.category.interactor.UpdateCategory
import eu.kanade.domain.chapter.interactor.GetChapter
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
import eu.kanade.domain.chapter.interactor.SetMangaDefaultChapterFlags
import eu.kanade.domain.chapter.interactor.SetReadStatus
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
import eu.kanade.domain.chapter.interactor.UpdateChapter
import eu.kanade.domain.download.interactor.DeleteDownload
import eu.kanade.domain.extension.interactor.GetExtensionLanguages
import eu.kanade.domain.extension.interactor.GetExtensionSources
import eu.kanade.domain.extension.interactor.GetExtensionsByType
import eu.kanade.domain.history.interactor.GetNextChapters
import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga
import eu.kanade.domain.manga.interactor.GetFavorites
import eu.kanade.domain.manga.interactor.GetLibraryManga
import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.GetMangaWithChapters
import eu.kanade.domain.manga.interactor.NetworkToLocalManga
import eu.kanade.domain.manga.interactor.ResetViewerFlags
import eu.kanade.domain.manga.interactor.SetMangaChapterFlags
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.source.interactor.GetEnabledSources
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
import eu.kanade.domain.source.interactor.GetRemoteManga
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
import eu.kanade.domain.source.interactor.GetSourcesWithNonLibraryManga
import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.domain.source.interactor.ToggleLanguage
import eu.kanade.domain.source.interactor.ToggleSource
import eu.kanade.domain.source.interactor.ToggleSourcePin
import eu.kanade.domain.source.repository.SourceRepository
import eu.kanade.domain.track.interactor.DeleteTrack
import eu.kanade.domain.track.interactor.GetTracks
import eu.kanade.domain.track.interactor.GetTracksPerManga
import eu.kanade.domain.track.interactor.InsertTrack
import eu.kanade.domain.track.interactor.AddTracks
import eu.kanade.domain.track.interactor.RefreshTracks
import eu.kanade.domain.track.interactor.SyncChapterProgressWithTrack
import eu.kanade.domain.track.interactor.TrackChapter
import tachiyomi.data.category.CategoryRepositoryImpl
import tachiyomi.data.chapter.ChapterRepositoryImpl
import tachiyomi.data.history.HistoryRepositoryImpl
import tachiyomi.data.manga.MangaRepositoryImpl
import tachiyomi.data.source.SourceDataRepositoryImpl
import tachiyomi.data.release.ReleaseServiceImpl
import tachiyomi.data.source.SourceRepositoryImpl
import tachiyomi.data.source.StubSourceRepositoryImpl
import tachiyomi.data.track.TrackRepositoryImpl
import tachiyomi.data.updates.UpdatesRepositoryImpl
import tachiyomi.domain.category.interactor.CreateCategoryWithName
import tachiyomi.domain.category.interactor.DeleteCategory
import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.category.interactor.RenameCategory
import tachiyomi.domain.category.interactor.ReorderCategory
import tachiyomi.domain.category.interactor.ResetCategoryFlags
import tachiyomi.domain.category.interactor.SetDisplayMode
import tachiyomi.domain.category.interactor.SetMangaCategories
import tachiyomi.domain.category.interactor.SetSortModeForCategory
import tachiyomi.domain.category.interactor.UpdateCategory
import tachiyomi.domain.category.repository.CategoryRepository
import tachiyomi.domain.chapter.interactor.GetChapter
import tachiyomi.domain.chapter.interactor.GetChapterByUrlAndMangaId
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
import tachiyomi.domain.chapter.interactor.SetMangaDefaultChapterFlags
import tachiyomi.domain.chapter.interactor.ShouldUpdateDbChapter
import tachiyomi.domain.chapter.interactor.UpdateChapter
import tachiyomi.domain.chapter.repository.ChapterRepository
import tachiyomi.domain.history.interactor.GetHistory
import tachiyomi.domain.history.interactor.GetNextChapters
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.interactor.FetchInterval
import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga
import tachiyomi.domain.manga.interactor.GetFavorites
import tachiyomi.domain.manga.interactor.GetLibraryManga
import tachiyomi.domain.manga.interactor.GetManga
import tachiyomi.domain.manga.interactor.GetMangaByUrlAndSourceId
import tachiyomi.domain.manga.interactor.GetMangaWithChapters
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
import tachiyomi.domain.manga.interactor.ResetViewerFlags
import tachiyomi.domain.manga.interactor.SetMangaChapterFlags
import tachiyomi.domain.manga.repository.MangaRepository
import tachiyomi.domain.source.repository.SourceDataRepository
import tachiyomi.domain.release.interactor.GetApplicationRelease
import tachiyomi.domain.release.service.ReleaseService
import tachiyomi.domain.source.interactor.GetRemoteManga
import tachiyomi.domain.source.interactor.GetSourcesWithNonLibraryManga
import tachiyomi.domain.source.repository.SourceRepository
import tachiyomi.domain.source.repository.StubSourceRepository
import tachiyomi.domain.track.interactor.DeleteTrack
import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.interactor.GetTracksPerManga
import tachiyomi.domain.track.interactor.InsertTrack
import tachiyomi.domain.track.repository.TrackRepository
import tachiyomi.domain.updates.interactor.GetUpdates
import tachiyomi.domain.updates.repository.UpdatesRepository
@@ -79,7 +88,7 @@ class DomainModule : InjektModule {
addSingletonFactory<CategoryRepository> { CategoryRepositoryImpl(get()) }
addFactory { GetCategories(get()) }
addFactory { ResetCategoryFlags(get(), get()) }
addFactory { SetDisplayModeForCategory(get(), get()) }
addFactory { SetDisplayMode(get()) }
addFactory { SetSortModeForCategory(get(), get()) }
addFactory { CreateCategoryWithName(get(), get()) }
addFactory { RenameCategory(get()) }
@@ -92,30 +101,39 @@ class DomainModule : InjektModule {
addFactory { GetFavorites(get()) }
addFactory { GetLibraryManga(get()) }
addFactory { GetMangaWithChapters(get(), get()) }
addFactory { GetMangaByUrlAndSourceId(get()) }
addFactory { GetManga(get()) }
addFactory { GetNextChapters(get(), get(), get()) }
addFactory { ResetViewerFlags(get()) }
addFactory { SetMangaChapterFlags(get()) }
addFactory { FetchInterval(get()) }
addFactory { SetMangaDefaultChapterFlags(get(), get(), get()) }
addFactory { SetMangaViewerFlags(get()) }
addFactory { NetworkToLocalManga(get()) }
addFactory { UpdateManga(get()) }
addFactory { UpdateManga(get(), get()) }
addFactory { SetMangaCategories(get()) }
addSingletonFactory<ReleaseService> { ReleaseServiceImpl(get(), get()) }
addFactory { GetApplicationRelease(get(), get()) }
addSingletonFactory<TrackRepository> { TrackRepositoryImpl(get()) }
addFactory { TrackChapter(get(), get(), get(), get()) }
addFactory { AddTracks(get(), get(), get(), get()) }
addFactory { RefreshTracks(get(), get(), get(), get()) }
addFactory { DeleteTrack(get()) }
addFactory { GetTracksPerManga(get()) }
addFactory { GetTracks(get()) }
addFactory { InsertTrack(get()) }
addFactory { SyncChapterProgressWithTrack(get(), get(), get()) }
addSingletonFactory<ChapterRepository> { ChapterRepositoryImpl(get()) }
addFactory { GetChapter(get()) }
addFactory { GetChapterByMangaId(get()) }
addFactory { GetChaptersByMangaId(get()) }
addFactory { GetChapterByUrlAndMangaId(get()) }
addFactory { UpdateChapter(get()) }
addFactory { SetReadStatus(get(), get(), get(), get()) }
addFactory { ShouldUpdateDbChapter() }
addFactory { SyncChaptersWithSource(get(), get(), get(), get()) }
addFactory { SyncChaptersWithTrackServiceTwoWay(get(), get()) }
addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get()) }
addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) }
addFactory { GetHistory(get()) }
@@ -133,7 +151,7 @@ class DomainModule : InjektModule {
addFactory { GetUpdates(get()) }
addSingletonFactory<SourceRepository> { SourceRepositoryImpl(get(), get()) }
addSingletonFactory<SourceDataRepository> { SourceDataRepositoryImpl(get()) }
addSingletonFactory<StubSourceRepository> { StubSourceRepositoryImpl(get()) }
addFactory { GetEnabledSources(get(), get()) }
addFactory { GetLanguagesWithSources(get(), get()) }
addFactory { GetRemoteManga(get()) }

View File

@@ -1,8 +1,11 @@
package eu.kanade.domain.base
import android.content.Context
import androidx.annotation.StringRes
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
import eu.kanade.tachiyomi.util.system.isReleaseBuildType
import tachiyomi.core.preference.Preference
import tachiyomi.core.preference.PreferenceStore
class BasePreferences(
@@ -10,15 +13,21 @@ class BasePreferences(
private val preferenceStore: PreferenceStore,
) {
fun confirmExit() = preferenceStore.getBoolean("pref_confirm_exit", false)
fun downloadedOnly() = preferenceStore.getBoolean(
Preference.appStateKey("pref_downloaded_only"),
false,
)
fun downloadedOnly() = preferenceStore.getBoolean("pref_downloaded_only", false)
fun incognitoMode() = preferenceStore.getBoolean("incognito_mode", false)
fun automaticExtUpdates() = preferenceStore.getBoolean("automatic_ext_updates", true)
fun incognitoMode() = preferenceStore.getBoolean(Preference.appStateKey("incognito_mode"), false)
fun extensionInstaller() = ExtensionInstallerPreference(context, preferenceStore)
fun acraEnabled() = preferenceStore.getBoolean("acra.enable", isPreviewBuildType || isReleaseBuildType)
enum class ExtensionInstaller(@StringRes val titleResId: Int) {
LEGACY(R.string.ext_installer_legacy),
PACKAGEINSTALLER(R.string.ext_installer_packageinstaller),
SHIZUKU(R.string.ext_installer_shizuku),
PRIVATE(R.string.ext_installer_private),
}
}

View File

@@ -1,7 +1,7 @@
package eu.kanade.domain.base
import android.content.Context
import eu.kanade.tachiyomi.data.preference.PreferenceValues.ExtensionInstaller
import eu.kanade.domain.base.BasePreferences.ExtensionInstaller
import eu.kanade.tachiyomi.util.system.hasMiuiPackageInstaller
import eu.kanade.tachiyomi.util.system.isShizukuInstalled
import kotlinx.coroutines.CoroutineScope
@@ -18,7 +18,7 @@ class ExtensionInstallerPreference(
override fun key() = "extension_installer"
val entries get() = ExtensionInstaller.values().run {
val entries get() = ExtensionInstaller.entries.run {
if (context.hasMiuiPackageInstaller) {
filter { it != ExtensionInstaller.PACKAGEINSTALLER }
} else {

View File

@@ -1,17 +0,0 @@
package eu.kanade.domain.category.interactor
import eu.kanade.domain.library.service.LibraryPreferences
import tachiyomi.domain.category.repository.CategoryRepository
import tachiyomi.domain.library.model.plus
class ResetCategoryFlags(
private val preferences: LibraryPreferences,
private val categoryRepository: CategoryRepository,
) {
suspend fun await() {
val display = preferences.libraryDisplayMode().get()
val sort = preferences.librarySortingMode().get()
categoryRepository.updateAllFlags(display + sort.type + sort.direction)
}
}

View File

@@ -1,34 +0,0 @@
package eu.kanade.domain.category.interactor
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(
private val preferences: LibraryPreferences,
private val categoryRepository: CategoryRepository,
) {
suspend fun await(categoryId: Long, display: LibraryDisplayMode) {
val category = categoryRepository.get(categoryId) ?: return
val flags = category.flags + display
if (preferences.categorizedDisplaySettings().get()) {
categoryRepository.updatePartial(
CategoryUpdate(
id = category.id,
flags = flags,
),
)
} else {
preferences.libraryDisplayMode().set(display)
categoryRepository.updateAllFlags(flags)
}
}
suspend fun await(category: Category, display: LibraryDisplayMode) {
await(category.id, display)
}
}

View File

@@ -1,13 +1,13 @@
package eu.kanade.domain.chapter.interactor
import eu.kanade.domain.download.interactor.DeleteDownload
import eu.kanade.domain.download.service.DownloadPreferences
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.download.service.DownloadPreferences
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.repository.MangaRepository
@@ -72,9 +72,9 @@ class SetReadStatus(
suspend fun await(manga: Manga, read: Boolean) =
await(manga.id, read)
sealed class Result {
object Success : Result()
object NoChapters : Result()
data class InternalError(val error: Throwable) : Result()
sealed interface Result {
data object Success : Result
data object NoChapters : Result
data class InternalError(val error: Throwable) : Result
}
}

View File

@@ -7,31 +7,32 @@ import eu.kanade.domain.manga.model.toSManga
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.DownloadProvider
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.isLocal
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.chapter.ChapterRecognition
import tachiyomi.data.chapter.ChapterSanitizer
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
import tachiyomi.domain.chapter.interactor.ShouldUpdateDbChapter
import tachiyomi.domain.chapter.interactor.UpdateChapter
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.chapter.service.ChapterRecognition
import tachiyomi.domain.manga.model.Manga
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import tachiyomi.source.local.isLocal
import java.lang.Long.max
import java.time.ZonedDateTime
import java.util.Date
import java.util.TreeSet
class SyncChaptersWithSource(
private val downloadManager: DownloadManager = Injekt.get(),
private val downloadProvider: DownloadProvider = Injekt.get(),
private val chapterRepository: ChapterRepository = Injekt.get(),
private val shouldUpdateDbChapter: ShouldUpdateDbChapter = Injekt.get(),
private val updateManga: UpdateManga = Injekt.get(),
private val updateChapter: UpdateChapter = Injekt.get(),
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
private val downloadManager: DownloadManager,
private val downloadProvider: DownloadProvider,
private val chapterRepository: ChapterRepository,
private val shouldUpdateDbChapter: ShouldUpdateDbChapter,
private val updateManga: UpdateManga,
private val updateChapter: UpdateChapter,
private val getChaptersByMangaId: GetChaptersByMangaId,
) {
/**
@@ -46,11 +47,15 @@ class SyncChaptersWithSource(
rawSourceChapters: List<SChapter>,
manga: Manga,
source: Source,
manualFetch: Boolean = false,
fetchWindow: Pair<Long, Long> = Pair(0, 0),
): List<Chapter> {
if (rawSourceChapters.isEmpty() && !source.isLocal()) {
throw NoChaptersException()
}
val now = ZonedDateTime.now()
val sourceChapters = rawSourceChapters
.distinctBy { it.url }
.mapIndexed { i, sChapter ->
@@ -61,7 +66,7 @@ class SyncChaptersWithSource(
}
// Chapters from db.
val dbChapters = getChapterByMangaId.await(manga.id)
val dbChapters = getChaptersByMangaId.await(manga.id)
// Chapters from the source not in db.
val toAdd = mutableListOf<Chapter>()
@@ -132,14 +137,21 @@ class SyncChaptersWithSource(
// Return if there's nothing to add, delete or change, avoiding unnecessary db transactions.
if (toAdd.isEmpty() && toDelete.isEmpty() && toChange.isEmpty()) {
if (manualFetch || manga.fetchInterval == 0 || manga.nextUpdate < fetchWindow.first) {
updateManga.awaitUpdateFetchInterval(
manga,
now,
fetchWindow,
)
}
return emptyList()
}
val reAdded = mutableListOf<Chapter>()
val deletedChapterNumbers = TreeSet<Float>()
val deletedReadChapterNumbers = TreeSet<Float>()
val deletedBookmarkedChapterNumbers = TreeSet<Float>()
val deletedChapterNumbers = TreeSet<Double>()
val deletedReadChapterNumbers = TreeSet<Double>()
val deletedBookmarkedChapterNumbers = TreeSet<Double>()
toDelete.forEach { chapter ->
if (chapter.read) deletedReadChapterNumbers.add(chapter.chapterNumber)
@@ -186,6 +198,7 @@ class SyncChaptersWithSource(
val chapterUpdates = toChange.map { it.toChapterUpdate() }
updateChapter.awaitAll(chapterUpdates)
}
updateManga.awaitUpdateFetchInterval(manga, now, fetchWindow)
// Set this manga as updated since chapters were changed
// Note that last_update actually represents last time the chapter list changed at all

View File

@@ -11,7 +11,7 @@ fun Chapter.toSChapter(): SChapter {
it.url = url
it.name = name
it.date_upload = dateUpload
it.chapter_number = chapterNumber
it.chapter_number = chapterNumber.toFloat()
it.scanlator = scanlator
}
}
@@ -21,7 +21,7 @@ fun Chapter.copyFromSChapter(sChapter: SChapter): Chapter {
name = sChapter.name,
url = sChapter.url,
dateUpload = sChapter.date_upload,
chapterNumber = sChapter.chapter_number,
chapterNumber = sChapter.chapter_number.toDouble(),
scanlator = sChapter.scanlator?.ifBlank { null },
)
}
@@ -37,6 +37,6 @@ fun Chapter.toDbChapter(): DbChapter = ChapterImpl().also {
it.last_page_read = lastPageRead.toInt()
it.date_fetch = dateFetch
it.date_upload = dateUpload
it.chapter_number = chapterNumber
it.chapter_number = chapterNumber.toFloat()
it.source_order = sourceOrder.toInt()
}

View File

@@ -1,14 +1,13 @@
package eu.kanade.domain.chapter.model
import eu.kanade.domain.manga.model.downloadedFilter
import eu.kanade.domain.manga.model.isLocal
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.manga.ChapterItem
import eu.kanade.tachiyomi.util.chapter.getChapterSort
import eu.kanade.tachiyomi.ui.manga.ChapterList
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.chapter.service.getChapterSort
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.TriStateFilter
import tachiyomi.domain.manga.model.applyFilter
import tachiyomi.source.local.isLocal
/**
* Applies the view filters to the list of chapters obtained from the database.
@@ -20,30 +19,12 @@ fun List<Chapter>.applyFilters(manga: Manga, downloadManager: DownloadManager):
val downloadedFilter = manga.downloadedFilter
val bookmarkedFilter = manga.bookmarkedFilter
return filter { chapter ->
when (unreadFilter) {
TriStateFilter.DISABLED -> true
TriStateFilter.ENABLED_IS -> !chapter.read
TriStateFilter.ENABLED_NOT -> chapter.read
}
}
return filter { chapter -> applyFilter(unreadFilter) { !chapter.read } }
.filter { chapter -> applyFilter(bookmarkedFilter) { chapter.bookmark } }
.filter { chapter ->
when (bookmarkedFilter) {
TriStateFilter.DISABLED -> true
TriStateFilter.ENABLED_IS -> chapter.bookmark
TriStateFilter.ENABLED_NOT -> !chapter.bookmark
}
}
.filter { chapter ->
val downloaded = downloadManager.isChapterDownloaded(chapter.name, chapter.scanlator, manga.title, manga.source)
val downloadState = when {
downloaded -> Download.State.DOWNLOADED
else -> Download.State.NOT_DOWNLOADED
}
when (downloadedFilter) {
TriStateFilter.DISABLED -> true
TriStateFilter.ENABLED_IS -> downloadState == Download.State.DOWNLOADED || isLocalManga
TriStateFilter.ENABLED_NOT -> downloadState != Download.State.DOWNLOADED && !isLocalManga
applyFilter(downloadedFilter) {
val downloaded = downloadManager.isChapterDownloaded(chapter.name, chapter.scanlator, manga.title, manga.source)
downloaded || isLocalManga
}
}
.sortedWith(getChapterSort(manga))
@@ -53,32 +34,14 @@ fun List<Chapter>.applyFilters(manga: Manga, downloadManager: DownloadManager):
* Applies the view filters to the list of chapters obtained from the database.
* @return an observable of the list of chapters filtered and sorted.
*/
fun List<ChapterItem>.applyFilters(manga: Manga): Sequence<ChapterItem> {
fun List<ChapterList.Item>.applyFilters(manga: Manga): Sequence<ChapterList.Item> {
val isLocalManga = manga.isLocal()
val unreadFilter = manga.unreadFilter
val downloadedFilter = manga.downloadedFilter
val bookmarkedFilter = manga.bookmarkedFilter
return asSequence()
.filter { (chapter) ->
when (unreadFilter) {
TriStateFilter.DISABLED -> true
TriStateFilter.ENABLED_IS -> !chapter.read
TriStateFilter.ENABLED_NOT -> chapter.read
}
}
.filter { (chapter) ->
when (bookmarkedFilter) {
TriStateFilter.DISABLED -> true
TriStateFilter.ENABLED_IS -> chapter.bookmark
TriStateFilter.ENABLED_NOT -> !chapter.bookmark
}
}
.filter {
when (downloadedFilter) {
TriStateFilter.DISABLED -> true
TriStateFilter.ENABLED_IS -> it.isDownloaded || isLocalManga
TriStateFilter.ENABLED_NOT -> !it.isDownloaded && !isLocalManga
}
}
.filter { (chapter) -> applyFilter(unreadFilter) { !chapter.read } }
.filter { (chapter) -> applyFilter(bookmarkedFilter) { chapter.bookmark } }
.filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalManga } }
.sortedWith { (chapter1), (chapter2) -> getChapterSort(manga).invoke(chapter1, chapter2) }
}

View File

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

View File

@@ -1,111 +0,0 @@
package eu.kanade.domain.library.service
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_NON_COMPLETED
import eu.kanade.tachiyomi.data.preference.MANGA_NON_READ
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(
private val preferenceStore: PreferenceStore,
) {
fun libraryDisplayMode() = preferenceStore.getObject("pref_display_mode_library", LibraryDisplayMode.default, LibraryDisplayMode.Serializer::serialize, LibraryDisplayMode.Serializer::deserialize)
fun librarySortingMode() = preferenceStore.getObject("library_sorting_mode", LibrarySort.default, LibrarySort.Serializer::serialize, LibrarySort.Serializer::deserialize)
fun portraitColumns() = preferenceStore.getInt("pref_library_columns_portrait_key", 0)
fun landscapeColumns() = preferenceStore.getInt("pref_library_columns_landscape_key", 0)
fun libraryUpdateInterval() = preferenceStore.getInt("pref_library_update_interval_key", 0)
fun libraryUpdateLastTimestamp() = preferenceStore.getLong("library_update_last_timestamp", 0L)
fun libraryUpdateDeviceRestriction() = preferenceStore.getStringSet("library_update_restriction", setOf(DEVICE_ONLY_ON_WIFI))
fun libraryUpdateMangaRestriction() = preferenceStore.getStringSet("library_update_manga_restriction", setOf(MANGA_HAS_UNREAD, MANGA_NON_COMPLETED, MANGA_NON_READ))
fun autoUpdateMetadata() = preferenceStore.getBoolean("auto_update_metadata", false)
fun autoUpdateTrackers() = preferenceStore.getBoolean("auto_update_trackers", false)
fun showContinueReadingButton() = preferenceStore.getBoolean("display_continue_reading_button", false)
// region Filter
fun filterDownloaded() = preferenceStore.getInt("pref_filter_library_downloaded", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
fun filterUnread() = preferenceStore.getInt("pref_filter_library_unread", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
fun filterStarted() = preferenceStore.getInt("pref_filter_library_started", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
fun filterBookmarked() = preferenceStore.getInt("pref_filter_library_bookmarked", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
fun filterCompleted() = preferenceStore.getInt("pref_filter_library_completed", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
fun filterTracking(name: Int) = preferenceStore.getInt("pref_filter_library_tracked_$name", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value)
// endregion
// region Badges
fun downloadBadge() = preferenceStore.getBoolean("display_download_badge", false)
fun localBadge() = preferenceStore.getBoolean("display_local_badge", true)
fun languageBadge() = preferenceStore.getBoolean("display_language_badge", false)
fun newShowUpdatesCount() = preferenceStore.getBoolean("library_show_updates_count", true)
fun newUpdatesCount() = preferenceStore.getInt("library_unseen_updates_count", 0)
// endregion
// region Category
fun defaultCategory() = preferenceStore.getInt("default_category", -1)
fun lastUsedCategory() = preferenceStore.getInt("last_used_category", 0)
fun categoryTabs() = preferenceStore.getBoolean("display_category_tabs", true)
fun categoryNumberOfItems() = preferenceStore.getBoolean("display_number_of_items", false)
fun categorizedDisplaySettings() = preferenceStore.getBoolean("categorized_display", false)
fun libraryUpdateCategories() = preferenceStore.getStringSet("library_update_categories", emptySet())
fun libraryUpdateCategoriesExclude() = preferenceStore.getStringSet("library_update_categories_exclude", emptySet())
// endregion
// region Chapter
fun filterChapterByRead() = preferenceStore.getLong("default_chapter_filter_by_read", Manga.SHOW_ALL)
fun filterChapterByDownloaded() = preferenceStore.getLong("default_chapter_filter_by_downloaded", Manga.SHOW_ALL)
fun filterChapterByBookmarked() = preferenceStore.getLong("default_chapter_filter_by_bookmarked", Manga.SHOW_ALL)
// and upload date
fun sortChapterBySourceOrNumber() = preferenceStore.getLong("default_chapter_sort_by_source_or_number", Manga.CHAPTER_SORTING_SOURCE)
fun displayChapterByNameOrNumber() = preferenceStore.getLong("default_chapter_display_by_name_or_number", Manga.CHAPTER_DISPLAY_NAME)
fun sortChapterByAscendingOrDescending() = preferenceStore.getLong("default_chapter_sort_by_ascending_or_descending", Manga.CHAPTER_SORT_DESC)
fun setChapterSettingsDefault(manga: Manga) {
filterChapterByRead().set(manga.unreadFilterRaw)
filterChapterByDownloaded().set(manga.downloadedFilterRaw)
filterChapterByBookmarked().set(manga.bookmarkedFilterRaw)
sortChapterBySourceOrNumber().set(manga.sorting)
displayChapterByNameOrNumber().set(manga.displayMode)
sortChapterByAscendingOrDescending().set(if (manga.sortDescending()) Manga.CHAPTER_SORT_DESC else Manga.CHAPTER_SORT_ASC)
}
fun autoClearChapterCache() = preferenceStore.getBoolean("auto_clear_chapter_cache", false)
// endregion
}

View File

@@ -9,7 +9,7 @@ class SetMangaViewerFlags(
private val mangaRepository: MangaRepository,
) {
suspend fun awaitSetMangaReadingMode(id: Long, flag: Long) {
suspend fun awaitSetReadingMode(id: Long, flag: Long) {
val manga = mangaRepository.getMangaById(id)
mangaRepository.update(
MangaUpdate(
@@ -19,7 +19,7 @@ class SetMangaViewerFlags(
)
}
suspend fun awaitSetOrientationType(id: Long, flag: Long) {
suspend fun awaitSetOrientation(id: Long, flag: Long) {
val manga = mangaRepository.getMangaById(id)
mangaRepository.update(
MangaUpdate(

View File

@@ -1,18 +1,21 @@
package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.model.hasCustomCover
import eu.kanade.domain.manga.model.isLocal
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.source.model.SManga
import tachiyomi.domain.manga.interactor.FetchInterval
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaUpdate
import tachiyomi.domain.manga.repository.MangaRepository
import tachiyomi.source.local.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.time.ZonedDateTime
import java.util.Date
class UpdateManga(
private val mangaRepository: MangaRepository,
private val fetchInterval: FetchInterval,
) {
suspend fun await(mangaUpdate: MangaUpdate): Boolean {
@@ -73,6 +76,16 @@ class UpdateManga(
)
}
suspend fun awaitUpdateFetchInterval(
manga: Manga,
dateTime: ZonedDateTime = ZonedDateTime.now(),
window: Pair<Long, Long> = fetchInterval.getWindow(dateTime),
): Boolean {
return fetchInterval.toMangaUpdateOrNull(manga, dateTime, window)
?.let { mangaRepository.update(it) }
?: false
}
suspend fun awaitUpdateLastUpdate(mangaId: Long): Boolean {
return mangaRepository.update(MangaUpdate(id = mangaId, lastUpdate = Date().time))
}

View File

@@ -2,12 +2,14 @@ package eu.kanade.domain.manga.model
import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import tachiyomi.core.metadata.comicinfo.ComicInfo
import tachiyomi.core.metadata.comicinfo.ComicInfoPublishingStatus
import tachiyomi.core.preference.TriState
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.TriStateFilter
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -18,19 +20,19 @@ val Manga.readingModeType: Long
val Manga.orientationType: Long
get() = viewerFlags and OrientationType.MASK.toLong()
val Manga.downloadedFilter: TriStateFilter
val Manga.downloadedFilter: TriState
get() {
if (forceDownloaded()) return TriStateFilter.ENABLED_IS
if (forceDownloaded()) return TriState.ENABLED_IS
return when (downloadedFilterRaw) {
Manga.CHAPTER_SHOW_DOWNLOADED -> TriStateFilter.ENABLED_IS
Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriStateFilter.ENABLED_NOT
else -> TriStateFilter.DISABLED
Manga.CHAPTER_SHOW_DOWNLOADED -> TriState.ENABLED_IS
Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriState.ENABLED_NOT
else -> TriState.DISABLED
}
}
fun Manga.chaptersFiltered(): Boolean {
return unreadFilter != TriStateFilter.DISABLED ||
downloadedFilter != TriStateFilter.DISABLED ||
bookmarkedFilter != TriStateFilter.DISABLED
return unreadFilter != TriState.DISABLED ||
downloadedFilter != TriState.DISABLED ||
bookmarkedFilter != TriState.DISABLED
}
fun Manga.forceDownloaded(): Boolean {
return favorite && Injekt.get<BasePreferences>().downloadedOnly().get()
@@ -86,8 +88,36 @@ fun SManga.toDomainManga(sourceId: Long): Manga {
)
}
fun Manga.isLocal(): Boolean = source == LocalSource.ID
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
return coverCache.getCustomCoverFile(id).exists()
}
/**
* Creates a ComicInfo instance based on the manga and chapter metadata.
*/
fun getComicInfo(manga: Manga, chapter: Chapter, chapterUrl: String, categories: List<String>?) = ComicInfo(
title = ComicInfo.Title(chapter.name),
series = ComicInfo.Series(manga.title),
number = chapter.chapterNumber.takeIf { it >= 0 }?.let {
if ((it.rem(1) == 0.0)) {
ComicInfo.Number(it.toInt().toString())
} else {
ComicInfo.Number(it.toString())
}
},
web = ComicInfo.Web(chapterUrl),
summary = manga.description?.let { ComicInfo.Summary(it) },
writer = manga.author?.let { ComicInfo.Writer(it) },
penciller = manga.artist?.let { ComicInfo.Penciller(it) },
translator = chapter.scanlator?.let { ComicInfo.Translator(it) },
genre = manga.genre?.let { ComicInfo.Genre(it.joinToString()) },
publishingStatus = ComicInfo.PublishingStatusTachiyomi(
ComicInfoPublishingStatus.toComicInfoValue(manga.status),
),
categories = categories?.let { ComicInfo.CategoriesTachiyomi(it.joinToString()) },
inker = null,
colorist = null,
letterer = null,
coverArtist = null,
tags = null,
)

View File

@@ -1,14 +1,14 @@
package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.repository.SourceRepository
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.source.LocalSource
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import tachiyomi.domain.source.model.Pin
import tachiyomi.domain.source.model.Pins
import tachiyomi.domain.source.model.Source
import tachiyomi.domain.source.repository.SourceRepository
import tachiyomi.source.local.isLocal
class GetEnabledSources(
private val repository: SourceRepository,
@@ -24,7 +24,7 @@ class GetEnabledSources(
repository.getSources(),
) { pinnedSourceIds, enabledLanguages, disabledSources, lastUsedSource, sources ->
sources
.filter { it.lang in enabledLanguages || it.id == LocalSource.ID }
.filter { it.lang in enabledLanguages || it.isLocal() }
.filterNot { it.id.toString() in disabledSources }
.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.name })
.flatMap {

View File

@@ -1,18 +1,19 @@
package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.repository.SourceRepository
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.util.system.LocaleHelper
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import tachiyomi.domain.source.model.Source
import tachiyomi.domain.source.repository.SourceRepository
import java.util.SortedMap
class GetLanguagesWithSources(
private val repository: SourceRepository,
private val preferences: SourcePreferences,
) {
fun subscribe(): Flow<Map<String, List<Source>>> {
fun subscribe(): Flow<SortedMap<String, List<Source>>> {
return combine(
preferences.enabledLanguages().changes(),
preferences.disabledSources().changes(),
@@ -23,7 +24,8 @@ class GetLanguagesWithSources(
.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name },
)
sortedSources.groupBy { it.lang }
sortedSources
.groupBy { it.lang }
.toSortedMap(
compareBy<String> { it !in enabledLanguage }.then(LocaleHelper.comparator),
)

View File

@@ -1,13 +1,13 @@
package eu.kanade.domain.source.interactor
import eu.kanade.domain.source.repository.SourceRepository
import eu.kanade.domain.source.service.SourcePreferences
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import tachiyomi.core.util.lang.compareToWithCollator
import tachiyomi.domain.source.model.Source
import java.text.Collator
import tachiyomi.domain.source.repository.SourceRepository
import tachiyomi.source.local.isLocal
import java.util.Collections
import java.util.Locale
class GetSourcesWithFavoriteCount(
private val repository: SourceRepository,
@@ -20,7 +20,9 @@ class GetSourcesWithFavoriteCount(
preferences.migrationSortingMode().changes(),
repository.getSourcesWithFavoriteCount(),
) { direction, mode, list ->
list.sortedWith(sortFn(direction, mode))
list
.filterNot { it.first.isLocal() }
.sortedWith(sortFn(direction, mode))
}
}
@@ -28,17 +30,13 @@ class GetSourcesWithFavoriteCount(
direction: SetMigrateSorting.Direction,
sorting: SetMigrateSorting.Mode,
): java.util.Comparator<Pair<Source, Long>> {
val locale = Locale.getDefault()
val collator = Collator.getInstance(locale).apply {
strength = Collator.PRIMARY
}
val sortFn: (Pair<Source, Long>, Pair<Source, Long>) -> Int = { a, b ->
when (sorting) {
SetMigrateSorting.Mode.ALPHABETICAL -> {
when {
a.first.isStub && b.first.isStub.not() -> -1
b.first.isStub && a.first.isStub.not() -> 1
else -> collator.compare(a.first.name.lowercase(locale), b.first.name.lowercase(locale))
else -> a.first.name.lowercase().compareToWithCollator(b.first.name.lowercase())
}
}
SetMigrateSorting.Mode.TOTAL -> {

View File

@@ -1,6 +0,0 @@
package eu.kanade.domain.source.model
import androidx.paging.PagingSource
import eu.kanade.tachiyomi.source.model.SManga
typealias SourcePagingSourceType = PagingSource<Long, SManga>

View File

@@ -2,6 +2,7 @@ package eu.kanade.domain.source.service
import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.core.preference.Preference
import tachiyomi.core.preference.PreferenceStore
import tachiyomi.core.preference.getEnum
import tachiyomi.domain.library.model.LibraryDisplayMode
@@ -18,7 +19,10 @@ class SourcePreferences(
fun pinnedSources() = preferenceStore.getStringSet("pinned_catalogues", emptySet())
fun lastUsedSource() = preferenceStore.getLong("last_catalogue_source", -1)
fun lastUsedSource() = preferenceStore.getLong(
Preference.appStateKey("last_catalogue_source"),
-1,
)
fun showNsfwSource() = preferenceStore.getBoolean("show_nsfw_source", true)
@@ -28,7 +32,7 @@ class SourcePreferences(
fun extensionUpdatesCount() = preferenceStore.getInt("ext_updates_count", 0)
fun trustedSignatures() = preferenceStore.getStringSet("trusted_signatures", emptySet())
fun trustedSignatures() = preferenceStore.getStringSet(Preference.appStateKey("trusted_signatures"), emptySet())
fun searchPinnedSourcesOnly() = preferenceStore.getBoolean("search_pinned_sources_only", false)
fun hideInLibraryItems() = preferenceStore.getBoolean("browse_hide_in_library_items", false)
}

View File

@@ -0,0 +1,104 @@
package eu.kanade.domain.track.interactor
import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.domain.track.model.toDomainTrack
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.EnhancedTracker
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone
import logcat.LogPriority
import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.lang.withNonCancellableContext
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
import tachiyomi.domain.history.interactor.GetHistory
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.interactor.InsertTrack
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.time.ZoneOffset
class AddTracks(
private val getTracks: GetTracks,
private val insertTrack: InsertTrack,
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
private val getChaptersByMangaId: GetChaptersByMangaId,
) {
// TODO: update all trackers based on common data
suspend fun bind(tracker: Tracker, item: Track, mangaId: Long) = withNonCancellableContext {
withIOContext {
val allChapters = getChaptersByMangaId.await(mangaId)
val hasReadChapters = allChapters.any { it.read }
tracker.bind(item, hasReadChapters)
var track = item.toDomainTrack(idRequired = false) ?: return@withIOContext
insertTrack.await(track)
// TODO: merge into [SyncChapterProgressWithTrack]?
// Update chapter progress if newer chapters marked read locally
if (hasReadChapters) {
val latestLocalReadChapterNumber = allChapters
.sortedBy { it.chapterNumber }
.takeWhile { it.read }
.lastOrNull()
?.chapterNumber ?: -1.0
if (latestLocalReadChapterNumber > track.lastChapterRead) {
track = track.copy(
lastChapterRead = latestLocalReadChapterNumber,
)
tracker.setRemoteLastChapterRead(track.toDbTrack(), latestLocalReadChapterNumber.toInt())
}
if (track.startDate <= 0) {
val firstReadChapterDate = Injekt.get<GetHistory>().await(mangaId)
.sortedBy { it.readAt }
.firstOrNull()
?.readAt
firstReadChapterDate?.let {
val startDate = firstReadChapterDate.time.convertEpochMillisZone(ZoneOffset.systemDefault(), ZoneOffset.UTC)
track = track.copy(
startDate = startDate,
)
tracker.setRemoteStartDate(track.toDbTrack(), startDate)
}
}
}
syncChapterProgressWithTrack.await(mangaId, track, tracker)
}
}
suspend fun bindEnhancedTrackers(manga: Manga, source: Source) = withNonCancellableContext {
withIOContext {
getTracks.await(manga.id)
.filterIsInstance<EnhancedTracker>()
.filter { it.accept(source) }
.forEach { service ->
try {
service.match(manga)?.let { track ->
track.manga_id = manga.id
(service as Tracker).bind(track)
insertTrack.await(track.toDomainTrack()!!)
syncChapterProgressWithTrack.await(
manga.id,
track.toDomainTrack()!!,
service,
)
}
} catch (e: Exception) {
logcat(
LogPriority.WARN,
e,
) { "Could not match manga: ${manga.title} with service $service" }
}
}
}
}
}

View File

@@ -0,0 +1,46 @@
package eu.kanade.domain.track.interactor
import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.domain.track.model.toDomainTrack
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.TrackerManager
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.supervisorScope
import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.interactor.InsertTrack
class RefreshTracks(
private val getTracks: GetTracks,
private val trackerManager: TrackerManager,
private val insertTrack: InsertTrack,
private val syncChapterProgressWithTrack: SyncChapterProgressWithTrack,
) {
/**
* Fetches updated tracking data from all logged in trackers.
*
* @return Failed updates.
*/
suspend fun await(mangaId: Long): List<Pair<Tracker?, Throwable>> {
return supervisorScope {
return@supervisorScope getTracks.await(mangaId)
.map { it to trackerManager.get(it.syncId) }
.filter { (_, service) -> service?.isLoggedIn == true }
.map { (track, service) ->
async {
return@async try {
val updatedTrack = service!!.refresh(track.toDbTrack())
insertTrack.await(updatedTrack.toDomainTrack()!!)
syncChapterProgressWithTrack.await(mangaId, track, service)
null
} catch (e: Throwable) {
service to e
}
}
}
.awaitAll()
.filterNotNull()
}
}
}

View File

@@ -1,27 +1,35 @@
package eu.kanade.domain.chapter.interactor
package eu.kanade.domain.track.interactor
import eu.kanade.domain.track.interactor.InsertTrack
import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.EnhancedTracker
import eu.kanade.tachiyomi.data.track.Tracker
import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
import tachiyomi.domain.chapter.interactor.UpdateChapter
import tachiyomi.domain.chapter.model.toChapterUpdate
import tachiyomi.domain.track.interactor.InsertTrack
import tachiyomi.domain.track.model.Track
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class SyncChaptersWithTrackServiceTwoWay(
private val updateChapter: UpdateChapter = Injekt.get(),
private val insertTrack: InsertTrack = Injekt.get(),
class SyncChapterProgressWithTrack(
private val updateChapter: UpdateChapter,
private val insertTrack: InsertTrack,
private val getChaptersByMangaId: GetChaptersByMangaId,
) {
suspend fun await(
chapters: List<Chapter>,
mangaId: Long,
remoteTrack: Track,
service: TrackService,
tracker: Tracker,
) {
val sortedChapters = chapters.sortedBy { it.chapterNumber }
if (tracker !is EnhancedTracker) {
return
}
val sortedChapters = getChaptersByMangaId.await(mangaId)
.sortedBy { it.chapterNumber }
.filter { it.isRecognizedNumber }
val chapterUpdates = sortedChapters
.filter { chapter -> chapter.chapterNumber <= remoteTrack.lastChapterRead && !chapter.read }
.map { it.copy(read = true).toChapterUpdate() }
@@ -31,7 +39,7 @@ class SyncChaptersWithTrackServiceTwoWay(
val updatedTrack = remoteTrack.copy(lastChapterRead = localLastRead.toDouble())
try {
service.update(updatedTrack.toDbTrack())
tracker.update(updatedTrack.toDbTrack())
updateChapter.awaitAll(chapterUpdates)
insertTrack.await(updatedTrack)
} catch (e: Throwable) {

View File

@@ -0,0 +1,57 @@
package eu.kanade.domain.track.interactor
import android.content.Context
import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.domain.track.model.toDomainTrack
import eu.kanade.domain.track.service.DelayedTrackingUpdateJob
import eu.kanade.domain.track.store.DelayedTrackingStore
import eu.kanade.tachiyomi.data.track.TrackerManager
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import logcat.LogPriority
import tachiyomi.core.util.lang.withNonCancellableContext
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.interactor.InsertTrack
class TrackChapter(
private val getTracks: GetTracks,
private val trackerManager: TrackerManager,
private val insertTrack: InsertTrack,
private val delayedTrackingStore: DelayedTrackingStore,
) {
suspend fun await(context: Context, mangaId: Long, chapterNumber: Double) {
withNonCancellableContext {
val tracks = getTracks.await(mangaId)
if (tracks.isEmpty()) return@withNonCancellableContext
tracks.mapNotNull { track ->
val service = trackerManager.get(track.syncId)
if (service == null || !service.isLoggedIn || chapterNumber <= track.lastChapterRead) {
return@mapNotNull null
}
async {
runCatching {
try {
val updatedTrack = service.refresh(track.toDbTrack())
.toDomainTrack(idRequired = true)!!
.copy(lastChapterRead = chapterNumber)
service.update(updatedTrack.toDbTrack(), true)
insertTrack.await(updatedTrack)
delayedTrackingStore.remove(track.id)
} catch (e: Exception) {
delayedTrackingStore.add(track.id, chapterNumber)
DelayedTrackingUpdateJob.setupTask(context)
throw e
}
}
}
}
.awaitAll()
.mapNotNull { it.exceptionOrNull() }
.forEach { logcat(LogPriority.WARN, it) }
}
}
}

View File

@@ -22,7 +22,7 @@ fun Track.toDbTrack(): DbTrack = DbTrack.create(syncId).also {
it.last_chapter_read = lastChapterRead.toFloat()
it.total_chapters = totalChapters.toInt()
it.status = status.toInt()
it.score = score
it.score = score.toFloat()
it.tracking_url = remoteUrl
it.started_reading_date = startDate
it.finished_reading_date = finishDate
@@ -40,7 +40,7 @@ fun DbTrack.toDomainTrack(idRequired: Boolean = true): Track? {
lastChapterRead = last_chapter_read.toDouble(),
totalChapters = total_chapters.toLong(),
status = status.toLong(),
score = score,
score = score.toDouble(),
remoteUrl = tracking_url,
startDate = started_reading_date,
finishDate = finished_reading_date,

View File

@@ -7,31 +7,33 @@ import androidx.work.CoroutineWorker
import androidx.work.ExistingWorkPolicy
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import eu.kanade.domain.track.interactor.GetTracks
import eu.kanade.domain.track.interactor.InsertTrack
import eu.kanade.domain.track.model.toDbTrack
import eu.kanade.domain.track.interactor.TrackChapter
import eu.kanade.domain.track.store.DelayedTrackingStore
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.util.system.workManager
import logcat.LogPriority
import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.track.interactor.GetTracks
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.TimeUnit
import kotlin.time.Duration.Companion.minutes
import kotlin.time.toJavaDuration
class DelayedTrackingUpdateJob(context: Context, workerParams: WorkerParameters) :
class DelayedTrackingUpdateJob(private val context: Context, workerParams: WorkerParameters) :
CoroutineWorker(context, workerParams) {
override suspend fun doWork(): Result {
val getTracks = Injekt.get<GetTracks>()
val insertTrack = Injekt.get<InsertTrack>()
if (runAttemptCount > 3) {
return Result.failure()
}
val getTracks = Injekt.get<GetTracks>()
val trackChapter = Injekt.get<TrackChapter>()
val trackManager = Injekt.get<TrackManager>()
val delayedTrackingStore = Injekt.get<DelayedTrackingStore>()
val results = withIOContext {
withIOContext {
delayedTrackingStore.getItems()
.mapNotNull {
val track = getTracks.awaitOne(it.trackId)
@@ -40,42 +42,30 @@ class DelayedTrackingUpdateJob(context: Context, workerParams: WorkerParameters)
}
track?.copy(lastChapterRead = it.lastChapterRead.toDouble())
}
.mapNotNull { track ->
try {
val service = trackManager.getService(track.syncId)
if (service != null && service.isLogged) {
logcat(LogPriority.DEBUG) { "Updating delayed track item: ${track.id}, last chapter read: ${track.lastChapterRead}" }
service.update(track.toDbTrack(), true)
insertTrack.await(track)
}
delayedTrackingStore.remove(track.id)
null
} catch (e: Exception) {
logcat(LogPriority.ERROR, e)
false
}
.forEach { track ->
logcat(LogPriority.DEBUG) { "Updating delayed track item: ${track.mangaId}, last chapter read: ${track.lastChapterRead}" }
trackChapter.await(context, track.mangaId, track.lastChapterRead)
}
}
return if (results.isNotEmpty()) Result.failure() else Result.success()
return if (delayedTrackingStore.getItems().isEmpty()) Result.success() else Result.retry()
}
companion object {
private const val TAG = "DelayedTrackingUpdate"
fun setupTask(context: Context) {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val constraints = Constraints(
requiredNetworkType = NetworkType.CONNECTED,
)
val request = OneTimeWorkRequestBuilder<DelayedTrackingUpdateJob>()
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 20, TimeUnit.SECONDS)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 5.minutes.toJavaDuration())
.addTag(TAG)
.build()
WorkManager.getInstance(context)
.enqueueUniqueWork(TAG, ExistingWorkPolicy.REPLACE, request)
context.workManager.enqueueUniqueWork(TAG, ExistingWorkPolicy.REPLACE, request)
}
}
}

View File

@@ -1,33 +1,34 @@
package eu.kanade.domain.track.service
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.anilist.Anilist
import tachiyomi.core.preference.Preference
import tachiyomi.core.preference.PreferenceStore
class TrackPreferences(
private val preferenceStore: PreferenceStore,
) {
fun trackUsername(sync: TrackService) = preferenceStore.getString(trackUsername(sync.id), "")
fun trackUsername(sync: Tracker) = preferenceStore.getString(trackUsername(sync.id), "")
fun trackPassword(sync: TrackService) = preferenceStore.getString(trackPassword(sync.id), "")
fun trackPassword(sync: Tracker) = preferenceStore.getString(trackPassword(sync.id), "")
fun setTrackCredentials(sync: TrackService, username: String, password: String) {
fun setCredentials(sync: Tracker, username: String, password: String) {
trackUsername(sync).set(username)
trackPassword(sync).set(password)
}
fun trackToken(sync: TrackService) = preferenceStore.getString(trackToken(sync.id), "")
fun trackToken(sync: Tracker) = preferenceStore.getString(trackToken(sync.id), "")
fun anilistScoreType() = preferenceStore.getString("anilist_score_type", Anilist.POINT_10)
fun autoUpdateTrack() = preferenceStore.getBoolean("pref_auto_update_manga_sync_key", true)
companion object {
fun trackUsername(syncId: Long) = "pref_mangasync_username_$syncId"
fun trackUsername(syncId: Long) = Preference.privateKey("pref_mangasync_username_$syncId")
private fun trackPassword(syncId: Long) = "pref_mangasync_password_$syncId"
private fun trackPassword(syncId: Long) = Preference.privateKey("pref_mangasync_password_$syncId")
private fun trackToken(syncId: Long) = "track_token_$syncId"
private fun trackToken(syncId: Long) = Preference.privateKey("track_token_$syncId")
}
}

View File

@@ -4,7 +4,6 @@ import android.content.Context
import androidx.core.content.edit
import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.track.model.Track
class DelayedTrackingStore(context: Context) {
@@ -13,13 +12,12 @@ class DelayedTrackingStore(context: Context) {
*/
private val preferences = context.getSharedPreferences("tracking_queue", Context.MODE_PRIVATE)
fun addItem(track: Track) {
val trackId = track.id.toString()
val lastChapterRead = preferences.getFloat(trackId, 0f)
if (track.lastChapterRead > lastChapterRead) {
logcat(LogPriority.DEBUG) { "Queuing track item: $trackId, last chapter read: ${track.lastChapterRead}" }
fun add(trackId: Long, lastChapterRead: Double) {
val previousLastChapterRead = preferences.getFloat(trackId.toString(), 0f)
if (lastChapterRead > previousLastChapterRead) {
logcat(LogPriority.DEBUG) { "Queuing track item: $trackId, last chapter read: $lastChapterRead" }
preferences.edit {
putFloat(trackId, track.lastChapterRead.toFloat())
putFloat(trackId.toString(), lastChapterRead.toFloat())
}
}
}

View File

@@ -1,6 +1,5 @@
package eu.kanade.domain.ui
import android.os.Build
import eu.kanade.domain.ui.model.AppTheme
import eu.kanade.domain.ui.model.TabletUiMode
import eu.kanade.domain.ui.model.ThemeMode
@@ -16,10 +15,7 @@ class UiPreferences(
private val preferenceStore: PreferenceStore,
) {
fun themeMode() = preferenceStore.getEnum(
"pref_theme_mode_key",
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { ThemeMode.SYSTEM } else { ThemeMode.LIGHT },
)
fun themeMode() = preferenceStore.getEnum("pref_theme_mode_key", ThemeMode.SYSTEM)
fun appTheme() = preferenceStore.getEnum(
"pref_app_theme",
@@ -28,7 +24,7 @@ class UiPreferences(
fun themeDarkAmoled() = preferenceStore.getBoolean("pref_theme_dark_amoled_key", false)
fun relativeTime() = preferenceStore.getInt("relative_time", 7)
fun relativeTime() = preferenceStore.getBoolean("relative_time_v2", true)
fun dateFormat() = preferenceStore.getString("app_date_format", "")

View File

@@ -1,13 +0,0 @@
package eu.kanade.presentation.browse
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.components.Badge
import eu.kanade.tachiyomi.R
@Composable
fun InLibraryBadge(enabled: Boolean) {
if (enabled) {
Badge(text = stringResource(R.string.in_library))
}
}

View File

@@ -14,24 +14,25 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems
import eu.kanade.data.source.NoResultsException
import eu.kanade.presentation.browse.components.BrowseSourceComfortableGrid
import eu.kanade.presentation.browse.components.BrowseSourceCompactGrid
import eu.kanade.presentation.browse.components.BrowseSourceList
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.EmptyScreenAction
import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.util.formattedMessage
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import kotlinx.coroutines.flow.StateFlow
import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.model.StubSource
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.EmptyScreenAction
import tachiyomi.presentation.core.screens.LoadingScreen
import tachiyomi.source.local.LocalSource
@Composable
fun BrowseSourceContent(
@@ -53,30 +54,26 @@ fun BrowseSourceContent(
?: mangaList.loadState.append.takeIf { it is LoadState.Error }
val getErrorMessage: (LoadState.Error) -> String = { state ->
when {
state.error is NoResultsException -> context.getString(R.string.no_results_found)
state.error.message.isNullOrEmpty() -> ""
state.error.message.orEmpty().startsWith("HTTP error") -> "${state.error.message}: ${context.getString(R.string.http_error_hint)}"
else -> state.error.message.orEmpty()
}
with(context) { state.error.formattedMessage }
}
LaunchedEffect(errorState) {
if (mangaList.itemCount > 0 && errorState != null && errorState is LoadState.Error) {
val result = snackbarHostState.showSnackbar(
message = getErrorMessage(errorState),
actionLabel = context.getString(R.string.action_webview_refresh),
actionLabel = context.getString(R.string.action_retry),
duration = SnackbarDuration.Indefinite,
)
when (result) {
SnackbarResult.Dismissed -> snackbarHostState.currentSnackbarData?.dismiss()
SnackbarResult.ActionPerformed -> mangaList.refresh()
SnackbarResult.ActionPerformed -> mangaList.retry()
}
}
}
if (mangaList.itemCount <= 0 && errorState != null && errorState is LoadState.Error) {
EmptyScreen(
modifier = Modifier.padding(contentPadding),
message = getErrorMessage(errorState),
actions = if (source is LocalSource) {
listOf(
@@ -111,7 +108,9 @@ fun BrowseSourceContent(
}
if (mangaList.itemCount == 0 && mangaList.loadState.refresh is LoadState.Loading) {
LoadingScreen()
LoadingScreen(
modifier = Modifier.padding(contentPadding),
)
return
}
@@ -147,7 +146,7 @@ fun BrowseSourceContent(
@Composable
fun MissingSourceScreen(
source: SourceManager.StubSource,
source: StubSource,
navigateUp: () -> Unit,
) {
Scaffold(
@@ -160,7 +159,7 @@ fun MissingSourceScreen(
},
) { paddingValues ->
EmptyScreen(
message = source.getSourceNotInstalledException().message!!,
message = stringResource(R.string.source_not_installed, source.toString()),
modifier = Modifier.padding(paddingValues),
)
}

View File

@@ -10,12 +10,10 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.HelpOutline
@@ -23,6 +21,7 @@ import androidx.compose.material.icons.outlined.History
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@@ -30,6 +29,7 @@ import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -47,25 +47,23 @@ import eu.kanade.domain.extension.interactor.ExtensionSourceItem
import eu.kanade.presentation.browse.components.ExtensionIcon
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.components.DIVIDER_ALPHA
import eu.kanade.presentation.components.Divider
import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.components.ScrollbarLazyColumn
import eu.kanade.presentation.components.WarningBanner
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
import eu.kanade.presentation.more.settings.widget.TrailingWidgetBuffer
import eu.kanade.presentation.util.padding
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsState
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsScreenModel
import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.screens.EmptyScreen
@Composable
fun ExtensionDetailsScreen(
navigateUp: () -> Unit,
state: ExtensionDetailsState,
state: ExtensionDetailsScreenModel.State,
onClickSourcePreferences: (sourceId: Long) -> Unit,
onClickWhatsNew: () -> Unit,
onClickReadme: () -> Unit,
@@ -176,7 +174,8 @@ private fun ExtensionDetails(
data = Uri.fromParts("package", extension.pkgName, null)
context.startActivity(this)
}
},
Unit
}.takeIf { extension.isShared },
onClickAgeRating = {
showNsfwWarning = true
},
@@ -209,7 +208,7 @@ private fun DetailsHeader(
extension: Extension,
onClickAgeRating: () -> Unit,
onClickUninstall: () -> Unit,
onClickAppInfo: () -> Unit,
onClickAppInfo: (() -> Unit)?,
) {
val context = LocalContext.current
@@ -293,6 +292,7 @@ private fun DetailsHeader(
top = MaterialTheme.padding.small,
bottom = MaterialTheme.padding.medium,
),
horizontalArrangement = Arrangement.spacedBy(16.dp),
) {
OutlinedButton(
modifier = Modifier.weight(1f),
@@ -301,20 +301,20 @@ private fun DetailsHeader(
Text(stringResource(R.string.ext_uninstall))
}
Spacer(Modifier.width(16.dp))
Button(
modifier = Modifier.weight(1f),
onClick = onClickAppInfo,
) {
Text(
text = stringResource(R.string.ext_app_info),
color = MaterialTheme.colorScheme.onPrimary,
)
if (onClickAppInfo != null) {
Button(
modifier = Modifier.weight(1f),
onClick = onClickAppInfo,
) {
Text(
text = stringResource(R.string.ext_app_info),
color = MaterialTheme.colorScheme.onPrimary,
)
}
}
}
Divider()
HorizontalDivider()
}
}
@@ -356,11 +356,8 @@ private fun InfoText(
@Composable
private fun InfoDivider() {
Divider(
modifier = Modifier
.height(20.dp)
.width(1.dp),
color = MaterialTheme.colorScheme.onSurface.copy(alpha = DIVIDER_ALPHA),
VerticalDivider(
modifier = Modifier.height(20.dp),
)
}
@@ -415,7 +412,7 @@ private fun NsfwWarningDialog(
},
confirmButton = {
TextButton(onClick = onClickConfirm) {
Text(text = stringResource(android.R.string.ok))
Text(text = stringResource(R.string.action_ok))
}
},
onDismissRequest = onClickConfirm,

View File

@@ -2,19 +2,19 @@ package eu.kanade.presentation.browse
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.FastScrollLazyColumn
import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionFilterState
import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.EmptyScreen
@Composable
fun ExtensionFilterScreen(
@@ -53,7 +53,7 @@ private fun ExtensionFilterContent(
onClickLang: (String) -> Unit,
) {
val context = LocalContext.current
FastScrollLazyColumn(
LazyColumn(
contentPadding = contentPadding,
) {
items(state.languages) { language ->

View File

@@ -3,8 +3,10 @@ package eu.kanade.presentation.browse
import androidx.annotation.StringRes
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
@@ -34,29 +36,28 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.google.accompanist.flowlayout.FlowRow
import eu.kanade.presentation.browse.components.BaseBrowseItem
import eu.kanade.presentation.browse.components.ExtensionIcon
import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.FastScrollLazyColumn
import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.components.PullRefresh
import eu.kanade.presentation.manga.components.DotSeparatorNoSpaceText
import eu.kanade.presentation.theme.header
import eu.kanade.presentation.util.padding
import eu.kanade.presentation.util.plus
import eu.kanade.presentation.util.secondaryItemAlpha
import eu.kanade.presentation.util.topSmallPaddingValues
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.extension.model.InstallStep
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionUiModel
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsState
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsScreenModel
import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.material.PullRefresh
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.LoadingScreen
import tachiyomi.presentation.core.theme.header
import tachiyomi.presentation.core.util.plus
import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable
fun ExtensionScreen(
state: ExtensionsState,
state: ExtensionsScreenModel.State,
contentPadding: PaddingValues,
searchQuery: String?,
onLongClickItem: (Extension) -> Unit,
@@ -75,7 +76,7 @@ fun ExtensionScreen(
enabled = !state.isLoading,
) {
when {
state.isLoading -> LoadingScreen(modifier = Modifier.padding(contentPadding))
state.isLoading -> LoadingScreen(Modifier.padding(contentPadding))
state.isEmpty -> {
val msg = if (!searchQuery.isNullOrEmpty()) {
R.string.no_results_found
@@ -107,7 +108,7 @@ fun ExtensionScreen(
@Composable
private fun ExtensionContent(
state: ExtensionsState,
state: ExtensionsScreenModel.State,
contentPadding: PaddingValues,
onLongClickItem: (Extension) -> Unit,
onClickItemCancel: (Extension) -> Unit,
@@ -243,7 +244,10 @@ private fun ExtensionItem(
)
}
val padding by animateDpAsState(targetValue = if (idle) 0.dp else 8.dp)
val padding by animateDpAsState(
targetValue = if (idle) 0.dp else 8.dp,
label = "iconPadding",
)
ExtensionIcon(
extension = extension,
modifier = Modifier
@@ -287,7 +291,7 @@ private fun ExtensionItemContent(
// Won't look good but it's not like we can ellipsize overflowing content
FlowRow(
modifier = Modifier.secondaryItemAlpha(),
mainAxisSpacing = 4.dp,
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
ProvideTextStyle(value = MaterialTheme.typography.bodySmall) {
if (extension is Extension.Installed && extension.lang.isNotEmpty()) {

View File

@@ -1,35 +1,31 @@
package eu.kanade.presentation.browse
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.browse.components.GlobalSearchCardRow
import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem
import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
import eu.kanade.presentation.browse.components.GlobalSearchResultItem
import eu.kanade.presentation.browse.components.GlobalSearchToolbar
import eu.kanade.presentation.components.LazyColumn
import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.util.padding
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.CatalogueSource
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.SearchScreenModel
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SourceFilter
import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.manga.model.Manga
import tachiyomi.presentation.core.components.material.Scaffold
@Composable
fun GlobalSearchScreen(
state: GlobalSearchState,
state: SearchScreenModel.State,
navigateUp: () -> Unit,
onChangeSearchQuery: (String?) -> Unit,
onSearch: (String) -> Unit,
getManga: @Composable (CatalogueSource, Manga) -> State<Manga>,
onChangeSearchFilter: (SourceFilter) -> Unit,
onToggleResults: () -> Unit,
getManga: @Composable (Manga) -> State<Manga>,
onClickSource: (CatalogueSource) -> Unit,
onClickItem: (Manga) -> Unit,
onLongClickItem: (Manga) -> Unit,
@@ -43,12 +39,16 @@ fun GlobalSearchScreen(
navigateUp = navigateUp,
onChangeSearchQuery = onChangeSearchQuery,
onSearch = onSearch,
sourceFilter = state.sourceFilter,
onChangeSearchFilter = onChangeSearchFilter,
onlyShowHasResults = state.onlyShowHasResults,
onToggleResults = onToggleResults,
scrollBehavior = scrollBehavior,
)
},
) { paddingValues ->
GlobalSearchContent(
items = state.items,
items = state.filteredItems,
contentPadding = paddingValues,
getManga = getManga,
onClickSource = onClickSource,
@@ -59,10 +59,11 @@ fun GlobalSearchScreen(
}
@Composable
fun GlobalSearchContent(
internal fun GlobalSearchContent(
fromSourceId: Long? = null,
items: Map<CatalogueSource, SearchItemResult>,
contentPadding: PaddingValues,
getManga: @Composable (CatalogueSource, Manga) -> State<Manga>,
getManga: @Composable (Manga) -> State<Manga>,
onClickSource: (CatalogueSource) -> Unit,
onClickItem: (Manga) -> Unit,
onLongClickItem: (Manga) -> Unit,
@@ -73,7 +74,7 @@ fun GlobalSearchContent(
items.forEach { (source, result) ->
item(key = source.id) {
GlobalSearchResultItem(
title = source.name,
title = fromSourceId?.let { "${source.name}".takeIf { source.id == fromSourceId } } ?: source.name,
subtitle = LocaleHelper.getDisplayName(source.lang),
onClick = { onClickSource(source) },
) {
@@ -82,21 +83,9 @@ fun GlobalSearchContent(
GlobalSearchLoadingResultItem()
}
is SearchItemResult.Success -> {
if (result.isEmpty) {
Text(
text = stringResource(R.string.no_results_found),
modifier = Modifier
.padding(
horizontal = MaterialTheme.padding.medium,
vertical = MaterialTheme.padding.small,
),
)
return@GlobalSearchResultItem
}
GlobalSearchCardRow(
titles = result.result,
getManga = { getManga(source, it) },
getManga = getManga,
onClick = onClickItem,
onLongClick = onLongClickItem,
)

View File

@@ -6,19 +6,19 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.FastScrollLazyColumn
import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.manga.components.BaseMangaListItem
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaState
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaScreenModel
import tachiyomi.domain.manga.model.Manga
import tachiyomi.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.EmptyScreen
@Composable
fun MigrateMangaScreen(
navigateUp: () -> Unit,
title: String?,
state: MigrateMangaState,
state: MigrateMangaScreenModel.State,
onClickItem: (Manga) -> Unit,
onClickCover: (Manga) -> Unit,
) {
@@ -51,7 +51,7 @@ fun MigrateMangaScreen(
@Composable
private fun MigrateMangaContent(
contentPadding: PaddingValues,
state: MigrateMangaState,
state: MigrateMangaScreenModel.State,
onClickItem: (Manga) -> Unit,
onClickCover: (Manga) -> Unit,
) {

View File

@@ -1,29 +1,24 @@
package eu.kanade.presentation.browse
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import eu.kanade.presentation.browse.components.GlobalSearchCardRow
import eu.kanade.presentation.browse.components.GlobalSearchEmptyResultItem
import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem
import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem
import eu.kanade.presentation.browse.components.GlobalSearchResultItem
import eu.kanade.presentation.browse.components.GlobalSearchToolbar
import eu.kanade.presentation.components.LazyColumn
import eu.kanade.presentation.components.Scaffold
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchState
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
import eu.kanade.tachiyomi.util.system.LocaleHelper
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchScreenModel
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SourceFilter
import tachiyomi.domain.manga.model.Manga
import tachiyomi.presentation.core.components.material.Scaffold
@Composable
fun MigrateSearchScreen(
state: SearchScreenModel.State,
fromSourceId: Long?,
navigateUp: () -> Unit,
state: MigrateSearchState,
getManga: @Composable (CatalogueSource, Manga) -> State<Manga>,
onChangeSearchQuery: (String?) -> Unit,
onSearch: (String) -> Unit,
onChangeSearchFilter: (SourceFilter) -> Unit,
onToggleResults: () -> Unit,
getManga: @Composable (Manga) -> State<Manga>,
onClickSource: (CatalogueSource) -> Unit,
onClickItem: (Manga) -> Unit,
onLongClickItem: (Manga) -> Unit,
@@ -37,13 +32,17 @@ fun MigrateSearchScreen(
navigateUp = navigateUp,
onChangeSearchQuery = onChangeSearchQuery,
onSearch = onSearch,
sourceFilter = state.sourceFilter,
onChangeSearchFilter = onChangeSearchFilter,
onlyShowHasResults = state.onlyShowHasResults,
onToggleResults = onToggleResults,
scrollBehavior = scrollBehavior,
)
},
) { paddingValues ->
MigrateSearchContent(
sourceId = state.manga?.source ?: -1,
items = state.items,
GlobalSearchContent(
fromSourceId = fromSourceId,
items = state.filteredItems,
contentPadding = paddingValues,
getManga = getManga,
onClickSource = onClickSource,
@@ -52,50 +51,3 @@ fun MigrateSearchScreen(
)
}
}
@Composable
fun MigrateSearchContent(
sourceId: Long,
items: Map<CatalogueSource, SearchItemResult>,
contentPadding: PaddingValues,
getManga: @Composable (CatalogueSource, Manga) -> State<Manga>,
onClickSource: (CatalogueSource) -> Unit,
onClickItem: (Manga) -> Unit,
onLongClickItem: (Manga) -> Unit,
) {
LazyColumn(
contentPadding = contentPadding,
) {
items.forEach { (source, result) ->
item(key = source.id) {
GlobalSearchResultItem(
title = if (source.id == sourceId) "${source.name}" else source.name,
subtitle = LocaleHelper.getDisplayName(source.lang),
onClick = { onClickSource(source) },
) {
when (result) {
SearchItemResult.Loading -> {
GlobalSearchLoadingResultItem()
}
is SearchItemResult.Success -> {
if (result.isEmpty) {
GlobalSearchEmptyResultItem()
return@GlobalSearchResultItem
}
GlobalSearchCardRow(
titles = result.result,
getManga = { getManga(source, it) },
onClick = onClickItem,
onLongClick = onLongClickItem,
)
}
is SearchItemResult.Error -> {
GlobalSearchErrorResultItem(message = result.throwable.message)
}
}
}
}
}
}
}

View File

@@ -25,25 +25,25 @@ import androidx.compose.ui.text.style.TextOverflow
import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.presentation.browse.components.BaseSourceItem
import eu.kanade.presentation.browse.components.SourceIcon
import eu.kanade.presentation.components.Badge
import eu.kanade.presentation.components.BadgeGroup
import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.components.ScrollbarLazyColumn
import eu.kanade.presentation.components.Scroller.STICKY_HEADER_KEY_PREFIX
import eu.kanade.presentation.theme.header
import eu.kanade.presentation.util.padding
import eu.kanade.presentation.util.plus
import eu.kanade.presentation.util.secondaryItemAlpha
import eu.kanade.presentation.util.topSmallPaddingValues
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrateSourceState
import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrateSourceScreenModel
import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.domain.source.model.Source
import tachiyomi.presentation.core.components.Badge
import tachiyomi.presentation.core.components.BadgeGroup
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.Scroller.STICKY_HEADER_KEY_PREFIX
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.LoadingScreen
import tachiyomi.presentation.core.theme.header
import tachiyomi.presentation.core.util.plus
import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable
fun MigrateSourceScreen(
state: MigrateSourceState,
state: MigrateSourceScreenModel.State,
contentPadding: PaddingValues,
onClickItem: (Source) -> Unit,
onToggleSortingDirection: () -> Unit,
@@ -51,7 +51,7 @@ fun MigrateSourceScreen(
) {
val context = LocalContext.current
when {
state.isLoading -> LoadingScreen(modifier = Modifier.padding(contentPadding))
state.isLoading -> LoadingScreen(Modifier.padding(contentPadding))
state.isEmpty -> EmptyScreen(
textResource = R.string.information_empty_library,
modifier = Modifier.padding(contentPadding),

View File

@@ -10,19 +10,19 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.browse.components.BaseSourceItem
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.FastScrollLazyColumn
import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterState
import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterScreenModel
import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.source.model.Source
import tachiyomi.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.EmptyScreen
@Composable
fun SourcesFilterScreen(
navigateUp: () -> Unit,
state: SourcesFilterState.Success,
state: SourcesFilterScreenModel.State.Success,
onClickLanguage: (String) -> Unit,
onClickSource: (Source) -> Unit,
) {
@@ -54,7 +54,7 @@ fun SourcesFilterScreen(
@Composable
private fun SourcesFilterContent(
contentPadding: PaddingValues,
state: SourcesFilterState.Success,
state: SourcesFilterScreenModel.State.Success,
onClickLanguage: (String) -> Unit,
onClickSource: (Source) -> Unit,
) {
@@ -64,7 +64,7 @@ private fun SourcesFilterContent(
state.items.forEach { (language, sources) ->
val enabled = language in state.enabledLanguages
item(
key = language.hashCode(),
key = language,
contentType = "source-filter-header",
) {
SourcesFilterHeader(
@@ -74,18 +74,19 @@ private fun SourcesFilterContent(
onClickItem = onClickLanguage,
)
}
if (!enabled) return@forEach
items(
items = sources,
key = { "source-filter-${it.key()}" },
contentType = { "source-filter-item" },
) { source ->
SourcesFilterItem(
modifier = Modifier.animateItemPlacement(),
source = source,
enabled = "${source.id}" !in state.disabledSources,
onClickItem = onClickSource,
)
if (enabled) {
items(
items = sources,
key = { "source-filter-${it.key()}" },
contentType = { "source-filter-item" },
) { source ->
SourcesFilterItem(
modifier = Modifier.animateItemPlacement(),
source = source,
enabled = "${source.id}" !in state.disabledSources,
onClickItem = onClickSource,
)
}
}
}
}

View File

@@ -22,31 +22,32 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.browse.components.BaseSourceItem
import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.components.ScrollbarLazyColumn
import eu.kanade.presentation.theme.header
import eu.kanade.presentation.util.padding
import eu.kanade.presentation.util.plus
import eu.kanade.presentation.util.topSmallPaddingValues
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.ui.browse.source.SourcesState
import eu.kanade.tachiyomi.ui.browse.source.SourcesScreenModel
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing
import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.source.model.Pin
import tachiyomi.domain.source.model.Source
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.LoadingScreen
import tachiyomi.presentation.core.theme.header
import tachiyomi.presentation.core.util.plus
import tachiyomi.source.local.isLocal
@Composable
fun SourcesScreen(
state: SourcesState,
state: SourcesScreenModel.State,
contentPadding: PaddingValues,
onClickItem: (Source, Listing) -> Unit,
onClickPin: (Source) -> Unit,
onLongClickItem: (Source) -> Unit,
) {
when {
state.isLoading -> LoadingScreen(modifier = Modifier.padding(contentPadding))
state.isLoading -> LoadingScreen(Modifier.padding(contentPadding))
state.isEmpty -> EmptyScreen(
textResource = R.string.source_empty_screen,
modifier = Modifier.padding(contentPadding),
@@ -143,7 +144,7 @@ private fun SourcePinButton(
onClick: () -> Unit,
) {
val icon = if (isPinned) Icons.Filled.PushPin else Icons.Outlined.PushPin
val tint = if (isPinned) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onBackground
val tint = if (isPinned) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onBackground.copy(alpha = SecondaryItemAlpha)
val description = if (isPinned) R.string.action_unpin else R.string.action_pin
IconButton(onClick = onClick) {
Icon(
@@ -175,7 +176,7 @@ fun SourceOptionsDialog(
.fillMaxWidth()
.padding(vertical = 16.dp),
)
if (source.id != LocalSource.ID) {
if (!source.isLocal()) {
Text(
text = stringResource(R.string.action_disable),
modifier = Modifier
@@ -191,7 +192,7 @@ fun SourceOptionsDialog(
)
}
sealed class SourceUiModel {
data class Item(val source: Source) : SourceUiModel()
data class Header(val language: String) : SourceUiModel()
sealed interface SourceUiModel {
data class Item(val source: Source) : SourceUiModel
data class Header(val language: String) : SourceUiModel
}

View File

@@ -8,7 +8,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import eu.kanade.presentation.util.padding
import tachiyomi.presentation.core.components.material.padding
@Composable
fun BaseBrowseItem(

View File

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

View File

@@ -0,0 +1,15 @@
package eu.kanade.presentation.browse.components
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.CollectionsBookmark
import androidx.compose.runtime.Composable
import tachiyomi.presentation.core.components.Badge
@Composable
internal fun InLibraryBadge(enabled: Boolean) {
if (enabled) {
Badge(
imageVector = Icons.Outlined.CollectionsBookmark,
)
}
}

View File

@@ -1,6 +1,5 @@
package eu.kanade.presentation.browse.components
import android.content.pm.PackageManager
import android.util.DisplayMetrics
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
@@ -31,9 +30,10 @@ import eu.kanade.domain.source.model.icon
import eu.kanade.presentation.util.rememberResourceBitmapPainter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.extension.util.ExtensionLoader
import tachiyomi.core.util.lang.withIOContext
import tachiyomi.domain.source.model.Source
import tachiyomi.source.local.isLocal
private val defaultModifier = Modifier
.height(40.dp)
@@ -62,7 +62,7 @@ fun SourceIcon(
modifier = modifier.then(defaultModifier),
)
}
source.id == LocalSource.ID -> {
source.isLocal() -> {
Image(
painter = painterResource(R.mipmap.ic_local_source),
contentDescription = null,
@@ -127,7 +127,7 @@ private fun Extension.getIcon(density: Int = DisplayMetrics.DENSITY_DEFAULT): St
return produceState<Result<ImageBitmap>>(initialValue = Result.Loading, this) {
withIOContext {
value = try {
val appInfo = context.packageManager.getApplicationInfo(pkgName, PackageManager.GET_META_DATA)
val appInfo = ExtensionLoader.getExtensionPackageInfoFromPkgName(context, pkgName)!!.applicationInfo
val appResources = context.packageManager.getResourcesForApplication(appInfo)
Result.Success(
appResources.getDrawableForDensity(appInfo.icon, density, null)!!
@@ -142,7 +142,7 @@ private fun Extension.getIcon(density: Int = DisplayMetrics.DENSITY_DEFAULT): St
}
sealed class Result<out T> {
object Loading : Result<Nothing>()
object Error : Result<Nothing>()
data object Loading : Result<Nothing>()
data object Error : Result<Nothing>()
data class Success<out T>(val value: T) : Result<T>()
}

View File

@@ -11,13 +11,12 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.unit.dp
import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems
import eu.kanade.presentation.browse.InLibraryBadge
import eu.kanade.presentation.components.CommonMangaItemDefaults
import eu.kanade.presentation.components.MangaComfortableGridItem
import eu.kanade.presentation.util.plus
import eu.kanade.presentation.library.components.CommonMangaItemDefaults
import eu.kanade.presentation.library.components.MangaComfortableGridItem
import kotlinx.coroutines.flow.StateFlow
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaCover
import tachiyomi.presentation.core.util.plus
@Composable
fun BrowseSourceComfortableGrid(
@@ -39,7 +38,7 @@ fun BrowseSourceComfortableGrid(
}
}
items(mangaList.itemCount) { index ->
items(count = mangaList.itemCount) { index ->
val manga by mangaList[index]?.collectAsState() ?: return@items
BrowseSourceComfortableGridItem(
manga = manga,
@@ -57,7 +56,7 @@ fun BrowseSourceComfortableGrid(
}
@Composable
fun BrowseSourceComfortableGridItem(
private fun BrowseSourceComfortableGridItem(
manga: Manga,
onClick: () -> Unit = {},
onLongClick: () -> Unit = onClick,

View File

@@ -11,13 +11,12 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.unit.dp
import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems
import eu.kanade.presentation.browse.InLibraryBadge
import eu.kanade.presentation.components.CommonMangaItemDefaults
import eu.kanade.presentation.components.MangaCompactGridItem
import eu.kanade.presentation.util.plus
import eu.kanade.presentation.library.components.CommonMangaItemDefaults
import eu.kanade.presentation.library.components.MangaCompactGridItem
import kotlinx.coroutines.flow.StateFlow
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaCover
import tachiyomi.presentation.core.util.plus
@Composable
fun BrowseSourceCompactGrid(
@@ -39,7 +38,7 @@ fun BrowseSourceCompactGrid(
}
}
items(mangaList.itemCount) { index ->
items(count = mangaList.itemCount) { index ->
val manga by mangaList[index]?.collectAsState() ?: return@items
BrowseSourceCompactGridItem(
manga = manga,

View File

@@ -1,21 +1,19 @@
package eu.kanade.presentation.browse.components
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.unit.dp
import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.items
import eu.kanade.presentation.browse.InLibraryBadge
import eu.kanade.presentation.components.CommonMangaItemDefaults
import eu.kanade.presentation.components.LazyColumn
import eu.kanade.presentation.components.MangaListItem
import eu.kanade.presentation.util.plus
import eu.kanade.presentation.library.components.CommonMangaItemDefaults
import eu.kanade.presentation.library.components.MangaListItem
import kotlinx.coroutines.flow.StateFlow
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaCover
import tachiyomi.presentation.core.util.plus
@Composable
fun BrowseSourceList(
@@ -33,9 +31,8 @@ fun BrowseSourceList(
}
}
items(mangaList) { mangaflow ->
mangaflow ?: return@items
val manga by mangaflow.collectAsState()
items(count = mangaList.itemCount) { index ->
val manga by mangaList[index]?.collectAsState() ?: return@items
BrowseSourceListItem(
manga = manga,
onClick = { onMangaClick(manga) },
@@ -52,7 +49,7 @@ fun BrowseSourceList(
}
@Composable
fun BrowseSourceListItem(
private fun BrowseSourceListItem(
manga: Manga,
onClick: () -> Unit = {},
onLongClick: () -> Unit = onClick,

View File

@@ -10,7 +10,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun BrowseSourceLoadingItem() {
internal fun BrowseSourceLoadingItem() {
Row(
modifier = Modifier
.fillMaxWidth()

View File

@@ -3,8 +3,6 @@ package eu.kanade.presentation.browse.components
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ViewList
import androidx.compose.material.icons.filled.ViewModule
import androidx.compose.material.icons.outlined.Help
import androidx.compose.material.icons.outlined.Public
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
@@ -20,9 +18,10 @@ import eu.kanade.presentation.components.DropdownMenu
import eu.kanade.presentation.components.RadioMenuItem
import eu.kanade.presentation.components.SearchToolbar
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.Source
import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.source.local.LocalSource
@Composable
fun BrowseSourceToolbar(
@@ -34,12 +33,16 @@ fun BrowseSourceToolbar(
navigateUp: () -> Unit,
onWebViewClick: () -> Unit,
onHelpClick: () -> Unit,
onSettingsClick: () -> Unit,
onSearch: (String) -> Unit,
scrollBehavior: TopAppBarScrollBehavior? = null,
) {
// Avoid capturing unstable source in actions lambda
val title = source?.name
val isLocalSource = source is LocalSource
val isConfigurableSource = source is ConfigurableSource
var selectingDisplayMode by remember { mutableStateOf(false) }
SearchToolbar(
navigateUp = navigateUp,
@@ -49,29 +52,31 @@ fun BrowseSourceToolbar(
onSearch = onSearch,
onClickCloseSearch = navigateUp,
actions = {
var selectingDisplayMode by remember { mutableStateOf(false) }
AppBarActions(
actions = listOf(
actions = listOfNotNull(
AppBar.Action(
title = stringResource(R.string.action_display_mode),
icon = if (displayMode == LibraryDisplayMode.List) Icons.Filled.ViewList else Icons.Filled.ViewModule,
onClick = { selectingDisplayMode = true },
),
if (isLocalSource) {
AppBar.Action(
AppBar.OverflowAction(
title = stringResource(R.string.label_help),
icon = Icons.Outlined.Help,
onClick = onHelpClick,
)
} else {
AppBar.Action(
title = stringResource(R.string.action_web_view),
icon = Icons.Outlined.Public,
AppBar.OverflowAction(
title = stringResource(R.string.action_open_in_web_view),
onClick = onWebViewClick,
)
},
AppBar.OverflowAction(
title = stringResource(R.string.action_settings),
onClick = onSettingsClick,
).takeIf { isConfigurableSource },
),
)
DropdownMenu(
expanded = selectingDisplayMode,
onDismissRequest = { selectingDisplayMode = false },

View File

@@ -1,33 +0,0 @@
package eu.kanade.presentation.browse.components
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.browse.InLibraryBadge
import eu.kanade.presentation.components.CommonMangaItemDefaults
import eu.kanade.presentation.components.MangaComfortableGridItem
import tachiyomi.domain.manga.model.MangaCover
@Composable
fun GlobalSearchCard(
title: String,
cover: MangaCover,
isFavorite: Boolean,
onClick: () -> Unit,
onLongClick: () -> Unit,
) {
Box(modifier = Modifier.width(96.dp)) {
MangaComfortableGridItem(
title = title,
coverData = cover,
coverBadgeStart = {
InLibraryBadge(enabled = isFavorite)
},
coverAlpha = if (isFavorite) CommonMangaItemDefaults.BrowseFavoriteCoverAlpha else 1f,
onClick = onClick,
onLongClick = onLongClick,
)
}
}

View File

@@ -1,16 +1,27 @@
package eu.kanade.presentation.browse.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import eu.kanade.presentation.util.padding
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.library.components.CommonMangaItemDefaults
import eu.kanade.presentation.library.components.MangaComfortableGridItem
import eu.kanade.tachiyomi.R
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaCover
import tachiyomi.domain.manga.model.asMangaCover
import tachiyomi.presentation.core.components.material.padding
@Composable
fun GlobalSearchCardRow(
@@ -19,13 +30,18 @@ fun GlobalSearchCardRow(
onClick: (Manga) -> Unit,
onLongClick: (Manga) -> Unit,
) {
if (titles.isEmpty()) {
EmptyResultItem()
return
}
LazyRow(
contentPadding = PaddingValues(MaterialTheme.padding.small),
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.tiny),
) {
items(titles) {
val title by getManga(it)
GlobalSearchCard(
MangaItem(
title = title.title,
cover = title.asMangaCover(),
isFavorite = title.favorite,
@@ -35,3 +51,38 @@ fun GlobalSearchCardRow(
}
}
}
@Composable
private fun MangaItem(
title: String,
cover: MangaCover,
isFavorite: Boolean,
onClick: () -> Unit,
onLongClick: () -> Unit,
) {
Box(modifier = Modifier.width(96.dp)) {
MangaComfortableGridItem(
title = title,
titleMaxLines = 3,
coverData = cover,
coverBadgeStart = {
InLibraryBadge(enabled = isFavorite)
},
coverAlpha = if (isFavorite) CommonMangaItemDefaults.BrowseFavoriteCoverAlpha else 1f,
onClick = onClick,
onLongClick = onLongClick,
)
}
}
@Composable
private fun EmptyResultItem() {
Text(
text = stringResource(R.string.no_results_found),
modifier = Modifier
.padding(
horizontal = MaterialTheme.padding.medium,
vertical = MaterialTheme.padding.small,
),
)
}

View File

@@ -24,8 +24,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.util.padding
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.material.padding
@Composable
fun GlobalSearchResultItem(
@@ -61,18 +61,6 @@ fun GlobalSearchResultItem(
}
}
@Composable
fun GlobalSearchEmptyResultItem() {
Text(
text = stringResource(R.string.no_results_found),
modifier = Modifier
.padding(
horizontal = MaterialTheme.padding.medium,
vertical = MaterialTheme.padding.small,
),
)
}
@Composable
fun GlobalSearchLoadingResultItem() {
Box(

View File

@@ -1,13 +1,36 @@
package eu.kanade.presentation.browse.components
import androidx.compose.foundation.background
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.DoneAll
import androidx.compose.material.icons.outlined.FilterList
import androidx.compose.material.icons.outlined.PushPin
import androidx.compose.material3.FilterChip
import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.components.SearchToolbar
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SourceFilter
import tachiyomi.presentation.core.components.material.padding
@Composable
fun GlobalSearchToolbar(
@@ -17,24 +40,89 @@ fun GlobalSearchToolbar(
navigateUp: () -> Unit,
onChangeSearchQuery: (String?) -> Unit,
onSearch: (String) -> Unit,
sourceFilter: SourceFilter,
onChangeSearchFilter: (SourceFilter) -> Unit,
onlyShowHasResults: Boolean,
onToggleResults: () -> Unit,
scrollBehavior: TopAppBarScrollBehavior,
) {
Box {
SearchToolbar(
searchQuery = searchQuery,
onChangeSearchQuery = onChangeSearchQuery,
onSearch = onSearch,
onClickCloseSearch = navigateUp,
navigateUp = navigateUp,
scrollBehavior = scrollBehavior,
)
if (progress in 1 until total) {
LinearProgressIndicator(
progress = progress / total.toFloat(),
modifier = Modifier
.align(Alignment.BottomStart)
.fillMaxWidth(),
Column(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) {
Box {
SearchToolbar(
searchQuery = searchQuery,
onChangeSearchQuery = onChangeSearchQuery,
onSearch = onSearch,
onClickCloseSearch = navigateUp,
navigateUp = navigateUp,
scrollBehavior = scrollBehavior,
)
if (progress in 1..<total) {
LinearProgressIndicator(
progress = progress / total.toFloat(),
modifier = Modifier
.align(Alignment.BottomStart)
.fillMaxWidth(),
)
}
}
Row(
modifier = Modifier
.horizontalScroll(rememberScrollState())
.padding(horizontal = MaterialTheme.padding.small),
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
) {
// TODO: make this UX better; it only applies when triggering a new search
FilterChip(
selected = sourceFilter == SourceFilter.PinnedOnly,
onClick = { onChangeSearchFilter(SourceFilter.PinnedOnly) },
leadingIcon = {
Icon(
imageVector = Icons.Outlined.PushPin,
contentDescription = null,
modifier = Modifier
.size(FilterChipDefaults.IconSize),
)
},
label = {
Text(text = stringResource(id = R.string.pinned_sources))
},
)
FilterChip(
selected = sourceFilter == SourceFilter.All,
onClick = { onChangeSearchFilter(SourceFilter.All) },
leadingIcon = {
Icon(
imageVector = Icons.Outlined.DoneAll,
contentDescription = null,
modifier = Modifier
.size(FilterChipDefaults.IconSize),
)
},
label = {
Text(text = stringResource(id = R.string.all))
},
)
VerticalDivider()
FilterChip(
selected = onlyShowHasResults,
onClick = { onToggleResults() },
leadingIcon = {
Icon(
imageVector = Icons.Outlined.FilterList,
contentDescription = null,
modifier = Modifier
.size(FilterChipDefaults.IconSize),
)
},
label = {
Text(text = stringResource(id = R.string.has_results))
},
)
}
HorizontalDivider()
}
}

View File

@@ -1,28 +1,36 @@
package eu.kanade.presentation.category
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.SortByAlpha
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.category.components.CategoryContent
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
import eu.kanade.presentation.category.components.CategoryListItem
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.util.padding
import eu.kanade.presentation.util.plus
import eu.kanade.presentation.util.topSmallPaddingValues
import eu.kanade.presentation.components.AppBarActions
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.category.CategoryScreenState
import tachiyomi.domain.category.model.Category
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.util.plus
@Composable
fun CategoryScreen(
state: CategoryScreenState.Success,
onClickCreate: () -> Unit,
onClickSortAlphabetically: () -> Unit,
onClickRename: (Category) -> Unit,
onClickDelete: (Category) -> Unit,
onClickMoveUp: (Category) -> Unit,
@@ -35,6 +43,17 @@ fun CategoryScreen(
AppBar(
title = stringResource(R.string.action_edit_categories),
navigateUp = navigateUp,
actions = {
AppBarActions(
listOf(
AppBar.Action(
title = stringResource(R.string.action_sort),
icon = Icons.Outlined.SortByAlpha,
onClick = onClickSortAlphabetically,
),
),
)
},
scrollBehavior = scrollBehavior,
)
},
@@ -64,3 +83,36 @@ fun CategoryScreen(
)
}
}
@Composable
private fun CategoryContent(
categories: List<Category>,
lazyListState: LazyListState,
paddingValues: PaddingValues,
onClickRename: (Category) -> Unit,
onClickDelete: (Category) -> Unit,
onMoveUp: (Category) -> Unit,
onMoveDown: (Category) -> Unit,
) {
LazyColumn(
state = lazyListState,
contentPadding = paddingValues,
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
) {
itemsIndexed(
items = categories,
key = { _, category -> "category-${category.id}" },
) { index, category ->
CategoryListItem(
modifier = Modifier.animateItemPlacement(),
category = category,
canMoveUp = index != 0,
canMoveDown = index != categories.lastIndex,
onMoveUp = onMoveUp,
onMoveDown = onMoveDown,
onRename = { onClickRename(category) },
onDelete = { onClickDelete(category) },
)
}
}
}

View File

@@ -1,45 +0,0 @@
package eu.kanade.presentation.category.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import eu.kanade.presentation.components.LazyColumn
import eu.kanade.presentation.util.padding
import tachiyomi.domain.category.model.Category
@Composable
fun CategoryContent(
categories: List<Category>,
lazyListState: LazyListState,
paddingValues: PaddingValues,
onClickRename: (Category) -> Unit,
onClickDelete: (Category) -> Unit,
onMoveUp: (Category) -> Unit,
onMoveDown: (Category) -> Unit,
) {
LazyColumn(
state = lazyListState,
contentPadding = paddingValues,
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
) {
itemsIndexed(
items = categories,
key = { _, category -> "category-${category.id}" },
) { index, category ->
CategoryListItem(
modifier = Modifier.animateItemPlacement(),
category = category,
canMoveUp = index != 0,
canMoveDown = index != categories.lastIndex,
onMoveUp = onMoveUp,
onMoveDown = onMoveDown,
onRename = { onClickRename(category) },
onDelete = { onClickDelete(category) },
)
}
}
}

View File

@@ -1,22 +1,38 @@
package eu.kanade.presentation.category.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TriStateCheckbox
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.res.stringResource
import eu.kanade.core.preference.asToggleableState
import eu.kanade.presentation.category.visualName
import eu.kanade.tachiyomi.R
import kotlinx.coroutines.delay
import tachiyomi.core.preference.CheckboxState
import tachiyomi.domain.category.model.Category
import tachiyomi.presentation.core.components.material.padding
import kotlin.time.Duration.Companion.seconds
@Composable
@@ -97,7 +113,7 @@ fun CategoryRenameDialog(
onDismissRequest()
},
) {
Text(text = stringResource(android.R.string.ok))
Text(text = stringResource(R.string.action_ok))
}
},
dismissButton = {
@@ -146,8 +162,8 @@ fun CategoryDeleteDialog(
TextButton(onClick = {
onDelete()
onDismissRequest()
},) {
Text(text = stringResource(android.R.string.ok))
}) {
Text(text = stringResource(R.string.action_ok))
}
},
dismissButton = {
@@ -164,6 +180,140 @@ fun CategoryDeleteDialog(
)
}
internal fun List<Category>.anyWithName(name: String): Boolean {
@Composable
fun CategorySortAlphabeticallyDialog(
onDismissRequest: () -> Unit,
onSort: () -> Unit,
) {
AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
TextButton(onClick = {
onSort()
onDismissRequest()
}) {
Text(text = stringResource(R.string.action_ok))
}
},
dismissButton = {
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(R.string.action_cancel))
}
},
title = {
Text(text = stringResource(R.string.action_sort_category))
},
text = {
Text(text = stringResource(R.string.sort_category_confirmation))
},
)
}
@Composable
fun ChangeCategoryDialog(
initialSelection: List<CheckboxState<Category>>,
onDismissRequest: () -> Unit,
onEditCategories: () -> Unit,
onConfirm: (List<Long>, List<Long>) -> Unit,
) {
if (initialSelection.isEmpty()) {
AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
tachiyomi.presentation.core.components.material.TextButton(
onClick = {
onDismissRequest()
onEditCategories()
},
) {
Text(text = stringResource(R.string.action_edit_categories))
}
},
title = {
Text(text = stringResource(R.string.action_move_category))
},
text = {
Text(text = stringResource(R.string.information_empty_category_dialog))
},
)
return
}
var selection by remember { mutableStateOf(initialSelection) }
AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
Row {
tachiyomi.presentation.core.components.material.TextButton(onClick = {
onDismissRequest()
onEditCategories()
}) {
Text(text = stringResource(R.string.action_edit))
}
Spacer(modifier = Modifier.weight(1f))
tachiyomi.presentation.core.components.material.TextButton(onClick = onDismissRequest) {
Text(text = stringResource(R.string.action_cancel))
}
tachiyomi.presentation.core.components.material.TextButton(
onClick = {
onDismissRequest()
onConfirm(
selection.filter { it is CheckboxState.State.Checked || it is CheckboxState.TriState.Include }.map { it.value.id },
selection.filter { it is CheckboxState.State.None || it is CheckboxState.TriState.None }.map { it.value.id },
)
},
) {
Text(text = stringResource(R.string.action_ok))
}
}
},
title = {
Text(text = stringResource(R.string.action_move_category))
},
text = {
Column(
modifier = Modifier.verticalScroll(rememberScrollState()),
) {
selection.forEach { checkbox ->
val onChange: (CheckboxState<Category>) -> Unit = {
val index = selection.indexOf(it)
if (index != -1) {
val mutableList = selection.toMutableList()
mutableList[index] = it.next()
selection = mutableList.toList()
}
}
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { onChange(checkbox) },
verticalAlignment = Alignment.CenterVertically,
) {
when (checkbox) {
is CheckboxState.TriState -> {
TriStateCheckbox(
state = checkbox.asToggleableState(),
onClick = { onChange(checkbox) },
)
}
is CheckboxState.State -> {
Checkbox(
checked = checkbox.isChecked,
onCheckedChange = { onChange(checkbox) },
)
}
}
Text(
text = checkbox.value.visualName,
modifier = Modifier.padding(horizontal = MaterialTheme.padding.medium),
)
}
}
}
},
)
}
private fun List<Category>.anyWithName(name: String): Boolean {
return any { name == it.name }
}

View File

@@ -7,10 +7,10 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.components.ExtendedFloatingActionButton
import eu.kanade.presentation.util.isScrolledToEnd
import eu.kanade.presentation.util.isScrollingUp
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
import tachiyomi.presentation.core.util.isScrolledToEnd
import tachiyomi.presentation.core.util.isScrollingUp
@Composable
fun CategoryFloatingActionButton(

View File

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

View File

@@ -1,75 +1,25 @@
package eu.kanade.presentation.components
import androidx.activity.compose.BackHandler
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.with
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredWidthIn
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.shape.ZeroCornerSize
import androidx.compose.material.SwipeableState
import androidx.compose.material.rememberSwipeableState
import androidx.compose.material.swipeable
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.animation.togetherWith
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import cafe.adriel.voyager.core.annotation.InternalVoyagerApi
import cafe.adriel.voyager.core.lifecycle.DisposableEffectIgnoringConfiguration
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.transitions.ScreenTransition
import eu.kanade.presentation.util.ScreenTransition
import eu.kanade.presentation.util.isTabletUi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
import kotlin.time.Duration.Companion.milliseconds
private const val SheetAnimationDuration = 500
private val SheetAnimationSpec = tween<Float>(durationMillis = SheetAnimationDuration)
private const val ScrimAnimationDuration = 350
private val ScrimAnimationSpec = tween<Float>(durationMillis = ScrimAnimationDuration)
import tachiyomi.presentation.core.components.AdaptiveSheet as AdaptiveSheetImpl
@OptIn(InternalVoyagerApi::class)
@Composable
fun NavigatorAdaptiveSheet(
screen: Screen,
@@ -88,7 +38,7 @@ fun NavigatorAdaptiveSheet(
ScreenTransition(
navigator = sheetNavigator,
transition = {
fadeIn(animationSpec = tween(220, delayMillis = 90)) with
fadeIn(animationSpec = tween(220, delayMillis = 90)) togetherWith
fadeOut(animationSpec = tween(90))
},
)
@@ -121,224 +71,31 @@ fun NavigatorAdaptiveSheet(
*/
@Composable
fun AdaptiveSheet(
modifier: Modifier = Modifier,
tonalElevation: Dp = 1.dp,
enableSwipeDismiss: Boolean = true,
onDismissRequest: () -> Unit,
content: @Composable (PaddingValues) -> Unit,
) {
val isTabletUi = isTabletUi()
AdaptiveSheetImpl(
isTabletUi = isTabletUi,
tonalElevation = tonalElevation,
enableSwipeDismiss = enableSwipeDismiss,
onDismissRequest = onDismissRequest,
) {
val contentPadding = if (isTabletUi) {
PaddingValues()
} else {
WindowInsets.navigationBars.only(WindowInsetsSides.Bottom).asPaddingValues()
}
content(contentPadding)
}
}
@Composable
fun AdaptiveSheetImpl(
isTabletUi: Boolean,
tonalElevation: Dp,
enableSwipeDismiss: Boolean,
onDismissRequest: () -> Unit,
content: @Composable () -> Unit,
) {
val scope = rememberCoroutineScope()
if (isTabletUi) {
var targetAlpha by remember { mutableStateOf(0f) }
val alpha by animateFloatAsState(
targetValue = targetAlpha,
animationSpec = ScrimAnimationSpec,
)
val internalOnDismissRequest: () -> Unit = {
scope.launch {
targetAlpha = 0f
delay(ScrimAnimationSpec.durationMillis.milliseconds)
onDismissRequest()
}
}
BoxWithConstraints(
modifier = Modifier
.clickable(
enabled = true,
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = internalOnDismissRequest,
)
.fillMaxSize()
.alpha(alpha),
contentAlignment = Alignment.Center,
) {
Box(
modifier = Modifier
.matchParentSize()
.background(MaterialTheme.colorScheme.scrim.copy(alpha = 0.32f)),
)
Surface(
modifier = Modifier
.requiredWidthIn(max = 460.dp)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = {},
)
.systemBarsPadding()
.padding(vertical = 16.dp),
shape = MaterialTheme.shapes.extraLarge,
tonalElevation = tonalElevation,
content = {
BackHandler(enabled = alpha > 0f, onBack = internalOnDismissRequest)
content()
},
)
val isTabletUi = isTabletUi()
LaunchedEffect(Unit) {
targetAlpha = 1f
}
}
} else {
val swipeState = rememberSwipeableState(
initialValue = 1,
animationSpec = SheetAnimationSpec,
)
val internalOnDismissRequest: () -> Unit = { if (swipeState.currentValue == 0) scope.launch { swipeState.animateTo(1) } }
BoxWithConstraints(
modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = internalOnDismissRequest,
)
.fillMaxSize(),
contentAlignment = Alignment.BottomCenter,
Dialog(
onDismissRequest = onDismissRequest,
properties = dialogProperties,
) {
AdaptiveSheetImpl(
modifier = modifier,
isTabletUi = isTabletUi,
tonalElevation = tonalElevation,
enableSwipeDismiss = enableSwipeDismiss,
onDismissRequest = onDismissRequest,
) {
val fullHeight = constraints.maxHeight.toFloat()
val anchors = mapOf(0f to 0, fullHeight to 1)
val scrimAlpha by animateFloatAsState(
targetValue = if (swipeState.targetValue == 1) 0f else 1f,
animationSpec = ScrimAnimationSpec,
)
Box(
modifier = Modifier
.matchParentSize()
.alpha(scrimAlpha)
.background(MaterialTheme.colorScheme.scrim.copy(alpha = 0.32f)),
)
Surface(
modifier = Modifier
.widthIn(max = 460.dp)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = {},
)
.nestedScroll(
remember(enableSwipeDismiss, anchors) {
swipeState.preUpPostDownNestedScrollConnection(
enabled = enableSwipeDismiss,
anchor = anchors,
)
},
)
.offset {
IntOffset(
0,
swipeState.offset.value.roundToInt(),
)
}
.swipeable(
enabled = enableSwipeDismiss,
state = swipeState,
anchors = anchors,
orientation = Orientation.Vertical,
resistance = null,
)
.windowInsetsPadding(
WindowInsets.systemBars
.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
)
.consumeWindowInsets(
WindowInsets.systemBars
.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
),
shape = MaterialTheme.shapes.extraLarge.copy(bottomStart = ZeroCornerSize, bottomEnd = ZeroCornerSize),
tonalElevation = tonalElevation,
content = {
BackHandler(enabled = swipeState.targetValue == 0, onBack = internalOnDismissRequest)
content()
},
)
LaunchedEffect(swipeState) {
scope.launch { swipeState.animateTo(0) }
snapshotFlow { swipeState.currentValue }
.drop(1)
.filter { it == 1 }
.collectLatest {
delay(ScrimAnimationSpec.durationMillis.milliseconds)
onDismissRequest()
}
}
content()
}
}
}
/**
* Yoinked from Swipeable.kt with modifications to disable
*/
private fun <T> SwipeableState<T>.preUpPostDownNestedScrollConnection(
enabled: Boolean = true,
anchor: Map<Float, T>,
) = object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
val delta = available.toFloat()
return if (enabled && delta < 0 && source == NestedScrollSource.Drag) {
performDrag(delta).toOffset()
} else {
Offset.Zero
}
}
override fun onPostScroll(
consumed: Offset,
available: Offset,
source: NestedScrollSource,
): Offset {
return if (enabled && source == NestedScrollSource.Drag) {
performDrag(available.toFloat()).toOffset()
} else {
Offset.Zero
}
}
override suspend fun onPreFling(available: Velocity): Velocity {
val toFling = Offset(available.x, available.y).toFloat()
return if (enabled && toFling < 0 && offset.value > anchor.keys.minOrNull()!!) {
performFling(velocity = toFling)
// since we go to the anchor with tween settling, consume all for the best UX
available
} else {
Velocity.Zero
}
}
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
return if (enabled) {
performFling(velocity = Offset(available.x, available.y).toFloat())
available
} else {
Velocity.Zero
}
}
private fun Float.toOffset(): Offset = Offset(0f, this)
private fun Offset.toFloat(): Float = this.y
}
private val dialogProperties = DialogProperties(
usePlatformDefaultWidth = false,
decorFitsSystemWindows = false,
)

View File

@@ -1,93 +0,0 @@
package eu.kanade.presentation.components
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun AlertDialogContent(
buttons: @Composable () -> Unit,
modifier: Modifier = Modifier,
icon: (@Composable () -> Unit)? = null,
title: (@Composable () -> Unit)? = null,
text: @Composable (() -> Unit)? = null,
) {
Column(
modifier = modifier
.sizeIn(minWidth = MinWidth, maxWidth = MaxWidth)
.padding(DialogPadding),
) {
icon?.let {
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.secondary) {
Box(
Modifier
.padding(IconPadding)
.align(Alignment.CenterHorizontally),
) {
icon()
}
}
}
title?.let {
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface) {
val textStyle = MaterialTheme.typography.headlineSmall
ProvideTextStyle(textStyle) {
Box(
// Align the title to the center when an icon is present.
Modifier
.padding(TitlePadding)
.align(
if (icon == null) {
Alignment.Start
} else {
Alignment.CenterHorizontally
},
),
) {
title()
}
}
}
}
text?.let {
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant) {
val textStyle = MaterialTheme.typography.bodyMedium
ProvideTextStyle(textStyle) {
Box(
Modifier
.weight(weight = 1f, fill = false)
.padding(TextPadding)
.align(Alignment.Start),
) {
text()
}
}
}
}
Box(modifier = Modifier.align(Alignment.End)) {
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.primary) {
val textStyle = MaterialTheme.typography.labelLarge
ProvideTextStyle(value = textStyle, content = buttons)
}
}
}
}
// Paddings for each of the dialog's parts.
private val DialogPadding = PaddingValues(all = 24.dp)
private val IconPadding = PaddingValues(bottom = 16.dp)
private val TitlePadding = PaddingValues(bottom = 16.dp)
private val TextPadding = PaddingValues(bottom = 24.dp)
private val MinWidth = 280.dp
private val MaxWidth = 560.dp

View File

@@ -1,5 +1,6 @@
package eu.kanade.presentation.components
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.RowScope
@@ -10,20 +11,22 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material.icons.outlined.ArrowForward
import androidx.compose.material.icons.outlined.Close
import androidx.compose.material.icons.outlined.MoreVert
import androidx.compose.material.icons.outlined.Search
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PlainTooltipBox
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
@@ -33,32 +36,38 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import eu.kanade.presentation.util.runOnEnterKeyPressed
import eu.kanade.presentation.util.secondaryItemAlpha
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.util.clearFocusOnSoftKeyboardHide
import tachiyomi.presentation.core.util.runOnEnterKeyPressed
import tachiyomi.presentation.core.util.secondaryItemAlpha
import tachiyomi.presentation.core.util.showSoftKeyboard
const val SEARCH_DEBOUNCE_MILLIS = 250L
@Composable
fun AppBar(
modifier: Modifier = Modifier,
backgroundColor: Color? = null,
// Text
title: String?,
subtitle: String? = null,
// Up button
navigateUp: (() -> Unit)? = null,
navigationIcon: ImageVector = Icons.Outlined.ArrowBack,
navigationIcon: ImageVector? = null,
// Menu
actions: @Composable RowScope.() -> Unit = {},
// Action mode
@@ -74,6 +83,7 @@ fun AppBar(
AppBar(
modifier = modifier,
backgroundColor = backgroundColor,
titleContent = {
if (isActionMode) {
AppBarTitle(actionModeCounter.toString())
@@ -99,11 +109,12 @@ fun AppBar(
@Composable
fun AppBar(
modifier: Modifier = Modifier,
backgroundColor: Color? = null,
// Title
titleContent: @Composable () -> Unit,
// Up button
navigateUp: (() -> Unit)? = null,
navigationIcon: ImageVector = Icons.Outlined.ArrowBack,
navigationIcon: ImageVector? = null,
// Menu
actions: @Composable RowScope.() -> Unit = {},
// Action mode
@@ -127,18 +138,15 @@ fun AppBar(
} else {
navigateUp?.let {
IconButton(onClick = it) {
Icon(
imageVector = navigationIcon,
contentDescription = stringResource(R.string.abc_action_bar_up_description),
)
UpIcon(navigationIcon)
}
}
}
},
title = titleContent,
actions = actions,
colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
colors = TopAppBarDefaults.topAppBarColors(
containerColor = backgroundColor ?: MaterialTheme.colorScheme.surfaceColorAtElevation(
elevation = if (isActionMode) 3.dp else 0.dp,
),
),
@@ -166,6 +174,9 @@ fun AppBarTitle(
style = MaterialTheme.typography.bodyMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.basicMarquee(
delayMillis = 2_000,
),
)
}
}
@@ -178,21 +189,37 @@ fun AppBarActions(
var showMenu by remember { mutableStateOf(false) }
actions.filterIsInstance<AppBar.Action>().map {
IconButton(
onClick = it.onClick,
enabled = it.enabled,
PlainTooltipBox(
tooltip = { Text(it.title) },
) {
Icon(
imageVector = it.icon,
contentDescription = it.title,
)
IconButton(
onClick = it.onClick,
enabled = it.enabled,
modifier = Modifier.tooltipTrigger(),
) {
Icon(
imageVector = it.icon,
tint = it.iconTint ?: LocalContentColor.current,
contentDescription = it.title,
)
}
}
}
val overflowActions = actions.filterIsInstance<AppBar.OverflowAction>()
if (overflowActions.isNotEmpty()) {
IconButton(onClick = { showMenu = !showMenu }) {
Icon(Icons.Outlined.MoreVert, contentDescription = stringResource(R.string.abc_action_menu_overflow_description))
PlainTooltipBox(
tooltip = { Text(stringResource(R.string.abc_action_menu_overflow_description)) },
) {
IconButton(
onClick = { showMenu = !showMenu },
modifier = Modifier.tooltipTrigger(),
) {
Icon(
Icons.Outlined.MoreVert,
contentDescription = stringResource(R.string.abc_action_menu_overflow_description),
)
}
}
DropdownMenu(
@@ -233,7 +260,6 @@ fun SearchToolbar(
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
val focusRequester = remember { FocusRequester() }
var searchClickCount by remember { mutableStateOf(0) }
AppBar(
titleContent = {
@@ -255,7 +281,9 @@ fun SearchToolbar(
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester)
.runOnEnterKeyPressed(action = searchAndClearFocus),
.runOnEnterKeyPressed(action = searchAndClearFocus)
.showSoftKeyboard(remember { searchQuery.isEmpty() })
.clearFocusOnSoftKeyboardHide(),
textStyle = MaterialTheme.typography.titleMedium.copy(
color = MaterialTheme.colorScheme.onBackground,
fontWeight = FontWeight.Normal,
@@ -276,18 +304,16 @@ fun SearchToolbar(
visualTransformation = visualTransformation,
interactionSource = interactionSource,
placeholder = {
(placeholderText ?: stringResource(R.string.action_search_hint)).let { placeholderText ->
Text(
modifier = Modifier.secondaryItemAlpha(),
text = placeholderText,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleMedium.copy(
fontSize = 18.sp,
fontWeight = FontWeight.Normal,
),
)
}
Text(
modifier = Modifier.secondaryItemAlpha(),
text = (placeholderText ?: stringResource(R.string.action_search_hint)),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleMedium.copy(
fontSize = 18.sp,
fontWeight = FontWeight.Normal,
),
)
},
)
},
@@ -296,20 +322,40 @@ fun SearchToolbar(
navigateUp = if (searchQuery == null) navigateUp else onClickCloseSearch,
actions = {
key("search") {
val onClick = {
searchClickCount++
onChangeSearchQuery("")
}
val onClick = { onChangeSearchQuery("") }
if (!searchEnabled) {
// Don't show search action
} else if (searchQuery == null) {
IconButton(onClick) {
Icon(Icons.Outlined.Search, contentDescription = stringResource(R.string.action_search))
PlainTooltipBox(
tooltip = { Text(stringResource(R.string.action_search)) },
) {
IconButton(
onClick = onClick,
modifier = Modifier.tooltipTrigger(),
) {
Icon(
Icons.Outlined.Search,
contentDescription = stringResource(R.string.action_search),
)
}
}
} else if (searchQuery.isNotEmpty()) {
IconButton(onClick) {
Icon(Icons.Outlined.Close, contentDescription = stringResource(R.string.action_reset))
PlainTooltipBox(
tooltip = { Text(stringResource(R.string.action_reset)) },
) {
IconButton(
onClick = {
onClick()
focusRequester.requestFocus()
},
modifier = Modifier.tooltipTrigger(),
) {
Icon(
Icons.Outlined.Close,
contentDescription = stringResource(R.string.action_reset),
)
}
}
}
}
@@ -319,15 +365,16 @@ fun SearchToolbar(
isActionMode = false,
scrollBehavior = scrollBehavior,
)
LaunchedEffect(searchClickCount) {
if (searchQuery == null) return@LaunchedEffect
if (searchClickCount == 0 && searchQuery.isNotEmpty()) return@LaunchedEffect
try {
focusRequester.requestFocus()
} catch (_: Throwable) {
// TextField is gone
}
}
}
@Composable
fun UpIcon(navigationIcon: ImageVector? = null) {
val icon = navigationIcon
?: if (LocalLayoutDirection.current == LayoutDirection.Ltr) Icons.Outlined.ArrowBack else Icons.Outlined.ArrowForward
Icon(
imageVector = icon,
contentDescription = stringResource(R.string.abc_action_bar_up_description),
)
}
sealed interface AppBar {
@@ -336,6 +383,7 @@ sealed interface AppBar {
data class Action(
val title: String,
val icon: ImageVector,
val iconTint: Color? = null,
val onClick: () -> Unit,
val enabled: Boolean = true,
) : AppBarAction

View File

@@ -1,47 +0,0 @@
package eu.kanade.presentation.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@Composable
fun BadgeGroup(
modifier: Modifier = Modifier,
shape: Shape = MaterialTheme.shapes.extraSmall,
content: @Composable RowScope.() -> Unit,
) {
Row(modifier = modifier.clip(shape)) {
content()
}
}
@Composable
fun Badge(
text: String,
color: Color = MaterialTheme.colorScheme.secondary,
textColor: Color = MaterialTheme.colorScheme.onSecondary,
shape: Shape = RectangleShape,
) {
Text(
text = text,
modifier = Modifier
.clip(shape)
.background(color)
.padding(horizontal = 3.dp, vertical = 1.dp),
color = textColor,
fontWeight = FontWeight.Medium,
maxLines = 1,
style = MaterialTheme.typography.bodySmall,
)
}

View File

@@ -1,133 +0,0 @@
package eu.kanade.presentation.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TriStateCheckbox
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import eu.kanade.core.prefs.CheckboxState
import eu.kanade.presentation.category.visualName
import eu.kanade.presentation.util.padding
import eu.kanade.tachiyomi.R
import tachiyomi.domain.category.model.Category
@Composable
fun ChangeCategoryDialog(
initialSelection: List<CheckboxState<Category>>,
onDismissRequest: () -> Unit,
onEditCategories: () -> Unit,
onConfirm: (List<Long>, List<Long>) -> Unit,
) {
if (initialSelection.isEmpty()) {
AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
TextButton(
onClick = {
onDismissRequest()
onEditCategories()
},
) {
Text(text = stringResource(R.string.action_edit_categories))
}
},
title = {
Text(text = stringResource(R.string.action_move_category))
},
text = {
Text(text = stringResource(R.string.information_empty_category_dialog))
},
)
return
}
var selection by remember { mutableStateOf(initialSelection) }
AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
Row {
TextButton(onClick = {
onDismissRequest()
onEditCategories()
},) {
Text(text = stringResource(R.string.action_edit))
}
Spacer(modifier = Modifier.weight(1f))
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(R.string.action_cancel))
}
TextButton(
onClick = {
onDismissRequest()
onConfirm(
selection.filter { it is CheckboxState.State.Checked || it is CheckboxState.TriState.Include }.map { it.value.id },
selection.filter { it is CheckboxState.State.None || it is CheckboxState.TriState.None }.map { it.value.id },
)
},
) {
Text(text = stringResource(android.R.string.ok))
}
}
},
title = {
Text(text = stringResource(R.string.action_move_category))
},
text = {
Column(
modifier = Modifier.verticalScroll(rememberScrollState()),
) {
selection.forEach { checkbox ->
val onChange: (CheckboxState<Category>) -> Unit = {
val index = selection.indexOf(it)
if (index != -1) {
val mutableList = selection.toMutableList()
mutableList[index] = it.next()
selection = mutableList.toList()
}
}
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { onChange(checkbox) },
verticalAlignment = Alignment.CenterVertically,
) {
when (checkbox) {
is CheckboxState.TriState -> {
TriStateCheckbox(
state = checkbox.asState(),
onClick = { onChange(checkbox) },
)
}
is CheckboxState.State -> {
Checkbox(
checked = checkbox.isChecked,
onCheckedChange = { onChange(checkbox) },
)
}
}
Text(
text = checkbox.value.visualName,
modifier = Modifier.padding(horizontal = MaterialTheme.padding.medium),
)
}
}
}
},
)
}

View File

@@ -1,44 +0,0 @@
package eu.kanade.presentation.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.material3.DividerDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
const val DIVIDER_ALPHA = 0.2f
@Composable
fun Divider(
modifier: Modifier = Modifier,
color: Color = DividerDefaults.color,
) {
Box(
modifier
.fillMaxWidth()
.height(1.dp)
.background(color = color)
.alpha(DIVIDER_ALPHA),
)
}
@Composable
fun VerticalDivider(
modifier: Modifier = Modifier,
color: Color = DividerDefaults.color,
) {
Box(
modifier
.fillMaxHeight()
.width(1.dp)
.background(color = color)
.alpha(DIVIDER_ALPHA),
)
}

View File

@@ -3,6 +3,7 @@ package eu.kanade.presentation.components
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.manga.DownloadAction
import eu.kanade.tachiyomi.R
@@ -12,52 +13,22 @@ fun DownloadDropdownMenu(
expanded: Boolean,
onDismissRequest: () -> Unit,
onDownloadClicked: (DownloadAction) -> Unit,
includeDownloadAllOption: Boolean = true,
) {
DropdownMenu(
expanded = expanded,
onDismissRequest = onDismissRequest,
) {
DropdownMenuItem(
text = { Text(text = stringResource(R.string.download_1)) },
onClick = {
onDownloadClicked(DownloadAction.NEXT_1_CHAPTER)
onDismissRequest()
},
)
DropdownMenuItem(
text = { Text(text = stringResource(R.string.download_5)) },
onClick = {
onDownloadClicked(DownloadAction.NEXT_5_CHAPTERS)
onDismissRequest()
},
)
DropdownMenuItem(
text = { Text(text = stringResource(R.string.download_10)) },
onClick = {
onDownloadClicked(DownloadAction.NEXT_10_CHAPTERS)
onDismissRequest()
},
)
DropdownMenuItem(
text = { Text(text = stringResource(R.string.download_custom)) },
onClick = {
onDownloadClicked(DownloadAction.CUSTOM)
onDismissRequest()
},
)
DropdownMenuItem(
text = { Text(text = stringResource(R.string.download_unread)) },
onClick = {
onDownloadClicked(DownloadAction.UNREAD_CHAPTERS)
onDismissRequest()
},
)
if (includeDownloadAllOption) {
listOfNotNull(
DownloadAction.NEXT_1_CHAPTER to pluralStringResource(R.plurals.download_amount, 1, 1),
DownloadAction.NEXT_5_CHAPTERS to pluralStringResource(R.plurals.download_amount, 5, 5),
DownloadAction.NEXT_10_CHAPTERS to pluralStringResource(R.plurals.download_amount, 10, 10),
DownloadAction.NEXT_25_CHAPTERS to pluralStringResource(R.plurals.download_amount, 25, 25),
DownloadAction.UNREAD_CHAPTERS to stringResource(R.string.download_unread),
).map { (downloadAction, string) ->
DropdownMenuItem(
text = { Text(text = stringResource(R.string.download_all)) },
text = { Text(text = string) },
onClick = {
onDownloadClicked(DownloadAction.ALL_CHAPTERS)
onDownloadClicked(downloadAction)
onDismissRequest()
},
)

View File

@@ -1,15 +1,14 @@
package eu.kanade.presentation.components
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.MoreVert
import androidx.compose.material.icons.outlined.ArrowLeft
import androidx.compose.material.icons.outlined.ArrowRight
import androidx.compose.material.icons.outlined.RadioButtonChecked
import androidx.compose.material.icons.outlined.RadioButtonUnchecked
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -17,13 +16,13 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.PopupProperties
import eu.kanade.tachiyomi.R
import me.saket.cascade.CascadeColumnScope
import me.saket.cascade.CascadeDropdownMenu
import androidx.compose.material3.DropdownMenu as ComposeDropdownMenu
@Composable
@@ -72,25 +71,29 @@ fun RadioMenuItem(
}
@Composable
fun OverflowMenu(
content: @Composable CascadeColumnScope.(() -> Unit) -> Unit,
fun NestedMenuItem(
text: @Composable () -> Unit,
children: @Composable ColumnScope.(() -> Unit) -> Unit,
) {
var moreExpanded by remember { mutableStateOf(false) }
val closeMenu = { moreExpanded = false }
var nestedExpanded by remember { mutableStateOf(false) }
val closeMenu = { nestedExpanded = false }
val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr
Box {
IconButton(onClick = { moreExpanded = !moreExpanded }) {
DropdownMenuItem(
text = text,
onClick = { nestedExpanded = true },
trailingIcon = {
Icon(
imageVector = Icons.Outlined.MoreVert,
contentDescription = stringResource(R.string.abc_action_menu_overflow_description),
imageVector = if (isLtr) Icons.Outlined.ArrowRight else Icons.Outlined.ArrowLeft,
contentDescription = null,
)
}
CascadeDropdownMenu(
expanded = moreExpanded,
onDismissRequest = closeMenu,
offset = DpOffset(8.dp, (-56).dp),
) {
content(closeMenu)
}
},
)
DropdownMenu(
expanded = nestedExpanded,
onDismissRequest = closeMenu,
) {
children(closeMenu)
}
}

View File

@@ -1,124 +1,15 @@
package eu.kanade.presentation.components
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.paddingFromBaseline
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.HelpOutline
import androidx.compose.material.icons.outlined.Refresh
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.presentation.util.ThemePreviews
import eu.kanade.presentation.util.padding
import eu.kanade.presentation.util.secondaryItemAlpha
import eu.kanade.tachiyomi.R
import kotlin.random.Random
@Composable
fun EmptyScreen(
@StringRes textResource: Int,
modifier: Modifier = Modifier,
actions: List<EmptyScreenAction>? = null,
) {
EmptyScreen(
message = stringResource(textResource),
modifier = modifier,
actions = actions,
)
}
@Composable
fun EmptyScreen(
message: String,
modifier: Modifier = Modifier,
actions: List<EmptyScreenAction>? = null,
) {
val face = remember { getRandomErrorFace() }
Column(
modifier = modifier
.fillMaxSize()
.padding(horizontal = 24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text(
text = face,
modifier = Modifier.secondaryItemAlpha(),
style = MaterialTheme.typography.displayMedium,
)
Text(
text = message,
modifier = Modifier.paddingFromBaseline(top = 24.dp).secondaryItemAlpha(),
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
)
if (!actions.isNullOrEmpty()) {
Row(
modifier = Modifier
.padding(
top = 24.dp,
start = 24.dp,
end = 24.dp,
),
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
) {
actions.forEach {
ActionButton(
modifier = Modifier.weight(1f),
title = stringResource(it.stringResId),
icon = it.icon,
onClick = it.onClick,
)
}
}
}
}
}
@Composable
private fun ActionButton(
modifier: Modifier = Modifier,
title: String,
icon: ImageVector,
onClick: () -> Unit,
) {
TextButton(
modifier = modifier,
onClick = onClick,
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Icon(
imageVector = icon,
contentDescription = null,
)
Spacer(Modifier.height(4.dp))
Text(
text = title,
textAlign = TextAlign.Center,
)
}
}
}
import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.EmptyScreenAction
import tachiyomi.presentation.core.util.ThemePreviews
@ThemePreviews
@Composable
@@ -155,22 +46,3 @@ private fun WithActionPreview() {
}
}
}
data class EmptyScreenAction(
@StringRes val stringResId: Int,
val icon: ImageVector,
val onClick: () -> Unit,
)
private val ERROR_FACES = listOf(
"(・o・;)",
"Σ(ಠ_ಠ)",
"ಥ_ಥ",
"(˘・_・˘)",
"(; ̄Д ̄)",
"(・Д・。",
)
private fun getRandomErrorFace(): String {
return ERROR_FACES[Random.nextInt(ERROR_FACES.size)]
}

View File

@@ -1,259 +0,0 @@
package eu.kanade.presentation.components
import androidx.compose.foundation.gestures.FlingBehavior
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyListItemInfo
import androidx.compose.foundation.lazy.LazyListLayoutInfo
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Density
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastMaxBy
import androidx.compose.ui.util.fastSumBy
import kotlinx.coroutines.flow.distinctUntilChanged
@Composable
fun HorizontalPager(
count: Int,
modifier: Modifier = Modifier,
state: PagerState = rememberPagerState(),
key: ((page: Int) -> Any)? = null,
contentPadding: PaddingValues = PaddingValues(),
verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
userScrollEnabled: Boolean = true,
content: @Composable BoxScope.(page: Int) -> Unit,
) {
Pager(
count = count,
modifier = modifier,
state = state,
isVertical = false,
key = key,
contentPadding = contentPadding,
verticalAlignment = verticalAlignment,
userScrollEnabled = userScrollEnabled,
content = content,
)
}
@Composable
private fun Pager(
count: Int,
modifier: Modifier,
state: PagerState,
isVertical: Boolean,
key: ((page: Int) -> Any)?,
contentPadding: PaddingValues,
userScrollEnabled: Boolean,
verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
content: @Composable BoxScope.(page: Int) -> Unit,
) {
LaunchedEffect(count) {
state.currentPage = minOf(count - 1, state.currentPage).coerceAtLeast(0)
}
LaunchedEffect(state) {
snapshotFlow { state.mostVisiblePageLayoutInfo?.index }
.distinctUntilChanged()
.collect { state.updateCurrentPageBasedOnLazyListState() }
}
if (isVertical) {
LazyColumn(
modifier = modifier,
state = state.lazyListState,
contentPadding = contentPadding,
horizontalAlignment = horizontalAlignment,
verticalArrangement = Arrangement.aligned(verticalAlignment),
userScrollEnabled = userScrollEnabled,
flingBehavior = rememberLazyListSnapFlingBehavior(lazyListState = state.lazyListState),
) {
items(
count = count,
key = key,
) { page ->
Box(
modifier = Modifier
.fillParentMaxHeight()
.wrapContentSize(),
) {
content(this, page)
}
}
}
} else {
LazyRow(
modifier = modifier,
state = state.lazyListState,
contentPadding = contentPadding,
verticalAlignment = verticalAlignment,
horizontalArrangement = Arrangement.aligned(horizontalAlignment),
userScrollEnabled = userScrollEnabled,
flingBehavior = rememberLazyListSnapFlingBehavior(lazyListState = state.lazyListState),
) {
items(
count = count,
key = key,
) { page ->
Box(
modifier = Modifier
.fillParentMaxWidth()
.wrapContentSize(),
) {
content(this, page)
}
}
}
}
}
@Composable
fun rememberPagerState(
initialPage: Int = 0,
) = rememberSaveable(saver = PagerState.Saver) {
PagerState(currentPage = initialPage)
}
@Stable
class PagerState(
currentPage: Int = 0,
) {
init { check(currentPage >= 0) { "currentPage cannot be less than zero" } }
val lazyListState = LazyListState(firstVisibleItemIndex = currentPage)
private var _currentPage by mutableStateOf(currentPage)
var currentPage: Int
get() = _currentPage
set(value) {
if (value != _currentPage) {
_currentPage = value
}
}
val mostVisiblePageLayoutInfo: LazyListItemInfo?
get() {
val layoutInfo = lazyListState.layoutInfo
return layoutInfo.visibleItemsInfo.fastMaxBy {
val start = maxOf(it.offset, 0)
val end = minOf(
it.offset + it.size,
layoutInfo.viewportEndOffset - layoutInfo.afterContentPadding,
)
end - start
}
}
fun updateCurrentPageBasedOnLazyListState() {
mostVisiblePageLayoutInfo?.let {
currentPage = it.index
}
}
suspend fun animateScrollToPage(page: Int) {
lazyListState.animateScrollToItem(index = page)
}
suspend fun scrollToPage(page: Int) {
lazyListState.scrollToItem(index = page)
updateCurrentPageBasedOnLazyListState()
}
companion object {
val Saver: Saver<PagerState, *> = listSaver(
save = { listOf(it.currentPage) },
restore = { PagerState(it[0]) },
)
}
}
// https://android.googlesource.com/platform/frameworks/support/+/refs/changes/78/2160778/35/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
private fun lazyListSnapLayoutInfoProvider(
lazyListState: LazyListState,
positionInLayout: (layoutSize: Float, itemSize: Float) -> Float = { layoutSize, itemSize ->
layoutSize / 2f - itemSize / 2f
},
) = object : SnapLayoutInfoProvider {
private val layoutInfo: LazyListLayoutInfo
get() = lazyListState.layoutInfo
// Single page snapping is the default
override fun Density.calculateApproachOffset(initialVelocity: Float): Float = 0f
override fun Density.calculateSnappingOffsetBounds(): ClosedFloatingPointRange<Float> {
var lowerBoundOffset = Float.NEGATIVE_INFINITY
var upperBoundOffset = Float.POSITIVE_INFINITY
layoutInfo.visibleItemsInfo.fastForEach { item ->
val offset =
calculateDistanceToDesiredSnapPosition(layoutInfo, item, positionInLayout)
// Find item that is closest to the center
if (offset <= 0 && offset > lowerBoundOffset) {
lowerBoundOffset = offset
}
// Find item that is closest to center, but after it
if (offset >= 0 && offset < upperBoundOffset) {
upperBoundOffset = offset
}
}
return lowerBoundOffset.rangeTo(upperBoundOffset)
}
override fun Density.calculateSnapStepSize(): Float = with(layoutInfo) {
if (visibleItemsInfo.isNotEmpty()) {
visibleItemsInfo.fastSumBy { it.size } / visibleItemsInfo.size.toFloat()
} else {
0f
}
}
}
@Composable
private fun rememberLazyListSnapFlingBehavior(lazyListState: LazyListState): FlingBehavior {
val snappingLayout = remember(lazyListState) { lazyListSnapLayoutInfoProvider(lazyListState) }
return rememberSnapFlingBehavior(snappingLayout)
}
private fun calculateDistanceToDesiredSnapPosition(
layoutInfo: LazyListLayoutInfo,
item: LazyListItemInfo,
positionInLayout: (layoutSize: Float, itemSize: Float) -> Float,
): Float {
val containerSize =
with(layoutInfo) { singleAxisViewportSize - beforeContentPadding - afterContentPadding }
val desiredDistance =
positionInLayout(containerSize.toFloat(), item.size.toFloat())
val itemCurrentPosition = item.offset
return itemCurrentPosition - desiredDistance
}
private val LazyListLayoutInfo.singleAxisViewportSize: Int
get() = if (orientation == Orientation.Vertical) viewportSize.height else viewportSize.width

View File

@@ -5,6 +5,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import eu.kanade.tachiyomi.util.lang.toRelativeString
import tachiyomi.presentation.core.components.ListGroupHeader
import java.text.DateFormat
import java.util.Date
@@ -12,7 +13,7 @@ import java.util.Date
fun RelativeDateHeader(
modifier: Modifier = Modifier,
date: Date,
relativeTime: Int,
relativeTime: Boolean,
dateFormat: DateFormat,
) {
val context = LocalContext.current

View File

@@ -1,135 +0,0 @@
package eu.kanade.presentation.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.ContentAlpha
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDownward
import androidx.compose.material.icons.filled.ArrowUpward
import androidx.compose.material.icons.rounded.CheckBox
import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank
import androidx.compose.material.icons.rounded.DisabledByDefault
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import tachiyomi.domain.manga.model.TriStateFilter
@Composable
fun TriStateItem(
label: String,
state: TriStateFilter,
onClick: ((TriStateFilter) -> Unit)?,
) {
Row(
modifier = Modifier
.clickable(
enabled = onClick != null,
onClick = {
when (state) {
TriStateFilter.DISABLED -> onClick?.invoke(TriStateFilter.ENABLED_IS)
TriStateFilter.ENABLED_IS -> onClick?.invoke(TriStateFilter.ENABLED_NOT)
TriStateFilter.ENABLED_NOT -> onClick?.invoke(TriStateFilter.DISABLED)
}
},
)
.fillMaxWidth()
.padding(horizontal = TabbedDialogPaddings.Horizontal, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(24.dp),
) {
val stateAlpha = if (onClick != null) 1f else ContentAlpha.disabled
Icon(
imageVector = when (state) {
TriStateFilter.DISABLED -> Icons.Rounded.CheckBoxOutlineBlank
TriStateFilter.ENABLED_IS -> Icons.Rounded.CheckBox
TriStateFilter.ENABLED_NOT -> Icons.Rounded.DisabledByDefault
},
contentDescription = null,
tint = if (state == TriStateFilter.DISABLED) {
MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = stateAlpha)
} else {
when (onClick) {
null -> MaterialTheme.colorScheme.onSurface.copy(alpha = ContentAlpha.disabled)
else -> MaterialTheme.colorScheme.primary
}
},
)
Text(
text = label,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = stateAlpha),
style = MaterialTheme.typography.bodyMedium,
)
}
}
@Composable
fun SortItem(
label: String,
sortDescending: Boolean?,
onClick: () -> Unit,
) {
val arrowIcon = when (sortDescending) {
true -> Icons.Default.ArrowDownward
false -> Icons.Default.ArrowUpward
null -> null
}
Row(
modifier = Modifier
.clickable(onClick = onClick)
.fillMaxWidth()
.padding(horizontal = TabbedDialogPaddings.Horizontal, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(24.dp),
) {
if (arrowIcon != null) {
Icon(
imageVector = arrowIcon,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
)
} else {
Spacer(modifier = Modifier.size(24.dp))
}
Text(
text = label,
style = MaterialTheme.typography.bodyMedium,
)
}
}
@Composable
fun RadioItem(
label: String,
selected: Boolean,
onClick: () -> Unit,
) {
Row(
modifier = Modifier
.clickable(onClick = onClick)
.fillMaxWidth()
.padding(horizontal = TabbedDialogPaddings.Horizontal, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(24.dp),
) {
RadioButton(
selected = selected,
onClick = null,
)
Text(
text = label,
style = MaterialTheme.typography.bodyMedium,
)
}
}

View File

@@ -1,36 +1,37 @@
package eu.kanade.presentation.components
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEachIndexed
import eu.kanade.tachiyomi.R
import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.HorizontalPager
import tachiyomi.presentation.core.components.material.TabIndicator
import tachiyomi.presentation.core.components.material.TabText
object TabbedDialogPaddings {
val Horizontal = 24.dp
@@ -39,68 +40,47 @@ object TabbedDialogPaddings {
@Composable
fun TabbedDialog(
modifier: Modifier = Modifier,
onDismissRequest: () -> Unit,
tabTitles: List<String>,
tabOverflowMenuContent: (@Composable ColumnScope.(() -> Unit) -> Unit)? = null,
content: @Composable (PaddingValues, Int) -> Unit,
pagerState: PagerState = rememberPagerState { tabTitles.size },
content: @Composable (Int) -> Unit,
) {
AdaptiveSheet(
modifier = modifier,
onDismissRequest = onDismissRequest,
) { contentPadding ->
) {
val scope = rememberCoroutineScope()
val pagerState = rememberPagerState()
Column {
Row {
TabRow(
modifier = Modifier.weight(1f),
selectedTabIndex = pagerState.currentPage,
indicator = { TabIndicator(it[pagerState.currentPage]) },
indicator = { TabIndicator(it[pagerState.currentPage], pagerState.currentPageOffsetFraction) },
divider = {},
) {
tabTitles.fastForEachIndexed { i, tab ->
val selected = pagerState.currentPage == i
tabTitles.fastForEachIndexed { index, tab ->
Tab(
selected = selected,
onClick = { scope.launch { pagerState.animateScrollToPage(i) } },
text = {
Text(
text = tab,
color = if (selected) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.onSurfaceVariant
},
)
},
selected = pagerState.currentPage == index,
onClick = { scope.launch { pagerState.animateScrollToPage(index) } },
text = { TabText(text = tab) },
unselectedContentColor = MaterialTheme.colorScheme.onSurface,
)
}
}
tabOverflowMenuContent?.let { MoreMenu(it) }
}
Divider()
HorizontalDivider()
val density = LocalDensity.current
var largestHeight by rememberSaveable { mutableStateOf(0f) }
HorizontalPager(
modifier = Modifier.heightIn(min = largestHeight.dp),
count = tabTitles.size,
modifier = Modifier.animateContentSize(),
state = pagerState,
verticalAlignment = Alignment.Top,
) { page ->
Box(
modifier = Modifier.onSizeChanged {
with(density) {
val heightDp = it.height.toDp()
if (heightDp.value > largestHeight) {
largestHeight = heightDp.value
}
}
},
) {
content(contentPadding, page)
}
content(page)
}
}
}

View File

@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
@@ -21,6 +22,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.HorizontalPager
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.components.material.TabIndicator
import tachiyomi.presentation.core.components.material.TabText
@Composable
fun TabbedScreen(
@@ -31,7 +36,7 @@ fun TabbedScreen(
onChangeSearchQuery: (String?) -> Unit = {},
) {
val scope = rememberCoroutineScope()
val state = rememberPagerState()
val state = rememberPagerState { tabs.size }
val snackbarHostState = remember { SnackbarHostState() }
LaunchedEffect(startIndex) {
@@ -64,7 +69,7 @@ fun TabbedScreen(
) {
TabRow(
selectedTabIndex = state.currentPage,
indicator = { TabIndicator(it[state.currentPage]) },
indicator = { TabIndicator(it[state.currentPage], state.currentPageOffsetFraction) },
) {
tabs.forEachIndexed { index, tab ->
Tab(
@@ -77,7 +82,6 @@ fun TabbedScreen(
}
HorizontalPager(
count = tabs.size,
modifier = Modifier.fillMaxSize(),
state = state,
verticalAlignment = Alignment.Top,

View File

@@ -1,48 +0,0 @@
package eu.kanade.presentation.components
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TabPosition
import androidx.compose.material3.TabRowDefaults
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
fun TabIndicator(currentTabPosition: TabPosition) {
TabRowDefaults.Indicator(
Modifier
.tabIndicatorOffset(currentTabPosition)
.padding(horizontal = 8.dp)
.clip(RoundedCornerShape(topStart = 3.dp, topEnd = 3.dp)),
)
}
@Composable
fun TabText(
text: String,
badgeCount: Int? = null,
) {
val pillAlpha = if (isSystemInDarkTheme()) 0.12f else 0.08f
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Text(text = text)
if (badgeCount != null) {
Pill(
text = "$badgeCount",
color = MaterialTheme.colorScheme.onBackground.copy(alpha = pillAlpha),
fontSize = 10.sp,
)
}
}
}

View File

@@ -14,13 +14,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.components.InfoScaffold
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.presentation.util.ThemePreviews
import eu.kanade.presentation.util.padding
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.CrashLogUtil
import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.screens.InfoScreen
import tachiyomi.presentation.core.util.ThemePreviews
@Composable
fun CrashScreen(
@@ -30,7 +30,7 @@ fun CrashScreen(
val scope = rememberCoroutineScope()
val context = LocalContext.current
InfoScaffold(
InfoScreen(
icon = Icons.Outlined.BugReport,
headingText = stringResource(R.string.crash_screen_title),
subtitleText = stringResource(R.string.crash_screen_description, stringResource(R.string.app_name)),

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