Compare commits
487 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b4668c6829 | |||
| 08d6c604bc | |||
| 02cec06535 | |||
| ebdb3f7478 | |||
| 3724d79825 | |||
| c3e2eb6672 | |||
| fa91695add | |||
| e7786bd16f | |||
| 3d70476b9f | |||
| e74e0de8f5 | |||
| a2f552d6d2 | |||
| a6bd0bbd2a | |||
| fd42bba188 | |||
| a0ec735066 | |||
| 89f5fce19d | |||
| bf711a995c | |||
| d977614b7a | |||
| d282df6973 | |||
| db5b3a69cc | |||
| c70c5dff25 | |||
| 25ace80419 | |||
| b8b468cea7 | |||
| 0ffc798e9a | |||
| ad5a76741a | |||
| 003c5ad39a | |||
| 582d0ef121 | |||
| 5566db160b | |||
| 6fb6838656 | |||
| 1e5d490c22 | |||
| 276aeb0f59 | |||
| c62d9d1446 | |||
| 4ff18364d9 | |||
| 6c8e4e951a | |||
| dc1fde628d | |||
| 241b70e5ce | |||
| 64c755ddf3 | |||
| 3ae6c0131b | |||
| e3b43de298 | |||
| 02ff6b4e2f | |||
| ee8379b12a | |||
| 20e1cc0a7d | |||
| 78b434e794 | |||
| 52c8b260e0 | |||
| cccdc99977 | |||
| ca15d2ccc5 | |||
| b7b0ffc885 | |||
| f1a57749c4 | |||
| 26d6c09d21 | |||
| 088e3b6800 | |||
| c8ee2dbff4 | |||
| 835a21b426 | |||
| 8d67c87639 | |||
| 73cd25e8ba | |||
| e9ed861f00 | |||
| e7c1d4deef | |||
| b8eb75fc68 | |||
| 3d34f3dd2f | |||
| 01dc277877 | |||
| b809ae5c6f | |||
| 4c563122f8 | |||
| 4c1124fdb0 | |||
| bdbaecd975 | |||
| 2b8641c1dd | |||
| f1a90000b2 | |||
| 3bd0f0b447 | |||
| cb5b618266 | |||
| c71c07690a | |||
| d85d77da01 | |||
| 164d7bc70f | |||
| db51b09f80 | |||
| 5f8d03ba9b | |||
| 4cae7b27a6 | |||
| edcf939611 | |||
| 208d291b3c | |||
| 19f049189a | |||
| c855276555 | |||
| 9e244e0889 | |||
| bf7a067908 | |||
| 67c4b71b88 | |||
| 1fa8a86cce | |||
| 73eb98960f | |||
| 59704221b7 | |||
| 19c23943ec | |||
| d068559dee | |||
| 8e6b5b8bee | |||
| c99ddbe10f | |||
| 23925c4ba6 | |||
| e71f0afd99 | |||
| 014bf97248 | |||
| fc0d666366 | |||
| eb7465e6f9 | |||
| ff6ad20a77 | |||
| 17c528a206 | |||
| 63f4034a7f | |||
| 6c1bfc2177 | |||
| 45ff1f06ba | |||
| 3e287a593a | |||
| 01420154be | |||
| 1e4c596d0e | |||
| 26cb2bbbd1 | |||
| 588db79a64 | |||
| e5aaf3b31f | |||
| 9c222b128b | |||
| c66e08d43e | |||
| 493d8fc45f | |||
| 47993cb55d | |||
| 4701cbea23 | |||
| 70efd6f2bf | |||
| b85b6a713c | |||
| 6291529a10 | |||
| 160907ab52 | |||
| bccd30a80f | |||
| ef4d3e6c4d | |||
| 4fe7a1375a | |||
| 02bc195068 | |||
| 1e20913237 | |||
| 48b488fa59 | |||
| 893f3b2e34 | |||
| 25be12852f | |||
| 4f0292b000 | |||
| f669fd9205 | |||
| c3e0646b61 | |||
| 0d2cad8693 | |||
| 237916a37b | |||
| 16653f9585 | |||
| 392b1009c9 | |||
| 10c184e58a | |||
| f6b8756dc0 | |||
| f2654807a4 | |||
| ab90b75d73 | |||
| 0b58a081af | |||
| ae98a5fe58 | |||
| 74fa4e3503 | |||
| c20b0f67a8 | |||
| 098c7196de | |||
| 99a25560c1 | |||
| 79d19a2d8b | |||
| 061d0809bd | |||
| 0ce0c45cc7 | |||
| e910362b16 | |||
| 96c05bf113 | |||
| 6d2bda5c9d | |||
| 4a080fba3f | |||
| ac5b3b164f | |||
| c43d7dbb31 | |||
| d218fdfcf4 | |||
| e59af2fd1f | |||
| 3d761b5bf4 | |||
| 1892101359 | |||
| d76d25379e | |||
| c96cf4b11a | |||
| 31601f523d | |||
| f9abe20b84 | |||
| f7030ed800 | |||
| f4173b3766 | |||
| 221a564644 | |||
| 7458eff2d8 | |||
| 187245885a | |||
| e5d8c2edbc | |||
| be4aa39c8a | |||
| 014c697773 | |||
| ea733de80e | |||
| 3ac5dcd66e | |||
| 054de1cc6f | |||
| b525c0988a | |||
| 691efe0831 | |||
| 8317a30d6e | |||
| 633937b0bc | |||
| 3152d2803a | |||
| 883c90adc8 | |||
| 8e658be7d7 | |||
| c27a4e2bf5 | |||
| 257f544a89 | |||
| f8cb08ce52 | |||
| d970eea7cf | |||
| a45fad81c8 | |||
| 76d6cf129a | |||
| 879427446f | |||
| 9edd7b0c04 | |||
| d97b83fe93 | |||
| 027e6bbb05 | |||
| 4cdead8006 | |||
| aaff472317 | |||
| 9b2febcd6d | |||
| 5de355238d | |||
| 644c8ec491 | |||
| 4b516ae4c5 | |||
| 223251f868 | |||
| 9b7d6bace1 | |||
| c15b8b65e5 | |||
| 4e9eaa5e81 | |||
| 066e10246f | |||
| 3294dd6ca8 | |||
| 2315295985 | |||
| 56ddf21107 | |||
| 3f47df21b8 | |||
| 712407f524 | |||
| 3a49a0a21a | |||
| 04b3e13ba1 | |||
| 39bc2b49c0 | |||
| 9765282640 | |||
| 320a620afa | |||
| ec2a720617 | |||
| 83d2ca7a54 | |||
| 891503c793 | |||
| 43abc6c797 | |||
| f916e94a4c | |||
| fc9b2b6e1e | |||
| 66bdd70485 | |||
| 29024ea03a | |||
| 6af33905be | |||
| f97d918728 | |||
| 6a38e501d9 | |||
| 8310733c4c | |||
| 28f0946877 | |||
| e135a0dc71 | |||
| 05a65773f0 | |||
| 8d794560a0 | |||
| 4341a98413 | |||
| 24f5a36350 | |||
| f125db6973 | |||
| 4ec969657e | |||
| cb7e790086 | |||
| 9012dafed4 | |||
| 8b24d7eded | |||
| dcc537accb | |||
| ab67775f13 | |||
| e105828e07 | |||
| 3c83edaad2 | |||
| b4f278e5fa | |||
| 29ca687a26 | |||
| ed975cae63 | |||
| 4fce0944b4 | |||
| 4a52898f08 | |||
| 92b48319ed | |||
| 9e113d80f7 | |||
| f960554cf8 | |||
| 0bdea705a5 | |||
| 33361ea7f6 | |||
| 717240f53c | |||
| 5156248a96 | |||
| e074df469e | |||
| d1277ecb02 | |||
| 8bcc235490 | |||
| aaa7171c10 | |||
| 887311b440 | |||
| 1c90aac059 | |||
| 3ad9765dcf | |||
| cc934607c8 | |||
| 5074e68b9c | |||
| ade41f113d | |||
| 95dc82594f | |||
| 80e585fa91 | |||
| 9f110f9db8 | |||
| 71470b9e02 | |||
| 4fd24accac | |||
| 31312fecac | |||
| b80d057922 | |||
| f01d8bc835 | |||
| ddffe71a22 | |||
| 649a19ec57 | |||
| e82fd99a09 | |||
| 67a9b8e2a0 | |||
| 2000f947c3 | |||
| f992ada0a8 | |||
| f876cdb037 | |||
| f919d42370 | |||
| ab5ff00c39 | |||
| 422738af56 | |||
| 81751fc9ce | |||
| 9b6c5effc9 | |||
| 129841d5c2 | |||
| 24d2460697 | |||
| ef3d9626c1 | |||
| 5c7b3c6c3b | |||
| 6257888735 | |||
| f5e714f794 | |||
| 3091f63504 | |||
| 39755cccdc | |||
| 9caf706ca3 | |||
| 6ba6a7c8d9 | |||
| 0a4a0e4c4c | |||
| b48d1e521a | |||
| 211d090a2d | |||
| b6e5943e15 | |||
| 78f6a34339 | |||
| de967ae149 | |||
| 4d075ff190 | |||
| 076e2961c6 | |||
| 7149de1bc3 | |||
| 091f2f583a | |||
| 1c0ef2ca98 | |||
| 2a845bd7b5 | |||
| afe326006f | |||
| 4b80154b09 | |||
| d6b230b8f1 | |||
| d02a2cbd29 | |||
| 17d225b0d9 | |||
| 6cbbaccaf4 | |||
| 94cc4027c2 | |||
| 03ae6ed2b0 | |||
| fa8c232a69 | |||
| 0386ce998a | |||
| 273f73e9a2 | |||
| 5e20e54649 | |||
| b8c3f9dcce | |||
| 802b6508fa | |||
| b6409b05e7 | |||
| 129f355b9c | |||
| 9ffacb80e3 | |||
| 85726db45d | |||
| 746b1b051c | |||
| 59887eed80 | |||
| b8267f1fef | |||
| 8c62bb6d6d | |||
| 751e04b87f | |||
| 9f0161ed70 | |||
| 7b2c341386 | |||
| c8b29ecf1c | |||
| c30381c6ec | |||
| f489531543 | |||
| 4bbe795626 | |||
| 8aa3dca95f | |||
| 5e0f730159 | |||
| f1aed0d8b9 | |||
| a3465c31c9 | |||
| 053c48613b | |||
| 615adc567b | |||
| b0f645d906 | |||
| 023c78d0e8 | |||
| 824550175a | |||
| ad53c0de83 | |||
| c8039739d5 | |||
| 26674136e6 | |||
| 9972fa1053 | |||
| ae3f974d8c | |||
| 027f179a4b | |||
| e80cb1795e | |||
| 66fe599498 | |||
| c9e6e321b3 | |||
| fb3c996904 | |||
| 70b25825ec | |||
| 290e8f5a1e | |||
| f6b1440bf2 | |||
| 77a4919656 | |||
| 84d901b8a3 | |||
| d27ed2580f | |||
| 87ea971be0 | |||
| 91ea70b335 | |||
| 2e94e152c2 | |||
| eece46fb0f | |||
| 34736bc26e | |||
| 82cf385f9d | |||
| 682dea2fb1 | |||
| c10588d183 | |||
| 6db1637770 | |||
| 5742d2e3fe | |||
| c2d0308ac0 | |||
| 84c7da5a7d | |||
| 274350c118 | |||
| 6bd978eef1 | |||
| e0f40fad8c | |||
| 5647665782 | |||
| df99e7ee49 | |||
| dbd4437474 | |||
| 9c198d0c33 | |||
| d62a8a138c | |||
| f8a57ec98c | |||
| aa6339df06 | |||
| 3fbbfbd9cb | |||
| 31d6bf1967 | |||
| 226b3f2ff4 | |||
| ac8dab75fe | |||
| aad2bf4645 | |||
| 7f71296e1c | |||
| 9137170fb8 | |||
| 0af667c9aa | |||
| 8dc6a95ce6 | |||
| 1eb64d117e | |||
| 8f48a80bc4 | |||
| e76dd7fab0 | |||
| b53a9ce5ae | |||
| 952f26929c | |||
| 9ff048e683 | |||
| a64fe8121b | |||
| 4db7a32075 | |||
| 20ee5ea3e1 | |||
| d9200ef006 | |||
| dfde271f7f | |||
| 5346eac037 | |||
| 95e151be4b | |||
| 98af745e08 | |||
| 56433a624e | |||
| c59cb620dd | |||
| f60cb9bb64 | |||
| 949a2a95ad | |||
| 0bd700699b | |||
| 1d10925829 | |||
| 0e2866260f | |||
| 02ace23c38 | |||
| 3e16adf961 | |||
| fb1a3da0ea | |||
| 5f2e979bb5 | |||
| 5d4d15aa9c | |||
| fb71d0cd68 | |||
| a189a7eaec | |||
| 59a6bd700b | |||
| 278224676b | |||
| 66f2877a3f | |||
| a97deb0036 | |||
| ab976d8b07 | |||
| cb2cfa7e94 | |||
| 2c2f84bb29 | |||
| 7156b0dcce | |||
| f62671742c | |||
| 58be872bef | |||
| ce6b847c8b | |||
| 9c22e7fcb7 | |||
| 452f36939a | |||
| f0b821e2df | |||
| cda87a5c07 | |||
| 10844339b8 | |||
| 042785e188 | |||
| 07740ae83c | |||
| 9d08fe05c1 | |||
| 516114011f | |||
| bb08522a32 | |||
| 25949c3296 | |||
| 5720774bbf | |||
| 74c8b20a85 | |||
| 744b714c25 | |||
| 73d57239f7 | |||
| 325a706840 | |||
| c179b1812c | |||
| 1fa05703fa | |||
| b34f807d33 | |||
| fda27e6eba | |||
| 217503eab0 | |||
| 8d062cecfd | |||
| 614839c023 | |||
| 254980695b | |||
| 28cca49635 | |||
| c95d7fe30f | |||
| 2b890c2057 | |||
| 456db52653 | |||
| 0a5e9dce24 | |||
| 9b6600d31f | |||
| 5f19859589 | |||
| a189780e7f | |||
| 664fcfd787 | |||
| 64e3e03a02 | |||
| 3139aa5e51 | |||
| 2744a8bd96 | |||
| 0ab7d18ad3 | |||
| 853288f71b | |||
| 4a2a81df80 | |||
| 6827a0899c | |||
| dd1cbb07f7 | |||
| 7f20006622 | |||
| 8d25074a3e | |||
| 73a265f5ad | |||
| 02da349080 | |||
| e218234f91 | |||
| 47b4be7fcf | |||
| fea11eaa06 | |||
| 99ef619603 | |||
| 93a5e70bbe | |||
| a3c1c63332 | |||
| 4348862e46 | |||
| 5a6aaf8dcf | |||
| bc28f7a4e9 | |||
| 27f6ed4338 | |||
| 4ec0a6d148 | |||
| 9e7a3c9e41 | |||
| 8794b7f5de | |||
| 2f02aa07c7 | |||
| 62f9c2b187 | |||
| 900ecfe372 | |||
| 72a19fc349 | |||
| 9e24276b59 | |||
| 46dea6d598 | |||
| d80ad3f145 | |||
| a7a3e5a2db | |||
| a32c7186e4 | |||
| a25aff7fb0 | |||
| 7721d8b733 | |||
| 75db0d09e5 |
+31
-4
@@ -1,16 +1,43 @@
|
|||||||
[*.{kt,kts}]
|
root = true
|
||||||
max_line_length = 120
|
|
||||||
indent_size = 4
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.{xml,sq,sqm,aidl}]
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
# noinspection EditorConfigKeyCorrectness
|
||||||
|
[*.{kt,kts}]
|
||||||
|
indent_size = 4
|
||||||
|
max_line_length = 120
|
||||||
|
|
||||||
ij_kotlin_allow_trailing_comma = true
|
ij_kotlin_allow_trailing_comma = true
|
||||||
ij_kotlin_allow_trailing_comma_on_call_site = 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 = 2147483647
|
||||||
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
|
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
|
||||||
|
|
||||||
|
ktlint_code_style = intellij_idea
|
||||||
|
ktlint_function_naming_ignore_when_annotated_with = Composable
|
||||||
|
ktlint_standard_class-signature = disabled
|
||||||
|
ktlint_standard_comment-wrapping = disabled
|
||||||
|
ktlint_standard_discouraged-comment-location = disabled
|
||||||
|
ktlint_standard_function-expression-body = disabled
|
||||||
|
ktlint_standard_function-signature = disabled
|
||||||
|
ktlint_standard_type-argument-comment = disabled
|
||||||
|
ktlint_standard_type-parameter-comment = disabled
|
||||||
|
ktlint_standard_blank-line-between-when-conditions = disabled
|
||||||
|
|
||||||
|
# SY
|
||||||
ktlint_standard_filename = disabled
|
ktlint_standard_filename = disabled
|
||||||
ktlint_standard_argument-list-wrapping = disabled
|
ktlint_standard_argument-list-wrapping = disabled
|
||||||
ktlint_standard_function-naming = disabled
|
ktlint_standard_function-naming = disabled
|
||||||
ktlint_standard_property-naming = disabled
|
ktlint_standard_property-naming = disabled
|
||||||
ktlint_standard_multiline-expression-wrapping = disabled
|
ktlint_standard_multiline-expression-wrapping = disabled
|
||||||
ktlint_standard_string-template-indent = disabled
|
ktlint_standard_string-template-indent = disabled
|
||||||
ktlint_standard_comment-wrapping = disabled
|
ktlint_standard_max-line-length = disabled
|
||||||
|
ktlint_standard_value-argument-comment = disabled
|
||||||
|
ktlint_standard_value-parameter-comment = disabled
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
|
- name: ❌ Help with Extensions
|
||||||
|
url: https://mihon.app/docs/faq/browse/extensions
|
||||||
|
about: For extension-related questions/issues
|
||||||
- name: 🖥️ Mihon website
|
- name: 🖥️ Mihon website
|
||||||
url: https://mihon.app/
|
url: https://mihon.app/
|
||||||
about: Guides, troubleshooting, and answers to common questions
|
about: Guides, troubleshooting, and answers to common questions
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Crash logs
|
label: Crash logs
|
||||||
description: |
|
description: |
|
||||||
If you're experiencing crashes, share the crash logs from **More → Settings → Advanced** then press **Dump crash logs**.
|
If you're experiencing crashes, if possible, go to the app's **More → Settings → Advanced** page, press **Dump crash logs** and share the crash logs here.
|
||||||
placeholder: |
|
placeholder: |
|
||||||
You can paste the crash logs in plain text or upload it as an attachment.
|
You can upload the crash log file as an attachment, or paste the crash logs in plain text if needed.
|
||||||
|
|
||||||
- type: input
|
- type: input
|
||||||
id: tachiyomisy-version
|
id: tachiyomisy-version
|
||||||
@@ -53,7 +53,7 @@ body:
|
|||||||
label: TachiyomiSY version
|
label: TachiyomiSY version
|
||||||
description: You can find your TachiyomiSY version in **More → About**.
|
description: You can find your TachiyomiSY version in **More → About**.
|
||||||
placeholder: |
|
placeholder: |
|
||||||
Example: "1.11.0"
|
Example: "1.12.0"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
@@ -94,11 +94,11 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: I have written a short but informative title.
|
- label: I have written a short but informative title.
|
||||||
required: true
|
required: true
|
||||||
- label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https:/mihon.app/docs/guides/troubleshooting/).
|
- label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/).
|
||||||
required: true
|
required: true
|
||||||
- label: I have updated the app to version **[1.11.0](https://github.com/jobobby04/tachiyomisy/releases/latest)**.
|
- label: I have updated the app to version **[1.12.0](https://github.com/jobobby04/tachiyomisy/releases/latest)**.
|
||||||
required: true
|
required: true
|
||||||
- label: I have updated all installed extensions.
|
- label: I have filled out all of the requested information in this form, including specific version numbers.
|
||||||
required: true
|
required: true
|
||||||
- label: I will fill out all of the requested information in this form.
|
- label: I understand that **Mihon does not have or fix any extensions**, and I **will not receive help** for any issues related to sources or extensions.
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ body:
|
|||||||
required: true
|
required: true
|
||||||
- label: I have written a short but informative title.
|
- label: I have written a short but informative title.
|
||||||
required: true
|
required: true
|
||||||
- label: I have updated the app to version **[1.11.0](https://github.com/jobobby04/tachiyomisy/releases/latest)**.
|
- label: I have updated the app to version **[1.12.0](https://github.com/jobobby04/tachiyomisy/releases/latest)**.
|
||||||
required: true
|
required: true
|
||||||
- label: I will fill out all of the requested information in this form.
|
- label: I will fill out all of the requested information in this form.
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
@@ -6,40 +6,28 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check_wrapper:
|
|
||||||
name: Validate Gradle Wrapper
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Clone repo
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Validate Gradle Wrapper
|
|
||||||
uses: gradle/actions/wrapper-validation@v4
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build app
|
name: Build app
|
||||||
needs: check_wrapper
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repo
|
- name: Clone repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Set up JDK
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 17
|
||||||
distribution: adopt
|
distribution: temurin
|
||||||
|
|
||||||
- name: Set up gradle
|
- name: Set up gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v5
|
||||||
|
|
||||||
- name: Build app
|
- name: Build app
|
||||||
run: ./gradlew spotlessCheck assembleDevDebug
|
run: ./gradlew spotlessCheck assembleDevDebug
|
||||||
|
|
||||||
- name: Upload APK
|
- name: Upload APK
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: TachiyomiSY-${{ github.sha }}.apk
|
name: TachiyomiSY-${{ github.sha }}.apk
|
||||||
path: app/build/outputs/apk/dev/debug/app-dev-debug.apk
|
path: app/build/outputs/apk/dev/debug/app-dev-debug.apk
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
name: Release Builder
|
name: Release Builder
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- 'release'
|
- 'release'
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build release app
|
name: Build release app
|
||||||
@@ -15,25 +15,22 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repo
|
- name: Clone repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Validate Gradle Wrapper
|
|
||||||
uses: gradle/actions/wrapper-validation@v4
|
|
||||||
|
|
||||||
- name: Setup Android SDK
|
- name: Setup Android SDK
|
||||||
run: |
|
run: |
|
||||||
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"
|
${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3"
|
||||||
|
|
||||||
- name: Set up JDK
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 17
|
||||||
distribution: adopt
|
distribution: temurin
|
||||||
|
|
||||||
- name: Set up gradle
|
- name: Set up gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v5
|
||||||
|
|
||||||
# SY <--
|
# SY -->
|
||||||
- name: Write google-services.json
|
- name: Write google-services.json
|
||||||
uses: DamianReeves/write-file-action@v1.3
|
uses: DamianReeves/write-file-action@v1.3
|
||||||
with:
|
with:
|
||||||
@@ -47,10 +44,16 @@ jobs:
|
|||||||
path: app/src/main/assets/client_secrets.json
|
path: app/src/main/assets/client_secrets.json
|
||||||
contents: ${{ secrets.CLIENT_SECRETS_TEXT }}
|
contents: ${{ secrets.CLIENT_SECRETS_TEXT }}
|
||||||
write-mode: overwrite
|
write-mode: overwrite
|
||||||
# SY -->
|
# SY <--
|
||||||
|
|
||||||
- name: Build app and run unit tests
|
- name: Check code format
|
||||||
run: ./gradlew spotlessCheck assembleStandardRelease testStandardReleaseUnitTest --stacktrace
|
run: ./gradlew spotlessCheck
|
||||||
|
|
||||||
|
- name: Build app
|
||||||
|
run: ./gradlew assembleStandardRelease
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: ./gradlew testReleaseUnitTest testStandardReleaseUnitTest
|
||||||
|
|
||||||
- name: Sign APK
|
- name: Sign APK
|
||||||
uses: r0adkll/sign-android-release@v1
|
uses: r0adkll/sign-android-release@v1
|
||||||
@@ -60,30 +63,18 @@ jobs:
|
|||||||
alias: ${{ secrets.ALIAS }}
|
alias: ${{ secrets.ALIAS }}
|
||||||
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
|
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
|
||||||
keyPassword: ${{ secrets.KEY_PASSWORD }}
|
keyPassword: ${{ secrets.KEY_PASSWORD }}
|
||||||
|
env:
|
||||||
|
BUILD_TOOLS_VERSION: '35.0.1'
|
||||||
|
|
||||||
- name: Clean up build artifacts
|
- name: Clean up build artifacts
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
mv app/build/outputs/apk/standard/release/app-standard-universal-release-unsigned-signed.apk TachiyomiSY.apk
|
mv app/build/outputs/apk/standard/release/app-standard-universal-release-unsigned-signed.apk TachiyomiSY.apk
|
||||||
sha=`sha256sum TachiyomiSY.apk | awk '{ print $1 }'`
|
mv app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release-unsigned-signed.apk TachiyomiSY-arm64-v8a.apk
|
||||||
echo "APK_UNIVERSAL_SHA=$sha" >> $GITHUB_ENV
|
mv app/build/outputs/apk/standard/release/app-standard-armeabi-v7a-release-unsigned-signed.apk TachiyomiSY-armeabi-v7a.apk
|
||||||
|
mv app/build/outputs/apk/standard/release/app-standard-x86-release-unsigned-signed.apk TachiyomiSY-x86.apk
|
||||||
cp app/build/outputs/apk/standard/release/app-standard-arm64-v8a-release-unsigned-signed.apk TachiyomiSY-arm64-v8a.apk
|
mv app/build/outputs/apk/standard/release/app-standard-x86_64-release-unsigned-signed.apk TachiyomiSY-x86_64.apk
|
||||||
sha=`sha256sum TachiyomiSY-arm64-v8a.apk | awk '{ print $1 }'`
|
|
||||||
echo "APK_ARM64_V8A_SHA=$sha" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
cp app/build/outputs/apk/standard/release/app-standard-armeabi-v7a-release-unsigned-signed.apk TachiyomiSY-armeabi-v7a.apk
|
|
||||||
sha=`sha256sum TachiyomiSY-armeabi-v7a.apk | awk '{ print $1 }'`
|
|
||||||
echo "APK_ARMEABI_V7A_SHA=$sha" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
cp app/build/outputs/apk/standard/release/app-standard-x86-release-unsigned-signed.apk TachiyomiSY-x86.apk
|
|
||||||
sha=`sha256sum TachiyomiSY-x86.apk | awk '{ print $1 }'`
|
|
||||||
echo "APK_X86_SHA=$sha" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
cp app/build/outputs/apk/standard/release/app-standard-x86_64-release-unsigned-signed.apk TachiyomiSY-x86_64.apk
|
|
||||||
sha=`sha256sum TachiyomiSY-x86_64.apk | awk '{ print $1 }'`
|
|
||||||
echo "APK_X86_64_SHA=$sha" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
@@ -91,19 +82,10 @@ jobs:
|
|||||||
tag_name: ${{ github.run_number }}
|
tag_name: ${{ github.run_number }}
|
||||||
name: TachiyomiSY
|
name: TachiyomiSY
|
||||||
body: |
|
body: |
|
||||||
---
|
<!-->
|
||||||
|
> [!TIP]
|
||||||
### Checksums
|
>
|
||||||
|
> ### If you are unsure which version to download then go with `TachiyomiSY.apk`
|
||||||
| Variant | SHA-256 |
|
|
||||||
| ------- | ------- |
|
|
||||||
| Universal | ${{ env.APK_UNIVERSAL_SHA }} |
|
|
||||||
| arm64-v8a | ${{ env.APK_ARM64_V8A_SHA }} |
|
|
||||||
| armeabi-v7a | ${{ env.APK_ARMEABI_V7A_SHA }} |
|
|
||||||
| x86 | ${{ env.APK_X86_SHA }} |
|
|
||||||
| x86_64 | ${{ env.APK_X86_64_SHA }} |
|
|
||||||
|
|
||||||
## If you are unsure which version to choose then go with TachiyomiSY.apk
|
|
||||||
files: |
|
files: |
|
||||||
TachiyomiSY.apk
|
TachiyomiSY.apk
|
||||||
TachiyomiSY-arm64-v8a.apk
|
TachiyomiSY-arm64-v8a.apk
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
name: Remote Dispatch Action Initiator
|
name: Remote Dispatch Action Initiator
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- 'preview'
|
- 'preview'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
trigger_preview_build:
|
trigger_preview_build:
|
||||||
name: Trigger preview build
|
name: Trigger preview build
|
||||||
@@ -12,10 +12,16 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repo
|
- name: Clone repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Validate Gradle Wrapper
|
- name: Set up JDK
|
||||||
uses: gradle/actions/wrapper-validation@v4
|
uses: actions/setup-java@v5
|
||||||
|
with:
|
||||||
|
java-version: 17
|
||||||
|
distribution: temurin
|
||||||
|
|
||||||
|
- name: Set up gradle
|
||||||
|
uses: gradle/actions/setup-gradle@v5
|
||||||
|
|
||||||
- name: Create Tag
|
- name: Create Tag
|
||||||
run: |
|
run: |
|
||||||
@@ -28,3 +34,6 @@ jobs:
|
|||||||
-H 'Accept: application/vnd.github.everest-preview+json' \
|
-H 'Accept: application/vnd.github.everest-preview+json' \
|
||||||
-u ${{ secrets.ACCESS_TOKEN }} \
|
-u ${{ secrets.ACCESS_TOKEN }} \
|
||||||
--data '{"event_type": "ping", "client_payload": { "repository": "'"$GITHUB_REPOSITORY"'" }}'
|
--data '{"event_type": "ping", "client_payload": { "repository": "'"$GITHUB_REPOSITORY"'" }}'
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: ./gradlew testDebugUnitTest testDevDebugUnitTest
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Moderate issues
|
- name: Moderate issues
|
||||||
uses: tachiyomiorg/issue-moderator-action@v2.6.0
|
uses: tachiyomiorg/issue-moderator-action@v2.6.1
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
duplicate-label: Duplicate
|
duplicate-label: Duplicate
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
name: Lock threads
|
|
||||||
|
|
||||||
on:
|
|
||||||
# Daily
|
|
||||||
schedule:
|
|
||||||
- cron: '0 0 * * *'
|
|
||||||
# Manual trigger
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
lock:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: dessant/lock-threads@v5
|
|
||||||
with:
|
|
||||||
github-token: ${{ github.token }}
|
|
||||||
issue-inactive-days: '2'
|
|
||||||
pr-inactive-days: '2'
|
|
||||||
@@ -10,7 +10,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check PR and Add Label
|
- name: Check PR and Add Label
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const prAuthor = context.payload.pull_request.user.login;
|
const prAuthor = context.payload.pull_request.user.login;
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ Additional features for some extensions, features include custom description, op
|
|||||||
* Mangadex
|
* Mangadex
|
||||||
* NHentai
|
* NHentai
|
||||||
* Puruin
|
* Puruin
|
||||||
* Tsumino
|
* LANraragi
|
||||||
|
|
||||||
## Download
|
## Download
|
||||||
Get the app from our [releases page](https://github.com/jobobby04/tachiyomisy/releases/latest).
|
Get the app from our [releases page](https://github.com/jobobby04/tachiyomisy/releases/latest).
|
||||||
|
|||||||
+17
-20
@@ -31,12 +31,12 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "eu.kanade.tachiyomi.sy"
|
applicationId = "eu.kanade.tachiyomi.sy"
|
||||||
|
|
||||||
versionCode = 71
|
versionCode = 75
|
||||||
versionName = "1.11.0"
|
versionName = "1.12.0"
|
||||||
|
|
||||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||||
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
||||||
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime()}\"")
|
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLastCommitTime = false)}\"")
|
||||||
buildConfigField("boolean", "INCLUDE_UPDATER", "false")
|
buildConfigField("boolean", "INCLUDE_UPDATER", "false")
|
||||||
|
|
||||||
ndk {
|
ndk {
|
||||||
@@ -71,6 +71,8 @@ android {
|
|||||||
isMinifyEnabled = true
|
isMinifyEnabled = true
|
||||||
isShrinkResources = true
|
isShrinkResources = true
|
||||||
setProguardFiles(listOf(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"))
|
setProguardFiles(listOf(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"))
|
||||||
|
|
||||||
|
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime(useLastCommitTime = true)}\"")
|
||||||
}
|
}
|
||||||
create("benchmark") {
|
create("benchmark") {
|
||||||
initWith(getByName("release"))
|
initWith(getByName("release"))
|
||||||
@@ -99,8 +101,6 @@ android {
|
|||||||
dimension = "default"
|
dimension = "default"
|
||||||
}
|
}
|
||||||
create("dev") {
|
create("dev") {
|
||||||
// Include pseudolocales: https://developer.android.com/guide/topics/resources/pseudolocales
|
|
||||||
resourceConfigurations.addAll(listOf("en", "en_XA", "ar_XB", "xxhdpi"))
|
|
||||||
dimension = "default"
|
dimension = "default"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,9 +129,9 @@ android {
|
|||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding = true
|
viewBinding = true
|
||||||
buildConfig = true
|
buildConfig = true
|
||||||
|
aidl = true
|
||||||
|
|
||||||
// Disable some unused things
|
// Disable some unused things
|
||||||
aidl = false
|
|
||||||
renderScript = false
|
renderScript = false
|
||||||
shaders = false
|
shaders = false
|
||||||
}
|
}
|
||||||
@@ -239,7 +239,7 @@ dependencies {
|
|||||||
implementation(libs.preferencektx)
|
implementation(libs.preferencektx)
|
||||||
|
|
||||||
// Dependency injection
|
// Dependency injection
|
||||||
implementation(libs.injekt.core)
|
implementation(libs.injekt)
|
||||||
|
|
||||||
// Image loading
|
// Image loading
|
||||||
implementation(platform(libs.coil.bom))
|
implementation(platform(libs.coil.bom))
|
||||||
@@ -256,14 +256,15 @@ dependencies {
|
|||||||
implementation(libs.directionalviewpager) {
|
implementation(libs.directionalviewpager) {
|
||||||
exclude(group = "androidx.viewpager", module = "viewpager")
|
exclude(group = "androidx.viewpager", module = "viewpager")
|
||||||
}
|
}
|
||||||
implementation(libs.insetter)
|
implementation(libs.richeditor.compose)
|
||||||
implementation(libs.bundles.richtext)
|
|
||||||
implementation(libs.aboutLibraries.compose)
|
implementation(libs.aboutLibraries.compose)
|
||||||
implementation(libs.bundles.voyager)
|
implementation(libs.bundles.voyager)
|
||||||
implementation(libs.compose.materialmotion)
|
implementation(libs.compose.materialmotion)
|
||||||
implementation(libs.swipe)
|
implementation(libs.swipe)
|
||||||
implementation(libs.compose.webview)
|
implementation(libs.compose.webview)
|
||||||
implementation(libs.compose.grid)
|
implementation(libs.compose.grid)
|
||||||
|
implementation(libs.reorderable)
|
||||||
|
implementation(libs.bundles.markdown)
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
implementation(libs.logcat)
|
implementation(libs.logcat)
|
||||||
@@ -276,8 +277,12 @@ dependencies {
|
|||||||
// Shizuku
|
// Shizuku
|
||||||
implementation(libs.bundles.shizuku)
|
implementation(libs.bundles.shizuku)
|
||||||
|
|
||||||
|
// String similarity
|
||||||
|
implementation(libs.stringSimilarity)
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
testImplementation(libs.bundles.test)
|
testImplementation(libs.bundles.test)
|
||||||
|
testRuntimeOnly(libs.junit.platform.launcher)
|
||||||
|
|
||||||
// For detecting memory leaks; see https://square.github.io/leakcanary/
|
// For detecting memory leaks; see https://square.github.io/leakcanary/
|
||||||
// debugImplementation(libs.leakcanary.android)
|
// debugImplementation(libs.leakcanary.android)
|
||||||
@@ -286,9 +291,6 @@ dependencies {
|
|||||||
testImplementation(kotlinx.coroutines.test)
|
testImplementation(kotlinx.coroutines.test)
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
// Text distance (EH)
|
|
||||||
implementation(sylibs.simularity)
|
|
||||||
|
|
||||||
// Firebase (EH)
|
// Firebase (EH)
|
||||||
implementation(platform(libs.firebase.bom))
|
implementation(platform(libs.firebase.bom))
|
||||||
implementation(libs.firebase.analytics)
|
implementation(libs.firebase.analytics)
|
||||||
@@ -308,17 +310,12 @@ dependencies {
|
|||||||
// Koin
|
// Koin
|
||||||
implementation(sylibs.koin.core)
|
implementation(sylibs.koin.core)
|
||||||
implementation(sylibs.koin.android)
|
implementation(sylibs.koin.android)
|
||||||
|
|
||||||
|
// ZXing Android Embedded
|
||||||
|
implementation(sylibs.zxing.android.embedded)
|
||||||
}
|
}
|
||||||
|
|
||||||
androidComponents {
|
androidComponents {
|
||||||
beforeVariants { variantBuilder ->
|
|
||||||
// Disables standardBenchmark
|
|
||||||
if (variantBuilder.buildType == "benchmark") {
|
|
||||||
variantBuilder.enable = variantBuilder.productFlavors.containsAll(
|
|
||||||
listOf("default" to "dev"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onVariants(selector().withFlavor("default" to "standard")) {
|
onVariants(selector().withFlavor("default" to "standard")) {
|
||||||
// Only excluding in standard flavor because this breaks
|
// Only excluding in standard flavor because this breaks
|
||||||
// Layout Inspector's Compose tree
|
// Layout Inspector's Compose tree
|
||||||
|
|||||||
@@ -359,7 +359,7 @@
|
|||||||
<data android:scheme="https" />
|
<data android:scheme="https" />
|
||||||
<data android:scheme="http" />
|
<data android:scheme="http" />
|
||||||
|
|
||||||
<data android:host="pururin.io" />
|
<data android:host="pururin.me" />
|
||||||
|
|
||||||
<data android:pathPattern="/gallery/..*" />
|
<data android:pathPattern="/gallery/..*" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -413,6 +413,10 @@
|
|||||||
android:scheme="tachiyomisy" />
|
android:scheme="tachiyomisy" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name="com.journeyapps.barcodescanner.CaptureActivity"
|
||||||
|
tools:remove="screenOrientation" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<uses-sdk tools:overrideLibrary="rikka.shizuku.api"
|
<uses-sdk tools:overrideLibrary="rikka.shizuku.api"
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package mihon.app.shizuku;
|
||||||
|
|
||||||
|
interface IShellInterface {
|
||||||
|
void install(in AssetFileDescriptor apk) = 1;
|
||||||
|
|
||||||
|
void destroy() = 16777114;
|
||||||
|
}
|
||||||
@@ -13,9 +13,11 @@ import eu.kanade.domain.manga.interactor.SetExcludedScanlators
|
|||||||
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
|
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.source.interactor.GetEnabledSources
|
import eu.kanade.domain.source.interactor.GetEnabledSources
|
||||||
|
import eu.kanade.domain.source.interactor.GetIncognitoState
|
||||||
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
|
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
|
||||||
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
|
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
|
||||||
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
||||||
|
import eu.kanade.domain.source.interactor.ToggleIncognito
|
||||||
import eu.kanade.domain.source.interactor.ToggleLanguage
|
import eu.kanade.domain.source.interactor.ToggleLanguage
|
||||||
import eu.kanade.domain.source.interactor.ToggleSource
|
import eu.kanade.domain.source.interactor.ToggleSource
|
||||||
import eu.kanade.domain.source.interactor.ToggleSourcePin
|
import eu.kanade.domain.source.interactor.ToggleSourcePin
|
||||||
@@ -36,6 +38,7 @@ import mihon.domain.extensionrepo.interactor.ReplaceExtensionRepo
|
|||||||
import mihon.domain.extensionrepo.interactor.UpdateExtensionRepo
|
import mihon.domain.extensionrepo.interactor.UpdateExtensionRepo
|
||||||
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
import mihon.domain.extensionrepo.repository.ExtensionRepoRepository
|
||||||
import mihon.domain.extensionrepo.service.ExtensionRepoService
|
import mihon.domain.extensionrepo.service.ExtensionRepoService
|
||||||
|
import mihon.domain.migration.usecases.MigrateMangaUseCase
|
||||||
import mihon.domain.upcoming.interactor.GetUpcomingManga
|
import mihon.domain.upcoming.interactor.GetUpcomingManga
|
||||||
import tachiyomi.data.category.CategoryRepositoryImpl
|
import tachiyomi.data.category.CategoryRepositoryImpl
|
||||||
import tachiyomi.data.chapter.ChapterRepositoryImpl
|
import tachiyomi.data.chapter.ChapterRepositoryImpl
|
||||||
@@ -80,6 +83,7 @@ import tachiyomi.domain.manga.interactor.GetMangaWithChapters
|
|||||||
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
|
import tachiyomi.domain.manga.interactor.NetworkToLocalManga
|
||||||
import tachiyomi.domain.manga.interactor.ResetViewerFlags
|
import tachiyomi.domain.manga.interactor.ResetViewerFlags
|
||||||
import tachiyomi.domain.manga.interactor.SetMangaChapterFlags
|
import tachiyomi.domain.manga.interactor.SetMangaChapterFlags
|
||||||
|
import tachiyomi.domain.manga.interactor.UpdateMangaNotes
|
||||||
import tachiyomi.domain.manga.repository.MangaRepository
|
import tachiyomi.domain.manga.repository.MangaRepository
|
||||||
import tachiyomi.domain.release.interactor.GetApplicationRelease
|
import tachiyomi.domain.release.interactor.GetApplicationRelease
|
||||||
import tachiyomi.domain.release.service.ReleaseService
|
import tachiyomi.domain.release.service.ReleaseService
|
||||||
@@ -108,7 +112,7 @@ class DomainModule : InjektModule {
|
|||||||
addFactory { RenameCategory(get()) }
|
addFactory { RenameCategory(get()) }
|
||||||
addFactory { ReorderCategory(get()) }
|
addFactory { ReorderCategory(get()) }
|
||||||
addFactory { UpdateCategory(get()) }
|
addFactory { UpdateCategory(get()) }
|
||||||
addFactory { DeleteCategory(get()) }
|
addFactory { DeleteCategory(get(), get(), get()) }
|
||||||
|
|
||||||
addSingletonFactory<MangaRepository> { MangaRepositoryImpl(get()) }
|
addSingletonFactory<MangaRepository> { MangaRepositoryImpl(get()) }
|
||||||
addFactory { GetDuplicateLibraryManga(get()) }
|
addFactory { GetDuplicateLibraryManga(get()) }
|
||||||
@@ -126,9 +130,15 @@ class DomainModule : InjektModule {
|
|||||||
addFactory { SetMangaViewerFlags(get()) }
|
addFactory { SetMangaViewerFlags(get()) }
|
||||||
addFactory { NetworkToLocalManga(get()) }
|
addFactory { NetworkToLocalManga(get()) }
|
||||||
addFactory { UpdateManga(get(), get()) }
|
addFactory { UpdateManga(get(), get()) }
|
||||||
|
addFactory { UpdateMangaNotes(get()) }
|
||||||
addFactory { SetMangaCategories(get()) }
|
addFactory { SetMangaCategories(get()) }
|
||||||
addFactory { GetExcludedScanlators(get()) }
|
addFactory { GetExcludedScanlators(get()) }
|
||||||
addFactory { SetExcludedScanlators(get()) }
|
addFactory { SetExcludedScanlators(get()) }
|
||||||
|
addFactory {
|
||||||
|
MigrateMangaUseCase(
|
||||||
|
get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
addSingletonFactory<ReleaseService> { ReleaseServiceImpl(get(), get()) }
|
addSingletonFactory<ReleaseService> { ReleaseServiceImpl(get(), get()) }
|
||||||
addFactory { GetApplicationRelease(get(), get()) }
|
addFactory { GetApplicationRelease(get(), get()) }
|
||||||
@@ -150,7 +160,7 @@ class DomainModule : InjektModule {
|
|||||||
addFactory { UpdateChapter(get()) }
|
addFactory { UpdateChapter(get()) }
|
||||||
addFactory { SetReadStatus(get(), get(), get(), get(), get()) }
|
addFactory { SetReadStatus(get(), get(), get(), get(), get()) }
|
||||||
addFactory { ShouldUpdateDbChapter() }
|
addFactory { ShouldUpdateDbChapter() }
|
||||||
addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get(), get()) }
|
addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get(), get(), get()) }
|
||||||
addFactory { GetAvailableScanlators(get()) }
|
addFactory { GetAvailableScanlators(get()) }
|
||||||
addFactory { FilterChaptersForDownload(get(), get(), get(), get()) }
|
addFactory { FilterChaptersForDownload(get(), get(), get(), get()) }
|
||||||
|
|
||||||
@@ -190,5 +200,7 @@ class DomainModule : InjektModule {
|
|||||||
addFactory { DeleteExtensionRepo(get()) }
|
addFactory { DeleteExtensionRepo(get()) }
|
||||||
addFactory { ReplaceExtensionRepo(get()) }
|
addFactory { ReplaceExtensionRepo(get()) }
|
||||||
addFactory { UpdateExtensionRepo(get(), get()) }
|
addFactory { UpdateExtensionRepo(get(), get()) }
|
||||||
|
addFactory { ToggleIncognito(get()) }
|
||||||
|
addFactory { GetIncognitoState(get(), get(), get()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import tachiyomi.data.source.SavedSearchRepositoryImpl
|
|||||||
import tachiyomi.domain.chapter.interactor.DeleteChapters
|
import tachiyomi.domain.chapter.interactor.DeleteChapters
|
||||||
import tachiyomi.domain.chapter.interactor.GetChapterByUrl
|
import tachiyomi.domain.chapter.interactor.GetChapterByUrl
|
||||||
import tachiyomi.domain.chapter.interactor.GetMergedChaptersByMangaId
|
import tachiyomi.domain.chapter.interactor.GetMergedChaptersByMangaId
|
||||||
import tachiyomi.domain.history.interactor.GetHistoryByMangaId
|
|
||||||
import tachiyomi.domain.manga.interactor.DeleteByMergeId
|
import tachiyomi.domain.manga.interactor.DeleteByMergeId
|
||||||
import tachiyomi.domain.manga.interactor.DeleteFavoriteEntries
|
import tachiyomi.domain.manga.interactor.DeleteFavoriteEntries
|
||||||
import tachiyomi.domain.manga.interactor.DeleteMangaById
|
import tachiyomi.domain.manga.interactor.DeleteMangaById
|
||||||
@@ -88,7 +87,6 @@ class SYDomainModule : InjektModule {
|
|||||||
addFactory { DeleteChapters(get()) }
|
addFactory { DeleteChapters(get()) }
|
||||||
addFactory { DeleteMangaById(get()) }
|
addFactory { DeleteMangaById(get()) }
|
||||||
addFactory { FilterSerializer() }
|
addFactory { FilterSerializer() }
|
||||||
addFactory { GetHistoryByMangaId(get()) }
|
|
||||||
addFactory { GetChapterByUrl(get()) }
|
addFactory { GetChapterByUrl(get()) }
|
||||||
addFactory { GetSourceCategories(get()) }
|
addFactory { GetSourceCategories(get()) }
|
||||||
addFactory { CreateSourceCategory(get()) }
|
addFactory { CreateSourceCategory(get()) }
|
||||||
|
|||||||
@@ -33,4 +33,6 @@ class BasePreferences(
|
|||||||
fun displayProfile() = preferenceStore.getString("pref_display_profile_key", "")
|
fun displayProfile() = preferenceStore.getString("pref_display_profile_key", "")
|
||||||
|
|
||||||
fun hardwareBitmapThreshold() = preferenceStore.getInt("pref_hardware_bitmap_threshold", GLUtil.SAFE_TEXTURE_LIMIT)
|
fun hardwareBitmapThreshold() = preferenceStore.getInt("pref_hardware_bitmap_threshold", GLUtil.SAFE_TEXTURE_LIMIT)
|
||||||
|
|
||||||
|
fun alwaysDecodeLongStripWithSSIV() = preferenceStore.getBoolean("pref_always_decode_long_strip_with_ssiv", false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import tachiyomi.domain.chapter.model.NoChaptersException
|
|||||||
import tachiyomi.domain.chapter.model.toChapterUpdate
|
import tachiyomi.domain.chapter.model.toChapterUpdate
|
||||||
import tachiyomi.domain.chapter.repository.ChapterRepository
|
import tachiyomi.domain.chapter.repository.ChapterRepository
|
||||||
import tachiyomi.domain.chapter.service.ChapterRecognition
|
import tachiyomi.domain.chapter.service.ChapterRecognition
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.source.local.isLocal
|
import tachiyomi.source.local.isLocal
|
||||||
import java.lang.Long.max
|
import java.lang.Long.max
|
||||||
@@ -35,6 +36,7 @@ class SyncChaptersWithSource(
|
|||||||
private val updateChapter: UpdateChapter,
|
private val updateChapter: UpdateChapter,
|
||||||
private val getChaptersByMangaId: GetChaptersByMangaId,
|
private val getChaptersByMangaId: GetChaptersByMangaId,
|
||||||
private val getExcludedScanlators: GetExcludedScanlators,
|
private val getExcludedScanlators: GetExcludedScanlators,
|
||||||
|
private val libraryPreferences: LibraryPreferences,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,6 +119,7 @@ class SyncChaptersWithSource(
|
|||||||
downloadManager.isChapterDownloaded(
|
downloadManager.isChapterDownloaded(
|
||||||
dbChapter.name,
|
dbChapter.name,
|
||||||
dbChapter.scanlator,
|
dbChapter.scanlator,
|
||||||
|
dbChapter.url,
|
||||||
/* SY --> */ manga.ogTitle /* SY <-- */,
|
/* SY --> */ manga.ogTitle /* SY <-- */,
|
||||||
manga.source,
|
manga.source,
|
||||||
)
|
)
|
||||||
@@ -124,12 +127,14 @@ class SyncChaptersWithSource(
|
|||||||
if (shouldRenameChapter) {
|
if (shouldRenameChapter) {
|
||||||
downloadManager.renameChapter(source, manga, dbChapter, chapter)
|
downloadManager.renameChapter(source, manga, dbChapter, chapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
var toChangeChapter = dbChapter.copy(
|
var toChangeChapter = dbChapter.copy(
|
||||||
name = chapter.name,
|
name = chapter.name,
|
||||||
chapterNumber = chapter.chapterNumber,
|
chapterNumber = chapter.chapterNumber,
|
||||||
scanlator = chapter.scanlator,
|
scanlator = chapter.scanlator,
|
||||||
sourceOrder = chapter.sourceOrder,
|
sourceOrder = chapter.sourceOrder,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (chapter.dateUpload != 0L) {
|
if (chapter.dateUpload != 0L) {
|
||||||
toChangeChapter = toChangeChapter.copy(dateUpload = chapter.dateUpload)
|
toChangeChapter = toChangeChapter.copy(dateUpload = chapter.dateUpload)
|
||||||
}
|
}
|
||||||
@@ -150,12 +155,18 @@ class SyncChaptersWithSource(
|
|||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
val reAdded = mutableListOf<Chapter>()
|
val changedOrDuplicateReadUrls = mutableSetOf<String>()
|
||||||
|
|
||||||
val deletedChapterNumbers = TreeSet<Double>()
|
val deletedChapterNumbers = TreeSet<Double>()
|
||||||
val deletedReadChapterNumbers = TreeSet<Double>()
|
val deletedReadChapterNumbers = TreeSet<Double>()
|
||||||
val deletedBookmarkedChapterNumbers = TreeSet<Double>()
|
val deletedBookmarkedChapterNumbers = TreeSet<Double>()
|
||||||
|
|
||||||
|
val readChapterNumbers = dbChapters
|
||||||
|
.asSequence()
|
||||||
|
.filter { it.read && it.isRecognizedNumber }
|
||||||
|
.map { it.chapterNumber }
|
||||||
|
.toSet()
|
||||||
|
|
||||||
removedChapters.forEach { chapter ->
|
removedChapters.forEach { chapter ->
|
||||||
if (chapter.read) deletedReadChapterNumbers.add(chapter.chapterNumber)
|
if (chapter.read) deletedReadChapterNumbers.add(chapter.chapterNumber)
|
||||||
if (chapter.bookmark) deletedBookmarkedChapterNumbers.add(chapter.chapterNumber)
|
if (chapter.bookmark) deletedBookmarkedChapterNumbers.add(chapter.chapterNumber)
|
||||||
@@ -165,12 +176,20 @@ class SyncChaptersWithSource(
|
|||||||
val deletedChapterNumberDateFetchMap = removedChapters.sortedByDescending { it.dateFetch }
|
val deletedChapterNumberDateFetchMap = removedChapters.sortedByDescending { it.dateFetch }
|
||||||
.associate { it.chapterNumber to it.dateFetch }
|
.associate { it.chapterNumber to it.dateFetch }
|
||||||
|
|
||||||
|
val markDuplicateAsRead = libraryPreferences.markDuplicateReadChapterAsRead().get()
|
||||||
|
.contains(LibraryPreferences.MARK_DUPLICATE_CHAPTER_READ_NEW)
|
||||||
|
|
||||||
// Date fetch is set in such a way that the upper ones will have bigger value than the lower ones
|
// Date fetch is set in such a way that the upper ones will have bigger value than the lower ones
|
||||||
// Sources MUST return the chapters from most to less recent, which is common.
|
// Sources MUST return the chapters from most to less recent, which is common.
|
||||||
var itemCount = newChapters.size
|
var itemCount = newChapters.size
|
||||||
var updatedToAdd = newChapters.map { toAddItem ->
|
var updatedToAdd = newChapters.map { toAddItem ->
|
||||||
var chapter = toAddItem.copy(dateFetch = nowMillis + itemCount--)
|
var chapter = toAddItem.copy(dateFetch = nowMillis + itemCount--)
|
||||||
|
|
||||||
|
if (chapter.chapterNumber in readChapterNumbers && markDuplicateAsRead) {
|
||||||
|
changedOrDuplicateReadUrls.add(chapter.url)
|
||||||
|
chapter = chapter.copy(read = true)
|
||||||
|
}
|
||||||
|
|
||||||
if (!chapter.isRecognizedNumber || chapter.chapterNumber !in deletedChapterNumbers) return@map chapter
|
if (!chapter.isRecognizedNumber || chapter.chapterNumber !in deletedChapterNumbers) return@map chapter
|
||||||
|
|
||||||
chapter = chapter.copy(
|
chapter = chapter.copy(
|
||||||
@@ -183,19 +202,19 @@ class SyncChaptersWithSource(
|
|||||||
chapter = chapter.copy(dateFetch = it)
|
chapter = chapter.copy(dateFetch = it)
|
||||||
}
|
}
|
||||||
|
|
||||||
reAdded.add(chapter)
|
changedOrDuplicateReadUrls.add(chapter.url)
|
||||||
|
|
||||||
chapter
|
chapter
|
||||||
}
|
}
|
||||||
|
|
||||||
// --> EXH (carry over reading progress)
|
// --> EXH (carry over reading progress)
|
||||||
if (manga.isEhBasedManga()) {
|
if (manga.isEhBasedManga()) {
|
||||||
val finalAdded = updatedToAdd.subtract(reAdded)
|
val finalAdded = updatedToAdd.filterNot { it.url in changedOrDuplicateReadUrls }
|
||||||
if (finalAdded.isNotEmpty()) {
|
if (finalAdded.isNotEmpty()) {
|
||||||
val max = dbChapters.maxOfOrNull { it.lastPageRead }
|
val max = dbChapters.maxOfOrNull { it.lastPageRead }
|
||||||
if (max != null && max > 0) {
|
if (max != null && max > 0) {
|
||||||
updatedToAdd = updatedToAdd.map {
|
updatedToAdd = updatedToAdd.map {
|
||||||
if (it !in reAdded) {
|
if (it.url !in changedOrDuplicateReadUrls) {
|
||||||
it.copy(lastPageRead = max)
|
it.copy(lastPageRead = max)
|
||||||
} else {
|
} else {
|
||||||
it
|
it
|
||||||
@@ -225,12 +244,8 @@ class SyncChaptersWithSource(
|
|||||||
// Note that last_update actually represents last time the chapter list changed at all
|
// Note that last_update actually represents last time the chapter list changed at all
|
||||||
updateManga.awaitUpdateLastUpdate(manga.id)
|
updateManga.awaitUpdateLastUpdate(manga.id)
|
||||||
|
|
||||||
val reAddedUrls = reAdded.map { it.url }.toHashSet()
|
|
||||||
|
|
||||||
val excludedScanlators = getExcludedScanlators.await(manga.id).toHashSet()
|
val excludedScanlators = getExcludedScanlators.await(manga.id).toHashSet()
|
||||||
|
|
||||||
return updatedToAdd.filterNot {
|
return updatedToAdd.filterNot { it.url in changedOrDuplicateReadUrls || it.scanlator in excludedScanlators }
|
||||||
it.url in reAddedUrls || it.scanlator in excludedScanlators
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ fun List<Chapter>.applyFilters(
|
|||||||
val downloaded = downloadManager.isChapterDownloaded(
|
val downloaded = downloadManager.isChapterDownloaded(
|
||||||
chapter.name,
|
chapter.name,
|
||||||
chapter.scanlator,
|
chapter.scanlator,
|
||||||
|
chapter.url,
|
||||||
/* SY --> */ manga.ogTitle /* SY <-- */,
|
/* SY --> */ manga.ogTitle /* SY <-- */,
|
||||||
manga.source,
|
manga.source,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class GetPagePreviews(
|
|||||||
return try {
|
return try {
|
||||||
val pagePreviews = try {
|
val pagePreviews = try {
|
||||||
pagePreviewCache.getPageListFromCache(manga, chapterIds, page)
|
pagePreviewCache.getPageListFromCache(manga, chapterIds, page)
|
||||||
} catch (e: Exception) {
|
} catch (_: Exception) {
|
||||||
source.getPagePreviewList(manga.toSManga(), chapters.map { it.toSChapter() }, page).also {
|
source.getPagePreviewList(manga.toSManga(), chapters.map { it.toSChapter() }, page).also {
|
||||||
pagePreviewCache.putPageListToCache(manga, chapterIds, it)
|
pagePreviewCache.putPageListToCache(manga, chapterIds, it)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import eu.kanade.domain.manga.model.hasCustomCover
|
|||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
import tachiyomi.domain.manga.interactor.FetchInterval
|
import tachiyomi.domain.manga.interactor.FetchInterval
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.manga.model.MangaUpdate
|
import tachiyomi.domain.manga.model.MangaUpdate
|
||||||
@@ -32,9 +33,8 @@ class UpdateManga(
|
|||||||
remoteManga: SManga,
|
remoteManga: SManga,
|
||||||
manualFetch: Boolean,
|
manualFetch: Boolean,
|
||||||
coverCache: CoverCache = Injekt.get(),
|
coverCache: CoverCache = Injekt.get(),
|
||||||
// SY -->
|
libraryPreferences: LibraryPreferences = Injekt.get(),
|
||||||
downloadManager: DownloadManager = Injekt.get(),
|
downloadManager: DownloadManager = Injekt.get(),
|
||||||
// SY <--
|
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val remoteTitle = try {
|
val remoteTitle = try {
|
||||||
remoteManga.title
|
remoteManga.title
|
||||||
@@ -42,14 +42,13 @@ class UpdateManga(
|
|||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
// if the manga isn't a favorite (or 'update titles' preference is enabled), set its title from source and update in db
|
||||||
val title = if (remoteTitle.isNotBlank() && localManga.ogTitle != remoteTitle) {
|
val title =
|
||||||
downloadManager.renameMangaDir(localManga.ogTitle, remoteTitle, localManga.source)
|
if (remoteTitle.isNotEmpty() && (!localManga.favorite || libraryPreferences.updateMangaTitles().get())) {
|
||||||
remoteTitle
|
remoteTitle
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
// SY <--
|
|
||||||
|
|
||||||
val coverLastModified =
|
val coverLastModified =
|
||||||
when {
|
when {
|
||||||
@@ -69,7 +68,7 @@ class UpdateManga(
|
|||||||
|
|
||||||
val thumbnailUrl = remoteManga.thumbnail_url?.takeIf { it.isNotEmpty() }
|
val thumbnailUrl = remoteManga.thumbnail_url?.takeIf { it.isNotEmpty() }
|
||||||
|
|
||||||
return mangaRepository.update(
|
val success = mangaRepository.update(
|
||||||
MangaUpdate(
|
MangaUpdate(
|
||||||
id = localManga.id,
|
id = localManga.id,
|
||||||
title = title,
|
title = title,
|
||||||
@@ -84,6 +83,10 @@ class UpdateManga(
|
|||||||
initialized = true,
|
initialized = true,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
if (success && title != null) {
|
||||||
|
downloadManager.renameManga(localManga, title)
|
||||||
|
}
|
||||||
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun awaitUpdateFetchInterval(
|
suspend fun awaitUpdateFetchInterval(
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ val Manga.readerOrientation: Long
|
|||||||
|
|
||||||
val Manga.downloadedFilter: TriState
|
val Manga.downloadedFilter: TriState
|
||||||
get() {
|
get() {
|
||||||
if (forceDownloaded()) return TriState.ENABLED_IS
|
if (Injekt.get<BasePreferences>().downloadedOnly().get()) return TriState.ENABLED_IS
|
||||||
return when (downloadedFilterRaw) {
|
return when (downloadedFilterRaw) {
|
||||||
Manga.CHAPTER_SHOW_DOWNLOADED -> TriState.ENABLED_IS
|
Manga.CHAPTER_SHOW_DOWNLOADED -> TriState.ENABLED_IS
|
||||||
Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriState.ENABLED_NOT
|
Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriState.ENABLED_NOT
|
||||||
@@ -35,18 +35,17 @@ fun Manga.chaptersFiltered(): Boolean {
|
|||||||
downloadedFilter != TriState.DISABLED ||
|
downloadedFilter != TriState.DISABLED ||
|
||||||
bookmarkedFilter != TriState.DISABLED
|
bookmarkedFilter != TriState.DISABLED
|
||||||
}
|
}
|
||||||
fun Manga.forceDownloaded(): Boolean {
|
|
||||||
return favorite && Injekt.get<BasePreferences>().downloadedOnly().get()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Manga.toSManga(): SManga = SManga.create().also {
|
fun Manga.toSManga(): SManga = SManga.create().also {
|
||||||
it.url = url
|
it.url = url
|
||||||
it.title = title
|
// SY -->
|
||||||
it.artist = artist
|
it.title = ogTitle
|
||||||
it.author = author
|
it.artist = ogArtist
|
||||||
it.description = description
|
it.author = ogAuthor
|
||||||
it.genre = genre.orEmpty().joinToString()
|
it.description = ogDescription
|
||||||
it.status = status.toInt()
|
it.genre = ogGenre.orEmpty().joinToString()
|
||||||
|
it.status = ogStatus.toInt()
|
||||||
|
// SY <--
|
||||||
it.thumbnail_url = thumbnailUrl
|
it.thumbnail_url = thumbnailUrl
|
||||||
it.initialized = initialized
|
it.initialized = initialized
|
||||||
}
|
}
|
||||||
@@ -79,24 +78,6 @@ fun Manga.copyFrom(other: SManga): Manga {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun SManga.toDomainManga(sourceId: Long): Manga {
|
|
||||||
return Manga.create().copy(
|
|
||||||
url = url,
|
|
||||||
// SY -->
|
|
||||||
ogTitle = title,
|
|
||||||
ogArtist = artist,
|
|
||||||
ogAuthor = author,
|
|
||||||
ogThumbnailUrl = thumbnail_url,
|
|
||||||
ogDescription = description,
|
|
||||||
ogGenre = getGenres(),
|
|
||||||
ogStatus = status.toLong(),
|
|
||||||
// SY <--
|
|
||||||
updateStrategy = update_strategy,
|
|
||||||
initialized = initialized,
|
|
||||||
source = sourceId,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
|
fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean {
|
||||||
return coverCache.getCustomCoverFile(id).exists()
|
return coverCache.getCustomCoverFile(id).exists()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonArray
|
import kotlinx.serialization.json.JsonArray
|
||||||
import tachiyomi.core.common.util.lang.withIOContext
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.base.BasePreferences
|
||||||
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
|
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
|
||||||
|
class GetIncognitoState(
|
||||||
|
private val basePreferences: BasePreferences,
|
||||||
|
private val sourcePreferences: SourcePreferences,
|
||||||
|
private val extensionManager: ExtensionManager,
|
||||||
|
) {
|
||||||
|
fun await(sourceId: Long?): Boolean {
|
||||||
|
if (basePreferences.incognitoMode().get()) return true
|
||||||
|
if (sourceId == null) return false
|
||||||
|
val extensionPackage = extensionManager.getExtensionPackage(sourceId) ?: return false
|
||||||
|
|
||||||
|
return extensionPackage in sourcePreferences.incognitoExtensions().get()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun subscribe(sourceId: Long?): Flow<Boolean> {
|
||||||
|
if (sourceId == null) return basePreferences.incognitoMode().changes()
|
||||||
|
|
||||||
|
return combine(
|
||||||
|
basePreferences.incognitoMode().changes(),
|
||||||
|
sourcePreferences.incognitoExtensions().changes(),
|
||||||
|
extensionManager.getExtensionPackageAsFlow(sourceId),
|
||||||
|
) { incognito, incognitoExtensions, extensionPackage ->
|
||||||
|
incognito || (extensionPackage in incognitoExtensions)
|
||||||
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
|
import tachiyomi.core.common.preference.getAndSet
|
||||||
|
|
||||||
|
class ToggleIncognito(
|
||||||
|
private val preferences: SourcePreferences,
|
||||||
|
) {
|
||||||
|
fun await(extensions: String, enable: Boolean) {
|
||||||
|
preferences.incognitoExtensions().getAndSet {
|
||||||
|
if (enable) it.plus(extensions) else it.minus(extensions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,16 +2,18 @@ package eu.kanade.domain.source.service
|
|||||||
|
|
||||||
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
|
import mihon.domain.migration.models.MigrationFlag
|
||||||
import tachiyomi.core.common.preference.Preference
|
import tachiyomi.core.common.preference.Preference
|
||||||
import tachiyomi.core.common.preference.PreferenceStore
|
import tachiyomi.core.common.preference.PreferenceStore
|
||||||
import tachiyomi.core.common.preference.getEnum
|
import tachiyomi.core.common.preference.getEnum
|
||||||
|
import tachiyomi.core.common.preference.getLongArray
|
||||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
import tachiyomi.domain.library.model.LibraryDisplayMode
|
||||||
|
|
||||||
class SourcePreferences(
|
class SourcePreferences(
|
||||||
private val preferenceStore: PreferenceStore,
|
private val preferenceStore: PreferenceStore,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun sourceDisplayMode() = preferenceStore.getObject(
|
fun sourceDisplayMode() = preferenceStore.getObjectFromString(
|
||||||
"pref_display_mode_catalogue",
|
"pref_display_mode_catalogue",
|
||||||
LibraryDisplayMode.default,
|
LibraryDisplayMode.default,
|
||||||
LibraryDisplayMode.Serializer::serialize,
|
LibraryDisplayMode.Serializer::serialize,
|
||||||
@@ -22,6 +24,8 @@ class SourcePreferences(
|
|||||||
|
|
||||||
fun disabledSources() = preferenceStore.getStringSet("hidden_catalogues", emptySet())
|
fun disabledSources() = preferenceStore.getStringSet("hidden_catalogues", emptySet())
|
||||||
|
|
||||||
|
fun incognitoExtensions() = preferenceStore.getStringSet("incognito_extensions", emptySet())
|
||||||
|
|
||||||
fun pinnedSources() = preferenceStore.getStringSet("pinned_catalogues", emptySet())
|
fun pinnedSources() = preferenceStore.getStringSet("pinned_catalogues", emptySet())
|
||||||
|
|
||||||
fun lastUsedSource() = preferenceStore.getLong(
|
fun lastUsedSource() = preferenceStore.getLong(
|
||||||
@@ -86,5 +90,33 @@ class SourcePreferences(
|
|||||||
BANDWIDTH_HERO,
|
BANDWIDTH_HERO,
|
||||||
WSRV_NL,
|
WSRV_NL,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun allowLocalSourceHiddenFolders() = preferenceStore.getBoolean("allow_local_source_hidden_folders", false)
|
||||||
|
|
||||||
|
fun preferredMangaDexId() = preferenceStore.getString("preferred_mangaDex_id", "0")
|
||||||
|
|
||||||
|
fun mangadexSyncToLibraryIndexes() = preferenceStore.getStringSet(
|
||||||
|
"pref_mangadex_sync_to_library_indexes",
|
||||||
|
emptySet(),
|
||||||
|
)
|
||||||
|
|
||||||
|
fun recommendationSearchFlags() = preferenceStore.getInt("rec_search_flags", Int.MAX_VALUE)
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
|
fun migrationSources() = preferenceStore.getLongArray("migration_sources", emptyList())
|
||||||
|
|
||||||
|
fun migrationFlags() = preferenceStore.getObjectFromInt(
|
||||||
|
key = "migration_flags",
|
||||||
|
defaultValue = MigrationFlag.entries.toSet(),
|
||||||
|
serializer = { MigrationFlag.toBit(it) },
|
||||||
|
deserializer = { value: Int -> MigrationFlag.fromBit(value) },
|
||||||
|
)
|
||||||
|
|
||||||
|
fun migrationDeepSearchMode() = preferenceStore.getBoolean("migration_deep_search", false)
|
||||||
|
|
||||||
|
fun migrationPrioritizeByChapters() = preferenceStore.getBoolean("migration_prioritize_by_chapters", false)
|
||||||
|
|
||||||
|
fun migrationHideUnmatched() = preferenceStore.getBoolean("migration_hide_unmatched", false)
|
||||||
|
|
||||||
|
fun migrationHideWithoutUpdates() = preferenceStore.getBoolean("migration_hide_without_updates", false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ fun Track.copyPersonalFrom(other: Track): Track {
|
|||||||
status = other.status,
|
status = other.status,
|
||||||
startDate = other.startDate,
|
startDate = other.startDate,
|
||||||
finishDate = other.finishDate,
|
finishDate = other.finishDate,
|
||||||
|
private = other.private,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ fun Track.toDbTrack(): DbTrack = DbTrack.create(trackerId).also {
|
|||||||
it.tracking_url = remoteUrl
|
it.tracking_url = remoteUrl
|
||||||
it.started_reading_date = startDate
|
it.started_reading_date = startDate
|
||||||
it.finished_reading_date = finishDate
|
it.finished_reading_date = finishDate
|
||||||
|
it.private = private
|
||||||
}
|
}
|
||||||
|
|
||||||
fun DbTrack.toDomainTrack(idRequired: Boolean = true): Track? {
|
fun DbTrack.toDomainTrack(idRequired: Boolean = true): Track? {
|
||||||
@@ -44,5 +46,6 @@ fun DbTrack.toDomainTrack(idRequired: Boolean = true): Track? {
|
|||||||
remoteUrl = tracking_url,
|
remoteUrl = tracking_url,
|
||||||
startDate = started_reading_date,
|
startDate = started_reading_date,
|
||||||
finishDate = finished_reading_date,
|
finishDate = finished_reading_date,
|
||||||
|
private = private,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,4 +42,11 @@ class TrackPreferences(
|
|||||||
"pref_auto_update_manga_on_mark_read",
|
"pref_auto_update_manga_on_mark_read",
|
||||||
AutoTrackState.ALWAYS,
|
AutoTrackState.ALWAYS,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SY -->
|
||||||
|
fun resolveUsingSourceMetadata() = preferenceStore.getBoolean(
|
||||||
|
"pref_resolve_using_source_metadata_key",
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
// SY <--
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ class UiPreferences(
|
|||||||
|
|
||||||
fun tabletUiMode() = preferenceStore.getEnum("tablet_ui_mode", TabletUiMode.AUTOMATIC)
|
fun tabletUiMode() = preferenceStore.getEnum("tablet_ui_mode", TabletUiMode.AUTOMATIC)
|
||||||
|
|
||||||
|
fun imagesInDescription() = preferenceStore.getBoolean("pref_render_images_description", true)
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
|
|
||||||
fun expandFilters() = preferenceStore.getBoolean("eh_expand_filters", false)
|
fun expandFilters() = preferenceStore.getBoolean("eh_expand_filters", false)
|
||||||
|
|||||||
@@ -1,25 +1,23 @@
|
|||||||
package eu.kanade.domain.ui.model
|
package eu.kanade.domain.ui.model
|
||||||
|
|
||||||
import dev.icerock.moko.resources.StringResource
|
import dev.icerock.moko.resources.StringResource
|
||||||
import eu.kanade.tachiyomi.util.system.isDevFlavor
|
|
||||||
import eu.kanade.tachiyomi.util.system.isPreviewBuildType
|
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
|
||||||
enum class AppTheme(val titleRes: StringResource?) {
|
enum class AppTheme(val titleRes: StringResource?) {
|
||||||
DEFAULT(MR.strings.label_default),
|
DEFAULT(MR.strings.label_default),
|
||||||
MONET(MR.strings.theme_monet),
|
MONET(MR.strings.theme_monet),
|
||||||
|
CATPPUCCIN(MR.strings.theme_catppuccin),
|
||||||
GREEN_APPLE(MR.strings.theme_greenapple),
|
GREEN_APPLE(MR.strings.theme_greenapple),
|
||||||
LAVENDER(MR.strings.theme_lavender),
|
LAVENDER(MR.strings.theme_lavender),
|
||||||
MIDNIGHT_DUSK(MR.strings.theme_midnightdusk),
|
MIDNIGHT_DUSK(MR.strings.theme_midnightdusk),
|
||||||
|
NORD(MR.strings.theme_nord),
|
||||||
// TODO: re-enable for preview
|
|
||||||
NORD(MR.strings.theme_nord.takeIf { isDevFlavor || isPreviewBuildType }),
|
|
||||||
STRAWBERRY_DAIQUIRI(MR.strings.theme_strawberrydaiquiri),
|
STRAWBERRY_DAIQUIRI(MR.strings.theme_strawberrydaiquiri),
|
||||||
TAKO(MR.strings.theme_tako),
|
TAKO(MR.strings.theme_tako),
|
||||||
TEALTURQUOISE(MR.strings.theme_tealturquoise),
|
TEALTURQUOISE(MR.strings.theme_tealturquoise),
|
||||||
TIDAL_WAVE(MR.strings.theme_tidalwave),
|
TIDAL_WAVE(MR.strings.theme_tidalwave),
|
||||||
YINYANG(MR.strings.theme_yinyang),
|
YINYANG(MR.strings.theme_yinyang),
|
||||||
YOTSUBA(MR.strings.theme_yotsuba),
|
YOTSUBA(MR.strings.theme_yotsuba),
|
||||||
|
MONOCHROME(MR.strings.theme_monochrome),
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated
|
||||||
DARK_BLUE(null),
|
DARK_BLUE(null),
|
||||||
|
|||||||
@@ -82,10 +82,18 @@ fun BrowseSourceContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mangaList.itemCount <= 0 && errorState != null && errorState is LoadState.Error) {
|
if (mangaList.itemCount == 0 && mangaList.loadState.refresh is LoadState.Loading) {
|
||||||
|
LoadingScreen(Modifier.padding(contentPadding))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mangaList.itemCount == 0) {
|
||||||
EmptyScreen(
|
EmptyScreen(
|
||||||
modifier = Modifier.padding(contentPadding),
|
modifier = Modifier.padding(contentPadding),
|
||||||
message = getErrorMessage(errorState),
|
message = when (errorState) {
|
||||||
|
is LoadState.Error -> getErrorMessage(errorState)
|
||||||
|
else -> stringResource(MR.strings.no_results_found)
|
||||||
|
},
|
||||||
actions = if (source is LocalSource /* SY --> */ && onLocalSourceHelpClick != null /* SY <-- */) {
|
actions = if (source is LocalSource /* SY --> */ && onLocalSourceHelpClick != null /* SY <-- */) {
|
||||||
persistentListOf(
|
persistentListOf(
|
||||||
EmptyScreenAction(
|
EmptyScreenAction(
|
||||||
@@ -104,7 +112,7 @@ fun BrowseSourceContent(
|
|||||||
// SY -->
|
// SY -->
|
||||||
if (onWebViewClick != null) {
|
if (onWebViewClick != null) {
|
||||||
EmptyScreenAction(
|
EmptyScreenAction(
|
||||||
MR.strings.action_open_in_web_view,
|
stringRes = MR.strings.action_open_in_web_view,
|
||||||
icon = Icons.Outlined.Public,
|
icon = Icons.Outlined.Public,
|
||||||
onClick = onWebViewClick,
|
onClick = onWebViewClick,
|
||||||
)
|
)
|
||||||
@@ -113,7 +121,7 @@ fun BrowseSourceContent(
|
|||||||
},
|
},
|
||||||
if (onHelpClick != null) {
|
if (onHelpClick != null) {
|
||||||
EmptyScreenAction(
|
EmptyScreenAction(
|
||||||
MR.strings.label_help,
|
stringRes = MR.strings.label_help,
|
||||||
icon = Icons.AutoMirrored.Outlined.HelpOutline,
|
icon = Icons.AutoMirrored.Outlined.HelpOutline,
|
||||||
onClick = onHelpClick,
|
onClick = onHelpClick,
|
||||||
)
|
)
|
||||||
@@ -128,13 +136,6 @@ fun BrowseSourceContent(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mangaList.itemCount == 0 && mangaList.loadState.refresh is LoadState.Loading) {
|
|
||||||
LoadingScreen(
|
|
||||||
modifier = Modifier.padding(contentPadding),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
if (source?.isEhBasedSource() == true && ehentaiBrowseDisplayMode) {
|
if (source?.isEhBasedSource() == true && ehentaiBrowseDisplayMode) {
|
||||||
BrowseSourceEHentaiList(
|
BrowseSourceEHentaiList(
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import tachiyomi.presentation.core.components.material.Scaffold
|
|||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BrowseTabWrapper(tab: TabContent) {
|
fun BrowseTabWrapper(tab: TabContent, onBackPressed: (() -> Unit)? = null) {
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = { scrollBehavior ->
|
topBar = { scrollBehavior ->
|
||||||
@@ -20,6 +20,7 @@ fun BrowseTabWrapper(tab: TabContent) {
|
|||||||
actions = {
|
actions = {
|
||||||
AppBarActions(tab.actions)
|
AppBarActions(tab.actions)
|
||||||
},
|
},
|
||||||
|
navigateUp = onBackPressed,
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -35,8 +35,10 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
|
import androidx.compose.ui.res.vectorResource
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
@@ -48,6 +50,7 @@ import eu.kanade.presentation.components.AppBarActions
|
|||||||
import eu.kanade.presentation.components.WarningBanner
|
import eu.kanade.presentation.components.WarningBanner
|
||||||
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
||||||
import eu.kanade.presentation.more.settings.widget.TrailingWidgetBuffer
|
import eu.kanade.presentation.more.settings.widget.TrailingWidgetBuffer
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsScreenModel
|
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsScreenModel
|
||||||
@@ -73,6 +76,7 @@ fun ExtensionDetailsScreen(
|
|||||||
onClickClearCookies: () -> Unit,
|
onClickClearCookies: () -> Unit,
|
||||||
onClickUninstall: () -> Unit,
|
onClickUninstall: () -> Unit,
|
||||||
onClickSource: (sourceId: Long) -> Unit,
|
onClickSource: (sourceId: Long) -> Unit,
|
||||||
|
onClickIncognito: (Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
val uriHandler = LocalUriHandler.current
|
val uriHandler = LocalUriHandler.current
|
||||||
val url = remember(state.extension) {
|
val url = remember(state.extension) {
|
||||||
@@ -141,9 +145,11 @@ fun ExtensionDetailsScreen(
|
|||||||
contentPadding = paddingValues,
|
contentPadding = paddingValues,
|
||||||
extension = state.extension,
|
extension = state.extension,
|
||||||
sources = state.sources,
|
sources = state.sources,
|
||||||
|
incognitoMode = state.isIncognito,
|
||||||
onClickSourcePreferences = onClickSourcePreferences,
|
onClickSourcePreferences = onClickSourcePreferences,
|
||||||
onClickUninstall = onClickUninstall,
|
onClickUninstall = onClickUninstall,
|
||||||
onClickSource = onClickSource,
|
onClickSource = onClickSource,
|
||||||
|
onClickIncognito = onClickIncognito,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,9 +159,11 @@ private fun ExtensionDetails(
|
|||||||
contentPadding: PaddingValues,
|
contentPadding: PaddingValues,
|
||||||
extension: Extension.Installed,
|
extension: Extension.Installed,
|
||||||
sources: ImmutableList<ExtensionSourceItem>,
|
sources: ImmutableList<ExtensionSourceItem>,
|
||||||
|
incognitoMode: Boolean,
|
||||||
onClickSourcePreferences: (sourceId: Long) -> Unit,
|
onClickSourcePreferences: (sourceId: Long) -> Unit,
|
||||||
onClickUninstall: () -> Unit,
|
onClickUninstall: () -> Unit,
|
||||||
onClickSource: (sourceId: Long) -> Unit,
|
onClickSource: (sourceId: Long) -> Unit,
|
||||||
|
onClickIncognito: (Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
var showNsfwWarning by remember { mutableStateOf(false) }
|
var showNsfwWarning by remember { mutableStateOf(false) }
|
||||||
@@ -179,6 +187,7 @@ private fun ExtensionDetails(
|
|||||||
item {
|
item {
|
||||||
DetailsHeader(
|
DetailsHeader(
|
||||||
extension = extension,
|
extension = extension,
|
||||||
|
extIncognitoMode = incognitoMode,
|
||||||
onClickUninstall = onClickUninstall,
|
onClickUninstall = onClickUninstall,
|
||||||
onClickAppInfo = {
|
onClickAppInfo = {
|
||||||
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
||||||
@@ -190,6 +199,7 @@ private fun ExtensionDetails(
|
|||||||
onClickAgeRating = {
|
onClickAgeRating = {
|
||||||
showNsfwWarning = true
|
showNsfwWarning = true
|
||||||
},
|
},
|
||||||
|
onExtIncognitoChange = onClickIncognito,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,9 +227,11 @@ private fun ExtensionDetails(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun DetailsHeader(
|
private fun DetailsHeader(
|
||||||
extension: Extension,
|
extension: Extension,
|
||||||
|
extIncognitoMode: Boolean,
|
||||||
onClickAgeRating: () -> Unit,
|
onClickAgeRating: () -> Unit,
|
||||||
onClickUninstall: () -> Unit,
|
onClickUninstall: () -> Unit,
|
||||||
onClickAppInfo: (() -> Unit)?,
|
onClickAppInfo: (() -> Unit)?,
|
||||||
|
onExtIncognitoChange: (Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
@@ -227,9 +239,8 @@ private fun DetailsHeader(
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = MaterialTheme.padding.medium)
|
||||||
.padding(
|
.padding(
|
||||||
start = MaterialTheme.padding.medium,
|
|
||||||
end = MaterialTheme.padding.medium,
|
|
||||||
top = MaterialTheme.padding.medium,
|
top = MaterialTheme.padding.medium,
|
||||||
bottom = MaterialTheme.padding.small,
|
bottom = MaterialTheme.padding.small,
|
||||||
)
|
)
|
||||||
@@ -321,12 +332,9 @@ private fun DetailsHeader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.padding(
|
modifier = Modifier
|
||||||
start = MaterialTheme.padding.medium,
|
.padding(horizontal = MaterialTheme.padding.medium)
|
||||||
end = MaterialTheme.padding.medium,
|
.padding(top = MaterialTheme.padding.small),
|
||||||
top = MaterialTheme.padding.small,
|
|
||||||
bottom = MaterialTheme.padding.medium,
|
|
||||||
),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.medium),
|
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.medium),
|
||||||
) {
|
) {
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
@@ -349,6 +357,24 @@ private fun DetailsHeader(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextPreferenceWidget(
|
||||||
|
modifier = Modifier.padding(horizontal = MaterialTheme.padding.small),
|
||||||
|
title = stringResource(MR.strings.pref_incognito_mode),
|
||||||
|
subtitle = stringResource(MR.strings.pref_incognito_mode_extension_summary),
|
||||||
|
icon = ImageVector.vectorResource(R.drawable.ic_glasses_24dp),
|
||||||
|
widget = {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Switch(
|
||||||
|
checked = extIncognitoMode,
|
||||||
|
onCheckedChange = onExtIncognitoChange,
|
||||||
|
modifier = Modifier.padding(start = TrailingWidgetBuffer),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
HorizontalDivider()
|
HorizontalDivider()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -351,13 +351,17 @@ private fun ExtensionItemContent(
|
|||||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall),
|
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall),
|
||||||
) {
|
) {
|
||||||
ProvideTextStyle(value = MaterialTheme.typography.bodySmall) {
|
ProvideTextStyle(value = MaterialTheme.typography.bodySmall) {
|
||||||
|
var hasAlreadyShownAnElement by remember { mutableStateOf(false) }
|
||||||
if (extension is Extension.Installed && extension.lang.isNotEmpty()) {
|
if (extension is Extension.Installed && extension.lang.isNotEmpty()) {
|
||||||
|
hasAlreadyShownAnElement = true
|
||||||
Text(
|
Text(
|
||||||
text = LocaleHelper.getSourceDisplayName(extension.lang, LocalContext.current),
|
text = LocaleHelper.getSourceDisplayName(extension.lang, LocalContext.current),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extension.versionName.isNotEmpty()) {
|
if (extension.versionName.isNotEmpty()) {
|
||||||
|
if (hasAlreadyShownAnElement) DotSeparatorNoSpaceText()
|
||||||
|
hasAlreadyShownAnElement = true
|
||||||
Text(
|
Text(
|
||||||
text = extension.versionName,
|
text = extension.versionName,
|
||||||
)
|
)
|
||||||
@@ -373,6 +377,8 @@ private fun ExtensionItemContent(
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
if (warning != null) {
|
if (warning != null) {
|
||||||
|
if (hasAlreadyShownAnElement) DotSeparatorNoSpaceText()
|
||||||
|
hasAlreadyShownAnElement = true
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(warning).uppercase(),
|
text = stringResource(warning).uppercase(),
|
||||||
color = MaterialTheme.colorScheme.error,
|
color = MaterialTheme.colorScheme.error,
|
||||||
@@ -380,6 +386,12 @@ private fun ExtensionItemContent(
|
|||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (extension is Extension.Installed && !extension.isShared) {
|
||||||
|
if (hasAlreadyShownAnElement) DotSeparatorNoSpaceText()
|
||||||
|
Text(
|
||||||
|
text = stringResource(MR.strings.ext_installer_private),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (!installStep.isCompleted()) {
|
if (!installStep.isCompleted()) {
|
||||||
DotSeparatorNoSpaceText()
|
DotSeparatorNoSpaceText()
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ fun GlobalSearchScreen(
|
|||||||
navigateUp = navigateUp,
|
navigateUp = navigateUp,
|
||||||
onChangeSearchQuery = onChangeSearchQuery,
|
onChangeSearchQuery = onChangeSearchQuery,
|
||||||
onSearch = onSearch,
|
onSearch = onSearch,
|
||||||
|
hideSourceFilter = false,
|
||||||
sourceFilter = state.sourceFilter,
|
sourceFilter = state.sourceFilter,
|
||||||
onChangeSearchFilter = onChangeSearchFilter,
|
onChangeSearchFilter = onChangeSearchFilter,
|
||||||
onlyShowHasResults = state.onlyShowHasResults,
|
onlyShowHasResults = state.onlyShowHasResults,
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
package eu.kanade.presentation.browse
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import eu.kanade.presentation.components.AppBar
|
|
||||||
import eu.kanade.presentation.manga.components.BaseMangaListItem
|
|
||||||
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaScreenModel
|
|
||||||
import tachiyomi.domain.manga.model.Manga
|
|
||||||
import tachiyomi.i18n.MR
|
|
||||||
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
|
||||||
import tachiyomi.presentation.core.components.material.Scaffold
|
|
||||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun MigrateMangaScreen(
|
|
||||||
navigateUp: () -> Unit,
|
|
||||||
title: String?,
|
|
||||||
state: MigrateMangaScreenModel.State,
|
|
||||||
onClickItem: (Manga) -> Unit,
|
|
||||||
onClickCover: (Manga) -> Unit,
|
|
||||||
) {
|
|
||||||
Scaffold(
|
|
||||||
topBar = { scrollBehavior ->
|
|
||||||
AppBar(
|
|
||||||
title = title,
|
|
||||||
navigateUp = navigateUp,
|
|
||||||
scrollBehavior = scrollBehavior,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
) { contentPadding ->
|
|
||||||
if (state.isEmpty) {
|
|
||||||
EmptyScreen(
|
|
||||||
stringRes = MR.strings.empty_screen,
|
|
||||||
modifier = Modifier.padding(contentPadding),
|
|
||||||
)
|
|
||||||
return@Scaffold
|
|
||||||
}
|
|
||||||
|
|
||||||
MigrateMangaContent(
|
|
||||||
contentPadding = contentPadding,
|
|
||||||
state = state,
|
|
||||||
onClickItem = onClickItem,
|
|
||||||
onClickCover = onClickCover,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun MigrateMangaContent(
|
|
||||||
contentPadding: PaddingValues,
|
|
||||||
state: MigrateMangaScreenModel.State,
|
|
||||||
onClickItem: (Manga) -> Unit,
|
|
||||||
onClickCover: (Manga) -> Unit,
|
|
||||||
) {
|
|
||||||
FastScrollLazyColumn(
|
|
||||||
contentPadding = contentPadding,
|
|
||||||
) {
|
|
||||||
items(state.titles) { manga ->
|
|
||||||
MigrateMangaItem(
|
|
||||||
manga = manga,
|
|
||||||
onClickItem = onClickItem,
|
|
||||||
onClickCover = onClickCover,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun MigrateMangaItem(
|
|
||||||
manga: Manga,
|
|
||||||
onClickItem: (Manga) -> Unit,
|
|
||||||
onClickCover: (Manga) -> Unit,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
BaseMangaListItem(
|
|
||||||
modifier = modifier,
|
|
||||||
manga = manga,
|
|
||||||
onClickItem = { onClickItem(manga) },
|
|
||||||
onClickCover = { onClickCover(manga) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -32,6 +32,7 @@ fun MigrateSearchScreen(
|
|||||||
navigateUp = navigateUp,
|
navigateUp = navigateUp,
|
||||||
onChangeSearchQuery = onChangeSearchQuery,
|
onChangeSearchQuery = onChangeSearchQuery,
|
||||||
onSearch = onSearch,
|
onSearch = onSearch,
|
||||||
|
hideSourceFilter = true,
|
||||||
sourceFilter = state.sourceFilter,
|
sourceFilter = state.sourceFilter,
|
||||||
onChangeSearchFilter = onChangeSearchFilter,
|
onChangeSearchFilter = onChangeSearchFilter,
|
||||||
onlyShowHasResults = state.onlyShowHasResults,
|
onlyShowHasResults = state.onlyShowHasResults,
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.outlined.ArrowForward
|
import androidx.compose.material.icons.automirrored.outlined.ArrowForward
|
||||||
import androidx.compose.material.icons.outlined.ArrowForward
|
|
||||||
import androidx.compose.material.icons.outlined.ContentCopy
|
import androidx.compose.material.icons.outlined.ContentCopy
|
||||||
import androidx.compose.material.icons.outlined.CopyAll
|
import androidx.compose.material.icons.outlined.CopyAll
|
||||||
import androidx.compose.material.icons.outlined.Done
|
import androidx.compose.material.icons.outlined.Done
|
||||||
|
|||||||
+9
@@ -19,6 +19,7 @@ import eu.kanade.presentation.library.components.MangaComfortableGridItem
|
|||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import exh.metadata.metadata.MangaDexSearchMetadata
|
import exh.metadata.metadata.MangaDexSearchMetadata
|
||||||
import exh.metadata.metadata.RaisedSearchMetadata
|
import exh.metadata.metadata.RaisedSearchMetadata
|
||||||
|
import exh.metadata.metadata.RankedSearchMetadata
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.manga.model.MangaCover
|
import tachiyomi.domain.manga.model.MangaCover
|
||||||
@@ -119,6 +120,14 @@ private fun BrowseSourceComfortableGridItem(
|
|||||||
textColor = MaterialTheme.colorScheme.onTertiary,
|
textColor = MaterialTheme.colorScheme.onTertiary,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else if (metadata is RankedSearchMetadata) {
|
||||||
|
metadata.rank?.let {
|
||||||
|
Badge(
|
||||||
|
text = "+$it",
|
||||||
|
color = MaterialTheme.colorScheme.tertiary,
|
||||||
|
textColor = MaterialTheme.colorScheme.onTertiary,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import eu.kanade.presentation.library.components.MangaCompactGridItem
|
|||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import exh.metadata.metadata.MangaDexSearchMetadata
|
import exh.metadata.metadata.MangaDexSearchMetadata
|
||||||
import exh.metadata.metadata.RaisedSearchMetadata
|
import exh.metadata.metadata.RaisedSearchMetadata
|
||||||
|
import exh.metadata.metadata.RankedSearchMetadata
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.manga.model.MangaCover
|
import tachiyomi.domain.manga.model.MangaCover
|
||||||
@@ -119,6 +120,14 @@ private fun BrowseSourceCompactGridItem(
|
|||||||
textColor = MaterialTheme.colorScheme.onTertiary,
|
textColor = MaterialTheme.colorScheme.onTertiary,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else if (metadata is RankedSearchMetadata) {
|
||||||
|
metadata.rank?.let {
|
||||||
|
Badge(
|
||||||
|
text = "+$it",
|
||||||
|
color = MaterialTheme.colorScheme.tertiary,
|
||||||
|
textColor = MaterialTheme.colorScheme.onTertiary,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import eu.kanade.presentation.library.components.MangaListItem
|
|||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import exh.metadata.metadata.MangaDexSearchMetadata
|
import exh.metadata.metadata.MangaDexSearchMetadata
|
||||||
import exh.metadata.metadata.RaisedSearchMetadata
|
import exh.metadata.metadata.RaisedSearchMetadata
|
||||||
|
import exh.metadata.metadata.RankedSearchMetadata
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.manga.model.MangaCover
|
import tachiyomi.domain.manga.model.MangaCover
|
||||||
@@ -110,6 +111,14 @@ private fun BrowseSourceListItem(
|
|||||||
textColor = MaterialTheme.colorScheme.onTertiary,
|
textColor = MaterialTheme.colorScheme.onTertiary,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else if (metadata is RankedSearchMetadata) {
|
||||||
|
metadata.rank?.let {
|
||||||
|
Badge(
|
||||||
|
text = "+$it",
|
||||||
|
color = MaterialTheme.colorScheme.tertiary,
|
||||||
|
textColor = MaterialTheme.colorScheme.onTertiary,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.automirrored.filled.ViewList
|
import androidx.compose.material.icons.automirrored.filled.ViewList
|
||||||
import androidx.compose.material.icons.automirrored.outlined.Help
|
import androidx.compose.material.icons.automirrored.outlined.Help
|
||||||
import androidx.compose.material.icons.filled.ViewModule
|
import androidx.compose.material.icons.filled.ViewModule
|
||||||
import androidx.compose.material.icons.outlined.Help
|
|
||||||
import androidx.compose.material.icons.outlined.Public
|
import androidx.compose.material.icons.outlined.Public
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ fun GlobalSearchToolbar(
|
|||||||
navigateUp: () -> Unit,
|
navigateUp: () -> Unit,
|
||||||
onChangeSearchQuery: (String?) -> Unit,
|
onChangeSearchQuery: (String?) -> Unit,
|
||||||
onSearch: (String) -> Unit,
|
onSearch: (String) -> Unit,
|
||||||
|
hideSourceFilter: Boolean,
|
||||||
sourceFilter: SourceFilter,
|
sourceFilter: SourceFilter,
|
||||||
onChangeSearchFilter: (SourceFilter) -> Unit,
|
onChangeSearchFilter: (SourceFilter) -> Unit,
|
||||||
onlyShowHasResults: Boolean,
|
onlyShowHasResults: Boolean,
|
||||||
@@ -73,38 +74,40 @@ fun GlobalSearchToolbar(
|
|||||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||||
) {
|
) {
|
||||||
// TODO: make this UX better; it only applies when triggering a new search
|
// TODO: make this UX better; it only applies when triggering a new search
|
||||||
FilterChip(
|
if (!hideSourceFilter) {
|
||||||
selected = sourceFilter == SourceFilter.PinnedOnly,
|
FilterChip(
|
||||||
onClick = { onChangeSearchFilter(SourceFilter.PinnedOnly) },
|
selected = sourceFilter == SourceFilter.PinnedOnly,
|
||||||
leadingIcon = {
|
onClick = { onChangeSearchFilter(SourceFilter.PinnedOnly) },
|
||||||
Icon(
|
leadingIcon = {
|
||||||
imageVector = Icons.Outlined.PushPin,
|
Icon(
|
||||||
contentDescription = null,
|
imageVector = Icons.Outlined.PushPin,
|
||||||
modifier = Modifier
|
contentDescription = null,
|
||||||
.size(FilterChipDefaults.IconSize),
|
modifier = Modifier
|
||||||
)
|
.size(FilterChipDefaults.IconSize),
|
||||||
},
|
)
|
||||||
label = {
|
},
|
||||||
Text(text = stringResource(MR.strings.pinned_sources))
|
label = {
|
||||||
},
|
Text(text = stringResource(MR.strings.pinned_sources))
|
||||||
)
|
},
|
||||||
FilterChip(
|
)
|
||||||
selected = sourceFilter == SourceFilter.All,
|
FilterChip(
|
||||||
onClick = { onChangeSearchFilter(SourceFilter.All) },
|
selected = sourceFilter == SourceFilter.All,
|
||||||
leadingIcon = {
|
onClick = { onChangeSearchFilter(SourceFilter.All) },
|
||||||
Icon(
|
leadingIcon = {
|
||||||
imageVector = Icons.Outlined.DoneAll,
|
Icon(
|
||||||
contentDescription = null,
|
imageVector = Icons.Outlined.DoneAll,
|
||||||
modifier = Modifier
|
contentDescription = null,
|
||||||
.size(FilterChipDefaults.IconSize),
|
modifier = Modifier
|
||||||
)
|
.size(FilterChipDefaults.IconSize),
|
||||||
},
|
)
|
||||||
label = {
|
},
|
||||||
Text(text = stringResource(MR.strings.all))
|
label = {
|
||||||
},
|
Text(text = stringResource(MR.strings.all))
|
||||||
)
|
},
|
||||||
|
)
|
||||||
|
|
||||||
VerticalDivider()
|
VerticalDivider()
|
||||||
|
}
|
||||||
|
|
||||||
FilterChip(
|
FilterChip(
|
||||||
selected = onlyShowHasResults,
|
selected = onlyShowHasResults,
|
||||||
|
|||||||
@@ -2,23 +2,25 @@ package eu.kanade.presentation.category
|
|||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
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.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.toMutableStateList
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
|
import eu.kanade.presentation.category.components.CategoryFloatingActionButton
|
||||||
import eu.kanade.presentation.category.components.CategoryListItem
|
import eu.kanade.presentation.category.components.CategoryListItem
|
||||||
import eu.kanade.presentation.components.AppBar
|
import eu.kanade.presentation.components.AppBar
|
||||||
import eu.kanade.presentation.components.AppBarActions
|
|
||||||
import eu.kanade.tachiyomi.ui.category.CategoryScreenState
|
import eu.kanade.tachiyomi.ui.category.CategoryScreenState
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import sh.calvin.reorderable.ReorderableItem
|
||||||
|
import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||||
import tachiyomi.domain.category.model.Category
|
import tachiyomi.domain.category.model.Category
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.material.Scaffold
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
@@ -32,11 +34,9 @@ import tachiyomi.presentation.core.util.plus
|
|||||||
fun CategoryScreen(
|
fun CategoryScreen(
|
||||||
state: CategoryScreenState.Success,
|
state: CategoryScreenState.Success,
|
||||||
onClickCreate: () -> Unit,
|
onClickCreate: () -> Unit,
|
||||||
onClickSortAlphabetically: () -> Unit,
|
|
||||||
onClickRename: (Category) -> Unit,
|
onClickRename: (Category) -> Unit,
|
||||||
onClickDelete: (Category) -> Unit,
|
onClickDelete: (Category) -> Unit,
|
||||||
onClickMoveUp: (Category) -> Unit,
|
onChangeOrder: (Category, Int) -> Unit,
|
||||||
onClickMoveDown: (Category) -> Unit,
|
|
||||||
navigateUp: () -> Unit,
|
navigateUp: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val lazyListState = rememberLazyListState()
|
val lazyListState = rememberLazyListState()
|
||||||
@@ -45,17 +45,6 @@ fun CategoryScreen(
|
|||||||
AppBar(
|
AppBar(
|
||||||
title = stringResource(MR.strings.action_edit_categories),
|
title = stringResource(MR.strings.action_edit_categories),
|
||||||
navigateUp = navigateUp,
|
navigateUp = navigateUp,
|
||||||
actions = {
|
|
||||||
AppBarActions(
|
|
||||||
persistentListOf(
|
|
||||||
AppBar.Action(
|
|
||||||
title = stringResource(MR.strings.action_sort),
|
|
||||||
icon = Icons.Outlined.SortByAlpha,
|
|
||||||
onClick = onClickSortAlphabetically,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -77,12 +66,10 @@ fun CategoryScreen(
|
|||||||
CategoryContent(
|
CategoryContent(
|
||||||
categories = state.categories,
|
categories = state.categories,
|
||||||
lazyListState = lazyListState,
|
lazyListState = lazyListState,
|
||||||
paddingValues = paddingValues + topSmallPaddingValues +
|
paddingValues = paddingValues,
|
||||||
PaddingValues(horizontal = MaterialTheme.padding.medium),
|
|
||||||
onClickRename = onClickRename,
|
onClickRename = onClickRename,
|
||||||
onClickDelete = onClickDelete,
|
onClickDelete = onClickDelete,
|
||||||
onMoveUp = onClickMoveUp,
|
onChangeOrder = onChangeOrder,
|
||||||
onMoveDown = onClickMoveDown,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,28 +81,44 @@ private fun CategoryContent(
|
|||||||
paddingValues: PaddingValues,
|
paddingValues: PaddingValues,
|
||||||
onClickRename: (Category) -> Unit,
|
onClickRename: (Category) -> Unit,
|
||||||
onClickDelete: (Category) -> Unit,
|
onClickDelete: (Category) -> Unit,
|
||||||
onMoveUp: (Category) -> Unit,
|
onChangeOrder: (Category, Int) -> Unit,
|
||||||
onMoveDown: (Category) -> Unit,
|
|
||||||
) {
|
) {
|
||||||
|
val categoriesState = remember { categories.toMutableStateList() }
|
||||||
|
val reorderableState = rememberReorderableLazyListState(lazyListState, paddingValues) { from, to ->
|
||||||
|
val item = categoriesState.removeAt(from.index)
|
||||||
|
categoriesState.add(to.index, item)
|
||||||
|
onChangeOrder(item, to.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(categories) {
|
||||||
|
if (!reorderableState.isAnyItemDragging) {
|
||||||
|
categoriesState.clear()
|
||||||
|
categoriesState.addAll(categories)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
state = lazyListState,
|
state = lazyListState,
|
||||||
contentPadding = paddingValues,
|
contentPadding = paddingValues +
|
||||||
|
topSmallPaddingValues +
|
||||||
|
PaddingValues(horizontal = MaterialTheme.padding.medium),
|
||||||
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||||
) {
|
) {
|
||||||
itemsIndexed(
|
items(
|
||||||
items = categories,
|
items = categoriesState,
|
||||||
key = { _, category -> "category-${category.id}" },
|
key = { category -> category.key },
|
||||||
) { index, category ->
|
) { category ->
|
||||||
CategoryListItem(
|
ReorderableItem(reorderableState, category.key) {
|
||||||
modifier = Modifier.animateItem(),
|
CategoryListItem(
|
||||||
category = category,
|
modifier = Modifier.animateItem(),
|
||||||
canMoveUp = index != 0,
|
category = category,
|
||||||
canMoveDown = index != categories.lastIndex,
|
onRename = { onClickRename(category) },
|
||||||
onMoveUp = onMoveUp,
|
onDelete = { onClickDelete(category) },
|
||||||
onMoveDown = onMoveDown,
|
)
|
||||||
onRename = { onClickRename(category) },
|
}
|
||||||
onDelete = { onClickDelete(category) },
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val Category.key inline get() = "category-$id"
|
||||||
|
|||||||
@@ -219,35 +219,6 @@ fun CategoryDeleteDialog(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun CategorySortAlphabeticallyDialog(
|
|
||||||
onDismissRequest: () -> Unit,
|
|
||||||
onSort: () -> Unit,
|
|
||||||
) {
|
|
||||||
AlertDialog(
|
|
||||||
onDismissRequest = onDismissRequest,
|
|
||||||
confirmButton = {
|
|
||||||
TextButton(onClick = {
|
|
||||||
onSort()
|
|
||||||
onDismissRequest()
|
|
||||||
}) {
|
|
||||||
Text(text = stringResource(MR.strings.action_ok))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dismissButton = {
|
|
||||||
TextButton(onClick = onDismissRequest) {
|
|
||||||
Text(text = stringResource(MR.strings.action_cancel))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
title = {
|
|
||||||
Text(text = stringResource(MR.strings.action_sort_category))
|
|
||||||
},
|
|
||||||
text = {
|
|
||||||
Text(text = stringResource(MR.strings.sort_category_confirmation))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChangeCategoryDialog(
|
fun ChangeCategoryDialog(
|
||||||
initialSelection: ImmutableList<CheckboxState<Category>>,
|
initialSelection: ImmutableList<CheckboxState<Category>>,
|
||||||
|
|||||||
@@ -2,14 +2,11 @@ package eu.kanade.presentation.category.components
|
|||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.outlined.Label
|
|
||||||
import androidx.compose.material.icons.outlined.ArrowDropDown
|
|
||||||
import androidx.compose.material.icons.outlined.ArrowDropUp
|
|
||||||
import androidx.compose.material.icons.outlined.Delete
|
import androidx.compose.material.icons.outlined.Delete
|
||||||
|
import androidx.compose.material.icons.outlined.DragHandle
|
||||||
import androidx.compose.material.icons.outlined.Edit
|
import androidx.compose.material.icons.outlined.Edit
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@@ -19,57 +16,42 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import sh.calvin.reorderable.ReorderableCollectionItemScope
|
||||||
import tachiyomi.domain.category.model.Category
|
import tachiyomi.domain.category.model.Category
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CategoryListItem(
|
fun ReorderableCollectionItemScope.CategoryListItem(
|
||||||
category: Category,
|
category: Category,
|
||||||
canMoveUp: Boolean,
|
|
||||||
canMoveDown: Boolean,
|
|
||||||
onMoveUp: (Category) -> Unit,
|
|
||||||
onMoveDown: (Category) -> Unit,
|
|
||||||
onRename: () -> Unit,
|
onRename: () -> Unit,
|
||||||
onDelete: () -> Unit,
|
onDelete: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
ElevatedCard(
|
ElevatedCard(modifier = modifier) {
|
||||||
modifier = modifier,
|
|
||||||
) {
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable { onRename() }
|
.clickable(onClick = onRename)
|
||||||
|
.padding(vertical = MaterialTheme.padding.small)
|
||||||
.padding(
|
.padding(
|
||||||
start = MaterialTheme.padding.medium,
|
start = MaterialTheme.padding.small,
|
||||||
top = MaterialTheme.padding.medium,
|
|
||||||
end = MaterialTheme.padding.medium,
|
end = MaterialTheme.padding.medium,
|
||||||
),
|
),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Icon(imageVector = Icons.AutoMirrored.Outlined.Label, contentDescription = null)
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.DragHandle,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(MaterialTheme.padding.medium)
|
||||||
|
.draggableHandle(),
|
||||||
|
)
|
||||||
Text(
|
Text(
|
||||||
text = category.name,
|
text = category.name,
|
||||||
modifier = Modifier
|
modifier = Modifier.weight(1f),
|
||||||
.padding(start = MaterialTheme.padding.medium),
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
Row {
|
|
||||||
IconButton(
|
|
||||||
onClick = { onMoveUp(category) },
|
|
||||||
enabled = canMoveUp,
|
|
||||||
) {
|
|
||||||
Icon(imageVector = Icons.Outlined.ArrowDropUp, contentDescription = null)
|
|
||||||
}
|
|
||||||
IconButton(
|
|
||||||
onClick = { onMoveDown(category) },
|
|
||||||
enabled = canMoveDown,
|
|
||||||
) {
|
|
||||||
Icon(imageVector = Icons.Outlined.ArrowDropDown, contentDescription = null)
|
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
|
||||||
IconButton(onClick = onRename) {
|
IconButton(onClick = onRename) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.Edit,
|
imageVector = Icons.Outlined.Edit,
|
||||||
@@ -77,7 +59,10 @@ fun CategoryListItem(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
IconButton(onClick = onDelete) {
|
IconButton(onClick = onDelete) {
|
||||||
Icon(imageVector = Icons.Outlined.Delete, contentDescription = stringResource(MR.strings.action_delete))
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Delete,
|
||||||
|
contentDescription = stringResource(MR.strings.action_delete),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.outlined.Label
|
import androidx.compose.material.icons.automirrored.outlined.Label
|
||||||
import androidx.compose.material.icons.outlined.Delete
|
import androidx.compose.material.icons.outlined.Delete
|
||||||
import androidx.compose.material.icons.outlined.Label
|
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import androidx.compose.material.icons.automirrored.outlined.Label
|
|||||||
import androidx.compose.material.icons.outlined.ArrowDropDown
|
import androidx.compose.material.icons.outlined.ArrowDropDown
|
||||||
import androidx.compose.material.icons.outlined.ArrowDropUp
|
import androidx.compose.material.icons.outlined.ArrowDropUp
|
||||||
import androidx.compose.material.icons.outlined.Delete
|
import androidx.compose.material.icons.outlined.Delete
|
||||||
import androidx.compose.material.icons.outlined.Label
|
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
|||||||
-1
@@ -9,7 +9,6 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.automirrored.outlined.Label
|
import androidx.compose.material.icons.automirrored.outlined.Label
|
||||||
import androidx.compose.material.icons.outlined.Delete
|
import androidx.compose.material.icons.outlined.Delete
|
||||||
import androidx.compose.material.icons.outlined.Edit
|
import androidx.compose.material.icons.outlined.Edit
|
||||||
import androidx.compose.material.icons.outlined.Label
|
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package eu.kanade.presentation.components
|
package eu.kanade.presentation.components
|
||||||
|
|
||||||
import androidx.activity.compose.BackHandler
|
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
@@ -28,8 +27,8 @@ fun NavigatorAdaptiveSheet(
|
|||||||
screen = screen,
|
screen = screen,
|
||||||
content = { sheetNavigator ->
|
content = { sheetNavigator ->
|
||||||
AdaptiveSheet(
|
AdaptiveSheet(
|
||||||
enableSwipeDismiss = enableSwipeDismiss(sheetNavigator),
|
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
|
enableSwipeDismiss = enableSwipeDismiss(sheetNavigator),
|
||||||
) {
|
) {
|
||||||
ScreenTransition(
|
ScreenTransition(
|
||||||
navigator = sheetNavigator,
|
navigator = sheetNavigator,
|
||||||
@@ -38,11 +37,6 @@ fun NavigatorAdaptiveSheet(
|
|||||||
fadeOut(animationSpec = tween(90))
|
fadeOut(animationSpec = tween(90))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
BackHandler(
|
|
||||||
enabled = sheetNavigator.size > 1,
|
|
||||||
onBack = sheetNavigator::pop,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure screens are disposed no matter what
|
// Make sure screens are disposed no matter what
|
||||||
@@ -79,10 +73,10 @@ fun AdaptiveSheet(
|
|||||||
properties = dialogProperties,
|
properties = dialogProperties,
|
||||||
) {
|
) {
|
||||||
AdaptiveSheetImpl(
|
AdaptiveSheetImpl(
|
||||||
modifier = modifier,
|
|
||||||
isTabletUi = isTabletUi,
|
isTabletUi = isTabletUi,
|
||||||
enableSwipeDismiss = enableSwipeDismiss,
|
enableSwipeDismiss = enableSwipeDismiss,
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
content()
|
content()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.focus.FocusDirection
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@@ -201,6 +202,7 @@ fun AppBarActions(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
state = rememberTooltipState(),
|
state = rememberTooltipState(),
|
||||||
|
focusable = false,
|
||||||
) {
|
) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = it.onClick,
|
onClick = it.onClick,
|
||||||
@@ -225,6 +227,7 @@ fun AppBarActions(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
state = rememberTooltipState(),
|
state = rememberTooltipState(),
|
||||||
|
focusable = false,
|
||||||
) {
|
) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = { showMenu = !showMenu },
|
onClick = { showMenu = !showMenu },
|
||||||
@@ -289,6 +292,7 @@ fun SearchToolbar(
|
|||||||
onSearch(searchQuery)
|
onSearch(searchQuery)
|
||||||
focusManager.clearFocus()
|
focusManager.clearFocus()
|
||||||
keyboardController?.hide()
|
keyboardController?.hide()
|
||||||
|
focusManager.moveFocus(FocusDirection.Next)
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicTextField(
|
BasicTextField(
|
||||||
@@ -352,6 +356,7 @@ fun SearchToolbar(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
state = rememberTooltipState(),
|
state = rememberTooltipState(),
|
||||||
|
focusable = false,
|
||||||
) {
|
) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
@@ -371,6 +376,7 @@ fun SearchToolbar(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
state = rememberTooltipState(),
|
state = rememberTooltipState(),
|
||||||
|
focusable = false,
|
||||||
) {
|
) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ fun relativeDateText(
|
|||||||
Instant.ofEpochMilli(dateEpochMillis),
|
Instant.ofEpochMilli(dateEpochMillis),
|
||||||
ZoneId.systemDefault(),
|
ZoneId.systemDefault(),
|
||||||
)
|
)
|
||||||
.takeIf { dateEpochMillis > 0L },
|
.takeIf { dateEpochMillis != 0L },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package eu.kanade.presentation.components
|
package eu.kanade.presentation.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.DpOffset
|
||||||
import eu.kanade.presentation.manga.DownloadAction
|
import eu.kanade.presentation.manga.DownloadAction
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
@@ -15,7 +17,41 @@ fun DownloadDropdownMenu(
|
|||||||
expanded: Boolean,
|
expanded: Boolean,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onDownloadClicked: (DownloadAction) -> Unit,
|
onDownloadClicked: (DownloadAction) -> Unit,
|
||||||
|
offset: DpOffset? = null,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
if (offset != null) {
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
modifier = modifier,
|
||||||
|
offset = offset,
|
||||||
|
content = {
|
||||||
|
DownloadDropdownMenuItems(
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
onDownloadClicked = onDownloadClicked,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
modifier = modifier,
|
||||||
|
content = {
|
||||||
|
DownloadDropdownMenuItems(
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
onDownloadClicked = onDownloadClicked,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ColumnScope.DownloadDropdownMenuItems(
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
onDownloadClicked: (DownloadAction) -> Unit,
|
||||||
) {
|
) {
|
||||||
val options = persistentListOf(
|
val options = persistentListOf(
|
||||||
DownloadAction.NEXT_1_CHAPTER to pluralStringResource(MR.plurals.download_amount, 1, 1),
|
DownloadAction.NEXT_1_CHAPTER to pluralStringResource(MR.plurals.download_amount, 1, 1),
|
||||||
@@ -25,19 +61,13 @@ fun DownloadDropdownMenu(
|
|||||||
DownloadAction.UNREAD_CHAPTERS to stringResource(MR.strings.download_unread),
|
DownloadAction.UNREAD_CHAPTERS to stringResource(MR.strings.download_unread),
|
||||||
)
|
)
|
||||||
|
|
||||||
DropdownMenu(
|
options.map { (downloadAction, string) ->
|
||||||
expanded = expanded,
|
DropdownMenuItem(
|
||||||
onDismissRequest = onDismissRequest,
|
text = { Text(text = string) },
|
||||||
modifier = modifier,
|
onClick = {
|
||||||
) {
|
onDownloadClicked(downloadAction)
|
||||||
options.map { (downloadAction, string) ->
|
onDismissRequest()
|
||||||
DropdownMenuItem(
|
},
|
||||||
text = { Text(text = string) },
|
)
|
||||||
onClick = {
|
|
||||||
onDownloadClicked(downloadAction)
|
|
||||||
onDismissRequest()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ fun HistoryScreen(
|
|||||||
onSearchQueryChange: (String?) -> Unit,
|
onSearchQueryChange: (String?) -> Unit,
|
||||||
onClickCover: (mangaId: Long) -> Unit,
|
onClickCover: (mangaId: Long) -> Unit,
|
||||||
onClickResume: (mangaId: Long, chapterId: Long) -> Unit,
|
onClickResume: (mangaId: Long, chapterId: Long) -> Unit,
|
||||||
|
onClickFavorite: (mangaId: Long) -> Unit,
|
||||||
onDialogChange: (HistoryScreenModel.Dialog?) -> Unit,
|
onDialogChange: (HistoryScreenModel.Dialog?) -> Unit,
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
@@ -85,6 +86,7 @@ fun HistoryScreen(
|
|||||||
onClickCover = { history -> onClickCover(history.mangaId) },
|
onClickCover = { history -> onClickCover(history.mangaId) },
|
||||||
onClickResume = { history -> onClickResume(history.mangaId, history.chapterId) },
|
onClickResume = { history -> onClickResume(history.mangaId, history.chapterId) },
|
||||||
onClickDelete = { item -> onDialogChange(HistoryScreenModel.Dialog.Delete(item)) },
|
onClickDelete = { item -> onDialogChange(HistoryScreenModel.Dialog.Delete(item)) },
|
||||||
|
onClickFavorite = { history -> onClickFavorite(history.mangaId) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,6 +100,7 @@ private fun HistoryScreenContent(
|
|||||||
onClickCover: (HistoryWithRelations) -> Unit,
|
onClickCover: (HistoryWithRelations) -> Unit,
|
||||||
onClickResume: (HistoryWithRelations) -> Unit,
|
onClickResume: (HistoryWithRelations) -> Unit,
|
||||||
onClickDelete: (HistoryWithRelations) -> Unit,
|
onClickDelete: (HistoryWithRelations) -> Unit,
|
||||||
|
onClickFavorite: (HistoryWithRelations) -> Unit,
|
||||||
) {
|
) {
|
||||||
FastScrollLazyColumn(
|
FastScrollLazyColumn(
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
@@ -127,6 +130,7 @@ private fun HistoryScreenContent(
|
|||||||
onClickCover = { onClickCover(value) },
|
onClickCover = { onClickCover(value) },
|
||||||
onClickResume = { onClickResume(value) },
|
onClickResume = { onClickResume(value) },
|
||||||
onClickDelete = { onClickDelete(value) },
|
onClickDelete = { onClickDelete(value) },
|
||||||
|
onClickFavorite = { onClickFavorite(value) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,6 +157,7 @@ internal fun HistoryScreenPreviews(
|
|||||||
onClickCover = {},
|
onClickCover = {},
|
||||||
onClickResume = { _, _ -> run {} },
|
onClickResume = { _, _ -> run {} },
|
||||||
onDialogChange = {},
|
onDialogChange = {},
|
||||||
|
onClickFavorite = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.height
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Delete
|
import androidx.compose.material.icons.outlined.Delete
|
||||||
|
import androidx.compose.material.icons.outlined.FavoriteBorder
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -39,6 +40,7 @@ fun HistoryItem(
|
|||||||
onClickCover: () -> Unit,
|
onClickCover: () -> Unit,
|
||||||
onClickResume: () -> Unit,
|
onClickResume: () -> Unit,
|
||||||
onClickDelete: () -> Unit,
|
onClickDelete: () -> Unit,
|
||||||
|
onClickFavorite: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
@@ -82,6 +84,16 @@ fun HistoryItem(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!history.coverData.isMangaFavorite) {
|
||||||
|
IconButton(onClick = onClickFavorite) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.FavoriteBorder,
|
||||||
|
contentDescription = stringResource(MR.strings.add_to_library),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurface,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IconButton(onClick = onClickDelete) {
|
IconButton(onClick = onClickDelete) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.Delete,
|
imageVector = Icons.Outlined.Delete,
|
||||||
@@ -105,6 +117,7 @@ private fun HistoryItemPreviews(
|
|||||||
onClickCover = {},
|
onClickCover = {},
|
||||||
onClickResume = {},
|
onClickResume = {},
|
||||||
onClickDelete = {},
|
onClickDelete = {},
|
||||||
|
onClickFavorite = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import androidx.compose.foundation.verticalScroll
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Refresh
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
import androidx.compose.material3.FilterChip
|
import androidx.compose.material3.FilterChip
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
@@ -309,15 +310,16 @@ private fun ColumnScope.DisplayPage(
|
|||||||
|
|
||||||
val columns by columnPreference.collectAsState()
|
val columns by columnPreference.collectAsState()
|
||||||
SliderItem(
|
SliderItem(
|
||||||
label = stringResource(MR.strings.pref_library_columns),
|
|
||||||
max = 10,
|
|
||||||
value = columns,
|
value = columns,
|
||||||
valueText = if (columns > 0) {
|
valueRange = 0..10,
|
||||||
stringResource(MR.strings.pref_library_columns_per_row, columns)
|
label = stringResource(MR.strings.pref_library_columns),
|
||||||
|
valueString = if (columns > 0) {
|
||||||
|
columns.toString()
|
||||||
} else {
|
} else {
|
||||||
stringResource(MR.strings.label_default)
|
stringResource(MR.strings.label_auto)
|
||||||
},
|
},
|
||||||
onChange = columnPreference::set,
|
onChange = columnPreference::set,
|
||||||
|
pillColor = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,6 +328,10 @@ private fun ColumnScope.DisplayPage(
|
|||||||
label = stringResource(MR.strings.action_display_download_badge),
|
label = stringResource(MR.strings.action_display_download_badge),
|
||||||
pref = screenModel.libraryPreferences.downloadBadge(),
|
pref = screenModel.libraryPreferences.downloadBadge(),
|
||||||
)
|
)
|
||||||
|
CheckboxItem(
|
||||||
|
label = stringResource(MR.strings.action_display_unread_badge),
|
||||||
|
pref = screenModel.libraryPreferences.unreadBadge(),
|
||||||
|
)
|
||||||
CheckboxItem(
|
CheckboxItem(
|
||||||
label = stringResource(MR.strings.action_display_local_badge),
|
label = stringResource(MR.strings.action_display_local_badge),
|
||||||
pref = screenModel.libraryPreferences.localBadge(),
|
pref = screenModel.libraryPreferences.localBadge(),
|
||||||
|
|||||||
+2
-3
@@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.lazy.grid.items
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.util.fastAny
|
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
||||||
import tachiyomi.domain.library.model.LibraryManga
|
import tachiyomi.domain.library.model.LibraryManga
|
||||||
import tachiyomi.domain.manga.model.MangaCover
|
import tachiyomi.domain.manga.model.MangaCover
|
||||||
@@ -15,7 +14,7 @@ internal fun LibraryComfortableGrid(
|
|||||||
items: List<LibraryItem>,
|
items: List<LibraryItem>,
|
||||||
columns: Int,
|
columns: Int,
|
||||||
contentPadding: PaddingValues,
|
contentPadding: PaddingValues,
|
||||||
selection: List<LibraryManga>,
|
selection: Set<Long>,
|
||||||
onClick: (LibraryManga) -> Unit,
|
onClick: (LibraryManga) -> Unit,
|
||||||
onLongClick: (LibraryManga) -> Unit,
|
onLongClick: (LibraryManga) -> Unit,
|
||||||
onClickContinueReading: ((LibraryManga) -> Unit)?,
|
onClickContinueReading: ((LibraryManga) -> Unit)?,
|
||||||
@@ -35,7 +34,7 @@ internal fun LibraryComfortableGrid(
|
|||||||
) { libraryItem ->
|
) { libraryItem ->
|
||||||
val manga = libraryItem.libraryManga.manga
|
val manga = libraryItem.libraryManga.manga
|
||||||
MangaComfortableGridItem(
|
MangaComfortableGridItem(
|
||||||
isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id },
|
isSelected = manga.id in selection,
|
||||||
title = manga.title,
|
title = manga.title,
|
||||||
coverData = MangaCover(
|
coverData = MangaCover(
|
||||||
mangaId = manga.id,
|
mangaId = manga.id,
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.lazy.grid.items
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.util.fastAny
|
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
||||||
import tachiyomi.domain.library.model.LibraryManga
|
import tachiyomi.domain.library.model.LibraryManga
|
||||||
import tachiyomi.domain.manga.model.MangaCover
|
import tachiyomi.domain.manga.model.MangaCover
|
||||||
@@ -16,7 +15,7 @@ internal fun LibraryCompactGrid(
|
|||||||
showTitle: Boolean,
|
showTitle: Boolean,
|
||||||
columns: Int,
|
columns: Int,
|
||||||
contentPadding: PaddingValues,
|
contentPadding: PaddingValues,
|
||||||
selection: List<LibraryManga>,
|
selection: Set<Long>,
|
||||||
onClick: (LibraryManga) -> Unit,
|
onClick: (LibraryManga) -> Unit,
|
||||||
onLongClick: (LibraryManga) -> Unit,
|
onLongClick: (LibraryManga) -> Unit,
|
||||||
onClickContinueReading: ((LibraryManga) -> Unit)?,
|
onClickContinueReading: ((LibraryManga) -> Unit)?,
|
||||||
@@ -36,7 +35,7 @@ internal fun LibraryCompactGrid(
|
|||||||
) { libraryItem ->
|
) { libraryItem ->
|
||||||
val manga = libraryItem.libraryManga.manga
|
val manga = libraryItem.libraryManga.manga
|
||||||
MangaCompactGridItem(
|
MangaCompactGridItem(
|
||||||
isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id },
|
isSelected = manga.id in selection,
|
||||||
title = manga.title.takeIf { showTitle },
|
title = manga.title.takeIf { showTitle },
|
||||||
coverData = MangaCover(
|
coverData = MangaCover(
|
||||||
mangaId = manga.id,
|
mangaId = manga.id,
|
||||||
|
|||||||
@@ -29,22 +29,22 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
fun LibraryContent(
|
fun LibraryContent(
|
||||||
categories: List<Category>,
|
categories: List<Category>,
|
||||||
searchQuery: String?,
|
searchQuery: String?,
|
||||||
selection: List<LibraryManga>,
|
selection: Set<Long>,
|
||||||
contentPadding: PaddingValues,
|
contentPadding: PaddingValues,
|
||||||
currentPage: () -> Int,
|
currentPage: Int,
|
||||||
hasActiveFilters: Boolean,
|
hasActiveFilters: Boolean,
|
||||||
showPageTabs: Boolean,
|
showPageTabs: Boolean,
|
||||||
onChangeCurrentPage: (Int) -> Unit,
|
onChangeCurrentPage: (Int) -> Unit,
|
||||||
onMangaClicked: (Long) -> Unit,
|
onClickManga: (Long) -> Unit,
|
||||||
onContinueReadingClicked: ((LibraryManga) -> Unit)?,
|
onContinueReadingClicked: ((LibraryManga) -> Unit)?,
|
||||||
onToggleSelection: (LibraryManga) -> Unit,
|
onToggleSelection: (Category, LibraryManga) -> Unit,
|
||||||
onToggleRangeSelection: (LibraryManga) -> Unit,
|
onToggleRangeSelection: (Category, LibraryManga) -> Unit,
|
||||||
onRefresh: (Category?) -> Boolean,
|
onRefresh: () -> Boolean,
|
||||||
onGlobalSearchClicked: () -> Unit,
|
onGlobalSearchClicked: () -> Unit,
|
||||||
getNumberOfMangaForCategory: (Category) -> Int?,
|
getItemCountForCategory: (Category) -> Int?,
|
||||||
getDisplayMode: (Int) -> PreferenceMutableState<LibraryDisplayMode>,
|
getDisplayMode: (Int) -> PreferenceMutableState<LibraryDisplayMode>,
|
||||||
getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>,
|
getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>,
|
||||||
getLibraryForPage: (Int) -> List<LibraryItem>,
|
getItemsForCategory: (Category) -> List<LibraryItem>,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(
|
modifier = Modifier.padding(
|
||||||
@@ -53,15 +53,14 @@ fun LibraryContent(
|
|||||||
end = contentPadding.calculateEndPadding(LocalLayoutDirection.current),
|
end = contentPadding.calculateEndPadding(LocalLayoutDirection.current),
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
// SY -->
|
val coercedCurrentPage = remember(categories, currentPage) { currentPage.coerceIn(0, categories.lastIndex) }
|
||||||
val coercedCurrentPage = remember(categories) { currentPage().coerceIn(0, categories.lastIndex) }
|
|
||||||
val pagerState = rememberPagerState(coercedCurrentPage) { categories.size }
|
|
||||||
// SY <--
|
// SY <--
|
||||||
|
val pagerState = rememberPagerState(coercedCurrentPage) { categories.size }
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
var isRefreshing by remember(pagerState.currentPage) { mutableStateOf(false) }
|
var isRefreshing by remember(pagerState.currentPage) { mutableStateOf(false) }
|
||||||
|
|
||||||
if (showPageTabs && categories.size > 1) {
|
if (showPageTabs && categories.isNotEmpty() && (categories.size > 1 || !categories.first().isSystemCategory)) {
|
||||||
LaunchedEffect(categories) {
|
LaunchedEffect(categories) {
|
||||||
if (categories.size <= pagerState.currentPage) {
|
if (categories.size <= pagerState.currentPage) {
|
||||||
pagerState.scrollToPage(categories.size - 1)
|
pagerState.scrollToPage(categories.size - 1)
|
||||||
@@ -70,23 +69,20 @@ fun LibraryContent(
|
|||||||
LibraryTabs(
|
LibraryTabs(
|
||||||
categories = categories,
|
categories = categories,
|
||||||
pagerState = pagerState,
|
pagerState = pagerState,
|
||||||
getNumberOfMangaForCategory = getNumberOfMangaForCategory,
|
getItemCountForCategory = getItemCountForCategory,
|
||||||
) { scope.launch { pagerState.animateScrollToPage(it) } }
|
onTabItemClick = {
|
||||||
}
|
scope.launch {
|
||||||
|
pagerState.animateScrollToPage(it)
|
||||||
val notSelectionMode = selection.isEmpty()
|
}
|
||||||
val onClickManga = { manga: LibraryManga ->
|
},
|
||||||
if (notSelectionMode) {
|
)
|
||||||
onMangaClicked(manga.manga.id)
|
|
||||||
} else {
|
|
||||||
onToggleSelection(manga)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PullRefresh(
|
PullRefresh(
|
||||||
refreshing = isRefreshing,
|
refreshing = isRefreshing,
|
||||||
|
enabled = selection.isEmpty(),
|
||||||
onRefresh = {
|
onRefresh = {
|
||||||
val started = onRefresh(categories.getOrNull(currentPage()) ?: return@PullRefresh)
|
val started = onRefresh()
|
||||||
if (!started) return@PullRefresh
|
if (!started) return@PullRefresh
|
||||||
scope.launch {
|
scope.launch {
|
||||||
// Fake refresh status but hide it after a second as it's a long running task
|
// Fake refresh status but hide it after a second as it's a long running task
|
||||||
@@ -95,19 +91,25 @@ fun LibraryContent(
|
|||||||
isRefreshing = false
|
isRefreshing = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
enabled = notSelectionMode,
|
|
||||||
) {
|
) {
|
||||||
LibraryPager(
|
LibraryPager(
|
||||||
state = pagerState,
|
state = pagerState,
|
||||||
contentPadding = PaddingValues(bottom = contentPadding.calculateBottomPadding()),
|
contentPadding = PaddingValues(bottom = contentPadding.calculateBottomPadding()),
|
||||||
hasActiveFilters = hasActiveFilters,
|
hasActiveFilters = hasActiveFilters,
|
||||||
selectedManga = selection,
|
selection = selection,
|
||||||
searchQuery = searchQuery,
|
searchQuery = searchQuery,
|
||||||
onGlobalSearchClicked = onGlobalSearchClicked,
|
onGlobalSearchClicked = onGlobalSearchClicked,
|
||||||
|
getCategoryForPage = { page -> categories[page] },
|
||||||
getDisplayMode = getDisplayMode,
|
getDisplayMode = getDisplayMode,
|
||||||
getColumnsForOrientation = getColumnsForOrientation,
|
getColumnsForOrientation = getColumnsForOrientation,
|
||||||
getLibraryForPage = getLibraryForPage,
|
getItemsForCategory = getItemsForCategory,
|
||||||
onClickManga = onClickManga,
|
onClickManga = { category, manga ->
|
||||||
|
if (selection.isNotEmpty()) {
|
||||||
|
onToggleSelection(category, manga)
|
||||||
|
} else {
|
||||||
|
onClickManga(manga.manga.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
onLongClickManga = onToggleRangeSelection,
|
onLongClickManga = onToggleRangeSelection,
|
||||||
onClickContinueReading = onContinueReadingClicked,
|
onClickContinueReading = onContinueReadingClicked,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import androidx.compose.foundation.lazy.items
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.util.fastAny
|
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
||||||
import tachiyomi.domain.library.model.LibraryManga
|
import tachiyomi.domain.library.model.LibraryManga
|
||||||
import tachiyomi.domain.manga.model.MangaCover
|
import tachiyomi.domain.manga.model.MangaCover
|
||||||
@@ -18,7 +17,7 @@ import tachiyomi.presentation.core.util.plus
|
|||||||
internal fun LibraryList(
|
internal fun LibraryList(
|
||||||
items: List<LibraryItem>,
|
items: List<LibraryItem>,
|
||||||
contentPadding: PaddingValues,
|
contentPadding: PaddingValues,
|
||||||
selection: List<LibraryManga>,
|
selection: Set<Long>,
|
||||||
onClick: (LibraryManga) -> Unit,
|
onClick: (LibraryManga) -> Unit,
|
||||||
onLongClick: (LibraryManga) -> Unit,
|
onLongClick: (LibraryManga) -> Unit,
|
||||||
onClickContinueReading: ((LibraryManga) -> Unit)?,
|
onClickContinueReading: ((LibraryManga) -> Unit)?,
|
||||||
@@ -45,7 +44,7 @@ internal fun LibraryList(
|
|||||||
) { libraryItem ->
|
) { libraryItem ->
|
||||||
val manga = libraryItem.libraryManga.manga
|
val manga = libraryItem.libraryManga.manga
|
||||||
MangaListItem(
|
MangaListItem(
|
||||||
isSelected = selection.fastAny { it.id == libraryItem.libraryManga.id },
|
isSelected = manga.id in selection,
|
||||||
title = manga.title,
|
title = manga.title,
|
||||||
coverData = MangaCover(
|
coverData = MangaCover(
|
||||||
mangaId = manga.id,
|
mangaId = manga.id,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import androidx.compose.ui.platform.LocalConfiguration
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.kanade.core.preference.PreferenceMutableState
|
import eu.kanade.core.preference.PreferenceMutableState
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
||||||
|
import tachiyomi.domain.category.model.Category
|
||||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
import tachiyomi.domain.library.model.LibraryDisplayMode
|
||||||
import tachiyomi.domain.library.model.LibraryManga
|
import tachiyomi.domain.library.model.LibraryManga
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
@@ -31,14 +32,15 @@ fun LibraryPager(
|
|||||||
state: PagerState,
|
state: PagerState,
|
||||||
contentPadding: PaddingValues,
|
contentPadding: PaddingValues,
|
||||||
hasActiveFilters: Boolean,
|
hasActiveFilters: Boolean,
|
||||||
selectedManga: List<LibraryManga>,
|
selection: Set<Long>,
|
||||||
searchQuery: String?,
|
searchQuery: String?,
|
||||||
onGlobalSearchClicked: () -> Unit,
|
onGlobalSearchClicked: () -> Unit,
|
||||||
|
getCategoryForPage: (Int) -> Category,
|
||||||
getDisplayMode: (Int) -> PreferenceMutableState<LibraryDisplayMode>,
|
getDisplayMode: (Int) -> PreferenceMutableState<LibraryDisplayMode>,
|
||||||
getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>,
|
getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>,
|
||||||
getLibraryForPage: (Int) -> List<LibraryItem>,
|
getItemsForCategory: (Category) -> List<LibraryItem>,
|
||||||
onClickManga: (LibraryManga) -> Unit,
|
onClickManga: (Category, LibraryManga) -> Unit,
|
||||||
onLongClickManga: (LibraryManga) -> Unit,
|
onLongClickManga: (Category, LibraryManga) -> Unit,
|
||||||
onClickContinueReading: ((LibraryManga) -> Unit)?,
|
onClickContinueReading: ((LibraryManga) -> Unit)?,
|
||||||
) {
|
) {
|
||||||
HorizontalPager(
|
HorizontalPager(
|
||||||
@@ -50,9 +52,10 @@ fun LibraryPager(
|
|||||||
// To make sure only one offscreen page is being composed
|
// To make sure only one offscreen page is being composed
|
||||||
return@HorizontalPager
|
return@HorizontalPager
|
||||||
}
|
}
|
||||||
val library = getLibraryForPage(page)
|
val category = getCategoryForPage(page)
|
||||||
|
val items = getItemsForCategory(category)
|
||||||
|
|
||||||
if (library.isEmpty()) {
|
if (items.isEmpty()) {
|
||||||
LibraryPagerEmptyScreen(
|
LibraryPagerEmptyScreen(
|
||||||
searchQuery = searchQuery,
|
searchQuery = searchQuery,
|
||||||
hasActiveFilters = hasActiveFilters,
|
hasActiveFilters = hasActiveFilters,
|
||||||
@@ -72,12 +75,15 @@ fun LibraryPager(
|
|||||||
remember { mutableIntStateOf(0) }
|
remember { mutableIntStateOf(0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val onClickManga: (LibraryManga) -> Unit = { onClickManga(category, it) }
|
||||||
|
val onLongClickManga: (LibraryManga) -> Unit = { onLongClickManga(category, it) }
|
||||||
|
|
||||||
when (displayMode) {
|
when (displayMode) {
|
||||||
LibraryDisplayMode.List -> {
|
LibraryDisplayMode.List -> {
|
||||||
LibraryList(
|
LibraryList(
|
||||||
items = library,
|
items = items,
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
selection = selectedManga,
|
selection = selection,
|
||||||
onClick = onClickManga,
|
onClick = onClickManga,
|
||||||
onLongClick = onLongClickManga,
|
onLongClick = onLongClickManga,
|
||||||
onClickContinueReading = onClickContinueReading,
|
onClickContinueReading = onClickContinueReading,
|
||||||
@@ -87,11 +93,11 @@ fun LibraryPager(
|
|||||||
}
|
}
|
||||||
LibraryDisplayMode.CompactGrid, LibraryDisplayMode.CoverOnlyGrid -> {
|
LibraryDisplayMode.CompactGrid, LibraryDisplayMode.CoverOnlyGrid -> {
|
||||||
LibraryCompactGrid(
|
LibraryCompactGrid(
|
||||||
items = library,
|
items = items,
|
||||||
showTitle = displayMode is LibraryDisplayMode.CompactGrid,
|
showTitle = displayMode is LibraryDisplayMode.CompactGrid,
|
||||||
columns = columns,
|
columns = columns,
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
selection = selectedManga,
|
selection = selection,
|
||||||
onClick = onClickManga,
|
onClick = onClickManga,
|
||||||
onLongClick = onLongClickManga,
|
onLongClick = onLongClickManga,
|
||||||
onClickContinueReading = onClickContinueReading,
|
onClickContinueReading = onClickContinueReading,
|
||||||
@@ -101,10 +107,10 @@ fun LibraryPager(
|
|||||||
}
|
}
|
||||||
LibraryDisplayMode.ComfortableGrid -> {
|
LibraryDisplayMode.ComfortableGrid -> {
|
||||||
LibraryComfortableGrid(
|
LibraryComfortableGrid(
|
||||||
items = library,
|
items = items,
|
||||||
columns = columns,
|
columns = columns,
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
selection = selectedManga,
|
selection = selection,
|
||||||
onClick = onClickManga,
|
onClick = onClickManga,
|
||||||
onLongClick = onLongClickManga,
|
onLongClick = onLongClickManga,
|
||||||
onClickContinueReading = onClickContinueReading,
|
onClickContinueReading = onClickContinueReading,
|
||||||
|
|||||||
@@ -18,13 +18,11 @@ import tachiyomi.presentation.core.components.material.TabText
|
|||||||
internal fun LibraryTabs(
|
internal fun LibraryTabs(
|
||||||
categories: List<Category>,
|
categories: List<Category>,
|
||||||
pagerState: PagerState,
|
pagerState: PagerState,
|
||||||
getNumberOfMangaForCategory: (Category) -> Int?,
|
getItemCountForCategory: (Category) -> Int?,
|
||||||
onTabItemClick: (Int) -> Unit,
|
onTabItemClick: (Int) -> Unit,
|
||||||
) {
|
) {
|
||||||
val currentPageIndex = pagerState.currentPage.coerceAtMost(categories.lastIndex)
|
val currentPageIndex = pagerState.currentPage.coerceAtMost(categories.lastIndex)
|
||||||
Column(
|
Column(modifier = Modifier.zIndex(2f)) {
|
||||||
modifier = Modifier.zIndex(1f),
|
|
||||||
) {
|
|
||||||
PrimaryScrollableTabRow(
|
PrimaryScrollableTabRow(
|
||||||
selectedTabIndex = currentPageIndex,
|
selectedTabIndex = currentPageIndex,
|
||||||
edgePadding = 0.dp,
|
edgePadding = 0.dp,
|
||||||
@@ -39,7 +37,7 @@ internal fun LibraryTabs(
|
|||||||
text = {
|
text = {
|
||||||
TabText(
|
TabText(
|
||||||
text = category.visualName,
|
text = category.visualName,
|
||||||
badgeCount = getNumberOfMangaForCategory(category),
|
badgeCount = getItemCountForCategory(category),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
unselectedContentColor = MaterialTheme.colorScheme.onSurface,
|
unselectedContentColor = MaterialTheme.colorScheme.onSurface,
|
||||||
|
|||||||
@@ -21,13 +21,14 @@ import androidx.compose.material3.TextButton
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import eu.kanade.domain.base.BasePreferences
|
||||||
import eu.kanade.domain.manga.model.downloadedFilter
|
import eu.kanade.domain.manga.model.downloadedFilter
|
||||||
import eu.kanade.domain.manga.model.forceDownloaded
|
|
||||||
import eu.kanade.presentation.components.TabbedDialog
|
import eu.kanade.presentation.components.TabbedDialog
|
||||||
import eu.kanade.presentation.components.TabbedDialogPaddings
|
import eu.kanade.presentation.components.TabbedDialogPaddings
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
@@ -40,6 +41,8 @@ import tachiyomi.presentation.core.components.SortItem
|
|||||||
import tachiyomi.presentation.core.components.TriStateItem
|
import tachiyomi.presentation.core.components.TriStateItem
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.theme.active
|
import tachiyomi.presentation.core.theme.active
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChapterSettingsDialog(
|
fun ChapterSettingsDialog(
|
||||||
@@ -63,6 +66,8 @@ fun ChapterSettingsDialog(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val downloadedOnly = remember { Injekt.get<BasePreferences>().downloadedOnly().get() }
|
||||||
|
|
||||||
TabbedDialog(
|
TabbedDialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
tabTitles = persistentListOf(
|
tabTitles = persistentListOf(
|
||||||
@@ -97,7 +102,7 @@ fun ChapterSettingsDialog(
|
|||||||
FilterPage(
|
FilterPage(
|
||||||
downloadFilter = manga?.downloadedFilter ?: TriState.DISABLED,
|
downloadFilter = manga?.downloadedFilter ?: TriState.DISABLED,
|
||||||
onDownloadFilterChanged = onDownloadFilterChanged
|
onDownloadFilterChanged = onDownloadFilterChanged
|
||||||
.takeUnless { manga?.forceDownloaded() == true },
|
.takeUnless { downloadedOnly },
|
||||||
unreadFilter = manga?.unreadFilter ?: TriState.DISABLED,
|
unreadFilter = manga?.unreadFilter ?: TriState.DISABLED,
|
||||||
onUnreadFilterChanged = onUnreadFilterChanged,
|
onUnreadFilterChanged = onUnreadFilterChanged,
|
||||||
bookmarkedFilter = manga?.bookmarkedFilter ?: TriState.DISABLED,
|
bookmarkedFilter = manga?.bookmarkedFilter ?: TriState.DISABLED,
|
||||||
|
|||||||
@@ -1,44 +1,95 @@
|
|||||||
package eu.kanade.presentation.manga
|
package eu.kanade.presentation.manga
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.sizeIn
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Brush
|
||||||
|
import androidx.compose.material.icons.filled.PersonOutline
|
||||||
|
import androidx.compose.material.icons.filled.Warning
|
||||||
import androidx.compose.material.icons.outlined.Add
|
import androidx.compose.material.icons.outlined.Add
|
||||||
import androidx.compose.material.icons.outlined.Book
|
import androidx.compose.material.icons.outlined.AttachMoney
|
||||||
import androidx.compose.material.icons.outlined.SwapVert
|
import androidx.compose.material.icons.outlined.Block
|
||||||
|
import androidx.compose.material.icons.outlined.Close
|
||||||
|
import androidx.compose.material.icons.outlined.Done
|
||||||
|
import androidx.compose.material.icons.outlined.DoneAll
|
||||||
|
import androidx.compose.material.icons.outlined.Pause
|
||||||
|
import androidx.compose.material.icons.outlined.Schedule
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.Typography
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.text.TextMeasurer
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.rememberTextMeasurer
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.Constraints
|
||||||
|
import androidx.compose.ui.unit.Density
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.util.fastMaxOfOrNull
|
||||||
|
import coil3.request.ImageRequest
|
||||||
|
import coil3.request.crossfade
|
||||||
import eu.kanade.presentation.components.AdaptiveSheet
|
import eu.kanade.presentation.components.AdaptiveSheet
|
||||||
import eu.kanade.presentation.components.TabbedDialogPaddings
|
import eu.kanade.presentation.components.TabbedDialogPaddings
|
||||||
|
import eu.kanade.presentation.manga.components.MangaCover
|
||||||
import eu.kanade.presentation.more.settings.LocalPreferenceMinHeight
|
import eu.kanade.presentation.more.settings.LocalPreferenceMinHeight
|
||||||
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
||||||
|
import eu.kanade.tachiyomi.source.Source
|
||||||
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
|
import tachiyomi.domain.manga.model.Manga
|
||||||
|
import tachiyomi.domain.manga.model.MangaWithChapterCount
|
||||||
|
import tachiyomi.domain.source.model.StubSource
|
||||||
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.components.Badge
|
||||||
|
import tachiyomi.presentation.core.components.BadgeGroup
|
||||||
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
|
import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
import tachiyomi.presentation.core.util.secondaryItemAlpha
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DuplicateMangaDialog(
|
fun DuplicateMangaDialog(
|
||||||
|
duplicates: List<MangaWithChapterCount>,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onConfirm: () -> Unit,
|
onConfirm: () -> Unit,
|
||||||
onOpenManga: () -> Unit,
|
onOpenManga: (manga: Manga) -> Unit,
|
||||||
onMigrate: () -> Unit,
|
onMigrate: (manga: Manga) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
val sourceManager = remember { Injekt.get<SourceManager>() }
|
||||||
val minHeight = LocalPreferenceMinHeight.current
|
val minHeight = LocalPreferenceMinHeight.current
|
||||||
|
val horizontalPadding = PaddingValues(horizontal = TabbedDialogPaddings.Horizontal)
|
||||||
|
val horizontalPaddingModifier = Modifier.padding(horizontalPadding)
|
||||||
|
|
||||||
AdaptiveSheet(
|
AdaptiveSheet(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
@@ -46,81 +97,310 @@ fun DuplicateMangaDialog(
|
|||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(
|
.padding(vertical = TabbedDialogPaddings.Vertical)
|
||||||
vertical = TabbedDialogPaddings.Vertical,
|
.verticalScroll(rememberScrollState())
|
||||||
horizontal = TabbedDialogPaddings.Horizontal,
|
|
||||||
)
|
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.medium),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(TitlePadding),
|
text = stringResource(MR.strings.possible_duplicates_title),
|
||||||
text = stringResource(MR.strings.are_you_sure),
|
|
||||||
style = MaterialTheme.typography.headlineMedium,
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
|
modifier = Modifier
|
||||||
|
.then(horizontalPaddingModifier)
|
||||||
|
.padding(top = MaterialTheme.padding.small),
|
||||||
)
|
)
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(MR.strings.confirm_add_duplicate_manga),
|
text = stringResource(MR.strings.possible_duplicates_summary),
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
modifier = Modifier.then(horizontalPaddingModifier),
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(Modifier.height(PaddingSize))
|
LazyRow(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||||
TextPreferenceWidget(
|
modifier = Modifier.height(getMaximumMangaCardHeight(duplicates)),
|
||||||
title = stringResource(MR.strings.action_show_manga),
|
contentPadding = horizontalPadding,
|
||||||
icon = Icons.Outlined.Book,
|
|
||||||
onPreferenceClick = {
|
|
||||||
onDismissRequest()
|
|
||||||
onOpenManga()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
HorizontalDivider()
|
|
||||||
|
|
||||||
TextPreferenceWidget(
|
|
||||||
title = stringResource(MR.strings.action_migrate_duplicate),
|
|
||||||
icon = Icons.Outlined.SwapVert,
|
|
||||||
onPreferenceClick = {
|
|
||||||
onDismissRequest()
|
|
||||||
onMigrate()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
HorizontalDivider()
|
|
||||||
|
|
||||||
TextPreferenceWidget(
|
|
||||||
title = stringResource(MR.strings.action_add_anyway),
|
|
||||||
icon = Icons.Outlined.Add,
|
|
||||||
onPreferenceClick = {
|
|
||||||
onDismissRequest()
|
|
||||||
onConfirm()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.sizeIn(minHeight = minHeight)
|
|
||||||
.clickable { onDismissRequest.invoke() }
|
|
||||||
.padding(ButtonPadding)
|
|
||||||
.fillMaxWidth(),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
horizontalArrangement = Arrangement.Center,
|
|
||||||
) {
|
) {
|
||||||
OutlinedButton(onClick = onDismissRequest, modifier = Modifier.fillMaxWidth()) {
|
items(
|
||||||
Text(
|
items = duplicates,
|
||||||
modifier = Modifier
|
key = { it.manga.id },
|
||||||
.padding(vertical = 8.dp),
|
) {
|
||||||
text = stringResource(MR.strings.action_cancel),
|
DuplicateMangaListItem(
|
||||||
color = MaterialTheme.colorScheme.primary,
|
duplicate = it,
|
||||||
style = MaterialTheme.typography.titleLarge,
|
getSource = { sourceManager.getOrStub(it.manga.source) },
|
||||||
fontSize = 16.sp,
|
onMigrate = { onMigrate(it.manga) },
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
onOpenManga = { onOpenManga(it.manga) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Column(modifier = horizontalPaddingModifier) {
|
||||||
|
HorizontalDivider()
|
||||||
|
|
||||||
|
TextPreferenceWidget(
|
||||||
|
title = stringResource(MR.strings.action_add_anyway),
|
||||||
|
icon = Icons.Outlined.Add,
|
||||||
|
onPreferenceClick = {
|
||||||
|
onDismissRequest()
|
||||||
|
onConfirm()
|
||||||
|
},
|
||||||
|
modifier = Modifier.clip(CircleShape),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = onDismissRequest,
|
||||||
|
modifier = Modifier
|
||||||
|
.then(horizontalPaddingModifier)
|
||||||
|
.padding(bottom = MaterialTheme.padding.medium)
|
||||||
|
.heightIn(min = minHeight)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(vertical = MaterialTheme.padding.extraSmall),
|
||||||
|
text = stringResource(MR.strings.action_cancel),
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val PaddingSize = 16.dp
|
@Composable
|
||||||
|
private fun DuplicateMangaListItem(
|
||||||
|
duplicate: MangaWithChapterCount,
|
||||||
|
getSource: () -> Source,
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
onOpenManga: () -> Unit,
|
||||||
|
onMigrate: () -> Unit,
|
||||||
|
) {
|
||||||
|
val source = getSource()
|
||||||
|
val manga = duplicate.manga
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(MangaCardWidth)
|
||||||
|
.clip(MaterialTheme.shapes.medium)
|
||||||
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
|
.combinedClickable(
|
||||||
|
onLongClick = { onOpenManga() },
|
||||||
|
onClick = {
|
||||||
|
onDismissRequest()
|
||||||
|
onMigrate()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.padding(MaterialTheme.padding.small),
|
||||||
|
) {
|
||||||
|
Box {
|
||||||
|
MangaCover.Book(
|
||||||
|
data = ImageRequest.Builder(LocalContext.current)
|
||||||
|
.data(manga)
|
||||||
|
.crossfade(true)
|
||||||
|
.build(),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
BadgeGroup(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(4.dp)
|
||||||
|
.align(Alignment.TopStart),
|
||||||
|
) {
|
||||||
|
Badge(
|
||||||
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
|
textColor = MaterialTheme.colorScheme.onSecondary,
|
||||||
|
text = pluralStringResource(
|
||||||
|
MR.plurals.manga_num_chapters,
|
||||||
|
duplicate.chapterCount.toInt(),
|
||||||
|
duplicate.chapterCount,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val ButtonPadding = PaddingValues(top = 16.dp, bottom = 16.dp)
|
Spacer(modifier = Modifier.height(MaterialTheme.padding.extraSmall))
|
||||||
private val TitlePadding = PaddingValues(bottom = 16.dp, top = 8.dp)
|
|
||||||
|
Text(
|
||||||
|
text = manga.title,
|
||||||
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 2,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!manga.author.isNullOrBlank()) {
|
||||||
|
MangaDetailRow(
|
||||||
|
text = manga.author!!,
|
||||||
|
iconImageVector = Icons.Filled.PersonOutline,
|
||||||
|
maxLines = 2,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!manga.artist.isNullOrBlank() && manga.author != manga.artist) {
|
||||||
|
MangaDetailRow(
|
||||||
|
text = manga.artist!!,
|
||||||
|
iconImageVector = Icons.Filled.Brush,
|
||||||
|
maxLines = 2,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
MangaDetailRow(
|
||||||
|
text = when (manga.status) {
|
||||||
|
SManga.ONGOING.toLong() -> stringResource(MR.strings.ongoing)
|
||||||
|
SManga.COMPLETED.toLong() -> stringResource(MR.strings.completed)
|
||||||
|
SManga.LICENSED.toLong() -> stringResource(MR.strings.licensed)
|
||||||
|
SManga.PUBLISHING_FINISHED.toLong() -> stringResource(MR.strings.publishing_finished)
|
||||||
|
SManga.CANCELLED.toLong() -> stringResource(MR.strings.cancelled)
|
||||||
|
SManga.ON_HIATUS.toLong() -> stringResource(MR.strings.on_hiatus)
|
||||||
|
else -> stringResource(MR.strings.unknown)
|
||||||
|
},
|
||||||
|
iconImageVector = when (manga.status) {
|
||||||
|
SManga.ONGOING.toLong() -> Icons.Outlined.Schedule
|
||||||
|
SManga.COMPLETED.toLong() -> Icons.Outlined.DoneAll
|
||||||
|
SManga.LICENSED.toLong() -> Icons.Outlined.AttachMoney
|
||||||
|
SManga.PUBLISHING_FINISHED.toLong() -> Icons.Outlined.Done
|
||||||
|
SManga.CANCELLED.toLong() -> Icons.Outlined.Close
|
||||||
|
SManga.ON_HIATUS.toLong() -> Icons.Outlined.Pause
|
||||||
|
else -> Icons.Outlined.Block
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
) {
|
||||||
|
if (source is StubSource) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Warning,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(16.dp),
|
||||||
|
tint = MaterialTheme.colorScheme.error,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = source.name,
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MangaDetailRow(
|
||||||
|
text: String,
|
||||||
|
iconImageVector: ImageVector,
|
||||||
|
maxLines: Int = 1,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.secondaryItemAlpha()
|
||||||
|
.padding(top = MaterialTheme.padding.extraSmall),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = iconImageVector,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(MangaDetailsIconWidth),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = maxLines,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun getMaximumMangaCardHeight(duplicates: List<MangaWithChapterCount>): Dp {
|
||||||
|
val density = LocalDensity.current
|
||||||
|
val typography = MaterialTheme.typography
|
||||||
|
val textMeasurer = rememberTextMeasurer()
|
||||||
|
|
||||||
|
val smallPadding = with(density) { MaterialTheme.padding.small.roundToPx() }
|
||||||
|
val extraSmallPadding = with(density) { MaterialTheme.padding.extraSmall.roundToPx() }
|
||||||
|
|
||||||
|
val width = with(density) { MangaCardWidth.roundToPx() - (2 * smallPadding) }
|
||||||
|
val iconWidth = with(density) { MangaDetailsIconWidth.roundToPx() }
|
||||||
|
|
||||||
|
val coverHeight = width / MangaCover.Book.ratio
|
||||||
|
val constraints = Constraints(maxWidth = width)
|
||||||
|
val detailsConstraints = Constraints(maxWidth = width - iconWidth - extraSmallPadding)
|
||||||
|
|
||||||
|
return remember(
|
||||||
|
duplicates,
|
||||||
|
density,
|
||||||
|
typography,
|
||||||
|
textMeasurer,
|
||||||
|
smallPadding,
|
||||||
|
extraSmallPadding,
|
||||||
|
coverHeight,
|
||||||
|
constraints,
|
||||||
|
detailsConstraints,
|
||||||
|
) {
|
||||||
|
duplicates.fastMaxOfOrNull {
|
||||||
|
calculateMangaCardHeight(
|
||||||
|
manga = it.manga,
|
||||||
|
density = density,
|
||||||
|
typography = typography,
|
||||||
|
textMeasurer = textMeasurer,
|
||||||
|
smallPadding = smallPadding,
|
||||||
|
extraSmallPadding = extraSmallPadding,
|
||||||
|
coverHeight = coverHeight,
|
||||||
|
constraints = constraints,
|
||||||
|
detailsConstraints = detailsConstraints,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
?: 0.dp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun calculateMangaCardHeight(
|
||||||
|
manga: Manga,
|
||||||
|
density: Density,
|
||||||
|
typography: Typography,
|
||||||
|
textMeasurer: TextMeasurer,
|
||||||
|
smallPadding: Int,
|
||||||
|
extraSmallPadding: Int,
|
||||||
|
coverHeight: Float,
|
||||||
|
constraints: Constraints,
|
||||||
|
detailsConstraints: Constraints,
|
||||||
|
): Dp {
|
||||||
|
val titleHeight = textMeasurer.measureHeight(manga.title, typography.titleSmall, 2, constraints)
|
||||||
|
val authorHeight = if (!manga.author.isNullOrBlank()) {
|
||||||
|
textMeasurer.measureHeight(manga.author!!, typography.bodySmall, 2, detailsConstraints)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
val artistHeight = if (!manga.artist.isNullOrBlank() && manga.author != manga.artist) {
|
||||||
|
textMeasurer.measureHeight(manga.artist!!, typography.bodySmall, 2, detailsConstraints)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
val statusHeight = textMeasurer.measureHeight("", typography.bodySmall, 2, detailsConstraints)
|
||||||
|
val sourceHeight = textMeasurer.measureHeight("", typography.labelSmall, 1, constraints)
|
||||||
|
|
||||||
|
val totalHeight = coverHeight + titleHeight + authorHeight + artistHeight + statusHeight + sourceHeight
|
||||||
|
return with(density) { ((2 * smallPadding) + totalHeight + (5 * extraSmallPadding)).toDp() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TextMeasurer.measureHeight(
|
||||||
|
text: String,
|
||||||
|
style: TextStyle,
|
||||||
|
maxLines: Int,
|
||||||
|
constraints: Constraints,
|
||||||
|
): Int = measure(
|
||||||
|
text = text,
|
||||||
|
style = style,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = maxLines,
|
||||||
|
constraints = constraints,
|
||||||
|
)
|
||||||
|
.size
|
||||||
|
.height
|
||||||
|
|
||||||
|
private val MangaCardWidth = 150.dp
|
||||||
|
private val MangaDetailsIconWidth = 16.dp
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package eu.kanade.presentation.manga
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||||
|
import androidx.compose.foundation.layout.imePadding
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import eu.kanade.presentation.components.AppBar
|
||||||
|
import eu.kanade.presentation.components.AppBarTitle
|
||||||
|
import eu.kanade.presentation.manga.components.MangaNotesTextArea
|
||||||
|
import eu.kanade.tachiyomi.ui.manga.notes.MangaNotesScreen
|
||||||
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MangaNotesScreen(
|
||||||
|
state: MangaNotesScreen.State,
|
||||||
|
navigateUp: () -> Unit,
|
||||||
|
onUpdate: (String) -> Unit,
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
|
topBar = { topBarScrollBehavior ->
|
||||||
|
AppBar(
|
||||||
|
titleContent = {
|
||||||
|
AppBarTitle(
|
||||||
|
title = stringResource(MR.strings.action_edit_notes),
|
||||||
|
subtitle = state.manga.title,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
navigateUp = navigateUp,
|
||||||
|
scrollBehavior = topBarScrollBehavior,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) { contentPadding ->
|
||||||
|
MangaNotesTextArea(
|
||||||
|
state = state,
|
||||||
|
onUpdate = onUpdate,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(contentPadding)
|
||||||
|
.consumeWindowInsets(contentPadding)
|
||||||
|
.imePadding(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,6 +69,7 @@ import eu.kanade.tachiyomi.source.Source
|
|||||||
import eu.kanade.tachiyomi.source.getNameForMangaInfo
|
import eu.kanade.tachiyomi.source.getNameForMangaInfo
|
||||||
import eu.kanade.tachiyomi.source.online.MetadataSource
|
import eu.kanade.tachiyomi.source.online.MetadataSource
|
||||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||||
|
import eu.kanade.tachiyomi.source.online.all.Lanraragi
|
||||||
import eu.kanade.tachiyomi.source.online.all.MangaDex
|
import eu.kanade.tachiyomi.source.online.all.MangaDex
|
||||||
import eu.kanade.tachiyomi.source.online.all.NHentai
|
import eu.kanade.tachiyomi.source.online.all.NHentai
|
||||||
import eu.kanade.tachiyomi.source.online.english.EightMuses
|
import eu.kanade.tachiyomi.source.online.english.EightMuses
|
||||||
@@ -87,6 +88,7 @@ import exh.source.isEhBasedManga
|
|||||||
import exh.ui.metadata.adapters.EHentaiDescription
|
import exh.ui.metadata.adapters.EHentaiDescription
|
||||||
import exh.ui.metadata.adapters.EightMusesDescription
|
import exh.ui.metadata.adapters.EightMusesDescription
|
||||||
import exh.ui.metadata.adapters.HBrowseDescription
|
import exh.ui.metadata.adapters.HBrowseDescription
|
||||||
|
import exh.ui.metadata.adapters.LanraragiDescription
|
||||||
import exh.ui.metadata.adapters.MangaDexDescription
|
import exh.ui.metadata.adapters.MangaDexDescription
|
||||||
import exh.ui.metadata.adapters.NHentaiDescription
|
import exh.ui.metadata.adapters.NHentaiDescription
|
||||||
import exh.ui.metadata.adapters.PururinDescription
|
import exh.ui.metadata.adapters.PururinDescription
|
||||||
@@ -117,7 +119,7 @@ fun MangaScreen(
|
|||||||
isTabletUi: Boolean,
|
isTabletUi: Boolean,
|
||||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
onBackClicked: () -> Unit,
|
navigateUp: () -> Unit,
|
||||||
onChapterClicked: (Chapter) -> Unit,
|
onChapterClicked: (Chapter) -> Unit,
|
||||||
onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?,
|
onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?,
|
||||||
onAddToLibraryClicked: () -> Unit,
|
onAddToLibraryClicked: () -> Unit,
|
||||||
@@ -142,6 +144,7 @@ fun MangaScreen(
|
|||||||
onEditCategoryClicked: (() -> Unit)?,
|
onEditCategoryClicked: (() -> Unit)?,
|
||||||
onEditFetchIntervalClicked: (() -> Unit)?,
|
onEditFetchIntervalClicked: (() -> Unit)?,
|
||||||
onMigrateClicked: (() -> Unit)?,
|
onMigrateClicked: (() -> Unit)?,
|
||||||
|
onEditNotesClicked: () -> Unit,
|
||||||
// SY -->
|
// SY -->
|
||||||
onMetadataViewerClicked: () -> Unit,
|
onMetadataViewerClicked: () -> Unit,
|
||||||
onEditInfoClicked: () -> Unit,
|
onEditInfoClicked: () -> Unit,
|
||||||
@@ -182,7 +185,7 @@ fun MangaScreen(
|
|||||||
nextUpdate = nextUpdate,
|
nextUpdate = nextUpdate,
|
||||||
chapterSwipeStartAction = chapterSwipeStartAction,
|
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||||
onBackClicked = onBackClicked,
|
navigateUp = navigateUp,
|
||||||
onChapterClicked = onChapterClicked,
|
onChapterClicked = onChapterClicked,
|
||||||
onDownloadChapter = onDownloadChapter,
|
onDownloadChapter = onDownloadChapter,
|
||||||
onAddToLibraryClicked = onAddToLibraryClicked,
|
onAddToLibraryClicked = onAddToLibraryClicked,
|
||||||
@@ -201,6 +204,7 @@ fun MangaScreen(
|
|||||||
onEditCategoryClicked = onEditCategoryClicked,
|
onEditCategoryClicked = onEditCategoryClicked,
|
||||||
onEditIntervalClicked = onEditFetchIntervalClicked,
|
onEditIntervalClicked = onEditFetchIntervalClicked,
|
||||||
onMigrateClicked = onMigrateClicked,
|
onMigrateClicked = onMigrateClicked,
|
||||||
|
onEditNotesClicked = onEditNotesClicked,
|
||||||
// SY -->
|
// SY -->
|
||||||
onMetadataViewerClicked = onMetadataViewerClicked,
|
onMetadataViewerClicked = onMetadataViewerClicked,
|
||||||
onEditInfoClicked = onEditInfoClicked,
|
onEditInfoClicked = onEditInfoClicked,
|
||||||
@@ -228,7 +232,7 @@ fun MangaScreen(
|
|||||||
chapterSwipeStartAction = chapterSwipeStartAction,
|
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||||
nextUpdate = nextUpdate,
|
nextUpdate = nextUpdate,
|
||||||
onBackClicked = onBackClicked,
|
navigateUp = navigateUp,
|
||||||
onChapterClicked = onChapterClicked,
|
onChapterClicked = onChapterClicked,
|
||||||
onDownloadChapter = onDownloadChapter,
|
onDownloadChapter = onDownloadChapter,
|
||||||
onAddToLibraryClicked = onAddToLibraryClicked,
|
onAddToLibraryClicked = onAddToLibraryClicked,
|
||||||
@@ -247,6 +251,7 @@ fun MangaScreen(
|
|||||||
onEditCategoryClicked = onEditCategoryClicked,
|
onEditCategoryClicked = onEditCategoryClicked,
|
||||||
onEditIntervalClicked = onEditFetchIntervalClicked,
|
onEditIntervalClicked = onEditFetchIntervalClicked,
|
||||||
onMigrateClicked = onMigrateClicked,
|
onMigrateClicked = onMigrateClicked,
|
||||||
|
onEditNotesClicked = onEditNotesClicked,
|
||||||
// SY -->
|
// SY -->
|
||||||
onMetadataViewerClicked = onMetadataViewerClicked,
|
onMetadataViewerClicked = onMetadataViewerClicked,
|
||||||
onEditInfoClicked = onEditInfoClicked,
|
onEditInfoClicked = onEditInfoClicked,
|
||||||
@@ -277,7 +282,7 @@ private fun MangaScreenSmallImpl(
|
|||||||
nextUpdate: Instant?,
|
nextUpdate: Instant?,
|
||||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
onBackClicked: () -> Unit,
|
navigateUp: () -> Unit,
|
||||||
onChapterClicked: (Chapter) -> Unit,
|
onChapterClicked: (Chapter) -> Unit,
|
||||||
onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?,
|
onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?,
|
||||||
onAddToLibraryClicked: () -> Unit,
|
onAddToLibraryClicked: () -> Unit,
|
||||||
@@ -303,6 +308,7 @@ private fun MangaScreenSmallImpl(
|
|||||||
onEditCategoryClicked: (() -> Unit)?,
|
onEditCategoryClicked: (() -> Unit)?,
|
||||||
onEditIntervalClicked: (() -> Unit)?,
|
onEditIntervalClicked: (() -> Unit)?,
|
||||||
onMigrateClicked: (() -> Unit)?,
|
onMigrateClicked: (() -> Unit)?,
|
||||||
|
onEditNotesClicked: () -> Unit,
|
||||||
// SY -->
|
// SY -->
|
||||||
onMetadataViewerClicked: () -> Unit,
|
onMetadataViewerClicked: () -> Unit,
|
||||||
onEditInfoClicked: () -> Unit,
|
onEditInfoClicked: () -> Unit,
|
||||||
@@ -345,14 +351,9 @@ private fun MangaScreenSmallImpl(
|
|||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
val internalOnBackPressed = {
|
BackHandler(enabled = isAnySelected) {
|
||||||
if (isAnySelected) {
|
onAllChapterSelected(false)
|
||||||
onAllChapterSelected(false)
|
|
||||||
} else {
|
|
||||||
onBackClicked()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
BackHandler(onBack = internalOnBackPressed)
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
@@ -365,26 +366,25 @@ private fun MangaScreenSmallImpl(
|
|||||||
val isFirstItemScrolled by remember {
|
val isFirstItemScrolled by remember {
|
||||||
derivedStateOf { chapterListState.firstVisibleItemScrollOffset > 0 }
|
derivedStateOf { chapterListState.firstVisibleItemScrollOffset > 0 }
|
||||||
}
|
}
|
||||||
val animatedTitleAlpha by animateFloatAsState(
|
val titleAlpha by animateFloatAsState(
|
||||||
if (!isFirstItemVisible) 1f else 0f,
|
if (!isFirstItemVisible) 1f else 0f,
|
||||||
label = "Top Bar Title",
|
label = "Top Bar Title",
|
||||||
)
|
)
|
||||||
val animatedBgAlpha by animateFloatAsState(
|
val backgroundAlpha by animateFloatAsState(
|
||||||
if (!isFirstItemVisible || isFirstItemScrolled) 1f else 0f,
|
if (!isFirstItemVisible || isFirstItemScrolled) 1f else 0f,
|
||||||
label = "Top Bar Background",
|
label = "Top Bar Background",
|
||||||
)
|
)
|
||||||
MangaToolbar(
|
MangaToolbar(
|
||||||
title = state.manga.title,
|
title = state.manga.title,
|
||||||
titleAlphaProvider = { animatedTitleAlpha },
|
|
||||||
backgroundAlphaProvider = { animatedBgAlpha },
|
|
||||||
hasFilters = state.filterActive,
|
hasFilters = state.filterActive,
|
||||||
onBackClicked = internalOnBackPressed,
|
navigateUp = navigateUp,
|
||||||
onClickFilter = onFilterClicked,
|
onClickFilter = onFilterClicked,
|
||||||
onClickShare = onShareClicked,
|
onClickShare = onShareClicked,
|
||||||
onClickDownload = onDownloadActionClicked,
|
onClickDownload = onDownloadActionClicked,
|
||||||
onClickEditCategory = onEditCategoryClicked,
|
onClickEditCategory = onEditCategoryClicked,
|
||||||
onClickRefresh = onRefresh,
|
onClickRefresh = onRefresh,
|
||||||
onClickMigrate = onMigrateClicked,
|
onClickMigrate = onMigrateClicked,
|
||||||
|
onClickEditNotes = onEditNotesClicked,
|
||||||
// SY -->
|
// SY -->
|
||||||
onClickEditInfo = onEditInfoClicked.takeIf { state.manga.favorite },
|
onClickEditInfo = onEditInfoClicked.takeIf { state.manga.favorite },
|
||||||
onClickRecommend = onRecommendClicked.takeIf { state.showRecommendationsInOverflow },
|
onClickRecommend = onRecommendClicked.takeIf { state.showRecommendationsInOverflow },
|
||||||
@@ -392,8 +392,11 @@ private fun MangaScreenSmallImpl(
|
|||||||
onClickMerge = onMergeClicked.takeIf { state.showMergeInOverflow },
|
onClickMerge = onMergeClicked.takeIf { state.showMergeInOverflow },
|
||||||
// SY <--
|
// SY <--
|
||||||
actionModeCounter = selectedChapterCount,
|
actionModeCounter = selectedChapterCount,
|
||||||
|
onCancelActionMode = { onAllChapterSelected(false) },
|
||||||
onSelectAll = { onAllChapterSelected(true) },
|
onSelectAll = { onAllChapterSelected(true) },
|
||||||
onInvertSelection = { onInvertSelection() },
|
onInvertSelection = { onInvertSelection() },
|
||||||
|
titleAlphaProvider = { titleAlpha },
|
||||||
|
backgroundAlphaProvider = { backgroundAlpha },
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
@@ -519,12 +522,14 @@ private fun MangaScreenSmallImpl(
|
|||||||
defaultExpandState = state.isFromSource,
|
defaultExpandState = state.isFromSource,
|
||||||
description = state.manga.description,
|
description = state.manga.description,
|
||||||
tagsProvider = { state.manga.genre },
|
tagsProvider = { state.manga.genre },
|
||||||
|
notes = state.manga.notes,
|
||||||
onTagSearch = onTagSearch,
|
onTagSearch = onTagSearch,
|
||||||
onCopyTagToClipboard = onCopyTagToClipboard,
|
onCopyTagToClipboard = onCopyTagToClipboard,
|
||||||
|
onEditNotes = onEditNotesClicked,
|
||||||
// SY -->
|
// SY -->
|
||||||
doSearch = onSearch,
|
doSearch = onSearch,
|
||||||
searchMetadataChips = remember(state.meta, state.source.id, state.manga.genre) {
|
searchMetadataChips = remember(state.meta, state.source.id, state.manga.genre) {
|
||||||
SearchMetadataChips(state.meta, state.source, state.manga.genre)
|
SearchMetadataChips(state.meta, state.source.id, state.manga.genre)
|
||||||
},
|
},
|
||||||
// SY <--
|
// SY <--
|
||||||
)
|
)
|
||||||
@@ -600,7 +605,7 @@ fun MangaScreenLargeImpl(
|
|||||||
nextUpdate: Instant?,
|
nextUpdate: Instant?,
|
||||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
onBackClicked: () -> Unit,
|
navigateUp: () -> Unit,
|
||||||
onChapterClicked: (Chapter) -> Unit,
|
onChapterClicked: (Chapter) -> Unit,
|
||||||
onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?,
|
onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?,
|
||||||
onAddToLibraryClicked: () -> Unit,
|
onAddToLibraryClicked: () -> Unit,
|
||||||
@@ -626,6 +631,7 @@ fun MangaScreenLargeImpl(
|
|||||||
onEditCategoryClicked: (() -> Unit)?,
|
onEditCategoryClicked: (() -> Unit)?,
|
||||||
onEditIntervalClicked: (() -> Unit)?,
|
onEditIntervalClicked: (() -> Unit)?,
|
||||||
onMigrateClicked: (() -> Unit)?,
|
onMigrateClicked: (() -> Unit)?,
|
||||||
|
onEditNotesClicked: () -> Unit,
|
||||||
// SY -->
|
// SY -->
|
||||||
onMetadataViewerClicked: () -> Unit,
|
onMetadataViewerClicked: () -> Unit,
|
||||||
onEditInfoClicked: () -> Unit,
|
onEditInfoClicked: () -> Unit,
|
||||||
@@ -672,14 +678,9 @@ fun MangaScreenLargeImpl(
|
|||||||
|
|
||||||
val chapterListState = rememberLazyListState()
|
val chapterListState = rememberLazyListState()
|
||||||
|
|
||||||
val internalOnBackPressed = {
|
BackHandler(enabled = isAnySelected) {
|
||||||
if (isAnySelected) {
|
onAllChapterSelected(false)
|
||||||
onAllChapterSelected(false)
|
|
||||||
} else {
|
|
||||||
onBackClicked()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
BackHandler(onBack = internalOnBackPressed)
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
@@ -689,25 +690,27 @@ fun MangaScreenLargeImpl(
|
|||||||
MangaToolbar(
|
MangaToolbar(
|
||||||
modifier = Modifier.onSizeChanged { topBarHeight = it.height },
|
modifier = Modifier.onSizeChanged { topBarHeight = it.height },
|
||||||
title = state.manga.title,
|
title = state.manga.title,
|
||||||
titleAlphaProvider = { if (isAnySelected) 1f else 0f },
|
|
||||||
backgroundAlphaProvider = { 1f },
|
|
||||||
hasFilters = state.filterActive,
|
hasFilters = state.filterActive,
|
||||||
onBackClicked = internalOnBackPressed,
|
navigateUp = navigateUp,
|
||||||
onClickFilter = onFilterButtonClicked,
|
onClickFilter = onFilterButtonClicked,
|
||||||
onClickShare = onShareClicked,
|
onClickShare = onShareClicked,
|
||||||
onClickDownload = onDownloadActionClicked,
|
onClickDownload = onDownloadActionClicked,
|
||||||
onClickEditCategory = onEditCategoryClicked,
|
onClickEditCategory = onEditCategoryClicked,
|
||||||
onClickRefresh = onRefresh,
|
onClickRefresh = onRefresh,
|
||||||
onClickMigrate = onMigrateClicked,
|
onClickMigrate = onMigrateClicked,
|
||||||
|
onClickEditNotes = onEditNotesClicked,
|
||||||
// SY -->
|
// SY -->
|
||||||
onClickEditInfo = onEditInfoClicked.takeIf { state.manga.favorite },
|
onClickEditInfo = onEditInfoClicked.takeIf { state.manga.favorite },
|
||||||
onClickRecommend = onRecommendClicked.takeIf { state.showRecommendationsInOverflow },
|
onClickRecommend = onRecommendClicked.takeIf { state.showRecommendationsInOverflow },
|
||||||
onClickMergedSettings = onMergedSettingsClicked.takeIf { state.manga.source == MERGED_SOURCE_ID },
|
onClickMergedSettings = onMergedSettingsClicked.takeIf { state.manga.source == MERGED_SOURCE_ID },
|
||||||
onClickMerge = onMergeClicked.takeIf { state.showMergeInOverflow },
|
onClickMerge = onMergeClicked.takeIf { state.showMergeInOverflow },
|
||||||
// SY <--
|
// SY <--
|
||||||
|
onCancelActionMode = { onAllChapterSelected(false) },
|
||||||
actionModeCounter = selectedChapterCount,
|
actionModeCounter = selectedChapterCount,
|
||||||
onSelectAll = { onAllChapterSelected(true) },
|
onSelectAll = { onAllChapterSelected(true) },
|
||||||
onInvertSelection = { onInvertSelection() },
|
onInvertSelection = { onInvertSelection() },
|
||||||
|
titleAlphaProvider = { 1f },
|
||||||
|
backgroundAlphaProvider = { 1f },
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
@@ -814,12 +817,14 @@ fun MangaScreenLargeImpl(
|
|||||||
defaultExpandState = true,
|
defaultExpandState = true,
|
||||||
description = state.manga.description,
|
description = state.manga.description,
|
||||||
tagsProvider = { state.manga.genre },
|
tagsProvider = { state.manga.genre },
|
||||||
|
notes = state.manga.notes,
|
||||||
onTagSearch = onTagSearch,
|
onTagSearch = onTagSearch,
|
||||||
onCopyTagToClipboard = onCopyTagToClipboard,
|
onCopyTagToClipboard = onCopyTagToClipboard,
|
||||||
|
onEditNotes = onEditNotesClicked,
|
||||||
// SY -->
|
// SY -->
|
||||||
doSearch = onSearch,
|
doSearch = onSearch,
|
||||||
searchMetadataChips = remember(state.meta, state.source.id, state.manga.genre) {
|
searchMetadataChips = remember(state.meta, state.source.id, state.manga.genre) {
|
||||||
SearchMetadataChips(state.meta, state.source, state.manga.genre)
|
SearchMetadataChips(state.meta, state.source.id, state.manga.genre)
|
||||||
},
|
},
|
||||||
// SY <--
|
// SY <--
|
||||||
)
|
)
|
||||||
@@ -1086,6 +1091,9 @@ fun metadataDescription(source: Source): MetadataDescriptionComposable? {
|
|||||||
is Tsumino -> { state, openMetadataViewer, _ ->
|
is Tsumino -> { state, openMetadataViewer, _ ->
|
||||||
TsuminoDescription(state, openMetadataViewer)
|
TsuminoDescription(state, openMetadataViewer)
|
||||||
}
|
}
|
||||||
|
is Lanraragi -> { state, openMetadataViewer, _ ->
|
||||||
|
LanraragiDescription(state, openMetadataViewer)
|
||||||
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-36
@@ -9,6 +9,7 @@ import androidx.compose.animation.fadeOut
|
|||||||
import androidx.compose.animation.shrinkVertically
|
import androidx.compose.animation.shrinkVertically
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
@@ -28,10 +29,8 @@ import androidx.compose.material.icons.outlined.BookmarkRemove
|
|||||||
import androidx.compose.material.icons.outlined.Delete
|
import androidx.compose.material.icons.outlined.Delete
|
||||||
import androidx.compose.material.icons.outlined.DoneAll
|
import androidx.compose.material.icons.outlined.DoneAll
|
||||||
import androidx.compose.material.icons.outlined.Download
|
import androidx.compose.material.icons.outlined.Download
|
||||||
import androidx.compose.material.icons.outlined.Label
|
|
||||||
import androidx.compose.material.icons.outlined.MoreVert
|
import androidx.compose.material.icons.outlined.MoreVert
|
||||||
import androidx.compose.material.icons.outlined.RemoveDone
|
import androidx.compose.material.icons.outlined.RemoveDone
|
||||||
import androidx.compose.material.icons.outlined.SwapCalls
|
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -53,6 +52,7 @@ import androidx.compose.ui.platform.LocalConfiguration
|
|||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
import androidx.compose.ui.res.vectorResource
|
import androidx.compose.ui.res.vectorResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.DpOffset
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.kanade.presentation.components.DownloadDropdownMenu
|
import eu.kanade.presentation.components.DownloadDropdownMenu
|
||||||
import eu.kanade.presentation.components.DropdownMenu
|
import eu.kanade.presentation.components.DropdownMenu
|
||||||
@@ -193,7 +193,7 @@ private fun RowScope.Button(
|
|||||||
targetValue = if (toConfirm) 2f else 1f,
|
targetValue = if (toConfirm) 2f else 1f,
|
||||||
label = "weight",
|
label = "weight",
|
||||||
)
|
)
|
||||||
Column(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(48.dp)
|
.size(48.dp)
|
||||||
.weight(animatedWeight)
|
.weight(animatedWeight)
|
||||||
@@ -203,24 +203,28 @@ private fun RowScope.Button(
|
|||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
),
|
),
|
||||||
verticalArrangement = Arrangement.Center,
|
contentAlignment = Alignment.Center,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
) {
|
) {
|
||||||
Icon(
|
Column(
|
||||||
imageVector = icon,
|
verticalArrangement = Arrangement.Center,
|
||||||
contentDescription = title,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
)
|
|
||||||
AnimatedVisibility(
|
|
||||||
visible = toConfirm,
|
|
||||||
enter = expandVertically(expandFrom = Alignment.Top) + fadeIn(),
|
|
||||||
exit = shrinkVertically(shrinkTowards = Alignment.Top) + fadeOut(),
|
|
||||||
) {
|
) {
|
||||||
Text(
|
Icon(
|
||||||
text = title,
|
imageVector = icon,
|
||||||
overflow = TextOverflow.Visible,
|
contentDescription = title,
|
||||||
maxLines = 1,
|
|
||||||
style = MaterialTheme.typography.labelSmall,
|
|
||||||
)
|
)
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = toConfirm,
|
||||||
|
enter = expandVertically(expandFrom = Alignment.Top) + fadeIn(),
|
||||||
|
exit = shrinkVertically(shrinkTowards = Alignment.Top) + fadeOut(),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
overflow = TextOverflow.Visible,
|
||||||
|
maxLines = 1,
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
content?.invoke()
|
content?.invoke()
|
||||||
}
|
}
|
||||||
@@ -234,9 +238,10 @@ fun LibraryBottomActionMenu(
|
|||||||
onMarkAsUnreadClicked: () -> Unit,
|
onMarkAsUnreadClicked: () -> Unit,
|
||||||
onDownloadClicked: ((DownloadAction) -> Unit)?,
|
onDownloadClicked: ((DownloadAction) -> Unit)?,
|
||||||
onDeleteClicked: () -> Unit,
|
onDeleteClicked: () -> Unit,
|
||||||
|
onMigrateClicked: (() -> Unit)?,
|
||||||
// SY -->
|
// SY -->
|
||||||
onClickCleanTitles: (() -> Unit)?,
|
onClickCleanTitles: (() -> Unit)?,
|
||||||
onClickMigrate: (() -> Unit)?,
|
onClickCollectRecommendations: (() -> Unit)?,
|
||||||
onClickAddToMangaDex: (() -> Unit)?,
|
onClickAddToMangaDex: (() -> Unit)?,
|
||||||
onClickResetInfo: (() -> Unit)?,
|
onClickResetInfo: (() -> Unit)?,
|
||||||
// SY <--
|
// SY <--
|
||||||
@@ -254,12 +259,11 @@ fun LibraryBottomActionMenu(
|
|||||||
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||||
) {
|
) {
|
||||||
val haptic = LocalHapticFeedback.current
|
val haptic = LocalHapticFeedback.current
|
||||||
val confirm =
|
val confirm = remember { mutableStateListOf(false, false, false, false, false, false) }
|
||||||
remember { mutableStateListOf(false, false, false, false, false /* SY --> */, false /* SY <-- */) }
|
|
||||||
var resetJob: Job? = remember { null }
|
var resetJob: Job? = remember { null }
|
||||||
val onLongClickItem: (Int) -> Unit = { toConfirmIndex ->
|
val onLongClickItem: (Int) -> Unit = { toConfirmIndex ->
|
||||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||||
(0..<5).forEach { i -> confirm[i] = i == toConfirmIndex }
|
(0..5).forEach { i -> confirm[i] = i == toConfirmIndex }
|
||||||
resetJob?.cancel()
|
resetJob?.cancel()
|
||||||
resetJob = scope.launch {
|
resetJob = scope.launch {
|
||||||
delay(1.seconds)
|
delay(1.seconds)
|
||||||
@@ -267,7 +271,11 @@ fun LibraryBottomActionMenu(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SY -->
|
// SY -->
|
||||||
val showOverflow = onClickCleanTitles != null || onClickAddToMangaDex != null || onClickResetInfo != null
|
val showOverflow = onClickCleanTitles != null ||
|
||||||
|
onClickAddToMangaDex != null ||
|
||||||
|
onClickResetInfo != null ||
|
||||||
|
onClickCollectRecommendations != null ||
|
||||||
|
onMigrateClicked != null
|
||||||
val configuration = LocalConfiguration.current
|
val configuration = LocalConfiguration.current
|
||||||
val moveMarkPrev = remember { !configuration.isTabletUi() }
|
val moveMarkPrev = remember { !configuration.isTabletUi() }
|
||||||
var overFlowOpen by remember { mutableStateOf(false) }
|
var overFlowOpen by remember { mutableStateOf(false) }
|
||||||
@@ -296,11 +304,11 @@ fun LibraryBottomActionMenu(
|
|||||||
onLongClick = { onLongClickItem(3) },
|
onLongClick = { onLongClickItem(3) },
|
||||||
onClick = { downloadExpanded = !downloadExpanded },
|
onClick = { downloadExpanded = !downloadExpanded },
|
||||||
) {
|
) {
|
||||||
val onDismissRequest = { downloadExpanded = false }
|
|
||||||
DownloadDropdownMenu(
|
DownloadDropdownMenu(
|
||||||
expanded = downloadExpanded,
|
expanded = downloadExpanded,
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = { downloadExpanded = false },
|
||||||
onDownloadClicked = onDownloadClicked,
|
onDownloadClicked = onDownloadClicked,
|
||||||
|
offset = BottomBarMenuDpOffset,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -352,10 +360,16 @@ fun LibraryBottomActionMenu(
|
|||||||
onClick = onClickCleanTitles,
|
onClick = onClickCleanTitles,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (onClickMigrate != null) {
|
if (onMigrateClicked != null) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(stringResource(MR.strings.migrate)) },
|
text = { Text(stringResource(MR.strings.migrate)) },
|
||||||
onClick = onClickMigrate,
|
onClick = onMigrateClicked,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (onClickCollectRecommendations != null) {
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text(stringResource(SYMR.strings.rec_search_short)) },
|
||||||
|
onClick = onClickCollectRecommendations,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (onClickAddToMangaDex != null) {
|
if (onClickAddToMangaDex != null) {
|
||||||
@@ -379,18 +393,11 @@ fun LibraryBottomActionMenu(
|
|||||||
onLongClick = { onLongClickItem(2) },
|
onLongClick = { onLongClickItem(2) },
|
||||||
onClick = onMarkAsUnreadClicked,
|
onClick = onMarkAsUnreadClicked,
|
||||||
)
|
)
|
||||||
if (onClickMigrate != null) {
|
|
||||||
Button(
|
|
||||||
title = stringResource(MR.strings.migrate),
|
|
||||||
icon = Icons.Outlined.SwapCalls,
|
|
||||||
toConfirm = confirm[5],
|
|
||||||
onLongClick = { onLongClickItem(5) },
|
|
||||||
onClick = onClickMigrate,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val BottomBarMenuDpOffset = DpOffset(0.dp, 0.dp)
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import androidx.core.graphics.drawable.toDrawable
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import coil3.asDrawable
|
import coil3.asDrawable
|
||||||
import coil3.imageLoader
|
import coil3.imageLoader
|
||||||
@@ -172,20 +173,20 @@ fun MangaCoverDialog(
|
|||||||
.memoryCachePolicy(CachePolicy.DISABLED)
|
.memoryCachePolicy(CachePolicy.DISABLED)
|
||||||
.target { image ->
|
.target { image ->
|
||||||
val drawable = image.asDrawable(view.context.resources)
|
val drawable = image.asDrawable(view.context.resources)
|
||||||
|
|
||||||
// Copy bitmap in case it came from memory cache
|
// Copy bitmap in case it came from memory cache
|
||||||
// Because SSIV needs to thoroughly read the image
|
// Because SSIV needs to thoroughly read the image
|
||||||
val copy = (drawable as? BitmapDrawable)?.let {
|
val copy = (drawable as? BitmapDrawable)
|
||||||
val config = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
?.bitmap
|
||||||
Bitmap.Config.HARDWARE
|
?.copy(
|
||||||
} else {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
Bitmap.Config.ARGB_8888
|
Bitmap.Config.HARDWARE
|
||||||
}
|
} else {
|
||||||
BitmapDrawable(
|
Bitmap.Config.ARGB_8888
|
||||||
view.context.resources,
|
},
|
||||||
it.bitmap.copy(config, false),
|
false,
|
||||||
)
|
)
|
||||||
} ?: drawable
|
?.toDrawable(view.context.resources)
|
||||||
|
?: drawable
|
||||||
view.setImage(copy, ReaderPageImageView.Config(zoomDuration = 500))
|
view.setImage(copy, ReaderPageImageView.Config(zoomDuration = 500))
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.foundation.layout.sizeIn
|
import androidx.compose.foundation.layout.sizeIn
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.text.appendInlineContent
|
||||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.outlined.CallMerge
|
import androidx.compose.material.icons.automirrored.outlined.CallMerge
|
||||||
@@ -33,7 +34,6 @@ import androidx.compose.material.icons.filled.PersonOutline
|
|||||||
import androidx.compose.material.icons.filled.Warning
|
import androidx.compose.material.icons.filled.Warning
|
||||||
import androidx.compose.material.icons.outlined.AttachMoney
|
import androidx.compose.material.icons.outlined.AttachMoney
|
||||||
import androidx.compose.material.icons.outlined.Block
|
import androidx.compose.material.icons.outlined.Block
|
||||||
import androidx.compose.material.icons.outlined.CallMerge
|
|
||||||
import androidx.compose.material.icons.outlined.Close
|
import androidx.compose.material.icons.outlined.Close
|
||||||
import androidx.compose.material.icons.outlined.Done
|
import androidx.compose.material.icons.outlined.Done
|
||||||
import androidx.compose.material.icons.outlined.DoneAll
|
import androidx.compose.material.icons.outlined.DoneAll
|
||||||
@@ -52,6 +52,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
@@ -67,9 +68,13 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.layout.Layout
|
import androidx.compose.ui.layout.Layout
|
||||||
|
import androidx.compose.ui.layout.onSizeChanged
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.LinkAnnotation
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.text.withLink
|
||||||
import androidx.compose.ui.unit.Constraints
|
import androidx.compose.ui.unit.Constraints
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -77,10 +82,17 @@ import androidx.compose.ui.unit.sp
|
|||||||
import coil3.compose.AsyncImage
|
import coil3.compose.AsyncImage
|
||||||
import coil3.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import coil3.request.crossfade
|
import coil3.request.crossfade
|
||||||
|
import com.mikepenz.markdown.model.markdownAnnotator
|
||||||
|
import com.mikepenz.markdown.model.markdownAnnotatorConfig
|
||||||
|
import com.mikepenz.markdown.utils.getUnescapedTextInNode
|
||||||
|
import eu.kanade.domain.ui.UiPreferences
|
||||||
import eu.kanade.presentation.components.DropdownMenu
|
import eu.kanade.presentation.components.DropdownMenu
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||||
|
import org.intellij.markdown.MarkdownElementTypes
|
||||||
|
import org.intellij.markdown.MarkdownTokenTypes
|
||||||
|
import org.intellij.markdown.ast.findChildOfType
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.i18n.sy.SYMR
|
import tachiyomi.i18n.sy.SYMR
|
||||||
@@ -91,12 +103,12 @@ import tachiyomi.presentation.core.i18n.pluralStringResource
|
|||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.util.clickableNoIndication
|
import tachiyomi.presentation.core.util.clickableNoIndication
|
||||||
import tachiyomi.presentation.core.util.secondaryItemAlpha
|
import tachiyomi.presentation.core.util.secondaryItemAlpha
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
private val whitespaceLineRegex = Regex("[\\r\\n]{2,}", setOf(RegexOption.MULTILINE))
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MangaInfoBox(
|
fun MangaInfoBox(
|
||||||
isTabletUi: Boolean,
|
isTabletUi: Boolean,
|
||||||
@@ -250,8 +262,10 @@ fun ExpandableMangaDescription(
|
|||||||
defaultExpandState: Boolean,
|
defaultExpandState: Boolean,
|
||||||
description: String?,
|
description: String?,
|
||||||
tagsProvider: () -> List<String>?,
|
tagsProvider: () -> List<String>?,
|
||||||
|
notes: String,
|
||||||
onTagSearch: (String) -> Unit,
|
onTagSearch: (String) -> Unit,
|
||||||
onCopyTagToClipboard: (tag: String) -> Unit,
|
onCopyTagToClipboard: (tag: String) -> Unit,
|
||||||
|
onEditNotes: () -> Unit,
|
||||||
// SY -->
|
// SY -->
|
||||||
searchMetadataChips: SearchMetadataChips?,
|
searchMetadataChips: SearchMetadataChips?,
|
||||||
doSearch: (query: String, global: Boolean) -> Unit,
|
doSearch: (query: String, global: Boolean) -> Unit,
|
||||||
@@ -264,15 +278,12 @@ fun ExpandableMangaDescription(
|
|||||||
}
|
}
|
||||||
val desc =
|
val desc =
|
||||||
description.takeIf { !it.isNullOrBlank() } ?: stringResource(MR.strings.description_placeholder)
|
description.takeIf { !it.isNullOrBlank() } ?: stringResource(MR.strings.description_placeholder)
|
||||||
val trimmedDescription = remember(desc) {
|
|
||||||
desc
|
|
||||||
.replace(whitespaceLineRegex, "\n")
|
|
||||||
.trimEnd()
|
|
||||||
}
|
|
||||||
MangaSummary(
|
MangaSummary(
|
||||||
expandedDescription = desc,
|
description = desc,
|
||||||
shrunkDescription = trimmedDescription,
|
|
||||||
expanded = expanded,
|
expanded = expanded,
|
||||||
|
notes = notes,
|
||||||
|
onEditNotesClicked = onEditNotes,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(top = 8.dp)
|
.padding(top = 8.dp)
|
||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
@@ -594,41 +605,93 @@ private fun ColumnScope.MangaContentInfo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun descriptionAnnotator(loadImages: Boolean, linkStyle: SpanStyle) = markdownAnnotator(
|
||||||
|
annotate = { content, child ->
|
||||||
|
if (!loadImages && child.type == MarkdownElementTypes.IMAGE) {
|
||||||
|
val inlineLink = child.findChildOfType(MarkdownElementTypes.INLINE_LINK)
|
||||||
|
|
||||||
|
val url = inlineLink?.findChildOfType(MarkdownElementTypes.LINK_DESTINATION)
|
||||||
|
?.getUnescapedTextInNode(content)
|
||||||
|
?: inlineLink?.findChildOfType(MarkdownElementTypes.AUTOLINK)
|
||||||
|
?.findChildOfType(MarkdownTokenTypes.AUTOLINK)
|
||||||
|
?.getUnescapedTextInNode(content)
|
||||||
|
?: return@markdownAnnotator false
|
||||||
|
|
||||||
|
val textNode = inlineLink?.findChildOfType(MarkdownElementTypes.LINK_TITLE)
|
||||||
|
?: inlineLink?.findChildOfType(MarkdownElementTypes.LINK_TEXT)
|
||||||
|
val altText = textNode?.findChildOfType(MarkdownTokenTypes.TEXT)
|
||||||
|
?.getUnescapedTextInNode(content).orEmpty()
|
||||||
|
|
||||||
|
withLink(LinkAnnotation.Url(url = url)) {
|
||||||
|
pushStyle(linkStyle)
|
||||||
|
appendInlineContent(MARKDOWN_INLINE_IMAGE_TAG)
|
||||||
|
append(altText)
|
||||||
|
pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
return@markdownAnnotator true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child.type in DISALLOWED_MARKDOWN_TYPES) {
|
||||||
|
append(content.substring(child.startOffset, child.endOffset))
|
||||||
|
return@markdownAnnotator true
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
},
|
||||||
|
config = markdownAnnotatorConfig(
|
||||||
|
eolAsNewLine = true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MangaSummary(
|
private fun MangaSummary(
|
||||||
expandedDescription: String,
|
description: String,
|
||||||
shrunkDescription: String,
|
notes: String,
|
||||||
expanded: Boolean,
|
expanded: Boolean,
|
||||||
|
onEditNotesClicked: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
val preferences = remember { Injekt.get<UiPreferences>() }
|
||||||
|
val loadImages = remember { preferences.imagesInDescription().get() }
|
||||||
val animProgress by animateFloatAsState(
|
val animProgress by animateFloatAsState(
|
||||||
targetValue = if (expanded) 1f else 0f,
|
targetValue = if (expanded) 1f else 0f,
|
||||||
label = "summary",
|
label = "summary",
|
||||||
)
|
)
|
||||||
|
var infoHeight by remember { mutableIntStateOf(0) }
|
||||||
Layout(
|
Layout(
|
||||||
modifier = modifier.clipToBounds(),
|
modifier = modifier.clipToBounds(),
|
||||||
contents = listOf(
|
contents = listOf(
|
||||||
{
|
{
|
||||||
Text(
|
Text(
|
||||||
text = "\n\n", // Shows at least 3 lines
|
// Shows at least 3 lines if no notes
|
||||||
|
// when there are notes show 6
|
||||||
|
text = if (notes.isBlank()) "\n\n" else "\n\n\n\n\n",
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Text(
|
Column(
|
||||||
text = expandedDescription,
|
modifier = Modifier.onSizeChanged { size ->
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
infoHeight = size.height
|
||||||
)
|
},
|
||||||
},
|
) {
|
||||||
{
|
MangaNotesSection(
|
||||||
SelectionContainer {
|
content = notes,
|
||||||
Text(
|
expanded = expanded,
|
||||||
text = if (expanded) expandedDescription else shrunkDescription,
|
onEditNotes = onEditNotesClicked,
|
||||||
maxLines = Int.MAX_VALUE,
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
color = MaterialTheme.colorScheme.onBackground,
|
|
||||||
modifier = Modifier.secondaryItemAlpha(),
|
|
||||||
)
|
)
|
||||||
|
SelectionContainer {
|
||||||
|
MarkdownRender(
|
||||||
|
content = description,
|
||||||
|
modifier = Modifier.secondaryItemAlpha(),
|
||||||
|
annotator = descriptionAnnotator(
|
||||||
|
loadImages = loadImages,
|
||||||
|
linkStyle = getMarkdownLinkStyle().toSpanStyle(),
|
||||||
|
),
|
||||||
|
loadImages = loadImages,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -649,14 +712,11 @@ private fun MangaSummary(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
) { (shrunk, expanded, actual, scrim), constraints ->
|
) { (shrunk, actual, scrim), constraints ->
|
||||||
val shrunkHeight = shrunk.single()
|
val shrunkHeight = shrunk.single()
|
||||||
.measure(constraints)
|
.measure(constraints)
|
||||||
.height
|
.height
|
||||||
val expandedHeight = expanded.single()
|
val heightDelta = infoHeight - shrunkHeight
|
||||||
.measure(constraints)
|
|
||||||
.height
|
|
||||||
val heightDelta = expandedHeight - shrunkHeight
|
|
||||||
val scrimHeight = 24.dp.roundToPx()
|
val scrimHeight = 24.dp.roundToPx()
|
||||||
|
|
||||||
val actualPlaceable = actual.single()
|
val actualPlaceable = actual.single()
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package eu.kanade.presentation.manga.components
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
|
import androidx.compose.animation.core.Animatable
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import com.mohamedrejeb.richeditor.model.rememberRichTextState
|
||||||
|
import com.mohamedrejeb.richeditor.ui.material3.RichText
|
||||||
|
|
||||||
|
private val FADE_TIME = tween<Float>(500)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MangaNotesDisplay(
|
||||||
|
content: String,
|
||||||
|
modifier: Modifier,
|
||||||
|
) {
|
||||||
|
val alpha = remember { Animatable(1f) }
|
||||||
|
var contentUpdatedOnce by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val richTextState = rememberRichTextState()
|
||||||
|
val primaryColor = MaterialTheme.colorScheme.primary
|
||||||
|
LaunchedEffect(content) {
|
||||||
|
richTextState.setMarkdown(content)
|
||||||
|
|
||||||
|
if (!contentUpdatedOnce) {
|
||||||
|
contentUpdatedOnce = true
|
||||||
|
return@LaunchedEffect
|
||||||
|
}
|
||||||
|
|
||||||
|
alpha.snapTo(targetValue = 0f)
|
||||||
|
alpha.animateTo(targetValue = 1f, animationSpec = FADE_TIME)
|
||||||
|
}
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
richTextState.config.unorderedListIndent = 4
|
||||||
|
richTextState.config.orderedListIndent = 20
|
||||||
|
}
|
||||||
|
LaunchedEffect(primaryColor) {
|
||||||
|
richTextState.config.linkColor = primaryColor
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectionContainer {
|
||||||
|
RichText(
|
||||||
|
modifier = modifier
|
||||||
|
// Only animate size if the notes changes
|
||||||
|
.then(if (contentUpdatedOnce) Modifier.animateContentSize() else Modifier)
|
||||||
|
.alpha(alpha.value),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
state = richTextState,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package eu.kanade.presentation.manga.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.EditNote
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.components.material.Button
|
||||||
|
import tachiyomi.presentation.core.components.material.ButtonDefaults
|
||||||
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MangaNotesSection(
|
||||||
|
content: String,
|
||||||
|
expanded: Boolean,
|
||||||
|
onEditNotes: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
if (content.isBlank()) return
|
||||||
|
Column(
|
||||||
|
modifier = modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
MangaNotesDisplay(
|
||||||
|
content = content,
|
||||||
|
modifier = modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
if (expanded) {
|
||||||
|
Button(
|
||||||
|
onClick = onEditNotes,
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = Color.Transparent,
|
||||||
|
contentColor = MaterialTheme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
shape = RoundedCornerShape(8.dp),
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.EditNote,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(16.dp),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
stringResource(MR.strings.action_edit_notes),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizontalDivider(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(
|
||||||
|
top = if (expanded) 0.dp else 12.dp,
|
||||||
|
bottom = if (expanded) 16.dp else 12.dp,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreviewLightDark
|
||||||
|
@Composable
|
||||||
|
private fun MangaNotesSectionPreview() {
|
||||||
|
MangaNotesSection(
|
||||||
|
onEditNotes = {},
|
||||||
|
expanded = true,
|
||||||
|
content = "# Hello world\ntest1234 hi there!",
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
package eu.kanade.presentation.manga.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.outlined.FormatListBulleted
|
||||||
|
import androidx.compose.material.icons.outlined.FormatBold
|
||||||
|
import androidx.compose.material.icons.outlined.FormatItalic
|
||||||
|
import androidx.compose.material.icons.outlined.FormatListNumbered
|
||||||
|
import androidx.compose.material.icons.outlined.FormatUnderlined
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.VerticalDivider
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.snapshotFlow
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.semantics.Role
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.mohamedrejeb.richeditor.model.rememberRichTextState
|
||||||
|
import com.mohamedrejeb.richeditor.ui.material3.RichTextEditor
|
||||||
|
import com.mohamedrejeb.richeditor.ui.material3.RichTextEditorDefaults.richTextEditorColors
|
||||||
|
import eu.kanade.tachiyomi.ui.manga.notes.MangaNotesScreen
|
||||||
|
import kotlinx.coroutines.flow.debounce
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
private const val MAX_LENGTH = 250
|
||||||
|
private const val MAX_LENGTH_WARN = MAX_LENGTH * 0.9
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MangaNotesTextArea(
|
||||||
|
state: MangaNotesScreen.State,
|
||||||
|
onUpdate: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val richTextState = rememberRichTextState()
|
||||||
|
val primaryColor = MaterialTheme.colorScheme.primary
|
||||||
|
|
||||||
|
DisposableEffect(scope, richTextState) {
|
||||||
|
snapshotFlow { richTextState.annotatedString }
|
||||||
|
.debounce(0.25.seconds)
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.map { richTextState.toMarkdown() }
|
||||||
|
.onEach { onUpdate(it) }
|
||||||
|
.launchIn(scope)
|
||||||
|
|
||||||
|
onDispose {
|
||||||
|
onUpdate(richTextState.toMarkdown())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
richTextState.setMarkdown(state.notes)
|
||||||
|
richTextState.config.unorderedListIndent = 4
|
||||||
|
richTextState.config.orderedListIndent = 20
|
||||||
|
}
|
||||||
|
LaunchedEffect(primaryColor) {
|
||||||
|
richTextState.config.linkColor = primaryColor
|
||||||
|
}
|
||||||
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
LaunchedEffect(focusRequester) {
|
||||||
|
focusRequester.requestFocus()
|
||||||
|
}
|
||||||
|
val textLength = remember(richTextState.annotatedString) { richTextState.toText().length }
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.padding(horizontal = MaterialTheme.padding.small)
|
||||||
|
.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
RichTextEditor(
|
||||||
|
state = richTextState,
|
||||||
|
textStyle = MaterialTheme.typography.bodyLarge,
|
||||||
|
maxLength = MAX_LENGTH,
|
||||||
|
placeholder = {
|
||||||
|
Text(text = stringResource(MR.strings.notes_placeholder))
|
||||||
|
},
|
||||||
|
colors = richTextEditorColors(
|
||||||
|
containerColor = Color.Transparent,
|
||||||
|
focusedIndicatorColor = Color.Transparent,
|
||||||
|
unfocusedIndicatorColor = Color.Transparent,
|
||||||
|
),
|
||||||
|
contentPadding = PaddingValues(
|
||||||
|
horizontal = MaterialTheme.padding.medium,
|
||||||
|
),
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.focusRequester(focusRequester),
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(vertical = MaterialTheme.padding.small)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
LazyRow(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
MangaNotesTextAreaButton(
|
||||||
|
onClick = { richTextState.toggleSpanStyle(SpanStyle(fontWeight = FontWeight.Bold)) },
|
||||||
|
isSelected = richTextState.currentSpanStyle.fontWeight == FontWeight.Bold,
|
||||||
|
icon = Icons.Outlined.FormatBold,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
MangaNotesTextAreaButton(
|
||||||
|
onClick = { richTextState.toggleSpanStyle(SpanStyle(fontStyle = FontStyle.Italic)) },
|
||||||
|
isSelected = richTextState.currentSpanStyle.fontStyle == FontStyle.Italic,
|
||||||
|
icon = Icons.Outlined.FormatItalic,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
MangaNotesTextAreaButton(
|
||||||
|
onClick = {
|
||||||
|
richTextState.toggleSpanStyle(SpanStyle(textDecoration = TextDecoration.Underline))
|
||||||
|
},
|
||||||
|
isSelected = richTextState.currentSpanStyle.textDecoration
|
||||||
|
?.contains(TextDecoration.Underline)
|
||||||
|
?: false,
|
||||||
|
icon = Icons.Outlined.FormatUnderlined,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
VerticalDivider(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = MaterialTheme.padding.extraSmall)
|
||||||
|
.height(MaterialTheme.padding.large),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
MangaNotesTextAreaButton(
|
||||||
|
onClick = { richTextState.toggleUnorderedList() },
|
||||||
|
isSelected = richTextState.isUnorderedList,
|
||||||
|
icon = Icons.AutoMirrored.Outlined.FormatListBulleted,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
MangaNotesTextAreaButton(
|
||||||
|
onClick = { richTextState.toggleOrderedList() },
|
||||||
|
isSelected = richTextState.isOrderedList,
|
||||||
|
icon = Icons.Outlined.FormatListNumbered,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = (MAX_LENGTH - textLength).toString(),
|
||||||
|
color = if (textLength > MAX_LENGTH_WARN) {
|
||||||
|
MaterialTheme.colorScheme.error
|
||||||
|
} else {
|
||||||
|
Color.Unspecified
|
||||||
|
},
|
||||||
|
modifier = Modifier.padding(MaterialTheme.padding.extraSmall),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MangaNotesTextAreaButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
icon: ImageVector,
|
||||||
|
isSelected: Boolean,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.clip(MaterialTheme.shapes.small)
|
||||||
|
.clickable(
|
||||||
|
onClick = onClick,
|
||||||
|
enabled = true,
|
||||||
|
role = Role.Button,
|
||||||
|
),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = icon.name,
|
||||||
|
tint = if (isSelected) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier
|
||||||
|
.background(color = if (isSelected) MaterialTheme.colorScheme.onBackground else Color.Transparent)
|
||||||
|
.padding(MaterialTheme.padding.extraSmall),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,12 @@
|
|||||||
package eu.kanade.presentation.manga.components
|
package eu.kanade.presentation.manga.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Close
|
|
||||||
import androidx.compose.material.icons.outlined.Download
|
import androidx.compose.material.icons.outlined.Download
|
||||||
import androidx.compose.material.icons.outlined.FilterList
|
import androidx.compose.material.icons.outlined.FilterList
|
||||||
import androidx.compose.material.icons.outlined.FlipToBack
|
import androidx.compose.material.icons.outlined.FlipToBack
|
||||||
import androidx.compose.material.icons.outlined.SelectAll
|
import androidx.compose.material.icons.outlined.SelectAll
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
|
||||||
import androidx.compose.material3.surfaceColorAtElevation
|
import androidx.compose.material3.surfaceColorAtElevation
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -20,12 +14,12 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.kanade.presentation.components.AppBar
|
import eu.kanade.presentation.components.AppBar
|
||||||
import eu.kanade.presentation.components.AppBarActions
|
import eu.kanade.presentation.components.AppBarActions
|
||||||
|
import eu.kanade.presentation.components.AppBarTitle
|
||||||
import eu.kanade.presentation.components.DownloadDropdownMenu
|
import eu.kanade.presentation.components.DownloadDropdownMenu
|
||||||
import eu.kanade.presentation.components.UpIcon
|
|
||||||
import eu.kanade.presentation.manga.DownloadAction
|
import eu.kanade.presentation.manga.DownloadAction
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
@@ -36,15 +30,15 @@ import tachiyomi.presentation.core.theme.active
|
|||||||
@Composable
|
@Composable
|
||||||
fun MangaToolbar(
|
fun MangaToolbar(
|
||||||
title: String,
|
title: String,
|
||||||
titleAlphaProvider: () -> Float,
|
|
||||||
hasFilters: Boolean,
|
hasFilters: Boolean,
|
||||||
onBackClicked: () -> Unit,
|
navigateUp: () -> Unit,
|
||||||
onClickFilter: () -> Unit,
|
onClickFilter: () -> Unit,
|
||||||
onClickShare: (() -> Unit)?,
|
onClickShare: (() -> Unit)?,
|
||||||
onClickDownload: ((DownloadAction) -> Unit)?,
|
onClickDownload: ((DownloadAction) -> Unit)?,
|
||||||
onClickEditCategory: (() -> Unit)?,
|
onClickEditCategory: (() -> Unit)?,
|
||||||
onClickRefresh: () -> Unit,
|
onClickRefresh: () -> Unit,
|
||||||
onClickMigrate: (() -> Unit)?,
|
onClickMigrate: (() -> Unit)?,
|
||||||
|
onClickEditNotes: () -> Unit,
|
||||||
// SY -->
|
// SY -->
|
||||||
onClickEditInfo: (() -> Unit)?,
|
onClickEditInfo: (() -> Unit)?,
|
||||||
onClickRecommend: (() -> Unit)?,
|
onClickRecommend: (() -> Unit)?,
|
||||||
@@ -54,152 +48,151 @@ fun MangaToolbar(
|
|||||||
|
|
||||||
// For action mode
|
// For action mode
|
||||||
actionModeCounter: Int,
|
actionModeCounter: Int,
|
||||||
|
onCancelActionMode: () -> Unit,
|
||||||
onSelectAll: () -> Unit,
|
onSelectAll: () -> Unit,
|
||||||
onInvertSelection: () -> Unit,
|
onInvertSelection: () -> Unit,
|
||||||
|
|
||||||
|
titleAlphaProvider: () -> Float,
|
||||||
|
backgroundAlphaProvider: () -> Float,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
backgroundAlphaProvider: () -> Float = titleAlphaProvider,
|
|
||||||
) {
|
) {
|
||||||
Column(
|
val isActionMode = actionModeCounter > 0
|
||||||
|
AppBar(
|
||||||
|
titleContent = {
|
||||||
|
if (isActionMode) {
|
||||||
|
AppBarTitle(actionModeCounter.toString())
|
||||||
|
} else {
|
||||||
|
AppBarTitle(title, modifier = Modifier.alpha(titleAlphaProvider()))
|
||||||
|
}
|
||||||
|
},
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
) {
|
backgroundColor = MaterialTheme.colorScheme
|
||||||
val isActionMode = actionModeCounter > 0
|
.surfaceColorAtElevation(3.dp)
|
||||||
TopAppBar(
|
.copy(alpha = if (isActionMode) 1f else backgroundAlphaProvider()),
|
||||||
title = {
|
navigateUp = navigateUp,
|
||||||
Text(
|
actions = {
|
||||||
text = if (isActionMode) actionModeCounter.toString() else title,
|
var downloadExpanded by remember { mutableStateOf(false) }
|
||||||
maxLines = 1,
|
if (onClickDownload != null) {
|
||||||
overflow = TextOverflow.Ellipsis,
|
val onDismissRequest = { downloadExpanded = false }
|
||||||
color = LocalContentColor.current.copy(alpha = if (isActionMode) 1f else titleAlphaProvider()),
|
DownloadDropdownMenu(
|
||||||
|
expanded = downloadExpanded,
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
onDownloadClicked = onClickDownload,
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
navigationIcon = {
|
|
||||||
IconButton(onClick = onBackClicked) {
|
val filterTint = if (hasFilters) MaterialTheme.colorScheme.active else LocalContentColor.current
|
||||||
UpIcon(navigationIcon = Icons.Outlined.Close.takeIf { isActionMode })
|
AppBarActions(
|
||||||
}
|
actions = persistentListOf<AppBar.AppBarAction>().builder().apply {
|
||||||
},
|
if (isActionMode) {
|
||||||
actions = {
|
add(
|
||||||
if (isActionMode) {
|
|
||||||
AppBarActions(
|
|
||||||
persistentListOf(
|
|
||||||
AppBar.Action(
|
AppBar.Action(
|
||||||
title = stringResource(MR.strings.action_select_all),
|
title = stringResource(MR.strings.action_select_all),
|
||||||
icon = Icons.Outlined.SelectAll,
|
icon = Icons.Outlined.SelectAll,
|
||||||
onClick = onSelectAll,
|
onClick = onSelectAll,
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
add(
|
||||||
AppBar.Action(
|
AppBar.Action(
|
||||||
title = stringResource(MR.strings.action_select_inverse),
|
title = stringResource(MR.strings.action_select_inverse),
|
||||||
icon = Icons.Outlined.FlipToBack,
|
icon = Icons.Outlined.FlipToBack,
|
||||||
onClick = onInvertSelection,
|
onClick = onInvertSelection,
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
)
|
return@apply
|
||||||
} else {
|
}
|
||||||
var downloadExpanded by remember { mutableStateOf(false) }
|
|
||||||
if (onClickDownload != null) {
|
if (onClickDownload != null) {
|
||||||
val onDismissRequest = { downloadExpanded = false }
|
add(
|
||||||
DownloadDropdownMenu(
|
AppBar.Action(
|
||||||
expanded = downloadExpanded,
|
title = stringResource(MR.strings.manga_download),
|
||||||
onDismissRequest = onDismissRequest,
|
icon = Icons.Outlined.Download,
|
||||||
onDownloadClicked = onClickDownload,
|
onClick = { downloadExpanded = !downloadExpanded },
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
add(
|
||||||
val filterTint = if (hasFilters) MaterialTheme.colorScheme.active else LocalContentColor.current
|
AppBar.Action(
|
||||||
AppBarActions(
|
title = stringResource(MR.strings.action_filter),
|
||||||
actions = persistentListOf<AppBar.AppBarAction>().builder()
|
icon = Icons.Outlined.FilterList,
|
||||||
.apply {
|
iconTint = filterTint,
|
||||||
if (onClickDownload != null) {
|
onClick = onClickFilter,
|
||||||
add(
|
),
|
||||||
AppBar.Action(
|
|
||||||
title = stringResource(MR.strings.manga_download),
|
|
||||||
icon = Icons.Outlined.Download,
|
|
||||||
onClick = { downloadExpanded = !downloadExpanded },
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
add(
|
|
||||||
AppBar.Action(
|
|
||||||
title = stringResource(MR.strings.action_filter),
|
|
||||||
icon = Icons.Outlined.FilterList,
|
|
||||||
iconTint = filterTint,
|
|
||||||
onClick = onClickFilter,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
add(
|
|
||||||
AppBar.OverflowAction(
|
|
||||||
title = stringResource(MR.strings.action_webview_refresh),
|
|
||||||
onClick = onClickRefresh,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if (onClickEditCategory != null) {
|
|
||||||
add(
|
|
||||||
AppBar.OverflowAction(
|
|
||||||
title = stringResource(MR.strings.action_edit_categories),
|
|
||||||
onClick = onClickEditCategory,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (onClickMigrate != null) {
|
|
||||||
add(
|
|
||||||
AppBar.OverflowAction(
|
|
||||||
title = stringResource(MR.strings.action_migrate),
|
|
||||||
onClick = onClickMigrate,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (onClickShare != null) {
|
|
||||||
add(
|
|
||||||
AppBar.OverflowAction(
|
|
||||||
title = stringResource(MR.strings.action_share),
|
|
||||||
onClick = onClickShare,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// SY -->
|
|
||||||
if (onClickMerge != null) {
|
|
||||||
add(
|
|
||||||
AppBar.OverflowAction(
|
|
||||||
title = stringResource(SYMR.strings.merge),
|
|
||||||
onClick = onClickMerge,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (onClickEditInfo != null) {
|
|
||||||
add(
|
|
||||||
AppBar.OverflowAction(
|
|
||||||
title = stringResource(SYMR.strings.action_edit_info),
|
|
||||||
onClick = onClickEditInfo,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (onClickRecommend != null) {
|
|
||||||
add(
|
|
||||||
AppBar.OverflowAction(
|
|
||||||
title = stringResource(SYMR.strings.az_recommends),
|
|
||||||
onClick = onClickRecommend,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (onClickMergedSettings != null) {
|
|
||||||
add(
|
|
||||||
AppBar.OverflowAction(
|
|
||||||
title = stringResource(SYMR.strings.merge_settings),
|
|
||||||
onClick = onClickMergedSettings,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// SY <--
|
|
||||||
}
|
|
||||||
.build(),
|
|
||||||
)
|
)
|
||||||
|
add(
|
||||||
|
AppBar.OverflowAction(
|
||||||
|
title = stringResource(MR.strings.action_webview_refresh),
|
||||||
|
onClick = onClickRefresh,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if (onClickEditCategory != null) {
|
||||||
|
add(
|
||||||
|
AppBar.OverflowAction(
|
||||||
|
title = stringResource(MR.strings.action_edit_categories),
|
||||||
|
onClick = onClickEditCategory,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (onClickMigrate != null) {
|
||||||
|
add(
|
||||||
|
AppBar.OverflowAction(
|
||||||
|
title = stringResource(MR.strings.action_migrate),
|
||||||
|
onClick = onClickMigrate,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (onClickShare != null) {
|
||||||
|
add(
|
||||||
|
AppBar.OverflowAction(
|
||||||
|
title = stringResource(MR.strings.action_share),
|
||||||
|
onClick = onClickShare,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
add(
|
||||||
|
AppBar.OverflowAction(
|
||||||
|
title = stringResource(MR.strings.action_notes),
|
||||||
|
onClick = onClickEditNotes,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
// SY -->
|
||||||
|
if (onClickMerge != null) {
|
||||||
|
add(
|
||||||
|
AppBar.OverflowAction(
|
||||||
|
title = stringResource(SYMR.strings.merge),
|
||||||
|
onClick = onClickMerge,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (onClickEditInfo != null) {
|
||||||
|
add(
|
||||||
|
AppBar.OverflowAction(
|
||||||
|
title = stringResource(SYMR.strings.action_edit_info),
|
||||||
|
onClick = onClickEditInfo,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (onClickRecommend != null) {
|
||||||
|
add(
|
||||||
|
AppBar.OverflowAction(
|
||||||
|
title = stringResource(SYMR.strings.az_recommends),
|
||||||
|
onClick = onClickRecommend,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (onClickMergedSettings != null) {
|
||||||
|
add(
|
||||||
|
AppBar.OverflowAction(
|
||||||
|
title = stringResource(SYMR.strings.merge_settings),
|
||||||
|
onClick = onClickMergedSettings,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// SY <--
|
||||||
}
|
}
|
||||||
},
|
.build(),
|
||||||
colors = TopAppBarDefaults.topAppBarColors(
|
)
|
||||||
containerColor = MaterialTheme.colorScheme
|
},
|
||||||
.surfaceColorAtElevation(3.dp)
|
isActionMode = isActionMode,
|
||||||
.copy(alpha = if (isActionMode) 1f else backgroundAlphaProvider()),
|
onCancelActionMode = onCancelActionMode,
|
||||||
),
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,286 @@
|
|||||||
|
package eu.kanade.presentation.manga.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.text.InlineTextContent
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Image
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.ReadOnlyComposable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.layout.FirstBaseline
|
||||||
|
import androidx.compose.ui.text.Placeholder
|
||||||
|
import androidx.compose.ui.text.PlaceholderVerticalAlign
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import androidx.compose.ui.text.TextLinkStyles
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.mikepenz.markdown.coil3.Coil3ImageTransformerImpl
|
||||||
|
import com.mikepenz.markdown.compose.LocalBulletListHandler
|
||||||
|
import com.mikepenz.markdown.compose.Markdown
|
||||||
|
import com.mikepenz.markdown.compose.components.markdownComponents
|
||||||
|
import com.mikepenz.markdown.compose.elements.MarkdownBulletList
|
||||||
|
import com.mikepenz.markdown.compose.elements.MarkdownDivider
|
||||||
|
import com.mikepenz.markdown.compose.elements.MarkdownOrderedList
|
||||||
|
import com.mikepenz.markdown.compose.elements.MarkdownTable
|
||||||
|
import com.mikepenz.markdown.compose.elements.MarkdownTableHeader
|
||||||
|
import com.mikepenz.markdown.compose.elements.MarkdownTableRow
|
||||||
|
import com.mikepenz.markdown.compose.elements.MarkdownText
|
||||||
|
import com.mikepenz.markdown.compose.elements.listDepth
|
||||||
|
import com.mikepenz.markdown.model.DefaultMarkdownColors
|
||||||
|
import com.mikepenz.markdown.model.DefaultMarkdownInlineContent
|
||||||
|
import com.mikepenz.markdown.model.DefaultMarkdownTypography
|
||||||
|
import com.mikepenz.markdown.model.MarkdownAnnotator
|
||||||
|
import com.mikepenz.markdown.model.MarkdownColors
|
||||||
|
import com.mikepenz.markdown.model.MarkdownPadding
|
||||||
|
import com.mikepenz.markdown.model.MarkdownTypography
|
||||||
|
import com.mikepenz.markdown.model.NoOpImageTransformerImpl
|
||||||
|
import com.mikepenz.markdown.model.markdownAnnotator
|
||||||
|
import com.mikepenz.markdown.model.rememberMarkdownState
|
||||||
|
import org.intellij.markdown.MarkdownTokenTypes.Companion.HTML_TAG
|
||||||
|
import org.intellij.markdown.flavours.MarkdownFlavourDescriptor
|
||||||
|
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
|
||||||
|
import org.intellij.markdown.flavours.commonmark.CommonMarkMarkerProcessor
|
||||||
|
import org.intellij.markdown.flavours.gfm.table.GitHubTableMarkerProvider
|
||||||
|
import org.intellij.markdown.parser.MarkerProcessor
|
||||||
|
import org.intellij.markdown.parser.MarkerProcessorFactory
|
||||||
|
import org.intellij.markdown.parser.ProductionHolder
|
||||||
|
import org.intellij.markdown.parser.constraints.CommonMarkdownConstraints
|
||||||
|
import org.intellij.markdown.parser.constraints.MarkdownConstraints
|
||||||
|
import org.intellij.markdown.parser.markerblocks.MarkerBlockProvider
|
||||||
|
import org.intellij.markdown.parser.markerblocks.providers.AtxHeaderProvider
|
||||||
|
import org.intellij.markdown.parser.markerblocks.providers.BlockQuoteProvider
|
||||||
|
import org.intellij.markdown.parser.markerblocks.providers.CodeBlockProvider
|
||||||
|
import org.intellij.markdown.parser.markerblocks.providers.CodeFenceProvider
|
||||||
|
import org.intellij.markdown.parser.markerblocks.providers.HorizontalRuleProvider
|
||||||
|
import org.intellij.markdown.parser.markerblocks.providers.ListMarkerProvider
|
||||||
|
import org.intellij.markdown.parser.markerblocks.providers.SetextHeaderProvider
|
||||||
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
|
|
||||||
|
const val MARKDOWN_INLINE_IMAGE_TAG = "MARKDOWN_INLINE_IMAGE"
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MarkdownRender(
|
||||||
|
content: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
flavour: MarkdownFlavourDescriptor = SimpleMarkdownFlavourDescriptor,
|
||||||
|
annotator: MarkdownAnnotator = remember { markdownAnnotator() },
|
||||||
|
loadImages: Boolean = true,
|
||||||
|
) {
|
||||||
|
Markdown(
|
||||||
|
markdownState = rememberMarkdownState(
|
||||||
|
content = content,
|
||||||
|
flavour = flavour,
|
||||||
|
immediate = true,
|
||||||
|
),
|
||||||
|
annotator = annotator,
|
||||||
|
colors = getMarkdownColors(),
|
||||||
|
typography = getMarkdownTypography(),
|
||||||
|
padding = markdownPadding,
|
||||||
|
components = markdownComponents,
|
||||||
|
imageTransformer = remember(loadImages) {
|
||||||
|
if (loadImages) Coil3ImageTransformerImpl else NoOpImageTransformerImpl()
|
||||||
|
},
|
||||||
|
inlineContent = getMarkdownInlineContent(),
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@ReadOnlyComposable
|
||||||
|
private fun getMarkdownColors(): MarkdownColors {
|
||||||
|
val codeBackground = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f)
|
||||||
|
return DefaultMarkdownColors(
|
||||||
|
text = MaterialTheme.colorScheme.onSurface,
|
||||||
|
codeBackground = codeBackground,
|
||||||
|
inlineCodeBackground = codeBackground,
|
||||||
|
dividerColor = MaterialTheme.colorScheme.outlineVariant,
|
||||||
|
tableBackground = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.05f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@ReadOnlyComposable
|
||||||
|
fun getMarkdownLinkStyle() = MaterialTheme.typography.bodyMedium.copy(
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@ReadOnlyComposable
|
||||||
|
private fun getMarkdownTypography(): MarkdownTypography {
|
||||||
|
val link = getMarkdownLinkStyle()
|
||||||
|
return DefaultMarkdownTypography(
|
||||||
|
h1 = MaterialTheme.typography.headlineMedium,
|
||||||
|
h2 = MaterialTheme.typography.headlineSmall,
|
||||||
|
h3 = MaterialTheme.typography.titleLarge,
|
||||||
|
h4 = MaterialTheme.typography.titleMedium,
|
||||||
|
h5 = MaterialTheme.typography.titleSmall,
|
||||||
|
h6 = MaterialTheme.typography.bodyLarge,
|
||||||
|
text = MaterialTheme.typography.bodyMedium,
|
||||||
|
code = MaterialTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Monospace),
|
||||||
|
inlineCode = MaterialTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Monospace),
|
||||||
|
quote = MaterialTheme.typography.bodyMedium.plus(SpanStyle(fontStyle = FontStyle.Italic)),
|
||||||
|
paragraph = MaterialTheme.typography.bodyMedium,
|
||||||
|
ordered = MaterialTheme.typography.bodyMedium,
|
||||||
|
bullet = MaterialTheme.typography.bodyMedium,
|
||||||
|
list = MaterialTheme.typography.bodyMedium,
|
||||||
|
textLink = TextLinkStyles(style = link.toSpanStyle()),
|
||||||
|
table = MaterialTheme.typography.bodyMedium,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val markdownPadding = object : MarkdownPadding {
|
||||||
|
override val block: Dp = 2.dp
|
||||||
|
override val blockQuote: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 0.dp)
|
||||||
|
override val blockQuoteBar: PaddingValues.Absolute = PaddingValues.Absolute(
|
||||||
|
left = 4.dp,
|
||||||
|
top = 2.dp,
|
||||||
|
right = 4.dp,
|
||||||
|
bottom = 2.dp,
|
||||||
|
)
|
||||||
|
override val blockQuoteText: PaddingValues = PaddingValues(vertical = 4.dp)
|
||||||
|
override val codeBlock: PaddingValues = PaddingValues(8.dp)
|
||||||
|
override val list: Dp = 0.dp
|
||||||
|
override val listIndent: Dp = 8.dp
|
||||||
|
override val listItemBottom: Dp = 0.dp
|
||||||
|
override val listItemTop: Dp = 0.dp
|
||||||
|
}
|
||||||
|
|
||||||
|
private val markdownComponents = markdownComponents(
|
||||||
|
horizontalRule = {
|
||||||
|
MarkdownDivider(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(vertical = MaterialTheme.padding.extraSmall)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
orderedList = { ol ->
|
||||||
|
Column(modifier = Modifier.padding(start = MaterialTheme.padding.small)) {
|
||||||
|
MarkdownOrderedList(
|
||||||
|
content = ol.content,
|
||||||
|
node = ol.node,
|
||||||
|
style = ol.typography.ordered,
|
||||||
|
depth = ol.listDepth,
|
||||||
|
markerModifier = { Modifier.alignBy(FirstBaseline) },
|
||||||
|
listModifier = { Modifier.alignBy(FirstBaseline) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unorderedList = { ul ->
|
||||||
|
val markers = listOf("•", "◦", "▸", "▹")
|
||||||
|
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalBulletListHandler provides { _, _, _, _, _ -> "${markers[ul.listDepth % markers.size]} " },
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.padding(start = MaterialTheme.padding.small)) {
|
||||||
|
MarkdownBulletList(
|
||||||
|
content = ul.content,
|
||||||
|
node = ul.node,
|
||||||
|
style = ul.typography.bullet,
|
||||||
|
markerModifier = { Modifier.alignBy(FirstBaseline) },
|
||||||
|
listModifier = { Modifier.alignBy(FirstBaseline) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
table = { t ->
|
||||||
|
MarkdownTable(
|
||||||
|
content = t.content,
|
||||||
|
node = t.node,
|
||||||
|
style = t.typography.text,
|
||||||
|
headerBlock = { content, header, tableWidth, style ->
|
||||||
|
MarkdownTableHeader(
|
||||||
|
content = content,
|
||||||
|
header = header,
|
||||||
|
tableWidth = tableWidth,
|
||||||
|
style = style,
|
||||||
|
maxLines = Int.MAX_VALUE,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
rowBlock = { content, header, tableWidth, style ->
|
||||||
|
MarkdownTableRow(
|
||||||
|
content = content,
|
||||||
|
header = header,
|
||||||
|
tableWidth = tableWidth,
|
||||||
|
style = style,
|
||||||
|
maxLines = Int.MAX_VALUE,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
custom = { type, model ->
|
||||||
|
if (type in DISALLOWED_MARKDOWN_TYPES) {
|
||||||
|
MarkdownText(
|
||||||
|
content = model.content.substring(model.node.startOffset, model.node.endOffset),
|
||||||
|
style = model.typography.text,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@ReadOnlyComposable
|
||||||
|
private fun getMarkdownInlineContent() = DefaultMarkdownInlineContent(
|
||||||
|
inlineContent = mapOf(
|
||||||
|
MARKDOWN_INLINE_IMAGE_TAG to InlineTextContent(
|
||||||
|
placeholder = Placeholder(
|
||||||
|
width = MaterialTheme.typography.bodyMedium.fontSize * 1.25,
|
||||||
|
height = MaterialTheme.typography.bodyMedium.fontSize * 1.25,
|
||||||
|
placeholderVerticalAlign = PlaceholderVerticalAlign.TextCenter,
|
||||||
|
),
|
||||||
|
children = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Image,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
private object SimpleMarkdownFlavourDescriptor : CommonMarkFlavourDescriptor() {
|
||||||
|
override val markerProcessorFactory: MarkerProcessorFactory = SimpleMarkdownProcessFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
private object SimpleMarkdownProcessFactory : MarkerProcessorFactory {
|
||||||
|
override fun createMarkerProcessor(productionHolder: ProductionHolder): MarkerProcessor<*> {
|
||||||
|
return SimpleMarkdownMarkerProcessor(productionHolder, CommonMarkdownConstraints.BASE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like `CommonMarkFlavour`, but with html blocks and reference links removed and
|
||||||
|
* table support added
|
||||||
|
*/
|
||||||
|
private class SimpleMarkdownMarkerProcessor(
|
||||||
|
productionHolder: ProductionHolder,
|
||||||
|
constraints: MarkdownConstraints,
|
||||||
|
) : CommonMarkMarkerProcessor(productionHolder, constraints) {
|
||||||
|
private val markerBlockProviders = listOf(
|
||||||
|
CodeBlockProvider(),
|
||||||
|
HorizontalRuleProvider(),
|
||||||
|
CodeFenceProvider(),
|
||||||
|
SetextHeaderProvider(),
|
||||||
|
BlockQuoteProvider(),
|
||||||
|
ListMarkerProvider(),
|
||||||
|
AtxHeaderProvider(),
|
||||||
|
GitHubTableMarkerProvider(),
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun getMarkerBlockProviders(): List<MarkerBlockProvider<StateInfo>> {
|
||||||
|
return markerBlockProviders
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val DISALLOWED_MARKDOWN_TYPES = arrayOf(HTML_TAG)
|
||||||
@@ -17,7 +17,6 @@ import androidx.compose.runtime.CompositionLocalProvider
|
|||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -25,8 +24,6 @@ import eu.kanade.presentation.components.ChipBorder
|
|||||||
import eu.kanade.presentation.components.SuggestionChip
|
import eu.kanade.presentation.components.SuggestionChip
|
||||||
import eu.kanade.presentation.components.SuggestionChipDefaults
|
import eu.kanade.presentation.components.SuggestionChipDefaults
|
||||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||||
import eu.kanade.tachiyomi.source.Source
|
|
||||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
|
||||||
import exh.metadata.metadata.EHentaiSearchMetadata
|
import exh.metadata.metadata.EHentaiSearchMetadata
|
||||||
import exh.metadata.metadata.RaisedSearchMetadata
|
import exh.metadata.metadata.RaisedSearchMetadata
|
||||||
import exh.metadata.metadata.base.RaisedTag
|
import exh.metadata.metadata.base.RaisedTag
|
||||||
@@ -49,7 +46,7 @@ value class SearchMetadataChips(
|
|||||||
val tags: Map<String, List<DisplayTag>>,
|
val tags: Map<String, List<DisplayTag>>,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
operator fun invoke(meta: RaisedSearchMetadata?, source: Source, tags: List<String>?): SearchMetadataChips? {
|
operator fun invoke(meta: RaisedSearchMetadata?, sourceId: Long, tags: List<String>?): SearchMetadataChips? {
|
||||||
return if (meta != null) {
|
return if (meta != null) {
|
||||||
SearchMetadataChips(
|
SearchMetadataChips(
|
||||||
meta.tags
|
meta.tags
|
||||||
@@ -59,11 +56,11 @@ value class SearchMetadataChips(
|
|||||||
namespace = it.namespace,
|
namespace = it.namespace,
|
||||||
text = it.name,
|
text = it.name,
|
||||||
search = if (!it.namespace.isNullOrEmpty()) {
|
search = if (!it.namespace.isNullOrEmpty()) {
|
||||||
SourceTagsUtil.getWrappedTag(source.id, namespace = it.namespace, tag = it.name)
|
SourceTagsUtil.getWrappedTag(sourceId, namespace = it.namespace, tag = it.name)
|
||||||
} else {
|
} else {
|
||||||
SourceTagsUtil.getWrappedTag(source.id, fullTag = it.name)
|
SourceTagsUtil.getWrappedTag(sourceId, fullTag = it.name)
|
||||||
} ?: it.name,
|
} ?: it.name,
|
||||||
border = if (source.id == EXH_SOURCE_ID || source.id == EH_SOURCE_ID) {
|
border = if (sourceId == EXH_SOURCE_ID || sourceId == EH_SOURCE_ID) {
|
||||||
when (it.type) {
|
when (it.type) {
|
||||||
EHentaiSearchMetadata.TAG_TYPE_NORMAL -> 2
|
EHentaiSearchMetadata.TAG_TYPE_NORMAL -> 2
|
||||||
EHentaiSearchMetadata.TAG_TYPE_LIGHT -> 1
|
EHentaiSearchMetadata.TAG_TYPE_LIGHT -> 1
|
||||||
@@ -178,7 +175,6 @@ fun TagsChip(
|
|||||||
fun NamespaceTagsPreview() {
|
fun NamespaceTagsPreview() {
|
||||||
TachiyomiPreviewTheme {
|
TachiyomiPreviewTheme {
|
||||||
Surface {
|
Surface {
|
||||||
val context = LocalContext.current
|
|
||||||
NamespaceTags(
|
NamespaceTags(
|
||||||
tags = remember {
|
tags = remember {
|
||||||
EHentaiSearchMetadata().apply {
|
EHentaiSearchMetadata().apply {
|
||||||
@@ -216,7 +212,7 @@ fun NamespaceTagsPreview() {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}.let { SearchMetadataChips(it, EHentai(EXH_SOURCE_ID, true, context), emptyList()) }!!
|
}.let { SearchMetadataChips(it, EXH_SOURCE_ID, emptyList()) }!!
|
||||||
},
|
},
|
||||||
onClick = {},
|
onClick = {},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,25 +1,15 @@
|
|||||||
package eu.kanade.presentation.more
|
package eu.kanade.presentation.more
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
|
||||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
|
||||||
import androidx.compose.foundation.layout.only
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.systemBars
|
|
||||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
|
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
|
||||||
import androidx.compose.material.icons.automirrored.outlined.Label
|
import androidx.compose.material.icons.automirrored.outlined.Label
|
||||||
import androidx.compose.material.icons.automirrored.outlined.PlaylistAdd
|
import androidx.compose.material.icons.automirrored.outlined.PlaylistAdd
|
||||||
import androidx.compose.material.icons.outlined.CloudOff
|
import androidx.compose.material.icons.outlined.CloudOff
|
||||||
import androidx.compose.material.icons.outlined.GetApp
|
import androidx.compose.material.icons.outlined.GetApp
|
||||||
import androidx.compose.material.icons.outlined.HelpOutline
|
|
||||||
import androidx.compose.material.icons.outlined.History
|
import androidx.compose.material.icons.outlined.History
|
||||||
import androidx.compose.material.icons.outlined.Info
|
import androidx.compose.material.icons.outlined.Info
|
||||||
import androidx.compose.material.icons.outlined.Label
|
|
||||||
import androidx.compose.material.icons.outlined.NewReleases
|
import androidx.compose.material.icons.outlined.NewReleases
|
||||||
import androidx.compose.material.icons.outlined.PlaylistAdd
|
|
||||||
import androidx.compose.material.icons.outlined.QueryStats
|
import androidx.compose.material.icons.outlined.QueryStats
|
||||||
import androidx.compose.material.icons.outlined.Settings
|
import androidx.compose.material.icons.outlined.Settings
|
||||||
import androidx.compose.material.icons.outlined.Storage
|
import androidx.compose.material.icons.outlined.Storage
|
||||||
@@ -29,7 +19,6 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import androidx.compose.ui.res.vectorResource
|
import androidx.compose.ui.res.vectorResource
|
||||||
import eu.kanade.presentation.components.WarningBanner
|
|
||||||
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
|
||||||
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
@@ -49,7 +38,6 @@ fun MoreScreen(
|
|||||||
onDownloadedOnlyChange: (Boolean) -> Unit,
|
onDownloadedOnlyChange: (Boolean) -> Unit,
|
||||||
incognitoMode: Boolean,
|
incognitoMode: Boolean,
|
||||||
onIncognitoModeChange: (Boolean) -> Unit,
|
onIncognitoModeChange: (Boolean) -> Unit,
|
||||||
isFDroid: Boolean,
|
|
||||||
// SY -->
|
// SY -->
|
||||||
showNavUpdates: Boolean,
|
showNavUpdates: Boolean,
|
||||||
showNavHistory: Boolean,
|
showNavHistory: Boolean,
|
||||||
@@ -66,26 +54,7 @@ fun MoreScreen(
|
|||||||
) {
|
) {
|
||||||
val uriHandler = LocalUriHandler.current
|
val uriHandler = LocalUriHandler.current
|
||||||
|
|
||||||
Scaffold(
|
Scaffold { contentPadding ->
|
||||||
topBar = {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.windowInsetsPadding(
|
|
||||||
WindowInsets.systemBars.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
if (isFDroid) {
|
|
||||||
WarningBanner(
|
|
||||||
textRes = MR.strings.fdroid_warning,
|
|
||||||
modifier = Modifier.clickable {
|
|
||||||
uriHandler.openUri(
|
|
||||||
"https://mihon.app/docs/faq/general#how-do-i-update-from-the-f-droid-builds",
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
) { contentPadding ->
|
|
||||||
ScrollbarLazyColumn(
|
ScrollbarLazyColumn(
|
||||||
modifier = Modifier.padding(contentPadding),
|
modifier = Modifier.padding(contentPadding),
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package eu.kanade.presentation.more
|
package eu.kanade.presentation.more
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
@@ -13,13 +14,10 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.SpanStyle
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||||
import com.halilibo.richtext.markdown.Markdown
|
import eu.kanade.presentation.manga.components.MarkdownRender
|
||||||
import com.halilibo.richtext.ui.RichTextStyle
|
|
||||||
import com.halilibo.richtext.ui.material3.RichText
|
|
||||||
import com.halilibo.richtext.ui.string.RichTextStringStyle
|
|
||||||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||||
|
import org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
@@ -42,17 +40,15 @@ fun NewUpdateScreen(
|
|||||||
rejectText = stringResource(MR.strings.action_not_now),
|
rejectText = stringResource(MR.strings.action_not_now),
|
||||||
onRejectClick = onRejectUpdate,
|
onRejectClick = onRejectUpdate,
|
||||||
) {
|
) {
|
||||||
RichText(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(vertical = MaterialTheme.padding.large),
|
.padding(vertical = MaterialTheme.padding.large),
|
||||||
style = RichTextStyle(
|
|
||||||
stringStyle = RichTextStringStyle(
|
|
||||||
linkStyle = SpanStyle(color = MaterialTheme.colorScheme.primary),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
) {
|
) {
|
||||||
Markdown(content = changelogInfo)
|
MarkdownRender(
|
||||||
|
content = changelogInfo,
|
||||||
|
flavour = GFMFlavourDescriptor(),
|
||||||
|
)
|
||||||
|
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = onOpenInBrowser,
|
onClick = onOpenInBrowser,
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ fun OnboardingScreen(
|
|||||||
}
|
}
|
||||||
val isLastStep = currentStep == steps.lastIndex
|
val isLastStep = currentStep == steps.lastIndex
|
||||||
|
|
||||||
BackHandler(enabled = currentStep != 0, onBack = { currentStep-- })
|
BackHandler(enabled = currentStep != 0) {
|
||||||
|
currentStep--
|
||||||
|
}
|
||||||
|
|
||||||
InfoScreen(
|
InfoScreen(
|
||||||
icon = Icons.Outlined.RocketLaunch,
|
icon = Icons.Outlined.RocketLaunch,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.Manifest
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
@@ -32,6 +31,7 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
@@ -111,7 +111,7 @@ internal class PermissionStep : OnboardingStep {
|
|||||||
onButtonClick = {
|
onButtonClick = {
|
||||||
@SuppressLint("BatteryLife")
|
@SuppressLint("BatteryLife")
|
||||||
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
|
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
|
||||||
data = Uri.parse("package:${context.packageName}")
|
data = "package:${context.packageName}".toUri()
|
||||||
}
|
}
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package eu.kanade.presentation.more.settings
|
package eu.kanade.presentation.more.settings
|
||||||
|
|
||||||
|
import androidx.annotation.IntRange
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
@@ -14,13 +15,13 @@ sealed class Preference {
|
|||||||
abstract val title: String
|
abstract val title: String
|
||||||
abstract val enabled: Boolean
|
abstract val enabled: Boolean
|
||||||
|
|
||||||
sealed class PreferenceItem<T> : Preference() {
|
sealed class PreferenceItem<T, R> : Preference() {
|
||||||
// SY -->
|
// SY -->
|
||||||
abstract val subtitle: CharSequence?
|
abstract val subtitle: CharSequence?
|
||||||
|
|
||||||
// SY <--
|
// SY <--
|
||||||
abstract val icon: ImageVector?
|
abstract val icon: ImageVector?
|
||||||
abstract val onValueChanged: suspend (newValue: T) -> Boolean
|
abstract val onValueChanged: suspend (value: T) -> R
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic [PreferenceItem] that only displays texts.
|
* A basic [PreferenceItem] that only displays texts.
|
||||||
@@ -28,57 +29,59 @@ sealed class Preference {
|
|||||||
data class TextPreference(
|
data class TextPreference(
|
||||||
override val title: String,
|
override val title: String,
|
||||||
override val subtitle: CharSequence? = null,
|
override val subtitle: CharSequence? = null,
|
||||||
override val icon: ImageVector? = null,
|
|
||||||
override val enabled: Boolean = true,
|
override val enabled: Boolean = true,
|
||||||
override val onValueChanged: suspend (newValue: String) -> Boolean = { true },
|
|
||||||
|
|
||||||
val onClick: (() -> Unit)? = null,
|
val onClick: (() -> Unit)? = null,
|
||||||
) : PreferenceItem<String>()
|
) : PreferenceItem<String, Unit>() {
|
||||||
|
override val icon: ImageVector? = null
|
||||||
|
override val onValueChanged: suspend (value: String) -> Unit = {}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [PreferenceItem] that provides a two-state toggleable option.
|
* A [PreferenceItem] that provides a two-state toggleable option.
|
||||||
*/
|
*/
|
||||||
data class SwitchPreference(
|
data class SwitchPreference(
|
||||||
val pref: PreferenceData<Boolean>,
|
val preference: PreferenceData<Boolean>,
|
||||||
override val title: String,
|
override val title: String,
|
||||||
override val subtitle: CharSequence? = null,
|
override val subtitle: CharSequence? = null,
|
||||||
override val icon: ImageVector? = null,
|
|
||||||
override val enabled: Boolean = true,
|
override val enabled: Boolean = true,
|
||||||
override val onValueChanged: suspend (newValue: Boolean) -> Boolean = { true },
|
override val onValueChanged: suspend (value: Boolean) -> Boolean = { true },
|
||||||
) : PreferenceItem<Boolean>()
|
) : PreferenceItem<Boolean, Boolean>() {
|
||||||
|
override val icon: ImageVector? = null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [PreferenceItem] that provides a slider to select an integer number.
|
* A [PreferenceItem] that provides a slider to select an integer number.
|
||||||
*/
|
*/
|
||||||
data class SliderPreference(
|
data class SliderPreference(
|
||||||
val value: Int,
|
val value: Int,
|
||||||
val min: Int = 0,
|
override val title: String,
|
||||||
val max: Int,
|
|
||||||
override val title: String = "",
|
|
||||||
override val subtitle: String? = null,
|
override val subtitle: String? = null,
|
||||||
override val icon: ImageVector? = null,
|
val valueString: String? = null,
|
||||||
|
val valueRange: IntProgression = 0..1,
|
||||||
|
@IntRange(from = 0) val steps: Int = with(valueRange) { (last - first) - 1 },
|
||||||
override val enabled: Boolean = true,
|
override val enabled: Boolean = true,
|
||||||
override val onValueChanged: suspend (newValue: Int) -> Boolean = { true },
|
override val onValueChanged: suspend (value: Int) -> Unit = {},
|
||||||
) : PreferenceItem<Int>()
|
) : PreferenceItem<Int, Unit>() {
|
||||||
|
override val icon: ImageVector? = null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [PreferenceItem] that displays a list of entries as a dialog.
|
* A [PreferenceItem] that displays a list of entries as a dialog.
|
||||||
*/
|
*/
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
data class ListPreference<T>(
|
data class ListPreference<T>(
|
||||||
val pref: PreferenceData<T>,
|
val preference: PreferenceData<T>,
|
||||||
|
val entries: ImmutableMap<T, String>,
|
||||||
override val title: String,
|
override val title: String,
|
||||||
override val subtitle: String? = "%s",
|
override val subtitle: String? = "%s",
|
||||||
val subtitleProvider: @Composable (value: T, entries: ImmutableMap<T, String>) -> String? =
|
val subtitleProvider: @Composable (value: T, entries: ImmutableMap<T, String>) -> String? =
|
||||||
{ v, e -> subtitle?.format(e[v]) },
|
{ v, e -> subtitle?.format(e[v]) },
|
||||||
override val icon: ImageVector? = null,
|
override val icon: ImageVector? = null,
|
||||||
override val enabled: Boolean = true,
|
override val enabled: Boolean = true,
|
||||||
override val onValueChanged: suspend (newValue: T) -> Boolean = { true },
|
override val onValueChanged: suspend (value: T) -> Boolean = { true },
|
||||||
|
) : PreferenceItem<T, Boolean>() {
|
||||||
val entries: ImmutableMap<T, String>,
|
internal fun internalSet(value: Any) = preference.set(value as T)
|
||||||
) : PreferenceItem<T>() {
|
internal suspend fun internalOnValueChanged(value: Any) = onValueChanged(value as T)
|
||||||
internal fun internalSet(newValue: Any) = pref.set(newValue as T)
|
|
||||||
internal suspend fun internalOnValueChanged(newValue: Any) = onValueChanged(newValue as T)
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun internalSubtitleProvider(value: Any?, entries: ImmutableMap<out Any?, String>) =
|
internal fun internalSubtitleProvider(value: Any?, entries: ImmutableMap<out Any?, String>) =
|
||||||
@@ -90,87 +93,85 @@ sealed class Preference {
|
|||||||
*/
|
*/
|
||||||
data class BasicListPreference(
|
data class BasicListPreference(
|
||||||
val value: String,
|
val value: String,
|
||||||
|
val entries: ImmutableMap<String, String>,
|
||||||
override val title: String,
|
override val title: String,
|
||||||
override val subtitle: String? = "%s",
|
override val subtitle: String? = "%s",
|
||||||
val subtitleProvider: @Composable (value: String, entries: ImmutableMap<String, String>) -> String? =
|
val subtitleProvider: @Composable (value: String, entries: ImmutableMap<String, String>) -> String? =
|
||||||
{ v, e -> subtitle?.format(e[v]) },
|
{ v, e -> subtitle?.format(e[v]) },
|
||||||
override val icon: ImageVector? = null,
|
override val icon: ImageVector? = null,
|
||||||
override val enabled: Boolean = true,
|
override val enabled: Boolean = true,
|
||||||
override val onValueChanged: suspend (newValue: String) -> Boolean = { true },
|
override val onValueChanged: suspend (value: String) -> Unit = {},
|
||||||
|
) : PreferenceItem<String, Unit>()
|
||||||
val entries: ImmutableMap<String, String>,
|
|
||||||
) : PreferenceItem<String>()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [PreferenceItem] that displays a list of entries as a dialog.
|
* A [PreferenceItem] that displays a list of entries as a dialog.
|
||||||
* Multiple entries can be selected at the same time.
|
* Multiple entries can be selected at the same time.
|
||||||
*/
|
*/
|
||||||
data class MultiSelectListPreference(
|
data class MultiSelectListPreference(
|
||||||
val pref: PreferenceData<Set<String>>,
|
val preference: PreferenceData<Set<String>>,
|
||||||
|
val entries: ImmutableMap<String, String>,
|
||||||
override val title: String,
|
override val title: String,
|
||||||
override val subtitle: String? = "%s",
|
override val subtitle: String? = "%s",
|
||||||
val subtitleProvider: @Composable (
|
val subtitleProvider: @Composable (value: Set<String>, entries: ImmutableMap<String, String>) -> String? =
|
||||||
value: Set<String>,
|
{ v, e ->
|
||||||
entries: ImmutableMap<String, String>,
|
val combined = remember(v, e) {
|
||||||
) -> String? = { v, e ->
|
v.mapNotNull { e[it] }
|
||||||
val combined = remember(v) {
|
.joinToString()
|
||||||
v.map { e[it] }
|
.takeUnless { it.isBlank() }
|
||||||
.takeIf { it.isNotEmpty() }
|
}
|
||||||
?.joinToString()
|
?: stringResource(MR.strings.none)
|
||||||
} ?: stringResource(MR.strings.none)
|
subtitle?.format(combined)
|
||||||
subtitle?.format(combined)
|
},
|
||||||
},
|
|
||||||
override val icon: ImageVector? = null,
|
override val icon: ImageVector? = null,
|
||||||
override val enabled: Boolean = true,
|
override val enabled: Boolean = true,
|
||||||
override val onValueChanged: suspend (newValue: Set<String>) -> Boolean = { true },
|
override val onValueChanged: suspend (value: Set<String>) -> Boolean = { true },
|
||||||
|
) : PreferenceItem<Set<String>, Boolean>()
|
||||||
val entries: ImmutableMap<String, String>,
|
|
||||||
) : PreferenceItem<Set<String>>()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [PreferenceItem] that shows a EditText in the dialog.
|
* A [PreferenceItem] that shows a EditText in the dialog.
|
||||||
*/
|
*/
|
||||||
data class EditTextPreference(
|
data class EditTextPreference(
|
||||||
val pref: PreferenceData<String>,
|
val preference: PreferenceData<String>,
|
||||||
override val title: String,
|
override val title: String,
|
||||||
override val subtitle: String? = "%s",
|
override val subtitle: String? = "%s",
|
||||||
override val icon: ImageVector? = null,
|
|
||||||
override val enabled: Boolean = true,
|
override val enabled: Boolean = true,
|
||||||
override val onValueChanged: suspend (newValue: String) -> Boolean = { true },
|
override val onValueChanged: suspend (value: String) -> Boolean = { true },
|
||||||
) : PreferenceItem<String>()
|
) : PreferenceItem<String, Boolean>() {
|
||||||
|
override val icon: ImageVector? = null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [PreferenceItem] for individual tracker.
|
* A [PreferenceItem] for individual tracker.
|
||||||
*/
|
*/
|
||||||
data class TrackerPreference(
|
data class TrackerPreference(
|
||||||
val tracker: Tracker,
|
val tracker: Tracker,
|
||||||
override val title: String,
|
|
||||||
val login: () -> Unit,
|
val login: () -> Unit,
|
||||||
val logout: () -> Unit,
|
val logout: () -> Unit,
|
||||||
) : PreferenceItem<String>() {
|
) : PreferenceItem<String, Unit>() {
|
||||||
|
override val title: String = ""
|
||||||
override val enabled: Boolean = true
|
override val enabled: Boolean = true
|
||||||
override val subtitle: String? = null
|
override val subtitle: String? = null
|
||||||
override val icon: ImageVector? = null
|
override val icon: ImageVector? = null
|
||||||
override val onValueChanged: suspend (newValue: String) -> Boolean = { true }
|
override val onValueChanged: suspend (value: String) -> Unit = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class InfoPreference(
|
data class InfoPreference(
|
||||||
override val title: String,
|
override val title: String,
|
||||||
) : PreferenceItem<String>() {
|
) : PreferenceItem<String, Unit>() {
|
||||||
override val enabled: Boolean = true
|
override val enabled: Boolean = true
|
||||||
override val subtitle: String? = null
|
override val subtitle: String? = null
|
||||||
override val icon: ImageVector? = null
|
override val icon: ImageVector? = null
|
||||||
override val onValueChanged: suspend (newValue: String) -> Boolean = { true }
|
override val onValueChanged: suspend (value: String) -> Unit = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class CustomPreference(
|
data class CustomPreference(
|
||||||
override val title: String,
|
override val title: String,
|
||||||
val content: @Composable () -> Unit,
|
val content: @Composable () -> Unit,
|
||||||
) : PreferenceItem<Unit>() {
|
) : PreferenceItem<Unit, Unit>() {
|
||||||
override val enabled: Boolean = true
|
override val enabled: Boolean = true
|
||||||
override val subtitle: String? = null
|
override val subtitle: String? = null
|
||||||
override val icon: ImageVector? = null
|
override val icon: ImageVector? = null
|
||||||
override val onValueChanged: suspend (newValue: Unit) -> Boolean = { true }
|
override val onValueChanged: suspend (value: Unit) -> Unit = {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +179,6 @@ sealed class Preference {
|
|||||||
override val title: String,
|
override val title: String,
|
||||||
override val enabled: Boolean = true,
|
override val enabled: Boolean = true,
|
||||||
|
|
||||||
val preferenceItems: ImmutableList<PreferenceItem<out Any>>,
|
val preferenceItems: ImmutableList<PreferenceItem<out Any, out Any>>,
|
||||||
) : Preference()
|
) : Preference()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import androidx.compose.animation.expandVertically
|
|||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.shrinkVertically
|
import androidx.compose.animation.shrinkVertically
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
@@ -12,16 +14,20 @@ import androidx.compose.runtime.compositionLocalOf
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.structuralEqualityPolicy
|
import androidx.compose.runtime.structuralEqualityPolicy
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.kanade.presentation.more.settings.widget.EditTextPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.EditTextPreferenceWidget
|
||||||
import eu.kanade.presentation.more.settings.widget.InfoWidget
|
import eu.kanade.presentation.more.settings.widget.InfoWidget
|
||||||
import eu.kanade.presentation.more.settings.widget.ListPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.ListPreferenceWidget
|
||||||
import eu.kanade.presentation.more.settings.widget.MultiSelectListPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.MultiSelectListPreferenceWidget
|
||||||
|
import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding
|
||||||
|
import eu.kanade.presentation.more.settings.widget.PrefsVerticalPadding
|
||||||
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
|
||||||
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
||||||
|
import eu.kanade.presentation.more.settings.widget.TitleFontSize
|
||||||
import eu.kanade.presentation.more.settings.widget.TrackingPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.TrackingPreferenceWidget
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import tachiyomi.presentation.core.components.SliderItem
|
import tachiyomi.presentation.core.components.BaseSliderItem
|
||||||
import tachiyomi.presentation.core.util.collectAsState
|
import tachiyomi.presentation.core.util.collectAsState
|
||||||
|
|
||||||
val LocalPreferenceHighlighted = compositionLocalOf(structuralEqualityPolicy()) { false }
|
val LocalPreferenceHighlighted = compositionLocalOf(structuralEqualityPolicy()) { false }
|
||||||
@@ -29,7 +35,7 @@ val LocalPreferenceMinHeight = compositionLocalOf(structuralEqualityPolicy()) {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun StatusWrapper(
|
fun StatusWrapper(
|
||||||
item: Preference.PreferenceItem<*>,
|
item: Preference.PreferenceItem<*, *>,
|
||||||
highlightKey: String?,
|
highlightKey: String?,
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
@@ -50,7 +56,7 @@ fun StatusWrapper(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun PreferenceItem(
|
internal fun PreferenceItem(
|
||||||
item: Preference.PreferenceItem<*>,
|
item: Preference.PreferenceItem<*, *>,
|
||||||
highlightKey: String?,
|
highlightKey: String?,
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
@@ -60,7 +66,7 @@ internal fun PreferenceItem(
|
|||||||
) {
|
) {
|
||||||
when (item) {
|
when (item) {
|
||||||
is Preference.PreferenceItem.SwitchPreference -> {
|
is Preference.PreferenceItem.SwitchPreference -> {
|
||||||
val value by item.pref.collectAsState()
|
val value by item.preference.collectAsState()
|
||||||
SwitchPreferenceWidget(
|
SwitchPreferenceWidget(
|
||||||
title = item.title,
|
title = item.title,
|
||||||
subtitle = item.subtitle,
|
subtitle = item.subtitle,
|
||||||
@@ -69,29 +75,34 @@ internal fun PreferenceItem(
|
|||||||
onCheckedChanged = { newValue ->
|
onCheckedChanged = { newValue ->
|
||||||
scope.launch {
|
scope.launch {
|
||||||
if (item.onValueChanged(newValue)) {
|
if (item.onValueChanged(newValue)) {
|
||||||
item.pref.set(newValue)
|
item.preference.set(newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is Preference.PreferenceItem.SliderPreference -> {
|
is Preference.PreferenceItem.SliderPreference -> {
|
||||||
// TODO: use different composable?
|
BaseSliderItem(
|
||||||
SliderItem(
|
|
||||||
label = item.title,
|
|
||||||
min = item.min,
|
|
||||||
max = item.max,
|
|
||||||
value = item.value,
|
value = item.value,
|
||||||
valueText = item.subtitle.takeUnless { it.isNullOrEmpty() } ?: item.value.toString(),
|
valueRange = item.valueRange,
|
||||||
|
steps = item.steps,
|
||||||
|
title = item.title,
|
||||||
|
subtitle = item.subtitle,
|
||||||
|
valueString = item.valueString.takeUnless { it.isNullOrEmpty() } ?: item.value.toString(),
|
||||||
onChange = {
|
onChange = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
item.onValueChanged(it)
|
item.onValueChanged(it)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
titleStyle = MaterialTheme.typography.titleLarge.copy(fontSize = TitleFontSize),
|
||||||
|
modifier = Modifier.padding(
|
||||||
|
horizontal = PrefsHorizontalPadding,
|
||||||
|
vertical = PrefsVerticalPadding,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is Preference.PreferenceItem.ListPreference<*> -> {
|
is Preference.PreferenceItem.ListPreference<*> -> {
|
||||||
val value by item.pref.collectAsState()
|
val value by item.preference.collectAsState()
|
||||||
ListPreferenceWidget(
|
ListPreferenceWidget(
|
||||||
value = value,
|
value = value,
|
||||||
title = item.title,
|
title = item.title,
|
||||||
@@ -118,14 +129,14 @@ internal fun PreferenceItem(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
is Preference.PreferenceItem.MultiSelectListPreference -> {
|
is Preference.PreferenceItem.MultiSelectListPreference -> {
|
||||||
val values by item.pref.collectAsState()
|
val values by item.preference.collectAsState()
|
||||||
MultiSelectListPreferenceWidget(
|
MultiSelectListPreferenceWidget(
|
||||||
preference = item,
|
preference = item,
|
||||||
values = values,
|
values = values,
|
||||||
onValuesChange = { newValues ->
|
onValuesChange = { newValues ->
|
||||||
scope.launch {
|
scope.launch {
|
||||||
if (item.onValueChanged(newValues)) {
|
if (item.onValueChanged(newValues)) {
|
||||||
item.pref.set(newValues.toMutableSet())
|
item.preference.set(newValues.toMutableSet())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -140,7 +151,7 @@ internal fun PreferenceItem(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
is Preference.PreferenceItem.EditTextPreference -> {
|
is Preference.PreferenceItem.EditTextPreference -> {
|
||||||
val values by item.pref.collectAsState()
|
val values by item.preference.collectAsState()
|
||||||
EditTextPreferenceWidget(
|
EditTextPreferenceWidget(
|
||||||
title = item.title,
|
title = item.title,
|
||||||
subtitle = item.subtitle,
|
subtitle = item.subtitle,
|
||||||
@@ -148,7 +159,7 @@ internal fun PreferenceItem(
|
|||||||
value = values,
|
value = values,
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
val accepted = item.onValueChanged(it)
|
val accepted = item.onValueChanged(it)
|
||||||
if (accepted) item.pref.set(it)
|
if (accepted) item.preference.set(it)
|
||||||
accepted
|
accepted
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ fun PreferenceScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create Preference Item
|
// Create Preference Item
|
||||||
is Preference.PreferenceItem<*> -> item {
|
is Preference.PreferenceItem<*, *> -> item {
|
||||||
PreferenceItem(
|
PreferenceItem(
|
||||||
item = preference,
|
item = preference,
|
||||||
highlightKey = highlightKey,
|
highlightKey = highlightKey,
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import exh.log.xLogE
|
import exh.log.xLogE
|
||||||
|
import exh.source.ExhPreferences
|
||||||
import exh.uconfig.EHConfigurator
|
import exh.uconfig.EHConfigurator
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.NonCancellable
|
import kotlinx.coroutines.NonCancellable
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import tachiyomi.core.common.util.lang.launchUI
|
import tachiyomi.core.common.util.lang.launchUI
|
||||||
import tachiyomi.domain.UnsortedPreferences
|
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.i18n.sy.SYMR
|
import tachiyomi.i18n.sy.SYMR
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
@@ -29,8 +29,8 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ConfigureExhDialog(run: Boolean, onRunning: () -> Unit) {
|
fun ConfigureExhDialog(run: Boolean, onRunning: () -> Unit) {
|
||||||
val unsortedPreferences = remember {
|
val exhPreferences = remember {
|
||||||
Injekt.get<UnsortedPreferences>()
|
Injekt.get<ExhPreferences>()
|
||||||
}
|
}
|
||||||
var warnDialogOpen by remember { mutableStateOf(false) }
|
var warnDialogOpen by remember { mutableStateOf(false) }
|
||||||
var configureDialogOpen by remember { mutableStateOf(false) }
|
var configureDialogOpen by remember { mutableStateOf(false) }
|
||||||
@@ -38,7 +38,7 @@ fun ConfigureExhDialog(run: Boolean, onRunning: () -> Unit) {
|
|||||||
|
|
||||||
LaunchedEffect(run) {
|
LaunchedEffect(run) {
|
||||||
if (run) {
|
if (run) {
|
||||||
if (unsortedPreferences.exhShowSettingsUploadWarning().get()) {
|
if (exhPreferences.exhShowSettingsUploadWarning().get()) {
|
||||||
warnDialogOpen = true
|
warnDialogOpen = true
|
||||||
} else {
|
} else {
|
||||||
configureDialogOpen = true
|
configureDialogOpen = true
|
||||||
@@ -57,7 +57,7 @@ fun ConfigureExhDialog(run: Boolean, onRunning: () -> Unit) {
|
|||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
unsortedPreferences.exhShowSettingsUploadWarning().set(false)
|
exhPreferences.exhShowSettingsUploadWarning().set(false)
|
||||||
configureDialogOpen = true
|
configureDialogOpen = true
|
||||||
warnDialogOpen = false
|
warnDialogOpen = false
|
||||||
},
|
},
|
||||||
|
|||||||
+82
-32
@@ -3,6 +3,7 @@ package eu.kanade.presentation.more.settings.screen
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.webkit.WebStorage
|
import android.webkit.WebStorage
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
@@ -72,6 +73,7 @@ import exh.pref.DelegateSourcePreferences
|
|||||||
import exh.source.BlacklistedSources
|
import exh.source.BlacklistedSources
|
||||||
import exh.source.EH_SOURCE_ID
|
import exh.source.EH_SOURCE_ID
|
||||||
import exh.source.EXH_SOURCE_ID
|
import exh.source.EXH_SOURCE_ID
|
||||||
|
import exh.source.ExhPreferences
|
||||||
import exh.util.toAnnotatedString
|
import exh.util.toAnnotatedString
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.persistentMapOf
|
import kotlinx.collections.immutable.persistentMapOf
|
||||||
@@ -86,8 +88,9 @@ import tachiyomi.core.common.util.lang.launchNonCancellable
|
|||||||
import tachiyomi.core.common.util.lang.withUIContext
|
import tachiyomi.core.common.util.lang.withUIContext
|
||||||
import tachiyomi.core.common.util.system.ImageUtil
|
import tachiyomi.core.common.util.system.ImageUtil
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.domain.UnsortedPreferences
|
|
||||||
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
|
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
|
||||||
|
import tachiyomi.domain.download.service.DownloadPreferences
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
import tachiyomi.domain.manga.interactor.GetAllManga
|
import tachiyomi.domain.manga.interactor.GetAllManga
|
||||||
import tachiyomi.domain.manga.interactor.ResetViewerFlags
|
import tachiyomi.domain.manga.interactor.ResetViewerFlags
|
||||||
import tachiyomi.domain.source.service.SourceManager
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
@@ -114,6 +117,8 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
|
|
||||||
val basePreferences = remember { Injekt.get<BasePreferences>() }
|
val basePreferences = remember { Injekt.get<BasePreferences>() }
|
||||||
val networkPreferences = remember { Injekt.get<NetworkPreferences>() }
|
val networkPreferences = remember { Injekt.get<NetworkPreferences>() }
|
||||||
|
val libraryPreferences = remember { Injekt.get<LibraryPreferences>() }
|
||||||
|
val downloadPreferences = remember { Injekt.get<DownloadPreferences>() }
|
||||||
|
|
||||||
return listOf(
|
return listOf(
|
||||||
Preference.PreferenceItem.TextPreference(
|
Preference.PreferenceItem.TextPreference(
|
||||||
@@ -126,7 +131,7 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
/* SY --> Preference.PreferenceItem.SwitchPreference(
|
/* SY --> Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = networkPreferences.verboseLogging(),
|
preference = networkPreferences.verboseLogging(),
|
||||||
title = stringResource(MR.strings.pref_verbose_logging),
|
title = stringResource(MR.strings.pref_verbose_logging),
|
||||||
subtitle = stringResource(MR.strings.pref_verbose_logging_summary),
|
subtitle = stringResource(MR.strings.pref_verbose_logging_summary),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
@@ -145,16 +150,26 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
Preference.PreferenceItem.TextPreference(
|
Preference.PreferenceItem.TextPreference(
|
||||||
title = stringResource(MR.strings.pref_manage_notifications),
|
title = stringResource(MR.strings.pref_manage_notifications),
|
||||||
onClick = {
|
onClick = {
|
||||||
val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
|
// SY -->
|
||||||
putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
|
val intent = Intent().apply {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
|
||||||
|
putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
|
||||||
|
} else {
|
||||||
|
setAction("android.settings.APP_NOTIFICATION_SETTINGS")
|
||||||
|
putExtra("app_package", context.packageName)
|
||||||
|
putExtra("app_uid", context.applicationInfo.uid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// SY <--
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
getBackgroundActivityGroup(),
|
getBackgroundActivityGroup(),
|
||||||
getDataGroup(),
|
getDataGroup(),
|
||||||
getNetworkGroup(networkPreferences = networkPreferences),
|
getNetworkGroup(networkPreferences = networkPreferences),
|
||||||
getLibraryGroup(),
|
getLibraryGroup(libraryPreferences = libraryPreferences),
|
||||||
|
getDownloadsGroup(downloadPreferences = downloadPreferences),
|
||||||
getReaderGroup(basePreferences = basePreferences),
|
getReaderGroup(basePreferences = basePreferences),
|
||||||
getExtensionsGroup(basePreferences = basePreferences),
|
getExtensionsGroup(basePreferences = basePreferences),
|
||||||
// SY -->
|
// SY -->
|
||||||
@@ -272,8 +287,7 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = networkPreferences.dohProvider(),
|
preference = networkPreferences.dohProvider(),
|
||||||
title = stringResource(MR.strings.pref_dns_over_https),
|
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
-1 to stringResource(MR.strings.disabled),
|
-1 to stringResource(MR.strings.disabled),
|
||||||
PREF_DOH_CLOUDFLARE to "Cloudflare",
|
PREF_DOH_CLOUDFLARE to "Cloudflare",
|
||||||
@@ -289,13 +303,14 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
PREF_DOH_NJALLA to "Njalla",
|
PREF_DOH_NJALLA to "Njalla",
|
||||||
PREF_DOH_SHECAN to "Shecan",
|
PREF_DOH_SHECAN to "Shecan",
|
||||||
),
|
),
|
||||||
|
title = stringResource(MR.strings.pref_dns_over_https),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
context.toast(MR.strings.requires_app_restart)
|
context.toast(MR.strings.requires_app_restart)
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.EditTextPreference(
|
Preference.PreferenceItem.EditTextPreference(
|
||||||
pref = userAgentPref,
|
preference = userAgentPref,
|
||||||
title = stringResource(MR.strings.pref_user_agent_string),
|
title = stringResource(MR.strings.pref_user_agent_string),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
try {
|
try {
|
||||||
@@ -322,7 +337,9 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun getLibraryGroup(): Preference.PreferenceGroup {
|
private fun getLibraryGroup(
|
||||||
|
libraryPreferences: LibraryPreferences,
|
||||||
|
): Preference.PreferenceGroup {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
@@ -350,10 +367,38 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
|
preference = libraryPreferences.updateMangaTitles(),
|
||||||
|
title = stringResource(MR.strings.pref_update_library_manga_titles),
|
||||||
|
subtitle = stringResource(MR.strings.pref_update_library_manga_titles_summary),
|
||||||
|
),
|
||||||
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
|
preference = libraryPreferences.disallowNonAsciiFilenames(),
|
||||||
|
title = stringResource(MR.strings.pref_disallow_non_ascii_filenames),
|
||||||
|
subtitle = stringResource(MR.strings.pref_disallow_non_ascii_filenames_details),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SY ->
|
||||||
|
@Composable
|
||||||
|
private fun getDownloadsGroup(
|
||||||
|
downloadPreferences: DownloadPreferences,
|
||||||
|
): Preference.PreferenceGroup {
|
||||||
|
return Preference.PreferenceGroup(
|
||||||
|
title = stringResource(MR.strings.pref_category_downloads),
|
||||||
|
preferenceItems = persistentListOf(
|
||||||
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
|
preference = downloadPreferences.includeChapterUrlHash(),
|
||||||
|
title = stringResource(SYMR.strings.pref_include_chapter_url_hash),
|
||||||
|
subtitle = stringResource(SYMR.strings.pref_include_chapter_url_hash_desc),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// <- SY
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun getReaderGroup(
|
private fun getReaderGroup(
|
||||||
basePreferences: BasePreferences,
|
basePreferences: BasePreferences,
|
||||||
@@ -372,13 +417,7 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.pref_category_reader),
|
title = stringResource(MR.strings.pref_category_reader),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = basePreferences.hardwareBitmapThreshold(),
|
preference = basePreferences.hardwareBitmapThreshold(),
|
||||||
title = stringResource(MR.strings.pref_hardware_bitmap_threshold),
|
|
||||||
subtitleProvider = { value, options ->
|
|
||||||
stringResource(MR.strings.pref_hardware_bitmap_threshold_summary, options[value].orEmpty())
|
|
||||||
},
|
|
||||||
enabled = !ImageUtil.HARDWARE_BITMAP_UNSUPPORTED &&
|
|
||||||
GLUtil.DEVICE_TEXTURE_LIMIT > GLUtil.SAFE_TEXTURE_LIMIT,
|
|
||||||
entries = GLUtil.CUSTOM_TEXTURE_LIMIT_OPTIONS
|
entries = GLUtil.CUSTOM_TEXTURE_LIMIT_OPTIONS
|
||||||
.mapIndexed { index, option ->
|
.mapIndexed { index, option ->
|
||||||
val display = if (index == 0) {
|
val display = if (index == 0) {
|
||||||
@@ -390,6 +429,17 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
.toMap()
|
.toMap()
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.pref_hardware_bitmap_threshold),
|
||||||
|
subtitleProvider = { value, options ->
|
||||||
|
stringResource(MR.strings.pref_hardware_bitmap_threshold_summary, options[value].orEmpty())
|
||||||
|
},
|
||||||
|
enabled = !ImageUtil.HARDWARE_BITMAP_UNSUPPORTED &&
|
||||||
|
GLUtil.DEVICE_TEXTURE_LIMIT > GLUtil.SAFE_TEXTURE_LIMIT,
|
||||||
|
),
|
||||||
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
|
preference = basePreferences.alwaysDecodeLongStripWithSSIV(),
|
||||||
|
title = stringResource(MR.strings.pref_always_decode_long_strip_with_ssiv_2),
|
||||||
|
subtitle = stringResource(MR.strings.pref_always_decode_long_strip_with_ssiv_summary),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.TextPreference(
|
Preference.PreferenceItem.TextPreference(
|
||||||
title = stringResource(MR.strings.pref_display_profile),
|
title = stringResource(MR.strings.pref_display_profile),
|
||||||
@@ -439,8 +489,7 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.label_extensions),
|
title = stringResource(MR.strings.label_extensions),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = extensionInstallerPref,
|
preference = extensionInstallerPref,
|
||||||
title = stringResource(MR.strings.ext_installer_pref),
|
|
||||||
entries = extensionInstallerPref.entries
|
entries = extensionInstallerPref.entries
|
||||||
.filter {
|
.filter {
|
||||||
// TODO: allow private option in stable versions once URL handling is more fleshed out
|
// TODO: allow private option in stable versions once URL handling is more fleshed out
|
||||||
@@ -452,6 +501,7 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
.associateWith { stringResource(it.titleRes) }
|
.associateWith { stringResource(it.titleRes) }
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.ext_installer_pref),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
if (it == BasePreferences.ExtensionInstaller.SHIZUKU &&
|
if (it == BasePreferences.ExtensionInstaller.SHIZUKU &&
|
||||||
!context.isShizukuInstalled
|
!context.isShizukuInstalled
|
||||||
@@ -613,7 +663,7 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
title = stringResource(SYMR.strings.data_saver),
|
title = stringResource(SYMR.strings.data_saver),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = sourcePreferences.dataSaver(),
|
preference = sourcePreferences.dataSaver(),
|
||||||
title = stringResource(SYMR.strings.data_saver),
|
title = stringResource(SYMR.strings.data_saver),
|
||||||
subtitle = stringResource(SYMR.strings.data_saver_summary),
|
subtitle = stringResource(SYMR.strings.data_saver_summary),
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
@@ -623,28 +673,28 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.EditTextPreference(
|
Preference.PreferenceItem.EditTextPreference(
|
||||||
pref = sourcePreferences.dataSaverServer(),
|
preference = sourcePreferences.dataSaverServer(),
|
||||||
title = stringResource(SYMR.strings.bandwidth_data_saver_server),
|
title = stringResource(SYMR.strings.bandwidth_data_saver_server),
|
||||||
subtitle = stringResource(SYMR.strings.data_saver_server_summary),
|
subtitle = stringResource(SYMR.strings.data_saver_server_summary),
|
||||||
enabled = dataSaver == DataSaver.BANDWIDTH_HERO,
|
enabled = dataSaver == DataSaver.BANDWIDTH_HERO,
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = sourcePreferences.dataSaverDownloader(),
|
preference = sourcePreferences.dataSaverDownloader(),
|
||||||
title = stringResource(SYMR.strings.data_saver_downloader),
|
title = stringResource(SYMR.strings.data_saver_downloader),
|
||||||
enabled = dataSaver != DataSaver.NONE,
|
enabled = dataSaver != DataSaver.NONE,
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = sourcePreferences.dataSaverIgnoreJpeg(),
|
preference = sourcePreferences.dataSaverIgnoreJpeg(),
|
||||||
title = stringResource(SYMR.strings.data_saver_ignore_jpeg),
|
title = stringResource(SYMR.strings.data_saver_ignore_jpeg),
|
||||||
enabled = dataSaver != DataSaver.NONE,
|
enabled = dataSaver != DataSaver.NONE,
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = sourcePreferences.dataSaverIgnoreGif(),
|
preference = sourcePreferences.dataSaverIgnoreGif(),
|
||||||
title = stringResource(SYMR.strings.data_saver_ignore_gif),
|
title = stringResource(SYMR.strings.data_saver_ignore_gif),
|
||||||
enabled = dataSaver != DataSaver.NONE,
|
enabled = dataSaver != DataSaver.NONE,
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = sourcePreferences.dataSaverImageQuality(),
|
preference = sourcePreferences.dataSaverImageQuality(),
|
||||||
title = stringResource(SYMR.strings.data_saver_image_quality),
|
title = stringResource(SYMR.strings.data_saver_image_quality),
|
||||||
subtitle = stringResource(SYMR.strings.data_saver_image_quality_summary),
|
subtitle = stringResource(SYMR.strings.data_saver_image_quality_summary),
|
||||||
entries = listOf(
|
entries = listOf(
|
||||||
@@ -663,7 +713,7 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
val dataSaverImageFormatJpeg by sourcePreferences.dataSaverImageFormatJpeg()
|
val dataSaverImageFormatJpeg by sourcePreferences.dataSaverImageFormatJpeg()
|
||||||
.collectAsState()
|
.collectAsState()
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = sourcePreferences.dataSaverImageFormatJpeg(),
|
preference = sourcePreferences.dataSaverImageFormatJpeg(),
|
||||||
title = stringResource(SYMR.strings.data_saver_image_format),
|
title = stringResource(SYMR.strings.data_saver_image_format),
|
||||||
subtitle = if (dataSaverImageFormatJpeg) {
|
subtitle = if (dataSaverImageFormatJpeg) {
|
||||||
stringResource(SYMR.strings.data_saver_image_format_summary_on)
|
stringResource(SYMR.strings.data_saver_image_format_summary_on)
|
||||||
@@ -674,7 +724,7 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = sourcePreferences.dataSaverColorBW(),
|
preference = sourcePreferences.dataSaverColorBW(),
|
||||||
title = stringResource(SYMR.strings.data_saver_color_bw),
|
title = stringResource(SYMR.strings.data_saver_color_bw),
|
||||||
enabled = dataSaver == DataSaver.BANDWIDTH_HERO,
|
enabled = dataSaver == DataSaver.BANDWIDTH_HERO,
|
||||||
),
|
),
|
||||||
@@ -687,14 +737,14 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
val sourcePreferences = remember { Injekt.get<SourcePreferences>() }
|
val sourcePreferences = remember { Injekt.get<SourcePreferences>() }
|
||||||
val unsortedPreferences = remember { Injekt.get<UnsortedPreferences>() }
|
val exhPreferences = remember { Injekt.get<ExhPreferences>() }
|
||||||
val delegateSourcePreferences = remember { Injekt.get<DelegateSourcePreferences>() }
|
val delegateSourcePreferences = remember { Injekt.get<DelegateSourcePreferences>() }
|
||||||
val securityPreferences = remember { Injekt.get<SecurityPreferences>() }
|
val securityPreferences = remember { Injekt.get<SecurityPreferences>() }
|
||||||
return Preference.PreferenceGroup(
|
return Preference.PreferenceGroup(
|
||||||
title = stringResource(SYMR.strings.developer_tools),
|
title = stringResource(SYMR.strings.developer_tools),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = unsortedPreferences.isHentaiEnabled(),
|
preference = exhPreferences.isHentaiEnabled(),
|
||||||
title = stringResource(SYMR.strings.toggle_hentai_features),
|
title = stringResource(SYMR.strings.toggle_hentai_features),
|
||||||
subtitle = stringResource(SYMR.strings.toggle_hentai_features_summary),
|
subtitle = stringResource(SYMR.strings.toggle_hentai_features_summary),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
@@ -709,7 +759,7 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = delegateSourcePreferences.delegateSources(),
|
preference = delegateSourcePreferences.delegateSources(),
|
||||||
title = stringResource(SYMR.strings.toggle_delegated_sources),
|
title = stringResource(SYMR.strings.toggle_delegated_sources),
|
||||||
subtitle = stringResource(
|
subtitle = stringResource(
|
||||||
SYMR.strings.toggle_delegated_sources_summary,
|
SYMR.strings.toggle_delegated_sources_summary,
|
||||||
@@ -719,7 +769,7 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = unsortedPreferences.logLevel(),
|
preference = exhPreferences.logLevel(),
|
||||||
title = stringResource(SYMR.strings.log_level),
|
title = stringResource(SYMR.strings.log_level),
|
||||||
subtitle = stringResource(SYMR.strings.log_level_summary),
|
subtitle = stringResource(SYMR.strings.log_level_summary),
|
||||||
entries = EHLogLevel.entries.mapIndexed { index, ehLogLevel ->
|
entries = EHLogLevel.entries.mapIndexed { index, ehLogLevel ->
|
||||||
@@ -729,7 +779,7 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
}.toMap().toImmutableMap(),
|
}.toMap().toImmutableMap(),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = sourcePreferences.enableSourceBlacklist(),
|
preference = sourcePreferences.enableSourceBlacklist(),
|
||||||
title = stringResource(SYMR.strings.enable_source_blacklist),
|
title = stringResource(SYMR.strings.enable_source_blacklist),
|
||||||
subtitle = stringResource(
|
subtitle = stringResource(
|
||||||
SYMR.strings.enable_source_blacklist_summary,
|
SYMR.strings.enable_source_blacklist_summary,
|
||||||
@@ -773,7 +823,7 @@ object SettingsAdvancedScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
title = stringResource(SYMR.strings.encrypt_database),
|
title = stringResource(SYMR.strings.encrypt_database),
|
||||||
pref = securityPreferences.encryptDatabase(),
|
preference = securityPreferences.encryptDatabase(),
|
||||||
subtitle = stringResource(SYMR.strings.encrypt_database_subtitle),
|
subtitle = stringResource(SYMR.strings.encrypt_database_subtitle),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
if (it) {
|
if (it) {
|
||||||
|
|||||||
+17
-14
@@ -88,7 +88,7 @@ object SettingsAppearanceScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = amoledPref,
|
preference = amoledPref,
|
||||||
title = stringResource(MR.strings.pref_dark_theme_pure_black),
|
title = stringResource(MR.strings.pref_dark_theme_pure_black),
|
||||||
enabled = themeMode != ThemeMode.LIGHT,
|
enabled = themeMode != ThemeMode.LIGHT,
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
@@ -122,28 +122,28 @@ object SettingsAppearanceScreen : SearchableSettings {
|
|||||||
onClick = { navigator.push(AppLanguageScreen()) },
|
onClick = { navigator.push(AppLanguageScreen()) },
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = uiPreferences.tabletUiMode(),
|
preference = uiPreferences.tabletUiMode(),
|
||||||
title = stringResource(MR.strings.pref_tablet_ui_mode),
|
|
||||||
entries = TabletUiMode.entries
|
entries = TabletUiMode.entries
|
||||||
.associateWith { stringResource(it.titleRes) }
|
.associateWith { stringResource(it.titleRes) }
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.pref_tablet_ui_mode),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
context.toast(MR.strings.requires_app_restart)
|
context.toast(MR.strings.requires_app_restart)
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = uiPreferences.dateFormat(),
|
preference = uiPreferences.dateFormat(),
|
||||||
title = stringResource(MR.strings.pref_date_format),
|
|
||||||
entries = DateFormats
|
entries = DateFormats
|
||||||
.associateWith {
|
.associateWith {
|
||||||
val formattedDate = UiPreferences.dateFormat(it).format(now)
|
val formattedDate = UiPreferences.dateFormat(it).format(now)
|
||||||
"${it.ifEmpty { stringResource(MR.strings.label_default) }} ($formattedDate)"
|
"${it.ifEmpty { stringResource(MR.strings.label_default) }} ($formattedDate)"
|
||||||
}
|
}
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.pref_date_format),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = uiPreferences.relativeTime(),
|
preference = uiPreferences.relativeTime(),
|
||||||
title = stringResource(MR.strings.pref_relative_format),
|
title = stringResource(MR.strings.pref_relative_format),
|
||||||
subtitle = stringResource(
|
subtitle = stringResource(
|
||||||
MR.strings.pref_relative_format_summary,
|
MR.strings.pref_relative_format_summary,
|
||||||
@@ -151,6 +151,10 @@ object SettingsAppearanceScreen : SearchableSettings {
|
|||||||
formattedNow,
|
formattedNow,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
|
preference = uiPreferences.imagesInDescription(),
|
||||||
|
title = stringResource(MR.strings.pref_display_images_description),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -164,16 +168,16 @@ object SettingsAppearanceScreen : SearchableSettings {
|
|||||||
stringResource(SYMR.strings.pref_category_fork),
|
stringResource(SYMR.strings.pref_category_fork),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = uiPreferences.expandFilters(),
|
preference = uiPreferences.expandFilters(),
|
||||||
title = stringResource(SYMR.strings.toggle_expand_search_filters),
|
title = stringResource(SYMR.strings.toggle_expand_search_filters),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = uiPreferences.recommendsInOverflow(),
|
preference = uiPreferences.recommendsInOverflow(),
|
||||||
title = stringResource(SYMR.strings.put_recommends_in_overflow),
|
title = stringResource(SYMR.strings.put_recommends_in_overflow),
|
||||||
subtitle = stringResource(SYMR.strings.put_recommends_in_overflow_summary),
|
subtitle = stringResource(SYMR.strings.put_recommends_in_overflow_summary),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = uiPreferences.mergeInOverflow(),
|
preference = uiPreferences.mergeInOverflow(),
|
||||||
title = stringResource(SYMR.strings.put_merge_in_overflow),
|
title = stringResource(SYMR.strings.put_merge_in_overflow),
|
||||||
subtitle = stringResource(SYMR.strings.put_merge_in_overflow_summary),
|
subtitle = stringResource(SYMR.strings.put_merge_in_overflow_summary),
|
||||||
),
|
),
|
||||||
@@ -189,8 +193,7 @@ object SettingsAppearanceScreen : SearchableSettings {
|
|||||||
} else {
|
} else {
|
||||||
stringResource(MR.strings.disabled)
|
stringResource(MR.strings.disabled)
|
||||||
},
|
},
|
||||||
min = 0,
|
valueRange = 0..10,
|
||||||
max = 10,
|
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
uiPreferences.previewsRowCount().set(it)
|
uiPreferences.previewsRowCount().set(it)
|
||||||
true
|
true
|
||||||
@@ -206,15 +209,15 @@ object SettingsAppearanceScreen : SearchableSettings {
|
|||||||
stringResource(SYMR.strings.pref_category_navbar),
|
stringResource(SYMR.strings.pref_category_navbar),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = uiPreferences.showNavUpdates(),
|
preference = uiPreferences.showNavUpdates(),
|
||||||
title = stringResource(SYMR.strings.pref_hide_updates_button),
|
title = stringResource(SYMR.strings.pref_hide_updates_button),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = uiPreferences.showNavHistory(),
|
preference = uiPreferences.showNavHistory(),
|
||||||
title = stringResource(SYMR.strings.pref_hide_history_button),
|
title = stringResource(SYMR.strings.pref_hide_history_button),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = uiPreferences.bottomBarLabels(),
|
preference = uiPreferences.bottomBarLabels(),
|
||||||
title = stringResource(SYMR.strings.pref_show_bottom_bar_labels),
|
title = stringResource(SYMR.strings.pref_show_bottom_bar_labels),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
+7
-9
@@ -20,7 +20,6 @@ import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate
|
|||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import mihon.domain.extensionrepo.interactor.GetExtensionRepoCount
|
import mihon.domain.extensionrepo.interactor.GetExtensionRepoCount
|
||||||
import tachiyomi.core.common.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
import tachiyomi.domain.UnsortedPreferences
|
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.i18n.sy.SYMR
|
import tachiyomi.i18n.sy.SYMR
|
||||||
import tachiyomi.presentation.core.i18n.pluralStringResource
|
import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||||
@@ -49,7 +48,6 @@ object SettingsBrowseScreen : SearchableSettings {
|
|||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val hideFeedTab by remember { Injekt.get<UiPreferences>().hideFeedTab().asState(scope) }
|
val hideFeedTab by remember { Injekt.get<UiPreferences>().hideFeedTab().asState(scope) }
|
||||||
val uiPreferences = remember { Injekt.get<UiPreferences>() }
|
val uiPreferences = remember { Injekt.get<UiPreferences>() }
|
||||||
val unsortedPreferences = remember { Injekt.get<UnsortedPreferences>() }
|
|
||||||
// SY <--
|
// SY <--
|
||||||
return listOf(
|
return listOf(
|
||||||
// SY -->
|
// SY -->
|
||||||
@@ -67,17 +65,17 @@ object SettingsBrowseScreen : SearchableSettings {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = sourcePreferences.sourcesTabCategoriesFilter(),
|
preference = sourcePreferences.sourcesTabCategoriesFilter(),
|
||||||
title = stringResource(SYMR.strings.pref_source_source_filtering),
|
title = stringResource(SYMR.strings.pref_source_source_filtering),
|
||||||
subtitle = stringResource(SYMR.strings.pref_source_source_filtering_summery),
|
subtitle = stringResource(SYMR.strings.pref_source_source_filtering_summery),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = uiPreferences.useNewSourceNavigation(),
|
preference = uiPreferences.useNewSourceNavigation(),
|
||||||
title = stringResource(SYMR.strings.pref_source_navigation),
|
title = stringResource(SYMR.strings.pref_source_navigation),
|
||||||
subtitle = stringResource(SYMR.strings.pref_source_navigation_summery),
|
subtitle = stringResource(SYMR.strings.pref_source_navigation_summery),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = unsortedPreferences.allowLocalSourceHiddenFolders(),
|
preference = sourcePreferences.allowLocalSourceHiddenFolders(),
|
||||||
title = stringResource(SYMR.strings.pref_local_source_hidden_folders),
|
title = stringResource(SYMR.strings.pref_local_source_hidden_folders),
|
||||||
subtitle = stringResource(SYMR.strings.pref_local_source_hidden_folders_summery),
|
subtitle = stringResource(SYMR.strings.pref_local_source_hidden_folders_summery),
|
||||||
),
|
),
|
||||||
@@ -87,11 +85,11 @@ object SettingsBrowseScreen : SearchableSettings {
|
|||||||
title = stringResource(SYMR.strings.feed),
|
title = stringResource(SYMR.strings.feed),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = uiPreferences.hideFeedTab(),
|
preference = uiPreferences.hideFeedTab(),
|
||||||
title = stringResource(SYMR.strings.pref_hide_feed),
|
title = stringResource(SYMR.strings.pref_hide_feed),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = uiPreferences.feedTabInFront(),
|
preference = uiPreferences.feedTabInFront(),
|
||||||
title = stringResource(SYMR.strings.pref_feed_position),
|
title = stringResource(SYMR.strings.pref_feed_position),
|
||||||
subtitle = stringResource(SYMR.strings.pref_feed_position_summery),
|
subtitle = stringResource(SYMR.strings.pref_feed_position_summery),
|
||||||
enabled = hideFeedTab.not(),
|
enabled = hideFeedTab.not(),
|
||||||
@@ -103,7 +101,7 @@ object SettingsBrowseScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.label_sources),
|
title = stringResource(MR.strings.label_sources),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = sourcePreferences.hideInLibraryItems(),
|
preference = sourcePreferences.hideInLibraryItems(),
|
||||||
title = stringResource(MR.strings.pref_hide_in_library_items),
|
title = stringResource(MR.strings.pref_hide_in_library_items),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.TextPreference(
|
Preference.PreferenceItem.TextPreference(
|
||||||
@@ -119,7 +117,7 @@ object SettingsBrowseScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.pref_category_nsfw_content),
|
title = stringResource(MR.strings.pref_category_nsfw_content),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = sourcePreferences.showNsfwSource(),
|
preference = sourcePreferences.showNsfwSource(),
|
||||||
title = stringResource(MR.strings.pref_show_nsfw_source),
|
title = stringResource(MR.strings.pref_show_nsfw_source),
|
||||||
subtitle = stringResource(MR.strings.requires_app_restart),
|
subtitle = stringResource(MR.strings.requires_app_restart),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
|
|||||||
+203
-10
@@ -7,7 +7,9 @@ import android.net.Uri
|
|||||||
import androidx.activity.compose.ManagedActivityResultLauncher
|
import androidx.activity.compose.ManagedActivityResultLauncher
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.IntrinsicSize
|
import androidx.compose.foundation.layout.IntrinsicSize
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
@@ -15,7 +17,9 @@ import androidx.compose.foundation.layout.height
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
|
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
|
||||||
|
import androidx.compose.material.icons.filled.QrCodeScanner
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Checkbox
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MultiChoiceSegmentedButtonRow
|
import androidx.compose.material3.MultiChoiceSegmentedButtonRow
|
||||||
@@ -24,6 +28,7 @@ import androidx.compose.material3.SegmentedButtonDefaults
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.ReadOnlyComposable
|
import androidx.compose.runtime.ReadOnlyComposable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
@@ -31,13 +36,17 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
|
import com.google.zxing.client.android.Intents
|
||||||
import com.hippo.unifile.UniFile
|
import com.hippo.unifile.UniFile
|
||||||
|
import com.journeyapps.barcodescanner.ScanContract
|
||||||
|
import com.journeyapps.barcodescanner.ScanOptions
|
||||||
import eu.kanade.domain.sync.SyncPreferences
|
import eu.kanade.domain.sync.SyncPreferences
|
||||||
import eu.kanade.presentation.more.settings.Preference
|
import eu.kanade.presentation.more.settings.Preference
|
||||||
import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen
|
import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen
|
||||||
@@ -46,12 +55,16 @@ import eu.kanade.presentation.more.settings.screen.data.StorageInfo
|
|||||||
import eu.kanade.presentation.more.settings.screen.data.SyncSettingsSelector
|
import eu.kanade.presentation.more.settings.screen.data.SyncSettingsSelector
|
||||||
import eu.kanade.presentation.more.settings.screen.data.SyncTriggerOptionsScreen
|
import eu.kanade.presentation.more.settings.screen.data.SyncTriggerOptionsScreen
|
||||||
import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget
|
||||||
|
import eu.kanade.presentation.more.settings.widget.EditTextPreferenceWidget
|
||||||
import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding
|
import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding
|
||||||
|
import eu.kanade.presentation.more.settings.widget.TrailingWidgetBuffer
|
||||||
import eu.kanade.presentation.util.relativeTimeSpanString
|
import eu.kanade.presentation.util.relativeTimeSpanString
|
||||||
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
|
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
|
||||||
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
|
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
|
||||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||||
import eu.kanade.tachiyomi.data.cache.PagePreviewCache
|
import eu.kanade.tachiyomi.data.cache.PagePreviewCache
|
||||||
|
import eu.kanade.tachiyomi.data.export.LibraryExporter
|
||||||
|
import eu.kanade.tachiyomi.data.export.LibraryExporter.ExportOptions
|
||||||
import eu.kanade.tachiyomi.data.sync.SyncDataJob
|
import eu.kanade.tachiyomi.data.sync.SyncDataJob
|
||||||
import eu.kanade.tachiyomi.data.sync.SyncManager
|
import eu.kanade.tachiyomi.data.sync.SyncManager
|
||||||
import eu.kanade.tachiyomi.data.sync.service.GoogleDriveService
|
import eu.kanade.tachiyomi.data.sync.service.GoogleDriveService
|
||||||
@@ -60,6 +73,7 @@ import eu.kanade.tachiyomi.util.system.DeviceUtil
|
|||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.persistentMapOf
|
import kotlinx.collections.immutable.persistentMapOf
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import tachiyomi.core.common.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
@@ -69,6 +83,8 @@ import tachiyomi.core.common.util.lang.withUIContext
|
|||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.domain.backup.service.BackupPreferences
|
import tachiyomi.domain.backup.service.BackupPreferences
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
|
import tachiyomi.domain.manga.interactor.GetFavorites
|
||||||
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.storage.service.StoragePreferences
|
import tachiyomi.domain.storage.service.StoragePreferences
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.i18n.sy.SYMR
|
import tachiyomi.i18n.sy.SYMR
|
||||||
@@ -111,6 +127,7 @@ object SettingsDataScreen : SearchableSettings {
|
|||||||
|
|
||||||
getBackupAndRestoreGroup(backupPreferences = backupPreferences),
|
getBackupAndRestoreGroup(backupPreferences = backupPreferences),
|
||||||
getDataGroup(),
|
getDataGroup(),
|
||||||
|
getExportGroup(),
|
||||||
) + getSyncPreferences(syncPreferences = syncPreferences, syncService = syncService)
|
) + getSyncPreferences(syncPreferences = syncPreferences, syncService = syncService)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,8 +272,7 @@ object SettingsDataScreen : SearchableSettings {
|
|||||||
|
|
||||||
// Automatic backups
|
// Automatic backups
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = backupPreferences.backupInterval(),
|
preference = backupPreferences.backupInterval(),
|
||||||
title = stringResource(MR.strings.pref_backup_interval),
|
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
0 to stringResource(MR.strings.off),
|
0 to stringResource(MR.strings.off),
|
||||||
6 to stringResource(MR.strings.update_6hour),
|
6 to stringResource(MR.strings.update_6hour),
|
||||||
@@ -265,6 +281,7 @@ object SettingsDataScreen : SearchableSettings {
|
|||||||
48 to stringResource(MR.strings.update_48hour),
|
48 to stringResource(MR.strings.update_48hour),
|
||||||
168 to stringResource(MR.strings.update_weekly),
|
168 to stringResource(MR.strings.update_weekly),
|
||||||
),
|
),
|
||||||
|
title = stringResource(MR.strings.pref_backup_interval),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
BackupCreateJob.setupTask(context, it)
|
BackupCreateJob.setupTask(context, it)
|
||||||
true
|
true
|
||||||
@@ -348,13 +365,151 @@ object SettingsDataScreen : SearchableSettings {
|
|||||||
),
|
),
|
||||||
// SY <--
|
// SY <--
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = libraryPreferences.autoClearChapterCache(),
|
preference = libraryPreferences.autoClearChapterCache(),
|
||||||
title = stringResource(MR.strings.pref_auto_clear_chapter_cache),
|
title = stringResource(MR.strings.pref_auto_clear_chapter_cache),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun getExportGroup(): Preference.PreferenceGroup {
|
||||||
|
var showDialog by remember { mutableStateOf(false) }
|
||||||
|
var exportOptions by remember {
|
||||||
|
mutableStateOf(
|
||||||
|
ExportOptions(
|
||||||
|
includeTitle = true,
|
||||||
|
includeAuthor = true,
|
||||||
|
includeArtist = true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val context = LocalContext.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val getFavorites = remember { Injekt.get<GetFavorites>() }
|
||||||
|
var favorites by remember { mutableStateOf<List<Manga>>(emptyList()) }
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
favorites = getFavorites.await()
|
||||||
|
}
|
||||||
|
|
||||||
|
val saveFileLauncher = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.CreateDocument("text/csv"),
|
||||||
|
) { uri ->
|
||||||
|
uri?.let {
|
||||||
|
scope.launch {
|
||||||
|
LibraryExporter.exportToCsv(
|
||||||
|
context = context,
|
||||||
|
uri = it,
|
||||||
|
favorites = favorites,
|
||||||
|
options = exportOptions,
|
||||||
|
onExportComplete = {
|
||||||
|
scope.launch(Dispatchers.Main) {
|
||||||
|
context.toast(MR.strings.library_exported)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showDialog) {
|
||||||
|
ColumnSelectionDialog(
|
||||||
|
options = exportOptions,
|
||||||
|
onConfirm = { options ->
|
||||||
|
exportOptions = options
|
||||||
|
saveFileLauncher.launch("mihon_library.csv")
|
||||||
|
},
|
||||||
|
onDismissRequest = { showDialog = false },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Preference.PreferenceGroup(
|
||||||
|
title = stringResource(MR.strings.export),
|
||||||
|
preferenceItems = persistentListOf(
|
||||||
|
Preference.PreferenceItem.TextPreference(
|
||||||
|
title = stringResource(MR.strings.library_list),
|
||||||
|
onClick = { showDialog = true },
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ColumnSelectionDialog(
|
||||||
|
options: ExportOptions,
|
||||||
|
onConfirm: (ExportOptions) -> Unit,
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
) {
|
||||||
|
var titleSelected by remember { mutableStateOf(options.includeTitle) }
|
||||||
|
var authorSelected by remember { mutableStateOf(options.includeAuthor) }
|
||||||
|
var artistSelected by remember { mutableStateOf(options.includeArtist) }
|
||||||
|
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
title = {
|
||||||
|
Text(text = stringResource(MR.strings.migration_dialog_what_to_include))
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Checkbox(
|
||||||
|
checked = titleSelected,
|
||||||
|
onCheckedChange = { checked ->
|
||||||
|
titleSelected = checked
|
||||||
|
if (!checked) {
|
||||||
|
authorSelected = false
|
||||||
|
artistSelected = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
Text(text = stringResource(MR.strings.title))
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Checkbox(
|
||||||
|
checked = authorSelected,
|
||||||
|
onCheckedChange = { authorSelected = it },
|
||||||
|
enabled = titleSelected,
|
||||||
|
)
|
||||||
|
Text(text = stringResource(MR.strings.author))
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Checkbox(
|
||||||
|
checked = artistSelected,
|
||||||
|
onCheckedChange = { artistSelected = it },
|
||||||
|
enabled = titleSelected,
|
||||||
|
)
|
||||||
|
Text(text = stringResource(MR.strings.artist))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
onConfirm(
|
||||||
|
ExportOptions(
|
||||||
|
includeTitle = titleSelected,
|
||||||
|
includeAuthor = authorSelected,
|
||||||
|
includeArtist = artistSelected,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
onDismissRequest()
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(MR.strings.action_save))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = onDismissRequest) {
|
||||||
|
Text(text = stringResource(MR.strings.action_cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SY -->
|
||||||
@Composable
|
@Composable
|
||||||
private fun getSyncPreferences(syncPreferences: SyncPreferences, syncService: Int): List<Preference> {
|
private fun getSyncPreferences(syncPreferences: SyncPreferences, syncService: Int): List<Preference> {
|
||||||
return listOf(
|
return listOf(
|
||||||
@@ -362,7 +517,7 @@ object SettingsDataScreen : SearchableSettings {
|
|||||||
title = stringResource(SYMR.strings.pref_sync_service_category),
|
title = stringResource(SYMR.strings.pref_sync_service_category),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = syncPreferences.syncService(),
|
preference = syncPreferences.syncService(),
|
||||||
title = stringResource(SYMR.strings.pref_sync_service),
|
title = stringResource(SYMR.strings.pref_sync_service),
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
SyncManager.SyncService.NONE.value to stringResource(MR.strings.off),
|
SyncManager.SyncService.NONE.value to stringResource(MR.strings.off),
|
||||||
@@ -502,11 +657,27 @@ object SettingsDataScreen : SearchableSettings {
|
|||||||
@Composable
|
@Composable
|
||||||
private fun getSelfHostPreferences(syncPreferences: SyncPreferences): List<Preference> {
|
private fun getSelfHostPreferences(syncPreferences: SyncPreferences): List<Preference> {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
val qrScanLauncher = rememberLauncherForActivityResult(ScanContract()) {
|
||||||
|
if (it.contents != null && it.contents.isNotEmpty()) {
|
||||||
|
syncPreferences.clientAPIKey().set(it.contents)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val context = LocalContext.current
|
||||||
|
val scanOptions = remember {
|
||||||
|
ScanOptions().apply {
|
||||||
|
setDesiredBarcodeFormats(ScanOptions.QR_CODE)
|
||||||
|
setOrientationLocked(false)
|
||||||
|
setPrompt(SYMR.strings.scan_qr_code.getString(context))
|
||||||
|
addExtra(Intents.Scan.SCAN_TYPE, Intents.Scan.MIXED_SCAN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return listOf(
|
return listOf(
|
||||||
Preference.PreferenceItem.EditTextPreference(
|
Preference.PreferenceItem.EditTextPreference(
|
||||||
title = stringResource(SYMR.strings.pref_sync_host),
|
title = stringResource(SYMR.strings.pref_sync_host),
|
||||||
subtitle = stringResource(SYMR.strings.pref_sync_host_summ),
|
subtitle = stringResource(SYMR.strings.pref_sync_host_summ),
|
||||||
pref = syncPreferences.clientHost(),
|
preference = syncPreferences.clientHost(),
|
||||||
onValueChanged = { newValue ->
|
onValueChanged = { newValue ->
|
||||||
scope.launch {
|
scope.launch {
|
||||||
// Trim spaces at the beginning and end, then remove trailing slash if present
|
// Trim spaces at the beginning and end, then remove trailing slash if present
|
||||||
@@ -517,11 +688,32 @@ object SettingsDataScreen : SearchableSettings {
|
|||||||
true
|
true
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.EditTextPreference(
|
Preference.PreferenceItem.CustomPreference(
|
||||||
title = stringResource(SYMR.strings.pref_sync_api_key),
|
title = stringResource(SYMR.strings.pref_sync_api_key),
|
||||||
subtitle = stringResource(SYMR.strings.pref_sync_api_key_summ),
|
) {
|
||||||
pref = syncPreferences.clientAPIKey(),
|
val values by syncPreferences.clientAPIKey().collectAsState()
|
||||||
),
|
EditTextPreferenceWidget(
|
||||||
|
title = stringResource(SYMR.strings.pref_sync_api_key),
|
||||||
|
subtitle = stringResource(SYMR.strings.pref_sync_api_key_summ),
|
||||||
|
onConfirm = {
|
||||||
|
syncPreferences.clientAPIKey().set(it)
|
||||||
|
true
|
||||||
|
},
|
||||||
|
icon = null,
|
||||||
|
value = values,
|
||||||
|
widget = {
|
||||||
|
IconButton(
|
||||||
|
onClick = { qrScanLauncher.launch(scanOptions) },
|
||||||
|
modifier = Modifier.padding(start = TrailingWidgetBuffer),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Filled.QrCodeScanner,
|
||||||
|
contentDescription = stringResource(SYMR.strings.scan_qr_code),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,7 +759,7 @@ object SettingsDataScreen : SearchableSettings {
|
|||||||
title = stringResource(SYMR.strings.pref_sync_automatic_category),
|
title = stringResource(SYMR.strings.pref_sync_automatic_category),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = syncIntervalPref,
|
preference = syncIntervalPref,
|
||||||
title = stringResource(SYMR.strings.pref_sync_interval),
|
title = stringResource(SYMR.strings.pref_sync_interval),
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
0 to stringResource(MR.strings.off),
|
0 to stringResource(MR.strings.off),
|
||||||
@@ -591,4 +783,5 @@ object SettingsDataScreen : SearchableSettings {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
// SY <--
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-14
@@ -37,20 +37,35 @@ object SettingsDownloadScreen : SearchableSettings {
|
|||||||
val allCategories by getCategories.subscribe().collectAsState(initial = emptyList())
|
val allCategories by getCategories.subscribe().collectAsState(initial = emptyList())
|
||||||
|
|
||||||
val downloadPreferences = remember { Injekt.get<DownloadPreferences>() }
|
val downloadPreferences = remember { Injekt.get<DownloadPreferences>() }
|
||||||
|
val parallelSourceLimit by downloadPreferences.parallelSourceLimit().collectAsState()
|
||||||
|
val parallelPageLimit by downloadPreferences.parallelPageLimit().collectAsState()
|
||||||
return listOf(
|
return listOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = downloadPreferences.downloadOnlyOverWifi(),
|
preference = downloadPreferences.downloadOnlyOverWifi(),
|
||||||
title = stringResource(MR.strings.connected_to_wifi),
|
title = stringResource(MR.strings.connected_to_wifi),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = downloadPreferences.saveChaptersAsCBZ(),
|
preference = downloadPreferences.saveChaptersAsCBZ(),
|
||||||
title = stringResource(MR.strings.save_chapter_as_cbz),
|
title = stringResource(MR.strings.save_chapter_as_cbz),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = downloadPreferences.splitTallImages(),
|
preference = downloadPreferences.splitTallImages(),
|
||||||
title = stringResource(MR.strings.split_tall_images),
|
title = stringResource(MR.strings.split_tall_images),
|
||||||
subtitle = stringResource(MR.strings.split_tall_images_summary),
|
subtitle = stringResource(MR.strings.split_tall_images_summary),
|
||||||
),
|
),
|
||||||
|
Preference.PreferenceItem.SliderPreference(
|
||||||
|
value = parallelSourceLimit,
|
||||||
|
valueRange = 1..10,
|
||||||
|
title = stringResource(MR.strings.pref_download_concurrent_sources),
|
||||||
|
onValueChanged = { downloadPreferences.parallelSourceLimit().set(it) },
|
||||||
|
),
|
||||||
|
Preference.PreferenceItem.SliderPreference(
|
||||||
|
value = parallelPageLimit,
|
||||||
|
valueRange = 1..15,
|
||||||
|
title = stringResource(MR.strings.pref_download_concurrent_pages),
|
||||||
|
subtitle = stringResource(MR.strings.pref_download_concurrent_pages_summary),
|
||||||
|
onValueChanged = { downloadPreferences.parallelPageLimit().set(it) },
|
||||||
|
),
|
||||||
getDeleteChaptersGroup(
|
getDeleteChaptersGroup(
|
||||||
downloadPreferences = downloadPreferences,
|
downloadPreferences = downloadPreferences,
|
||||||
categories = allCategories,
|
categories = allCategories,
|
||||||
@@ -72,12 +87,11 @@ object SettingsDownloadScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.pref_category_delete_chapters),
|
title = stringResource(MR.strings.pref_category_delete_chapters),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = downloadPreferences.removeAfterMarkedAsRead(),
|
preference = downloadPreferences.removeAfterMarkedAsRead(),
|
||||||
title = stringResource(MR.strings.pref_remove_after_marked_as_read),
|
title = stringResource(MR.strings.pref_remove_after_marked_as_read),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = downloadPreferences.removeAfterReadSlots(),
|
preference = downloadPreferences.removeAfterReadSlots(),
|
||||||
title = stringResource(MR.strings.pref_remove_after_read),
|
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
-1 to stringResource(MR.strings.disabled),
|
-1 to stringResource(MR.strings.disabled),
|
||||||
0 to stringResource(MR.strings.last_read_chapter),
|
0 to stringResource(MR.strings.last_read_chapter),
|
||||||
@@ -86,9 +100,10 @@ object SettingsDownloadScreen : SearchableSettings {
|
|||||||
3 to stringResource(MR.strings.fourth_to_last),
|
3 to stringResource(MR.strings.fourth_to_last),
|
||||||
4 to stringResource(MR.strings.fifth_to_last),
|
4 to stringResource(MR.strings.fifth_to_last),
|
||||||
),
|
),
|
||||||
|
title = stringResource(MR.strings.pref_remove_after_read),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = downloadPreferences.removeBookmarkedChapters(),
|
preference = downloadPreferences.removeBookmarkedChapters(),
|
||||||
title = stringResource(MR.strings.pref_remove_bookmarked_chapters),
|
title = stringResource(MR.strings.pref_remove_bookmarked_chapters),
|
||||||
),
|
),
|
||||||
getExcludedCategoriesPreference(
|
getExcludedCategoriesPreference(
|
||||||
@@ -105,11 +120,11 @@ object SettingsDownloadScreen : SearchableSettings {
|
|||||||
categories: () -> List<Category>,
|
categories: () -> List<Category>,
|
||||||
): Preference.PreferenceItem.MultiSelectListPreference {
|
): Preference.PreferenceItem.MultiSelectListPreference {
|
||||||
return Preference.PreferenceItem.MultiSelectListPreference(
|
return Preference.PreferenceItem.MultiSelectListPreference(
|
||||||
pref = downloadPreferences.removeExcludeCategories(),
|
preference = downloadPreferences.removeExcludeCategories(),
|
||||||
title = stringResource(MR.strings.pref_remove_exclude_categories),
|
|
||||||
entries = categories()
|
entries = categories()
|
||||||
.associate { it.id.toString() to it.visualName }
|
.associate { it.id.toString() to it.visualName }
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.pref_remove_exclude_categories),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,11 +164,11 @@ object SettingsDownloadScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.pref_category_auto_download),
|
title = stringResource(MR.strings.pref_category_auto_download),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = downloadNewChaptersPref,
|
preference = downloadNewChaptersPref,
|
||||||
title = stringResource(MR.strings.pref_download_new),
|
title = stringResource(MR.strings.pref_download_new),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = downloadNewUnreadChaptersOnlyPref,
|
preference = downloadNewUnreadChaptersOnlyPref,
|
||||||
title = stringResource(MR.strings.pref_download_new_unread_chapters_only),
|
title = stringResource(MR.strings.pref_download_new_unread_chapters_only),
|
||||||
enabled = downloadNewChapters,
|
enabled = downloadNewChapters,
|
||||||
),
|
),
|
||||||
@@ -164,8 +179,8 @@ object SettingsDownloadScreen : SearchableSettings {
|
|||||||
included = included,
|
included = included,
|
||||||
excluded = excluded,
|
excluded = excluded,
|
||||||
),
|
),
|
||||||
onClick = { showDialog = true },
|
|
||||||
enabled = downloadNewChapters,
|
enabled = downloadNewChapters,
|
||||||
|
onClick = { showDialog = true },
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -179,8 +194,7 @@ object SettingsDownloadScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.download_ahead),
|
title = stringResource(MR.strings.download_ahead),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = downloadPreferences.autoDownloadWhileReading(),
|
preference = downloadPreferences.autoDownloadWhileReading(),
|
||||||
title = stringResource(MR.strings.auto_download_while_reading),
|
|
||||||
entries = listOf(0, 2, 3, 5, 10)
|
entries = listOf(0, 2, 3, 5, 10)
|
||||||
.associateWith {
|
.associateWith {
|
||||||
if (it == 0) {
|
if (it == 0) {
|
||||||
@@ -190,6 +204,7 @@ object SettingsDownloadScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.auto_download_while_reading),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.download_ahead_info)),
|
Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.download_ahead_info)),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.core.content.ContextCompat.startActivity
|
|
||||||
import eu.kanade.presentation.library.components.SyncFavoritesWarningDialog
|
import eu.kanade.presentation.library.components.SyncFavoritesWarningDialog
|
||||||
import eu.kanade.presentation.more.settings.Preference
|
import eu.kanade.presentation.more.settings.Preference
|
||||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||||
@@ -52,6 +51,7 @@ import exh.eh.EHentaiUpdateWorker
|
|||||||
import exh.eh.EHentaiUpdateWorkerConstants
|
import exh.eh.EHentaiUpdateWorkerConstants
|
||||||
import exh.eh.EHentaiUpdaterStats
|
import exh.eh.EHentaiUpdaterStats
|
||||||
import exh.metadata.metadata.EHentaiSearchMetadata
|
import exh.metadata.metadata.EHentaiSearchMetadata
|
||||||
|
import exh.source.ExhPreferences
|
||||||
import exh.ui.login.EhLoginActivity
|
import exh.ui.login.EhLoginActivity
|
||||||
import exh.util.nullIfBlank
|
import exh.util.nullIfBlank
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
@@ -64,7 +64,6 @@ import tachiyomi.core.common.util.lang.launchNonCancellable
|
|||||||
import tachiyomi.core.common.util.lang.withIOContext
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
import tachiyomi.core.common.util.lang.withUIContext
|
import tachiyomi.core.common.util.lang.withUIContext
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.domain.UnsortedPreferences
|
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences.Companion.DEVICE_CHARGING
|
import tachiyomi.domain.library.service.LibraryPreferences.Companion.DEVICE_CHARGING
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences.Companion.DEVICE_ONLY_ON_WIFI
|
import tachiyomi.domain.library.service.LibraryPreferences.Companion.DEVICE_ONLY_ON_WIFI
|
||||||
import tachiyomi.domain.manga.interactor.DeleteFavoriteEntries
|
import tachiyomi.domain.manga.interactor.DeleteFavoriteEntries
|
||||||
@@ -89,22 +88,22 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
@Composable
|
@Composable
|
||||||
override fun getTitleRes() = SYMR.strings.pref_category_eh
|
override fun getTitleRes() = SYMR.strings.pref_category_eh
|
||||||
|
|
||||||
override fun isEnabled(): Boolean = Injekt.get<UnsortedPreferences>().isHentaiEnabled().get()
|
override fun isEnabled(): Boolean = Injekt.get<ExhPreferences>().isHentaiEnabled().get()
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Reconfigure(
|
fun Reconfigure(
|
||||||
unsortedPreferences: UnsortedPreferences,
|
exhPreferences: ExhPreferences,
|
||||||
openWarnConfigureDialogController: () -> Unit,
|
openWarnConfigureDialogController: () -> Unit,
|
||||||
) {
|
) {
|
||||||
var initialLoadGuard by remember { mutableStateOf(false) }
|
var initialLoadGuard by remember { mutableStateOf(false) }
|
||||||
val useHentaiAtHome by unsortedPreferences.useHentaiAtHome().collectAsState()
|
val useHentaiAtHome by exhPreferences.useHentaiAtHome().collectAsState()
|
||||||
val useJapaneseTitle by unsortedPreferences.useJapaneseTitle().collectAsState()
|
val useJapaneseTitle by exhPreferences.useJapaneseTitle().collectAsState()
|
||||||
val useOriginalImages by unsortedPreferences.exhUseOriginalImages().collectAsState()
|
val useOriginalImages by exhPreferences.exhUseOriginalImages().collectAsState()
|
||||||
val ehTagFilterValue by unsortedPreferences.ehTagFilterValue().collectAsState()
|
val ehTagFilterValue by exhPreferences.ehTagFilterValue().collectAsState()
|
||||||
val ehTagWatchingValue by unsortedPreferences.ehTagWatchingValue().collectAsState()
|
val ehTagWatchingValue by exhPreferences.ehTagWatchingValue().collectAsState()
|
||||||
val settingsLanguages by unsortedPreferences.exhSettingsLanguages().collectAsState()
|
val settingsLanguages by exhPreferences.exhSettingsLanguages().collectAsState()
|
||||||
val enabledCategories by unsortedPreferences.exhEnabledCategories().collectAsState()
|
val enabledCategories by exhPreferences.exhEnabledCategories().collectAsState()
|
||||||
val imageQuality by unsortedPreferences.imageQuality().collectAsState()
|
val imageQuality by exhPreferences.imageQuality().collectAsState()
|
||||||
DisposableEffect(
|
DisposableEffect(
|
||||||
useHentaiAtHome,
|
useHentaiAtHome,
|
||||||
useJapaneseTitle,
|
useJapaneseTitle,
|
||||||
@@ -125,15 +124,15 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun getPreferences(): List<Preference> {
|
override fun getPreferences(): List<Preference> {
|
||||||
val unsortedPreferences: UnsortedPreferences = remember { Injekt.get() }
|
val exhPreferences: ExhPreferences = remember { Injekt.get() }
|
||||||
val getFlatMetadataById: GetFlatMetadataById = remember { Injekt.get() }
|
val getFlatMetadataById: GetFlatMetadataById = remember { Injekt.get() }
|
||||||
val deleteFavoriteEntries: DeleteFavoriteEntries = remember { Injekt.get() }
|
val deleteFavoriteEntries: DeleteFavoriteEntries = remember { Injekt.get() }
|
||||||
val getExhFavoriteMangaWithMetadata: GetExhFavoriteMangaWithMetadata = remember { Injekt.get() }
|
val getExhFavoriteMangaWithMetadata: GetExhFavoriteMangaWithMetadata = remember { Injekt.get() }
|
||||||
val exhentaiEnabled by unsortedPreferences.enableExhentai().collectAsState()
|
val exhentaiEnabled by exhPreferences.enableExhentai().collectAsState()
|
||||||
var runConfigureDialog by remember { mutableStateOf(false) }
|
var runConfigureDialog by remember { mutableStateOf(false) }
|
||||||
val openWarnConfigureDialogController = { runConfigureDialog = true }
|
val openWarnConfigureDialogController = { runConfigureDialog = true }
|
||||||
|
|
||||||
Reconfigure(unsortedPreferences, openWarnConfigureDialogController)
|
Reconfigure(exhPreferences, openWarnConfigureDialogController)
|
||||||
|
|
||||||
ConfigureExhDialog(run = runConfigureDialog, onRunning = { runConfigureDialog = false })
|
ConfigureExhDialog(run = runConfigureDialog, onRunning = { runConfigureDialog = false })
|
||||||
|
|
||||||
@@ -141,36 +140,36 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
Preference.PreferenceGroup(
|
Preference.PreferenceGroup(
|
||||||
stringResource(SYMR.strings.ehentai_prefs_account_settings),
|
stringResource(SYMR.strings.ehentai_prefs_account_settings),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
getLoginPreference(unsortedPreferences, openWarnConfigureDialogController),
|
getLoginPreference(exhPreferences, openWarnConfigureDialogController),
|
||||||
useHentaiAtHome(exhentaiEnabled, unsortedPreferences),
|
useHentaiAtHome(exhentaiEnabled, exhPreferences),
|
||||||
useJapaneseTitle(exhentaiEnabled, unsortedPreferences),
|
useJapaneseTitle(exhentaiEnabled, exhPreferences),
|
||||||
useOriginalImages(exhentaiEnabled, unsortedPreferences),
|
useOriginalImages(exhentaiEnabled, exhPreferences),
|
||||||
watchedTags(exhentaiEnabled),
|
watchedTags(exhentaiEnabled),
|
||||||
tagFilterThreshold(exhentaiEnabled, unsortedPreferences),
|
tagFilterThreshold(exhentaiEnabled, exhPreferences),
|
||||||
tagWatchingThreshold(exhentaiEnabled, unsortedPreferences),
|
tagWatchingThreshold(exhentaiEnabled, exhPreferences),
|
||||||
settingsLanguages(exhentaiEnabled, unsortedPreferences),
|
settingsLanguages(exhentaiEnabled, exhPreferences),
|
||||||
enabledCategories(exhentaiEnabled, unsortedPreferences),
|
enabledCategories(exhentaiEnabled, exhPreferences),
|
||||||
watchedListDefaultState(exhentaiEnabled, unsortedPreferences),
|
watchedListDefaultState(exhentaiEnabled, exhPreferences),
|
||||||
imageQuality(exhentaiEnabled, unsortedPreferences),
|
imageQuality(exhentaiEnabled, exhPreferences),
|
||||||
enhancedEhentaiView(unsortedPreferences),
|
enhancedEhentaiView(exhPreferences),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Preference.PreferenceGroup(
|
Preference.PreferenceGroup(
|
||||||
stringResource(SYMR.strings.favorites_sync),
|
stringResource(SYMR.strings.favorites_sync),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
readOnlySync(unsortedPreferences),
|
readOnlySync(exhPreferences),
|
||||||
syncFavoriteNotes(),
|
syncFavoriteNotes(),
|
||||||
lenientSync(unsortedPreferences),
|
lenientSync(exhPreferences),
|
||||||
forceSyncReset(deleteFavoriteEntries),
|
forceSyncReset(deleteFavoriteEntries),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Preference.PreferenceGroup(
|
Preference.PreferenceGroup(
|
||||||
stringResource(SYMR.strings.gallery_update_checker),
|
stringResource(SYMR.strings.gallery_update_checker),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
updateCheckerFrequency(unsortedPreferences),
|
updateCheckerFrequency(exhPreferences),
|
||||||
autoUpdateRequirements(unsortedPreferences),
|
autoUpdateRequirements(exhPreferences),
|
||||||
updaterStatistics(
|
updaterStatistics(
|
||||||
unsortedPreferences,
|
exhPreferences,
|
||||||
getExhFavoriteMangaWithMetadata,
|
getExhFavoriteMangaWithMetadata,
|
||||||
getFlatMetadataById,
|
getFlatMetadataById,
|
||||||
),
|
),
|
||||||
@@ -181,7 +180,7 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun getLoginPreference(
|
fun getLoginPreference(
|
||||||
unsortedPreferences: UnsortedPreferences,
|
exhPreferences: ExhPreferences,
|
||||||
openWarnConfigureDialogController: () -> Unit,
|
openWarnConfigureDialogController: () -> Unit,
|
||||||
): Preference.PreferenceItem.SwitchPreference {
|
): Preference.PreferenceItem.SwitchPreference {
|
||||||
val activityResultContract =
|
val activityResultContract =
|
||||||
@@ -192,9 +191,9 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val value by unsortedPreferences.enableExhentai().collectAsState()
|
val value by exhPreferences.enableExhentai().collectAsState()
|
||||||
return Preference.PreferenceItem.SwitchPreference(
|
return Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = unsortedPreferences.enableExhentai(),
|
preference = exhPreferences.enableExhentai(),
|
||||||
title = stringResource(SYMR.strings.enable_exhentai),
|
title = stringResource(SYMR.strings.enable_exhentai),
|
||||||
subtitle = if (!value) {
|
subtitle = if (!value) {
|
||||||
stringResource(SYMR.strings.requires_login)
|
stringResource(SYMR.strings.requires_login)
|
||||||
@@ -203,7 +202,7 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
},
|
},
|
||||||
onValueChanged = { newVal ->
|
onValueChanged = { newVal ->
|
||||||
if (!newVal) {
|
if (!newVal) {
|
||||||
unsortedPreferences.enableExhentai().set(false)
|
exhPreferences.enableExhentai().set(false)
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
activityResultContract.launch(EhLoginActivity.newIntent(context))
|
activityResultContract.launch(EhLoginActivity.newIntent(context))
|
||||||
@@ -216,10 +215,10 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
@Composable
|
@Composable
|
||||||
fun useHentaiAtHome(
|
fun useHentaiAtHome(
|
||||||
exhentaiEnabled: Boolean,
|
exhentaiEnabled: Boolean,
|
||||||
unsortedPreferences: UnsortedPreferences,
|
exhPreferences: ExhPreferences,
|
||||||
): Preference.PreferenceItem.ListPreference<Int> {
|
): Preference.PreferenceItem.ListPreference<Int> {
|
||||||
return Preference.PreferenceItem.ListPreference(
|
return Preference.PreferenceItem.ListPreference(
|
||||||
pref = unsortedPreferences.useHentaiAtHome(),
|
preference = exhPreferences.useHentaiAtHome(),
|
||||||
title = stringResource(SYMR.strings.use_hentai_at_home),
|
title = stringResource(SYMR.strings.use_hentai_at_home),
|
||||||
subtitle = stringResource(SYMR.strings.use_hentai_at_home_summary),
|
subtitle = stringResource(SYMR.strings.use_hentai_at_home_summary),
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
@@ -233,11 +232,11 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
@Composable
|
@Composable
|
||||||
fun useJapaneseTitle(
|
fun useJapaneseTitle(
|
||||||
exhentaiEnabled: Boolean,
|
exhentaiEnabled: Boolean,
|
||||||
unsortedPreferences: UnsortedPreferences,
|
exhPreferences: ExhPreferences,
|
||||||
): Preference.PreferenceItem.SwitchPreference {
|
): Preference.PreferenceItem.SwitchPreference {
|
||||||
val value by unsortedPreferences.useJapaneseTitle().collectAsState()
|
val value by exhPreferences.useJapaneseTitle().collectAsState()
|
||||||
return Preference.PreferenceItem.SwitchPreference(
|
return Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = unsortedPreferences.useJapaneseTitle(),
|
preference = exhPreferences.useJapaneseTitle(),
|
||||||
title = stringResource(SYMR.strings.show_japanese_titles),
|
title = stringResource(SYMR.strings.show_japanese_titles),
|
||||||
subtitle = if (value) {
|
subtitle = if (value) {
|
||||||
stringResource(SYMR.strings.show_japanese_titles_option_1)
|
stringResource(SYMR.strings.show_japanese_titles_option_1)
|
||||||
@@ -251,11 +250,11 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
@Composable
|
@Composable
|
||||||
fun useOriginalImages(
|
fun useOriginalImages(
|
||||||
exhentaiEnabled: Boolean,
|
exhentaiEnabled: Boolean,
|
||||||
unsortedPreferences: UnsortedPreferences,
|
exhPreferences: ExhPreferences,
|
||||||
): Preference.PreferenceItem.SwitchPreference {
|
): Preference.PreferenceItem.SwitchPreference {
|
||||||
val value by unsortedPreferences.exhUseOriginalImages().collectAsState()
|
val value by exhPreferences.exhUseOriginalImages().collectAsState()
|
||||||
return Preference.PreferenceItem.SwitchPreference(
|
return Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = unsortedPreferences.exhUseOriginalImages(),
|
preference = exhPreferences.exhUseOriginalImages(),
|
||||||
title = stringResource(SYMR.strings.use_original_images),
|
title = stringResource(SYMR.strings.use_original_images),
|
||||||
subtitle = if (value) {
|
subtitle = if (value) {
|
||||||
stringResource(SYMR.strings.use_original_images_on)
|
stringResource(SYMR.strings.use_original_images_on)
|
||||||
@@ -273,8 +272,7 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
title = stringResource(SYMR.strings.watched_tags),
|
title = stringResource(SYMR.strings.watched_tags),
|
||||||
subtitle = stringResource(SYMR.strings.watched_tags_summary),
|
subtitle = stringResource(SYMR.strings.watched_tags_summary),
|
||||||
onClick = {
|
onClick = {
|
||||||
startActivity(
|
context.startActivity(
|
||||||
context,
|
|
||||||
WebViewActivity.newIntent(
|
WebViewActivity.newIntent(
|
||||||
context,
|
context,
|
||||||
url = "https://exhentai.org/mytags",
|
url = "https://exhentai.org/mytags",
|
||||||
@@ -353,9 +351,9 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
@Composable
|
@Composable
|
||||||
fun tagFilterThreshold(
|
fun tagFilterThreshold(
|
||||||
exhentaiEnabled: Boolean,
|
exhentaiEnabled: Boolean,
|
||||||
unsortedPreferences: UnsortedPreferences,
|
exhPreferences: ExhPreferences,
|
||||||
): Preference.PreferenceItem.TextPreference {
|
): Preference.PreferenceItem.TextPreference {
|
||||||
val value by unsortedPreferences.ehTagFilterValue().collectAsState()
|
val value by exhPreferences.ehTagFilterValue().collectAsState()
|
||||||
var dialogOpen by remember { mutableStateOf(false) }
|
var dialogOpen by remember { mutableStateOf(false) }
|
||||||
if (dialogOpen) {
|
if (dialogOpen) {
|
||||||
TagThresholdDialog(
|
TagThresholdDialog(
|
||||||
@@ -366,7 +364,7 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
outsideRangeError = stringResource(SYMR.strings.tag_filtering_threshhold_error),
|
outsideRangeError = stringResource(SYMR.strings.tag_filtering_threshhold_error),
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
dialogOpen = false
|
dialogOpen = false
|
||||||
unsortedPreferences.ehTagFilterValue().set(it)
|
exhPreferences.ehTagFilterValue().set(it)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -383,9 +381,9 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
@Composable
|
@Composable
|
||||||
fun tagWatchingThreshold(
|
fun tagWatchingThreshold(
|
||||||
exhentaiEnabled: Boolean,
|
exhentaiEnabled: Boolean,
|
||||||
unsortedPreferences: UnsortedPreferences,
|
exhPreferences: ExhPreferences,
|
||||||
): Preference.PreferenceItem.TextPreference {
|
): Preference.PreferenceItem.TextPreference {
|
||||||
val value by unsortedPreferences.ehTagWatchingValue().collectAsState()
|
val value by exhPreferences.ehTagWatchingValue().collectAsState()
|
||||||
var dialogOpen by remember { mutableStateOf(false) }
|
var dialogOpen by remember { mutableStateOf(false) }
|
||||||
if (dialogOpen) {
|
if (dialogOpen) {
|
||||||
TagThresholdDialog(
|
TagThresholdDialog(
|
||||||
@@ -396,7 +394,7 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
outsideRangeError = stringResource(SYMR.strings.tag_watching_threshhold_error),
|
outsideRangeError = stringResource(SYMR.strings.tag_watching_threshhold_error),
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
dialogOpen = false
|
dialogOpen = false
|
||||||
unsortedPreferences.ehTagWatchingValue().set(it)
|
exhPreferences.ehTagWatchingValue().set(it)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -606,9 +604,9 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
@Composable
|
@Composable
|
||||||
fun settingsLanguages(
|
fun settingsLanguages(
|
||||||
exhentaiEnabled: Boolean,
|
exhentaiEnabled: Boolean,
|
||||||
unsortedPreferences: UnsortedPreferences,
|
exhPreferences: ExhPreferences,
|
||||||
): Preference.PreferenceItem.TextPreference {
|
): Preference.PreferenceItem.TextPreference {
|
||||||
val value by unsortedPreferences.exhSettingsLanguages().collectAsState()
|
val value by exhPreferences.exhSettingsLanguages().collectAsState()
|
||||||
var dialogOpen by remember { mutableStateOf(false) }
|
var dialogOpen by remember { mutableStateOf(false) }
|
||||||
if (dialogOpen) {
|
if (dialogOpen) {
|
||||||
LanguagesDialog(
|
LanguagesDialog(
|
||||||
@@ -616,7 +614,7 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
initialValue = value,
|
initialValue = value,
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
dialogOpen = false
|
dialogOpen = false
|
||||||
unsortedPreferences.exhSettingsLanguages().set(it)
|
exhPreferences.exhSettingsLanguages().set(it)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -772,9 +770,9 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
@Composable
|
@Composable
|
||||||
fun enabledCategories(
|
fun enabledCategories(
|
||||||
exhentaiEnabled: Boolean,
|
exhentaiEnabled: Boolean,
|
||||||
unsortedPreferences: UnsortedPreferences,
|
exhPreferences: ExhPreferences,
|
||||||
): Preference.PreferenceItem.TextPreference {
|
): Preference.PreferenceItem.TextPreference {
|
||||||
val value by unsortedPreferences.exhEnabledCategories().collectAsState()
|
val value by exhPreferences.exhEnabledCategories().collectAsState()
|
||||||
var dialogOpen by remember { mutableStateOf(false) }
|
var dialogOpen by remember { mutableStateOf(false) }
|
||||||
if (dialogOpen) {
|
if (dialogOpen) {
|
||||||
FrontPageCategoriesDialog(
|
FrontPageCategoriesDialog(
|
||||||
@@ -782,7 +780,7 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
initialValue = value,
|
initialValue = value,
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
dialogOpen = false
|
dialogOpen = false
|
||||||
unsortedPreferences.exhEnabledCategories().set(it)
|
exhPreferences.exhEnabledCategories().set(it)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -799,10 +797,10 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
@Composable
|
@Composable
|
||||||
fun watchedListDefaultState(
|
fun watchedListDefaultState(
|
||||||
exhentaiEnabled: Boolean,
|
exhentaiEnabled: Boolean,
|
||||||
unsortedPreferences: UnsortedPreferences,
|
exhPreferences: ExhPreferences,
|
||||||
): Preference.PreferenceItem.SwitchPreference {
|
): Preference.PreferenceItem.SwitchPreference {
|
||||||
return Preference.PreferenceItem.SwitchPreference(
|
return Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = unsortedPreferences.exhWatchedListDefaultState(),
|
preference = exhPreferences.exhWatchedListDefaultState(),
|
||||||
title = stringResource(SYMR.strings.watched_list_default),
|
title = stringResource(SYMR.strings.watched_list_default),
|
||||||
subtitle = stringResource(SYMR.strings.watched_list_state_summary),
|
subtitle = stringResource(SYMR.strings.watched_list_state_summary),
|
||||||
enabled = exhentaiEnabled,
|
enabled = exhentaiEnabled,
|
||||||
@@ -812,10 +810,10 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
@Composable
|
@Composable
|
||||||
fun imageQuality(
|
fun imageQuality(
|
||||||
exhentaiEnabled: Boolean,
|
exhentaiEnabled: Boolean,
|
||||||
unsortedPreferences: UnsortedPreferences,
|
exhPreferences: ExhPreferences,
|
||||||
): Preference.PreferenceItem.ListPreference<String> {
|
): Preference.PreferenceItem.ListPreference<String> {
|
||||||
return Preference.PreferenceItem.ListPreference(
|
return Preference.PreferenceItem.ListPreference(
|
||||||
pref = unsortedPreferences.imageQuality(),
|
preference = exhPreferences.imageQuality(),
|
||||||
title = stringResource(SYMR.strings.eh_image_quality_summary),
|
title = stringResource(SYMR.strings.eh_image_quality_summary),
|
||||||
subtitle = stringResource(SYMR.strings.eh_image_quality),
|
subtitle = stringResource(SYMR.strings.eh_image_quality),
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
@@ -831,18 +829,18 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun enhancedEhentaiView(unsortedPreferences: UnsortedPreferences): Preference.PreferenceItem.SwitchPreference {
|
fun enhancedEhentaiView(exhPreferences: ExhPreferences): Preference.PreferenceItem.SwitchPreference {
|
||||||
return Preference.PreferenceItem.SwitchPreference(
|
return Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = unsortedPreferences.enhancedEHentaiView(),
|
preference = exhPreferences.enhancedEHentaiView(),
|
||||||
title = stringResource(SYMR.strings.pref_enhanced_e_hentai_view),
|
title = stringResource(SYMR.strings.pref_enhanced_e_hentai_view),
|
||||||
subtitle = stringResource(SYMR.strings.pref_enhanced_e_hentai_view_summary),
|
subtitle = stringResource(SYMR.strings.pref_enhanced_e_hentai_view_summary),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun readOnlySync(unsortedPreferences: UnsortedPreferences): Preference.PreferenceItem.SwitchPreference {
|
fun readOnlySync(exhPreferences: ExhPreferences): Preference.PreferenceItem.SwitchPreference {
|
||||||
return Preference.PreferenceItem.SwitchPreference(
|
return Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = unsortedPreferences.exhReadOnlySync(),
|
preference = exhPreferences.exhReadOnlySync(),
|
||||||
title = stringResource(SYMR.strings.disable_favorites_uploading),
|
title = stringResource(SYMR.strings.disable_favorites_uploading),
|
||||||
subtitle = stringResource(SYMR.strings.disable_favorites_uploading_summary),
|
subtitle = stringResource(SYMR.strings.disable_favorites_uploading_summary),
|
||||||
)
|
)
|
||||||
@@ -865,9 +863,9 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun lenientSync(unsortedPreferences: UnsortedPreferences): Preference.PreferenceItem.SwitchPreference {
|
fun lenientSync(exhPreferences: ExhPreferences): Preference.PreferenceItem.SwitchPreference {
|
||||||
return Preference.PreferenceItem.SwitchPreference(
|
return Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = unsortedPreferences.exhLenientSync(),
|
preference = exhPreferences.exhLenientSync(),
|
||||||
title = stringResource(SYMR.strings.ignore_sync_errors),
|
title = stringResource(SYMR.strings.ignore_sync_errors),
|
||||||
subtitle = stringResource(SYMR.strings.ignore_sync_errors_summary),
|
subtitle = stringResource(SYMR.strings.ignore_sync_errors_summary),
|
||||||
)
|
)
|
||||||
@@ -937,12 +935,12 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun updateCheckerFrequency(
|
fun updateCheckerFrequency(
|
||||||
unsortedPreferences: UnsortedPreferences,
|
exhPreferences: ExhPreferences,
|
||||||
): Preference.PreferenceItem.ListPreference<Int> {
|
): Preference.PreferenceItem.ListPreference<Int> {
|
||||||
val value by unsortedPreferences.exhAutoUpdateFrequency().collectAsState()
|
val value by exhPreferences.exhAutoUpdateFrequency().collectAsState()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
return Preference.PreferenceItem.ListPreference(
|
return Preference.PreferenceItem.ListPreference(
|
||||||
pref = unsortedPreferences.exhAutoUpdateFrequency(),
|
preference = exhPreferences.exhAutoUpdateFrequency(),
|
||||||
title = stringResource(SYMR.strings.time_between_batches),
|
title = stringResource(SYMR.strings.time_between_batches),
|
||||||
subtitle = if (value == 0) {
|
subtitle = if (value == 0) {
|
||||||
stringResource(SYMR.strings.time_between_batches_summary_1, stringResource(MR.strings.app_name))
|
stringResource(SYMR.strings.time_between_batches_summary_1, stringResource(MR.strings.app_name))
|
||||||
@@ -973,12 +971,12 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun autoUpdateRequirements(
|
fun autoUpdateRequirements(
|
||||||
unsortedPreferences: UnsortedPreferences,
|
exhPreferences: ExhPreferences,
|
||||||
): Preference.PreferenceItem.MultiSelectListPreference {
|
): Preference.PreferenceItem.MultiSelectListPreference {
|
||||||
val value by unsortedPreferences.exhAutoUpdateRequirements().collectAsState()
|
val value by exhPreferences.exhAutoUpdateRequirements().collectAsState()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
return Preference.PreferenceItem.MultiSelectListPreference(
|
return Preference.PreferenceItem.MultiSelectListPreference(
|
||||||
pref = unsortedPreferences.exhAutoUpdateRequirements(),
|
preference = exhPreferences.exhAutoUpdateRequirements(),
|
||||||
title = stringResource(SYMR.strings.auto_update_restrictions),
|
title = stringResource(SYMR.strings.auto_update_restrictions),
|
||||||
subtitle = remember(value) {
|
subtitle = remember(value) {
|
||||||
context.stringResource(
|
context.stringResource(
|
||||||
@@ -1141,7 +1139,7 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun updaterStatistics(
|
fun updaterStatistics(
|
||||||
unsortedPreferences: UnsortedPreferences,
|
exhPreferences: ExhPreferences,
|
||||||
getExhFavoriteMangaWithMetadata: GetExhFavoriteMangaWithMetadata,
|
getExhFavoriteMangaWithMetadata: GetExhFavoriteMangaWithMetadata,
|
||||||
getFlatMetadataById: GetFlatMetadataById,
|
getFlatMetadataById: GetFlatMetadataById,
|
||||||
): Preference.PreferenceItem.TextPreference {
|
): Preference.PreferenceItem.TextPreference {
|
||||||
@@ -1152,7 +1150,7 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
value = withIOContext {
|
value = withIOContext {
|
||||||
try {
|
try {
|
||||||
val stats =
|
val stats =
|
||||||
unsortedPreferences.exhAutoUpdateStats().get().nullIfBlank()?.let {
|
exhPreferences.exhAutoUpdateStats().get().nullIfBlank()?.let {
|
||||||
Json.decodeFromString<EHentaiUpdaterStats>(it)
|
Json.decodeFromString<EHentaiUpdaterStats>(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+33
-50
@@ -25,7 +25,6 @@ import kotlinx.collections.immutable.persistentListOf
|
|||||||
import kotlinx.collections.immutable.persistentMapOf
|
import kotlinx.collections.immutable.persistentMapOf
|
||||||
import kotlinx.collections.immutable.toImmutableMap
|
import kotlinx.collections.immutable.toImmutableMap
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import tachiyomi.domain.UnsortedPreferences
|
|
||||||
import tachiyomi.domain.category.interactor.GetCategories
|
import tachiyomi.domain.category.interactor.GetCategories
|
||||||
import tachiyomi.domain.category.interactor.ResetCategoryFlags
|
import tachiyomi.domain.category.interactor.ResetCategoryFlags
|
||||||
import tachiyomi.domain.category.model.Category
|
import tachiyomi.domain.category.model.Category
|
||||||
@@ -38,6 +37,8 @@ import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_HAS_U
|
|||||||
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_COMPLETED
|
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_COMPLETED
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_READ
|
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_READ
|
||||||
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_OUTSIDE_RELEASE_PERIOD
|
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_OUTSIDE_RELEASE_PERIOD
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MARK_DUPLICATE_CHAPTER_READ_EXISTING
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MARK_DUPLICATE_CHAPTER_READ_NEW
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.i18n.sy.SYMR
|
import tachiyomi.i18n.sy.SYMR
|
||||||
import tachiyomi.presentation.core.i18n.pluralStringResource
|
import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||||
@@ -57,17 +58,13 @@ object SettingsLibraryScreen : SearchableSettings {
|
|||||||
val getCategories = remember { Injekt.get<GetCategories>() }
|
val getCategories = remember { Injekt.get<GetCategories>() }
|
||||||
val libraryPreferences = remember { Injekt.get<LibraryPreferences>() }
|
val libraryPreferences = remember { Injekt.get<LibraryPreferences>() }
|
||||||
val allCategories by getCategories.subscribe().collectAsState(initial = emptyList())
|
val allCategories by getCategories.subscribe().collectAsState(initial = emptyList())
|
||||||
// SY -->
|
|
||||||
val unsortedPreferences = remember { Injekt.get<UnsortedPreferences>() }
|
|
||||||
// SY <--
|
|
||||||
|
|
||||||
return listOf(
|
return listOf(
|
||||||
getCategoriesGroup(LocalNavigator.currentOrThrow, allCategories, libraryPreferences),
|
getCategoriesGroup(LocalNavigator.currentOrThrow, allCategories, libraryPreferences),
|
||||||
getGlobalUpdateGroup(allCategories, libraryPreferences),
|
getGlobalUpdateGroup(allCategories, libraryPreferences),
|
||||||
getChapterSwipeActionsGroup(libraryPreferences),
|
getBehaviorGroup(libraryPreferences),
|
||||||
// SY -->
|
// SY -->
|
||||||
getSortingCategory(LocalNavigator.currentOrThrow, libraryPreferences),
|
getSortingCategory(LocalNavigator.currentOrThrow, libraryPreferences),
|
||||||
getMigrationCategory(unsortedPreferences),
|
|
||||||
// SY <--
|
// SY <--
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -100,12 +97,12 @@ object SettingsLibraryScreen : SearchableSettings {
|
|||||||
onClick = { navigator.push(CategoryScreen()) },
|
onClick = { navigator.push(CategoryScreen()) },
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = libraryPreferences.defaultCategory(),
|
preference = libraryPreferences.defaultCategory(),
|
||||||
title = stringResource(MR.strings.default_category),
|
|
||||||
entries = ids.zip(labels).toMap().toImmutableMap(),
|
entries = ids.zip(labels).toMap().toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.default_category),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = libraryPreferences.categorizedDisplaySettings(),
|
preference = libraryPreferences.categorizedDisplaySettings(),
|
||||||
title = stringResource(MR.strings.categorized_display_settings),
|
title = stringResource(MR.strings.categorized_display_settings),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
if (!it) {
|
if (!it) {
|
||||||
@@ -157,8 +154,7 @@ object SettingsLibraryScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.pref_category_library_update),
|
title = stringResource(MR.strings.pref_category_library_update),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = autoUpdateIntervalPref,
|
preference = autoUpdateIntervalPref,
|
||||||
title = stringResource(MR.strings.pref_library_update_interval),
|
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
0 to stringResource(MR.strings.update_never),
|
0 to stringResource(MR.strings.update_never),
|
||||||
12 to stringResource(MR.strings.update_12hour),
|
12 to stringResource(MR.strings.update_12hour),
|
||||||
@@ -167,21 +163,22 @@ object SettingsLibraryScreen : SearchableSettings {
|
|||||||
72 to stringResource(MR.strings.update_72hour),
|
72 to stringResource(MR.strings.update_72hour),
|
||||||
168 to stringResource(MR.strings.update_weekly),
|
168 to stringResource(MR.strings.update_weekly),
|
||||||
),
|
),
|
||||||
|
title = stringResource(MR.strings.pref_library_update_interval),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
LibraryUpdateJob.setupTask(context, it)
|
LibraryUpdateJob.setupTask(context, it)
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.MultiSelectListPreference(
|
Preference.PreferenceItem.MultiSelectListPreference(
|
||||||
pref = libraryPreferences.autoUpdateDeviceRestrictions(),
|
preference = libraryPreferences.autoUpdateDeviceRestrictions(),
|
||||||
enabled = autoUpdateInterval > 0,
|
|
||||||
title = stringResource(MR.strings.pref_library_update_restriction),
|
|
||||||
subtitle = stringResource(MR.strings.restrictions),
|
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
DEVICE_ONLY_ON_WIFI to stringResource(MR.strings.connected_to_wifi),
|
DEVICE_ONLY_ON_WIFI to stringResource(MR.strings.connected_to_wifi),
|
||||||
DEVICE_NETWORK_NOT_METERED to stringResource(MR.strings.network_not_metered),
|
DEVICE_NETWORK_NOT_METERED to stringResource(MR.strings.network_not_metered),
|
||||||
DEVICE_CHARGING to stringResource(MR.strings.charging),
|
DEVICE_CHARGING to stringResource(MR.strings.charging),
|
||||||
),
|
),
|
||||||
|
title = stringResource(MR.strings.pref_library_update_restriction),
|
||||||
|
subtitle = stringResource(MR.strings.restrictions),
|
||||||
|
enabled = autoUpdateInterval > 0,
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
// Post to event looper to allow the preference to be updated.
|
// Post to event looper to allow the preference to be updated.
|
||||||
ContextCompat.getMainExecutor(context).execute { LibraryUpdateJob.setupTask(context) }
|
ContextCompat.getMainExecutor(context).execute { LibraryUpdateJob.setupTask(context) }
|
||||||
@@ -199,7 +196,7 @@ object SettingsLibraryScreen : SearchableSettings {
|
|||||||
),
|
),
|
||||||
// SY -->
|
// SY -->
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = libraryPreferences.groupLibraryUpdateType(),
|
preference = libraryPreferences.groupLibraryUpdateType(),
|
||||||
title = stringResource(SYMR.strings.library_group_updates),
|
title = stringResource(SYMR.strings.library_group_updates),
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
GroupLibraryMode.GLOBAL to stringResource(SYMR.strings.library_group_updates_global),
|
GroupLibraryMode.GLOBAL to stringResource(SYMR.strings.library_group_updates_global),
|
||||||
@@ -210,45 +207,37 @@ object SettingsLibraryScreen : SearchableSettings {
|
|||||||
),
|
),
|
||||||
// SY <--
|
// SY <--
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = libraryPreferences.autoUpdateMetadata(),
|
preference = libraryPreferences.autoUpdateMetadata(),
|
||||||
title = stringResource(MR.strings.pref_library_update_refresh_metadata),
|
title = stringResource(MR.strings.pref_library_update_refresh_metadata),
|
||||||
subtitle = stringResource(MR.strings.pref_library_update_refresh_metadata_summary),
|
subtitle = stringResource(MR.strings.pref_library_update_refresh_metadata_summary),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.MultiSelectListPreference(
|
Preference.PreferenceItem.MultiSelectListPreference(
|
||||||
pref = libraryPreferences.autoUpdateMangaRestrictions(),
|
preference = libraryPreferences.autoUpdateMangaRestrictions(),
|
||||||
title = stringResource(MR.strings.pref_library_update_smart_update),
|
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
MANGA_HAS_UNREAD to stringResource(MR.strings.pref_update_only_completely_read),
|
MANGA_HAS_UNREAD to stringResource(MR.strings.pref_update_only_completely_read),
|
||||||
MANGA_NON_READ to stringResource(MR.strings.pref_update_only_started),
|
MANGA_NON_READ to stringResource(MR.strings.pref_update_only_started),
|
||||||
MANGA_NON_COMPLETED to stringResource(MR.strings.pref_update_only_non_completed),
|
MANGA_NON_COMPLETED to stringResource(MR.strings.pref_update_only_non_completed),
|
||||||
MANGA_OUTSIDE_RELEASE_PERIOD to stringResource(MR.strings.pref_update_only_in_release_period),
|
MANGA_OUTSIDE_RELEASE_PERIOD to stringResource(MR.strings.pref_update_only_in_release_period),
|
||||||
),
|
),
|
||||||
|
title = stringResource(MR.strings.pref_library_update_smart_update),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = libraryPreferences.newShowUpdatesCount(),
|
preference = libraryPreferences.newShowUpdatesCount(),
|
||||||
title = stringResource(MR.strings.pref_library_update_show_tab_badge),
|
title = stringResource(MR.strings.pref_library_update_show_tab_badge),
|
||||||
),
|
),
|
||||||
// SY -->
|
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
|
||||||
pref = libraryPreferences.libraryReadDuplicateChapters(),
|
|
||||||
title = stringResource(SYMR.strings.pref_library_mark_duplicate_chapters),
|
|
||||||
subtitle = stringResource(SYMR.strings.pref_library_mark_duplicate_chapters_summary),
|
|
||||||
),
|
|
||||||
// SY <--
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun getChapterSwipeActionsGroup(
|
private fun getBehaviorGroup(
|
||||||
libraryPreferences: LibraryPreferences,
|
libraryPreferences: LibraryPreferences,
|
||||||
): Preference.PreferenceGroup {
|
): Preference.PreferenceGroup {
|
||||||
return Preference.PreferenceGroup(
|
return Preference.PreferenceGroup(
|
||||||
title = stringResource(MR.strings.pref_chapter_swipe),
|
title = stringResource(MR.strings.pref_behavior),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = libraryPreferences.swipeToStartAction(),
|
preference = libraryPreferences.swipeToStartAction(),
|
||||||
title = stringResource(MR.strings.pref_chapter_swipe_start),
|
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
LibraryPreferences.ChapterSwipeAction.Disabled to
|
LibraryPreferences.ChapterSwipeAction.Disabled to
|
||||||
stringResource(MR.strings.disabled),
|
stringResource(MR.strings.disabled),
|
||||||
@@ -259,10 +248,10 @@ object SettingsLibraryScreen : SearchableSettings {
|
|||||||
LibraryPreferences.ChapterSwipeAction.Download to
|
LibraryPreferences.ChapterSwipeAction.Download to
|
||||||
stringResource(MR.strings.action_download),
|
stringResource(MR.strings.action_download),
|
||||||
),
|
),
|
||||||
|
title = stringResource(MR.strings.pref_chapter_swipe_start),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = libraryPreferences.swipeToEndAction(),
|
preference = libraryPreferences.swipeToEndAction(),
|
||||||
title = stringResource(MR.strings.pref_chapter_swipe_end),
|
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
LibraryPreferences.ChapterSwipeAction.Disabled to
|
LibraryPreferences.ChapterSwipeAction.Disabled to
|
||||||
stringResource(MR.strings.disabled),
|
stringResource(MR.strings.disabled),
|
||||||
@@ -273,6 +262,17 @@ object SettingsLibraryScreen : SearchableSettings {
|
|||||||
LibraryPreferences.ChapterSwipeAction.Download to
|
LibraryPreferences.ChapterSwipeAction.Download to
|
||||||
stringResource(MR.strings.action_download),
|
stringResource(MR.strings.action_download),
|
||||||
),
|
),
|
||||||
|
title = stringResource(MR.strings.pref_chapter_swipe_end),
|
||||||
|
),
|
||||||
|
Preference.PreferenceItem.MultiSelectListPreference(
|
||||||
|
preference = libraryPreferences.markDuplicateReadChapterAsRead(),
|
||||||
|
entries = persistentMapOf(
|
||||||
|
MARK_DUPLICATE_CHAPTER_READ_EXISTING to
|
||||||
|
stringResource(MR.strings.pref_mark_duplicate_read_chapter_read_existing),
|
||||||
|
MARK_DUPLICATE_CHAPTER_READ_NEW to
|
||||||
|
stringResource(MR.strings.pref_mark_duplicate_read_chapter_read_new),
|
||||||
|
),
|
||||||
|
title = stringResource(MR.strings.pref_mark_duplicate_read_chapter_read),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -295,22 +295,5 @@ object SettingsLibraryScreen : SearchableSettings {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun getMigrationCategory(unsortedPreferences: UnsortedPreferences): Preference.PreferenceGroup {
|
|
||||||
val skipPreMigration by unsortedPreferences.skipPreMigration().collectAsState()
|
|
||||||
val migrationSources by unsortedPreferences.migrationSources().collectAsState()
|
|
||||||
return Preference.PreferenceGroup(
|
|
||||||
stringResource(SYMR.strings.migration),
|
|
||||||
enabled = skipPreMigration || migrationSources.isNotEmpty(),
|
|
||||||
preferenceItems = persistentListOf(
|
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
|
||||||
pref = unsortedPreferences.skipPreMigration(),
|
|
||||||
title = stringResource(SYMR.strings.skip_pre_migration),
|
|
||||||
subtitle = stringResource(SYMR.strings.pref_skip_pre_migration_summary),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// SY <--
|
// SY <--
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.outlined.ChromeReaderMode
|
import androidx.compose.material.icons.automirrored.outlined.ChromeReaderMode
|
||||||
import androidx.compose.material.icons.outlined.ChromeReaderMode
|
|
||||||
import androidx.compose.material.icons.outlined.Code
|
import androidx.compose.material.icons.outlined.Code
|
||||||
import androidx.compose.material.icons.outlined.CollectionsBookmark
|
import androidx.compose.material.icons.outlined.CollectionsBookmark
|
||||||
import androidx.compose.material.icons.outlined.Explore
|
import androidx.compose.material.icons.outlined.Explore
|
||||||
|
|||||||
+6
-9
@@ -44,7 +44,6 @@ import logcat.LogPriority
|
|||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
import tachiyomi.core.common.util.lang.withUIContext
|
import tachiyomi.core.common.util.lang.withUIContext
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.domain.UnsortedPreferences
|
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.i18n.sy.SYMR
|
import tachiyomi.i18n.sy.SYMR
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
@@ -65,14 +64,13 @@ object SettingsMangadexScreen : SearchableSettings {
|
|||||||
@Composable
|
@Composable
|
||||||
override fun getPreferences(): List<Preference> {
|
override fun getPreferences(): List<Preference> {
|
||||||
val sourcePreferences: SourcePreferences = remember { Injekt.get() }
|
val sourcePreferences: SourcePreferences = remember { Injekt.get() }
|
||||||
val unsortedPreferences: UnsortedPreferences = remember { Injekt.get() }
|
|
||||||
val trackPreferences: TrackPreferences = remember { Injekt.get() }
|
val trackPreferences: TrackPreferences = remember { Injekt.get() }
|
||||||
val mdex = remember { MdUtil.getEnabledMangaDex(unsortedPreferences, sourcePreferences) } ?: return emptyList()
|
val mdex = remember { MdUtil.getEnabledMangaDex(sourcePreferences) } ?: return emptyList()
|
||||||
|
|
||||||
return listOf(
|
return listOf(
|
||||||
loginPreference(mdex, trackPreferences),
|
loginPreference(mdex, trackPreferences),
|
||||||
preferredMangaDexId(unsortedPreferences, sourcePreferences),
|
preferredMangaDexId(sourcePreferences),
|
||||||
syncMangaDexIntoThis(unsortedPreferences),
|
syncMangaDexIntoThis(sourcePreferences),
|
||||||
syncLibraryToMangaDex(),
|
syncLibraryToMangaDex(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -174,11 +172,10 @@ object SettingsMangadexScreen : SearchableSettings {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun preferredMangaDexId(
|
fun preferredMangaDexId(
|
||||||
unsortedPreferences: UnsortedPreferences,
|
|
||||||
sourcePreferences: SourcePreferences,
|
sourcePreferences: SourcePreferences,
|
||||||
): Preference.PreferenceItem.ListPreference<String> {
|
): Preference.PreferenceItem.ListPreference<String> {
|
||||||
return Preference.PreferenceItem.ListPreference(
|
return Preference.PreferenceItem.ListPreference(
|
||||||
pref = unsortedPreferences.preferredMangaDexId(),
|
preference = sourcePreferences.preferredMangaDexId(),
|
||||||
title = stringResource(SYMR.strings.mangadex_preffered_source),
|
title = stringResource(SYMR.strings.mangadex_preffered_source),
|
||||||
subtitle = stringResource(SYMR.strings.mangadex_preffered_source_summary),
|
subtitle = stringResource(SYMR.strings.mangadex_preffered_source_summary),
|
||||||
entries = MdUtil.getEnabledMangaDexs(sourcePreferences)
|
entries = MdUtil.getEnabledMangaDexs(sourcePreferences)
|
||||||
@@ -250,7 +247,7 @@ object SettingsMangadexScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun syncMangaDexIntoThis(unsortedPreferences: UnsortedPreferences): Preference.PreferenceItem.TextPreference {
|
fun syncMangaDexIntoThis(sourcePreferences: SourcePreferences): Preference.PreferenceItem.TextPreference {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
var dialogOpen by remember { mutableStateOf(false) }
|
var dialogOpen by remember { mutableStateOf(false) }
|
||||||
if (dialogOpen) {
|
if (dialogOpen) {
|
||||||
@@ -258,7 +255,7 @@ object SettingsMangadexScreen : SearchableSettings {
|
|||||||
onDismissRequest = { dialogOpen = false },
|
onDismissRequest = { dialogOpen = false },
|
||||||
onSelectionConfirmed = { items ->
|
onSelectionConfirmed = { items ->
|
||||||
dialogOpen = false
|
dialogOpen = false
|
||||||
unsortedPreferences.mangadexSyncToLibraryIndexes().set(
|
sourcePreferences.mangadexSyncToLibraryIndexes().set(
|
||||||
List(items.size) { index -> (index + 1).toString() }.toSet(),
|
List(items.size) { index -> (index + 1).toString() }.toSet(),
|
||||||
)
|
)
|
||||||
LibraryUpdateJob.startNow(
|
LibraryUpdateJob.startNow(
|
||||||
|
|||||||
+87
-106
@@ -1,6 +1,5 @@
|
|||||||
package eu.kanade.presentation.more.settings.screen
|
package eu.kanade.presentation.more.settings.screen
|
||||||
|
|
||||||
import android.os.Build
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.ReadOnlyComposable
|
import androidx.compose.runtime.ReadOnlyComposable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -12,6 +11,7 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
|
|||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
|
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig
|
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig
|
||||||
|
import eu.kanade.tachiyomi.util.system.hasDisplayCutout
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.persistentMapOf
|
import kotlinx.collections.immutable.persistentMapOf
|
||||||
import kotlinx.collections.immutable.toImmutableMap
|
import kotlinx.collections.immutable.toImmutableMap
|
||||||
@@ -39,45 +39,45 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
|
|
||||||
return listOf(
|
return listOf(
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = readerPref.defaultReadingMode(),
|
preference = readerPref.defaultReadingMode(),
|
||||||
title = stringResource(MR.strings.pref_viewer_type),
|
|
||||||
entries = ReadingMode.entries.drop(1)
|
entries = ReadingMode.entries.drop(1)
|
||||||
.associate { it.flagValue to stringResource(it.stringRes) }
|
.associate { it.flagValue to stringResource(it.stringRes) }
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.pref_viewer_type),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = readerPref.doubleTapAnimSpeed(),
|
preference = readerPref.doubleTapAnimSpeed(),
|
||||||
title = stringResource(MR.strings.pref_double_tap_anim_speed),
|
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
1 to stringResource(MR.strings.double_tap_anim_speed_0),
|
1 to stringResource(MR.strings.double_tap_anim_speed_0),
|
||||||
500 to stringResource(MR.strings.double_tap_anim_speed_normal),
|
500 to stringResource(MR.strings.double_tap_anim_speed_normal),
|
||||||
250 to stringResource(MR.strings.double_tap_anim_speed_fast),
|
250 to stringResource(MR.strings.double_tap_anim_speed_fast),
|
||||||
),
|
),
|
||||||
|
title = stringResource(MR.strings.pref_double_tap_anim_speed),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPref.showReadingMode(),
|
preference = readerPref.showReadingMode(),
|
||||||
title = stringResource(MR.strings.pref_show_reading_mode),
|
title = stringResource(MR.strings.pref_show_reading_mode),
|
||||||
subtitle = stringResource(MR.strings.pref_show_reading_mode_summary),
|
subtitle = stringResource(MR.strings.pref_show_reading_mode_summary),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPref.showNavigationOverlayOnStart(),
|
preference = readerPref.showNavigationOverlayOnStart(),
|
||||||
title = stringResource(MR.strings.pref_show_navigation_mode),
|
title = stringResource(MR.strings.pref_show_navigation_mode),
|
||||||
subtitle = stringResource(MR.strings.pref_show_navigation_mode_summary),
|
subtitle = stringResource(MR.strings.pref_show_navigation_mode_summary),
|
||||||
),
|
),
|
||||||
// SY -->
|
// SY -->
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPref.forceHorizontalSeekbar(),
|
preference = readerPref.forceHorizontalSeekbar(),
|
||||||
title = stringResource(SYMR.strings.pref_force_horz_seekbar),
|
title = stringResource(SYMR.strings.pref_force_horz_seekbar),
|
||||||
subtitle = stringResource(SYMR.strings.pref_force_horz_seekbar_summary),
|
subtitle = stringResource(SYMR.strings.pref_force_horz_seekbar_summary),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPref.landscapeVerticalSeekbar(),
|
preference = readerPref.landscapeVerticalSeekbar(),
|
||||||
title = stringResource(SYMR.strings.pref_show_vert_seekbar_landscape),
|
title = stringResource(SYMR.strings.pref_show_vert_seekbar_landscape),
|
||||||
subtitle = stringResource(SYMR.strings.pref_show_vert_seekbar_landscape_summary),
|
subtitle = stringResource(SYMR.strings.pref_show_vert_seekbar_landscape_summary),
|
||||||
enabled = !forceHorizontalSeekbar,
|
enabled = !forceHorizontalSeekbar,
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPref.leftVerticalSeekbar(),
|
preference = readerPref.leftVerticalSeekbar(),
|
||||||
title = stringResource(SYMR.strings.pref_left_handed_vertical_seekbar),
|
title = stringResource(SYMR.strings.pref_left_handed_vertical_seekbar),
|
||||||
subtitle = stringResource(SYMR.strings.pref_left_handed_vertical_seekbar_summary),
|
subtitle = stringResource(SYMR.strings.pref_left_handed_vertical_seekbar_summary),
|
||||||
enabled = !forceHorizontalSeekbar,
|
enabled = !forceHorizontalSeekbar,
|
||||||
@@ -85,7 +85,7 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
// SY <--
|
// SY <--
|
||||||
/* SY -->
|
/* SY -->
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPref.pageTransitions(),
|
preference = readerPref.pageTransitions(),
|
||||||
title = stringResource(MR.strings.pref_page_transitions),
|
title = stringResource(MR.strings.pref_page_transitions),
|
||||||
),
|
),
|
||||||
SY <-- */
|
SY <-- */
|
||||||
@@ -114,39 +114,37 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.pref_category_display),
|
title = stringResource(MR.strings.pref_category_display),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = readerPreferences.defaultOrientationType(),
|
preference = readerPreferences.defaultOrientationType(),
|
||||||
title = stringResource(MR.strings.pref_rotation_type),
|
|
||||||
entries = ReaderOrientation.entries.drop(1)
|
entries = ReaderOrientation.entries.drop(1)
|
||||||
.associate { it.flagValue to stringResource(it.stringRes) }
|
.associate { it.flagValue to stringResource(it.stringRes) }
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.pref_rotation_type),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = readerPreferences.readerTheme(),
|
preference = readerPreferences.readerTheme(),
|
||||||
title = stringResource(MR.strings.pref_reader_theme),
|
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
1 to stringResource(MR.strings.black_background),
|
1 to stringResource(MR.strings.black_background),
|
||||||
2 to stringResource(MR.strings.gray_background),
|
2 to stringResource(MR.strings.gray_background),
|
||||||
0 to stringResource(MR.strings.white_background),
|
0 to stringResource(MR.strings.white_background),
|
||||||
3 to stringResource(MR.strings.automatic_background),
|
3 to stringResource(MR.strings.automatic_background),
|
||||||
),
|
),
|
||||||
|
title = stringResource(MR.strings.pref_reader_theme),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = fullscreenPref,
|
preference = fullscreenPref,
|
||||||
title = stringResource(MR.strings.pref_fullscreen),
|
title = stringResource(MR.strings.pref_fullscreen),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.cutoutShort(),
|
preference = readerPreferences.drawUnderCutout(),
|
||||||
title = stringResource(MR.strings.pref_cutout_short),
|
title = stringResource(MR.strings.pref_cutout_short),
|
||||||
enabled = fullscreen &&
|
enabled = LocalView.current.hasDisplayCutout() && fullscreen,
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.P &&
|
|
||||||
LocalView.current.rootWindowInsets?.displayCutout != null, // has cutout
|
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.keepScreenOn(),
|
preference = readerPreferences.keepScreenOn(),
|
||||||
title = stringResource(MR.strings.pref_keep_screen_on),
|
title = stringResource(MR.strings.pref_keep_screen_on),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.showPageNumber(),
|
preference = readerPreferences.showPageNumber(),
|
||||||
title = stringResource(MR.strings.pref_show_page_number),
|
title = stringResource(MR.strings.pref_show_page_number),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -169,43 +167,35 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
title = "E-Ink",
|
title = "E-Ink",
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.flashOnPageChange(),
|
preference = readerPreferences.flashOnPageChange(),
|
||||||
title = stringResource(MR.strings.pref_flash_page),
|
title = stringResource(MR.strings.pref_flash_page),
|
||||||
subtitle = stringResource(MR.strings.pref_flash_page_summ),
|
subtitle = stringResource(MR.strings.pref_flash_page_summ),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SliderPreference(
|
Preference.PreferenceItem.SliderPreference(
|
||||||
value = flashMillis / ReaderPreferences.MILLI_CONVERSION,
|
value = flashMillis / ReaderPreferences.MILLI_CONVERSION,
|
||||||
min = 1,
|
valueRange = 1..15,
|
||||||
max = 15,
|
|
||||||
title = stringResource(MR.strings.pref_flash_duration),
|
title = stringResource(MR.strings.pref_flash_duration),
|
||||||
subtitle = stringResource(MR.strings.pref_flash_duration_summary, flashMillis),
|
valueString = stringResource(MR.strings.pref_flash_duration_summary, flashMillis),
|
||||||
onValueChanged = {
|
|
||||||
flashMillisPref.set(it * ReaderPreferences.MILLI_CONVERSION)
|
|
||||||
true
|
|
||||||
},
|
|
||||||
enabled = flashPageState,
|
enabled = flashPageState,
|
||||||
|
onValueChanged = { flashMillisPref.set(it * ReaderPreferences.MILLI_CONVERSION) },
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SliderPreference(
|
Preference.PreferenceItem.SliderPreference(
|
||||||
value = flashInterval,
|
value = flashInterval,
|
||||||
min = 1,
|
valueRange = 1..10,
|
||||||
max = 10,
|
|
||||||
title = stringResource(MR.strings.pref_flash_page_interval),
|
title = stringResource(MR.strings.pref_flash_page_interval),
|
||||||
subtitle = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval),
|
valueString = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval),
|
||||||
onValueChanged = {
|
|
||||||
flashIntervalPref.set(it)
|
|
||||||
true
|
|
||||||
},
|
|
||||||
enabled = flashPageState,
|
enabled = flashPageState,
|
||||||
|
onValueChanged = { flashIntervalPref.set(it) },
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = flashColorPref,
|
preference = flashColorPref,
|
||||||
title = stringResource(MR.strings.pref_flash_with),
|
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
ReaderPreferences.FlashColor.BLACK to stringResource(MR.strings.pref_flash_style_black),
|
ReaderPreferences.FlashColor.BLACK to stringResource(MR.strings.pref_flash_style_black),
|
||||||
ReaderPreferences.FlashColor.WHITE to stringResource(MR.strings.pref_flash_style_white),
|
ReaderPreferences.FlashColor.WHITE to stringResource(MR.strings.pref_flash_style_white),
|
||||||
ReaderPreferences.FlashColor.WHITE_BLACK
|
ReaderPreferences.FlashColor.WHITE_BLACK
|
||||||
to stringResource(MR.strings.pref_flash_style_white_black),
|
to stringResource(MR.strings.pref_flash_style_white_black),
|
||||||
),
|
),
|
||||||
|
title = stringResource(MR.strings.pref_flash_with),
|
||||||
enabled = flashPageState,
|
enabled = flashPageState,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -218,26 +208,19 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.pref_category_reading),
|
title = stringResource(MR.strings.pref_category_reading),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.skipRead(),
|
preference = readerPreferences.skipRead(),
|
||||||
title = stringResource(MR.strings.pref_skip_read_chapters),
|
title = stringResource(MR.strings.pref_skip_read_chapters),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.skipFiltered(),
|
preference = readerPreferences.skipFiltered(),
|
||||||
title = stringResource(MR.strings.pref_skip_filtered_chapters),
|
title = stringResource(MR.strings.pref_skip_filtered_chapters),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.skipDupe(),
|
preference = readerPreferences.skipDupe(),
|
||||||
title = stringResource(MR.strings.pref_skip_dupe_chapters),
|
title = stringResource(MR.strings.pref_skip_dupe_chapters),
|
||||||
),
|
),
|
||||||
// SY -->
|
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.markReadDupe(),
|
preference = readerPreferences.alwaysShowChapterTransition(),
|
||||||
title = stringResource(SYMR.strings.pref_mark_read_dupe_chapters),
|
|
||||||
subtitle = stringResource(SYMR.strings.pref_mark_read_dupe_chapters_summary),
|
|
||||||
),
|
|
||||||
// SY <--
|
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
|
||||||
pref = readerPreferences.alwaysShowChapterTransition(),
|
|
||||||
title = stringResource(MR.strings.pref_always_show_chapter_transition),
|
title = stringResource(MR.strings.pref_always_show_chapter_transition),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -260,16 +243,15 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.pager_viewer),
|
title = stringResource(MR.strings.pager_viewer),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = navModePref,
|
preference = navModePref,
|
||||||
title = stringResource(MR.strings.pref_viewer_nav),
|
|
||||||
entries = ReaderPreferences.TapZones
|
entries = ReaderPreferences.TapZones
|
||||||
.mapIndexed { index, it -> index to stringResource(it) }
|
.mapIndexed { index, it -> index to stringResource(it) }
|
||||||
.toMap()
|
.toMap()
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.pref_viewer_nav),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = readerPreferences.pagerNavInverted(),
|
preference = readerPreferences.pagerNavInverted(),
|
||||||
title = stringResource(MR.strings.pref_read_with_tapping_inverted),
|
|
||||||
entries = persistentListOf(
|
entries = persistentListOf(
|
||||||
ReaderPreferences.TappingInvertMode.NONE,
|
ReaderPreferences.TappingInvertMode.NONE,
|
||||||
ReaderPreferences.TappingInvertMode.HORIZONTAL,
|
ReaderPreferences.TappingInvertMode.HORIZONTAL,
|
||||||
@@ -278,46 +260,47 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
)
|
)
|
||||||
.associateWith { stringResource(it.titleRes) }
|
.associateWith { stringResource(it.titleRes) }
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.pref_read_with_tapping_inverted),
|
||||||
enabled = navMode != 5,
|
enabled = navMode != 5,
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = imageScaleTypePref,
|
preference = imageScaleTypePref,
|
||||||
title = stringResource(MR.strings.pref_image_scale_type),
|
|
||||||
entries = ReaderPreferences.ImageScaleType
|
entries = ReaderPreferences.ImageScaleType
|
||||||
.mapIndexed { index, it -> index + 1 to stringResource(it) }
|
.mapIndexed { index, it -> index + 1 to stringResource(it) }
|
||||||
.toMap()
|
.toMap()
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.pref_image_scale_type),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = readerPreferences.zoomStart(),
|
preference = readerPreferences.zoomStart(),
|
||||||
title = stringResource(MR.strings.pref_zoom_start),
|
|
||||||
entries = ReaderPreferences.ZoomStart
|
entries = ReaderPreferences.ZoomStart
|
||||||
.mapIndexed { index, it -> index + 1 to stringResource(it) }
|
.mapIndexed { index, it -> index + 1 to stringResource(it) }
|
||||||
.toMap()
|
.toMap()
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.pref_zoom_start),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.cropBorders(),
|
preference = readerPreferences.cropBorders(),
|
||||||
title = stringResource(MR.strings.pref_crop_borders),
|
title = stringResource(MR.strings.pref_crop_borders),
|
||||||
),
|
),
|
||||||
// SY -->
|
// SY -->
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.pageTransitionsPager(),
|
preference = readerPreferences.pageTransitionsPager(),
|
||||||
title = stringResource(MR.strings.pref_page_transitions),
|
title = stringResource(MR.strings.pref_page_transitions),
|
||||||
),
|
),
|
||||||
// SY <--
|
// SY <--
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.landscapeZoom(),
|
preference = readerPreferences.landscapeZoom(),
|
||||||
title = stringResource(MR.strings.pref_landscape_zoom),
|
title = stringResource(MR.strings.pref_landscape_zoom),
|
||||||
enabled = imageScaleType == 1,
|
enabled = imageScaleType == 1,
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.navigateToPan(),
|
preference = readerPreferences.navigateToPan(),
|
||||||
title = stringResource(MR.strings.pref_navigate_pan),
|
title = stringResource(MR.strings.pref_navigate_pan),
|
||||||
enabled = navMode != 5,
|
enabled = navMode != 5,
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = dualPageSplitPref,
|
preference = dualPageSplitPref,
|
||||||
title = stringResource(MR.strings.pref_dual_page_split),
|
title = stringResource(MR.strings.pref_dual_page_split),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
rotateToFitPref.set(false)
|
rotateToFitPref.set(false)
|
||||||
@@ -325,13 +308,13 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.dualPageInvertPaged(),
|
preference = readerPreferences.dualPageInvertPaged(),
|
||||||
title = stringResource(MR.strings.pref_dual_page_invert),
|
title = stringResource(MR.strings.pref_dual_page_invert),
|
||||||
subtitle = stringResource(MR.strings.pref_dual_page_invert_summary),
|
subtitle = stringResource(MR.strings.pref_dual_page_invert_summary),
|
||||||
enabled = dualPageSplit,
|
enabled = dualPageSplit,
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = rotateToFitPref,
|
preference = rotateToFitPref,
|
||||||
title = stringResource(MR.strings.pref_page_rotate),
|
title = stringResource(MR.strings.pref_page_rotate),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
dualPageSplitPref.set(false)
|
dualPageSplitPref.set(false)
|
||||||
@@ -339,7 +322,7 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.dualPageRotateToFitInvert(),
|
preference = readerPreferences.dualPageRotateToFitInvert(),
|
||||||
title = stringResource(MR.strings.pref_page_rotate_invert),
|
title = stringResource(MR.strings.pref_page_rotate_invert),
|
||||||
enabled = rotateToFit,
|
enabled = rotateToFit,
|
||||||
),
|
),
|
||||||
@@ -365,16 +348,15 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.webtoon_viewer),
|
title = stringResource(MR.strings.webtoon_viewer),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = navModePref,
|
preference = navModePref,
|
||||||
title = stringResource(MR.strings.pref_viewer_nav),
|
|
||||||
entries = ReaderPreferences.TapZones
|
entries = ReaderPreferences.TapZones
|
||||||
.mapIndexed { index, it -> index to stringResource(it) }
|
.mapIndexed { index, it -> index to stringResource(it) }
|
||||||
.toMap()
|
.toMap()
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.pref_viewer_nav),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = readerPreferences.webtoonNavInverted(),
|
preference = readerPreferences.webtoonNavInverted(),
|
||||||
title = stringResource(MR.strings.pref_read_with_tapping_inverted),
|
|
||||||
entries = persistentListOf(
|
entries = persistentListOf(
|
||||||
ReaderPreferences.TappingInvertMode.NONE,
|
ReaderPreferences.TappingInvertMode.NONE,
|
||||||
ReaderPreferences.TappingInvertMode.HORIZONTAL,
|
ReaderPreferences.TappingInvertMode.HORIZONTAL,
|
||||||
@@ -383,35 +365,34 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
)
|
)
|
||||||
.associateWith { stringResource(it.titleRes) }
|
.associateWith { stringResource(it.titleRes) }
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.pref_read_with_tapping_inverted),
|
||||||
enabled = navMode != 5,
|
enabled = navMode != 5,
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SliderPreference(
|
Preference.PreferenceItem.SliderPreference(
|
||||||
value = webtoonSidePadding,
|
value = webtoonSidePadding,
|
||||||
title = stringResource(MR.strings.pref_webtoon_side_padding),
|
valueRange = ReaderPreferences.let {
|
||||||
subtitle = numberFormat.format(webtoonSidePadding / 100f),
|
it.WEBTOON_PADDING_MIN..it.WEBTOON_PADDING_MAX
|
||||||
min = ReaderPreferences.WEBTOON_PADDING_MIN,
|
|
||||||
max = ReaderPreferences.WEBTOON_PADDING_MAX,
|
|
||||||
onValueChanged = {
|
|
||||||
webtoonSidePaddingPref.set(it)
|
|
||||||
true
|
|
||||||
},
|
},
|
||||||
|
title = stringResource(MR.strings.pref_webtoon_side_padding),
|
||||||
|
valueString = numberFormat.format(webtoonSidePadding / 100f),
|
||||||
|
onValueChanged = { webtoonSidePaddingPref.set(it) },
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = readerPreferences.readerHideThreshold(),
|
preference = readerPreferences.readerHideThreshold(),
|
||||||
title = stringResource(MR.strings.pref_hide_threshold),
|
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
ReaderPreferences.ReaderHideThreshold.HIGHEST to stringResource(MR.strings.pref_highest),
|
ReaderPreferences.ReaderHideThreshold.HIGHEST to stringResource(MR.strings.pref_highest),
|
||||||
ReaderPreferences.ReaderHideThreshold.HIGH to stringResource(MR.strings.pref_high),
|
ReaderPreferences.ReaderHideThreshold.HIGH to stringResource(MR.strings.pref_high),
|
||||||
ReaderPreferences.ReaderHideThreshold.LOW to stringResource(MR.strings.pref_low),
|
ReaderPreferences.ReaderHideThreshold.LOW to stringResource(MR.strings.pref_low),
|
||||||
ReaderPreferences.ReaderHideThreshold.LOWEST to stringResource(MR.strings.pref_lowest),
|
ReaderPreferences.ReaderHideThreshold.LOWEST to stringResource(MR.strings.pref_lowest),
|
||||||
),
|
),
|
||||||
|
title = stringResource(MR.strings.pref_hide_threshold),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.cropBordersWebtoon(),
|
preference = readerPreferences.cropBordersWebtoon(),
|
||||||
title = stringResource(MR.strings.pref_crop_borders),
|
title = stringResource(MR.strings.pref_crop_borders),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = dualPageSplitPref,
|
preference = dualPageSplitPref,
|
||||||
title = stringResource(MR.strings.pref_dual_page_split),
|
title = stringResource(MR.strings.pref_dual_page_split),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
rotateToFitPref.set(false)
|
rotateToFitPref.set(false)
|
||||||
@@ -419,13 +400,13 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.dualPageInvertWebtoon(),
|
preference = readerPreferences.dualPageInvertWebtoon(),
|
||||||
title = stringResource(MR.strings.pref_dual_page_invert),
|
title = stringResource(MR.strings.pref_dual_page_invert),
|
||||||
subtitle = stringResource(MR.strings.pref_dual_page_invert_summary),
|
subtitle = stringResource(MR.strings.pref_dual_page_invert_summary),
|
||||||
enabled = dualPageSplit,
|
enabled = dualPageSplit,
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = rotateToFitPref,
|
preference = rotateToFitPref,
|
||||||
title = stringResource(MR.strings.pref_page_rotate),
|
title = stringResource(MR.strings.pref_page_rotate),
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
dualPageSplitPref.set(false)
|
dualPageSplitPref.set(false)
|
||||||
@@ -433,21 +414,21 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.dualPageRotateToFitInvertWebtoon(),
|
preference = readerPreferences.dualPageRotateToFitInvertWebtoon(),
|
||||||
title = stringResource(MR.strings.pref_page_rotate_invert),
|
title = stringResource(MR.strings.pref_page_rotate_invert),
|
||||||
enabled = rotateToFit,
|
enabled = rotateToFit,
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.webtoonDoubleTapZoomEnabled(),
|
preference = readerPreferences.webtoonDoubleTapZoomEnabled(),
|
||||||
title = stringResource(MR.strings.pref_double_tap_zoom),
|
title = stringResource(MR.strings.pref_double_tap_zoom),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.webtoonDisableZoomOut(),
|
preference = readerPreferences.webtoonDisableZoomOut(),
|
||||||
title = stringResource(MR.strings.pref_webtoon_disable_zoom_out),
|
title = stringResource(MR.strings.pref_webtoon_disable_zoom_out),
|
||||||
),
|
),
|
||||||
// SY -->
|
// SY -->
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.pageTransitionsWebtoon(),
|
preference = readerPreferences.pageTransitionsWebtoon(),
|
||||||
title = stringResource(MR.strings.pref_page_transitions),
|
title = stringResource(MR.strings.pref_page_transitions),
|
||||||
),
|
),
|
||||||
// SY <--
|
// SY <--
|
||||||
@@ -462,12 +443,12 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.vertical_plus_viewer),
|
title = stringResource(MR.strings.vertical_plus_viewer),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.continuousVerticalTappingByPage(),
|
preference = readerPreferences.continuousVerticalTappingByPage(),
|
||||||
title = stringResource(SYMR.strings.tap_scroll_page),
|
title = stringResource(SYMR.strings.tap_scroll_page),
|
||||||
subtitle = stringResource(SYMR.strings.tap_scroll_page_summary),
|
subtitle = stringResource(SYMR.strings.tap_scroll_page_summary),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.cropBordersContinuousVertical(),
|
preference = readerPreferences.cropBordersContinuousVertical(),
|
||||||
title = stringResource(MR.strings.pref_crop_borders),
|
title = stringResource(MR.strings.pref_crop_borders),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -483,11 +464,11 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.pref_reader_navigation),
|
title = stringResource(MR.strings.pref_reader_navigation),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readWithVolumeKeysPref,
|
preference = readWithVolumeKeysPref,
|
||||||
title = stringResource(MR.strings.pref_read_with_volume_keys),
|
title = stringResource(MR.strings.pref_read_with_volume_keys),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.readWithVolumeKeysInverted(),
|
preference = readerPreferences.readWithVolumeKeysInverted(),
|
||||||
title = stringResource(MR.strings.pref_read_with_volume_keys_inverted),
|
title = stringResource(MR.strings.pref_read_with_volume_keys_inverted),
|
||||||
enabled = readWithVolumeKeys,
|
enabled = readWithVolumeKeys,
|
||||||
),
|
),
|
||||||
@@ -501,11 +482,11 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.pref_reader_actions),
|
title = stringResource(MR.strings.pref_reader_actions),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.readWithLongTap(),
|
preference = readerPreferences.readWithLongTap(),
|
||||||
title = stringResource(MR.strings.pref_read_with_long_tap),
|
title = stringResource(MR.strings.pref_read_with_long_tap),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.folderPerManga(),
|
preference = readerPreferences.folderPerManga(),
|
||||||
title = stringResource(MR.strings.pref_create_folder_per_manga),
|
title = stringResource(MR.strings.pref_create_folder_per_manga),
|
||||||
subtitle = stringResource(MR.strings.pref_create_folder_per_manga_summary),
|
subtitle = stringResource(MR.strings.pref_create_folder_per_manga_summary),
|
||||||
),
|
),
|
||||||
@@ -520,7 +501,7 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
title = stringResource(SYMR.strings.page_downloading),
|
title = stringResource(SYMR.strings.page_downloading),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = readerPreferences.preloadSize(),
|
preference = readerPreferences.preloadSize(),
|
||||||
title = stringResource(SYMR.strings.reader_preload_amount),
|
title = stringResource(SYMR.strings.reader_preload_amount),
|
||||||
subtitle = stringResource(SYMR.strings.reader_preload_amount_summary),
|
subtitle = stringResource(SYMR.strings.reader_preload_amount_summary),
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
@@ -535,13 +516,13 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = readerPreferences.readerThreads(),
|
preference = readerPreferences.readerThreads(),
|
||||||
title = stringResource(SYMR.strings.download_threads),
|
title = stringResource(SYMR.strings.download_threads),
|
||||||
subtitle = stringResource(SYMR.strings.download_threads_summary),
|
subtitle = stringResource(SYMR.strings.download_threads_summary),
|
||||||
entries = List(5) { it }.associateWith { it.toString() }.toImmutableMap(),
|
entries = List(5) { it }.associateWith { it.toString() }.toImmutableMap(),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = readerPreferences.cacheSize(),
|
preference = readerPreferences.cacheSize(),
|
||||||
title = stringResource(SYMR.strings.reader_cache_size),
|
title = stringResource(SYMR.strings.reader_cache_size),
|
||||||
subtitle = stringResource(SYMR.strings.reader_cache_size_summary),
|
subtitle = stringResource(SYMR.strings.reader_cache_size_summary),
|
||||||
entries = persistentMapOf(
|
entries = persistentMapOf(
|
||||||
@@ -564,7 +545,7 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.aggressivePageLoading(),
|
preference = readerPreferences.aggressivePageLoading(),
|
||||||
title = stringResource(SYMR.strings.aggressively_load_pages),
|
title = stringResource(SYMR.strings.aggressively_load_pages),
|
||||||
subtitle = stringResource(SYMR.strings.aggressively_load_pages_summary),
|
subtitle = stringResource(SYMR.strings.aggressively_load_pages_summary),
|
||||||
),
|
),
|
||||||
@@ -579,21 +560,21 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
title = stringResource(SYMR.strings.pref_category_fork),
|
title = stringResource(SYMR.strings.pref_category_fork),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.readerInstantRetry(),
|
preference = readerPreferences.readerInstantRetry(),
|
||||||
title = stringResource(SYMR.strings.skip_queue_on_retry),
|
title = stringResource(SYMR.strings.skip_queue_on_retry),
|
||||||
subtitle = stringResource(SYMR.strings.skip_queue_on_retry_summary),
|
subtitle = stringResource(SYMR.strings.skip_queue_on_retry_summary),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.preserveReadingPosition(),
|
preference = readerPreferences.preserveReadingPosition(),
|
||||||
title = stringResource(SYMR.strings.preserve_reading_position),
|
title = stringResource(SYMR.strings.preserve_reading_position),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.useAutoWebtoon(),
|
preference = readerPreferences.useAutoWebtoon(),
|
||||||
title = stringResource(SYMR.strings.auto_webtoon_mode),
|
title = stringResource(SYMR.strings.auto_webtoon_mode),
|
||||||
subtitle = stringResource(SYMR.strings.auto_webtoon_mode_summary),
|
subtitle = stringResource(SYMR.strings.auto_webtoon_mode_summary),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.MultiSelectListPreference(
|
Preference.PreferenceItem.MultiSelectListPreference(
|
||||||
pref = readerPreferences.readerBottomButtons(),
|
preference = readerPreferences.readerBottomButtons(),
|
||||||
title = stringResource(SYMR.strings.reader_bottom_buttons),
|
title = stringResource(SYMR.strings.reader_bottom_buttons),
|
||||||
subtitle = stringResource(SYMR.strings.reader_bottom_buttons_summary),
|
subtitle = stringResource(SYMR.strings.reader_bottom_buttons_summary),
|
||||||
entries = ReaderBottomButton.entries
|
entries = ReaderBottomButton.entries
|
||||||
@@ -601,21 +582,21 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = readerPreferences.pageLayout(),
|
preference = readerPreferences.pageLayout(),
|
||||||
title = stringResource(SYMR.strings.page_layout),
|
title = stringResource(SYMR.strings.page_layout),
|
||||||
subtitle = stringResource(SYMR.strings.automatic_can_still_switch),
|
subtitle = stringResource(SYMR.strings.automatic_can_still_switch),
|
||||||
entries = ReaderPreferences.PageLayouts
|
entries = ReaderPreferences.PageLayouts
|
||||||
.mapIndexed { index, it -> index + 1 to stringResource(it) }
|
.mapIndexed { index, it -> index to stringResource(it) }
|
||||||
.toMap()
|
.toMap()
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = readerPreferences.invertDoublePages(),
|
preference = readerPreferences.invertDoublePages(),
|
||||||
title = stringResource(SYMR.strings.invert_double_pages),
|
title = stringResource(SYMR.strings.invert_double_pages),
|
||||||
enabled = pageLayout != PagerConfig.PageLayout.SINGLE_PAGE,
|
enabled = pageLayout != PagerConfig.PageLayout.SINGLE_PAGE,
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = readerPreferences.centerMarginType(),
|
preference = readerPreferences.centerMarginType(),
|
||||||
title = stringResource(SYMR.strings.center_margin),
|
title = stringResource(SYMR.strings.center_margin),
|
||||||
subtitle = stringResource(SYMR.strings.pref_center_margin_summary),
|
subtitle = stringResource(SYMR.strings.pref_center_margin_summary),
|
||||||
entries = ReaderPreferences.CenterMarginTypes
|
entries = ReaderPreferences.CenterMarginTypes
|
||||||
@@ -624,7 +605,7 @@ object SettingsReaderScreen : SearchableSettings {
|
|||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = readerPreferences.archiveReaderMode(),
|
preference = readerPreferences.archiveReaderMode(),
|
||||||
title = stringResource(SYMR.strings.pref_archive_reader_mode),
|
title = stringResource(SYMR.strings.pref_archive_reader_mode),
|
||||||
subtitle = stringResource(SYMR.strings.pref_archive_reader_mode_summary),
|
subtitle = stringResource(SYMR.strings.pref_archive_reader_mode_summary),
|
||||||
entries = ReaderPreferences.archiveModeTypes
|
entries = ReaderPreferences.archiveModeTypes
|
||||||
|
|||||||
+1
-1
@@ -183,7 +183,7 @@ private fun SearchResult(
|
|||||||
emptySequence()
|
emptySequence()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Preference.PreferenceItem<*> -> sequenceOf(null to p)
|
is Preference.PreferenceItem<*, *> -> sequenceOf(null to p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Don't show info preference
|
// Don't show info preference
|
||||||
|
|||||||
+11
-11
@@ -96,7 +96,7 @@ object SettingsSecurityScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.pref_security),
|
title = stringResource(MR.strings.pref_security),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = useAuthPref,
|
preference = useAuthPref,
|
||||||
title = stringResource(MR.strings.lock_with_biometrics),
|
title = stringResource(MR.strings.lock_with_biometrics),
|
||||||
enabled = authSupported,
|
enabled = authSupported,
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
@@ -106,9 +106,7 @@ object SettingsSecurityScreen : SearchableSettings {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = securityPreferences.lockAppAfter(),
|
preference = securityPreferences.lockAppAfter(),
|
||||||
title = stringResource(MR.strings.lock_when_idle),
|
|
||||||
enabled = authSupported && useAuth,
|
|
||||||
entries = LockAfterValues
|
entries = LockAfterValues
|
||||||
.associateWith {
|
.associateWith {
|
||||||
when (it) {
|
when (it) {
|
||||||
@@ -118,6 +116,8 @@ object SettingsSecurityScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.lock_when_idle),
|
||||||
|
enabled = authSupported && useAuth,
|
||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
(context as FragmentActivity).authenticate(
|
(context as FragmentActivity).authenticate(
|
||||||
title = context.stringResource(MR.strings.lock_when_idle),
|
title = context.stringResource(MR.strings.lock_when_idle),
|
||||||
@@ -125,25 +125,25 @@ object SettingsSecurityScreen : SearchableSettings {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = securityPreferences.hideNotificationContent(),
|
preference = securityPreferences.hideNotificationContent(),
|
||||||
title = stringResource(MR.strings.hide_notification_content),
|
title = stringResource(MR.strings.hide_notification_content),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = securityPreferences.secureScreen(),
|
preference = securityPreferences.secureScreen(),
|
||||||
title = stringResource(MR.strings.secure_screen),
|
|
||||||
entries = SecurityPreferences.SecureScreenMode.entries
|
entries = SecurityPreferences.SecureScreenMode.entries
|
||||||
.associateWith { stringResource(it.titleRes) }
|
.associateWith { stringResource(it.titleRes) }
|
||||||
.toImmutableMap(),
|
.toImmutableMap(),
|
||||||
|
title = stringResource(MR.strings.secure_screen),
|
||||||
),
|
),
|
||||||
// SY -->
|
// SY -->
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = securityPreferences.passwordProtectDownloads(),
|
preference = securityPreferences.passwordProtectDownloads(),
|
||||||
title = stringResource(SYMR.strings.password_protect_downloads),
|
title = stringResource(SYMR.strings.password_protect_downloads),
|
||||||
subtitle = stringResource(SYMR.strings.password_protect_downloads_summary),
|
subtitle = stringResource(SYMR.strings.password_protect_downloads_summary),
|
||||||
enabled = isCbzPasswordSet,
|
enabled = isCbzPasswordSet,
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = securityPreferences.encryptionType(),
|
preference = securityPreferences.encryptionType(),
|
||||||
title = stringResource(SYMR.strings.encryption_type),
|
title = stringResource(SYMR.strings.encryption_type),
|
||||||
entries = SecurityPreferences.EncryptionType.entries
|
entries = SecurityPreferences.EncryptionType.entries
|
||||||
.associateWith { stringResource(it.titleRes) }
|
.associateWith { stringResource(it.titleRes) }
|
||||||
@@ -384,12 +384,12 @@ object SettingsSecurityScreen : SearchableSettings {
|
|||||||
title = stringResource(MR.strings.pref_firebase),
|
title = stringResource(MR.strings.pref_firebase),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = privacyPreferences.crashlytics(),
|
preference = privacyPreferences.crashlytics(),
|
||||||
title = stringResource(MR.strings.onboarding_permission_crashlytics),
|
title = stringResource(MR.strings.onboarding_permission_crashlytics),
|
||||||
subtitle = stringResource(MR.strings.onboarding_permission_crashlytics_description),
|
subtitle = stringResource(MR.strings.onboarding_permission_crashlytics_description),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = privacyPreferences.analytics(),
|
preference = privacyPreferences.analytics(),
|
||||||
title = stringResource(MR.strings.onboarding_permission_analytics),
|
title = stringResource(MR.strings.onboarding_permission_analytics),
|
||||||
subtitle = stringResource(MR.strings.onboarding_permission_analytics_description),
|
subtitle = stringResource(MR.strings.onboarding_permission_analytics_description),
|
||||||
),
|
),
|
||||||
|
|||||||
+21
-13
@@ -30,8 +30,11 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.autofill.ContentType
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
|
import androidx.compose.ui.semantics.contentType
|
||||||
|
import androidx.compose.ui.semantics.semantics
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
@@ -59,6 +62,7 @@ import tachiyomi.core.common.util.lang.launchIO
|
|||||||
import tachiyomi.core.common.util.lang.withUIContext
|
import tachiyomi.core.common.util.lang.withUIContext
|
||||||
import tachiyomi.domain.source.service.SourceManager
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.i18n.sy.SYMR
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
@@ -125,51 +129,52 @@ object SettingsTrackingScreen : SearchableSettings {
|
|||||||
|
|
||||||
return listOf(
|
return listOf(
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
pref = trackPreferences.autoUpdateTrack(),
|
preference = trackPreferences.autoUpdateTrack(),
|
||||||
title = stringResource(MR.strings.pref_auto_update_manga_sync),
|
title = stringResource(MR.strings.pref_auto_update_manga_sync),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.ListPreference(
|
Preference.PreferenceItem.ListPreference(
|
||||||
pref = trackPreferences.autoUpdateTrackOnMarkRead(),
|
preference = trackPreferences.autoUpdateTrackOnMarkRead(),
|
||||||
title = stringResource(MR.strings.pref_auto_update_manga_on_mark_read),
|
|
||||||
entries = AutoTrackState.entries
|
entries = AutoTrackState.entries
|
||||||
.associateWith { stringResource(it.titleRes) }
|
.associateWith { stringResource(it.titleRes) }
|
||||||
.toPersistentMap(),
|
.toPersistentMap(),
|
||||||
|
title = stringResource(MR.strings.pref_auto_update_manga_on_mark_read),
|
||||||
),
|
),
|
||||||
|
// SY -->
|
||||||
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
|
preference = trackPreferences.resolveUsingSourceMetadata(),
|
||||||
|
title = stringResource(SYMR.strings.pref_tracker_resolve_using_source_metadata),
|
||||||
|
subtitle = stringResource(SYMR.strings.pref_tracker_resolve_using_source_metadata_summary),
|
||||||
|
),
|
||||||
|
// SY <--
|
||||||
Preference.PreferenceGroup(
|
Preference.PreferenceGroup(
|
||||||
title = stringResource(MR.strings.services),
|
title = stringResource(MR.strings.services),
|
||||||
preferenceItems = persistentListOf(
|
preferenceItems = persistentListOf(
|
||||||
Preference.PreferenceItem.TrackerPreference(
|
Preference.PreferenceItem.TrackerPreference(
|
||||||
title = trackerManager.myAnimeList.name,
|
|
||||||
tracker = trackerManager.myAnimeList,
|
tracker = trackerManager.myAnimeList,
|
||||||
login = { context.openInBrowser(MyAnimeListApi.authUrl(), forceDefaultBrowser = true) },
|
login = { context.openInBrowser(MyAnimeListApi.authUrl(), forceDefaultBrowser = true) },
|
||||||
logout = { dialog = LogoutDialog(trackerManager.myAnimeList) },
|
logout = { dialog = LogoutDialog(trackerManager.myAnimeList) },
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.TrackerPreference(
|
Preference.PreferenceItem.TrackerPreference(
|
||||||
title = trackerManager.aniList.name,
|
|
||||||
tracker = trackerManager.aniList,
|
tracker = trackerManager.aniList,
|
||||||
login = { context.openInBrowser(AnilistApi.authUrl(), forceDefaultBrowser = true) },
|
login = { context.openInBrowser(AnilistApi.authUrl(), forceDefaultBrowser = true) },
|
||||||
logout = { dialog = LogoutDialog(trackerManager.aniList) },
|
logout = { dialog = LogoutDialog(trackerManager.aniList) },
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.TrackerPreference(
|
Preference.PreferenceItem.TrackerPreference(
|
||||||
title = trackerManager.kitsu.name,
|
|
||||||
tracker = trackerManager.kitsu,
|
tracker = trackerManager.kitsu,
|
||||||
login = { dialog = LoginDialog(trackerManager.kitsu, MR.strings.email) },
|
login = { dialog = LoginDialog(trackerManager.kitsu, MR.strings.email) },
|
||||||
logout = { dialog = LogoutDialog(trackerManager.kitsu) },
|
logout = { dialog = LogoutDialog(trackerManager.kitsu) },
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.TrackerPreference(
|
Preference.PreferenceItem.TrackerPreference(
|
||||||
title = trackerManager.mangaUpdates.name,
|
|
||||||
tracker = trackerManager.mangaUpdates,
|
tracker = trackerManager.mangaUpdates,
|
||||||
login = { dialog = LoginDialog(trackerManager.mangaUpdates, MR.strings.username) },
|
login = { dialog = LoginDialog(trackerManager.mangaUpdates, MR.strings.username) },
|
||||||
logout = { dialog = LogoutDialog(trackerManager.mangaUpdates) },
|
logout = { dialog = LogoutDialog(trackerManager.mangaUpdates) },
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.TrackerPreference(
|
Preference.PreferenceItem.TrackerPreference(
|
||||||
title = trackerManager.shikimori.name,
|
|
||||||
tracker = trackerManager.shikimori,
|
tracker = trackerManager.shikimori,
|
||||||
login = { context.openInBrowser(ShikimoriApi.authUrl(), forceDefaultBrowser = true) },
|
login = { context.openInBrowser(ShikimoriApi.authUrl(), forceDefaultBrowser = true) },
|
||||||
logout = { dialog = LogoutDialog(trackerManager.shikimori) },
|
logout = { dialog = LogoutDialog(trackerManager.shikimori) },
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.TrackerPreference(
|
Preference.PreferenceItem.TrackerPreference(
|
||||||
title = trackerManager.bangumi.name,
|
|
||||||
tracker = trackerManager.bangumi,
|
tracker = trackerManager.bangumi,
|
||||||
login = { context.openInBrowser(BangumiApi.authUrl(), forceDefaultBrowser = true) },
|
login = { context.openInBrowser(BangumiApi.authUrl(), forceDefaultBrowser = true) },
|
||||||
logout = { dialog = LogoutDialog(trackerManager.bangumi) },
|
logout = { dialog = LogoutDialog(trackerManager.bangumi) },
|
||||||
@@ -183,7 +188,6 @@ object SettingsTrackingScreen : SearchableSettings {
|
|||||||
enhancedTrackers.first
|
enhancedTrackers.first
|
||||||
.map { service ->
|
.map { service ->
|
||||||
Preference.PreferenceItem.TrackerPreference(
|
Preference.PreferenceItem.TrackerPreference(
|
||||||
title = service.name,
|
|
||||||
tracker = service,
|
tracker = service,
|
||||||
login = { (service as EnhancedTracker).loginNoop() },
|
login = { (service as EnhancedTracker).loginNoop() },
|
||||||
logout = service::logout,
|
logout = service::logout,
|
||||||
@@ -227,7 +231,9 @@ object SettingsTrackingScreen : SearchableSettings {
|
|||||||
text = {
|
text = {
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.semantics { contentType = ContentType.Username + ContentType.EmailAddress },
|
||||||
value = username,
|
value = username,
|
||||||
onValueChange = { username = it },
|
onValueChange = { username = it },
|
||||||
label = { Text(text = stringResource(uNameStringRes)) },
|
label = { Text(text = stringResource(uNameStringRes)) },
|
||||||
@@ -238,7 +244,9 @@ object SettingsTrackingScreen : SearchableSettings {
|
|||||||
|
|
||||||
var hidePassword by remember { mutableStateOf(true) }
|
var hidePassword by remember { mutableStateOf(true) }
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.semantics { contentType = ContentType.Password },
|
||||||
value = password,
|
value = password,
|
||||||
onValueChange = { password = it },
|
onValueChange = { password = it },
|
||||||
label = { Text(text = stringResource(MR.strings.password)) },
|
label = { Text(text = stringResource(MR.strings.password)) },
|
||||||
@@ -287,7 +295,7 @@ object SettingsTrackingScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
val id = if (processing) MR.strings.loading else MR.strings.login
|
val id = if (processing) MR.strings.logging_in else MR.strings.login
|
||||||
Text(text = stringResource(id))
|
Text(text = stringResource(id))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
|
import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
|
||||||
import com.mikepenz.aboutlibraries.ui.compose.m3.util.htmlReadyLicenseContent
|
import com.mikepenz.aboutlibraries.ui.compose.util.htmlReadyLicenseContent
|
||||||
import eu.kanade.presentation.components.AppBar
|
import eu.kanade.presentation.components.AppBar
|
||||||
import eu.kanade.presentation.util.Screen
|
import eu.kanade.presentation.util.Screen
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
|||||||
+38
-28
@@ -1,8 +1,10 @@
|
|||||||
package eu.kanade.presentation.more.settings.screen.advanced
|
package eu.kanade.presentation.more.settings.screen.advanced
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
@@ -12,6 +14,7 @@ import androidx.compose.material.icons.outlined.SelectAll
|
|||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -42,16 +45,16 @@ import kotlinx.coroutines.flow.collectLatest
|
|||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
import tachiyomi.core.common.util.lang.launchUI
|
import tachiyomi.core.common.util.lang.launchUI
|
||||||
|
import tachiyomi.core.common.util.lang.toLong
|
||||||
import tachiyomi.core.common.util.lang.withNonCancellableContext
|
import tachiyomi.core.common.util.lang.withNonCancellableContext
|
||||||
import tachiyomi.data.Database
|
import tachiyomi.data.Database
|
||||||
import tachiyomi.domain.source.interactor.GetSourcesWithNonLibraryManga
|
import tachiyomi.domain.source.interactor.GetSourcesWithNonLibraryManga
|
||||||
import tachiyomi.domain.source.model.Source
|
import tachiyomi.domain.source.model.Source
|
||||||
import tachiyomi.domain.source.model.SourceWithCount
|
import tachiyomi.domain.source.model.SourceWithCount
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.i18n.sy.SYMR
|
|
||||||
import tachiyomi.presentation.core.components.LabeledCheckbox
|
|
||||||
import tachiyomi.presentation.core.components.LazyColumnWithAction
|
import tachiyomi.presentation.core.components.LazyColumnWithAction
|
||||||
import tachiyomi.presentation.core.components.material.Scaffold
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||||
@@ -73,18 +76,45 @@ class ClearDatabaseScreen : Screen() {
|
|||||||
is ClearDatabaseScreenModel.State.Loading -> LoadingScreen()
|
is ClearDatabaseScreenModel.State.Loading -> LoadingScreen()
|
||||||
is ClearDatabaseScreenModel.State.Ready -> {
|
is ClearDatabaseScreenModel.State.Ready -> {
|
||||||
if (s.showConfirmation) {
|
if (s.showConfirmation) {
|
||||||
// SY -->
|
|
||||||
var keepReadManga by remember { mutableStateOf(true) }
|
var keepReadManga by remember { mutableStateOf(true) }
|
||||||
// SY <--
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
|
title = {
|
||||||
|
Text(text = stringResource(MR.strings.are_you_sure))
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(MR.strings.clear_database_text))
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(MR.strings.clear_db_exclude_read),
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
Switch(
|
||||||
|
checked = keepReadManga,
|
||||||
|
onCheckedChange = { keepReadManga = it },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (!keepReadManga) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(MR.strings.clear_database_history_warning),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
onDismissRequest = model::hideConfirmation,
|
onDismissRequest = model::hideConfirmation,
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launchUI {
|
scope.launchUI {
|
||||||
// SY -->
|
|
||||||
model.removeMangaBySourceId(keepReadManga)
|
model.removeMangaBySourceId(keepReadManga)
|
||||||
// SY <--
|
|
||||||
model.clearSelection()
|
model.clearSelection()
|
||||||
model.hideConfirmation()
|
model.hideConfirmation()
|
||||||
context.toast(MR.strings.clear_database_completed)
|
context.toast(MR.strings.clear_database_completed)
|
||||||
@@ -99,20 +129,6 @@ class ClearDatabaseScreen : Screen() {
|
|||||||
Text(text = stringResource(MR.strings.action_cancel))
|
Text(text = stringResource(MR.strings.action_cancel))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
text = {
|
|
||||||
// SY -->
|
|
||||||
Column {
|
|
||||||
// SY <--
|
|
||||||
Text(text = stringResource(MR.strings.clear_database_confirmation))
|
|
||||||
// SY -->
|
|
||||||
LabeledCheckbox(
|
|
||||||
label = stringResource(SYMR.strings.clear_db_exclude_read),
|
|
||||||
checked = keepReadManga,
|
|
||||||
onCheckedChange = { keepReadManga = it },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// SY <--
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,15 +240,9 @@ private class ClearDatabaseScreenModel : StateScreenModel<ClearDatabaseScreenMod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun removeMangaBySourceId(/* SY --> */keepReadManga: Boolean /* SY <-- */) = withNonCancellableContext {
|
suspend fun removeMangaBySourceId(keepReadManga: Boolean) = withNonCancellableContext {
|
||||||
val state = state.value as? State.Ready ?: return@withNonCancellableContext
|
val state = state.value as? State.Ready ?: return@withNonCancellableContext
|
||||||
// SY -->
|
database.mangasQueries.deleteNonLibraryManga(state.selection, keepReadManga.toLong())
|
||||||
if (keepReadManga) {
|
|
||||||
database.mangasQueries.deleteMangasNotInLibraryAndNotReadBySourceIds(state.selection)
|
|
||||||
} else {
|
|
||||||
database.mangasQueries.deleteMangasNotInLibraryBySourceIds(state.selection)
|
|
||||||
}
|
|
||||||
// SY <--
|
|
||||||
database.historyQueries.removeResettedHistory()
|
database.historyQueries.removeResettedHistory()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Immutable
|
|||||||
import cafe.adriel.voyager.core.model.StateScreenModel
|
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||||
import cafe.adriel.voyager.core.model.screenModelScope
|
import cafe.adriel.voyager.core.model.screenModelScope
|
||||||
import dev.icerock.moko.resources.StringResource
|
import dev.icerock.moko.resources.StringResource
|
||||||
|
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||||
import kotlinx.collections.immutable.ImmutableSet
|
import kotlinx.collections.immutable.ImmutableSet
|
||||||
import kotlinx.collections.immutable.toImmutableSet
|
import kotlinx.collections.immutable.toImmutableSet
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
@@ -27,6 +28,7 @@ class ExtensionReposScreenModel(
|
|||||||
private val deleteExtensionRepo: DeleteExtensionRepo = Injekt.get(),
|
private val deleteExtensionRepo: DeleteExtensionRepo = Injekt.get(),
|
||||||
private val replaceExtensionRepo: ReplaceExtensionRepo = Injekt.get(),
|
private val replaceExtensionRepo: ReplaceExtensionRepo = Injekt.get(),
|
||||||
private val updateExtensionRepo: UpdateExtensionRepo = Injekt.get(),
|
private val updateExtensionRepo: UpdateExtensionRepo = Injekt.get(),
|
||||||
|
private val extensionManager: ExtensionManager = Injekt.get(),
|
||||||
) : StateScreenModel<RepoScreenState>(RepoScreenState.Loading) {
|
) : StateScreenModel<RepoScreenState>(RepoScreenState.Loading) {
|
||||||
|
|
||||||
private val _events: Channel<RepoEvent> = Channel(Int.MAX_VALUE)
|
private val _events: Channel<RepoEvent> = Channel(Int.MAX_VALUE)
|
||||||
@@ -53,6 +55,7 @@ class ExtensionReposScreenModel(
|
|||||||
fun createRepo(baseUrl: String) {
|
fun createRepo(baseUrl: String) {
|
||||||
screenModelScope.launchIO {
|
screenModelScope.launchIO {
|
||||||
when (val result = createExtensionRepo.await(baseUrl)) {
|
when (val result = createExtensionRepo.await(baseUrl)) {
|
||||||
|
CreateExtensionRepo.Result.Success -> extensionManager.findAvailableExtensions()
|
||||||
CreateExtensionRepo.Result.InvalidUrl -> _events.send(RepoEvent.InvalidUrl)
|
CreateExtensionRepo.Result.InvalidUrl -> _events.send(RepoEvent.InvalidUrl)
|
||||||
CreateExtensionRepo.Result.RepoAlreadyExists -> _events.send(RepoEvent.RepoAlreadyExists)
|
CreateExtensionRepo.Result.RepoAlreadyExists -> _events.send(RepoEvent.RepoAlreadyExists)
|
||||||
is CreateExtensionRepo.Result.DuplicateFingerprint -> {
|
is CreateExtensionRepo.Result.DuplicateFingerprint -> {
|
||||||
@@ -93,6 +96,7 @@ class ExtensionReposScreenModel(
|
|||||||
fun deleteRepo(baseUrl: String) {
|
fun deleteRepo(baseUrl: String) {
|
||||||
screenModelScope.launchIO {
|
screenModelScope.launchIO {
|
||||||
deleteExtensionRepo.await(baseUrl)
|
deleteExtensionRepo.await(baseUrl)
|
||||||
|
extensionManager.findAvailableExtensions()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -57,7 +57,7 @@ fun ExtensionRepoCreateDialog(
|
|||||||
},
|
},
|
||||||
text = {
|
text = {
|
||||||
Column {
|
Column {
|
||||||
Text(text = stringResource(MR.strings.action_add_repo_message))
|
Text(text = stringResource(MR.strings.action_add_repo_message, stringResource(MR.strings.app_name)))
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user