Compare commits

...
243 Commits
Author SHA1 Message Date
GitHub Actions 5d87bb077f feat(release): bump version 2023-01-21 07:25:25 +00:00
GitHub Actions 72b5b2c795 chore(android): refresh baseline profile 2023-01-21 07:16:07 +00:00
Harsh Shandilya ce91650788 fix(deps): downgrade to Compose 2023.01.00
There seems to be a regression with `produceState` in this release

Also reverts fb4beb08ee
2023-01-21 12:32:13 +05:30
GitHub Actions 1b2534eca6 chore(android): refresh baseline profile 2023-01-21 01:23:34 +00:00
Harsh Shandilya 5e3356e1d4 fix(android): set a key for database items 2023-01-21 01:50:00 +05:30
Harsh Shandilya ddc7a7598d fix(android): throw a different error for removed stories 2023-01-21 01:46:59 +05:30
Harsh Shandilya 2cfe9d73a2 chore(deps): update app.cash.sqldelight to 2.0.0-alpha05 2023-01-21 00:07:20 +05:30
Harsh Shandilya dbed41bdbd fix(renovate): disable updates for SQLDelight 2023-01-20 23:57:54 +05:30
GitHub Actions a0296f5d5a chore(android): refresh baseline profile 2023-01-20 01:19:27 +00:00
GitHub Actions 39cca12708 chore(android): refresh baseline profile 2023-01-19 01:21:48 +00:00
renovate[bot]andGitHub 0c4e3db59c chore(deps): update github/codeql-action digest to a34ca99 2023-01-18 20:27:54 +00:00
Harsh ShandilyaandGitHub 8dea17cbf2 chore(deps): update androidx.compose.compiler to 1.4.0 2023-01-18 14:32:52 +05:30
GitHub Actions b428c73602 chore(android): refresh baseline profile 2023-01-18 01:22:44 +00:00
Harsh ShandilyaandGitHub 8c9ce29a7a chore: add Renovate config rename to blame ignore revs 2023-01-17 22:14:22 +05:30
Harsh ShandilyaandGitHub f304304bc5 fix(renovate): switch config to JSON5 2023-01-17 22:13:41 +05:30
Harsh Shandilya f14941cf27 chore(deps): upgrade to Gradle 8.0-RC-2 2023-01-17 18:54:46 +05:30
GitHub Actions c83afb3cd7 chore(android): refresh baseline profile 2023-01-17 01:18:23 +00:00
GitHub Actions dfc6d80127 chore(android): refresh baseline profile 2023-01-16 01:18:43 +00:00
GitHub Actions 3ad1b07ff8 chore(android): refresh baseline profile 2023-01-15 01:23:13 +00:00
Harsh Shandilya d3c2861939 fix(renovate): adapt to Qodana linter switch 2023-01-14 19:30:27 +05:30
renovate[bot]andGitHub 8632f8fd7a fix(deps): update dependency com.diffplug.spotless:spotless-plugin-gradle to v6.13.0 2023-01-14 02:22:57 +00:00
GitHub Actions 83585b4a8a chore(android): refresh baseline profile 2023-01-14 01:24:57 +00:00
Harsh Shandilya e2e3186bb4 chore(gradle): update to 8.0-20230104085204+0000 nightly 2023-01-13 15:16:18 +05:30
Harsh Shandilya 29765f0024 fix(gradle): remove unnecessary properties 2023-01-13 11:12:55 +05:30
Harsh Shandilya 43edeaff04 fix(gradle): remove obsolete CC workaround 2023-01-13 11:11:33 +05:30
Harsh Shandilya 2b7cf7d3cb fix(renovate): remove AGP version clamp 2023-01-13 10:55:23 +05:30
Harsh Shandilya fdf13fd201 chore(deps): bump com.squareup:anvil to v2.4.4 2023-01-13 10:54:39 +05:30
renovate[bot]andGitHub 92ba77a838 chore(deps): update agp to v7.4.0 2023-01-13 02:07:17 +00:00
GitHub Actions d910810dd4 chore(android): refresh baseline profile 2023-01-13 01:22:45 +00:00
Harsh Shandilya 5f8853c69d fix(renovate): fix Compose BOM resolution 2023-01-13 00:09:08 +05:30
Harsh Shandilya 0f9b35efb1 fix(renovate): remove obsoleted Compose group 2023-01-12 22:39:47 +05:30
Harsh Shandilya 7096e62948 fix(deps): switch to Chris Banes' Compose alpha BOM 2023-01-12 22:37:10 +05:30
renovate[bot]andGitHub acb7c0e670 chore(deps): update github/codeql-action digest to 515828d 2023-01-12 14:00:44 +00:00
Harsh ShandilyaandGitHub bb91e6e083 fix(deps): update accompanist to 0.29.0-alpha 2023-01-12 19:02:26 +05:30
Harsh ShandilyaandGitHub 67a922362d chore(deps): bump accompanist to 0.29.0 2023-01-12 17:39:39 +05:30
GitHub Actions 56be44b918 feat(release): start next development iteration 2023-01-12 07:38:31 +00:00
GitHub Actions 15493aec1a feat(release): bump version 2023-01-12 07:35:33 +00:00
Harsh Shandilya 9aef7582bd chore: update README 2023-01-12 12:52:29 +05:30
Harsh Shandilya 5378ec6854 fix(ci): disable scheduled code analysis jobs
They both fail for stupid reasons 🤷
2023-01-12 12:42:00 +05:30
Harsh Shandilya 79dbb64275 chore(gradle): reformat version catalog 2023-01-12 11:27:54 +05:30
GitHub Actions b8912e747a chore(android): refresh baseline profile 2023-01-12 01:24:00 +00:00
renovate[bot]andHarsh Shandilya a9dcd0157a fix(deps): update dependency androidx.lifecycle:lifecycle-viewmodel-compose to v2.6.0-alpha04 2023-01-12 02:55:54 +05:30
renovate[bot]andHarsh Shandilya 45e8993d75 fix(deps): update dependency androidx.test.uiautomator:uiautomator to v2.3.0-alpha02 2023-01-12 02:55:54 +05:30
renovate[bot]andHarsh Shandilya 39bd1fa2eb fix(deps): update dependency androidx.profileinstaller:profileinstaller to v1.3.0-alpha03 2023-01-12 02:55:54 +05:30
renovate[bot]andHarsh Shandilya 5b33fbc20b fix(deps): update dependency androidx.browser:browser to v1.5.0-beta01 2023-01-12 02:55:53 +05:30
renovate[bot]andHarsh Shandilya 196195d867 fix(deps): update dependency androidx.benchmark:benchmark-macro-junit4 to v1.2.0-alpha09 2023-01-12 02:55:28 +05:30
renovate[bot]andHarsh Shandilya 20b8076235 fix(deps): update dependency androidx.activity:activity-compose to v1.7.0-alpha03 2023-01-12 02:54:44 +05:30
Harsh Shandilya 5f49c8149a fix(renovate): batch Compose updates together 2023-01-12 02:51:01 +05:30
Harsh Shandilya fb4beb08ee fix(common): adopt new Compose APIs 2023-01-12 02:40:37 +05:30
Harsh Shandilya 6750eff9fa chore(deps): upgrade to Compose 2023-01-12 alpha release 2023-01-12 02:31:00 +05:30
Harsh Shandilya 692cc8ce67 chore: update changelog 2023-01-12 02:20:09 +05:30
Harsh Shandilya a59b6411b1 feat: implement support for persisting read comments
Fixes #358
2023-01-12 02:20:09 +05:30
Harsh Shandilya 1345709450 feat(android): wire up PostCommentsQueries 2023-01-12 01:06:34 +05:30
Harsh Shandilya 8433f38f6e fix(model): add Comment#shortId 2023-01-12 01:06:34 +05:30
Harsh Shandilya 91c3d0f9a6 feat(common): add support for unread comments to UI 2023-01-12 01:06:34 +05:30
Harsh Shandilya 579a2ffe2e fix(database): add a migration to generate an empty PostComments table 2023-01-12 00:58:07 +05:30
Harsh Shandilya 918f28feab fix(renovate): prevent Anvil from being queried in the snapshots repo
Renovate incorrectly picks up the 2.4.4-1-8-0-SNAPSHOT version
2023-01-11 18:04:51 +05:30
Harsh Shandilya 7b13d40797 chore(deps): upgrade to Kotlin 1.8.0 2023-01-11 17:41:04 +05:30
GitHub Actions 002588a45a chore(android): refresh baseline profile 2023-01-11 01:20:37 +00:00
Harsh Shandilya 1ae0cbb95d feat(database): add a table for post comments 2023-01-11 03:05:36 +05:30
Harsh Shandilya 3e2d32e4c9 chore: refresh license headers 2023-01-11 02:40:44 +05:30
Harsh Shandilya 403f7d47b1 refactor(database): rename test class 2023-01-11 02:39:11 +05:30
GitHub Actions d1fcebdf4c chore(android): refresh baseline profile 2023-01-10 01:17:58 +00:00
renovate[bot]andGitHub a983bd729c chore(deps): update actions/cache action to v3.2.3 2023-01-09 05:49:54 +00:00
GitHub Actions 8d311a1193 chore(android): refresh baseline profile 2023-01-09 01:20:48 +00:00
GitHub Actions 6bbd2c73b5 chore(android): refresh baseline profile 2023-01-08 01:16:53 +00:00
GitHub Actions f851e2d626 chore(android): refresh baseline profile 2023-01-07 01:25:39 +00:00
renovate[bot]andGitHub 97aafc764f chore(deps): update agp to v7.4.0-rc03 2023-01-06 19:21:43 +00:00
Harsh Shandilya 183e10dba8 chore(renovate): prevent AGP from being upgraded past 7.4.x 2023-01-07 00:47:48 +05:30
Harsh Shandilya f5deaaa8ff fix(deps): downgrade AGP to 7.4.0-beta02 2023-01-07 00:44:38 +05:30
Harsh Shandilya 55537ea191 fix(deps): update dependency org.gradle.android.cache-fix:org.gradle.android.cache-fix.gradle.plugin to v2.6.1 2023-01-07 00:36:03 +05:30
Harsh Shandilya 98818023e9 chore: update changelog 2023-01-07 00:36:03 +05:30
Harsh Shandilya e67df253de Revert "feat(android): use enter always scroll behavior for TopAppBar"
This reverts commit eeef2c347d.
2023-01-07 00:36:03 +05:30
renovate[bot]andGitHub 53524c8ef8 chore(deps): update actions/upload-artifact action to v3.1.2 2023-01-06 15:46:07 +00:00
Harsh Shandilya bc90a8941a chore(github): allow running analysis actions manually 2023-01-06 20:37:42 +05:30
Harsh Shandilya 5c413d1f12 chore(qodana): switch to the Android linter 2023-01-06 20:37:42 +05:30
renovate[bot]andGitHub 8b5ab33730 chore(deps): update github/codeql-action digest to 959cbb7 2023-01-06 03:58:04 +00:00
GitHub Actions d4d1288900 chore(android): refresh baseline profile 2023-01-06 01:31:11 +00:00
renovate[bot]andGitHub f4f07d0ce3 chore(deps): update actions/checkout action to v3.3.0 2023-01-05 22:08:46 +00:00
renovate[bot]andGitHub 6282f4d6e0 chore(deps): update actions/checkout digest to ac59398 2023-01-05 18:54:06 +00:00
Harsh Shandilya 19f5f8a0ca chore: hide baseline-prof from diffs
Having this pollute regular diffs doesn't provide much value if any
at all, since it is generated by an automated process and never
touched by hand.
2023-01-05 21:08:55 +05:30
Harsh Shandilya a296d2b827 sync-backend: delete
Realistically this will be much more enjoyable to do in Rust or Go *shudder*
so let's cut out the unused code until we can tackle the backend.
2023-01-05 21:07:07 +05:30
Harsh Shandilya 45872f1653 chore: re-enable code quality workflows to run on a schedule 2023-01-05 21:00:06 +05:30
GitHub Actions 074420056b chore(android): refresh baseline profile 2023-01-05 01:23:05 +00:00
renovate[bot]andHarsh Shandilya 9f7e05d79a chore(deps): update agp to v8.0.0-alpha11 2023-01-05 00:30:58 +05:30
Harsh Shandilya 955299bd89 chore(renovate): re-enable AGP updates 2023-01-04 23:58:57 +05:30
renovate[bot]andHarsh Shandilya 6520758722 fix(deps): update dependency androidx.test.espresso:espresso-core to v3.5.1 2023-01-04 13:06:36 +05:30
renovate[bot]andGitHub 583137aa25 fix(deps): update dependency androidx.test.ext:junit to v1.1.5 2023-01-04 04:14:16 +00:00
GitHub Actions 8afce7706c chore(android): refresh baseline profile 2023-01-04 01:19:47 +00:00
renovate[bot]andGitHub 7ae0a0bae6 fix(deps): update dependency androidx.test:runner to v1.5.2 2023-01-03 21:54:08 +00:00
renovate[bot]andGitHub f34d6c5222 fix(deps): update ktor to v2.2.2 2023-01-03 18:18:52 +00:00
GitHub Actions 002f4f9309 chore(android): refresh baseline profile 2023-01-03 01:18:09 +00:00
renovate[bot]andGitHub bf77a9aaed fix(deps): update dependency com.diffplug.spotless:spotless-plugin-gradle to v6.12.1 2023-01-02 05:09:06 +00:00
GitHub Actions d17c178eaa chore(android): refresh baseline profile 2023-01-02 01:22:20 +00:00
GitHub Actions 2c0ba0fae9 chore(android): refresh baseline profile 2023-01-01 01:24:21 +00:00
renovate[bot]andGitHub 0396a488cd fix(deps): update dependency com.slack.eithernet:eithernet to v1.3.1 2022-12-31 18:53:39 +00:00
GitHub ActionsandHarsh Shandilya f5ef483cd9 chore(android): refresh baseline profile 2022-12-31 05:38:40 +00:00
Harsh Shandilya 78960f6d4e feat(deps): upgrade to Gradle 8.0-rc-1 2022-12-31 05:38:40 +00:00
GitHub Actions 2be9408560 chore(android): refresh baseline profile 2022-12-30 01:16:41 +00:00
GitHub Actions 46dc6fbe91 chore(android): refresh baseline profile 2022-12-29 01:15:22 +00:00
GitHub Actions e411cb3d7f chore(android): refresh baseline profile 2022-12-28 01:18:43 +00:00
renovate[bot]andGitHub 00535dc859 chore(deps): update actions/cache action to v3.2.2 2022-12-27 13:49:21 +00:00
GitHub Actions 465e6931ad chore(android): refresh baseline profile 2022-12-27 01:15:23 +00:00
GitHub Actions 7e64100491 chore(android): refresh baseline profile 2022-12-26 01:19:10 +00:00
GitHub Actions e199f00ca0 chore(android): refresh baseline profile 2022-12-25 01:18:18 +00:00
renovate[bot]andGitHub 8722636b13 fix(deps): update dependency ch.qos.logback:logback-classic to v1.4.5 2022-12-24 15:42:59 +00:00
Harsh Shandilya 5db4a1ee8d sync-backend: initialize barebones module
This is simply an import of the project generated by start.ktor.io cleaned of
its ungodly wildcard import fetish and better integrated into our existing build
system.
2022-12-24 14:17:59 +00:00
GitHub Actions b0bae12e6b feat(release): start next development iteration 2022-12-24 02:26:25 +00:00
GitHub Actions 585d42c6aa feat(release): bump version 2022-12-24 02:25:00 +00:00
GitHub Actions 47448616dd chore(android): refresh baseline profile 2022-12-24 01:19:28 +00:00
GitHub Actions ccd64b46e3 chore(android): refresh baseline profile 2022-12-23 20:38:40 +00:00
Harsh Shandilya bce227357f chore(gradle): run versionCatalogFormat 2022-12-24 01:43:30 +05:30
Harsh Shandilya 2ec1633c51 fix(gradle): remove unnecessary repository declarations 2022-12-24 01:38:04 +05:30
Harsh Shandilya ed8528aea4 fix(build-logic): tweak DependencyUpdates configuration 2022-12-24 01:36:29 +05:30
Harsh Shandilya 3cc74f9bcf fix(android): stupid copypasta :clown:
Fixes 2afbc73bd4
2022-12-24 01:22:38 +05:30
renovate[bot]andGitHub 015c9929b3 chore(deps): update actions/cache action to v3.2.1 2022-12-23 07:53:38 +00:00
Harsh Shandilya 76737b0eed chore(gradle): remove Compose Compiler metrics opt-in
This doesn't seem worth generating for each build

Revert "fix(gradle): address list reassignment lints"

This reverts commit 1d2da7c2f8.

