Compare commits

...
214 Commits
Author SHA1 Message Date
GitHub Actions 595dbd96e9 feat(release): bump version 2023-02-01 13:51:21 +00:00
Harsh Shandilya b62bc8dc89 chore: update changelog 2023-02-01 15:29:29 +05:30
GitHub Actions a909f1274d chore(android): refresh baseline profile 2023-02-01 07:46:45 +00:00
GitHub Actions 94a7ad788b chore(android): refresh baseline profile 2023-02-01 07:25:57 +00:00
Harsh Shandilya cfa8bc4971 fix(benchmark): enforce profile stability 2023-02-01 11:17:40 +05:30
GitHub Actions a4ad701aff chore(android): refresh baseline profile 2023-01-31 01:20:11 +00:00
renovate[bot]andGitHub 00e94eb1cb chore(deps): update actions/cache action to v3.2.4 2023-01-30 12:14:02 +00:00
Harsh Shandilya ff478b3365 fix(renovate): specify explicit registry URLs for regex Maven deps 2023-01-30 10:03:51 +05:30
GitHub Actions 7bf599ce19 chore(android): refresh baseline profile 2023-01-30 01:17:26 +00:00
GitHub Actions a4b9e227bf chore(android): refresh baseline profile 2023-01-29 01:17:30 +00:00
GitHub Actions cb9039e927 chore(android): refresh baseline profile 2023-01-28 01:17:50 +00:00
renovate[bot]andGitHub bf65027105 chore(deps): update github/codeql-action digest to 3ebbd71 2023-01-27 11:23:21 +00:00
GitHub Actions 28d655efc2 chore(android): refresh baseline profile 2023-01-27 09:47:51 +00:00
renovate[bot]andGitHub e711781eb0 fix(deps): update dependency com.diffplug.spotless:spotless-plugin-gradle to v6.14.0 2023-01-27 04:47:56 +00:00
renovate[bot]andGitHub 8576bad580 chore(deps): update jetbrains/qodana-action action to v2022.3.2 2023-01-27 01:49:31 +00:00
GitHub Actions 56e089ffa9 chore(android): refresh baseline profile 2023-01-27 01:29:13 +00:00
renovate[bot]andGitHub 6625ebcfc7 chore(deps): update github/codeql-action digest to 436dbd9 2023-01-26 18:02:20 +00:00
Harsh Shandilya 540ea7ba43 feat: upgrade kotlinx.serialization to 1.5.0-RC 2023-01-26 21:49:29 +05:30
Harsh Shandilya dea2d8b468 fix(android): raise target API suppression in manifest 2023-01-26 11:49:08 +05:30
renovate[bot]andHarsh Shandilya ba292bf709 fix(deps): update dependency androidx.lifecycle:lifecycle-viewmodel-compose to v2.6.0-alpha05
(cherry picked from commit 073a8d00dc6bfb10654b040e3d79dc296b01aab2)
2023-01-26 10:35:22 +05:30
renovate[bot]andHarsh Shandilya 2ce4d1de2e fix(deps): update dependency androidx.browser:browser to v1.5.0-rc01
(cherry picked from commit ea010bf8e1c5dfb1497c2a2478054cf0927dc3db)
2023-01-26 10:35:21 +05:30
renovate[bot]andHarsh Shandilya 9d6712d94b fix(deps): update dependency androidx.activity:activity-compose to v1.7.0-alpha04
(cherry picked from commit ef7b1062eebea83c935e57a39477bc6378619596)
2023-01-26 10:35:21 +05:30
Harsh Shandilya 4e920328e1 fix(gradle): allow resolution of androidx.resourceinspection
Newer AppCompat releases depend on this
2023-01-26 10:35:11 +05:30
GitHub Actions 1865c62db8 chore(android): refresh baseline profile 2023-01-24 01:18:43 +00:00
GitHub Actions d61206a7d2 chore(android): refresh baseline profile 2023-01-23 01:19:01 +00:00
GitHub Actions a94c48028d chore(android): refresh baseline profile 2023-01-22 01:22:12 +00:00
GitHub Actions 909c615ae6 feat(release): start next development iteration 2023-01-21 07:26:31 +00:00
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
87 changed files with 25804 additions and 13766 deletions
+3
View File
@@ -3,3 +3,6 @@
e7f4c8a7ebe4e907f65af46e6dddfd3a542415c0
ab2713154f846b3db6d44625d1022bf421b275d0
cbdfa67d075ec1f8ceec0f3559498cbca01783a0
# Renamed Renovate config file
f304304bc5b64edf189aaa473b05df912067a8bd
+1 -1
View File
@@ -1,4 +1,4 @@
* text=auto eol=lf
**/dependencies/** linguist-generated
**/clawicons/** linguist-generated
android/src/main/baseline-prof.txt linguist-generated
android/src/main/baseline-prof.txt linguist-generated -diff
@@ -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": [
{
@@ -52,7 +36,8 @@
"KTFMT_VERSION = \"(?<currentValue>.*)\""
],
"datasourceTemplate": "maven",
"depNameTemplate": "com.facebook:ktfmt"
"depNameTemplate": "com.facebook:ktfmt",
"registryUrlTemplate": "https://repo1.maven.org/maven2/",
},
{
"fileMatch": [
@@ -62,7 +47,8 @@
"TWITTER_RULES_VERSION = \"(?<currentValue>.*)\""
],
"datasourceTemplate": "maven",
"depNameTemplate": "com.twitter.compose.rules:detekt"
"depNameTemplate": "com.twitter.compose.rules:detekt",
"registryUrlTemplate": "https://repo1.maven.org/maven2/",
},
{
"fileMatch": [
@@ -72,7 +58,18 @@
"composeCompiler = \"(?<currentValue>.*)\""
],
"datasourceTemplate": "maven",
"depNameTemplate": "androidx.compose.compiler:compiler"
"depNameTemplate": "androidx.compose.compiler:compiler",
"registryUrlTemplate": "https://maven.google.com",
},
{
"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@627f0f41f6904a5b1efbaed9f96d9eb58e92e920 # v3.2.4
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
+8 -6
View File
@@ -18,10 +18,10 @@ jobs:
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@c3ac5dd0ed8db40fedb61c32fbe677e6b355e94c # v3.8.0
uses: actions/setup-java@1df8dbefe2a8cbc99770194893dd902763bee34b # v3.9.0
with:
distribution: temurin
java-version: 18
@@ -42,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
@@ -53,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@c3ac5dd0ed8db40fedb61c32fbe677e6b355e94c # v3.8.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 }}
+5 -9
View File
@@ -1,11 +1,7 @@
name: "CodeQL"
on:
push:
branches:
- main
schedule:
- cron: "20 3 * * 0"
workflow_dispatch:
jobs:
analyze:
@@ -17,14 +13,14 @@ jobs:
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3
- name: Initialize CodeQL
uses: github/codeql-action/init@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2
uses: github/codeql-action/init@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # v2
with:
languages: java
- name: Autobuild
uses: github/codeql-action/autobuild@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2
uses: github/codeql-action/autobuild@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2
uses: github/codeql-action/analyze@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # v2
with:
category: "/language:java"
+3 -6
View File
@@ -1,19 +1,16 @@
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches:
- main
jobs:
qodana:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3
with:
fetch-depth: 0
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2022.2.3
uses: JetBrains/qodana-action@c25ee7dc4d0697b27ac54877a4ff71cf5b6dde3c # v2022.3.2
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
+7 -4
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@c3ac5dd0ed8db40fedb61c32fbe677e6b355e94c # v3.8.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
+72 -5
View File
@@ -7,6 +7,59 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [1.19.0] - 2023-02-01
### Changed
- Upgrade dependencies
- Optimize packaged baseline profiles
## [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
@@ -104,15 +157,29 @@ 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.12.0...HEAD
[Unreleased]: https://github.com/msfjarvis/compose-lobsters/compare/v1.19.0...HEAD
[1.12.0]: https://github.com/msfjarvis/compose-lobsters/compare/1.11.0...1.12.0
[1.19.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.18.0...v1.19.0
[1.11.0]: https://github.com/msfjarvis/compose-lobsters/compare/1.10.0...1.11.0
[1.18.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.17.0...v1.18.0
[1.10.0]: https://github.com/msfjarvis/compose-lobsters/compare/1.9.0...1.10.0
[1.17.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.16.0...v1.17.0
[1.9.0]: https://github.com/msfjarvis/compose-lobsters/compare/1.8.0...1.9.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 -6
View File
@@ -2,18 +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).
<img src="https://github.com/msfjarvis/compose-lobsters/blob/main/.github/readme_feature.webp"
alt="Get it on Google Play"
height="550" />
## 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
-27
View File
@@ -1,29 +1,2 @@
# Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
static <1>$Companion Companion;
}
# Keep `serializer()` on companion objects (both default and named) of serializable classes.
-if @kotlinx.serialization.Serializable class ** {
static **$* *;
}
-keepclassmembers class <2>$<3> {
kotlinx.serialization.KSerializer serializer(...);
}
# Keep `INSTANCE.serializer()` of serializable objects.
-if @kotlinx.serialization.Serializable class ** {
public static ** INSTANCE;
}
-keepclassmembers class <1> {
public static <1> INSTANCE;
kotlinx.serialization.KSerializer serializer(...);
}
# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
-dontobfuscate
-keepattributes SourceFile, LineNumberTable
+5 -1
View File
@@ -13,7 +13,7 @@
android:supportsRtl="true"
android:theme="@style/Theme.Claw.SplashScreen"
android:enableOnBackInvokedCallback="true"
tools:targetApi="s">
tools:targetApi="tiramisu">
<profileable
android:shell="true"
tools:targetApi="29" />
@@ -149,6 +149,10 @@
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
+24944 -13383
View File
File diff suppressed because it is too large Load Diff
@@ -46,7 +46,7 @@ class ClawApplication : Application(), ApplicationComponentOwner {
WorkManager.getInstance(this)
.enqueueUniquePeriodicWork(
"updateSavedPosts",
ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
ExistingPeriodicWorkPolicy.UPDATE,
postUpdateWorkRequest,
)
}
@@ -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.
@@ -217,6 +217,8 @@ fun LobstersApp(
postId = postId,
postActions = postActions,
htmlConverter = htmlConverter,
getSeenComments = viewModel::getSeenComments,
markSeenComments = viewModel::markSeenComments,
)
}
composable(
@@ -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 },
@@ -57,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(
@@ -70,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
@@ -38,9 +42,13 @@ constructor(
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
) : ViewModel() {
private val hottestPostsPager =
Pager(PagingConfig(pageSize = 20)) { pagingSourceFactory.create(api::getHottestPosts) }
Pager(PagingConfig(pageSize = PAGE_SIZE), initialKey = STARTING_PAGE_INDEX) {
pagingSourceFactory.create(api::getHottestPosts)
}
private val newestPostsPager =
Pager(PagingConfig(pageSize = 20)) { pagingSourceFactory.create(api::getHottestPosts) }
Pager(PagingConfig(pageSize = PAGE_SIZE), initialKey = STARTING_PAGE_INDEX) {
pagingSourceFactory.create(api::getNewestPosts)
}
val hottestPosts
get() = hottestPostsPager.flow
@@ -80,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) }
@@ -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=11200
versioning-plugin.versionName=1.12.0
versioning-plugin.versionCode=11900
versioning-plugin.versionName=1.19.0
+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)
@@ -13,11 +13,17 @@ import dev.msfjarvis.claw.model.LobstersPost
import dev.msfjarvis.claw.model.LobstersPostDetails
import dev.msfjarvis.claw.model.User
import dev.msfjarvis.claw.util.TestUtils.getJson
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonNamingStrategy
@OptIn(ExperimentalSerializationApi::class)
class ApiWrapper(controller: EitherNetController<LobstersApi>) {
private val json = Json { ignoreUnknownKeys = true }
private val json = Json {
ignoreUnknownKeys = true
namingStrategy = JsonNamingStrategy.SnakeCase
}
private val hottest: List<LobstersPost> = json.decodeFromString(getJson("hottest.json"))
private val postDetails: LobstersPostDetails =
json.decodeFromString(getJson("post_details_tdfoqh.json"))
@@ -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.
@@ -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()
}
}
}
@@ -8,15 +8,13 @@ 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
@ExperimentalStableBaselineProfilesApi
@MediumTest
@OptIn(ExperimentalStableBaselineProfilesApi::class)
class BaselineProfileGenerator {
@get:Rule val baselineProfileRule = BaselineProfileRule()
private lateinit var device: UiDevice
@@ -31,8 +29,8 @@ class BaselineProfileGenerator {
fun startup() =
baselineProfileRule.collectStableBaselineProfile(
packageName = PACKAGE_NAME,
maxIterations = 5,
maxIterations = 15,
) {
exploreUI(device)
startActivityAndWait()
}
}
@@ -19,6 +19,7 @@ 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 {
+4 -5
View File
@@ -47,14 +47,14 @@ gradlePlugin {
id = "dev.msfjarvis.claw.kotlin-common"
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.claw.gradle.KotlinKaptPlugin"
}
register("kotlin-library") {
id = "dev.msfjarvis.claw.kotlin-library"
implementationClass = "dev.msfjarvis.claw.gradle.KotlinLibraryPlugin"
}
register("rename-artifacts") {
id = "dev.msfjarvis.claw.rename-artifacts"
implementationClass = "dev.msfjarvis.claw.gradle.RenameArtifactsPlugin"
@@ -79,7 +79,6 @@ dependencies {
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)
@@ -1,5 +1,5 @@
/*
* 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.
@@ -23,8 +23,6 @@ class ApplicationPlugin : Plugin<Project> {
project.pluginManager.apply(AppPlugin::class)
project.pluginManager.apply(AndroidCommonPlugin::class)
project.extensions.configure<ApplicationExtension> {
installation.installOptions("--user 0")
dependenciesInfo {
includeInBundle = false
includeInApk = false
@@ -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.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()
}
}
@@ -9,11 +9,12 @@ 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,5 +1,5 @@
/*
* 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.
@@ -11,7 +11,6 @@ import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.api.variant.VariantOutputConfiguration
import dev.msfjarvis.claw.gradle.artifacts.CollectApksTask
import dev.msfjarvis.claw.gradle.artifacts.CollectBundleTask
import java.util.Locale
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
@@ -24,7 +23,7 @@ class RenameArtifactsPlugin : Plugin<Project> {
project.pluginManager.withPlugin("com.android.application") {
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))
-1
View File
@@ -27,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.
@@ -26,15 +26,16 @@ 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.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
@@ -43,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,
@@ -101,7 +101,7 @@ fun CommentsHeader(
}
@Composable
fun PostLink(
private fun PostLink(
linkMetadata: LinkMetadata,
modifier: Modifier = Modifier,
) {
@@ -133,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,
@@ -159,6 +160,7 @@ fun CommentEntry(
buildCommenterString(
commenterName = comment.user.username,
score = comment.score,
isUnread = commentNode.isUnread,
createdAt = comment.createdAt,
updatedAt = comment.updatedAt,
),
@@ -167,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),
@@ -183,6 +185,7 @@ fun CommentEntry(
fun buildCommenterString(
commenterName: String,
score: Int,
isUnread: Boolean,
createdAt: TemporalAccessor,
updatedAt: TemporalAccessor,
): AnnotatedString {
@@ -221,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
}
@@ -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,7 +12,7 @@ import androidx.compose.ui.graphics.painter.Painter
import coil.compose.AsyncImage
@Composable
fun NetworkImage(
internal fun NetworkImage(
url: String?,
placeholder: Painter,
contentDescription: String,
@@ -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.
@@ -14,14 +14,15 @@ import dagger.Provides
import dagger.multibindings.IntoSet
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonNamingStrategy
import okhttp3.MediaType
import retrofit2.Converter
@Module
@ContributesTo(ApplicationScope::class)
@OptIn(ExperimentalSerializationApi::class)
object RetrofitModule {
@OptIn(ExperimentalSerializationApi::class)
@Provides
@IntoSet
fun provideJsonConverterFactory(json: Json): Converter.Factory {
@@ -31,6 +32,9 @@ object RetrofitModule {
@Provides
fun provideJsonSerializer(): Json {
return Json { ignoreUnknownKeys = true }
return Json {
ignoreUnknownKeys = true
namingStrategy = JsonNamingStrategy.SnakeCase
}
}
}
+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)
+9 -8
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)
}
}
}
@@ -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
}
}
}
@@ -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.
@@ -9,13 +9,13 @@ 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 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 SqlDelightQueriesTest : FunSpec() {
class SavedPostQueriesTest : FunSpec() {
private lateinit var postQueries: SavedPostQueries
@@ -26,7 +26,8 @@ class SqlDelightQueriesTest : FunSpec() {
val database =
LobstersDatabase(
driver,
SavedPost.Adapter(IntColumnAdapter, TagsAdapter()),
PostComments.Adapter(CSVAdapter()),
SavedPost.Adapter(IntColumnAdapter, CSVAdapter()),
)
postQueries = database.savedPostQueries
}
@@ -131,6 +132,7 @@ class SqlDelightQueriesTest : FunSpec() {
submitterName = "test_user_$i",
submitterAvatarUrl = "test_avatar_url",
tags = listOf(),
description = "",
)
posts.add(post)
+2 -18
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.
@@ -63,21 +56,12 @@ android.defaults.buildfeatures.shaders=false
# Experimental features
android.disableEarlyManifestParsing=true
android.enablePartialRIncrementalBuilds=true
# TODO: enable once we're back on AGP 8.x
# android.experimental.testOptions.uninstallIncompatibleApks=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.disableEarlyManifestParsing,android.enablePartialRIncrementalBuilds,android.enableBuildConfigAsBytecode,android.experimental.enableNewResourceShrinker.preciseShrinking,android.suppressUnsupportedOptionWarnings
# 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
+23 -23
View File
@@ -1,32 +1,32 @@
[versions]
accompanist = "0.28.0"
# @pin https://issuetracker.google.com/issues/259556213
agp = "7.3.1"
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"
kotest = "5.5.4"
kotlin = "1.7.21"
kotlin = "1.8.0"
# @pin Needs to be aligned with Retrofit
okhttp = "3.14.9"
retrofit = "2.9.0"
richtext = "0.16.0"
serialization = "1.4.1"
sqldelight = "2.0.0-alpha04"
whetstone = "0.5.0-SNAPSHOT"
serialization = "1.5.0-RC"
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-alpha08"
androidx-browser = "androidx.browser:browser:1.5.0-alpha02"
androidx-activity-compose = "androidx.activity:activity-compose:1.7.0-alpha04"
androidx-benchmark-macro-junit4 = "androidx.benchmark:benchmark-macro-junit4:1.2.0-alpha09"
androidx-browser = "androidx.browser:browser:1.5.0-rc01"
androidx-compose-animation = { module = "androidx.compose.animation:animation" }
androidx-compose-bom = "androidx.compose:compose-bom:2022.12.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" }
@@ -36,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-alpha05"
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.14.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" }
@@ -65,12 +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-runner-junit5 = { module = "io.kotest:kotest-runner-junit5", version.ref = "kotest" }
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" }
@@ -88,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 = "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
+1 -4
View File
@@ -6,10 +6,7 @@
*/
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
plugins {
kotlin("jvm")
id("dev.msfjarvis.claw.kotlin-library")
}
plugins { id("dev.msfjarvis.claw.kotlin-jvm") }
dependencies {
api(libs.crux)
+2 -3
View File
@@ -7,9 +7,8 @@
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
plugins {
kotlin("jvm")
kotlin("plugin.serialization")
id("dev.msfjarvis.claw.kotlin-common")
id("dev.msfjarvis.claw.kotlin-jvm")
alias(libs.plugins.kotlin.serialization)
}
dependencies { implementation(libs.kotlinx.serialization.core) }
@@ -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,15 +15,12 @@ import kotlinx.serialization.Serializable
@Serializable
class Comment(
val shortId: String,
val comment: String,
val url: String,
val score: Int,
@Serializable(with = JavaInstantSerializer::class)
@SerialName("created_at")
val createdAt: TemporalAccessor,
@Serializable(with = JavaInstantSerializer::class)
@SerialName("updated_at")
val updatedAt: TemporalAccessor,
@SerialName("indent_level") val indentLevel: Int,
@Serializable(with = JavaInstantSerializer::class) val createdAt: TemporalAccessor,
@Serializable(with = JavaInstantSerializer::class) val updatedAt: TemporalAccessor,
val indentLevel: Int,
@SerialName("commenting_user") val user: User,
)
@@ -13,13 +13,13 @@ import kotlinx.serialization.Serializable
@Serializable
class LobstersPost(
@SerialName("short_id") val shortId: String,
@SerialName("created_at") val createdAt: String,
val shortId: String,
val createdAt: String,
val title: String,
val url: String,
val description: String,
@SerialName("comment_count") val commentCount: Int,
@SerialName("comments_url") val commentsUrl: String,
val commentCount: Int,
val commentsUrl: String,
@SerialName("submitter_user") val submitter: User,
val tags: List<String>,
)
@@ -13,13 +13,13 @@ import kotlinx.serialization.Serializable
@Serializable
class LobstersPostDetails(
@SerialName("short_id") val shortId: String,
@SerialName("created_at") val createdAt: String,
val shortId: String,
val createdAt: String,
val title: String,
val url: String,
val description: String,
@SerialName("comment_count") val commentCount: Int,
@SerialName("comments_url") val commentsUrl: String,
val commentCount: Int,
val commentsUrl: String,
@SerialName("submitter_user") val submitter: User,
val tags: List<String>,
val comments: List<Comment>,
@@ -15,7 +15,7 @@ import kotlinx.serialization.Serializable
class User(
val username: String,
val about: String,
@SerialName("invited_by_user") val invitedBy: String,
@SerialName("avatar_url") val avatarUrl: String,
@SerialName("created_at") val createdAt: String,
@SerialName("invited_by_user") val invitedBy: String?,
val avatarUrl: String,
val createdAt: String,
)
+3
View File
@@ -0,0 +1,3 @@
version: "1.0"
# Also update renovate.json when the linter is changed
linter: jetbrains/qodana-jvm-android:2022.3-eap
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
set -euxo pipefail
TEMP_DIR="$(mktemp -d)"
BIN_DIR=""
AGE_VERSION="v1.0.0"
AGE_FILE=""
case "$(uname)" in
Linux)
AGE_FILE="age-${AGE_VERSION}-linux-amd64.tar.gz"
BIN_DIR="${HOME}/.local/bin"
;;
Darwin)
AGE_FILE="age-${AGE_VERSION}-darwin-amd64.tar.gz"
BIN_DIR="${HOME}/bin"
;;
*) echo "Unsupported system: $(uname)"; exit 1 ;;
esac
pushd "${TEMP_DIR}"
curl -L --silent --show-error --retry 3 --fail -o age.tar.gz "https://github.com/FiloSottile/age/releases/download/v1.0.0/${AGE_FILE:?}"
tar xvf age.tar.gz
rm age/LICENSE
mkdir -p "${BIN_DIR}"
mv -v age/age "${BIN_DIR}" && chmod +x "${BIN_DIR}/age"
mv -v age/age-keygen "${BIN_DIR}" && chmod +x "${BIN_DIR}/age-keygen"
popd
+17 -11
View File
@@ -2,16 +2,22 @@
set -euo pipefail
ENCRYPT_KEY="${1:-}"
ENCRYPT_KEY="${1}"
TEMP_KEY="$(mktemp)"
declare -A SECRETS
SECRETS[secrets/keystore.cipher]=keystore.jks
SECRETS[secrets/props.cipher]=keystore.properties
trap "rm -rf ${TEMP_KEY} 2>/dev/null" INT TERM EXIT
if [[ -n "$ENCRYPT_KEY" ]]; then
for src in "${!SECRETS[@]}"; do
openssl enc -aes-256-cbc -md sha256 -pbkdf2 -d -in "${src}" -out "${SECRETS[${src}]}" -k "${ENCRYPT_KEY}"
done
else
echo "Usage: ./signing-setup.sh <encryption key>"
fi
echo "${ENCRYPT_KEY:?}" > "${TEMP_KEY}"
function decrypt() {
if ! command -v age 1>/dev/null; then
echo "age not installed"
exit 1
fi
SRC="${1}"
DST="${2}"
age --decrypt -i "${TEMP_KEY}" -o "${DST:?}" "${SRC:?}"
}
decrypt secrets/keystore.cipher keystore.jks
decrypt secrets/props.cipher keystore.properties
Binary file not shown.
Binary file not shown.
+3 -17
View File
@@ -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.
@@ -26,21 +26,12 @@ pluginManagement {
includeModule("com.android.test", "com.android.test.gradle.plugin")
}
}
exclusiveContent {
forRepository { maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") }
filter {
includeGroupByRegex("org\\.jetbrains\\.compose.*")
includeGroup("org.jetbrains.skiko")
}
}
exclusiveContent {
forRepository { gradlePluginPortal() }
filter {
includeModule("com.github.ben-manes", "gradle-versions-plugin")
includeModule("org.gradle.android.cache-fix", "org.gradle.android.cache-fix.gradle.plugin")
includeModule("gradle.plugin.org.gradle.android", "android-cache-fix-gradle-plugin")
includeModule("com.sergei-lapin.napt", "com.sergei-lapin.napt.gradle.plugin")
includeModule("com.sergei-lapin.napt", "gradle")
}
}
exclusiveContent {
@@ -80,6 +71,7 @@ dependencyResolutionManagement {
includeGroup("androidx.customview")
includeGroup("androidx.databinding")
includeGroup("androidx.drawerlayout")
includeGroup("androidx.emoji2")
includeGroup("androidx.exifinterface")
includeGroup("androidx.fragment")
includeGroup("androidx.interpolator")
@@ -88,6 +80,7 @@ dependencyResolutionManagement {
includeGroup("androidx.navigation")
includeGroup("androidx.paging")
includeGroup("androidx.profileinstaller")
includeGroup("androidx.resourceinspection")
includeGroup("androidx.room")
includeGroup("androidx.savedstate")
includeGroup("androidx.startup")
@@ -118,13 +111,6 @@ dependencyResolutionManagement {
includeModule("com.google.android.material", "material")
}
}
exclusiveContent {
forRepository { maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") }
filter {
includeGroupByRegex("org\\.jetbrains\\.compose.*")
includeGroup("org.jetbrains.skiko")
}
}
exclusiveContent {
forRepository { maven("https://oss.sonatype.org/content/repositories/snapshots/") }
filter { includeGroup("dev.msfjarvis.whetstone") }
-25
View File
@@ -1,25 +0,0 @@
{ pkgs ? import <nixpkgs> { config.android_sdk.accept_license = true; } }:
let
androidComposition = pkgs.androidenv.composeAndroidPackages {
toolsVersion = "26.1.1";
platformToolsVersion = "31.0.3";
buildToolsVersions = [ "31.0.0" ];
includeEmulator = false;
emulatorVersion = "30.9.0";
platformVersions = [ "31" ];
includeSources = false;
includeSystemImages = false;
systemImageTypes = [ ];
abiVersions = [ ];
cmakeVersions = [ ];
includeNDK = false;
ndkVersions = [ ];
useGoogleAPIs = false;
useGoogleTVAddOns = false;
includeExtras = [ ];
};
in pkgs.mkShell rec {
ANDROID_SDK_ROOT = "${androidComposition.androidsdk}/libexec/android-sdk";
buildInputs = with pkgs; [ gradle glibc zlib ];
}