Revert "chore: enable Compose Compiler metrics"

This reverts commit 0d668ac3aa.
2022-12-22 11:25:06 +05:30
Harsh Shandilya 28c454261f fix(deps): return to AGP 8.x 2022-12-22 11:19:05 +05:30
GitHub Actions 327ec9b6a5 chore(android): refresh baseline profile 2022-12-22 01:14:47 +00:00
GitHub Actions fc617a5dfa chore(android): refresh baseline profile 2022-12-21 19:16:21 +00:00
renovate[bot]andGitHub c6289908c4 chore(deps): update actions/cache action to v3.2.0 2022-12-21 15:13:13 +00:00
Harsh ShandilyaandGitHub ca0706f94b fix(ci): fix baseline profile path 2022-12-21 16:44:18 +05:30
Harsh Shandilya 350a64d479 fix(benchmark): simplify 2022-12-21 15:03:05 +05:30
Harsh Shandilya 058561e4d6 chore(ci): tweak baseline profile execution environment 2022-12-21 14:31:36 +05:30
Harsh Shandilya a4bc5476a4 feat(ci): rework benchmark workflow to improve perf 2022-12-21 14:13:23 +05:30
Harsh Shandilya 1fe559ff04 chore(benchmark): remove test filter annotations 2022-12-21 13:55:15 +05:30
Harsh Shandilya 78f050f115 Revert "fix(benchmark): remove startup benchmarks"
This reverts commit fab5cb45c4.
2022-12-21 13:36:54 +05:30
Harsh Shandilya 0d964c4a5d fix(ci): use a more granular test filter 2022-12-21 13:33:06 +05:30
Harsh Shandilya c24c433c0e chore(ci): log stacktrace on failure 2022-12-21 13:18:07 +05:30
Harsh Shandilya fd9010d468 chore(android): refresh baseline profile 2022-12-21 13:11:08 +05:30
Harsh Shandilya 90f40075e8 fix(ci): fix incorrect baseline profile destination 2022-12-21 13:10:13 +05:30
GitHub Actions f621f785f8 feat(release): start next development iteration 2022-12-20 19:17:34 +00:00
GitHub Actions 76d62906c8 feat(release): bump version 2022-12-20 19:16:18 +00:00
Harsh ShandilyaandGitHub dbeafbb569 fix(benchmark): upgrade baseline profile generator size
This takes long enough to be a large test
2022-12-21 00:05:24 +05:30
Harsh ShandilyaandGitHub fab5cb45c4 fix(benchmark): remove startup benchmarks
These just always fail and it's not worth pursuing a fix
2022-12-20 23:28:59 +05:30
Harsh Shandilya 7b593806eb fix: prevent unwanted list jumping during paging refresh
Fixes #347

Fixes SDP-34
2022-12-20 22:47:53 +05:30
Harsh ShandilyaandGitHub f6064616b0 chore: reword changelog entries 2022-12-20 16:57:24 +05:30
Harsh ShandilyaandGitHub 36d999ebb4 chore(build-logic): use kotlin DSL's apply extension 2022-12-20 16:40:20 +05:30
Harsh Shandilya 79920f6180 chore: align copyright headers with Spotless expectations 2022-12-20 16:01:44 +05:30
Harsh Shandilya 36ab72226c refactor(build-logic): simplify plugin setup 2022-12-20 16:01:18 +05:30
renovate[bot]andGitHub 7cc81047d4 chore(deps): update plugin anvil to v2.4.3 2022-12-17 00:47:20 +00:00
Harsh Shandilya 82252dbe2d chore: remove unused Nix shell config 2022-12-16 18:19:16 +05:30
renovate[bot]andGitHub da1825498a chore(deps): update actions/setup-java action to v3.9.0 2022-12-14 16:13:19 +00:00
Harsh Shandilya 947a04d82d chore: update changelog 2022-12-14 19:41:14 +05:30
Harsh Shandilya 75ab508dc3 refactor: rework ProgressBar usage 2022-12-14 18:32:28 +05:30
Harsh Shandilya 1d2da7c2f8 fix(gradle): address list reassignment lints 2022-12-14 18:27:01 +05:30
Harsh Shandilya dd019279ec fix(comments): ensure load failure screen renders correctly
Fixes SDP-21
2022-12-14 17:56:16 +05:30
Harsh Shandilya 485282a48d feat(common): change progress indicator to linear
Fixes SDP-20
2022-12-14 17:46:42 +05:30
Harsh Shandilya eeef2c347d feat(android): use enter always scroll behavior for TopAppBar 2022-12-13 17:03:04 +00:00
Harsh Shandilya b7229c6d15 chore: add changelog entry for expanded state fix 2022-12-13 11:43:46 +05:30
Harsh Shandilya da80605ae1 chore(common): updateAllExpanded -> setExpanded 2022-12-13 11:37:41 +05:30
Anunay Maheshwari 4066a86ad6 comments: Update isExpanded based on the parent node being toggled (Fixes #352) 2022-12-13 10:56:28 +05:30
renovate[bot]andGitHub 0f595f7b02 chore(deps): update actions/checkout action to v3.2.0 2022-12-12 19:52:23 +00:00
GitHub Actions 42f3068b01 feat(release): start next development iteration 2022-12-12 08:02:05 +00:00
GitHub Actions 0312c3f431 feat(release): bump version 2022-12-12 08:01:02 +00:00
Harsh Shandilya 7d324ddb8c refactor(common): tweak visibility of module members 2022-12-12 12:47:23 +05:30
Harsh Shandilya c7b7a786f3 chore: update changelog 2022-12-12 12:02:59 +05:30
Harsh Shandilya 26f69c2027 feat(common): match node collapse behaviour with the lobste.rs site 2022-12-12 12:02:59 +05:30
Harsh Shandilya 450601f26b feat(common): use a simplified search algorithm 2022-12-12 11:26:29 +05:30
Harsh Shandilya 2ab69de686 fix(common): do not compute a new node if it cannot be used 2022-12-12 10:44:00 +05:30
Harsh Shandilya 0083aa69b5 refactor(common): optimize comment entry display 2022-12-12 10:44:00 +05:30
Anunay MaheshwariandHarsh Shandilya 18d82a5eb3 refactor(comments): convert internal representation to a tree 2022-12-12 10:44:00 +05:30
Harsh Shandilya 0d668ac3aa chore: enable Compose Compiler metrics 2022-12-12 02:41:12 +05:30
Harsh Shandilya b1a32496ba refactor: improve skippability of Composables 2022-12-12 02:41:12 +05:30
Harsh Shandilya 4db59db7a1 fix(scripts): set a trap to delete temp file 2022-12-12 02:10:58 +05:30
Harsh Shandilya 0843352514 fix(ci): fix benchmark assemble tasks 2022-12-12 02:10:42 +05:30
renovate[bot]andGitHub aa50d6449d chore(deps): pin reactivecircus/android-emulator-runner action to 50986b1 2022-12-11 15:02:50 +00:00
Harsh Shandilya 6a41b966c5 feat(ci): disable lint workflows
This hasn't brought much value but eats up too much CI time
2022-12-11 19:09:44 +05:30
Harsh Shandilya 943b9bd091 chore: re-encrypt secrets with age 2022-12-11 19:09:44 +05:30
Harsh Shandilya 010f69031a feat(ci): add a job to regenerate baseline profile 2022-12-11 17:22:07 +05:30
Harsh Shandilya 168271a8f4 chore: add changelog entry for post update worker tweaks 2022-12-10 20:16:04 +05:30
Harsh Shandilya ea92d64972 fix(android): do not force re-enqueue post update job on each start 2022-12-10 20:16:04 +05:30
Harsh Shandilya 766f14cd22 refactor(android): run post updates in a batch transaction 2022-12-10 20:16:04 +05:30
Harsh Shandilya 33210319bb fix(android): manually set up WorkManager 2022-12-10 19:31:22 +05:30
Harsh Shandilya 7ef0a8fc51 fix(database): ensure schema migrations can run 2022-12-10 18:27:12 +05:30
Harsh Shandilya 1f850435b1 feat(database): add description to the saved post table 2022-12-10 18:27:12 +05:30
GitHub Actions 9d8fd77a08 feat(release): start next development iteration 2022-12-10 10:21:13 +00:00
GitHub Actions e2c760e7f3 feat(release): bump version 2022-12-10 10:18:13 +00:00
Harsh Shandilya 51a8538d6a fix: users can exist without inviters 2022-12-10 10:14:35 +00:00
renovate[bot]andGitHub 4b9b148519 chore(deps): update jetbrains/qodana-jvm-community docker tag to v2022.3 2022-12-09 16:13:12 +00:00
Harsh Shandilya ced16c3794 feat(renovate): add a regex manager for Qodana linters 2022-12-09 21:39:16 +05:30
renovate[bot]andHarsh Shandilya e824294ea0 chore(deps): update jetbrains/qodana-action action to v2022.3.0 2022-12-09 21:30:48 +05:30
renovate[bot]andHarsh Shandilya ffce7c2b3e chore(deps): pin dependencies 2022-12-09 21:30:23 +05:30
Harsh Shandilya f0d615bd44 feat(qodana): check-in configuration 2022-12-09 21:20:42 +05:30
Harsh Shandilya df546947bb chore(changelog): fix diff links 2022-12-09 21:19:01 +05:30
Harsh Shandilya 3e0544279c fix(release): ensure changelog links are valid 2022-12-09 21:18:20 +05:30
GitHub Actions 25bfed3c71 feat(release): start next development iteration 2022-12-09 15:40:46 +00:00
GitHub Actions 6c88ecf5d5 feat(release): bump version 2022-12-09 15:37:20 +00:00
Harsh Shandilya 4f60db29bd Revert "refactor(android): move post save state logic to a Paging transform"
This reverts commit 3829abeb8f.
2022-12-09 21:02:24 +05:30
Harsh Shandilya e6eaadbaa1 chore: add CHANGELOG entry for d36bcd6b26 2022-12-09 21:01:34 +05:30
Harsh Shandilya 30fa04db56 fix(android): only queue post update worker on Application init 2022-12-09 20:58:42 +05:30
Harsh Shandilya 74684fe115 fix(common): reduce space between favicon and URL 2022-12-09 18:19:15 +05:30
Harsh Shandilya d36bcd6b26 fix(common): simplify favicon display 2022-12-09 18:19:10 +05:30
Harsh Shandilya a2f3faad0f chore: add an editorconfig file to approximate ktfmt formatting
This avoids having to reconfigure IntelliJ again and again
2022-12-09 16:08:36 +05:30
Harsh Shandilya 9b25cd0fea feat(ci): add Qodana scan workflow 2022-12-09 16:08:11 +05:30
renovate[bot]andGitHub db75d1edfe chore(deps): update github/codeql-action digest to a669cc5 2022-12-08 22:27:59 +00:00
GitHub Actions ac98ebda3f feat(release): start next development iteration 2022-12-07 21:51:44 +00:00
GitHub Actions c8d03c515a feat(release): bump version 2022-12-07 21:47:57 +00:00
Harsh Shandilya a77f098822 chore: update changelog 2022-12-08 03:13:50 +05:30
Harsh Shandilya 608720ac35 chore(android): refresh baseline profile 2022-12-08 03:12:59 +05:30
renovate[bot]andHarsh Shandilya f1b68b238c fix(deps): update richtext to v0.16.0 2022-12-08 03:08:58 +05:30
renovate[bot]andGitHub 977e3cbc8e fix(deps): update dependency androidx.compose:compose-bom to v2022.12.00 2022-12-07 21:38:17 +00:00
renovate[bot]andHarsh Shandilya fe2c3f630b fix(deps): update dependency androidx.work:work-runtime-ktx to v2.8.0-rc01 2022-12-08 03:06:00 +05:30
renovate[bot]andGitHub 4fac861656 chore(deps): pin dependencies 2022-12-07 21:33:05 +00:00
Harsh Shandilya 0ec03fc979 fix(benchmark): re-enable BaselineProfileBenchmark 2022-12-08 02:59:58 +05:30
renovate[bot]andHarsh Shandilya 46a9658dcd fix(deps): update dependency androidx.browser:browser to v1.5.0-alpha02 2022-12-08 02:48:05 +05:30
Harsh Shandilya 387881181a feat(benchmark): switch to new BaselineProfileRule.collectStableBaselineProfile API 2022-12-08 02:46:55 +05:30
renovate[bot]andHarsh Shandilya cb619ca6a9 fix(deps): update dependency androidx.benchmark:benchmark-macro-junit4 to v1.2.0-alpha08 2022-12-08 02:40:33 +05:30
Harsh Shandilya 37fc528053 fix(benchmark): wut 2022-12-08 02:39:33 +05:30
Harsh Shandilya b19617f8c7 feat(ci): configure CodeQL analysis 2022-12-08 01:40:44 +05:30
Harsh Shandilya 3829abeb8f refactor(android): move post save state logic to a Paging transform
Fixes #349
2022-12-08 01:32:04 +05:30
renovate[bot]andGitHub bd5cf0f167 chore(deps): update actions/setup-java action to v3.8.0 2022-12-06 16:56:08 +00:00
Harsh Shandilya a8ba0ed3d9 refactor(build-logic): massage around some types and extensions 2022-12-04 20:24:33 +05:30
Harsh Shandilya 1267cf81e3 refactor(build-logic): make everything top-level 2022-12-04 20:13:08 +05:30
Harsh Shandilya f48ef0206f fix(gradle): remove unused exclusiveContent block 2022-12-03 20:22:54 +05:30
renovate[bot]andGitHub 7b31dc740b chore(deps): update actions/setup-java action to v3.7.0 2022-12-01 14:38:07 +00:00
Harsh Shandilya 69c76a06c2 fix(gradle): use internal kotlin-android plugin 2022-12-01 12:18:45 +05:30
GitHub Actions 83e01c7cbc feat(release): start next development iteration 2022-11-30 14:38:50 +00:00
GitHub Actions b6421b01ba feat(release): bump version 2022-11-30 14:37:38 +00:00
Harsh Shandilya 9bc4a8c81b fix(readme): use an HTML image to downsize featured graphic 2022-11-30 15:01:20 +05:30
Harsh Shandilya 8cae135784 chore: refresh AGP experimental features opt-in 2022-11-30 14:35:56 +05:30
Harsh Shandilya eba5d64998 refactor: migrate all tests to kotest 2022-11-30 01:47:17 +05:30
Harsh Shandilya 8eec09e832 feat(gradle): add kotest deps 2022-11-30 01:32:15 +05:30
Harsh Shandilya 69537da95b chore: mark baseline profile as generated 2022-11-30 01:04:38 +05:30
Harsh Shandilya a679b88497 chore: update changelog 2022-11-30 01:04:38 +05:30
Harsh Shandilya e570b9d88f feat(android): refresh baseline profile 2022-11-30 01:04:38 +05:30
Harsh Shandilya ce37523e11 fix(benchmark): ensure UiAutomator is able to correctly find UI elements 2022-11-30 01:04:38 +05:30
Harsh Shandilya 3601c190ac fix(benchmark): disable baseline profile benchmark 2022-11-29 21:26:55 +05:30
Harsh Shandilya b0044dd0fc chore(deps): override ProfileInstaller version 2022-11-29 21:22:39 +05:30
Harsh Shandilya b2c2be45bf refactor: cleanup and simplify AndroidCommonPlugin 2022-11-29 21:22:39 +05:30
Harsh Shandilya 6f219b6da2 fix(benchmark): ensure benchmark build type is not obfuscated 2022-11-29 21:22:39 +05:30
Harsh Shandilya 8d6c567c84 refactor: simplify build type configuration 2022-11-29 21:22:39 +05:30
Harsh Shandilya 2b2010a22c fix(ci): only run one instance of snapshot workflow 2022-11-29 18:15:25 +05:30
Harsh Shandilya 27af06e180 chore: try out a full size image 2022-11-29 18:11:46 +05:30
Harsh Shandilya 499088cf43 chore: re-do README screenshot 2022-11-29 18:09:01 +05:30
renovate[bot]andGitHub ce579f21f9 fix(deps): update accompanist to v0.28.0 2022-11-29 04:51:00 +00:00
Harsh Shandilya 5a4643dc57 refactor: start consuming whetstone through Sonatype snapshots repository
It was much easier to set up automatic publishing than to constantly debug weird build errors from included builds
2022-11-29 03:29:06 +05:30
Harsh Shandilya 2a65dc4a9e chore(deps): bump whetstone tag 2022-11-29 02:17:04 +05:30
Harsh Shandilya d08aa25f76 fix: add changelog entry 2022-11-29 01:53:48 +05:30
Harsh Shandilya 410c7ccd1d fix: add missing DI annotation to CoilAppPlugin 2022-11-29 01:53:48 +05:30
Harsh Shandilya 5443cc1486 refactor: simplify swipe to refresh 2022-11-29 01:25:20 +05:30
Harsh Shandilya 683cb0cfbb fix: adjust capitalisation of CI steps 2022-11-28 19:56:59 +05:30
GitHub Actions cbf89b4158 feat(release): start next development iteration 2022-11-28 14:18:53 +00:00
111 changed files with 20175 additions and 5509 deletions
+93
View File
@@ -0,0 +1,93 @@
# This .editorconfig section approximates ktfmt's formatting rules. You can include it in an
# existing .editorconfig file or use it standalone by copying it to <project root>/.editorconfig
# and making sure your editor is set to read settings from .editorconfig files.
#
# It includes editor-specific config options for IntelliJ IDEA.
#
# If any option is wrong, PR are welcome
[{*.kt,*.kts}]
indent_style = space
insert_final_newline = true
max_line_length = 100
indent_size = 2
ij_continuation_indent_size = 2
ij_java_names_count_to_use_import_on_demand = 9999
ij_kotlin_align_in_columns_case_branch = false
ij_kotlin_align_multiline_binary_operation = false
ij_kotlin_align_multiline_extends_list = false
ij_kotlin_align_multiline_method_parentheses = false
ij_kotlin_align_multiline_parameters = true
ij_kotlin_align_multiline_parameters_in_calls = false
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true
ij_kotlin_assignment_wrap = normal
ij_kotlin_blank_lines_after_class_header = 0
ij_kotlin_blank_lines_around_block_when_branches = 0
ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
ij_kotlin_block_comment_at_first_column = true
ij_kotlin_call_parameters_new_line_after_left_paren = true
ij_kotlin_call_parameters_right_paren_on_new_line = false
ij_kotlin_call_parameters_wrap = on_every_item
ij_kotlin_catch_on_new_line = false
ij_kotlin_class_annotation_wrap = split_into_lines
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
ij_kotlin_continuation_indent_for_chained_calls = true
ij_kotlin_continuation_indent_for_expression_bodies = true
ij_kotlin_continuation_indent_in_argument_lists = true
ij_kotlin_continuation_indent_in_elvis = false
ij_kotlin_continuation_indent_in_if_conditions = false
ij_kotlin_continuation_indent_in_parameter_lists = false
ij_kotlin_continuation_indent_in_supertype_lists = false
ij_kotlin_else_on_new_line = false
ij_kotlin_enum_constants_wrap = off
ij_kotlin_extends_list_wrap = normal
ij_kotlin_field_annotation_wrap = split_into_lines
ij_kotlin_finally_on_new_line = false
ij_kotlin_if_rparen_on_new_line = false
ij_kotlin_import_nested_classes = false
ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
ij_kotlin_keep_blank_lines_before_right_brace = 2
ij_kotlin_keep_blank_lines_in_code = 2
ij_kotlin_keep_blank_lines_in_declarations = 2
ij_kotlin_keep_first_column_comment = true
ij_kotlin_keep_indents_on_empty_lines = false
ij_kotlin_keep_line_breaks = true
ij_kotlin_lbrace_on_next_line = false
ij_kotlin_line_comment_add_space = false
ij_kotlin_line_comment_at_first_column = true
ij_kotlin_method_annotation_wrap = split_into_lines
ij_kotlin_method_call_chain_wrap = normal
ij_kotlin_method_parameters_new_line_after_left_paren = true
ij_kotlin_method_parameters_right_paren_on_new_line = true
ij_kotlin_method_parameters_wrap = on_every_item
ij_kotlin_name_count_to_use_star_import = 9999
ij_kotlin_name_count_to_use_star_import_for_members = 9999
ij_kotlin_parameter_annotation_wrap = off
ij_kotlin_space_after_comma = true
ij_kotlin_space_after_extend_colon = true
ij_kotlin_space_after_type_colon = true
ij_kotlin_space_before_catch_parentheses = true
ij_kotlin_space_before_comma = false
ij_kotlin_space_before_extend_colon = true
ij_kotlin_space_before_for_parentheses = true
ij_kotlin_space_before_if_parentheses = true
ij_kotlin_space_before_lambda_arrow = true
ij_kotlin_space_before_type_colon = false
ij_kotlin_space_before_when_parentheses = true
ij_kotlin_space_before_while_parentheses = true
ij_kotlin_spaces_around_additive_operators = true
ij_kotlin_spaces_around_assignment_operators = true
ij_kotlin_spaces_around_equality_operators = true
ij_kotlin_spaces_around_function_type_arrow = true
ij_kotlin_spaces_around_logical_operators = true
ij_kotlin_spaces_around_multiplicative_operators = true
ij_kotlin_spaces_around_range = false
ij_kotlin_spaces_around_relational_operators = true
ij_kotlin_spaces_around_unary_operator = false
ij_kotlin_spaces_around_when_arrow = true
ij_kotlin_variable_annotation_wrap = off
ij_kotlin_while_on_new_line = false
ij_kotlin_wrap_elvis_expressions = 1
ij_kotlin_wrap_expression_body_functions = 1
ij_kotlin_wrap_first_method_in_call_chain = false
+3
View File
@@ -3,3 +3,6 @@
e7f4c8a7ebe4e907f65af46e6dddfd3a542415c0
ab2713154f846b3db6d44625d1022bf421b275d0
cbdfa67d075ec1f8ceec0f3559498cbca01783a0
# Renamed Renovate config file
f304304bc5b64edf189aaa473b05df912067a8bd
+1
View File
@@ -1,3 +1,4 @@
* text=auto eol=lf
**/dependencies/** linguist-generated
**/clawicons/** linguist-generated
android/src/main/baseline-prof.txt linguist-generated -diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

@@ -6,14 +6,6 @@
"github>msfjarvis/shared-workflows//renovate/automerge"
],
"packageRules": [
{
"matchPackagePatterns": [
"^com.android.tools.build",
"^com.android.test",
"^gradle"
],
"enabled": false
},
{
"matchPackagePatterns": [
"^kotlin",
@@ -27,21 +19,13 @@
"gradle"
],
"packagePatterns": [
"^com.squareup.okhttp3"
// We want to stay in sync with Retrofit
"^com.squareup.okhttp3",
// Renovate refuses to stop resolving this from the snapshots repo
"^app.cash.sqldelight",
],
"enabled": false
},
{
"matchDatasources": [
"maven"
],
"matchPackageNames": [
"androidx.compose.compiler:compiler"
],
"registryUrls": [
"https://maven.google.com/"
]
}
],
"regexManagers": [
{
@@ -73,6 +57,16 @@
],
"datasourceTemplate": "maven",
"depNameTemplate": "androidx.compose.compiler:compiler"
},
{
"fileMatch": [
"qodana.yaml"
],
"matchStrings": [
"jetbrains/qodana-jvm-android:(?<currentValue>.*)"
],
"datasourceTemplate": "docker",
"depNameTemplate": "jetbrains/qodana-jvm-android",
}
]
}
+124
View File
@@ -0,0 +1,124 @@
name: Baseline profile generation
on:
# every day at 00:43
schedule:
- cron: '43 0 * * *'
workflow_dispatch:
env:
AVD_API_LEVEL: 31
AVD_ARCH: x86_64
jobs:
baseline-profile:
runs-on: macos-latest
timeout-minutes: 45
env:
TERM: dumb
steps:
- name: Checkout repository
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
with:
token: ${{ secrets.POST_RELEASE_GH_TOKEN }}
fetch-depth: 0
- name: Set up JDK
uses: actions/setup-java@1df8dbefe2a8cbc99770194893dd902763bee34b # v3.9.0
with:
distribution: temurin
java-version: 18
- name: Set up Git author
shell: bash
run: |
git config user.name "GitHub Actions"
git config user.email noreply@github.com
- name: Setup Gradle caching
uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2.3.3
with:
gradle-home-cache-cleanup: true
# Restore AVD from cache
- name: Cache AVD
uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12 # v3.2.3
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: ${{ runner.os }}-avd-${{ env.AVD_API_LEVEL }}-${{ env.AVD_ARCH }}
- name: Decrypt secrets
run: |
./scripts/setup-age.sh
./scripts/signing-setup.sh "$ENCRYPT_KEY"
env:
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
# This allows us to build most of what we need without the emulator running
# and using resources
- name: Build app and benchmark
run: ./gradlew :benchmark:assembleBenchmark :android:assembleBenchmark
# Now use reactivecircus/android-emulator-runner to spin up an emulator. We're gonna use it again
# to actually run the test once the emulator has booted.
- name: Create AVD snapshot for caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@50986b1464923454c95e261820bc626f38490ec0 # v2
with:
api-level: ${{ env.AVD_API_LEVEL }}
arch: ${{ env.AVD_ARCH }}
channel: canary
target: aosp_atd
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: false
disk-size: 8G
script: echo "Generated AVD snapshot for caching."
# Run our baseline profile generator. We need to manually pull the baseline profiles off the
# emulator when using the GA runner
- name: Run benchmark
uses: reactivecircus/android-emulator-runner@50986b1464923454c95e261820bc626f38490ec0 # v2
with:
api-level: ${{ env.AVD_API_LEVEL }}
arch: ${{ env.AVD_ARCH }}
channel: canary
target: aosp_atd
disable-animations: true
disable-spellchecker: true
disk-size: 8G
script: |
# Run our benchmark, enabling only tests using BaselineProfile
./gradlew connectedBenchmarkAndroidTest -Pandroid.testInstrumentationRunnerArguments.androidx.benchmark.enabledRules=BaselineProfile --stacktrace
# Need to manually pull the generated profiles from the emulator
adb pull /sdcard/Android/media/dev.msfjarvis.claw.benchmark benchmark/build/outputs/baseline-prof/
# If we're on main branch, copy over the baseline profile and
# commit it to the repository (if changed)
- name: Commit baseline profile into main
if: github.ref == 'refs/heads/main'
run: |
# Pull down any changes which may have been committed while this workflow has been running
git pull
# Sort the baseline profile, saving it to app/
sort -o android/src/main/baseline-prof.txt benchmark/build/outputs/baseline-prof/BaselineProfileGenerator_startup-baseline-prof.txt
# If the baseline profile has changed, commit it
if [[ $(git diff --stat android/src/main/baseline-prof.txt) != '' ]]; then
git add android/src/main/baseline-prof.txt
git commit -m "chore(android): refresh baseline profile" && git push
fi
# Upload the entire generated folder and attach it to the CI run
- name: Attach baseline profile
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: Baseline profile output
path: benchmark/build/outputs/baseline-prof
- name: Clean secrets
if: always()
run: scripts/signing-cleanup.sh
+11 -6
View File
@@ -10,15 +10,18 @@ on:
branches:
- main
concurrency:
group: ${{ github.workflow }}
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3.1.0
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- name: Set up JDK
uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3.6.0
uses: actions/setup-java@1df8dbefe2a8cbc99770194893dd902763bee34b # v3.9.0
with:
distribution: temurin
java-version: 18
@@ -39,7 +42,7 @@ jobs:
- name: (Fail-only) Upload test report
if: failure()
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3.1.1
uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
with:
name: Test report
path: android/build/reports
@@ -50,16 +53,18 @@ jobs:
needs: [ "check" ]
steps:
- name: Checkout repository
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3.1.0
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- name: Set up JDK
uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3.6.0
uses: actions/setup-java@1df8dbefe2a8cbc99770194893dd902763bee34b # v3.9.0
with:
distribution: temurin
java-version: 18
- name: Decrypt secrets
run: scripts/signing-setup.sh "$ENCRYPT_KEY"
run: |
./scripts/setup-age.sh
./scripts/signing-setup.sh "$ENCRYPT_KEY"
env:
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
+26
View File
@@ -0,0 +1,26 @@
name: "CodeQL"
on:
workflow_dispatch:
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3
- name: Initialize CodeQL
uses: github/codeql-action/init@a34ca99b4610d924e04c68db79e503e1f79f9f02 # v2
with:
languages: java
- name: Autobuild
uses: github/codeql-action/autobuild@a34ca99b4610d924e04c68db79e503e1f79f9f02 # v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@a34ca99b4610d924e04c68db79e503e1f79f9f02 # v2
with:
category: "/language:java"
+16
View File
@@ -0,0 +1,16 @@
name: Qodana
on:
workflow_dispatch:
jobs:
qodana:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3
with:
fetch-depth: 0
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@3fdeefa830a7634a5c04f1cd70fcfb69956137a2 # v2022.3.0
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
+8 -5
View File
@@ -7,13 +7,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3.1.0
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
with:
token: ${{ secrets.POST_RELEASE_GH_TOKEN }}
fetch-depth: 0
- name: Set up JDK
uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3.6.0
uses: actions/setup-java@1df8dbefe2a8cbc99770194893dd902763bee34b # v3.9.0
with:
distribution: temurin
java-version: 18
@@ -31,7 +31,9 @@ jobs:
cache-read-only: true
- name: Decrypt secrets
run: scripts/signing-setup.sh "$ENCRYPT_KEY"
run: |
./scripts/setup-age.sh
./scripts/signing-setup.sh "$ENCRYPT_KEY"
env:
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
@@ -45,7 +47,8 @@ jobs:
- name: Update changelog
uses: thomaseizinger/keep-a-changelog-new-release@5bc232893483441c5d6cd6c9dcb1e48abf9a2bae # 1.3.0
with:
tag: ${{ env.VERSION }}
tag: v${{ env.VERSION }}
version: ${{ env.VERSION }}
- name: Commit changes
shell: bash
@@ -60,7 +63,7 @@ jobs:
- name: Clean secrets
run: scripts/signing-cleanup.sh
- name: Get Changelog Entry
- name: Get changelog entry
id: changelog_reader
uses: mindsers/changelog-reader-action@b97ce03a10d9bdbb07beb491c76a5a01d78cd3ef # v2.2.2
with:
+89 -2
View File
@@ -7,6 +7,75 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [1.18.0] - 2023-01-21
### Changed
- Special-case deleted stories in comments API
- Fix regression where save button had the incorrect visual state
## [1.17.0] - 2023-01-12
### Added
- The app will now mark new comments as unread when returning to posts
### Changed
- Upgrade to Kotlin 1.8.0
- Revert questionable app bar scrolling behaviour change
## [1.16.0] - 2022-12-24
### Changed
- Fix bug where newest posts tab actually showed hottest posts instead
## [1.15.0] - 2022-12-20
### Changed
- Fix a case where collapsing the parent of an already collapsed comment caused it to become expanded
- Tweak progress bar UI to use a linear indicator
- Allow app bar to hide when scrolling down
- Fix bug where refreshing a list of posts caused it to jump around multiple times
## [1.14.0] - 2022-12-12
### Changed
- Rework how often saved posts are updated
- Collapsing a comment now collapses all comments under it similar to how it works on the website
## [1.13.0] - 2022-12-10
### Changed
- Fix crash when viewing [jcs](https://lobste.rs/u/jcs)' comments
## [1.12.0] - 2022-12-09
### Changed
- Do not schedule post update job every time the app starts
- Improve favicon loading to reduce unnecessary redraws
- Fix post lists being reloaded unnecessarily
## [1.11.0] - 2022-12-07
### Changed
- Upgrade to Compose 2022.12.00 release
- Refresh packaged baseline profile
- Improve how saved state of posts is queried
## [1.10.0] - 2022-11-30
### Changed
- Configure Coil image loader with better caching settings
- Refresh packaged baseline profile
## [1.9.0] - 2022-11-28
### Changed
@@ -81,9 +150,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Initial Play Store release
[Unreleased]: https://github.com/msfjarvis/compose-lobsters/compare/1.9.0...HEAD
[Unreleased]: https://github.com/msfjarvis/compose-lobsters/compare/v1.18.0...HEAD
[1.9.0]: https://github.com/msfjarvis/compose-lobsters/compare/1.8.0...1.9.0
[1.18.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.17.0...v1.18.0
[1.17.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.16.0...v1.17.0
[1.16.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.15.0...v1.16.0
[1.15.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.14.0...v1.15.0
[1.14.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.13.0...v1.14.0
[1.13.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.12.0...v1.13.0
[1.12.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.11.0...v1.12.0
[1.11.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.10.0...v1.11.0
[1.10.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.9.0...v1.10.0
[1.9.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.8.0...v1.9.0
[1.8.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.7.0...1.8.0
+4 -4
View File
@@ -2,16 +2,16 @@
Unofficial Android app for read-only access to [lobste.rs](https://lobste.rs), built with [Jetpack Compose](https://developer.android.com/jetpack/compose).
![Screenshots](./.github/readme_feature.png)
## Download
<a href="https://play.google.com/store/apps/details?id=dev.msfjarvis.claw.android">
<img src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png"
alt="Get it on Google Play"
height="80" />
</a>
<img src="https://github.com/msfjarvis/compose-lobsters/blob/main/.github/readme_feature.webp"
alt="Side by side screenshots of the app's main page in dark and light UI modes"
height="550" />
Snapshots from the development branch can be obtained [here](https://github.com/msfjarvis/compose-lobsters/releases/tag/nightly).
## License
+1
View File
@@ -0,0 +1 @@
-dontobfuscate
+4 -2
View File
@@ -33,9 +33,10 @@ android {
}
buildTypes {
create("benchmark") {
signingConfig = signingConfigs.getByName("debug")
matchingFallbacks += listOf("release")
signingConfig = signingConfigs["debug"]
matchingFallbacks += "release"
isDebuggable = false
proguardFile("benchmark-rules.pro")
}
}
}
@@ -59,6 +60,7 @@ dependencies {
implementation(libs.androidx.lifecycle.compose)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.paging.compose)
implementation(libs.androidx.profileinstaller)
implementation(libs.androidx.work.runtime.ktx)
implementation(libs.coil)
implementation(libs.copydown)
+13
View File
@@ -149,6 +149,19 @@
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
<meta-data
android:name="com.deliveryhero.whetstone.worker.WhetstoneWorkerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
<!-- https://issuetracker.google.com/issues/258619948 -->
<receiver
android:name="androidx.profileinstaller.ProfileInstallReceiver"
android:permission="android.permission.DUMP"
android:exported="true">
<intent-filter>
<action android:name="androidx.profileinstaller.action.BENCHMARK_OPERATION" />
</intent-filter>
</receiver>
</application>
</manifest>
+18827 -4760
View File
File diff suppressed because it is too large Load Diff
@@ -7,10 +7,17 @@
package dev.msfjarvis.claw.android
import android.app.Application
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import com.deliveryhero.whetstone.Whetstone
import com.deliveryhero.whetstone.app.ApplicationComponentOwner
import com.deliveryhero.whetstone.app.ContributesAppInjector
import dev.msfjarvis.claw.android.work.SavedPostUpdaterWorker
import dev.msfjarvis.claw.core.injection.AppPlugin
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ContributesAppInjector(generateAppComponent = true)
@@ -25,5 +32,26 @@ class ClawApplication : Application(), ApplicationComponentOwner {
Whetstone.inject(this)
super.onCreate()
plugins.forEach { plugin -> plugin.apply(this) }
val postUpdateWorkRequest =
PeriodicWorkRequestBuilder<SavedPostUpdaterWorker>(POST_REFRESH_PERIOD, TimeUnit.HOURS)
.setConstraints(
Constraints.Builder()
.setRequiresCharging(false)
.setRequiresBatteryNotLow(true)
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresDeviceIdle(true)
.build()
)
.build()
WorkManager.getInstance(this)
.enqueueUniquePeriodicWork(
"updateSavedPosts",
ExistingPeriodicWorkPolicy.UPDATE,
postUpdateWorkRequest,
)
}
private companion object {
private const val POST_REFRESH_PERIOD = 24L // 24 hours
}
}
@@ -12,18 +12,11 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import com.deliveryhero.whetstone.Whetstone
import com.deliveryhero.whetstone.activity.ContributesActivityInjector
import dev.msfjarvis.claw.android.ui.LobstersApp
import dev.msfjarvis.claw.android.work.SavedPostUpdaterWorker
import dev.msfjarvis.claw.common.comments.HTMLConverter
import dev.msfjarvis.claw.common.urllauncher.UrlLauncher
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ContributesActivityInjector
@@ -44,23 +37,6 @@ class MainActivity : ComponentActivity() {
setWebUri = { url -> webUri = url },
)
}
val postUpdateWorkRequest =
PeriodicWorkRequestBuilder<SavedPostUpdaterWorker>(POST_REFRESH_PERIOD, TimeUnit.HOURS)
.setConstraints(
Constraints.Builder()
.setRequiresCharging(false)
.setRequiresBatteryNotLow(true)
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresDeviceIdle(true)
.build()
)
.build()
WorkManager.getInstance(this@MainActivity)
.enqueueUniquePeriodicWork(
"updateSavedPosts",
ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
postUpdateWorkRequest,
)
}
override fun onProvideAssistContent(outContent: AssistContent?) {
@@ -73,8 +49,4 @@ class MainActivity : ComponentActivity() {
}
}
}
private companion object {
private const val POST_REFRESH_PERIOD = 24L // 24 hours
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2021 Harsh Shandilya.
* Copyright © 2021-2022 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
@@ -8,22 +8,13 @@ package dev.msfjarvis.claw.android.injection
import com.deliveryhero.whetstone.app.ApplicationScope
import com.squareup.anvil.annotations.ContributesTo
import dagger.Binds
import dagger.Module
import dagger.Provides
import dev.msfjarvis.claw.android.ui.util.HTMLConverterImpl
import dev.msfjarvis.claw.common.comments.HTMLConverter
import io.github.furstenheim.CopyDown
@Module
@ContributesTo(ApplicationScope::class)
object HTMLConverterModule {
@Provides
fun provideHTMLConverter() =
object : HTMLConverter {
private val copydown = CopyDown()
override fun convertHTMLToMarkdown(html: String): String {
return copydown.convert(html)
}
}
interface HTMLConverterModule {
@Binds fun HTMLConverterImpl.bind(): HTMLConverter
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2021 Harsh Shandilya.
* Copyright © 2021-2022 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
@@ -0,0 +1,34 @@
/*
* Copyright © 2022 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.android.injection
import android.app.Application
import android.util.Log
import androidx.work.Configuration
import androidx.work.WorkManager
import androidx.work.WorkerFactory
import com.deliveryhero.whetstone.app.ApplicationScope
import com.squareup.anvil.annotations.ContributesMultibinding
import dev.msfjarvis.claw.core.injection.AppPlugin
import javax.inject.Inject
@ContributesMultibinding(ApplicationScope::class)
class WorkManagerPlugin
@Inject
constructor(
private val workerFactory: WorkerFactory,
) : AppPlugin {
override fun apply(application: Application) {
WorkManager.initialize(
application,
Configuration.Builder()
.setWorkerFactory(workerFactory)
.setMinimumLoggingLevel(Log.DEBUG)
.build()
)
}
}
@@ -27,13 +27,14 @@ constructor(
) : PagingSource<Int, LobstersPost>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, LobstersPost> {
val page = params.key ?: 1
val page = params.key ?: STARTING_PAGE_INDEX
return when (val result = withContext(ioDispatcher) { remoteFetcher.getItemsAtPage(page) }) {
is Success ->
LoadResult.Page(
itemsBefore = (page - 1) * PAGE_SIZE,
data = result.value,
prevKey = if (page == 1) null else page - 1,
nextKey = page.plus(1)
prevKey = if (page == STARTING_PAGE_INDEX) null else page - 1,
nextKey = page + 1,
)
is Failure.NetworkFailure -> LoadResult.Error(result.error)
is Failure.UnknownFailure -> LoadResult.Error(result.error)
@@ -42,12 +43,19 @@ constructor(
}
}
override fun getRefreshKey(state: PagingState<Int, LobstersPost>): Int {
return state.pages.size + 1
override fun getRefreshKey(state: PagingState<Int, LobstersPost>): Int? {
return state.anchorPosition?.let { anchorPosition ->
(anchorPosition / PAGE_SIZE).coerceAtLeast(STARTING_PAGE_INDEX)
}
}
@AssistedFactory
interface Factory {
fun create(remoteFetcher: RemoteFetcher<LobstersPost>): LobstersPagingSource
}
companion object {
const val PAGE_SIZE = 25
const val STARTING_PAGE_INDEX = 1
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2021-2022 Harsh Shandilya.
* Copyright © 2021-2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
@@ -20,11 +20,14 @@ import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.compose.ui.text.font.FontWeight
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
@@ -54,7 +57,10 @@ import dev.msfjarvis.claw.common.urllauncher.UrlLauncher
import dev.msfjarvis.claw.common.user.UserProfile
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@OptIn(
ExperimentalComposeUiApi::class,
ExperimentalMaterial3Api::class,
)
@Composable
fun LobstersApp(
urlLauncher: UrlLauncher,
@@ -156,7 +162,7 @@ fun LobstersApp(
isVisible = navItems.any { it.route == currentDestination },
)
},
modifier = modifier,
modifier = modifier.semantics { testTagsAsResourceId = true },
) { paddingValues ->
NavHost(
navController = navController,
@@ -174,7 +180,6 @@ fun LobstersApp(
lazyPagingItems = hottestPosts,
listState = hottestListState,
isPostSaved = viewModel::isPostSaved,
reloadPosts = viewModel::refreshHottestPosts,
postActions = postActions,
)
}
@@ -186,7 +191,6 @@ fun LobstersApp(
lazyPagingItems = newestPosts,
listState = newestListState,
isPostSaved = viewModel::isPostSaved,
reloadPosts = viewModel::refreshNewestPosts,
postActions = postActions,
)
}
@@ -213,6 +217,8 @@ fun LobstersApp(
postId = postId,
postActions = postActions,
htmlConverter = htmlConverter,
getSeenComments = viewModel::getSeenComments,
markSeenComments = viewModel::markSeenComments,
)
}
composable(
@@ -20,6 +20,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.testTag
import androidx.navigation.NavController
import dev.msfjarvis.claw.android.ui.navigation.Destinations
@@ -73,7 +74,8 @@ fun ClawNavigationBar(
if (navItem.route != Destinations.startDestination.route) {
navController.navigate(navItem.route)
}
}
},
modifier = Modifier.testTag(navItem.label.uppercase()),
)
}
}
@@ -32,7 +32,7 @@ fun DatabasePosts(
) {
items.forEach { (month, posts) ->
stickyHeader { MonthHeader(month = month) }
items(posts) { item ->
items(posts, { it.shortId }) { item ->
ListItem(
item = item,
isSaved = { true },
@@ -40,13 +40,12 @@ fun NetworkPosts(
lazyPagingItems: LazyPagingItems<LobstersPost>,
listState: LazyListState,
isPostSaved: suspend (SavedPost) -> Boolean,
reloadPosts: () -> Unit,
postActions: PostActions,
modifier: Modifier = Modifier,
) {
val refreshLoadState = lazyPagingItems.loadState.refresh
val isRefreshing = refreshLoadState == LoadState.Loading
val pullRefreshState = rememberPullRefreshState(isRefreshing, reloadPosts)
val pullRefreshState = rememberPullRefreshState(isRefreshing, lazyPagingItems::refresh)
Box(modifier = modifier.fillMaxSize().pullRefresh(pullRefreshState)) {
if (lazyPagingItems.itemCount == 0 && refreshLoadState is LoadState.Error) {
NetworkError(
@@ -58,7 +57,7 @@ fun NetworkPosts(
LazyColumn(
state = listState,
) {
items(lazyPagingItems) { item ->
items(items = lazyPagingItems, key = { it.shortId }) { item ->
if (item != null) {
val dbModel = item.toDbModel()
ListItem(
@@ -71,12 +70,12 @@ fun NetworkPosts(
}
}
if (lazyPagingItems.loadState.append == LoadState.Loading) {
item {
item(key = "progressbar") {
ProgressBar(
modifier =
Modifier.fillMaxWidth()
.wrapContentWidth(Alignment.CenterHorizontally)
.padding(top = 4.dp),
.padding(vertical = 16.dp),
)
}
}
@@ -0,0 +1,21 @@
/*
* Copyright © 2022 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.android.ui.util
import androidx.compose.runtime.Stable
import dev.msfjarvis.claw.common.comments.HTMLConverter
import io.github.furstenheim.CopyDown
import javax.inject.Inject
@Stable
class HTMLConverterImpl @Inject constructor() : HTMLConverter {
private val copydown = CopyDown()
override fun convertHTMLToMarkdown(html: String): String {
return copydown.convert(html)
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2021-2022 Harsh Shandilya.
* Copyright © 2021-2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
@@ -15,10 +15,14 @@ import com.slack.eithernet.ApiResult.Failure
import com.slack.eithernet.ApiResult.Success
import dev.msfjarvis.claw.android.injection.IODispatcher
import dev.msfjarvis.claw.android.paging.LobstersPagingSource
import dev.msfjarvis.claw.android.paging.LobstersPagingSource.Companion.PAGE_SIZE
import dev.msfjarvis.claw.android.paging.LobstersPagingSource.Companion.STARTING_PAGE_INDEX
import dev.msfjarvis.claw.android.ui.toLocalDateTime
import dev.msfjarvis.claw.api.LobstersApi
import dev.msfjarvis.claw.database.local.SavedPost
import dev.msfjarvis.claw.model.Comment
import java.io.IOException
import java.net.HttpURLConnection
import java.time.Month
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -37,15 +41,13 @@ constructor(
private val pagingSourceFactory: LobstersPagingSource.Factory,
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
) : ViewModel() {
private var hottestPostsPagingSource: LobstersPagingSource? = null
private var newestPostsPagingSource: LobstersPagingSource? = null
private val hottestPostsPager =
Pager(PagingConfig(20)) {
pagingSourceFactory.create(api::getHottestPosts).also { hottestPostsPagingSource = it }
Pager(PagingConfig(pageSize = PAGE_SIZE), initialKey = STARTING_PAGE_INDEX) {
pagingSourceFactory.create(api::getHottestPosts)
}
private val newestPostsPager =
Pager(PagingConfig(20)) {
pagingSourceFactory.create(api::getHottestPosts).also { newestPostsPagingSource = it }
Pager(PagingConfig(pageSize = PAGE_SIZE), initialKey = STARTING_PAGE_INDEX) {
pagingSourceFactory.create(api::getNewestPosts)
}
val hottestPosts
@@ -86,11 +88,23 @@ constructor(
is Success -> result.value
is Failure.NetworkFailure -> throw result.error
is Failure.UnknownFailure -> throw result.error
is Failure.HttpFailure,
is Failure.HttpFailure -> {
if (result.code == HttpURLConnection.HTTP_NOT_FOUND) {
throw IOException("Story was removed by moderator")
} else {
throw IOException("API returned an invalid response")
}
}
is Failure.ApiFailure -> throw IOException("API returned an invalid response")
}
}
suspend fun getSeenComments(postId: String) = savedPostsRepository.getSeenComments(postId)
fun markSeenComments(postId: String, comments: List<Comment>) {
viewModelScope.launch { savedPostsRepository.markSeenComments(postId, comments) }
}
suspend fun getLinkMetadata(url: String) =
withContext(ioDispatcher) { linkMetadataRepository.getLinkMetadata(url) }
@@ -104,12 +118,4 @@ constructor(
is Failure.ApiFailure -> throw IOException("API returned an invalid response")
}
}
fun refreshHottestPosts() {
hottestPostsPagingSource?.invalidate()
}
fun refreshNewestPosts() {
newestPostsPagingSource?.invalidate()
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2021-2022 Harsh Shandilya.
* Copyright © 2021-2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
@@ -10,7 +10,9 @@ import app.cash.sqldelight.coroutines.asFlow
import app.cash.sqldelight.coroutines.mapToList
import dev.msfjarvis.claw.android.injection.DatabaseDispatcher
import dev.msfjarvis.claw.database.LobstersDatabase
import dev.msfjarvis.claw.database.local.PostComments
import dev.msfjarvis.claw.database.local.SavedPost
import dev.msfjarvis.claw.model.Comment
import io.github.aakira.napier.Napier
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -23,6 +25,7 @@ constructor(
@DatabaseDispatcher private val dbDispatcher: CoroutineDispatcher,
) {
private val savedPostQueries = database.savedPostQueries
private val postCommentsQueries = database.postCommentsQueries
val savedPosts = savedPostQueries.selectAllPosts().asFlow().mapToList(dbDispatcher)
suspend fun savePost(post: SavedPost) {
@@ -30,11 +33,29 @@ constructor(
withContext(dbDispatcher) { savedPostQueries.insertOrReplacePost(post) }
}
suspend fun savePosts(posts: List<SavedPost>) {
Napier.d(tag = TAG) { "Saving posts: ${posts.joinToString(",") { it.shortId }}" }
withContext(dbDispatcher) {
savedPostQueries.transaction {
posts.forEach { post -> savedPostQueries.insertOrReplacePost(post) }
}
}
}
suspend fun removePost(post: SavedPost) {
Napier.d(tag = TAG) { "Removing post: ${post.shortId}" }
withContext(dbDispatcher) { savedPostQueries.deletePost(post.shortId) }
}
suspend fun getSeenComments(postId: String) =
withContext(dbDispatcher) { postCommentsQueries.getCommentIds(postId).executeAsOneOrNull() }
suspend fun markSeenComments(postId: String, comments: List<Comment>) {
withContext(dbDispatcher) {
postCommentsQueries.rememberComments(PostComments(postId, comments.map { it.shortId }))
}
}
private companion object {
private const val TAG = "SavedPostsRepository"
}
@@ -12,15 +12,13 @@ import androidx.work.WorkerParameters
import com.deliveryhero.whetstone.ForScope
import com.deliveryhero.whetstone.worker.ContributesWorker
import com.deliveryhero.whetstone.worker.WorkerScope
import com.slack.eithernet.ApiResult
import com.slack.eithernet.ApiResult.Success
import dev.msfjarvis.claw.android.viewmodel.SavedPostsRepository
import dev.msfjarvis.claw.api.LobstersApi
import dev.msfjarvis.claw.common.posts.toDbModel
import dev.msfjarvis.claw.model.LobstersPostDetails
import javax.inject.Inject
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.supervisorScope
/**
* WorkManager-backed [CoroutineWorker] that gets all the posts from [SavedPostsRepository], fetches
@@ -38,19 +36,13 @@ constructor(
private val lobstersApi: LobstersApi,
) : CoroutineWorker(appContext, workerParams) {
override suspend fun doWork(): Result {
val posts = savedPostsRepository.savedPosts.first()
supervisorScope {
posts
.map { post ->
async {
val details = lobstersApi.getPostDetails(post.shortId)
if (details is ApiResult.Success) {
savedPostsRepository.savePost(details.value.toDbModel())
}
}
}
.awaitAll()
}
savedPostsRepository.savedPosts
.first()
.map { post -> lobstersApi.getPostDetails(post.shortId) }
.filterIsInstance<Success<LobstersPostDetails>>()
.map { result -> result.value.toDbModel() }
.toList()
.let { savedPostsRepository.savePosts(it) }
return Result.success()
}
}
+2 -2
View File
@@ -1,5 +1,5 @@
#
# This file was automatically generated by 'versioning-plugin'. DO NOT EDIT MANUALLY.
#
versioning-plugin.versionCode=10900
versioning-plugin.versionName=1.9.0
versioning-plugin.versionCode=11800
versioning-plugin.versionName=1.18.0
+3 -4
View File
@@ -7,8 +7,7 @@
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
plugins {
kotlin("android")
id("dev.msfjarvis.claw.kotlin-common")
id("dev.msfjarvis.claw.kotlin-android")
id("dev.msfjarvis.claw.android-library")
alias(libs.plugins.anvil)
alias(libs.plugins.whetstone)
@@ -25,8 +24,8 @@ dependencies {
implementation(libs.dagger)
implementation(libs.javax.inject)
testImplementation(testFixtures(libs.eithernet))
testImplementation(kotlin("test-junit"))
testImplementation(libs.kotlinx.coroutines.core)
testImplementation(libs.kotest.assertions.core)
testImplementation(libs.kotest.runner.junit5)
testImplementation(libs.kotlinx.serialization.json)
testImplementation(libs.retrofit.kotlinxSerializationConverter)
}
@@ -11,42 +11,40 @@ import com.slack.eithernet.test.newEitherNetController
import dev.msfjarvis.claw.model.LobstersPost
import dev.msfjarvis.claw.model.LobstersPostDetails
import dev.msfjarvis.claw.model.User
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlinx.coroutines.runBlocking
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeTypeOf
class ApiTest {
class ApiTest : FunSpec() {
private val wrapper = ApiWrapper(newEitherNetController())
private val api
get() = wrapper.api
@Test
fun `api gets correct number of items`() = runBlocking {
val posts = api.getHottestPosts(1)
assertIs<Success<List<LobstersPost>>>(posts)
assertEquals(25, posts.value.size)
}
init {
test("api gets correct number of items") {
val posts = api.getHottestPosts(1)
posts.shouldBeTypeOf<Success<List<LobstersPost>>>()
posts.value shouldHaveSize 25
}
@Test
fun `posts with no urls`() = runBlocking {
val posts = api.getHottestPosts(1)
assertIs<Success<List<LobstersPost>>>(posts)
val commentsOnlyPosts = posts.value.asSequence().filter { it.url.isEmpty() }.toSet()
assertEquals(2, commentsOnlyPosts.size)
}
test("posts with no urls") {
val posts = api.getHottestPosts(1)
posts.shouldBeTypeOf<Success<List<LobstersPost>>>()
val commentsOnlyPosts = posts.value.asSequence().filter { it.url.isEmpty() }.toSet()
commentsOnlyPosts shouldHaveSize 2
}
@Test
fun `post details with comments`() = runBlocking {
val postDetails = api.getPostDetails("tdfoqh")
assertIs<Success<LobstersPostDetails>>(postDetails)
assertEquals(7, postDetails.value.comments.size)
}
test("post details with comments") {
val postDetails = api.getPostDetails("tdfoqh")
postDetails.shouldBeTypeOf<Success<LobstersPostDetails>>()
postDetails.value.comments shouldHaveSize 7
}
@Test
fun `get user details`() = runBlocking {
val user = api.getUser("msfjarvis")
assertIs<Success<User>>(user)
assertEquals("msfjarvis", user.value.username)
test("get user details") {
val user = api.getUser("msfjarvis")
user.shouldBeTypeOf<Success<User>>()
user.value.username shouldBe "msfjarvis"
}
}
}
@@ -27,7 +27,7 @@ class ApiWrapper(controller: EitherNetController<LobstersApi>) {
init {
controller.enqueue(LobstersApi::getHottestPosts) { success(hottest) }
controller.enqueue(LobstersApi::getNewestPosts) { success(hottest) }
controller.enqueue(LobstersApi::getHottestPosts) { success(hottest) }
controller.enqueue(LobstersApi::getPostDetails) { success(postDetails) }
controller.enqueue(LobstersApi::getUser) { success(user) }
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2021 Harsh Shandilya.
* Copyright © 2021-2022 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
+2 -2
View File
@@ -20,8 +20,8 @@ android {
buildTypes {
create("benchmark") {
isDebuggable = true
signingConfig = getByName("debug").signingConfig
matchingFallbacks += listOf("release")
signingConfig = signingConfigs["debug"]
matchingFallbacks += "release"
}
}
@@ -11,14 +11,12 @@ import androidx.benchmark.macro.CompilationMode
import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.StartupTimingMetric
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.filters.LargeTest
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@LargeTest
class BaselineProfileBenchmark {
@get:Rule val benchmarkRule = MacrobenchmarkRule()
private lateinit var device: UiDevice
@@ -52,7 +50,7 @@ class BaselineProfileBenchmark {
startupMode = StartupMode.COLD,
compilationMode = compilationMode
) {
exploreUI(device)
startActivityAndWait()
}
}
}
@@ -6,15 +6,15 @@
*/
package dev.msfjarvis.claw.benchmark
import androidx.benchmark.macro.ExperimentalStableBaselineProfilesApi
import androidx.benchmark.macro.junit4.BaselineProfileRule
import androidx.test.filters.MediumTest
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@MediumTest
@OptIn(ExperimentalStableBaselineProfilesApi::class)
class BaselineProfileGenerator {
@get:Rule val baselineProfileRule = BaselineProfileRule()
private lateinit var device: UiDevice
@@ -27,5 +27,10 @@ class BaselineProfileGenerator {
@Test
fun startup() =
baselineProfileRule.collectBaselineProfile(packageName = PACKAGE_NAME) { exploreUI(device) }
baselineProfileRule.collectStableBaselineProfile(
packageName = PACKAGE_NAME,
maxIterations = 5,
) {
startActivityAndWait()
}
}
@@ -9,19 +9,52 @@ package dev.msfjarvis.claw.benchmark
import androidx.benchmark.macro.MacrobenchmarkScope
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
const val PACKAGE_NAME = "dev.msfjarvis.claw.android"
private const val AWAIT_TIMEOUT = 10_000L
private const val SAVE_BUTTON_DESC = "Add to saved posts"
private const val NAV_ID_HOTTEST = "HOTTEST"
private const val NAV_ID_NEWEST = "NEWEST"
private const val NAV_ID_SAVED = "SAVED"
private const val COMMENT_BUTTON_DESC = "Open comments"
@Suppress("Unused")
fun MacrobenchmarkScope.exploreUI(device: UiDevice) {
startActivityAndWait()
device.run {
listOf("HOTTEST", "NEWEST", "SAVED").forEach { desc ->
findObject(By.desc(desc)).click()
waitForIdle()
}
findObject(By.desc("HOTTEST")).click()
waitForIdle()
findObjects(By.desc("Open comments")).first().click()
waitForIdle()
savePosts()
exploreScreens()
returnToHottestScreen()
openCommentsScreen()
}
}
private fun UiDevice.waitForSubmitterName() {
wait(Until.hasObject(By.textContains("Submitted by")), AWAIT_TIMEOUT)
}
private fun UiDevice.savePosts() {
waitForSubmitterName()
findObjects(By.desc(SAVE_BUTTON_DESC)).forEach { btn -> btn.click() }
}
private fun UiDevice.exploreScreens() {
listOf(NAV_ID_HOTTEST, NAV_ID_NEWEST, NAV_ID_SAVED).forEach { tag ->
findObject(By.res(tag)).click()
waitForSubmitterName()
}
}
private fun UiDevice.returnToHottestScreen() {
findObject(By.res(NAV_ID_HOTTEST)).click()
waitForSubmitterName()
}
private fun UiDevice.openCommentsScreen() {
waitForSubmitterName()
findObjects(By.desc(COMMENT_BUTTON_DESC)).first().click()
}
-1
View File
@@ -1 +0,0 @@
/build
@@ -1,57 +0,0 @@
/*
* Copyright © 2022 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
import org.gradle.api.JavaVersion
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { `kotlin-dsl` }
afterEvaluate {
tasks.withType<JavaCompile>().configureEach {
sourceCompatibility = JavaVersion.VERSION_11.toString()
targetCompatibility = JavaVersion.VERSION_11.toString()
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
freeCompilerArgs = freeCompilerArgs + "-Xsam-conversions=class"
}
}
}
gradlePlugin {
plugins {
register("android-application") {
id = "dev.msfjarvis.claw.android-application"
implementationClass = "dev.msfjarvis.aps.gradle.ApplicationPlugin"
}
register("android-common") {
id = "dev.msfjarvis.claw.android-common"
implementationClass = "dev.msfjarvis.aps.gradle.AndroidCommonPlugin"
}
register("android-library") {
id = "dev.msfjarvis.claw.android-library"
implementationClass = "dev.msfjarvis.aps.gradle.LibraryPlugin"
}
register("rename-artifacts") {
id = "dev.msfjarvis.claw.rename-artifacts"
implementationClass = "dev.msfjarvis.aps.gradle.RenameArtifactsPlugin"
}
register("versioning") {
id = "dev.msfjarvis.claw.versioning-plugin"
implementationClass = "dev.msfjarvis.aps.gradle.versioning.VersioningPlugin"
}
}
}
dependencies {
implementation(libs.build.agp)
implementation(libs.build.cachefix)
implementation(libs.build.semver)
}
@@ -27,38 +27,59 @@ afterEvaluate {
gradlePlugin {
plugins {
register("android-application") {
id = "dev.msfjarvis.claw.android-application"
implementationClass = "dev.msfjarvis.claw.gradle.ApplicationPlugin"
}
register("android-common") {
id = "dev.msfjarvis.claw.android-common"
implementationClass = "dev.msfjarvis.claw.gradle.AndroidCommonPlugin"
}
register("android-library") {
id = "dev.msfjarvis.claw.android-library"
implementationClass = "dev.msfjarvis.claw.gradle.LibraryPlugin"
}
register("kotlin-android") {
id = "dev.msfjarvis.claw.kotlin-android"
implementationClass = "dev.msfjarvis.aps.gradle.KotlinAndroidPlugin"
implementationClass = "dev.msfjarvis.claw.gradle.KotlinAndroidPlugin"
}
register("kotlin-common") {
id = "dev.msfjarvis.claw.kotlin-common"
implementationClass = "dev.msfjarvis.aps.gradle.KotlinCommonPlugin"
implementationClass = "dev.msfjarvis.claw.gradle.KotlinCommonPlugin"
}
register("kotlin-jvm") {
id = "dev.msfjarvis.claw.kotlin-jvm"
implementationClass = "dev.msfjarvis.claw.gradle.KotlinJvmPlugin"
}
register("kotlin-kapt") {
id = "dev.msfjarvis.claw.kotlin-kapt"
implementationClass = "dev.msfjarvis.aps.gradle.KotlinKaptPlugin"
implementationClass = "dev.msfjarvis.claw.gradle.KotlinKaptPlugin"
}
register("kotlin-library") {
id = "dev.msfjarvis.claw.kotlin-library"
implementationClass = "dev.msfjarvis.aps.gradle.KotlinLibraryPlugin"
register("rename-artifacts") {
id = "dev.msfjarvis.claw.rename-artifacts"
implementationClass = "dev.msfjarvis.claw.gradle.RenameArtifactsPlugin"
}
register("spotless") {
id = "dev.msfjarvis.claw.spotless"
implementationClass = "dev.msfjarvis.aps.gradle.SpotlessPlugin"
implementationClass = "dev.msfjarvis.claw.gradle.SpotlessPlugin"
}
register("versioning") {
id = "dev.msfjarvis.claw.versioning-plugin"
implementationClass = "dev.msfjarvis.claw.gradle.versioning.VersioningPlugin"
}
register("versions") {
id = "dev.msfjarvis.claw.versions"
implementationClass = "dev.msfjarvis.aps.gradle.DependencyUpdatesPlugin"
implementationClass = "dev.msfjarvis.claw.gradle.DependencyUpdatesPlugin"
}
}
}
dependencies {
implementation(libs.build.agp)
implementation(libs.build.cachefix)
implementation(libs.build.detekt)
implementation(libs.build.kotlin.gradle)
implementation(libs.build.kotlin.serialization)
implementation(libs.build.semver)
implementation(libs.build.spotless)
implementation(libs.build.vcu)
implementation(libs.build.versions)
-4
View File
@@ -39,7 +39,3 @@ dependencyResolutionManagement {
}
versionCatalogs { create("libs") { from(files("../gradle/libs.versions.toml")) } }
}
include("android-plugins")
include("kotlin-plugins")
@@ -6,20 +6,20 @@
*/
@file:Suppress("UnstableApiUsage")
package dev.msfjarvis.aps.gradle
package dev.msfjarvis.claw.gradle
import com.android.build.api.dsl.ApplicationExtension
import com.android.build.api.dsl.LibraryExtension
import com.android.build.api.dsl.Lint
import com.android.build.api.dsl.TestExtension
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.api.variant.LibraryAndroidComponentsExtension
import com.android.build.gradle.TestedExtension
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
import com.android.build.gradle.BaseExtension
import org.gradle.android.AndroidCacheFixPlugin
import org.gradle.api.JavaVersion
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.findByType
private const val SLIM_TESTS_PROPERTY = "slimTests"
@@ -35,21 +35,13 @@ class AndroidCommonPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.configureSlimTests()
project.pluginManager.apply(AndroidCacheFixPlugin::class)
project.extensions.findByType<BaseAppModuleExtension>()?.run { lint.configureLint(project) }
project.extensions.findByType<LibraryExtension>()?.run { lint.configureLint(project) }
project.extensions.findByType<TestedExtension>()?.run {
project.extensions.configure<BaseExtension> {
compileSdkVersion(COMPILE_SDK)
defaultConfig {
minSdk = MIN_SDK
targetSdk = TARGET_SDK
}
sourceSets {
named("main") { java.srcDirs("src/main/kotlin") }
named("test") { java.srcDirs("src/test/kotlin") }
named("androidTest") { java.srcDirs("src/androidTest/kotlin") }
}
packagingOptions {
resources.excludes.add("**/*.version")
resources.excludes.add("**/*.txt")
@@ -69,22 +61,8 @@ class AndroidCommonPlugin : Plugin<Project> {
unitTests.isReturnDefaultValues = true
}
}
project.extensions.findByType<TestExtension>()?.run {
compileSdk = COMPILE_SDK
defaultConfig {
minSdk = MIN_SDK
targetSdk = TARGET_SDK
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
testOptions {
animationsDisabled = true
unitTests.isReturnDefaultValues = true
}
}
project.extensions.findByType<ApplicationExtension>()?.run { lint.configureLint(project) }
project.extensions.findByType<LibraryExtension>()?.run { lint.configureLint(project) }
}
}
@@ -1,20 +1,20 @@
/*
* Copyright © 2022 Harsh Shandilya.
* Copyright © 2022-2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
@file:Suppress("UnstableApiUsage")
package dev.msfjarvis.aps.gradle
package dev.msfjarvis.claw.gradle
import com.android.build.api.dsl.ApplicationExtension
import com.android.build.gradle.AppPlugin
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
import dev.msfjarvis.aps.gradle.signing.configureBuildSigning
import dev.msfjarvis.claw.gradle.signing.configureBuildSigning
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.configure
@Suppress("Unused")
class ApplicationPlugin : Plugin<Project> {
@@ -22,9 +22,7 @@ class ApplicationPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.pluginManager.apply(AppPlugin::class)
project.pluginManager.apply(AndroidCommonPlugin::class)
project.extensions.getByType<BaseAppModuleExtension>().run {
adbOptions.installOptions("--user 0")
project.extensions.configure<ApplicationExtension> {
dependenciesInfo {
includeInBundle = false
includeInApk = false
@@ -4,7 +4,7 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.aps.gradle
package dev.msfjarvis.claw.gradle
import com.github.benmanes.gradle.versions.VersionsPlugin
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
@@ -13,7 +13,7 @@ import nl.littlerobots.vcu.plugin.VersionCatalogUpdatePlugin
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.withType
@Suppress("Unused")
@@ -26,13 +26,22 @@ class DependencyUpdatesPlugin : Plugin<Project> {
when (candidate.group) {
"com.squareup.okhttp3",
"org.jetbrains.kotlin" -> true
else -> false
else -> isNonStable(candidate.version) && !isNonStable(currentVersion)
}
}
checkConstraints = true
checkBuildEnvironmentConstraints = true
checkForGradleUpdate = true
}
project.extensions.getByType<VersionCatalogUpdateExtension>().run {
project.extensions.configure<VersionCatalogUpdateExtension> {
keep.keepUnusedLibraries.set(true)
}
}
private fun isNonStable(version: String): Boolean {
val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase().contains(it) }
val regex = "^[0-9,.v-]+(-r)?$".toRegex()
val isStable = stableKeyword || regex.matches(version)
return isStable.not()
}
}
@@ -4,7 +4,7 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.aps.gradle
package dev.msfjarvis.claw.gradle
import io.gitlab.arturbosch.detekt.DetektPlugin
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
@@ -4,7 +4,7 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.aps.gradle
package dev.msfjarvis.claw.gradle
import org.gradle.api.Plugin
import org.gradle.api.Project
@@ -4,7 +4,7 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.aps.gradle
package dev.msfjarvis.claw.gradle
import org.gradle.api.JavaVersion
import org.gradle.api.Plugin
@@ -37,6 +37,7 @@ class KotlinCommonPlugin : Plugin<Project> {
withType<Test>().configureEach {
maxParallelForks = Runtime.getRuntime().availableProcessors() * 2
testLogging { events(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED) }
useJUnitPlatform()
}
}
}
@@ -4,16 +4,17 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.aps.gradle
package dev.msfjarvis.claw.gradle
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.jetbrains.kotlin.gradle.plugin.KotlinPluginWrapper
@Suppress("Unused")
class KotlinLibraryPlugin : Plugin<Project> {
class KotlinJvmPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.pluginManager.apply(KotlinPluginWrapper::class)
project.pluginManager.apply(KotlinCommonPlugin::class)
}
}
@@ -1,15 +1,15 @@
/*
* Copyright © 2021-2022 Harsh Shandilya.
* Copyright © 2022 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.aps.gradle
package dev.msfjarvis.claw.gradle
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.configure
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
import org.jetbrains.kotlin.gradle.plugin.KaptExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPluginWrapper
@@ -23,7 +23,7 @@ class KotlinKaptPlugin : Plugin<Project> {
apply(Kapt3GradleSubplugin::class)
}
project.afterEvaluate {
project.extensions.getByType<KaptExtension>().run {
project.extensions.configure<KaptExtension> {
javacOptions {
if (hasDaggerCompilerDependency()) {
// https://dagger.dev/dev-guide/compiler-options#fastinit-mode
@@ -4,7 +4,7 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.aps.gradle
package dev.msfjarvis.claw.gradle
import com.android.build.gradle.LibraryPlugin
import org.gradle.api.Plugin
@@ -4,17 +4,16 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.aps.gradle
package dev.msfjarvis.claw.gradle
import com.android.build.api.artifact.SingleArtifact
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.api.variant.VariantOutputConfiguration
import dev.msfjarvis.aps.gradle.artifacts.CollectApksTask
import dev.msfjarvis.aps.gradle.artifacts.CollectBundleTask
import java.util.Locale
import dev.msfjarvis.claw.gradle.artifacts.CollectApksTask
import dev.msfjarvis.claw.gradle.artifacts.CollectBundleTask
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.register
@Suppress("Unused")
@@ -22,9 +21,9 @@ class RenameArtifactsPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.pluginManager.withPlugin("com.android.application") {
project.extensions.getByType<ApplicationAndroidComponentsExtension>().run {
project.extensions.configure<ApplicationAndroidComponentsExtension> {
onVariants { variant ->
val taskPrefix = "collect${variant.name.capitalize(Locale.ROOT)}"
val taskPrefix = "collect${variant.name.replaceFirstChar { it.uppercase() }}"
project.tasks.register<CollectApksTask>("${taskPrefix}Apks") {
variantName.set(variant.name)
apkFolder.set(variant.artifacts.get(SingleArtifact.APK))
@@ -4,7 +4,7 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.aps.gradle
package dev.msfjarvis.claw.gradle
import com.diffplug.gradle.spotless.SpotlessExtension
import com.diffplug.gradle.spotless.SpotlessPlugin
@@ -12,7 +12,7 @@ import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.configure
@Suppress("Unused")
class SpotlessPlugin : Plugin<Project> {
@@ -22,7 +22,7 @@ class SpotlessPlugin : Plugin<Project> {
throw GradleException("Spotless plugin must only be applied to the root project.")
}
project.pluginManager.apply(SpotlessPlugin::class)
project.extensions.getByType<SpotlessExtension>().run {
project.extensions.configure<SpotlessExtension> {
kotlin {
ktfmt(KTFMT_VERSION).googleStyle()
target("**/*.kt")
@@ -4,7 +4,7 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.aps.gradle.artifacts
package dev.msfjarvis.claw.gradle.artifacts
import com.android.build.api.variant.BuiltArtifactsLoader
import java.io.File
@@ -4,7 +4,7 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.aps.gradle.artifacts
package dev.msfjarvis.claw.gradle.artifacts
import java.io.File
import java.nio.file.Files
@@ -4,7 +4,7 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.aps.gradle.signing
package dev.msfjarvis.claw.gradle.signing
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
import java.util.Properties
@@ -4,7 +4,7 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.aps.gradle.versioning
package dev.msfjarvis.claw.gradle.versioning
const val VERSIONING_PROP_FILE = "version.properties"
const val VERSIONING_PROP_VERSION_NAME = "versioning-plugin.versionName"
@@ -4,7 +4,7 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.aps.gradle.versioning
package dev.msfjarvis.claw.gradle.versioning
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.api.variant.VariantOutputConfiguration
@@ -14,7 +14,7 @@ import java.util.Properties
import java.util.concurrent.atomic.AtomicBoolean
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.register
import org.gradle.kotlin.dsl.withType
@@ -46,11 +46,15 @@ class VersioningPlugin : Plugin<Project> {
}
project.plugins.withType<AppPlugin> {
androidAppPluginApplied.set(true)
extensions.getByType<ApplicationAndroidComponentsExtension>().onVariants { variant ->
val mainOutput =
variant.outputs.single { it.outputType == VariantOutputConfiguration.OutputType.SINGLE }
mainOutput.versionName.set(versionName)
mainOutput.versionCode.set(versionCode)
extensions.configure<ApplicationAndroidComponentsExtension> {
onVariants { variant ->
val mainOutput =
variant.outputs.single {
it.outputType == VariantOutputConfiguration.OutputType.SINGLE
}
mainOutput.versionName.set(versionName)
mainOutput.versionCode.set(versionCode)
}
}
}
val version = Semver(versionName)
@@ -4,7 +4,7 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.aps.gradle.versioning
package dev.msfjarvis.claw.gradle.versioning
import com.vdurmont.semver4j.Semver
import org.gradle.api.DefaultTask
+1 -3
View File
@@ -7,8 +7,7 @@
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
plugins {
kotlin("android")
id("dev.msfjarvis.claw.kotlin-common")
id("dev.msfjarvis.claw.kotlin-android")
id("dev.msfjarvis.claw.android-library")
alias(libs.plugins.anvil)
alias(libs.plugins.whetstone)
@@ -28,7 +27,6 @@ dependencies {
implementation(libs.androidx.browser)
implementation(libs.androidx.compose.animation)
implementation(libs.androidx.compose.foundation)
implementation(libs.androidx.compose.material)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.runtime)
implementation(libs.androidx.compose.ui.text)
@@ -6,7 +6,7 @@
*/
package dev.msfjarvis.claw.common
sealed class NetworkState {
internal sealed class NetworkState {
class Success<T>(val data: T) : NetworkState()
class Error(val error: Throwable, val description: String) : NetworkState()
object Loading : NetworkState()
@@ -1,5 +1,5 @@
/*
* Copyright © 2021-2022 Harsh Shandilya.
* Copyright © 2021-2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
@@ -20,24 +20,22 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import dev.msfjarvis.claw.common.posts.PostActions
import dev.msfjarvis.claw.common.posts.PostTitle
@@ -46,14 +44,13 @@ import dev.msfjarvis.claw.common.posts.TagRow
import dev.msfjarvis.claw.common.res.ClawIcons
import dev.msfjarvis.claw.common.ui.NetworkImage
import dev.msfjarvis.claw.common.ui.ThemedRichText
import dev.msfjarvis.claw.model.Comment
import dev.msfjarvis.claw.model.LinkMetadata
import dev.msfjarvis.claw.model.LobstersPostDetails
import java.time.Instant
import java.time.temporal.TemporalAccessor
@Composable
fun CommentsHeader(
internal fun CommentsHeader(
postDetails: LobstersPostDetails,
postActions: PostActions,
htmlConverter: HTMLConverter,
@@ -104,7 +101,7 @@ fun CommentsHeader(
}
@Composable
fun PostLink(
private fun PostLink(
linkMetadata: LinkMetadata,
modifier: Modifier = Modifier,
) {
@@ -114,22 +111,13 @@ fun PostLink(
shape = RoundedCornerShape(8.dp),
)
) {
Row(modifier = Modifier.padding(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp)) {
if (linkMetadata.faviconUrl != null) {
NetworkImage(
url = linkMetadata.faviconUrl!!,
placeholder = ClawIcons.Web,
contentDescription = "",
modifier = Modifier.size(24.dp),
)
} else {
Icon(
painter = ClawIcons.Web,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSecondary,
modifier = Modifier.clip(CircleShape),
)
}
Row(modifier = Modifier.padding(16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
NetworkImage(
url = linkMetadata.faviconUrl,
placeholder = ClawIcons.Web,
contentDescription = "",
modifier = Modifier.size(24.dp),
)
Text(
text = linkMetadata.url,
overflow = TextOverflow.Ellipsis,
@@ -145,18 +133,19 @@ private val CommentEntryPadding = 16f.dp
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun CommentEntry(
comment: Comment,
internal fun CommentEntry(
commentNode: CommentNode,
htmlConverter: HTMLConverter,
toggleExpanded: (CommentNode) -> Unit,
modifier: Modifier = Modifier,
) {
var expanded by remember(comment) { mutableStateOf(true) }
val uriHandler = LocalUriHandler.current
val comment = commentNode.comment
Box(
modifier =
modifier
.fillMaxWidth()
.clickable { expanded = !expanded }
.clickable { toggleExpanded(commentNode) }
.background(MaterialTheme.colorScheme.background)
.padding(
start = CommentEntryPadding * comment.indentLevel,
@@ -171,6 +160,7 @@ fun CommentEntry(
buildCommenterString(
commenterName = comment.user.username,
score = comment.score,
isUnread = commentNode.isUnread,
createdAt = comment.createdAt,
updatedAt = comment.updatedAt,
),
@@ -179,7 +169,7 @@ fun CommentEntry(
modifier =
Modifier.clickable { uriHandler.openUri("https://lobste.rs/u/${comment.user.username}") },
)
AnimatedContent(targetState = expanded) { expandedState ->
AnimatedContent(targetState = commentNode.isExpanded) { expandedState ->
if (expandedState) {
ThemedRichText(
text = htmlConverter.convertHTMLToMarkdown(comment.comment),
@@ -195,6 +185,7 @@ fun CommentEntry(
fun buildCommenterString(
commenterName: String,
score: Int,
isUnread: Boolean,
createdAt: TemporalAccessor,
updatedAt: TemporalAccessor,
): AnnotatedString {
@@ -233,5 +224,17 @@ fun buildCommenterString(
append(updatedRelative.toString())
append(')')
}
if (isUnread) {
append(' ')
withStyle(
style =
SpanStyle(
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.error,
)
) {
append("(unread)")
}
}
}
}
@@ -0,0 +1,107 @@
/*
* Copyright © 2022-2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.common.comments
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.Divider
import dev.msfjarvis.claw.database.local.PostComments
import dev.msfjarvis.claw.model.Comment
internal data class CommentNode(
val comment: Comment,
var parent: CommentNode? = null,
val children: MutableList<CommentNode> = mutableListOf(),
val isUnread: Boolean = false,
var isExpanded: Boolean = true,
) {
fun addChild(child: CommentNode) {
if (comment.indentLevel + 1 == child.comment.indentLevel) {
children.add(child)
child.parent = this
} else {
children.last().addChild(child)
}
}
}
internal fun createListNode(
comments: List<Comment>,
commentState: PostComments?
): MutableList<CommentNode> {
val commentNodes = mutableListOf<CommentNode>()
val isUnread = { id: String -> commentState?.commentIds?.contains(id) == false }
for (i in comments.indices) {
if (comments[i].indentLevel == 1) {
commentNodes.add(CommentNode(comment = comments[i], isUnread = isUnread(comments[i].shortId)))
} else {
commentNodes
.last()
.addChild(CommentNode(comment = comments[i], isUnread = isUnread(comments[i].shortId)))
}
}
return commentNodes
}
internal fun setExpanded(commentNode: CommentNode, expanded: Boolean): CommentNode {
commentNode.isExpanded = expanded
if (commentNode.children.isNotEmpty()) {
commentNode.children.forEach { setExpanded(it, expanded) }
}
return commentNode
}
internal fun findTopMostParent(node: CommentNode): CommentNode {
val parent = node.parent
return if (parent != null) {
findTopMostParent(parent)
} else {
node
}
}
internal fun LazyListScope.nodes(
nodes: List<CommentNode>,
htmlConverter: HTMLConverter,
toggleExpanded: (CommentNode) -> Unit,
) {
nodes.forEach { node ->
node(
node = node,
htmlConverter = htmlConverter,
toggleExpanded = toggleExpanded,
)
}
}
private fun LazyListScope.node(
node: CommentNode,
htmlConverter: HTMLConverter,
toggleExpanded: (CommentNode) -> Unit,
) {
// Skip the node if neither the node nor its parent is expanded
if (!node.isExpanded && node.parent?.isExpanded == false) {
return
}
item {
CommentEntry(
commentNode = node,
htmlConverter = htmlConverter,
toggleExpanded = toggleExpanded,
)
Divider()
}
if (node.children.isNotEmpty()) {
nodes(
node.children,
htmlConverter = htmlConverter,
toggleExpanded = toggleExpanded,
)
}
}
@@ -1,24 +1,26 @@
/*
* Copyright © 2021-2022 Harsh Shandilya.
* Copyright © 2021-2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.common.comments
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
@@ -30,15 +32,23 @@ import dev.msfjarvis.claw.common.NetworkState.Success
import dev.msfjarvis.claw.common.posts.PostActions
import dev.msfjarvis.claw.common.ui.NetworkError
import dev.msfjarvis.claw.common.ui.ProgressBar
import dev.msfjarvis.claw.database.local.PostComments
import dev.msfjarvis.claw.model.Comment
import dev.msfjarvis.claw.model.LobstersPostDetails
@Suppress("LongParameterList")
@Composable
private fun CommentsPageInternal(
details: LobstersPostDetails,
postActions: PostActions,
htmlConverter: HTMLConverter,
commentState: PostComments?,
markSeenComments: (String, List<Comment>) -> Unit,
modifier: Modifier = Modifier,
) {
val commentNodes = createListNode(details.comments, commentState).toMutableStateList()
LaunchedEffect(key1 = commentNodes) { markSeenComments(details.shortId, details.comments) }
Surface(color = MaterialTheme.colorScheme.surfaceVariant) {
LazyColumn(modifier = modifier, contentPadding = PaddingValues(bottom = 24.dp)) {
item {
@@ -49,7 +59,7 @@ private fun CommentsPageInternal(
)
}
if (details.comments.isNotEmpty()) {
if (commentNodes.isNotEmpty()) {
item {
Text(
text = "Comments",
@@ -58,12 +68,20 @@ private fun CommentsPageInternal(
)
}
itemsIndexed(details.comments) { index, item ->
if (index != 0) {
Divider()
}
CommentEntry(comment = item, htmlConverter = htmlConverter)
}
nodes(
nodes = commentNodes,
htmlConverter = htmlConverter,
toggleExpanded = { node ->
val newNode = setExpanded(node, !node.isExpanded)
val parent = findTopMostParent(newNode)
val index =
commentNodes.indexOf(commentNodes.find { it.comment.url == parent.comment.url })
if (index != -1) {
commentNodes.removeAt(index)
commentNodes.add(index, parent)
}
},
)
} else {
item {
Text(
@@ -79,12 +97,14 @@ private fun CommentsPageInternal(
}
}
@Suppress("UNCHECKED_CAST")
@Suppress("UNCHECKED_CAST", "LongParameterList")
@Composable
fun CommentsPage(
postId: String,
postActions: PostActions,
htmlConverter: HTMLConverter,
getSeenComments: suspend (String) -> PostComments?,
markSeenComments: (String, List<Comment>) -> Unit,
modifier: Modifier = Modifier,
) {
val postDetails by
@@ -95,6 +115,8 @@ fun CommentsPage(
onFailure = { value = Error(error = it, description = "Failed to load comments") }
)
}
val commentState by
produceState<PostComments?>(initialValue = null) { value = getSeenComments(postId) }
when (postDetails) {
is Success<*> -> {
@@ -102,13 +124,25 @@ fun CommentsPage(
details = (postDetails as Success<LobstersPostDetails>).data,
postActions = postActions,
htmlConverter = htmlConverter,
commentState = commentState,
markSeenComments = markSeenComments,
modifier = modifier.fillMaxSize(),
)
}
is Error -> {
val error = postDetails as Error
NetworkError(label = error.description, error = error.error)
Box(modifier = Modifier.fillMaxSize()) {
NetworkError(
label = error.description,
error = error.error,
modifier = Modifier.align(Alignment.Center),
)
}
}
Loading -> {
Box(modifier = Modifier.fillMaxSize()) {
ProgressBar(modifier = Modifier.align(Alignment.Center))
}
}
Loading -> ProgressBar()
}
}
@@ -6,7 +6,10 @@
*/
package dev.msfjarvis.claw.common.comments
import androidx.compose.runtime.Stable
/** Defines a contract to convert strings of HTML to Markdown. */
@Stable
fun interface HTMLConverter {
fun convertHTMLToMarkdown(html: String): String
}
@@ -11,9 +11,13 @@ import coil.Coil
import coil.ImageLoader
import coil.disk.DiskCache
import coil.memory.MemoryCache
import com.deliveryhero.whetstone.app.ApplicationScope
import com.squareup.anvil.annotations.ContributesMultibinding
import dev.msfjarvis.claw.core.injection.AppPlugin
import javax.inject.Inject
class CoilAppPlugin : AppPlugin {
@ContributesMultibinding(ApplicationScope::class)
class CoilAppPlugin @Inject constructor() : AppPlugin {
override fun apply(application: Application) {
Coil.setImageLoader {
ImageLoader.Builder(application)
@@ -115,7 +115,7 @@ fun PostDetails(post: SavedPost, modifier: Modifier = Modifier) {
}
@Composable
fun PostTitle(
internal fun PostTitle(
title: String,
modifier: Modifier = Modifier,
) {
@@ -128,7 +128,7 @@ fun PostTitle(
}
@Composable
fun Submitter(
internal fun Submitter(
text: AnnotatedString,
avatarUrl: String,
contentDescription: String,
@@ -151,7 +151,7 @@ fun Submitter(
}
@Composable
fun SaveButton(
private fun SaveButton(
isSaved: Boolean,
modifier: Modifier = Modifier,
) {
@@ -169,7 +169,7 @@ fun SaveButton(
@Composable
@OptIn(ExperimentalMaterial3Api::class)
fun CommentsButton(
private fun CommentsButton(
commentCount: Int?,
modifier: Modifier = Modifier,
) {
@@ -200,7 +200,7 @@ fun CommentsButton(
}
@Composable
fun TagRow(
internal fun TagRow(
tags: List<String>,
modifier: Modifier = Modifier,
) {
@@ -214,7 +214,7 @@ fun TagRow(
}
@Composable
fun TagText(
private fun TagText(
tag: String,
modifier: Modifier = Modifier,
) {
@@ -6,10 +6,12 @@
*/
package dev.msfjarvis.claw.common.posts
import androidx.compose.runtime.Stable
import dev.msfjarvis.claw.database.local.SavedPost
import dev.msfjarvis.claw.model.LinkMetadata
import dev.msfjarvis.claw.model.LobstersPostDetails
@Stable
interface PostActions {
fun viewPost(postUrl: String, commentsUrl: String)
fun viewComments(postId: String)
@@ -21,6 +21,7 @@ fun LobstersPost.toDbModel(): SavedPost {
submitterName = submitter.username,
submitterAvatarUrl = submitter.avatarUrl,
tags = tags,
description = description,
)
}
@@ -35,5 +36,6 @@ fun LobstersPostDetails.toDbModel(): SavedPost {
submitterName = submitter.username,
submitterAvatarUrl = submitter.avatarUrl,
tags = tags,
description = description,
)
}
@@ -19,7 +19,7 @@ import androidx.compose.runtime.ProvidedValue
import androidx.compose.ui.platform.LocalContext
import com.halilibo.richtext.ui.material3.SetupMaterial3RichText
internal val LightThemeColors =
private val LightThemeColors =
lightColorScheme(
primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary,
@@ -48,7 +48,7 @@ internal val LightThemeColors =
inverseSurface = md_theme_light_inverseSurface,
)
internal val DarkThemeColors =
private val DarkThemeColors =
darkColorScheme(
primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary,
@@ -23,7 +23,7 @@ import dev.msfjarvis.claw.common.theme.LobstersTheme
import dev.msfjarvis.claw.common.ui.preview.ThemePreviews
@Composable
fun ThemedRichText(
internal fun ThemedRichText(
text: String,
modifier: Modifier = Modifier,
) {
@@ -50,7 +50,7 @@ fun ThemedRichText(
@ThemePreviews
@Composable
fun ThemedRichTextPreview() {
internal fun ThemedRichTextPreview() {
val text =
"""
### Heading
@@ -72,6 +72,6 @@ fun NetworkError(
@ThemePreviews
@Composable
fun NetworkErrorPreview() {
internal fun NetworkErrorPreview() {
LobstersTheme { NetworkError(label = "Failed to load posts", error = Throwable("Preview")) }
}
@@ -12,8 +12,8 @@ import androidx.compose.ui.graphics.painter.Painter
import coil.compose.AsyncImage
@Composable
fun NetworkImage(
url: String,
internal fun NetworkImage(
url: String?,
placeholder: Painter,
contentDescription: String,
modifier: Modifier = Modifier,
@@ -7,8 +7,7 @@
package dev.msfjarvis.claw.common.ui
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -16,7 +15,7 @@ import androidx.compose.ui.Modifier
@Composable
fun ProgressBar(modifier: Modifier = Modifier) {
Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
CircularProgressIndicator(color = MaterialTheme.colorScheme.secondary)
Box(modifier = modifier, contentAlignment = Alignment.Center) {
LinearProgressIndicator(color = MaterialTheme.colorScheme.secondary)
}
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2021-2022 Harsh Shandilya.
* Copyright © 2021-2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
@@ -11,9 +11,11 @@ import android.content.Context
import android.net.Uri
import android.widget.Toast
import androidx.browser.customtabs.CustomTabsIntent
import androidx.compose.runtime.Stable
import androidx.compose.ui.platform.UriHandler
import io.github.aakira.napier.Napier
@Stable
class UrlLauncher(private val context: Context) : UriHandler {
override fun openUri(uri: String) {
val customTabsIntent =
@@ -7,6 +7,7 @@
package dev.msfjarvis.claw.common.user
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
@@ -60,9 +61,19 @@ fun UserProfile(
}
is Error -> {
val error = user as Error
NetworkError(label = error.description, error = error.error)
Box(modifier = Modifier.fillMaxSize()) {
NetworkError(
label = error.description,
error = error.error,
modifier = Modifier.align(Alignment.Center),
)
}
}
Loading -> {
Box(modifier = Modifier.fillMaxSize()) {
ProgressBar(modifier = Modifier.align(Alignment.Center))
}
}
Loading -> ProgressBar()
}
}
@@ -90,9 +101,11 @@ private fun UserProfileInternal(
ThemedRichText(
text = user.about,
)
ThemedRichText(
text = "Invited by [${user.invitedBy}](https://lobste.rs/u/${user.invitedBy})",
)
user.invitedBy?.let { invitedBy ->
ThemedRichText(
text = "Invited by [${invitedBy}](https://lobste.rs/u/${user.invitedBy})",
)
}
}
}
}
+1 -2
View File
@@ -7,8 +7,7 @@
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
plugins {
kotlin("android")
id("dev.msfjarvis.claw.kotlin-common")
id("dev.msfjarvis.claw.kotlin-android")
id("dev.msfjarvis.claw.android-library")
alias(libs.plugins.anvil)
alias(libs.plugins.whetstone)
@@ -1,5 +1,5 @@
/*
* Copyright © 2022 Harsh Shandilya.
* Copyright © 2021-2022 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
+1 -4
View File
@@ -4,10 +4,7 @@
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
plugins {
kotlin("jvm")
id("dev.msfjarvis.claw.kotlin-library")
}
plugins { id("dev.msfjarvis.claw.kotlin-jvm") }
dependencies {
api(libs.kotlinx.coroutines.core)
+12 -10
View File
@@ -7,11 +7,10 @@
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
plugins {
kotlin("android")
alias(libs.plugins.sqldelight)
id("dev.msfjarvis.claw.kotlin-common")
id("dev.msfjarvis.claw.kotlin-android")
id("dev.msfjarvis.claw.android-library")
alias(libs.plugins.anvil)
alias(libs.plugins.sqldelight)
alias(libs.plugins.whetstone)
}
@@ -20,11 +19,13 @@ anvil { generateDaggerFactories.set(true) }
android { namespace = "dev.msfjarvis.claw.database" }
sqldelight {
database("LobstersDatabase") {
packageName = "dev.msfjarvis.claw.database"
sourceFolders = listOf("sqldelight")
schemaOutputDirectory = file("src/main/sqldelight/databases")
verifyMigrations = true
databases {
create("LobstersDatabase") {
packageName.set("dev.msfjarvis.claw.database")
sourceFolders.set(listOf("sqldelight"))
schemaOutputDirectory.set(file("src/main/sqldelight/databases"))
verifyMigrations.set(true)
}
}
}
@@ -33,7 +34,8 @@ dependencies {
implementation(projects.core)
implementation(libs.sqldelight.androidDriver)
implementation(libs.sqldelight.primitiveAdapters)
testImplementation(libs.sqldelight.jvmDriver)
testImplementation(libs.kotest.assertions.core)
testImplementation(libs.kotest.runner.junit5)
testImplementation(libs.kotlinx.coroutines.core)
testImplementation(kotlin("test-junit"))
testImplementation(libs.sqldelight.jvmDriver)
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2021-2022 Harsh Shandilya.
* Copyright © 2021-2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
@@ -15,8 +15,9 @@ import com.squareup.anvil.annotations.ContributesTo
import dagger.Module
import dagger.Provides
import dev.msfjarvis.claw.database.LobstersDatabase
import dev.msfjarvis.claw.database.local.PostComments
import dev.msfjarvis.claw.database.local.SavedPost
import dev.msfjarvis.claw.database.model.TagsAdapter
import dev.msfjarvis.claw.database.model.CSVAdapter
@Module
@ContributesTo(ApplicationScope::class)
@@ -27,6 +28,10 @@ object DatabaseModule {
@Provides
fun provideDatabase(@ForScope(ApplicationScope::class) context: Context): LobstersDatabase {
val driver = AndroidSqliteDriver(LobstersDatabase.Schema, context, LOBSTERS_DATABASE_NAME)
return LobstersDatabase(driver, SavedPost.Adapter(IntColumnAdapter, TagsAdapter()))
return LobstersDatabase(
driver,
PostComments.Adapter(CSVAdapter()),
SavedPost.Adapter(IntColumnAdapter, CSVAdapter()),
)
}
}
@@ -1,20 +0,0 @@
/*
* Copyright © 2021-2022 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.database.local
import android.content.Context
import app.cash.sqldelight.adapter.primitive.IntColumnAdapter
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
import dev.msfjarvis.claw.database.LobstersDatabase
import dev.msfjarvis.claw.database.model.TagsAdapter
internal const val LobstersDatabaseName = "SavedPosts.db"
fun createDatabase(context: Context): LobstersDatabase {
val driver = AndroidSqliteDriver(LobstersDatabase.Schema, context, LobstersDatabaseName)
return LobstersDatabase(driver, SavedPost.Adapter(IntColumnAdapter, TagsAdapter()))
}
@@ -1,5 +1,5 @@
/*
* Copyright © 2021-2022 Harsh Shandilya.
* Copyright © 2021-2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
@@ -8,7 +8,7 @@ package dev.msfjarvis.claw.database.model
import app.cash.sqldelight.ColumnAdapter
class TagsAdapter : ColumnAdapter<List<String>, String> {
class CSVAdapter : ColumnAdapter<List<String>, String> {
override fun decode(databaseValue: String): List<String> {
return databaseValue.split(SEPARATOR)
}
@@ -0,0 +1,2 @@
ALTER TABLE SavedPost
ADD COLUMN description TEXT NOT NULL DEFAULT "";
@@ -0,0 +1,7 @@
import kotlin.String;
import kotlin.collections.List;
CREATE TABLE IF NOT EXISTS PostComments(
postId TEXT NOT NULL PRIMARY KEY,
commentIds TEXT AS List<String> NOT NULL
);
@@ -0,0 +1,17 @@
import kotlin.String;
import kotlin.collections.List;
CREATE TABLE IF NOT EXISTS PostComments(
postId TEXT NOT NULL PRIMARY KEY,
commentIds TEXT AS List<String> NOT NULL
);
rememberComments:
INSERT OR REPLACE
INTO PostComments
VALUES ?;
getCommentIds:
SELECT *
FROM PostComments
WHERE postId = ?;
@@ -11,7 +11,8 @@ CREATE TABLE IF NOT EXISTS SavedPost(
commentsUrl TEXT NOT NULL,
submitterName TEXT NOT NULL,
submitterAvatarUrl TEXT NOT NULL,
tags TEXT AS List<String> NOT NULL
tags TEXT AS List<String> NOT NULL,
description TEXT NOT NULL DEFAULT ""
);
insertOrReplacePost:
@@ -0,0 +1,47 @@
/*
* Copyright © 2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.database.local
import app.cash.sqldelight.adapter.primitive.IntColumnAdapter
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
import dev.msfjarvis.claw.database.LobstersDatabase
import dev.msfjarvis.claw.database.model.CSVAdapter
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.shouldBe
import java.util.UUID
class PostCommentsQueriesTest : FunSpec() {
private lateinit var postQueries: PostCommentsQueries
init {
beforeEach {
val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)
LobstersDatabase.Schema.create(driver)
val database =
LobstersDatabase(
driver,
PostComments.Adapter(CSVAdapter()),
SavedPost.Adapter(IntColumnAdapter, CSVAdapter()),
)
postQueries = database.postCommentsQueries
}
test("get non-existent post") {
val ids = postQueries.getCommentIds(UUID.randomUUID().toString()).executeAsOneOrNull()
ids shouldBe null
}
test("put and get post comments") {
val postId = UUID.randomUUID().toString()
val comments = PostComments(postId, List(10) { UUID.randomUUID().toString() })
postQueries.rememberComments(comments)
val ids = postQueries.getCommentIds(postId).executeAsOne().commentIds
ids shouldHaveSize 10
}
}
}
@@ -0,0 +1,143 @@
/*
* Copyright © 2021-2023 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.database.local
import app.cash.sqldelight.adapter.primitive.IntColumnAdapter
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
import dev.msfjarvis.claw.database.LobstersDatabase
import dev.msfjarvis.claw.database.model.CSVAdapter
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldBeEmpty
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.shouldBe
class SavedPostQueriesTest : FunSpec() {
private lateinit var postQueries: SavedPostQueries
init {
beforeEach {
val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)
LobstersDatabase.Schema.create(driver)
val database =
LobstersDatabase(
driver,
PostComments.Adapter(CSVAdapter()),
SavedPost.Adapter(IntColumnAdapter, CSVAdapter()),
)
postQueries = database.savedPostQueries
}
test("add and count posts") {
val posts = createTestData(5)
posts.forEach { postQueries.insertOrReplacePost(it) }
val postCount = postQueries.selectCount().executeAsOne()
postCount shouldBe 5
}
test("update post in database") {
// Get 1 post
val post = createTestData(1)[0]
// Insert post into DB
postQueries.insertOrReplacePost(post)
// Create a new post and try replacing it
val newPost = post.copy(submitterName = "Fake name")
postQueries.insertOrReplacePost(newPost)
// Check post count
val postsCount = postQueries.selectCount().executeAsOne()
postsCount shouldBe 1
// Check if post is updated
val postFromDb = postQueries.selectPost(post.shortId).executeAsOne()
postFromDb.submitterName shouldBe "Fake name"
}
test("get post from db") {
// Get 1 post
val post = createTestData(1)[0]
// Insert post into DB
postQueries.insertOrReplacePost(post)
val postFromDb = postQueries.selectAllPosts().executeAsOne()
postFromDb.shortId shouldBe "test_id_1"
}
test("get multiple posts from db") {
// Get 5 post
val posts = createTestData(5)
// Insert posts into DB
posts.forEach { postQueries.insertOrReplacePost(it) }
val postsFromDb = postQueries.selectAllPosts().executeAsList()
// Check if all posts have correct shortId
postsFromDb.forEachIndexed { index, post -> post.shortId shouldBe "test_id_${index.inc()}" }
}
test("delete post") {
// Create 3 posts and insert them to DB
val posts = createTestData(3)
posts.forEach { postQueries.insertOrReplacePost(it) }
// Delete 2nd post
postQueries.deletePost("test_id_2")
val postsFromDB = postQueries.selectAllPosts().executeAsList()
// Check if size is 2, and only the correct post is deleted
postsFromDB shouldHaveSize 2
postsFromDB[0].shortId shouldBe "test_id_1"
postsFromDB[1].shortId shouldBe "test_id_3"
}
test("delete all posts") {
// Create 5 posts and insert them to DB
val posts = createTestData(5)
posts.forEach { postQueries.insertOrReplacePost(it) }
// Delete all posts
postQueries.deleteAllPosts()
val dbPosts = postQueries.selectAllPosts().executeAsList()
dbPosts.shouldBeEmpty()
}
}
private fun createTestData(count: Int): ArrayList<SavedPost> {
val posts = arrayListOf<SavedPost>()
for (i in 1..count) {
val post =
SavedPost(
shortId = "test_id_$i",
createdAt = "0",
title = "test",
url = "test_url",
commentCount = 0,
commentsUrl = "test_comments_url",
submitterName = "test_user_$i",
submitterAvatarUrl = "test_avatar_url",
tags = listOf(),
description = "",
)
posts.add(post)
}
return posts
}
}
@@ -1,160 +0,0 @@
/*
* Copyright © 2021-2022 Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
package dev.msfjarvis.claw.database.local
import app.cash.sqldelight.adapter.primitive.IntColumnAdapter
import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
import dev.msfjarvis.claw.database.LobstersDatabase
import dev.msfjarvis.claw.database.model.TagsAdapter
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlinx.coroutines.runBlocking
import org.junit.Before
class SqlDelightQueriesTest {
private lateinit var postQueries: SavedPostQueries
@Before
fun setUp() {
val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)
LobstersDatabase.Schema.create(driver)
val database =
LobstersDatabase(
driver,
SavedPost.Adapter(IntColumnAdapter, TagsAdapter()),
)
postQueries = database.savedPostQueries
}
@Test
fun selectCount() = runBlocking {
val posts = createTestData(5)
posts.forEach { postQueries.insertOrReplacePost(it) }
val postCount = postQueries.selectCount().executeAsOne()
assertEquals(5, postCount)
}
@Test
fun insertIntoDatabase() = runBlocking {
// Get 5 posts
val posts = createTestData(5)
// Insert posts into DB
posts.forEach { postQueries.insertOrReplacePost(it) }
// Check post count
val postsCount = postQueries.selectCount().executeAsOne()
assertEquals(5, postsCount)
}
@Test
fun replaceFromDatabase() = runBlocking {
// Get 1 post
val post = createTestData(1)[0]
// Insert post into DB
postQueries.insertOrReplacePost(post)
// Create a new post and try replacing it
val newPost = post.copy(submitterName = "Fake name")
postQueries.insertOrReplacePost(newPost)
// Check post count
val postsCount = postQueries.selectCount().executeAsOne()
assertEquals(1, postsCount)
// Check if post is updated
val postFromDb = postQueries.selectPost(post.shortId).executeAsOne()
assertEquals("Fake name", postFromDb.submitterName)
}
@Test
fun selectPost() = runBlocking {
// Get 1 post
val post = createTestData(1)[0]
// Insert post into DB
postQueries.insertOrReplacePost(post)
val postFromDb = postQueries.selectAllPosts().executeAsOne()
assertEquals("test_id_1", postFromDb.shortId)
}
@Test
fun selectAllPosts() = runBlocking {
// Get 5 post
val posts = createTestData(5)
// Insert posts into DB
posts.forEach { postQueries.insertOrReplacePost(it) }
val postsFromDb = postQueries.selectAllPosts().executeAsList()
// Check if all posts have correct shortId
for (i in 1..5) {
assertEquals("test_id_$i", postsFromDb[i - 1].shortId)
}
}
@Test
fun deletePost() = runBlocking {
// Create 3 posts and insert them to DB
val posts = createTestData(3)
posts.forEach { postQueries.insertOrReplacePost(it) }
// Delete 2nd post
postQueries.deletePost("test_id_2")
val postsFromDB = postQueries.selectAllPosts().executeAsList()
// Check if size is 2, and only the correct post is deleted
assertEquals(2, postsFromDB.size)
assertEquals("test_id_1", postsFromDB[0].shortId)
assertEquals("test_id_3", postsFromDB[1].shortId)
}
@Test
fun deleteAllPost() = runBlocking {
// Create 5 posts and insert them to DB
val posts = createTestData(5)
posts.forEach { postQueries.insertOrReplacePost(it) }
// Delete all posts
postQueries.deleteAllPosts()
val postsCount = postQueries.selectCount().executeAsOne()
// Check if db is empty
assertEquals(0, postsCount)
}
private fun createTestData(count: Int): ArrayList<SavedPost> {
val posts = arrayListOf<SavedPost>()
for (i in 1..count) {
val post =
SavedPost(
shortId = "test_id_$i",
createdAt = "0",
title = "test",
url = "test_url",
commentCount = 0,
commentsUrl = "test_comments_url",
submitterName = "test_user_$i",
submitterAvatarUrl = "test_avatar_url",
tags = listOf(),
)
posts.add(post)
}
return posts
}
}
+1 -2
View File
@@ -2,7 +2,6 @@
<SmellBaseline>
<ManuallySuppressedIssues></ManuallySuppressedIssues>
<CurrentIssues>
<ID>LongMethod:LobstersApp.kt$@OptIn(ExperimentalMaterial3Api::class) @Composable fun LobstersApp( urlLauncher: UrlLauncher, htmlConverter: HTMLConverter, setWebUri: (String?) -&gt; Unit, modifier: Modifier = Modifier, viewModel: ClawViewModel = injectedViewModel(), )</ID>
<ID>LongParameterList:NetworkPosts.kt$( lazyPagingItems: LazyPagingItems&lt;LobstersPost&gt;, listState: LazyListState, isPostSaved: suspend (SavedPost) -&gt; Boolean, reloadPosts: () -&gt; Unit, postActions: PostActions, modifier: Modifier = Modifier, )</ID>
<ID>LongMethod:LobstersApp.kt$@OptIn( ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class, ) @Composable fun LobstersApp( urlLauncher: UrlLauncher, htmlConverter: HTMLConverter, setWebUri: (String?) -&gt; Unit, modifier: Modifier = Modifier, viewModel: ClawViewModel = injectedViewModel(), )</ID>
</CurrentIssues>
</SmellBaseline>
+8 -19
View File
@@ -31,19 +31,12 @@ android.injected.testOnly=false
kotlin.incremental.useClasspathSnapshot=true
kotlin.build.report.output=file
# Disable Gradle task that breaks configuration caching
kotlin.mpp.enableKotlinToolingMetadataArtifact=false
# Use R8 instead of ProGuard for code shrinking.
android.enableR8.fullMode=false
# Enable AndroidX
android.useAndroidX=true
# Kotlin Multiplatform
kotlin.mpp.stability.nowarn=true
import_orphan_source_sets=false
# Enable non-transitive R class namespacing where each library only contains
# references to the resources it declares instead of declarations plus all
# transitive dependency references.
@@ -53,9 +46,6 @@ android.nonTransitiveRClass=true
# instead of using a parameter wildcard which keeps them all.
android.useMinimalKeepRules=true
# Enable resource optimizations for release build
android.enableResourceOptimizations=true
# Default Android build features
android.defaults.buildfeatures.buildconfig=false
android.defaults.buildfeatures.aidl=false
@@ -63,16 +53,15 @@ android.defaults.buildfeatures.renderscript=false
android.defaults.buildfeatures.resvalues=false
android.defaults.buildfeatures.shaders=false
# Experimental features
android.disableEarlyManifestParsing=true
android.enablePartialRIncrementalBuilds=true
android.experimental.testOptions.uninstallIncompatibleApks=true
android.enableBuildConfigAsBytecode=true
android.experimental.enableNewResourceShrinker.preciseShrinking=true
# Disable warnings about unsupported features, we know what we're doing
android.suppressUnsupportedOptionWarnings=android.enableR8.fullMode,android.enableResourceOptimizations,android.nonTransitiveRClass,android.suppressUnsupportedOptionWarnings,android.sdk.channel
# Maven publishing
GROUP=com.github.android-password-store
# OSSRH sometimes struggles with slow deployments, so this makes Gradle
# more tolerant to those delays.
systemProp.org.gradle.internal.http.connectionTimeout=500000
systemProp.org.gradle.internal.http.socketTimeout=500000
android.suppressUnsupportedOptionWarnings=android.disableEarlyManifestParsing,android.enablePartialRIncrementalBuilds,android.experimental.testOptions.uninstallIncompatibleApks,android.enableBuildConfigAsBytecode,android.experimental.enableNewResourceShrinker.preciseShrinking,android.suppressUnsupportedOptionWarnings
# Add opens for KAPT
# https://youtrack.jetbrains.com/issue/KT-45545#focus=Comments-27-4862682.0-0
+28 -25
View File
@@ -1,31 +1,32 @@
[versions]
accompanist = "0.27.1"
# @pin https://issuetracker.google.com/issues/259556213
agp = "7.3.1"
accompanist = "0.28.0"
agp = "7.4.0"
androidx-compose-ui = "1.4.0-alpha04"
androidx-test = "1.5.0"
coil = "2.2.2"
# @keep used for kotlinCompilerExtensionVersion
composeCompiler = "1.4.0-alpha02"
composeCompiler = "1.4.0"
coroutines = "1.6.4"
dagger = "2.44.2"
kotlin = "1.7.21"
kotest = "5.5.4"
kotlin = "1.8.0"
# @pin Needs to be aligned with Retrofit
okhttp = "3.14.9"
retrofit = "2.9.0"
richtext = "0.15.0"
richtext = "0.16.0"
serialization = "1.4.1"
sqldelight = "2.0.0-alpha04"
whetstone = "0.3.0"
workmanager = "2.8.0-beta02"
sqldelight = "2.0.0-alpha05"
whetstone = "0.6.0-SNAPSHOT"
workmanager = "2.8.0-rc01"
[libraries]
accompanist-flowlayout = { module = "com.google.accompanist:accompanist-flowlayout", version.ref = "accompanist" }
accompanist-sysuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" }
androidx-activity-compose = "androidx.activity:activity-compose:1.7.0-alpha02"
androidx-benchmark-macro-junit4 = "androidx.benchmark:benchmark-macro-junit4:1.2.0-alpha07"
androidx-browser = "androidx.browser:browser:1.5.0-alpha01"
androidx-activity-compose = "androidx.activity:activity-compose:1.7.0-alpha03"
androidx-benchmark-macro-junit4 = "androidx.benchmark:benchmark-macro-junit4:1.2.0-alpha09"
androidx-browser = "androidx.browser:browser:1.5.0-beta01"
androidx-compose-animation = { module = "androidx.compose.animation:animation" }
androidx-compose-bom = "androidx.compose:compose-bom:2022.11.00"
androidx-compose-bom = "androidx.compose:compose-bom:2023.01.00"
androidx-compose-foundation = { module = "androidx.compose.foundation:foundation" }
androidx-compose-material = { module = "androidx.compose.material:material" }
androidx-compose-material3 = { module = "androidx.compose.material3:material3" }
@@ -35,24 +36,23 @@ androidx-compose-ui-text = { module = "androidx.compose.ui:ui-text" }
androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
androidx-core-splashscreen = "androidx.core:core-splashscreen:1.0.0"
androidx-lifecycle-compose = "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.0-alpha03"
androidx-lifecycle-compose = "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.0-alpha04"
androidx-navigation-compose = "androidx.navigation:navigation-compose:2.6.0-alpha04"
androidx-paging-compose = "androidx.paging:paging-compose:1.0.0-alpha17"
androidx-profileinstaller = "androidx.profileinstaller:profileinstaller:1.3.0-alpha02"
androidx-profileinstaller = "androidx.profileinstaller:profileinstaller:1.3.0-alpha03"
androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test" }
androidx-test-espresso-core = "androidx.test.espresso:espresso-core:3.5.0"
androidx-test-ext-junit = "androidx.test.ext:junit:1.1.4"
androidx-test-espresso-core = "androidx.test.espresso:espresso-core:3.5.1"
androidx-test-ext-junit = "androidx.test.ext:junit:1.1.5"
androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidx-test" }
androidx-test-runner = "androidx.test:runner:1.5.1"
androidx-test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0-alpha01"
androidx-test-runner = "androidx.test:runner:1.5.2"
androidx-test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0-alpha02"
androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workmanager" }
build-agp = { module = "com.android.tools.build:gradle", version.ref = "agp" }
build-cachefix = "org.gradle.android.cache-fix:org.gradle.android.cache-fix.gradle.plugin:2.6.0"
build-cachefix = "org.gradle.android.cache-fix:org.gradle.android.cache-fix.gradle.plugin:2.6.1"
build-detekt = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.22.0"
build-kotlin-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
build-kotlin-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin" }
build-semver = "com.vdurmont:semver4j:3.1.0"
build-spotless = "com.diffplug.spotless:spotless-plugin-gradle:6.12.0"
build-spotless = "com.diffplug.spotless:spotless-plugin-gradle:6.13.0"
build-vcu = "nl.littlerobots.version-catalog-update:nl.littlerobots.version-catalog-update.gradle.plugin:0.7.0"
build-versions = "com.github.ben-manes:gradle-versions-plugin:0.44.0"
coil = { module = "io.coil-kt:coil", version.ref = "coil" }
@@ -64,10 +64,12 @@ copydown = "io.github.furstenheim:copy_down:1.1"
crux = "com.chimbori.crux:crux:3.11.0"
dagger = { module = "com.google.dagger:dagger", version.ref = "dagger" }
dagger-compiler = { module = "com.google.dagger:dagger-compiler", version.ref = "dagger" }
eithernet = "com.slack.eithernet:eithernet:1.2.1"
eithernet = "com.slack.eithernet:eithernet:1.3.1"
javapoet = "com.squareup:javapoet:1.13.0"
javax-inject = "javax.inject:javax.inject:1"
jsoup = "org.jsoup:jsoup:1.15.3"
kotest-assertions-core = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest" }
kotest-runner-junit5 = { module = "io.kotest:kotest-runner-junit5", version.ref = "kotest" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "serialization" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }
@@ -85,6 +87,7 @@ whetstone = { module = "com.deliveryhero.whetstone:whetstone", version.ref = "wh
[plugins]
android-test = { id = "com.android.test", version.ref = "agp" }
anvil = "com.squareup.anvil:2.4.2"
anvil = "com.squareup.anvil:2.4.4"
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
whetstone = { id = "com.deliveryhero.whetstone", version.ref = "whetstone" }
whetstone = { id = "dev.msfjarvis.whetstone", version.ref = "whetstone" }
Binary file not shown.
+2 -2
View File
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=7ba68c54029790ab444b39d7e293d3236b2632631fb5f2e012bb28b4ff669e4b
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
distributionSha256Sum=28ebe9afc20564bcdc39bfe36f6b60a373e40be2c3c307a0028b545b8ccf6ba0
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-rc-2-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+2 -2
View File
@@ -144,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -152,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac

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