Compare commits

..
857 Commits
Author SHA1 Message Date
c1bd679046 Merge #256
256: Upgrade Dagger and fix dependency mess r=msfjarvis a=msfjarvis

Fixes #255

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-26 03:51:21 +00:00
Harsh Shandilya 34cfeb3250 build: bump Dagger to 2.36
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-26 09:20:03 +05:30
Harsh Shandilya 6bbcb979c3 Revert "build: upgrade all dependencies"
This reverts commit ffd46f1d29.
2021-05-23 18:53:03 +05:30
Harsh Shandilya 5ef621d26a Revert "github: switch snapshots for debug builds"
This reverts commit d7c1c6bca6.
2021-05-23 18:53:02 +05:30
d5bc33a280 Merge #254
254: Upgrade all dependencies r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-22 10:36:30 +00:00
Harsh Shandilya d7c1c6bca6 github: switch snapshots for debug builds
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-22 16:05:50 +05:30
Harsh Shandilya 4b968f3bed app: customize app name for debug builds
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-22 16:05:50 +05:30
Harsh Shandilya ffd46f1d29 build: upgrade all dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-22 16:05:50 +05:30
70b4edcbb7 Merge #252
252: Handle lobste.rs deeplinks in the app r=msfjarvis a=Skrilltrax

Deeplink handling in Android is _interesting_.

Here's what is happening atm, 

```
<data
	android:ssp="//lobste.rs"
    android:scheme="https" />
```
Handles `https://lobste.rs`. We are using `ssp` here because using `host` without path `/` will match with all of the possible paths e.g, `https://lobste.rs/login` which we do not want.


```
<data
	android:host="lobste.rs"
    android:scheme="https"
    android:path="/"/>
```
Handles `https://lobste.rs/`


```
<data
	android:host="lobste.rs"
    android:scheme="https"
    android:pathPrefix="/recent" />
```
Handles `https://lobste.rs/recent` and `https://lobste.rs/recent/`


```
<data
	android:host="lobste.rs"
    android:scheme="https"
    android:pathPattern="/s/......" />
```
Handles `https://lobste.rs/nr9kzv`.

```
<data
	android:host="lobste.rs"
    android:scheme="https"
    android:pathPattern="/s/....../" />
```
Handles `https://lobste.rs/nr9kzv/`.

```
<data
	android:host="lobste.rs"
    android:scheme="https"
    android:pathPattern="/s/....../...*" />
```
Handles everything after `https://lobste.rs/s/nr9kzv/` except for a case where the `postTitle` is one character long. This one character string is used for the reply URL.

Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
2021-05-16 17:28:27 +00:00
Aditya WasanandHarsh Shandilya 96d8bfdb73 HACK: app: add workaround to prevent deeplinking comment replies back to the app
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-05-16 22:52:23 +05:30
Aditya WasanandHarsh Shandilya bdb1c8ef1b manifest: remove comments pathPattern
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-05-16 22:52:00 +05:30
Aditya WasanandHarsh Shandilya ce66536dde app: initial deeplinks handling
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-05-16 22:51:59 +05:30
829e1906d6 Merge #250
250: Bump Gradle to 7.0.2 r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-16 06:07:44 +00:00
Harsh Shandilya 8f1153b05b Bump Gradle
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-16 11:37:08 +05:30
92db1a4ce5 Merge #249
249: Remove duplicate UrlLauncher from desktop r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-12 23:40:27 +00:00
Harsh Shandilya 4ca4fe81f0 desktop: use the UrlLauncher from common
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-13 04:20:48 +05:30
d00cf77d1d Merge #248
248: Update JB Compose r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-12 17:06:31 +00:00
Harsh Shandilya 4d6eb9ff1d gradle: bump JB Compose to 0.4.0-build190
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-12 22:32:52 +05:30
022de71c62 Merge #247
247: Bump Gradle to 7.0.1 r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-10 18:52:50 +00:00
Harsh Shandilya 0fde488dde gradle: bump to 7.0.1
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-11 00:22:11 +05:30
76c758d027 Merge #246
246: Bump deps and remove swipe refreshing for now r=msfjarvis a=msfjarvis

Swipe refresh is causing crashes for me deep in the bowels of NestedScroll

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-08 19:33:48 +00:00
Harsh Shandilya b847feae22 app: remove swipe refresh
This is causing crashes

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-09 00:49:56 +05:30
Harsh Shandilya 5a4efa33cf gradle: bump accompanist and activity-compose
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-09 00:30:49 +05:30
fb45ed99c2 Merge #245
245: Enable cacheability of internal AGP tasks r=msfjarvis a=msfjarvis

Taken from https://issuetracker.google.com/issues/141301405

bors r+


Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-06 16:28:07 +00:00
Harsh Shandilya 9967a534bd gradle: enable cacheability of internal AGP tasks
https://issuetracker.google.com/issues/141301405

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-06 21:56:45 +05:30
9745243cda Merge #244
244: AndroidX update day r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-05 19:39:10 +00:00
Harsh Shandilya ab10e04c72 gradle: bump deps
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-06 01:08:29 +05:30
d9ebe79ec7 Merge #243
243: Fix target SDK r=msfjarvis a=msfjarvis

Copypasta fail in #238
bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-05 08:04:20 +00:00
Harsh Shandilya e2774e48cd Set target SDK back to 30
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-05 13:33:18 +05:30
a6c476a97a Merge #242
242: Remove Shot entirely r=msfjarvis a=msfjarvis

It hasn't worked in a month and the dependencies are just dead weight

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-04 21:28:34 +00:00
Harsh Shandilya a4313bc401 Completely remove screenshot tests
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-05 02:58:04 +05:30
Harsh Shandilya e8b313605f app: directly use AndroidX dependencies
The JB Compose Gradle plugin will inject substitution rules automatically

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-05 02:36:55 +05:30
c1a56e59dd Merge #241
241: Bump JB Compose r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-04 20:39:07 +00:00
Harsh Shandilya e909f6e2bd build: bump JB Compose
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-05 02:08:26 +05:30
8becbb7f53 Merge #239
239: Add FAB animation  r=msfjarvis a=Skrilltrax



Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-04 06:37:18 +00:00
Harsh Shandilya 81b4da3584 Constify scroll delta and animation duration
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-05-04 12:06:47 +05:30
Aditya Wasan ea6f633d9b app: add hide animation to comment screen fab
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-05-04 11:13:13 +05:30
09dc4fc87b Merge #238
238: Bump to AGP 7.0.0-alpha15 r=msfjarvis a=msfjarvis

Also adapts the snapshot deployment scripts for removal of variant output API.

bors r+


Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-30 04:47:32 +00:00
Harsh Shandilya 85a7d0df84 scripts/deploy-snapshot: adapt for removal of variant output API
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-30 10:10:24 +05:30
Harsh Shandilya a602a22eb0 Bump AGP to 7.0.0-alpha15
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-30 10:08:19 +05:30
7a9cc78264 Merge #237
237: Bump Dagger r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-28 02:47:30 +00:00
Harsh Shandilya 8152f4db96 build: bump Dagger Hilt
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-28 08:16:51 +05:30
f316a6ebaa Merge #236
236: Bump JB Compose to 0.4.0-build185 r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-27 04:53:01 +00:00
Harsh Shandilya ab1a9daefb build: bump JB Compose to 0.4.0-build185
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-27 10:22:13 +05:30
bbf3261815 Merge #227
227: Track comment parents via colored lines r=msfjarvis a=msfjarvis

Work towards #204

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
2021-04-26 10:37:01 +00:00
Aditya WasanandHarsh Shandilya 45715a6c45 fix: re-use colors when comment depth becomes greater than the number of colors available
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-04-26 16:06:02 +05:30
Aditya WasanandHarsh Shandilya 635b508795 fix: set content size to be the min size of the container
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-04-26 16:05:02 +05:30
Harsh Shandilya 24f31f6fa2 Initial parent tracking implementation
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-26 16:04:59 +05:30
73708a52d9 Merge #235
235: Use bors to manage spotless check r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-24 23:16:10 +00:00
Harsh Shandilya ef37928981 github: move stylecheck to bors
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-25 04:45:30 +05:30
8f2fe76706 Merge #234
234: Integrate Spotless and check codestyle in CI r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-24 23:08:17 +00:00
Harsh Shandilya 1c2deee063 github: add stylecheck workflow
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-25 04:36:39 +05:30
Harsh Shandilya fcdced1a00 build: configure Spotless
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-25 04:35:23 +05:30
Harsh Shandilya ff17ab3e9b build: add and apply spotless plugin
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-25 04:35:23 +05:30
Harsh Shandilya 5458e174e6 build: don't add clean task
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-25 04:18:18 +05:30
7505832fb9 Merge #233
233: Revert "Merge #231" r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-24 14:19:46 +00:00
Harsh Shandilya b3bd8c8db1 Revert "Merge #231"
RIP

This reverts commit 407ab3d17a, reversing
changes made to 2cd44707b7.
2021-04-24 19:44:09 +05:30
407ab3d17a Merge #231
231: Add dependency-tree-diff workflow r=msfjarvis a=msfjarvis



Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-24 14:01:09 +00:00
Harsh Shandilya d0a9d80341 github: add dependency-tree-diff workflow
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-24 19:30:23 +05:30
2cd44707b7 Merge #230
230: Sync dependency versions r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-24 13:02:24 +00:00
Harsh Shandilya 2690525ab5 build: fix dependencies and repositories
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-24 18:31:11 +05:30
Harsh Shandilya 5dd467cb84 database: fix typecasting
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-24 18:17:10 +05:30
Harsh Shandilya 1641c896d7 buildSrc: remove deprecated warning suppression
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-24 15:23:52 +05:30
Harsh Shandilya 6c702d4556 build: sync version numbers with catalog
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-24 15:21:00 +05:30
Harsh Shandilya 457ffc2066 idea: untrack gradle.xml
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-24 15:19:56 +05:30
7f171594f3 Merge #229
229: Update SQLDelight to 1.5.0 r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-24 07:46:36 +00:00
Harsh Shandilya 4cab5459dd gradle: bump SQLDelight to 1.5.0
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-24 13:07:38 +05:30
0c31b66116 Merge #228
228: Bump Accompanist to 0.8.1 r=msfjarvis a=msfjarvis

Removes the custom elevation increase since it was changed in the library too

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-23 18:03:20 +00:00
Harsh Shandilya 343e3d3bc7 gradle: bump accompanist to 0.8.1
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-23 16:47:02 +05:30
Harsh Shandilya 5c8e6841ee Revert "NetworkPosts: increase swipe refresh indicator elevation"
This reverts commit c38ed8ff51.
2021-04-23 16:46:44 +05:30
f2ccc339aa Merge #226
226: Add moshi-metadata-reflect r=msfjarvis a=msfjarvis

This was always needed but weird cache inconsistencies kept it working on my local builds /shrug

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-23 04:40:32 +00:00
Harsh Shandilya eec16bdfbd Use moshi-metadata-reflect
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-23 10:07:44 +05:30
0675a6addb Merge #225
225: Bump Dagger and Hilt to 2.35 r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-23 01:03:48 +00:00
Harsh Shandilya 72ec208253 gradle: bump Hilt to 2.35
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-23 06:33:14 +05:30
54f293b0e7 Merge #224
224: Increase SwipeRefreshIndicator elevation r=msfjarvis a=msfjarvis

Matches the change in Accompanist

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-22 11:03:50 +00:00
Harsh Shandilya 36537a1edf all: run ktfmt
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-22 16:25:52 +05:30
Harsh Shandilya c38ed8ff51 NetworkPosts: increase swipe refresh indicator elevation
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-22 16:25:52 +05:30
06a03157d3 Merge #223
223: Bump KSP to 1.4.32-1.0.0-alpha08 r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-22 04:46:37 +00:00
Harsh Shandilya e5ac0394b9 gradle: bump KSP to 1.4.32-1.0.0-alpha08
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-22 10:15:55 +05:30
bc3ece6ca8 Merge #222
222: Tie refresh state with Paging r=msfjarvis a=msfjarvis

This makes the indicator go away in a more consistent fashion

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-21 19:18:07 +00:00
Harsh Shandilya 4367a2821f NetworkPosts: tie refresh state directly into Paging
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-22 00:44:53 +05:30
baa60254f0 Merge #221
221: Update dependencies r=msfjarvis a=Skrilltrax



Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-21 19:02:58 +00:00
Harsh Shandilya bc9f9bdac4 app: remove unused PullToRefresh code
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-22 00:30:05 +05:30
Harsh Shandilya f1d497a70c app: replace PullToRefresh with accompanist-swiperefresh
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-22 00:30:05 +05:30
Harsh Shandilya e1a6ccd9d0 app: address CoilImage deprecation
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-22 00:30:04 +05:30
Aditya WasanandHarsh Shandilya b888e127d2 build: update androidx-compose and accompanist
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-04-22 00:30:03 +05:30
5fad9ccc45 Merge #220
220: build: remove KotlinX Bintray repository r=msfjarvis a=msfjarvis

0.4.0-build182 upgraded kotlinx-collections-immutable to a version that no longer requires this repository

Ref: https://github.com/JetBrains/compose-jb/commit/8c8b4147abd0b697afd5b4ac216e426328054411

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-21 15:41:38 +00:00
Harsh ShandilyaandGitHub 92a0cb70b4 build: remove KotlinX Bintray repository
0.4.0-build182 upgraded kotlinx-immutable-jvm to a version that no longer requires this repository

Ref: https://github.com/JetBrains/compose-jb/commit/8c8b4147abd0b697afd5b4ac216e426328054411
2021-04-21 21:10:14 +05:30
55733e47af Merge #219
219: Remove IDEA files that create unnecessary churn r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-20 06:29:31 +00:00
Harsh Shandilya 843843e735 idea: remove infinitely churning files
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-20 11:58:45 +05:30
6f3685ec11 Merge #218
218: Bump JB Compose to 0.4.0-build184 r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-20 04:56:53 +00:00
Harsh Shandilya 47cc883f25 gradle: bump JB Compose
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-20 10:26:06 +05:30
7a0d8e785c Merge #217
217: Use gradle catalogs r=Skrilltrax a=Skrilltrax



Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
2021-04-17 12:34:21 +00:00
3276c0e072 Apply suggestions from code review
Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-17 18:03:41 +05:30
Aditya Wasan 6772303bf5 build: use gradle catalogs for dependency management
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-04-17 17:56:24 +05:30
561a651dc4 Merge #215
215: Fix refresh action r=msfjarvis a=msfjarvis

🤦

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-15 20:48:20 +00:00
Harsh Shandilya 65c1122bb1 app: fix paging source copypasta fail
Fixes: #211
Fixes: 654eb39877 ("app: de-duplicate paging sources")
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-16 02:17:09 +05:30
e57d0138a6 Merge #214
214: Bump JB Compose r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-15 08:55:54 +00:00
Harsh Shandilya cac7614bc0 build: bump JB compose
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-15 14:25:14 +05:30
8414567a63 Merge #213
213: Add fastlane screenshots r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-14 14:11:31 +00:00
Harsh Shandilya 3b18b74f3a fastlane: add Play Store screenshots
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-14 19:34:11 +05:30
e7254ab764 Merge #212
212: Fix submitter name color in dark mode r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-14 12:51:46 +00:00
Harsh Shandilya 2d1e1ece4d app: fix submitter text color in replies and reformat
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-14 18:21:08 +05:30
7af22a422e Merge #210
210: Bump Dagger and JetBrains Compose r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-14 05:21:04 +00:00
Harsh Shandilya 34034bee34 build: update Dagger and JB Compose
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-14 10:44:19 +05:30
f398e9b27b Merge #208
208: Bump JetBrains Compose to 0.4.0-build181 r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-13 06:26:55 +00:00
Harsh Shandilya b758f7843d build: bump JB Compose to 0.4.0-build181
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-13 11:56:18 +05:30
8a9b7d5a6c Merge #207
207: Add comment reply button r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-12 21:53:54 +00:00
Harsh Shandilya b3b8e7f509 app: add a reply button to comments view
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-13 03:23:11 +05:30
Harsh Shandilya a083230d06 app: parse description and comments the same way
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-13 03:07:03 +05:30
67f1c935ec Merge #206
206: Handle network errors in comments view r=msfjarvis a=msfjarvis

bors r+


Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-12 21:23:15 +00:00
Harsh Shandilya f14052800e Comments: show snackbar with error if we fail to get post
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-13 02:46:52 +05:30
Harsh Shandilya c409b72beb Comments: properly handle errors
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-13 02:33:44 +05:30
cbdb6c0934 Merge #205
205: Protect against database thrashing by paging r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-12 20:59:26 +00:00
Harsh Shandilya 8fc008bce9 app: update saved posts cache at ViewModel init
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-13 02:18:39 +05:30
Harsh Shandilya 654eb39877 app: de-duplicate paging sources
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-13 02:17:22 +05:30
Harsh Shandilya 8e67be7a35 BackupHandler: remove unused imports
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-13 02:08:25 +05:30
45d19a669e Merge #203
203: Update docs r=msfjarvis a=msfjarvis

bors merge

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-12 10:40:44 +00:00
Harsh Shandilya d0cbe2a27e Add more details to hacking.md
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-12 16:10:16 +05:30
Harsh Shandilya d86fda521f README: add distribution note
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-12 16:05:16 +05:30
be666be9a6 Merge #202
202: Force refresh post cache after importing saved posts r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-11 17:31:04 +00:00
Harsh Shandilya 1fe259a6f2 backup: update post cache in repository after import
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-11 22:58:02 +05:30
37c3c1cb84 Merge #201
201: Use secondary color variant for tag background r=msfjarvis a=msfjarvis

Fixes #200

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-11 17:11:02 +00:00
Harsh Shandilya 2b35f6bb9a LobstersItem: use secondary color variant for tag background
Fixes #200

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-11 22:40:39 +05:30
308489dc6c Merge #199
199: Deploy snapshots from develop merges r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-11 08:07:15 +00:00
Harsh Shandilya b0bd790141 app: set output file name
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-11 13:31:44 +05:30
Harsh Shandilya 53c96d21dd github: add snapshot deployment workflow
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-11 13:31:44 +05:30
Harsh Shandilya acc21e207f secrets: add keystore
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-11 13:31:44 +05:30
Harsh Shandilya a7c55544c3 idea: MPP churn
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-11 13:31:43 +05:30
Harsh Shandilya b621a8ebd0 scripts: add secrets and snapshot helpers
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-11 13:31:43 +05:30
b431832c83 Merge #198
198: Add licence to README r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-11 06:47:24 +00:00
Harsh Shandilya 828a8bb212 README: add licence
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-11 12:16:01 +05:30
e4bcb276df Merge #187
187: Integrate comments natively r=msfjarvis a=msfjarvis

This is just the grunt work, the actual UI and integration is still left.

bors try

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
2021-04-11 05:49:56 +00:00
Harsh Shandilya 4b5e4a43a3 app: refactor Submitter composables and re-use them in CommentsEntry
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-11 11:14:07 +05:30
Harsh Shandilya 216fee6757 idea: sync up with desktop multiplatform switch
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-11 10:50:11 +05:30
Aditya WasanandHarsh Shandilya 8e66ed043d feat: add comments ui
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-04-10 22:57:34 +05:30
Harsh Shandilya 1f60cf7d1c app: add view comments navigation action
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-10 22:01:24 +05:30
Harsh Shandilya 02b61cd36a app: add post details methods to VM
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-10 22:01:24 +05:30
Harsh Shandilya 70e0b32265 app: add logging to API module
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-10 22:01:24 +05:30
Harsh Shandilya 65c6f8ff52 app: add Comments UI composables
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-10 22:01:23 +05:30
Harsh Shandilya f37590f088 api: add tests for post details
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-10 22:01:23 +05:30
Harsh Shandilya e5ea28ea34 api: add getPostDetails method
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-10 22:01:23 +05:30
Harsh Shandilya a77e972b82 api: add models for post details
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-10 22:01:22 +05:30
Harsh Shandilya 56c4974803 api: rename Submitter to User
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-10 22:01:22 +05:30
6ec8fe035c Merge #196
196: Bump MoshiX to 0.10.0 r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-10 16:25:03 +00:00
Harsh Shandilya 9287777b87 build: bump MoshiX to 0.10.0
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-10 21:51:52 +05:30
1abf793e19 Merge #195
195: Bump AGP and Gradle r=msfjarvis a=msfjarvis

bors try

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-10 16:06:03 +00:00
Harsh Shandilya e44b743016 build: bump AGP and remove unused lint dependency
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-10 21:22:08 +05:30
Harsh Shandilya 789dae907a build: use typesafe project accessors preview feature
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-10 21:20:44 +05:30
Harsh Shandilya de0d7b28f8 desktop: fix compat with Gradle 7.0
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-10 21:19:21 +05:30
Harsh Shandilya 19248c3a30 build: bump to Gradle 7.0
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-10 21:08:11 +05:30
cdb72b7789 Merge #194
194: Improve dependency organization r=msfjarvis a=msfjarvis

Also flesh out desktop packaging configuration

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-09 18:44:17 +00:00
Harsh ShandilyaandGitHub 4a51dc8d64 all: reorganize dependencies and improve desktop packaging
Signed-off-by: GitHub <noreply@github.com>
2021-04-09 18:43:45 +00:00
388014d62b Merge #193
193: Update JetBrains Compose r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-08 13:27:22 +00:00
Harsh Shandilya f5956aa44d build: bump JetBrains Compose
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-08 18:56:05 +05:30
1e09ce7a65 Merge #191
191: Update Compose, Dagger and KSP r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-08 00:46:15 +00:00
Harsh Shandilya 24990cde08 all: bump dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-08 06:15:33 +05:30
92ebb82a6a Merge #190
190: Sort build dependencies r=msfjarvis a=msfjarvis



Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-06 21:11:30 +00:00
Harsh Shandilya ad6d023f46 common: remove unnecessary dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-07 02:25:34 +05:30
Harsh Shandilya b0d5f65dfe build: bump AGP
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-07 02:16:23 +05:30
Harsh Shandilya 50598cc3e7 build: remove JCenter repository
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-07 02:15:59 +05:30
Harsh Shandilya b3c1985d45 Revert "gradle: update to 7.0 RC1"
This reverts commit 1171fb17e6.
2021-04-07 02:15:44 +05:30
ad2bc470a1 Merge #188
188: Bump JetBrains Compose r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-06 09:37:23 +00:00
Harsh Shandilya 88ef01f129 build: bump Kotlin
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-06 15:06:48 +05:30
Harsh Shandilya 5da858aa4c build: bump JetBrains Compose
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-06 14:41:15 +05:30
e50a8e8d68 Merge #165
165: Allow backup and restore of saved posts r=msfjarvis a=msfjarvis

Very much WIP, lacks essentially everything right now.


Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-03 10:03:10 +00:00
Harsh Shandilya b4069af1c7 app: move ByteArray interpolation to BackupHandler
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-03 15:15:53 +05:30
Harsh Shandilya 31fbc8c95c Options: manually close streams
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-03 15:14:14 +05:30
Harsh Shandilya cf5410622f app: integrate settings into main UI
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-03 15:13:50 +05:30
Aditya WasanandHarsh Shandilya ef09b241ea common/app: add Settings UI composables
Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-03 15:13:47 +05:30
Harsh Shandilya a08109d745 app: add BackupHandler
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-03 15:00:57 +05:30
Aditya WasanandHarsh Shandilya d86d12f2db database: convert to multiplatform
Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-03 15:00:54 +05:30
bc9ad69260 Merge #186
186: Simplify ordering logic r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-03 09:09:40 +00:00
Harsh Shandilya 314aba6d65 app: revise saved posts sorting logic again
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-03 14:38:16 +05:30
Aditya WasanandHarsh Shandilya 995eba2c11 gradle: fix incorrect sha256
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
(cherry picked from commit 59dbf417a57b0e7cda8aa5b0efbec68f58da0e05)
2021-04-03 14:04:04 +05:30
4e73075728 Merge #185
185: Preserve backstack in navigation r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-02 15:50:27 +00:00
Harsh Shandilya f785031ad8 LobstersApp: don't clear navigation backstack on going back
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-02 21:19:00 +05:30
9c10cb02b8 Merge #184
184: Fixup lingering formatting issues r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-02 10:56:53 +00:00
Harsh Shandilya 2ab4001a43 app: remove unnecessary constructor qualifications
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-02 16:25:21 +05:30
Harsh Shandilya 94afe3c15f app: remove lingering TODO
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-02 16:24:00 +05:30
d48d1c44ea Merge #183
183: Sort saved posts months as well r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-02 10:19:50 +00:00
Harsh Shandilya 0fbee213a3 app: sort months as well as posts
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-02 15:48:26 +05:30
d2c2c73eeb Merge #182
182: Reformat source tree with ktfmt r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-02 07:38:50 +00:00
Harsh Shandilya db07a12be5 all: reformat with ktfmt google style
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-02 13:06:49 +05:30
8448910628 Merge #181
181: Update JB Compose and Moshi r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-02 05:46:24 +00:00
Harsh Shandilya a33fc389e3 bors: don't want for screenshot testing
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-02 11:15:53 +05:30
Harsh Shandilya 5637d46488 build: update Moshi
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-02 10:45:03 +05:30
Harsh Shandilya 6d71154589 all: bump JB Compose to 0.4.0-build178
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-04-02 10:36:00 +05:30
Harsh Shandilya b6540b10b9 github: disable screenshot tests task
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 16:11:54 +05:30
Harsh Shandilya a182265077 app: disable all screenshot tests
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 16:09:41 +05:30
Harsh Shandilya 0e11815206 Merge remote-tracking branches 'origin/newest-posts' and 'origin/vendor-pulltorefresh' into develop
* origin/newest-posts:
  app: update screenshot tests
  app: add missing ui-tooling dependency
  app: add newest posts screen
  app: add support for fetching newest posts
  app: start qualifying infra for hottest posts
  app: rename HottestPosts to NetworkPosts
  api: add support for fetching newest posts

* origin/vendor-pulltorefresh:
  app: add missing ui-tooling dependency
  app: vendor PullToRefresh for Compose beta03 ABI compatibility
2021-03-29 16:07:38 +05:30
Harsh Shandilya 86455a2d46 app: update screenshot tests
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 13:41:09 +05:30
Harsh Shandilya abc5475bea app: add missing ui-tooling dependency
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 12:44:42 +05:30
Harsh Shandilya cabf13fb3a app: add missing ui-tooling dependency
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 12:25:58 +05:30
Harsh Shandilya 79b03a4907 app: add newest posts screen
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 12:25:57 +05:30
Harsh Shandilya fcd27863a1 app: add support for fetching newest posts
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 12:25:57 +05:30
Harsh Shandilya e3c5097035 app: start qualifying infra for hottest posts
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 12:25:57 +05:30
Harsh Shandilya b9c253733e app: rename HottestPosts to NetworkPosts
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 12:25:56 +05:30
Harsh Shandilya 1d8c4a42a8 api: add support for fetching newest posts
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 12:25:56 +05:30
Harsh Shandilya 48f45beeeb app: vendor PullToRefresh for Compose beta03 ABI compatibility
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 12:19:29 +05:30
5c7f38c2dc Merge #175
175: Update all dependencies r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 05:44:33 +00:00
Harsh Shandilya c4e3b2f6dd app: update screenshots
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 05:43:33 +05:30
Harsh Shandilya f26e8be3f4 app: switch to accompanist-flowlayout
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 05:43:32 +05:30
Harsh Shandilya a42012a958 build: update AndroidX, Compose and Accompanist
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 05:43:32 +05:30
Harsh Shandilya 659676adc2 common/desktop: upgrade Jetbrains Compose
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 05:43:31 +05:30
2cb30deeda Merge #169
169: Update to Gradle 7.0-RC1 r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-28 23:19:20 +00:00
Harsh Shandilya ff3b4c7a27 api: update KSP for Gradle 7.0-RC1 support
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 04:20:14 +05:30
Harsh Shandilya 1171fb17e6 gradle: update to 7.0 RC1
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-29 04:19:50 +05:30
0052312459 Merge #177
177: Bump AGP r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-26 06:45:28 +00:00
Harsh Shandilya 826347023b build: bump AGP
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-26 12:01:54 +05:30
f2ea2993cc Merge #176
176: Enable R8 full mode r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-25 11:25:27 +00:00
Harsh Shandilya be95d0c198 gradle: enable R8 full mode
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-25 15:37:30 +05:30
f6d8d3c536 Merge #174
174: Cleanup string abstraction and switch everything to it r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-24 06:55:17 +00:00
Harsh Shandilya 1abbd8baba common/app: migrate all string usages to common Strings API
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-24 11:56:52 +05:30
Harsh Shandilya cad53dcce5 common: remove old string API
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-24 11:56:51 +05:30
Harsh Shandilya 4016c22289 app: switch to extension API
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-24 11:56:51 +05:30
Harsh Shandilya 56fd18be75 common: attempt extension backed string API
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-24 11:56:51 +05:30
Harsh Shandilya 00c1977f87 common/app: rename StringEnum to Strings
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-24 10:43:47 +05:30
Harsh Shandilya b19a9267f6 common: link bug in code
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-24 10:38:44 +05:30
Harsh Shandilya 5783210873 app: add sun.misc.Unsafe to proguard rules
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-24 10:34:18 +05:30
Harsh Shandilya f242ffb2af common: cleanup Gradle build
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-24 10:30:45 +05:30
60bf7ecde7 Merge #172
172: Abstract strings into common multiplatform library r=msfjarvis a=Skrilltrax

Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>

Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-23 20:03:42 +00:00
Aditya Wasan c52f7812f1 cleanup: add newline at the end of the file
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-03-24 01:17:47 +05:30
Aditya Wasan 44d220b74c revertme: hack to make StringValue compile
Currently compose does not support default values with actual/expect functions.
When we use vararg it creates a default emptyArray if we do not provide an argument which causes the compose compiler to throw an ISE.
We can revert this commit once https://youtrack.jetbrains.com/issue/KT-44499 is merged.

Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-03-24 01:08:29 +05:30
Aditya Wasan 13b3abdcd4 ui: use stringValue to get resources inside app module
This will not compile but still adding this so that we can revert the hack in-future and get it running correctly.

Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-03-24 01:05:43 +05:30
Aditya Wasan 282da93630 build: enable androidResources for common module
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-03-24 00:25:47 +05:30
ccbcea162d Merge #171
171: Update Jetbrains Compose and pull-to-refresh r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-23 16:40:54 +00:00
Harsh Shandilya 5f227b2327 build: update deps
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-23 22:02:12 +05:30
Harsh Shandilya b233129c32 common: include androidMain resources directory
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-23 21:15:37 +05:30
Aditya WasanandHarsh Shandilya 01ffc49e18 common: add common stringValue api
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-03-23 21:15:37 +05:30
4a40192647 Merge #170
170: Add ability to change saved posts sorting order r=msfjarvis a=msfjarvis

Fixes #167

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-23 12:18:47 +00:00
Harsh Shandilya 08e8993101 Add tests for LobstersTopAppBar
(cherry picked from commit eee0737a1c)
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-23 17:45:54 +05:30
Harsh Shandilya 3be1d2ad87 Wire in sorting preference awareness
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-23 17:42:14 +05:30
Harsh Shandilya 1408bee041 Wire in ability to toggle sorting order
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-23 17:41:58 +05:30
Harsh Shandilya 9bf5ee90ef Introduce and integrate ClawPreferences
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-23 17:40:30 +05:30
Harsh Shandilya 9e3f718ab9 Add DataStore dependency and set up injection
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-22 23:26:52 +05:30
b37707d933 Merge #168
168: Update to AGP 7.0.0-alpha10 r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-22 16:49:48 +00:00
Harsh Shandilya ae1018f8c5 build: update AGP
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-22 22:19:05 +05:30
faa6fbcc39 Merge #166
166: Refactor LobstersItem r=msfjarvis a=msfjarvis

Breaks apart PostDetails into multiple individual composables and reimplements the layout logic with a more easy to understand mental model.


Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-21 19:10:24 +00:00
Harsh Shandilya 2b68bd82a4 LobstersItem: break avatar URL for test post
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-22 00:36:46 +05:30
Harsh Shandilya 88f44bd63b app: update screenshots
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-21 22:57:28 +05:30
Harsh Shandilya ced3aa44f1 Add tests for list of posts
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-21 22:57:12 +05:30
Harsh Shandilya 6f1a488b92 Rewrite LobstersItem
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-21 22:56:01 +05:30
b229f1b1dc Merge #164
164: Fix AVD name in emulator bootup script r=msfjarvis a=msfjarvis

It didn't fail in this repo because we stick to API 30 only but it's good to fix it anyway

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-20 13:12:24 +00:00
Harsh Shandilya 88a309712d scripts/boot_emulator: dynamically set AVD name
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-20 18:41:14 +05:30
3d93f9b04b Merge #163
163: Update scripts r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-20 12:44:02 +00:00
Harsh Shandilya f37682c154 scripts: import cleanups from APS
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-20 18:13:05 +05:30
4cd00a4b44 Merge #162
162: Refactor LobstersItem and add a dedicated comments button r=msfjarvis a=msfjarvis

Refer to updated screenshot test for the UI changes.

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-19 12:44:59 +00:00
Harsh Shandilya ce8d9f09fa app: update screenshots
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-19 16:26:07 +05:30
Harsh Shandilya 82707f8347 LobstersItem: tweak paddings
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-19 16:24:05 +05:30
Harsh Shandilya eae231722f LobstersItem: tweak paddings
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-19 16:02:40 +05:30
Harsh Shandilya f269c0ae08 app: rename LobstersItem parameters
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-19 15:18:22 +05:30
Harsh Shandilya 840361707a LobstersItem: add a dedicated comments button
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-19 15:17:32 +05:30
Harsh Shandilya dbab82c533 LobstersItem: adjust row weightage
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-19 15:04:40 +05:30
Harsh Shandilya fa605f5416 LobstersItem: split details and button to separate composables
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-19 14:57:57 +05:30
1983f1e864 Merge #161
161: Add missing R8 rules r=msfjarvis a=msfjarvis

Suggested by AGP 7.0.0-alpha10

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-18 21:27:29 +00:00
Harsh Shandilya cfc2680786 build: add missing R8 rules and sort
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-19 02:52:59 +05:30
7eb7ee81ba Merge #160
160: Switch to kotlin.test and move AndroidManifest r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-18 18:34:20 +00:00
Harsh Shandilya 420f792290 all: switch to kotlin.test for unit testing
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-18 23:56:28 +05:30
Harsh Shandilya 733fb5d96c common: move AndroidManifest to the correct source set
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-18 21:54:49 +05:30
640b64a407 Merge #158
158: Update dependencies r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-16 13:04:05 +00:00
Harsh Shandilya 0388010c8b build: remove unused dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-16 18:33:24 +05:30
Harsh Shandilya ab6816be62 build: update to AGP 7.0.0-alpha10
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-16 18:31:43 +05:30
1d3278f837 Merge #157
157: Reimplement CI testing infrastructure r=msfjarvis a=msfjarvis

bors try

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-16 10:11:15 +00:00
Harsh Shandilya ede879c550 github: restructure CI test setup
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-16 15:26:58 +05:30
ddc504eb05 Merge #156
156: Fix scroll crash r=msfjarvis a=msfjarvis

Fixes #155
bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-15 17:17:29 +00:00
Harsh Shandilya 8bf42fa46a Disable KMP warning
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-15 21:57:40 +05:30
Harsh Shandilya 879cb951a0 app: don't attempt to scroll if posts are loading
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-15 21:57:19 +05:30
3939706f42 Merge #154
154: Cleanups post desktop and common churn r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-14 18:32:31 +00:00
Harsh Shandilya f5c3a950bf common: make UrlLauncher use expect/actual
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-15 00:00:37 +05:30
Harsh Shandilya 3021f96465 common: cleanup duplicated Gradle configs
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-14 23:47:05 +05:30
Harsh Shandilya 117c063f78 desktop: fix kotlin directory
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-14 23:39:21 +05:30
10232720ae Merge #153
153: Use multiplatform compose r=Skrilltrax a=Skrilltrax



Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
2021-03-14 17:33:34 +00:00
Aditya Wasan d2feeedec1 src: do not use same package name as the app
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-03-14 23:02:39 +05:30
Aditya Wasan 42f2b25f5c build: use org.jetbrains.compose artifacts
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-03-14 22:42:50 +05:30
Aditya Wasan c5360f264f bug: pass UrlLauncher type to LocalUrlLauncher
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-03-14 21:54:27 +05:30
Aditya Wasan 9517531388 build: setup compose multiplatform
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-03-14 21:54:27 +05:30
be282f3ac7 Merge #152
152: Add a desktop MVP r=msfjarvis a=msfjarvis

Unfortunately I haven't figured out yet how to share Compose code, so there is a lot of duplication happening here. We're also missing a lot of other things, but this is a good jumping off point

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-14 14:39:39 +00:00
Harsh Shandilya c5fafba7b5 desktop: sort everything under packages
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-14 20:01:45 +05:30
Harsh Shandilya 704e59afdf desktop: integrate with API
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-14 19:57:10 +05:30
Harsh Shandilya be6e0c9498 desktop: initialize subproject
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-14 19:23:40 +05:30
2a543d3aad Merge #151
151: Use pull-to-refresh for reloading r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-12 21:16:22 +00:00
Harsh Shandilya 697e806dd8 app: switch to PullToRefresh
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-13 02:44:12 +05:30
Harsh Shandilya a28d5085bb app: remove LobstersTopAppBar
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-11 17:59:47 +05:30
4cac015176 Merge #150
150: Enable screenshot testing in CI and document the setup r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-11 11:28:16 +00:00
Harsh Shandilya ae1770e4ac Add a basic HACKING.md
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-11 16:57:31 +05:30
Harsh Shandilya a5a710cbb1 scripts: add boot_emulator.sh
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-11 16:38:25 +05:30
Harsh Shandilya 3d7172d664 Switch to Pixel XL profile
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-11 10:40:01 +05:30
Harsh Shandilya 68431d4a25 github: fix CI workflow to include screenshot tests
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-11 10:28:41 +05:30
5d5f602e80 Merge #149
149: Update AndroidX dependencies r=msfjarvis a=msfjarvis

bors r+ p=1000

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-11 04:38:08 +00:00
Harsh Shandilya aa6256b2d4 build: update all dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-11 10:05:51 +05:30
17ff1b4217 Merge #147
147: Better handle tag overflows r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-10 09:22:28 +00:00
Harsh Shandilya 7709665538 app: update screenshots
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-10 14:51:15 +05:30
Harsh Shandilya 00d15f5475 LobstersItem: improve TagRow reflow behavior
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-10 14:47:02 +05:30
23c4ca5d3f Merge #146
146: Update to AGP 7.0.0-alpha09 r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-10 04:18:35 +00:00
Harsh Shandilya c048ecf9cf Supress false-positive Lint issue
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-10 09:48:01 +05:30
Harsh Shandilya d5e51366e9 build: upgrade to AGP 7.0.0-alpha09
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-10 09:30:42 +05:30
94eb5ce091 Merge #144
144: Update system bar colors r=msfjarvis a=Skrilltrax



Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
2021-03-09 18:30:42 +00:00
055290f075 Merge #145
145: Brighten up secondary color for dark theme r=msfjarvis a=msfjarvis

bors r+ p=1000

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-09 18:16:48 +00:00
Harsh Shandilya 008c2171ff Brighten up secondary color for dark theme
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-09 23:46:04 +05:30
Aditya Wasan 835d5aee82 res: update system bar colors
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-03-09 23:30:52 +05:30
bdf5cb7a2e Merge #143
143: Use FlowLayout for TagRow and cleanup testing infra r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-09 14:47:01 +00:00
Harsh Shandilya 81bf27c13e Slightly improve header test
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-09 20:16:13 +05:30
Harsh Shandilya 8c8af56e67 app: update test screenshots
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-09 20:14:28 +05:30
Harsh Shandilya 418d1b22d6 build: set applicationIdSuffix for debug builds
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-09 20:13:45 +05:30
Harsh Shandilya 607e93e180 app: switch to FlowLayout for TagRow
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-09 19:53:15 +05:30
08adc87493 Merge #142
142: Update Shot r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-09 06:15:22 +00:00
Harsh Shandilya d42adbf85d build: update shot
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-09 11:44:33 +05:30
6dc17f51ce Merge #141
141: Improve LobstersItem layout logic and make it easier to click save button r=msfjarvis a=msfjarvis

bors r+


Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-09 05:16:56 +00:00
Harsh Shandilya 87e0ede2be app: update screenshots
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-09 10:35:07 +05:30
Harsh Shandilya 330c3a6d22 LobstersItem: improve clickability of save icon
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-09 10:32:34 +05:30
6b53f132d5 Merge #140
140: Use a livelier loading animation r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-08 10:55:09 +00:00
Harsh Shandilya 08c511f854 posts: use a shimmering animation for loading posts
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-08 16:22:39 +05:30
00c6cbd7a6 Merge #138
138: Tweak month header design r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-05 19:39:50 +00:00
Harsh Shandilya 5718628d72 app: refresh UI test screenshots
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-06 01:08:05 +05:30
Harsh Shandilya 0ca4aadfb5 all: remove unneeded manifests
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-06 00:58:35 +05:30
Harsh Shandilya 8e2d8fb89d build: upgrade to Shot 5.10.0
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-06 00:32:00 +05:30
Harsh Shandilya ce34b1173d Header: tweak design a bit
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-06 00:09:31 +05:30
790e785dd3 Merge #137
137: Add support for refreshing hottest posts r=msfjarvis a=msfjarvis

Fixes #133
bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-05 08:06:46 +00:00
Harsh Shandilya 97ac1033eb gitignore: add deployment target XML
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-05 13:35:21 +05:30
Harsh Shandilya eee0737a1c Add tests for LobstersTopAppBar
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-05 13:35:17 +05:30
Harsh ShandilyaandAditya Wasan 83af1e1c53 Don't inject paging source
Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-05 13:35:16 +05:30
Harsh Shandilya 4585757ac2 app: add support for refreshing hottest posts list
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-05 13:35:16 +05:30
5990d27a07 Merge #136
136: Update android-emulator-runner r=msfjarvis a=msfjarvis

The emulator now runs with two cores so hopefully we'll see faster tests
bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-05 05:45:09 +00:00
Harsh Shandilya f80c3a0090 github: update emulator action
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-05 11:14:09 +05:30
385fdd1fae Merge #135
135: Update to coroutines 1.4.3 r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-04 04:24:31 +00:00
Harsh Shandilya 3a75dd75b5 build: upgrade coroutines to 1.4.3
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-04 09:34:00 +05:30
fc3238a618 Merge #132
132: Scope down coroutines r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-02 03:40:07 +00:00
Harsh Shandilya f7c6330910 LobstersApp: lower coroutine scope further in the view tree
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-02 09:08:17 +05:30
e53471f85e Merge #131
131: Update MoshiX r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-02 02:51:48 +00:00
Harsh Shandilya 4423d0f17f build: update MoshiX
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-03-02 08:02:27 +05:30
f10e2b79ac Merge #130
130: Split API and database models r=msfjarvis a=msfjarvis

- Introduces a new `SavedPost` table stripped down to only the necessary fields for persistence
- Deletes the old `LobstersPost` table
- Switches `LobstersItem` to take in a `SavedPost` for rendering
- Replaces moshi-metadata-reflect with moshi-ksp

Fixes #118 

bors r+


Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 15:29:33 +00:00
Harsh Shandilya 777ee3d408 github: add ci-gradle.properties
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 20:59:00 +05:30
Harsh Shandilya e440ac088b idea: regular churn
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 20:47:41 +05:30
Harsh Shandilya 7e7609ca1f build: force Moshi to latest version
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 20:47:41 +05:30
Harsh Shandilya fb2005fd83 all: switch to moshi-ksp
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 20:47:41 +05:30
Harsh Shandilya 7b87792d8a app: switch to new models
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 20:47:40 +05:30
Harsh Shandilya 9d684536a1 database: remove LobstersPost table
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 20:47:40 +05:30
Harsh Shandilya 75888c5ba6 database: add SavedPost table and switch tests to it
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 20:47:39 +05:30
Harsh Shandilya f76628574a api: separate API response model from database model
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 20:22:23 +05:30
b2d6dd6897 Merge #129
129: Fix system bars colors r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 10:15:19 +00:00
Harsh Shandilya b577ab01ae themes: set system bars colors to black
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 15:42:32 +05:30
ad75065848 Merge #128
128: Enable Auto Backup for database files r=msfjarvis a=msfjarvis

We persist saved posts in our SQLDelight database and while Android has been automatically doing backups, this makes it more explicit
bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 09:37:51 +00:00
Harsh Shandilya cf6ff4c315 manifest: turn on full backups
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 15:00:53 +05:30
58116fb451 Merge #127
127: Add regression test for month header r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 09:22:54 +00:00
Harsh Shandilya f15f609832 Add regression test for month header
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 14:52:24 +05:30
799dd5adbe Merge #126
126: Fix sticky header background being transparent r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 08:46:44 +00:00
Harsh Shandilya 582fb5ba93 header: fix unwanted transparency
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-28 14:15:37 +05:30
0f03ac28d8 Merge #124
124: Initial draft of screenshot testing setup r=msfjarvis a=Skrilltrax



Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
2021-02-27 20:00:37 +00:00
Harsh Shandilya 4418404a71 github: use google_apis variant of emulator image
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-27 23:37:18 +05:30
Harsh Shandilya 8427cc52d8 github: remove unnecessary animation control
The action does it for us.

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-27 23:29:28 +05:30
Aditya Wasan d6bda5c3d7 ci: use api level 30
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-02-27 23:28:32 +05:30
Aditya Wasan 9a79f3ba40 test: add more bottom nav tests
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-02-27 23:24:59 +05:30
d08855585a Merge #125
125: Update accompanist r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-27 17:49:56 +00:00
Harsh Shandilya 6c523d006b build: accompanist 0.6.1
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-27 23:19:19 +05:30
Aditya Wasan 9185c0e36b chore: fix formatting
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-02-27 20:40:09 +05:30
Aditya Wasan 3013dcd9f3 Revert "github: disable unneeded emulator runner"
This reverts commit 5d74c6787f.
2021-02-27 20:37:39 +05:30
Aditya Wasan ce7abc7afb test: add screenshot test for LobstersBottomNav
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-02-27 20:32:29 +05:30
Harsh Shandilya f5bc2ebb59 Initial draft of screenshot testing setup
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-27 19:01:58 +05:30
5714ac881e Merge #123
123: Remove JCenter r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-27 12:50:09 +00:00
Harsh Shandilya 160ebcf7f5 build: add KotlinX Bintray repository
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-27 18:19:38 +05:30
Harsh Shandilya 8638868fb7 build: remove JCenter repository
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-27 18:09:36 +05:30
883c27885f Merge #122
122: Remove ConstraintLayout r=msfjarvis a=msfjarvis

Compose can handle nesting very well so CL is not a necessity.
bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-27 12:33:39 +00:00
Harsh Shandilya c2ee9032b9 build: remove ConstraintLayout dependency
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-27 18:02:16 +05:30
Harsh Shandilya ebc75bddca LobstersItem: reimplement with Rows and Columns
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-27 18:02:06 +05:30
5971163361 Merge #121
121: Add sticky headers to saved posts list r=msfjarvis a=msfjarvis

![Screenshot](https://i.imgur.com/QLAqRXk.png)

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-27 12:04:18 +00:00
Harsh Shandilya 639f814ef8 Add sticky headers to saved posts
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-27 17:33:42 +05:30
1733b3124d Merge #120
120: Update Hilt and Kotlin r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-26 05:00:47 +00:00
Harsh Shandilya f3e632078a build: upgrade to Hilt 2.33-beta
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-26 09:53:32 +05:30
b174c20faf Merge #119
119: Update AGP and Compose r=msfjarvis a=msfjarvis

bors r+
🤖

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-25 14:26:09 +00:00
Harsh Shandilya 5d74c6787f github: disable unneeded emulator runner
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-25 19:55:33 +05:30
Harsh Shandilya 095cb8d3a1 Remove BottomNavigationLayoutTest
Boss man Jim Sproch recommends we use screenshot testing rather than asserting on nodes
since they can change under us at any time as more optimisations come into play.

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-25 19:54:43 +05:30
Harsh Shandilya f745ff3e91 github: don't run tests on merge to dev and use macOS runner
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-25 09:58:18 +05:30
Harsh Shandilya 8b976cee3c BottomNavigationLayoutTest: remove unneeded assertExists
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-25 09:57:08 +05:30
Harsh Shandilya 6e77a5e94b build: update to Compose 1.0.0-beta01
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-24 23:52:17 +05:30
Harsh Shandilya c8052be29a build: upgrade to AGP 7.0.0-alpha08
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-24 23:52:16 +05:30
7d83b9d86a Merge #116
116: Move all KAPT processing to app module r=msfjarvis a=msfjarvis

Reduces build times and allows sharing common code with JVM-only projects

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-23 12:24:15 +00:00
Harsh Shandilya b977113e34 api/database: switch to kotlin JVM plugin
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-23 17:44:42 +05:30
Harsh Shandilya 027e3410d8 all: move all Hilt processing to app module
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-23 17:44:41 +05:30
282cbf301a Merge #117
117: Update to Gradle 6.8.3 r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-23 12:09:04 +00:00
Harsh Shandilya 75a83606f2 build: upgrade to Gradle 6.8.3
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-23 17:38:14 +05:30
cff16d17b5 Merge #115
115: Clarify unofficial nature of the client r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-23 11:30:13 +00:00
Harsh Shandilya 859ab94104 README: clarify unofficial nature
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-23 13:17:27 +05:30
b6390003b6 Merge #114
114: Only run CI on reviewed pull requests r=msfjarvis a=msfjarvis

Saves execution time and is just a safer option
bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-23 07:25:47 +00:00
Harsh Shandilya c4514ad4cd github: only run CI once approved
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-23 12:19:39 +05:30
e7360e07ab Merge #113
113: Revert BackdropScaffold r=msfjarvis a=msfjarvis

This was always a terrible idea lol

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-20 08:30:39 +00:00
Harsh Shandilya 875e4ea32c Revert "LobstersApp: replace with a BackgroundScaffold"
This reverts commit be58017cac.
2021-02-20 13:58:05 +05:30
Harsh Shandilya 72d51d5c52 Revert "app: remove now uneeded navigation code"
This reverts commit 41c64f4b02.
2021-02-20 13:58:01 +05:30
3d5ad28eb0 Merge #112
112: Update dependencies r=msfjarvis a=msfjarvis

bors r+
🤖

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-19 04:35:07 +00:00
Harsh Shandilya 7bb8c5b8f4 build: update AGP, MoshiX and JUnit
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-19 10:04:05 +05:30
a917917166 Merge #111
111: Switch to BackdropScaffold for the main screen r=msfjarvis a=msfjarvis

This is peak fanciness, we can't do any better

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-16 18:57:27 +00:00
Harsh Shandilya 5c39c481b7 app: add proguard rules for release builds
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-17 00:25:41 +05:30
Harsh Shandilya 66a2e9ef69 BaseProjectConfig: properly register clean task
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-17 00:15:19 +05:30
Harsh Shandilya 41c64f4b02 app: remove now uneeded navigation code
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-17 00:11:48 +05:30
Harsh Shandilya be58017cac LobstersApp: replace with a BackgroundScaffold
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-17 00:10:28 +05:30
Harsh Shandilya d82228483d main: split out LobstersApp
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-16 23:54:16 +05:30
8f249ec2a2 Merge #110
110: Shorten Gradle project name r=msfjarvis a=msfjarvis

Just takes less horizontal space in the tree so what the hell

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-15 03:26:22 +00:00
Harsh Shandilya 5815afafb7 gradle: shorten project name
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-14 18:42:29 +05:30
6fd45a2608 Merge #109
109: Misc cleanups r=msfjarvis a=msfjarvis

bors r+
🤖

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-14 12:54:09 +00:00
Harsh Shandilya f30fd72778 app: remove unused methods
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-14 18:23:26 +05:30
Harsh Shandilya b455724b1c build: cleanup Plugin declarations
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-14 18:23:26 +05:30
ddafaa60eb Merge #108
108: Update AGP and cleanup formatting rules r=msfjarvis a=msfjarvis

bors r+
🤖

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-14 12:27:14 +00:00
Harsh Shandilya 9bf7a97e55 buildSrc: codestyle fixups
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-14 17:47:02 +05:30
Harsh Shandilya 7afff19fce build: update AGP
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-14 17:33:30 +05:30
9f88bf15ab Merge #106
106: Decouple navigation from UI composables r=msfjarvis a=Skrilltrax

Navigation library recommends to decouple the navigation from the composables. They specially points to pass lambdas instead of passing the NavController directly.

https://developer.android.com/jetpack/compose/navigation#testing

Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-12 08:20:28 +00:00
Aditya Wasan 25b3ad4f8e Add test for BottomNavigationLayout
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-02-12 13:24:38 +05:30
Harsh ShandilyaandAditya Wasan 91cfb0b2d2 github: prep for running instrumentation tests
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-12 11:33:47 +05:30
Harsh ShandilyaandAditya Wasan 7da2cfd456 buildSrc: exclude duplicate license file from packaging
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-12 11:33:37 +05:30
Harsh ShandilyaandAditya Wasan 97eaf20dbc build(app): add compose testing dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-12 11:33:23 +05:30
Harsh Shandilya aff52e7d5f all: reformat
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-11 16:46:50 +05:30
Aditya WasanandHarsh Shandilya 1a6174b1c8 fix typo 2021-02-11 16:45:51 +05:30
Harsh Shandilya 98866ee407 Simplify getDestinationFromRoute
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-11 16:45:50 +05:30
Aditya WasanandHarsh Shandilya 703957e2b7 navigation: do not pass navController to composables
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-02-11 16:45:50 +05:30
baada58e8c Merge #107
107: Update dependencies r=msfjarvis a=msfjarvis

bors r+
🤖

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-11 10:59:21 +00:00
Harsh Shandilya 885c2a5865 build: upgrade to Compose 1.0.0-alpha12
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-11 16:28:34 +05:30
Harsh Shandilya 5fef8a4d51 build: replace JCenter with MavenCentral and drop JitPack
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-11 16:28:10 +05:30
40c4c425f6 Merge #105
105: Upgrade to Dagger 2.32 r=msfjarvis a=msfjarvis

bors r+
🤖

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-09 09:39:30 +00:00
Harsh Shandilya c0e0e53ee7 build: upgrade to Dagger 2.32
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-09 15:08:22 +05:30
Harsh Shandilya 2029ceeee0 build: upgrade to Dagger 2.32
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-09 15:08:01 +05:30
a9999aae2e Merge #104
104: Add icons for IDE project list r=msfjarvis a=msfjarvis

bors r+
🤖

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-08 08:57:05 +00:00
Harsh Shandilya b5a4787424 idea: add icons
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-08 14:21:58 +05:30
2b39717cab Merge #103
103: Dependency updates r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-08 08:45:46 +00:00
Harsh Shandilya 8f53a39f63 build: upgrade kotlin
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-08 14:14:45 +05:30
Harsh Shandilya 020976e257 build: update remaining dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-08 14:14:44 +05:30
Harsh Shandilya cdfa20b9a7 build: upgrade to Compose alpha11
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-08 14:14:43 +05:30
Harsh Shandilya b87e242de2 build: remove gradle-versions-plugin
I use a Gradle init script for this now

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-08 13:13:02 +05:30
147f2d23a5 Merge #101
101: Fix cache issues r=msfjarvis a=Skrilltrax



Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
2021-02-05 10:38:39 +00:00
Aditya Wasan 0fcf584b01 src: make LobstersRepository a singleton
Earlier both LobstersViewModel and LobstersPagingSource were getting a different instance of LobstersRepository.
This lead to cache issues where LobstersPagingSource filled the cache but it was not available to the LobstersViewModel.

Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-02-05 15:00:23 +05:30
43361b3a2a Merge #102
102: Gradle build improvements r=msfjarvis a=msfjarvis


- Cleans up unnecessary dependencies
- Skips configuring Dagger on modules where it's not needed
- Hacks together a way to load buildSrc plugin dependencies from Kotlin sources

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-01 13:14:30 +00:00
Harsh Shandilya df1db3d0ff buildSrc: move plugin dependencies to Dependencies.kt
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-01 18:43:52 +05:30
Harsh Shandilya 2a4c58a680 buildSrc: conditionally configure Dagger and disable kapt in tests
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-01 18:32:47 +05:30
Harsh Shandilya 0c1b20d3e7 buildSrc: remove unneeded AGP builder dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-02-01 18:25:30 +05:30
Aditya Wasan 9ce33bba75 src: add isCacheReady to let users know cache status
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-31 23:02:16 +05:30
23ec6ea9b1 Merge #75
75: Use SQLDelight instead of Room r=msfjarvis a=Skrilltrax

Deletes all the existing Room database code and replaces it with a simplified SQLDelight-backed implementation ready for Kotlin Multiplatform.

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-31 15:16:14 +00:00
Aditya Wasan 1946b1c333 build: remove ExperimentalStdlibApi compiler flag
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-31 19:13:58 +05:30
Aditya Wasan 1251ae3bc0 src: remove is_saved from LobstersPost and fix tests
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-31 18:49:49 +05:30
Aditya Wasan 6b3b35d4df src: manually pass submitterJsonAdapter to SubmitterAdapter
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-31 18:22:23 +05:30
Aditya Wasan 9e943c82b9 src: add hilt and update hilt modules
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-31 17:51:21 +05:30
Aditya Wasan 9c4c0b1e56 src: use IconToggleButton and add crossfade animation
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-31 17:23:26 +05:30
Aditya Wasan a5538ece8e src: add filename to AndroidSqliteDriver
SQLDelight creates a in-memory DB if the file name is null.

Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-31 16:31:20 +05:30
Aditya Wasan 092599d167 src: add isSaved parameter to LobstersItem
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-31 16:17:08 +05:30
Aditya Wasan af72415952 build: remove kotlinReflect dependency
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-31 15:48:43 +05:30
Aditya Wasan a2c058877a src: add an in-memory map to cache db posts
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-31 15:44:39 +05:30
Harsh Shandilya d4ecf97436 Correctly set -Xopt-in=kotlin.RequiresOptIn on all modules
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-31 15:06:36 +05:30
Harsh Shandilya 49d9ceb73a app: cleanups
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-31 15:06:18 +05:30
Aditya Wasan 7c8cbc4b3f src: make dataSource non nullable
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-31 14:58:09 +05:30
Aditya Wasan ff4a2a751f src: fix is_saved logic
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-31 14:46:29 +05:30
Aditya WasanandHarsh Shandilya 6fdf1d0ca0 src(databse,buildSrc): remove kotlinReflect and opt into ExperimentalStdlibApi
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-30 12:51:17 +05:30
Aditya WasanandHarsh Shandilya 8303159abf src(api): use moshi-metadta-reflect
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-30 12:51:16 +05:30
Aditya WasanandHarsh Shandilya 0a6371b159 Revert "Revert "all: switch to moshi-kotlin and fix tests""
This reverts commit 602d11de35180d72fae9472b280d08af460d940f.
2021-01-30 12:51:16 +05:30
Harsh Shandilya c835a59cbf proguard(app): disable ProGuard inlining optimisations
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-30 12:51:16 +05:30
Harsh Shandilya 32bfdde04e Revert "all: switch to moshi-kotlin and fix tests"
This reverts commit da25dd2ed9bea50691b889e093ff896fea598b1a.

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-30 12:51:15 +05:30
Aditya WasanandHarsh Shandilya f9fcb089a1 src: remove cache and simplify db logic
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-30 12:51:15 +05:30
Aditya WasanandHarsh Shandilya 3bcdcbdac7 Add LobstersRepo to handle DB operations
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-30 12:51:15 +05:30
Harsh Shandilya 4f9d0c09ef Make is_saved nullable
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-30 12:51:14 +05:30
Aditya WasanandHarsh Shandilya ab3f112981 Use boolean for is_saved field
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2021-01-30 12:51:14 +05:30
Harsh Shandilya 740d9e432a all: switch to moshi-kotlin and fix tests
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-30 12:51:14 +05:30
Harsh Shandilya bca96ae215 app: switch to SQLDelight version of LobstersPost
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-30 12:51:13 +05:30
Harsh Shandilya b18de72bdd all: move model classes to database, rename model to api
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-30 12:50:09 +05:30
Aditya WasanandHarsh Shandilya 62d7590501 Add tests for SQLDelight database
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-30 12:50:08 +05:30
Aditya WasanandHarsh Shandilya f3b02e8fc2 database: add Post model
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-30 12:50:08 +05:30
Harsh Shandilya f17becff10 app: remove Room
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-30 12:50:07 +05:30
Harsh Shandilya 042b8f60d5 database: generate skeleton project
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-30 12:49:43 +05:30
2dfcc4de8f Merge #99
99: Tweak alignment and fixup build nits r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-28 08:17:44 +00:00
Harsh Shandilya 33b238c646 LobstersItem: extract hard-coded submitter text
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-28 13:45:13 +05:30
Harsh Shandilya a7bd050013 LobstersItem: use existing BASE_URL field
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-28 13:42:54 +05:30
Harsh Shandilya 5ab5d1ece7 LobstersItem: fix submitter name alignment with avatar
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-28 13:41:49 +05:30
Harsh Shandilya 07930d416d build(app): remove kotlinCompilerVersion
It is deprecated now

'ComposeOptions.kotlinCompilerVersion is deprecated. Compose now uses the kotlin compiler defined in your buildscript.'

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-28 13:38:30 +05:30
Harsh Shandilya 929816c342 Revert "gradle: disable problematic flag"
Fixed in AGP 7.0.0-alpha05

This reverts commit 6b85bc51de.
2021-01-28 13:26:56 +05:30
5cee5ad5d3 Merge #98
98: Dependency updates r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-28 06:35:31 +00:00
Harsh Shandilya 9d2a0acfcf build: upgrade Room
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-28 12:04:14 +05:30
Harsh Shandilya b2719a263f build: sync Dagger Hilt gradle plugin version with library
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-28 12:03:24 +05:30
Harsh Shandilya 3eead80cb4 build: upgrade AGP
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-28 12:02:04 +05:30
9efc9d53a5 Merge #97
97: Resolve a couple Lint issues r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-26 05:47:30 +00:00
Harsh Shandilya 82b1887f6d app: remove identical icon
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-26 11:16:25 +05:30
Harsh Shandilya dee0c54fde app: rename UrlLauncherAmbient to appease Lint
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-26 11:16:24 +05:30
92cea96616 Merge #96
96: Remove unnecessary dependencies r=msfjarvis a=msfjarvis

Drops unused Jetpack libraries as well as custom tabs helper and upgrades Dagger Hilt

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-25 13:47:54 +00:00
Harsh Shandilya e1aa0e5bc6 app: remove customtabs dependency
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-25 19:15:51 +05:30
Harsh Shandilya 57a2f67646 build: pare down unnecessary Jetpack dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-25 18:58:57 +05:30
Harsh Shandilya 5f752ed251 build: upgrade Dagger
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-25 18:55:47 +05:30
2297eb0d1c Merge #95
95: Improve scroll semantics r=msfjarvis a=msfjarvis

Allows scrolling back to the top with a single tap as opposed to a fling

bors r+

Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-25 13:15:57 +00:00
Harsh Shandilya 7a8583e352 build: upgrade to Gradle 6.8.1
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-25 18:45:32 +05:30
Harsh Shandilya 04658e99fd MainActivity: snap to top of list when pressing hottest posts tab
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-25 18:44:00 +05:30
Aditya WasanandHarsh Shandilya 7e7eff6c8e src: use launchSingleTop to prevent adding duplicate composables
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
(cherry picked from commit 45e3bd56855570db0f449ae5dde89dd46e4f2129)
2021-01-25 17:43:00 +05:30
2547d36254 Merge #94
94: Update AGP r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-18 13:23:55 +00:00
Harsh Shandilya 45c3ec504f all: reformat
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-18 18:53:18 +05:30
Harsh Shandilya 6b85bc51de gradle: disable problematic flag
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-18 18:51:51 +05:30
Harsh Shandilya 19c5bac6d3 all: use Dagger Hilt VM injection
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-18 18:51:51 +05:30
Harsh Shandilya e9a55cc1db buildSrc: introduce versioning plugin
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-18 18:43:05 +05:30
Harsh Shandilya dc61aa07ac build: bump Dagger Hilt
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-15 11:19:05 +05:30
Harsh Shandilya a36ca26720 build: update Jetpack and Accompanist
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-14 16:02:35 +05:30
Harsh Shandilya a4e0c90fe9 build: bump Gradle
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-09 15:59:22 +05:30
Harsh Shandilya bf46187d0a build: bump AGP and transitive dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2021-01-09 15:54:05 +05:30
119ceb258e Merge #93
93: Update dependencies and target Java 11 r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-23 23:18:44 +00:00
Harsh Shandilya a737f7c7da build: upgrade material and accompanist
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-24 04:46:33 +05:30
Harsh Shandilya a4f05c744e build: target Java 11
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-24 04:45:47 +05:30
274f2d4c84 Merge #92
92: Refactor for configuration cache compatibility r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-20 11:42:19 +00:00
Harsh Shandilya 7c2528f4a7 build: refactor for configuration cache compatibility
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-20 17:11:37 +05:30
7d1b36c7fb Merge #91
91: Tweak ripple bounds and adjust R8 and AGP options r=msfjarvis a=msfjarvis


Setting padding in individual lists caused the ripple from LobstersItem to not draw to the edges, which was annoying to look at. Now we set the padding in LobstersItem itself and skip doing so in the individual list composables.

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-18 07:42:09 +00:00
Harsh Shandilya 156c58e544 app: increase horizontal padding in LobstersItem
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-18 13:10:29 +05:30
Harsh Shandilya 542b6c4e1a gradle: remove AGP suppression list
It's a pain to maintain and doesn't seem to be working either

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-18 13:08:23 +05:30
Harsh Shandilya 3cde52a3a5 app: silence proguard spam
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-18 13:07:04 +05:30
Harsh Shandilya 8abb5cbd3a app: remove extraneous padding from post lists
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-18 13:06:25 +05:30
47648f6e2d Merge #90
90: Update gradle property set r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 18:21:24 +00:00
Harsh Shandilya d0b2bbee15 gradle: sync properties with ReactiveCircus/streamlined
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 23:50:43 +05:30
c7f0eab9c2 Merge #88
88: Migrate away from kotlinx libraries r=msfjarvis a=msfjarvis

Both Ktor and Kotlinx.serialization have been extremely annoying to work with so I'm switching back to Retrofit and Moshi

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 06:36:46 +00:00
Harsh Shandilya e504a2c591 model: don't unit test against the live server
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 12:06:14 +05:30
Harsh Shandilya 76f613f82f gradle: disable R8 full mode
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 11:43:11 +05:30
Harsh Shandilya 623a9ffcb5 app: switch from kotlinx to square
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 11:43:11 +05:30
Harsh Shandilya 3641cd81cd model: switch from kotlinx to square
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 11:43:11 +05:30
Harsh Shandilya e93e6f0061 build: fix ui-test dependency name
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 11:08:22 +05:30
Harsh Shandilya d917f7c881 build: replace kotlinx dependencies with square ones
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 11:08:08 +05:30
461b466bb8 Merge #84
84: Switch to Paging Compose r=msfjarvis a=msfjarvis

~~Blocked on a Compose bug: https://issuetracker.google.com/issues/174839532~~ 
~~Fix is arriving in alpha09 (and 174826387 snapshot)~~ 
Available now

TODO: add back persistence layer

Fixes #47

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-16 20:21:32 +00:00
Harsh Shandilya 39e912bb7f build: upgrade to AGP 7.0.0-alpha03
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 01:19:00 +05:30
Harsh Shandilya 4922f3daf8 app: switch to LazyColumn
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 01:12:08 +05:30
Harsh Shandilya 60620eccec build: upgrade paging
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 01:11:10 +05:30
Harsh Shandilya 343f7e7f56 app: switch to Paging
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 01:11:09 +05:30
Harsh Shandilya d0d08f8a3c app: remove existing post loading
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 01:11:09 +05:30
Harsh Shandilya ada6a6f3ff app: add LobstersPagingSource
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 01:11:08 +05:30
Harsh Shandilya c29edfd402 model: import API related classes
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 01:11:08 +05:30
Harsh Shandilya f16d557138 idea: change test runner to Gradle
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 01:11:08 +05:30
4f871534b0 Merge #89
89: Upgrade dependencies r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-16 19:31:57 +00:00
Harsh Shandilya 1c6f6ad619 build: upgrade Activity, Compose, Navigation, and Room
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-17 00:58:53 +05:30
f55604f8fd Merge #87
87: Dependency updates r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-08 06:20:50 +00:00
Harsh Shandilya 09cb29c532 build: dependency updates
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-08 11:50:14 +05:30
e3a931fab4 Merge #86
86: build: bump to AGP 7.0.0-alpha02 r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-03 19:18:04 +00:00
Harsh Shandilya 6ee7682547 build: bump to AGP 7.0.0-alpha02
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-04 00:47:06 +05:30
24f1b1d377 Merge #85
85: Compose and Kotlin updates r=msfjarvis a=msfjarvis


Updates Compose to alpha08
Updates Kotlin and Kotlinx.serialization to 1.4.20
Updates Ktor to 1.4.2

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-02 20:25:48 +00:00
Harsh Shandilya f8b8ce0b5b all: update to Compose alpha08 and Kotlin 1.4.20
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-03 01:53:31 +05:30
6597f0058d Merge #83
83: Update AGP to 7.0.0-alpha01 r=msfjarvis a=msfjarvis

Also upgrades Gradle to 6.8-rc-1

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
Co-authored-by: Aditya Wasan <adityawasan55@gmail.com>
2020-12-02 15:21:08 +00:00
Harsh Shandilya e06f5380a1 github: remove ci-gradle.properties
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-02 20:50:30 +05:30
Aditya WasanandGitHub c74079797a Update ci-gradle.properties 2020-12-02 20:42:30 +05:30
Harsh Shandilya 4f10e3f40a gradle: upgrade to 6.8-rc-1
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-02 20:36:39 +05:30
Harsh Shandilya 52a87c10b5 github: hail mary
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-02 20:24:37 +05:30
Harsh Shandilya 04f0aa1a7a github: setup JDK 11 in PR workflow
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-02 20:14:04 +05:30
Harsh Shandilya 9ca72341a8 buildSrc: add transitive AGP dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-02 20:09:22 +05:30
Harsh Shandilya 0588cceee0 build: uprev AGP and add AS version note to README
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-02 14:59:34 +05:30
3e7b1a1de8 Merge #82
82: Fix navigation backstack behaviour and make some minor tweaks r=msfjarvis a=msfjarvis

Fixes #66

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-01 16:39:35 +00:00
Harsh Shandilya 39d36eece6 ui: fix backstack behavior
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-01 22:06:13 +05:30
Harsh Shandilya 2b2acd1311 UrlLauncherAmbient: switch to staticAmbientOf
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-01 21:55:00 +05:30
Harsh Shandilya b5c039eaba injection: replace deprecated component
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-12-01 21:17:08 +05:30
8633908301 Merge #80
80: Upgrade dependencies and switch to Compose snapshot r=msfjarvis a=msfjarvis

~~Kotlin 1.4.20 needs the Compose snapshot but some dependency resolution bullshit is breaking it 🤷~~

Dropped the Kotlin 1.4.20 bump, sticking with 1.4.10 until a Compose update is out.

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-29 16:29:05 +00:00
Harsh Shandilya 425d6b7deb build: upgrade dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-29 21:56:46 +05:30
Harsh Shandilya a6da85d2e5 app: remove redundant JitPack inclusion
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-29 21:56:08 +05:30
fbbbacb121 Merge #81
81: Theme: add back a light palette r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-29 16:20:30 +00:00
Harsh Shandilya 5c0471d06e Theme: add back a light palette
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-29 21:49:41 +05:30
cf3ec3687c Merge #78
78: Switch to new adaptive icon r=msfjarvis a=msfjarvis

Designed by @SphericalKat

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-17 10:38:45 +00:00
Harsh Shandilya 398c7ef80c Change name to Claw for lobste.rs
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-17 16:07:59 +05:30
2da5d6c28d Merge #79
79: Update Gradle r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-17 08:17:26 +00:00
Harsh Shandilya 8960130548 build: update to Gradle 6.7.1
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-17 13:46:31 +05:30
Harsh Shandilya b4ff025836 app: switch to new adaptive icon
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-12 15:58:28 +05:30
9eae76442f Merge #77
77: Dependency updates r=msfjarvis a=msfjarvis

Updates Compose, Accompanist, browser, core-ktx and drops some now deprecated artifacts. Also fixes a navigation bug that caused backstack to pop indefinitely even when on the same screen.

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-11 20:35:31 +00:00
Harsh Shandilya e5a6c97058 app: don't pop back stack if already on route
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-12 01:54:50 +05:30
Harsh Shandilya ff4dfcac61 treewide: update Compose and other deps
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-12 01:54:20 +05:30
d2bc068f95 Merge #74
74: BottomNavigation tweaks r=msfjarvis a=msfjarvis

Extracts the code into its own composable and slightly tweaks the UI to hide labels for inactive destinations. Blocked on #66

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-11 14:54:49 +00:00
Harsh Shandilya 2778c88d3a Add bottom padding to list composables
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-11 20:17:20 +05:30
Harsh Shandilya 5b66b440ce Separate out BottomNavigation to its own composable
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-11 20:17:19 +05:30
7b4e8a8f68 Merge #76
76: Update dependencies r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-11 14:22:36 +00:00
Harsh Shandilya 587f40f79f build: update Ktor and gradle-versions-plugin
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-11 19:51:58 +05:30
3cb1c4b027 Merge #73
73: Reimplement LobstersItem to not require swipe actions r=msfjarvis a=msfjarvis

Work towards #65

TODO:
- [x] ~~Figure out why only the last tag shows in the UI~~ Fixed by using `Row` to lay it out like earlier


<details>
<summary>Screenshot</summary>

![screenshot-20201109-113553](https://user-images.githubusercontent.com/13348378/98505720-ce2a4600-227f-11eb-8342-863f2122b928.png)


</details>

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-09 06:41:07 +00:00
Harsh Shandilya 5678215b25 Tweak lambda parameters for LobstersItem
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-09 11:45:05 +05:30
Harsh Shandilya 5acab43669 Rename LobstersItemRedux to LobstersItem
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-09 11:35:42 +05:30
Harsh Shandilya 974076c5f1 Remove LobstersItem
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-09 11:34:55 +05:30
Harsh Shandilya a4bac75d2a Replace ConstraintLayout-backed tag list with Row
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-09 11:34:39 +05:30
Harsh Shandilya a0a899f999 Make the preview look like an actual list
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 19:19:57 +05:30
Harsh Shandilya 92eecce9b0 Update KDoc
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 19:15:06 +05:30
Harsh Shandilya a9b5521dfe Check in a revamped LobstersPost composable
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 19:09:03 +05:30
Harsh Shandilya be1921240a Remove swipe actions
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 17:25:10 +05:30
f602fe8d7d Merge #72
72: Migrate to Ktor for network operations r=msfjarvis a=msfjarvis

~~Still a work in progress, crashes during loading right now~~ Crashes are fixed and tests were added back, good to go now.

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 11:35:41 +00:00
Harsh Shandilya abe6c4c2b8 app: port tests to Ktor
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 17:04:23 +05:30
Harsh Shandilya d01a7e18a2 app: tweak proguard rules to fix crash
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 16:44:52 +05:30
Harsh Shandilya 276877119d app: reinstate API wrapper to hide client implementation
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 16:41:02 +05:30
Harsh Shandilya 5b77fdf54b buildSrc: remove now unused dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 16:15:00 +05:30
Harsh Shandilya b71bb9e48d lobsters-api: remove
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 16:14:48 +05:30
Harsh Shandilya 05b5ecaa5f app: remove LobstersApi module
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 16:14:27 +05:30
Harsh Shandilya f7e0ba6544 app: switch to loading posts using Ktor
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 16:13:41 +05:30
Harsh Shandilya 836b5ef725 app: add Ktor client module
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 16:12:25 +05:30
Harsh Shandilya bc7eb9abaa idea: don't wildcard ktor imports
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 16:00:07 +05:30
Harsh Shandilya dd177c5578 buildSrc: add Ktor dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 15:53:46 +05:30
Harsh Shandilya 03b2bbd22c app: sort dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 15:44:05 +05:30
8729ad9cd1 Merge #71
71: Follow up on Gradle build rewrite r=msfjarvis a=msfjarvis

Adds back a dependency that was accidentally removed, and converts `settings.gradle` to Kotlin

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 09:19:20 +00:00
Harsh Shandilya fa9d964a32 Convert settings.gradle to Kotlin and enable Gradle metadata
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 14:47:57 +05:30
Harsh Shandilya dc12917b66 lobsters-api: add back accidentally removed Retrofit dependency
Fixes: 84f45f523a ("Replace Moshi with kotlinx.serialization")
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-08 14:46:06 +05:30
800c91c637 Merge #69
69: Replace Moshi with kotlinx.serialization r=msfjarvis a=msfjarvis

More work towards #67

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-07 05:49:58 +00:00
Harsh Shandilya c3e73256bf app: add R8 rules for kotlinx.serialization
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-07 11:18:10 +05:30
Harsh Shandilya 84f45f523a Replace Moshi with kotlinx.serialization
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-07 11:15:54 +05:30
Harsh Shandilya 43295a58eb build: add kotlinx.serialization dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-07 11:06:03 +05:30
10630944d3 Merge #68
68: Convert Gradle build configuration to Kotlin r=msfjarvis a=msfjarvis

First steps toward #67

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-06 12:00:00 +00:00
Harsh Shandilya 1e2fef884f build: convert to Gradle Kotlin DSL
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-06 17:28:26 +05:30
9839bf4014 Merge #63
63: Add more tests for Lobsters API and fix okhttp version r=msfjarvis a=msfjarvis

Downgrades OkHttp back to 3.14.9 because that's what Retrofit uses and using a higher version results in test failures for me when executed through the IDE. Also adds a couple more tests for the API data, and adds a `.git-blame-ignore-revs` file to skip some noisy commits that make `git-blame` that much useless.

bors r+


Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-02 08:23:58 +00:00
Harsh Shandilya dd680ecf70 git: add git-blame-ignored-revs file for noisy changes
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-02 13:51:16 +05:30
Harsh Shandilya 1528231032 lobsters-api: add some more tests
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-02 13:50:10 +05:30
Harsh Shandilya 6f5d88a5c1 build: downgrade OkHttp back to the retrofit version
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-02 13:44:00 +05:30
442293215c Merge #61
61: Revamp database setup r=msfjarvis a=msfjarvis

An assortment of database related changes

- Moved the `data` module into `app`
- Enabled schema generation for `PostsDatabase`
- Added and enabled [Roomigrant](https://github.com/MatrixDev/Roomigrant)
- Removed the now unused `LobstersPost#isLiked` field
- Bumped the database version to let Roomigrant handle the upgrade

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-01 20:22:48 +00:00
Harsh Shandilya ab2ba579d1 app: bump database version
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-02 01:48:55 +05:30
Harsh Shandilya c22ede7aa1 model: remove unused field
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-02 01:48:55 +05:30
Harsh Shandilya bfa4cd58a7 app: setup Roomigrant
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-02 01:48:54 +05:30
Harsh Shandilya 4b5c1e6d15 app: add roomigrant dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-02 01:48:54 +05:30
Harsh Shandilya f367c79e8e app: turn on schema generation
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-02 01:48:53 +05:30
Harsh Shandilya c3cead5993 data: move into app module
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-02 01:48:53 +05:30
713706caff Merge #60
60: Make saving a swipe action r=msfjarvis a=msfjarvis

~~This isn't ready yet, because when done we want to make this use just `Modifier.swipeable` and not actually dismiss the view.~~ Good to go now 


Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-01 20:05:40 +00:00
Harsh Shandilya b84d6ac2ef model: add back unused variable for db compat
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-02 01:35:00 +05:30
Harsh Shandilya 78f15ac7d9 Use enum for swipe states
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-02 01:10:19 +05:30
Harsh Shandilya 3f223f4306 Remove liked post coloring from UI
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-02 01:07:12 +05:30
Harsh Shandilya a52c75601c Switch to Modifier.swipeable
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-02 01:03:54 +05:30
Harsh Shandilya 8c162c75c5 Make saving a swipe dismiss action
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-02 00:41:08 +05:30
bb8c07f232 Merge #59
59: Reorganize classes based on Buffer's package structure r=msfjarvis a=msfjarvis

Following the overview from [this post](https://buffer.com/resources/android-rethinking-package-structure/). I personally find myself agreeing with the reasoning behind these changes and can see the benefits.

bors r+


Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-01 18:01:48 +00:00
Harsh Shandilya 3d117362ef app: restructure files based on Buffer's package structure
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-11-01 23:29:08 +05:30
023601fd09 Merge #58
58: Fix Jetpack Compose link r=msfjarvis a=rleppink

The current link does not resolve because of the double `https://`

Co-authored-by: Ronald Leppink <rleppink@users.noreply.github.com>
2020-10-31 16:01:02 +00:00
Ronald LeppinkandGitHub 5b1c7cbf5c Fix Jetpack Compose link
The current link does not resolve because of the double `https://`
2020-10-31 16:51:24 +01:00
4f2c5b1909 Merge #57
57: Prepare for testing r=msfjarvis a=msfjarvis

Work towards #56

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-30 09:18:43 +00:00
Harsh Shandilya 779ad2f8b9 Make UrlLauncher a fun interface
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-30 14:47:47 +05:30
Harsh Shandilya b195c0d203 Make bottom nav icons part of destination classes
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-30 14:22:16 +05:30
Harsh Shandilya 41521cca95 Persist hottest list scroll state across navigation events
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-30 14:16:09 +05:30
Harsh Shandilya 108a28384c Extract viewModel references out of post list composables
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-30 14:11:25 +05:30
b641cab624 Merge #55
55: Only navigate when not on the current screen r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 12:10:19 +00:00
Harsh Shandilya b031d36fa2 Only navigate when not on the current screen
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 17:09:27 +05:30
97dc62afe1 Merge #54
54: Cleanup and reformat r=msfjarvis a=msfjarvis

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 11:29:57 +00:00
Harsh Shandilya e37d71fba6 Move hardcoded strings to resources
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 16:53:03 +05:30
Harsh Shandilya 1be10010c8 app: reformat XML resources
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 16:48:21 +05:30
Harsh Shandilya cec0e1bbcb gradle: reformat all build files with 2SI
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 16:38:01 +05:30
Harsh Shandilya 7c4e4aea26 Remove unused variable
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 16:35:14 +05:30
60f6108d35 Merge #53
53: Fix pagination and extract UrlLauncherAmbient r=msfjarvis a=msfjarvis

Our pagination hack currently does not update the last index field which has now been fixed. While here we also take the time to extract UrlLauncherAmbient and prepare the post list composables for tweaking from the root UI

bors r+

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 11:01:46 +00:00
Harsh Shandilya 43fb668c7d ui: Add Modifier params to [Hottest|Saved]Posts composables
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 16:27:12 +05:30
Harsh Shandilya ca77d3cf2a Move our UrlLauncherAmbient to individual composables
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 16:25:12 +05:30
Harsh Shandilya 906cfb5e43 Fix pagination
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 16:22:10 +05:30
20a38c662e Merge #52
52: Switch to BottomNav and use AndroidX navigation r=msfjarvis a=msfjarvis

This couldn't have been any easier to implement. Everything worked in the very first try, just amazing, amazing work from the Compose and Navigation teams.

bors r+


Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 09:33:17 +00:00
Harsh Shandilya 1aa0934104 Switch to BottomNav backed by AndroidX navigation
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 14:59:54 +05:30
Harsh Shandilya 415d9e075d build: upgrade accompanist
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 14:57:30 +05:30
Harsh Shandilya df74ff587a build: add navigation-compose
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 14:18:54 +05:30
44dbcccd72 Merge #51
51: Upgrade Browser and Compose r=msfjarvis a=msfjarvis

bors r+


Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 08:43:39 +00:00
Harsh Shandilya 195dcf3fd7 build: upgrade browser and compose
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-29 14:12:54 +05:30
ee9a10f39d Merge #50
50: Update coroutines and AGP r=msfjarvis a=msfjarvis

Updates AGP to 4.2.0-alpha15 that resolves the [AGP inlining bug](https://msfjarvis.github.io/notes/r8-inlining-bugs.html) and to coroutines 1.4.0 that brings performance improvements.


Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-28 08:28:06 +00:00
Harsh Shandilya 188b15172f gradle: re-enable R8's full mode
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-28 13:56:21 +05:30
Harsh Shandilya d2604c7848 build: uprev coroutines
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-28 13:34:15 +05:30
Harsh Shandilya a50057d80c build: uprev AGP
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-28 13:33:46 +05:30
8b28267b68 Merge #49
49: Setup for managing the repository via bors r=msfjarvis a=msfjarvis

Removes the automerge action, sets up PR checks to run on bors test branches, and adds the status checks TOML file that bors requires.


Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-27 23:27:46 +00:00
Harsh Shandilya fa281cb400 github: remove file check logic
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-28 04:57:01 +05:30
Harsh Shandilya 1e3b321dcf bors: init
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-28 04:54:49 +05:30
Harsh Shandilya 7b0f7639e9 github: run checks on pushes to staging and trying
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-28 04:54:48 +05:30
Harsh Shandilya 9d271eb932 github: remove automerge action
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-28 04:45:26 +05:30
github-actions[bot]andGitHub 103332e087 Merge pull request #48 from msfjarvis/enable-proguard 2020-10-24 19:16:31 +00:00
Harsh Shandilya 405a60b09b gradle: disable R8 full mode
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-25 00:38:53 +05:30
Harsh Shandilya c903c7357a build: turn on minification
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-25 00:38:41 +05:30
github-actions[bot]andGitHub 3c34adad82 Merge pull request #46 from msfjarvis/dependency-updates 2020-10-24 09:07:06 +00:00
Harsh Shandilya 2970cc5061 gradle: uprev to Gradle 6.7
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-24 14:34:25 +05:30
Harsh Shandilya a2e71ee52a build: update dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-24 14:30:50 +05:30
Harsh Shandilya 661d10d256 build: add dependency updates plugin
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-24 14:23:38 +05:30
Harsh ShandilyaandGitHub 85a5c829e0 Merge pull request #43 from Skrilltrax/develop 2020-10-19 00:31:11 +05:30
Harsh Shandilya 5782ae9438 Revert "Pass correct variable to EmptyList"
This is wrong and causes the saved posts message to be shown on both screens.

This reverts commit d93910ba5a.
2020-10-19 00:26:55 +05:30
Harsh Shandilya 600c93ed0f Replace material icons with drawable resources
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-19 00:25:19 +05:30
Aditya Wasan a38a52ebf3 Use transformLikedFlag in savePost method
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2020-10-19 00:24:30 +05:30
Harsh Shandilya dc83ad2dee Update liked state when removing a saved post as well
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-19 00:20:37 +05:30
Aditya Wasan d93910ba5a Pass correct variable to EmptyList
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2020-10-19 00:09:45 +05:30
Harsh Shandilya c54b375c06 Liked flag toggle should go both ways
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-18 23:28:33 +05:30
Harsh Shandilya e35a25b135 Simplify API of child components
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-18 23:26:28 +05:30
Harsh Shandilya b4df9d87d8 Allow reusing save action to remove from saved list
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-18 23:14:31 +05:30
Harsh Shandilya 0336af0624 Set liked flag on first load
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-18 23:05:59 +05:30
Harsh Shandilya 1021604889 data: add method to check if a post is already liked
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-18 22:58:51 +05:30
Harsh Shandilya 204e8d1b2b Cleanup formatting
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-18 22:47:51 +05:30
Harsh Shandilya 9f32f3a025 Merge branch 'develop' into Skrilltrax/develop
* develop:
  github: switch gradle-cache-action back to tag qualifier
  github: update actions in pull request workflow
  github: update automerge action
  gradle: silence warnings from beta options
  app: replace deprecated contentColor extension
  build: update Compose and Room
2020-10-18 22:37:29 +05:30
github-actions[bot]andGitHub 41dc71cf80 Merge pull request #45 from msfjarvis/update-actions 2020-10-18 17:02:37 +00:00
Harsh Shandilya 646ab03660 github: switch gradle-cache-action back to tag qualifier
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-18 22:28:58 +05:30
Harsh Shandilya f3107ed19a github: update actions in pull request workflow
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-18 22:24:54 +05:30
Harsh Shandilya 978d373893 github: update automerge action
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-18 22:22:38 +05:30
Harsh ShandilyaandGitHub a6fe748b01 Merge pull request #44 from msfjarvis/update-dependencies 2020-10-18 22:17:56 +05:30
Harsh Shandilya db26f4dbd9 gradle: silence warnings from beta options
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-18 22:13:37 +05:30
Harsh Shandilya d8435602d3 app: replace deprecated contentColor extension
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-18 22:12:46 +05:30
Harsh Shandilya 53643c9c5a build: update Compose and Room
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-18 22:03:52 +05:30
Aditya Wasan d6d82248a8 Add saved lists feature
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2020-10-18 16:03:07 +05:30
Aditya Wasan 3bd3f2dff8 Add saved entity and dao
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2020-10-18 12:19:21 +05:30
Harsh ShandilyaandGitHub 8cf2dfc71b Merge pull request #41 from msfjarvis/ui-tweaks 2020-10-10 19:22:50 +05:30
Harsh Shandilya b7dc92d85a app: tweak first load behavior
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-10 17:58:14 +05:30
Harsh Shandilya bd5647f4c7 app: reword loading state text
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-10 17:58:02 +05:30
Harsh Shandilya b28eb09e3f app: add a FAB to reload posts
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-10 17:57:35 +05:30
Harsh Shandilya 9d22682626 res: remove unused drawable
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-10 17:37:42 +05:30
Harsh Shandilya 0dabcec67c MainActivity: remove unneeded injected field
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-10 17:29:08 +05:30
probot-auto-merge[bot]andGitHub 3c21002d32 Merge pull request #40 from msfjarvis/more-ui-tweaks
Multiple improvements across the board
2020-10-07 20:10:42 +00:00
Harsh Shandilya 20e45d4d42 urllauncher: request dark theme from sites
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-08 01:36:14 +05:30
Harsh Shandilya da13725bea app: swallow more errors in view model
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-08 01:20:50 +05:30
Harsh Shandilya 43c7bcced0 app: rewrite theming to use a dark palette
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-08 01:18:19 +05:30
Harsh Shandilya 07d46605c7 github: tweak workflows and remove unused cruft
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-08 01:13:43 +05:30
Harsh Shandilya daa3d07f7a app: drop border and make tags rounded
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-08 01:02:58 +05:30
github-actions[bot]andGitHub 729f59ba96 Merge pull request #39 from msfjarvis/user-avatars 2020-10-07 19:28:58 +00:00
Harsh Shandilya 7da14338f9 app: add user avatars to items
Uses Chris Banes' amazing Accompanist library to interface with Coil and load images
with minimum hassle. The resultant UI has been made to loosely resemble https://lobste.rs
since our end goal is to fully match the website.

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-08 00:51:59 +05:30
Harsh Shandilya cc73774085 github: add automerge action
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-08 00:51:29 +05:30
Harsh ShandilyaandGitHub 6c83385af7 Merge pull request #38 from msfjarvis/gradle-cleanup
build: extract all versions to ext
2020-10-08 00:48:36 +05:30
Harsh Shandilya 196940778f build: extract all versions to ext
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-08 00:22:12 +05:30
Harsh ShandilyaandGitHub 7544eb96fc Merge pull request #37 from msfjarvis/comments-only
Fallback to comments if post has no URL
2020-10-06 19:08:52 +05:30
Harsh Shandilya 0da85ba015 app: fallback to comments if post has no url
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-06 18:53:57 +05:30
Harsh ShandilyaandGitHub 78484de959 Merge pull request #36 from msfjarvis/custom-tabs-update 2020-10-03 02:19:48 +05:30
Harsh Shandilya 829a6bbdc6 UrlLauncherImpl: use AndroidX Browser methods to launch custom tab
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-03 02:08:22 +05:30
Harsh ShandilyaandGitHub f71bd27a03 Merge pull request #35 from msfjarvis/update-followups 2020-10-03 02:08:10 +05:30
Harsh Shandilya 89aeb66a96 LobstersItem: replace ListItem with Column
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-03 01:59:26 +05:30
Harsh ShandilyaandGitHub a4eb6b3cb3 Merge pull request #34 from msfjarvis/dependency-updates 2020-10-02 13:20:25 +05:30
Harsh Shandilya a2eb97e504 build: update all dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-10-02 04:40:59 +05:30
Harsh ShandilyaandGitHub bab3a53a53 Merge pull request #32 from msfjarvis/first-load
Clear local cache of posts on first load
2020-09-28 11:07:15 +05:30
Harsh Shandilya 837970147c app: add local cache busting
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-28 11:03:32 +05:30
probot-auto-merge[bot]andGitHub 0ecf21467b Merge pull request #31 from msfjarvis/offline-cache
Set up offline caching and no posts state
2020-09-27 11:44:40 +00:00
Harsh Shandilya 62c2cb3fb7 app: add no posts state
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-27 16:44:39 +05:30
Harsh Shandilya 4fdb5a60d5 app: wire in local persistence in LobstersViewModel
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-27 16:24:36 +05:30
Harsh Shandilya 95334d2c8b data: ignore overwriting when inserting posts
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-27 16:23:41 +05:30
probot-auto-merge[bot]andGitHub 6867d84ca9 Merge pull request #27 from msfjarvis/comments
Add comment open action and cleanup unused code
2020-09-27 09:05:19 +00:00
Harsh Shandilya baddf7e763 app: remove unused resources
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-27 14:29:04 +05:30
Harsh Shandilya c21a2bf13a app: add comment open action
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-27 14:14:12 +05:30
Harsh Shandilya ac336efa33 app: restrict tags to just the first 4
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-27 14:08:41 +05:30
Harsh Shandilya 52e010d79b app: remove unnecessary Column from LobstersItem
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-27 14:00:26 +05:30
Harsh Shandilya 91205a0b2a data: cleanup unused code
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-27 13:42:39 +05:30
probot-auto-merge[bot]andGitHub a510565324 Merge pull request #26 from msfjarvis/leaky-abstractions
Cure a case of leaky abstractions with sad indirection
2020-09-27 05:02:55 +00:00
Harsh Shandilya 3693bc39f4 model: remove Room annotations and unused dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-27 10:28:36 +05:30
Harsh Shandilya aa7a732cce data: wrap LobstersPost into an intermediate entity
This lets us keep the same database structure without needing to leak Room-specific properties into our model

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-27 10:28:22 +05:30
Harsh Shandilya 8d4535d6bf build: uprev to Gradle 6.7 RC2
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-27 10:24:27 +05:30
probot-auto-merge[bot]andGitHub d6b4f2c682 Merge pull request #25 from msfjarvis/ui-work
Perf and UX improvements
2020-09-25 09:40:37 +00:00
Harsh Shandilya 1ba1701574 app: rewrite UrlLauncherImpl to use Chrome Custom Tabs
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-25 15:05:52 +05:30
Harsh Shandilya b30ab6a537 Revert "app: use ConstraintLayout for laying out LobstersItem"
Won't solve the problem I was hoping to solve.

This reverts commit a14484583d.
2020-09-25 14:35:10 +05:30
Harsh Shandilya 8462e5c763 app: pass --user 0 to ADB when installing
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-25 12:10:55 +05:30
Harsh Shandilya 0db3502855 build: use release signing for debug builds as well
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-25 02:36:14 +05:30
Harsh ShandilyaandGitHub c300a85204 Merge pull request #24 from msfjarvis/ui-improvements 2020-09-25 02:35:05 +05:30
Harsh Shandilya a14484583d app: use ConstraintLayout for laying out LobstersItem
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-25 02:25:47 +05:30
Harsh Shandilya b68eda3051 app: tweak padding for tags and add border
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-25 02:04:33 +05:30
Harsh Shandilya 36804a43bc app: re-enable preview for LobstersItem
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-25 01:55:01 +05:30
Harsh ShandilyaandGitHub 2f70acd5d0 Merge pull request #23 from msfjarvis/row-ui-tweaks
Use consistent vertical padding
2020-09-24 05:08:42 +05:30
Harsh Shandilya 2d649d70be app: use consistent vertical padding and reword submitter text
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-24 04:47:56 +05:30
Harsh ShandilyaandGitHub 7b42915a1b Merge pull request #22 from msfjarvis/pagination-prep 2020-09-23 22:17:13 +05:30
Harsh Shandilya 590bd2dfd8 app: enable kotlinx.coroutines.ExperimentalCoroutinesApi for entire module
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 22:14:20 +05:30
Harsh Shandilya 22abaa15c2 model: add default value for submitter karma
The API sometimes doesn't pass this value

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 22:09:15 +05:30
Harsh Shandilya e1c9b34bbe app: setup pagination and a viewmodel for posts
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 22:06:56 +05:30
Harsh Shandilya 5260f454ef build: add lifecycle-viewmodel-ktx
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 21:35:53 +05:30
Harsh Shandilya b3ee3f44c4 build: commonize application of kotlin-android plugin
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 17:19:11 +05:30
Harsh Shandilya 64ac515ed0 build: force correct coroutines and kotlin-reflect artifacts
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 17:19:10 +05:30
Harsh Shandilya 29e72da327 app: add persistence module
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 17:19:10 +05:30
Harsh Shandilya 96c25c428d data: add database for lobsters posts
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 17:19:10 +05:30
Harsh Shandilya 699a475315 app: use lifecycleScope for fetching posts
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 17:19:09 +05:30
Harsh Shandilya e7860e4f55 api: add support for pagination
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 17:19:09 +05:30
probot-auto-merge[bot]andGitHub 93985ee02d Merge pull request #21 from msfjarvis/rewire-api
Use retrofit suspend support to hide implementation details of API
2020-09-23 09:04:15 +00:00
Harsh Shandilya 0806d71e96 lobsters-api: fix tests
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 14:32:01 +05:30
Harsh Shandilya a66186adc3 all: use retrofit suspend support to hide implementation details of API
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 13:48:37 +05:30
Harsh ShandilyaandGitHub a3b1e02783 Merge pull request #20 from msfjarvis/pivot-to-lobsters 2020-09-23 04:03:26 +05:30
Harsh Shandilya 4a507e4d79 all: cleanup
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 04:00:57 +05:30
Harsh Shandilya 8808e0dfb8 github: only run unit tests in CI
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 03:58:45 +05:30
Harsh Shandilya 9a00122525 README: init
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 03:54:34 +05:30
Harsh Shandilya ff775176a6 app: steal lobste.rs logo for app icon
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 03:51:16 +05:30
Harsh Shandilya 38943a74d5 app: tweak post list UI
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 03:45:15 +05:30
Harsh Shandilya 63172ad79b build(app): add signing configuration
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 03:35:40 +05:30
Harsh Shandilya 06549bad2c all: remove now unused to-do app code
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 03:32:32 +05:30
Harsh Shandilya 63f5bea155 app: implement first draft of lobste.rs UI
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 03:29:21 +05:30
Harsh Shandilya 6e4a9c6a73 app: refactor theming and naming
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 02:53:19 +05:30
Harsh Shandilya 2c0cadf296 build: bump AGP
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-23 02:26:18 +05:30
Harsh Shandilya 54a0cd2b50 model: annotate for Room compatibility
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-22 13:50:18 +05:30
Harsh Shandilya 4a363200a0 model: init
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-22 13:47:06 +05:30
Harsh Shandilya ee07f8391c app: add API module
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-22 05:33:30 +05:30
Harsh Shandilya 7489100c26 lobsters-api: init
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-22 05:33:18 +05:30
Harsh Shandilya 7bb1fd6947 all: refactor package structure and naming
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-22 04:20:25 +05:30
probot-auto-merge[bot]andGitHub c8081da596 Merge pull request #19 from msfjarvis/gradle-props
build: update gradle properties
2020-09-19 16:59:52 +00:00
Harsh Shandilya b970cfb305 build: update gradle properties
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-19 22:21:26 +05:30
probot-auto-merge[bot]andGitHub cf2ce55d02 Merge pull request #18 from msfjarvis/swipe-dismiss
Add swipe dismiss
2020-09-19 00:43:15 +00:00
Harsh Shandilya a847a110c7 Wire in AnimatedSwipeDismiss into UI
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-19 05:45:20 +05:30
Harsh Shandilya 3201bfe507 ui: import AnimatedSwipeDismiss code from Brandon McAnsh
Source: https://gist.github.com/bmc08gt/fca95db3bf9fcf255d76f03ec10ea3f9
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-19 05:35:46 +05:30
Harsh Shandilya c8d2de60bb build: bump AGO to 4.2.0-alpha11
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-19 05:19:52 +05:30
Harsh ShandilyaandGitHub 9df4d5f381 Merge pull request #17 from msfjarvis/update-compose
build: update Compose
2020-09-17 03:11:40 +05:30
Harsh Shandilya c75955a4a3 Update tests for Compose alpha03
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-17 02:06:43 +05:30
Harsh ShandilyaandGitHub 43e9b000d9 build: update Compose 2020-09-17 01:46:04 +05:30
probot-auto-merge[bot]andGitHub 35bec0bed2 Merge pull request #16 from msfjarvis/optimize-for-urls
Optimize UI and UX for URLs
2020-09-13 22:05:20 +00:00
Harsh Shandilya d84d99632e Ignore tests because what the actual fuck
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-14 03:26:03 +05:30
Harsh Shandilya 0eb4f74e36 Provide UrlLauncher through Ambient
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-14 00:12:03 +05:30
Harsh Shandilya 44393643c9 idea: tweak codestyle
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-14 00:12:03 +05:30
Harsh Shandilya 1a1410eb1c Add and implement UrlLauncher
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-14 00:12:03 +05:30
Harsh Shandilya 34ba71e229 Revamp UI for supporting a new click action
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-13 23:48:03 +05:30
Harsh Shandilya 1c05556d98 Disable jetifier
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-13 23:42:20 +05:30
Harsh Shandilya 94258b8d97 MainActivity: drop hardcoded top appbar title
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-13 23:24:52 +05:30
Harsh ShandilyaandGitHub 00310914da Merge pull request #15 from msfjarvis/dagger-hilt
Switch to Dagger Hilt for dependency injection
2020-09-11 23:49:54 +05:30
Harsh Shandilya d42657b0a5 app: switch to Hilt for dependency injection
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-11 23:38:03 +05:30
Harsh Shandilya 57f7a72d8d build: add Hilt dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-11 23:38:03 +05:30
Harsh ShandilyaandGitHub 36474b9705 Merge pull request #14 from msfjarvis/dependency-updates 2020-09-11 23:37:42 +05:30
Harsh Shandilya 048e0d01de github: run pull requests on both API 23 and 29
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-11 23:19:49 +05:30
Harsh Shandilya a2cb0f073c build: move library desugaring to app module for runtime compatibility
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-11 23:18:17 +05:30
Harsh Shandilya 67a8826d24 build: remove unnecessary deps
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-11 23:10:58 +05:30
Harsh Shandilya e3301801b7 build: update Kotlin and AGP
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-11 23:10:40 +05:30
Harsh Shandilya 4ec76766d0 build: update to Gradle 6.7 RC1
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-11 22:51:48 +05:30
probot-auto-merge[bot]andGitHub b3008e99dc Merge pull request #13 from msfjarvis/modularization
Modularize data package
2020-09-08 01:20:21 +00:00
Harsh Shandilya 1f76a177fe build: commonize Gradle configurations
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-08 06:42:47 +05:30
Harsh Shandilya 19817bf254 Move data package to its own module
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-08 06:35:38 +05:30
probot-auto-merge[bot]andGitHub b5a3d6489b Merge pull request #12 from msfjarvis/testing
Increase test surface to cover more cases
2020-09-08 00:40:10 +00:00
Harsh Shandilya 272f0d1d99 Add test to ensure new item is not added if name is empty
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-08 06:01:57 +05:30
Harsh Shandilya 0ccd3b353d Add test to ensure new item is added correctly
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-08 06:01:57 +05:30
Harsh Shandilya e0e28b5e35 Add test to check item add dialog is shown
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-08 06:01:57 +05:30
Harsh Shandilya 48b3b83cae Set test tag on ItemAddDialog
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-08 06:01:56 +05:30
Harsh Shandilya 2407a0125e probot-auto-merge: delete merged branches
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-08 06:01:27 +05:30
probot-auto-merge[bot]andGitHub 865b01c93b Merge pull request #11 from msfjarvis/dialog-improvements
Correctly enable now-fixed theming and tweak dialog layout
2020-09-08 00:26:42 +00:00
Harsh Shandilya 34ce5e9448 Tweak item addition dialog layout
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-08 05:44:01 +05:30
Harsh Shandilya 8df3db3607 TodoTheme: remove typography customizations
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-08 05:36:30 +05:30
Harsh Shandilya b7124e5bda TodoTheme: wire in customized elements
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-08 05:35:26 +05:30
probot-auto-merge[bot]andGitHub 57d6855c78 Merge pull request #10 from Skrilltrax/fix-theme
Fix theme colors
2020-09-07 06:45:06 +00:00
Harsh Shandilya 64cc897f1a Add missing import
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-07 12:09:24 +05:30
Aditya Wasan caa2811c0a Fix theme colors
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2020-09-07 11:42:26 +05:30
probot-auto-merge[bot]andGitHub c43e8b6b41 Merge pull request #9 from msfjarvis/custom-names
Allow providing names for items
2020-09-06 17:42:24 +00:00
Harsh Shandilya 3ed1cdb261 TodoTheme: use default colors but with system theme awareness
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-06 23:05:09 +05:30
Harsh Shandilya bfd060e66e TodoTheme: strip out all custom theming
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-06 22:55:46 +05:30
Harsh Shandilya 91fc3aa9fa all: reformat
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-06 22:49:05 +05:30
Harsh Shandilya 153f94715d MainActivity: allow setting a custom name for the item
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-06 22:48:38 +05:30
Harsh Shandilya e8719f3796 build: update testing libraries
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-06 21:29:03 +05:30
Harsh Shandilya f73d75dd6a editorconfig: init
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-06 21:01:51 +05:30
probot-auto-merge[bot]andGitHub 6a3ede9102 Merge pull request #8 from msfjarvis/dependency-updates
Update compose and AGP
2020-09-03 06:22:51 +00:00
Harsh Shandilya b4aef5d14d Update compose and AGP
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-09-03 11:44:26 +05:30
probot-auto-merge[bot]andGitHub 32f8ea31e2 Merge pull request #7 from msfjarvis/restore-preview
Separate out item loading to enable host previews again
2020-08-30 12:34:30 +00:00
Harsh Shandilya 841759ddd8 REVERTME: copy preview code into tests
We wanna better mimic real behavior as opposed to this broken preview.

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-30 17:55:18 +05:30
Harsh Shandilya bd6f7583c8 Borrow some improvements from my previous attempt
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-30 16:46:57 +05:30
Harsh Shandilya 0c3b1d388f Separate out item loading to enable host previews again
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-30 16:38:55 +05:30
probot-auto-merge[bot]andGitHub 778faf2950 Merge pull request #6 from msfjarvis/test-improvements
Switch to createComposeRule
2020-08-30 10:29:16 +00:00
Harsh Shandilya 4581705d56 Switch to createComposeRule
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-30 15:58:07 +05:30
Harsh ShandilyaandGitHub 001fdab48b Merge pull request #5 from msfjarvis/persistence 2020-08-27 22:27:49 +05:30
Harsh Shandilya 6c5778e547 Fix tests
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 21:39:52 +05:30
Harsh Shandilya 2450512059 Drop default item checks from tests
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 21:10:30 +05:30
Harsh Shandilya bb80b42426 Add delete operation
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 21:02:48 +05:30
Harsh Shandilya 717eefad97 Switch over persistence to Room
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 20:59:35 +05:30
Harsh Shandilya 644a8a6110 Create a rudimentary DI container for Room
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 20:59:35 +05:30
Harsh Shandilya 3d8935dc2b Add backing infra for Room
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 20:59:34 +05:30
Harsh Shandilya e16b4cb82d Enable desugaring of core library elements
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 20:59:34 +05:30
Harsh Shandilya 5ce951d599 Update TodoItem for Room
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 20:59:33 +05:30
Harsh Shandilya 0b5d7a1624 Add room dependencies and sort
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 20:56:41 +05:30
Harsh Shandilya e256d988fc Update test dependencies
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 20:56:41 +05:30
Harsh Shandilya 25ca5d52b2 Drop extra dependencies and host icon in-app
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 20:56:40 +05:30
Harsh Shandilya e6942fe353 Mark TodoItem as Immutable
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 15:18:42 +05:30
Harsh Shandilya f9e17678b0 Relocate TodoItem class
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 15:17:47 +05:30
Harsh Shandilya f385504c30 Separate out preview composable
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 15:04:42 +05:30
Harsh ShandilyaandGitHub e34268c4c1 Merge pull request #4 from msfjarvis/drop-alternation
Use a single row item for all elements
2020-08-27 03:35:55 +05:30
probot-auto-merge[bot]andGitHub 4b95927f5f Merge pull request #3 from msfjarvis/dependency-updates
Update dependencies
2020-08-26 21:55:29 +00:00
Harsh Shandilya 2ccefb76a5 Use a single row item for all elements
The WireGuardItem class was created to compare an existing UI elements from standard view-based apps into a Compose alternative and doesn't really belong here

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 03:04:23 +05:30
Harsh Shandilya 44d8ee902a build: update androidx and material
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 02:53:46 +05:30
Harsh Shandilya 4bc184f06c build: update AGP
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 02:50:39 +05:30
Harsh Shandilya a3e0503613 build: update compose
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 02:50:28 +05:30
Harsh Shandilya fc06690ffd github: configure probot-auto-merge
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 02:47:28 +05:30
Harsh ShandilyaandGitHub 9606932a5c Merge pull request #2 from msfjarvis/compose-ui-tests
Add basic UI tests
2020-08-27 00:35:05 +05:30
Harsh Shandilya 55e7ab5490 github: switch to API 29
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 00:20:48 +05:30
Harsh Shandilya 3d77f397f6 Expand UI tests for addition
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 00:18:55 +05:30
Harsh Shandilya 2c0b4fc072 Add a basic UI test
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 00:18:55 +05:30
Harsh Shandilya 6e246ed246 github: add missing ci-gradle.properties
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 00:18:43 +05:30
Harsh Shandilya 39e695dbcc github: add PR workflow
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-27 00:16:51 +05:30
Harsh Shandilya 3d42671277 Fix ripple indication
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-25 22:17:47 +05:30
Harsh Shandilya 28fd3f6f7e WireGuardItem: make clickable
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-25 01:05:39 +05:30
Harsh Shandilya 24bf7e5165 Drop in some trailing commas for later
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-25 00:43:05 +05:30
Harsh Shandilya afe93e989f Alternate between WireGuardItem and TodoRowItem
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-25 00:38:59 +05:30
Harsh Shandilya b6e01e71fa WireGuardItem: fix checked change listener and theming
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-25 00:38:42 +05:30
Harsh Shandilya 07d30410da Fix items persistence
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-25 00:30:22 +05:30
Harsh Shandilya 79299ee95a Properly do state persistence
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-24 23:55:39 +05:30
Harsh Shandilya eb01ca2b81 idea: don't star imports too early
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-24 23:41:48 +05:30
Harsh Shandilya 15d8f61e97 Add ripple indication
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-24 23:15:33 +05:30
Harsh Shandilya bfdc4b3183 Add broken reactivity
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-24 22:34:13 +05:30
Harsh Shandilya 405cf34193 Add a simple checkbox style view mirroring the wireguard app
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-24 22:29:05 +05:30
Harsh Shandilya d930182e54 Move TodoRow to a separate file
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-24 21:59:09 +05:30
Harsh Shandilya 4ddd01b77c Remove unused import
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-22 20:12:35 +05:30
Aditya Wasan 5a4b2754e3 Haha material design go brrrrr
Signed-off-by: Aditya Wasan <adityawasan55@gmail.com>
2020-08-22 19:45:08 +05:30
Harsh Shandilya af88005cb5 Break out TodoRowItem to a separate Composable
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-22 14:53:47 +05:30
Harsh Shandilya 6558ec7909 Use a Scaffold to lay things out
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-22 14:49:39 +05:30
Harsh Shandilya 04f2b20503 Switch addition over to FAB
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-22 14:44:46 +05:30
Harsh Shandilya d2ca717f48 Remove default tests
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-22 00:47:34 +05:30
Harsh Shandilya a1237ab9b6 Initial MVP
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
2020-08-22 00:45:00 +05:30
528 changed files with 4526 additions and 127650 deletions
-93
View File
@@ -1,93 +0,0 @@
# This .editorconfig section approximates ktfmt's formatting rules. You can include it in an
# existing .editorconfig file or use it standalone by copying it to <project root>/.editorconfig
# and making sure your editor is set to read settings from .editorconfig files.
#
# It includes editor-specific config options for IntelliJ IDEA.
#
# If any option is wrong, PR are welcome
[{*.kt,*.kts}]
indent_style = space
insert_final_newline = true
max_line_length = 100
indent_size = 2
ij_continuation_indent_size = 2
ij_java_names_count_to_use_import_on_demand = 9999
ij_kotlin_align_in_columns_case_branch = false
ij_kotlin_align_multiline_binary_operation = false
ij_kotlin_align_multiline_extends_list = false
ij_kotlin_align_multiline_method_parentheses = false
ij_kotlin_align_multiline_parameters = true
ij_kotlin_align_multiline_parameters_in_calls = false
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true
ij_kotlin_assignment_wrap = normal
ij_kotlin_blank_lines_after_class_header = 0
ij_kotlin_blank_lines_around_block_when_branches = 0
ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
ij_kotlin_block_comment_at_first_column = true
ij_kotlin_call_parameters_new_line_after_left_paren = true
ij_kotlin_call_parameters_right_paren_on_new_line = false
ij_kotlin_call_parameters_wrap = on_every_item
ij_kotlin_catch_on_new_line = false
ij_kotlin_class_annotation_wrap = split_into_lines
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
ij_kotlin_continuation_indent_for_chained_calls = true
ij_kotlin_continuation_indent_for_expression_bodies = true
ij_kotlin_continuation_indent_in_argument_lists = true
ij_kotlin_continuation_indent_in_elvis = false
ij_kotlin_continuation_indent_in_if_conditions = false
ij_kotlin_continuation_indent_in_parameter_lists = false
ij_kotlin_continuation_indent_in_supertype_lists = false
ij_kotlin_else_on_new_line = false
ij_kotlin_enum_constants_wrap = off
ij_kotlin_extends_list_wrap = normal
ij_kotlin_field_annotation_wrap = split_into_lines
ij_kotlin_finally_on_new_line = false
ij_kotlin_if_rparen_on_new_line = false
ij_kotlin_import_nested_classes = false
ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
ij_kotlin_keep_blank_lines_before_right_brace = 2
ij_kotlin_keep_blank_lines_in_code = 2
ij_kotlin_keep_blank_lines_in_declarations = 2
ij_kotlin_keep_first_column_comment = true
ij_kotlin_keep_indents_on_empty_lines = false
ij_kotlin_keep_line_breaks = true
ij_kotlin_lbrace_on_next_line = false
ij_kotlin_line_comment_add_space = false
ij_kotlin_line_comment_at_first_column = true
ij_kotlin_method_annotation_wrap = split_into_lines
ij_kotlin_method_call_chain_wrap = normal
ij_kotlin_method_parameters_new_line_after_left_paren = true
ij_kotlin_method_parameters_right_paren_on_new_line = true
ij_kotlin_method_parameters_wrap = on_every_item
ij_kotlin_name_count_to_use_star_import = 9999
ij_kotlin_name_count_to_use_star_import_for_members = 9999
ij_kotlin_parameter_annotation_wrap = off
ij_kotlin_space_after_comma = true
ij_kotlin_space_after_extend_colon = true
ij_kotlin_space_after_type_colon = true
ij_kotlin_space_before_catch_parentheses = true
ij_kotlin_space_before_comma = false
ij_kotlin_space_before_extend_colon = true
ij_kotlin_space_before_for_parentheses = true
ij_kotlin_space_before_if_parentheses = true
ij_kotlin_space_before_lambda_arrow = true
ij_kotlin_space_before_type_colon = false
ij_kotlin_space_before_when_parentheses = true
ij_kotlin_space_before_while_parentheses = true
ij_kotlin_spaces_around_additive_operators = true
ij_kotlin_spaces_around_assignment_operators = true
ij_kotlin_spaces_around_equality_operators = true
ij_kotlin_spaces_around_function_type_arrow = true
ij_kotlin_spaces_around_logical_operators = true
ij_kotlin_spaces_around_multiplicative_operators = true
ij_kotlin_spaces_around_range = false
ij_kotlin_spaces_around_relational_operators = true
ij_kotlin_spaces_around_unary_operator = false
ij_kotlin_spaces_around_when_arrow = true
ij_kotlin_variable_annotation_wrap = off
ij_kotlin_while_on_new_line = false
ij_kotlin_wrap_elvis_expressions = 1
ij_kotlin_wrap_expression_body_functions = 1
ij_kotlin_wrap_first_method_in_call_chain = false
+2 -11
View File
@@ -1,11 +1,2 @@
# KMP removal
3d496c99c14106a7f433d01a6a9a1301cc89844a
e7f4c8a7ebe4e907f65af46e6dddfd3a542415c0
ab2713154f846b3db6d44625d1022bf421b275d0
cbdfa67d075ec1f8ceec0f3559498cbca01783a0
# Renamed Renovate config file
f304304bc5b64edf189aaa473b05df912067a8bd
# Refactored database module into core and impl
7b0b206905b8dc1f824a2d2a10e3d94c6b9dd11c
cec0e1bbcb7eec6c8eb90807c105cbeab9c01c6c # Reformatted Gradle build files with 2 space indent
c3cead59934c8c5221694940fc83be57fa50ac20 # Moved data module into app
-4
View File
@@ -1,4 +0,0 @@
* text=auto eol=lf
**/*.html linguist-vendored
android/src/main/baseline-prof.txt linguist-generated -diff
android/src/main/generated/** linguist-generated -diff
+1
View File
@@ -0,0 +1 @@
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 432 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 435 KiB

-80
View File
@@ -1,80 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":dependencyDashboard",
"helpers:pinGitHubActionDigests",
"github>msfjarvis/shared-workflows//renovate/automerge"
],
"branchConcurrentLimit": 15,
"github-actions": {
"managerFilePatterns": [
"/.github/reusable-workflows/.+\\.ya?ml$/",
],
},
"packageRules": [
{
matchDatasources: [
"maven"
],
registryUrls: [
"https://maven.google.com/",
"https://repo1.maven.org/maven2",
"https://plugins.gradle.org/m2/",
"https://jitpack.io",
],
},
{
"matchDepNames": [
"com.google.devtools.ksp",
"dev.drewhamilton.poko",
"org.jetbrains.kotlin:kotlin-gradle-plugin",
"org.jetbrains.kotlin.plugin.compose",
"org.jetbrains.kotlin.plugin.serialization",
],
"groupName": "Kotlin and Compose",
},
{
"matchPackageNames": [
"/^androidx.*/",
],
"groupName": "AndroidX"
},
{
"matchPackageNames": [
"/^io.sentry/",
"/^sentry/",
],
"groupName": "Sentry"
},
{
"matchPackageNames": [
"/^com.squareup.anvil/"
],
"allowedVersions": "!/.*-1-8$/",
},
{
"matchManagers": [
"github-actions"
],
"matchDepNames": [
"msfjarvis/compose-lobsters"
],
"enabled": false,
},
],
"customManagers": [
{
"customType": "regex",
"managerFilePatterns": [
"/SpotlessPlugin\.kt$/"
],
"matchStrings": [
"KTFMT_VERSION = \"(?<currentValue>.*)\""
],
"datasourceTemplate": "maven",
"depNameTemplate": "com.facebook:ktfmt",
"registryUrlTemplate": "https://repo1.maven.org/maven2/",
},
],
}
@@ -1,61 +0,0 @@
name: 'Setup Gradle'
description: 'Checks out the repository and sets up Java and Gradle'
inputs:
token:
description: 'token input for actions/checkout'
required: false
default: ${{ github.token }}
fetch-depth:
description: 'fetch-depth input for actions/checkout'
required: false
default: 1
cache-read-only:
description: 'cache-read-only input for gradle/actions/setup-gradle'
required: false
default: ${{ github.event.repository != null && github.ref_name != github.event.repository.default_branch }}
dependency-graph:
description: 'dependency-graph input for gradle/actions/setup-gradle'
required: false
default: 'disabled'
persist-credentials:
description: 'Control the actions/checkout persist-credentials setting'
required: false
default: false
runs:
using: "composite"
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: ${{ inputs.fetch-depth }}
token: ${{ inputs.token }}
lfs: true
persist-credentials: ${{ inputs.persist-credentials }}
- name: Validate Gradle wrapper
uses: gradle/actions/wrapper-validation@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6
- name: Set up JDK
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: temurin
java-version: 25
- name: Accept Android SDK licenses
shell: bash
run: |
yes | "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --licenses || true
- name: Set global Gradle properties
shell: bash
run: |
mkdir -p ~/.gradle/
echo "android.testoptions.manageddevices.emulator.gpu=swiftshader_indirect" >> ~/.gradle/gradle.properties
- name: Setup Gradle
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6
with:
add-job-summary: always
cache-read-only: ${{ inputs.cache-read-only }}
dependency-graph: ${{ inputs.dependency-graph }}
validate-wrappers: true
-80
View File
@@ -1,80 +0,0 @@
name: CI
on:
push:
branches:
- main
- renovate/**
pull_request:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check:
runs-on: macos-26
steps:
- name: Setup build environment
uses: msfjarvis/compose-lobsters/.github/reusable-workflows/setup-gradle@92aae743ab8e1c98741128f1bfda3715750f5cf6 # main
with:
fetch-depth: 0
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
dependency-graph: generate-and-submit
- name: Check changelog format
uses: mindsers/changelog-reader-action@1faaf50aa09d5793d9a100819973df801febfb31 # v2.4.0
with:
path: ./CHANGELOG.md
validation_level: error
- name: Run unit tests
run: ./gradlew --no-configuration-cache --stacktrace :build-logic:check check -PslimTests
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: (Fail-only) Upload test report
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: Test report
path: '**/build/reports/tests/**'
dependency-review:
runs-on: macos-26
if: "github.event_name == 'push' && github.event.ref != 'refs/heads/main'"
needs:
- check
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Dependency Review
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
with:
base-ref: refs/heads/main
head-ref: ${{ github.ref }}
release-app:
runs-on: macos-26
needs:
- check
steps:
- name: Setup build environment
uses: msfjarvis/compose-lobsters/.github/reusable-workflows/setup-gradle@92aae743ab8e1c98741128f1bfda3715750f5cf6 # main
with:
cache-read-only: true
- name: Build release app
run: |
./scripts/setup-age.sh
./scripts/signing-setup.sh "$AGE_SECRET_KEY"
./gradlew --no-configuration-cache --stacktrace assembleRelease collectReleaseBundle
./scripts/signing-cleanup.sh
env:
AGE_SECRET_KEY: ${{ secrets.AGE_SECRET_KEY }}
+42
View File
@@ -0,0 +1,42 @@
on:
push:
branches:
- develop
name: Deploy snapshot builds
jobs:
deploy-release-snapshot:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[ci skip]')"
steps:
- uses: actions/setup-java@d202f5dbf7256730fb690ec59f6381650114feb2
with:
java-version: '11'
- name: Checkout repository
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Decrypt secrets
run: scripts/signing-setup.sh "$ENCRYPT_KEY"
env:
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
- name: Copy CI gradle.properties
run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties
- name: Build release app
uses: burrunan/gradle-cache-action@03c71a8ba93d670980695505f48f49daf43704a6
with:
arguments: :app:assembleRelease
- name: Clean secrets
run: scripts/signing-cleanup.sh
- name: Deploy snapshot
run: scripts/deploy-snapshot.sh
env:
ACTIONS_DEPLOY_KEY: ${{ secrets.ACTIONS_DEPLOY_KEY }}
SSH_USERNAME: ${{ secrets.SSH_USERNAME }}
SERVER_ADDRESS: ${{ secrets.SERVER_ADDRESS }}
SERVER_DESTINATION: ${{ secrets.SERVER_DESTINATION }}
SSH_PORT: ${{ secrets.SSH_PORT }}
+55
View File
@@ -0,0 +1,55 @@
name: CI
on:
push:
branches:
- staging
- trying
jobs:
run-unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-java@d202f5dbf7256730fb690ec59f6381650114feb2
with:
java-version: '11'
- name: Checkout repository
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
- name: Copy CI gradle.properties
run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties
- uses: burrunan/gradle-cache-action@03c71a8ba93d670980695505f48f49daf43704a6
name: Run unit tests
with:
arguments: test --stacktrace
- name: (Fail-only) upload test report
if: failure()
uses: actions/upload-artifact@27bce4eee761b5bc643f46a8dfb41b430c8d05f6
with:
name: Test report
path: app/build/reports
stylecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-java@d202f5dbf7256730fb690ec59f6381650114feb2
with:
java-version: '11'
- name: Checkout repository
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
with:
fetch-depth: 0
- name: Copy CI gradle.properties
run: mkdir -p ~/.gradle ; cp .github/ci-gradle.properties ~/.gradle/gradle.properties
- uses: burrunan/gradle-cache-action@03c71a8ba93d670980695505f48f49daf43704a6
name: Check codestyle with Spotless
with:
arguments: spotlessCheck
-176
View File
@@ -1,176 +0,0 @@
name: Release to Google Play
on:
workflow_dispatch:
inputs:
inAppUpdatePriority:
description: 'In app update priority (0-5)'
type: number
default: 0
required: false
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
publish-google-play-release:
runs-on: macos-26
steps:
- name: Setup build environment
uses: msfjarvis/compose-lobsters/.github/reusable-workflows/setup-gradle@92aae743ab8e1c98741128f1bfda3715750f5cf6 # main
with:
token: ${{ secrets.POST_RELEASE_GH_TOKEN }}
fetch-depth: 0
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
persist-credentials: true
- name: Set up Git author
shell: bash
run: |
git config user.name "GitHub Actions"
git config user.email noreply@github.com
- name: Decrypt secrets
run: |
./scripts/setup-age.sh
./scripts/signing-setup.sh "$AGE_SECRET_KEY"
env:
AGE_SECRET_KEY: ${{ secrets.AGE_SECRET_KEY }}
- name: Get current version
shell: bash
run: |
./gradlew -q --no-configuration-cache clearPreRelease
VERSION="$(grep 'versioning-plugin.versionName' android/version.properties | cut -d = -f 2)"
VERSION_CODE="$(grep 'versioning-plugin.versionCode' android/version.properties | cut -d = -f 2)"
echo VERSION="${VERSION}" >> $GITHUB_ENV
echo VERSION_CODE="${VERSION_CODE}" >> $GITHUB_ENV
- name: Update changelog
uses: msfjarvis/keep-a-changelog-new-release@e60399f488b25149c0963a4e22fff928a9277ce7 # v2.2.0
with:
tag: v${{ env.VERSION }}
version: ${{ env.VERSION }}
- name: Get changelog entry
id: changelog_reader
uses: mindsers/changelog-reader-action@1faaf50aa09d5793d9a100819973df801febfb31 # v2.4.0
with:
version: ${{ env.VERSION }}
path: ./CHANGELOG.md
- name: Write changelog to fastlane metadata
shell: bash
env:
CHANGELOG: ${{ steps.changelog_reader.outputs.changes }}
run: |
mkdir -p fastlane/metadata/android/en-US/changelogs
printf '%s\n' "${CHANGELOG}" > fastlane/metadata/android/en-US/changelogs/${VERSION_CODE}.txt
- name: Commit changes
shell: bash
run: |
git add .
git commit -m 'feat(release): bump version'
- name: Calculate VCS info for Sentry
shell: bash
run: |
# Get HEAD commit SHA
HEAD_SHA=$(git rev-parse HEAD)
echo "SENTRY_VCS_HEAD_SHA=${HEAD_SHA}" >> $GITHUB_ENV
# Get parent info (format: commit parent1 [parent2...])
PARENTS=$(git rev-list --parents -n 1 HEAD)
PARENT_COUNT=$(echo "$PARENTS" | wc -w)
if [ "$PARENT_COUNT" -ge 3 ]; then
# Merge commit (commit + 2+ parents): use first parent
BASE_SHA=$(echo "$PARENTS" | awk '{print $2}')
else
# Normal commit: use last release
LAST_TAG=$(gh release list --limit 1 --json tagName -q '.[0].tagName' || echo "")
if [ -n "$LAST_TAG" ]; then
BASE_SHA=$(git rev-parse "${LAST_TAG}^{commit}")
else
BASE_SHA="${HEAD_SHA}"
fi
fi
echo "SENTRY_VCS_BASE_SHA=${BASE_SHA}" >> $GITHUB_ENV
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build release assets
shell: bash
env:
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_VCS_HEAD_SHA: ${{ env.SENTRY_VCS_HEAD_SHA }}
SENTRY_VCS_BASE_SHA: ${{ env.SENTRY_VCS_BASE_SHA }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
./gradlew --no-configuration-cache assembleRelease uploadSentryProguardMappingsRelease collectReleaseApks collectReleaseBundle -PenableSentry
- name: Clean secrets
run: scripts/signing-cleanup.sh
- name: Write release notes file
shell: bash
env:
CHANGELOG: ${{ steps.changelog_reader.outputs.changes }}
run: |
mkdir -p distribution/whatsnew
printf '%s' "${CHANGELOG}" >> distribution/whatsnew/whatsnew-en-GB
- name: Publish bundle to Google Play
uses: r0adkll/upload-google-play@e738b9dd8f2476ea806d921b64aacd24f34515a5 # v1.1.5
with:
mappingFile: android/bundle/mapping.txt
packageName: dev.msfjarvis.claw.android
releaseFiles: android/bundle/*.aab
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
status: completed
track: production
whatsNewDirectory: distribution/whatsnew
inAppUpdatePriority: ${{ inputs.inAppUpdatePriority }}
- name: Post-release work
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.POST_RELEASE_GH_TOKEN }}
run: |
# Tag the current version
git tag "v${VERSION}" -F distribution/whatsnew/whatsnew-en-GB
# Push the tag to GitHub
git push origin "v${VERSION}"
# Create GitHub release notes with footer
RELEASE_NOTES=$(mktemp)
{
cat distribution/whatsnew/whatsnew-en-GB
echo ""
echo "---"
echo ""
echo '<a href="https://play.google.com/store/apps/details?id=dev.msfjarvis.claw.android">'
echo ' <img src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png"'
echo ' alt="Get it on Google Play"'
echo ' height="80" />'
echo '</a>'
echo ""
echo '<a href="https://android.izzysoft.de/repo/apk/dev.msfjarvis.claw.android">'
echo ' <img src="./.github/izzy-badge.webp"'
echo ' alt="Get it on IzzyOnDroid"'
echo ' height="65" />'
echo '</a>'
} > "$RELEASE_NOTES"
# Create a GitHub release
gh release create "v${VERSION}" --notes-file "$RELEASE_NOTES" --title "v${VERSION}" ./android/apk/*.apk ./android/bundle/*.aab
# Clean up temp file
rm "$RELEASE_NOTES"
# Start the next development iteration
./gradlew -q --no-configuration-cache bumpSnapshot
git commit -am 'feat(release): start next development iteration'
git push -u origin
-72
View File
@@ -1,72 +0,0 @@
name: Android release build
on:
workflow_dispatch:
inputs:
tramline-input:
description: "Tramline input"
required: false
jobs:
signed-build:
runs-on: ubuntu-latest
steps:
- name: Setup build environment
uses: msfjarvis/compose-lobsters/.github/reusable-workflows/setup-gradle@92aae743ab8e1c98741128f1bfda3715750f5cf6 # main
with:
cache-read-only: true
fetch-depth: 0
- name: Configure Tramline
id: tramline
uses: tramlinehq/deploy-action@58cc98c188b9eda853e549ede6a8b2f58fe29516 # v0.1.7
with:
input: ${{ github.event.inputs.tramline-input }}
- name: Calculate VCS info for Sentry
shell: bash
run: |
# Get HEAD commit SHA
HEAD_SHA=$(git rev-parse HEAD)
echo "SENTRY_VCS_HEAD_SHA=${HEAD_SHA}" >> $GITHUB_ENV
# Get parent info (format: commit parent1 [parent2...])
PARENTS=$(git rev-list --parents -n 1 HEAD)
PARENT_COUNT=$(echo "$PARENTS" | wc -w)
if [ "$PARENT_COUNT" -ge 3 ]; then
# Merge commit (commit + 2+ parents): use first parent
BASE_SHA=$(echo "$PARENTS" | awk '{print $2}')
else
# Normal commit: use last release
LAST_TAG=$(gh release list --limit 1 --json tagName -q '.[0].tagName' || echo "")
if [ -n "$LAST_TAG" ]; then
BASE_SHA=$(git rev-parse "${LAST_TAG}^{commit}")
else
BASE_SHA="${HEAD_SHA}"
fi
fi
echo "SENTRY_VCS_BASE_SHA=${BASE_SHA}" >> $GITHUB_ENV
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build release app
run: |
./scripts/setup-age.sh
./scripts/signing-setup.sh "$AGE_SECRET_KEY"
./gradlew --no-configuration-cache --stacktrace assembleRelease uploadSentryProguardMappingsRelease collectReleaseBundle -PenableSentry
./scripts/signing-cleanup.sh
env:
AGE_SECRET_KEY: ${{ secrets.AGE_SECRET_KEY }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_VCS_HEAD_SHA: ${{ env.SENTRY_VCS_HEAD_SHA }}
SENTRY_VCS_BASE_SHA: ${{ env.SENTRY_VCS_BASE_SHA }}
ORG_GRADLE_PROJECT_VERSION_NAME: ${{ steps.tramline.outputs.version_name }}
ORG_GRADLE_PROJECT_VERSION_CODE: ${{ steps.tramline.outputs.version_code }}
- name: Upload app bundle
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-aab
path: android/build/outputs/bundle/release/android-release.aab
+29 -125
View File
@@ -1,102 +1,10 @@
# Created by https://www.toptal.com/developers/gitignore/api/androidstudio,gradle,intellij+all
# Edit at https://www.toptal.com/developers/gitignore?templates=androidstudio,gradle,intellij+all
### Intellij+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
### START CHANGES - We use Gradle auto-import ###
.idea/artifacts
.idea/compiler.xml
.idea/jarRepositories.xml
.idea/modules.xml
.idea/*.iml
.idea/modules
*.iml
*.ipr
### END CHANGES - We use Gradle auto-import ###
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Intellij+all Patch ###
# Ignore everything but code style settings and run configurations
# that are supposed to be shared within teams.
.idea/*
### START CHANGES - We use Spotless to enforce code style ###
.idea/codeStyles
### END CHANGES - We use Spotless to enforce code style ###
!.idea/runConfigurations
# Created by https://www.gitignore.io/api/androidstudio,gradle
# Edit at https://www.gitignore.io/?templates=androidstudio,gradle
### Gradle ###
.gradle
**/build/
!src/**/build/
build/
# Ignore Gradle GUI config
gradle-app.setting
@@ -104,21 +12,14 @@ gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
# Avoid ignore Gradle wrappper properties
!gradle-wrapper.properties
# Cache of project
.gradletasknamecache
# Eclipse Gradle plugin generated files
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
### Gradle Patch ###
# Java heap dump
*.hprof
**/build/
### AndroidStudio ###
# Covers files to be ignored for android development using Android Studio.
@@ -126,7 +27,6 @@ gradle-app.setting
# Built application files
*.apk
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
@@ -137,13 +37,15 @@ gradle-app.setting
# Generated files
bin/
gen/
out/
# Gradle files
.gradle/
build/
# Signing files
.signing/
keystore.jks
keystore.properties
# Local configuration file (sdk path, etc)
local.properties
@@ -166,13 +68,6 @@ captures/
*~
*.swp
# Keystore files
*.jks
*.keystore
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Android Patch
gen-external-apklibs
@@ -184,6 +79,7 @@ obj/
# IntelliJ IDEA
*.iml
*.iws
/out/
# User-specific configurations
@@ -208,11 +104,20 @@ obj/
.idea/dynamic.xml
.idea/uiDesigner.xml
.idea/assetWizardSettings.xml
.idea/gradle.xml
.idea/jarRepositories.xml
.idea/navEditor.xml
.idea/deploymentTargetDropDown.xml
# OS-specific files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Legacy Eclipse project files
.classpath
.project
.cproject
.settings/
@@ -229,23 +134,22 @@ hs_err_pid*
## Plugin-specific files:
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Mongo Explorer plugin
.idea/mongoSettings.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### AndroidStudio Patch ###
!/gradle/wrapper/gradle-wrapper.jar
# End of https://www.toptal.com/developers/gitignore/api/androidstudio,gradle,intellij+all
keystore.properties
distribution/
screenshots/
claw-export.json
.kotlin/
.worktrees/
# End of https://www.gitignore.io/api/androidstudio,gradle
+5
View File
@@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
/artifacts/
/gradle.xml
-21
View File
@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaProjectCodeInsightSettings">
<excluded-names>
<name>android.app.LauncherActivity.ListItem</name>
<name>android.graphics.Color</name>
<name>android.graphics.drawable.Icon</name>
<name>android.graphics.fonts.FontFamily</name>
<name>android.inputmethodservice.Keyboard.Row</name>
<name>android.text.layout.Alignment</name>
<name>android.view.RoundedCorner</name>
<name>android.view.Surface</name>
<name>android.widget.GridLayout.Alignment</name>
<name>java.lang.reflect.Modifier</name>
<name>java.nio.file.WatchEvent.Modifier</name>
<name>java.time.format.TextStyle</name>
<name>java.util.Observable</name>
<name>org.w3c.dom.Text</name>
</excluded-names>
</component>
</project>
+143
View File
@@ -0,0 +1,143 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="LINE_SEPARATOR" value="&#10;" />
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value />
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="100" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="100" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
<option name="FIELD_ANNOTATION_WRAP" value="1" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>
+5
View File
@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

+20
View File
@@ -0,0 +1,20 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="previewFile" value="true" />
</inspection_tool>
</profile>
</component>
+35
View File
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://dl.bintray.com/kotlin/kotlinx" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://maven.pkg.jetbrains.space/public/p/compose/dev" />
</remote-repository>
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinScriptingSettings">
<option name="suppressDefinitionsCheck" value="true" />
</component>
</project>
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KtfmtSettings">
<option name="enableKtfmt" value="Enabled" />
<option name="enabled" value="true" />
<option name="uiFormatterStyle" value="Google (internal)" />
</component>
</project>
-4
View File
@@ -1,4 +0,0 @@
*.json
baseline-prof.txt
startup-prof.txt
**/test/resources/**
-678
View File
@@ -1,678 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [1.64.0] - 2026-05-26
### Added
- Allow logging in to avoid rate limiting problems.
### Changed
- The migration path for tag data format changes in 1.61.0 has been removed.
- The app no longer uses the unsupported JSON endpoints on lobste.rs, instead relying
on parsing the HTML pages instead.
## [1.63.0] - 2026-04-02
### Fixed
- Comments are no longer indicated as unread on the first visit of a post
### Changed
- Post titles in the list view can now take up 2 lines to make ellipsizing less likely
- Action buttons now have a prominent background to make their touch target more obvious
- Action buttons now have additional padding to avoid accidental presses when interacting
with system gestures
Thanks to Charles Lombardo for contributing the action button UI changes.
## [1.62.0] - 2026-03-10
### Fixed
- Improve comment scrolling performance
- Ensure saved/read state for posts updates properly in UI
- All strings in the app are now translatable, open to volunteer contributions.
## [1.61.0] - 2026-02-28
### Fixed
- Collapsed comments state are now persisted across navigation events
### Changed
- Tags use a more muted background color to avoid being distracting.
- Tag blocks can now be time-bound
## [1.60.0] - 2026-02-14
### Fixed
- Rare crash in widget handling
- Line-breaking behavior in comments now packs text densely
- Tag filtering now applies to widgets as well
### Changed
- Links to other posts now open in the app itself
- Widget handles bad network scenarios better
## [1.59.0] - 2026-01-09
### Fixed
- Crashes from process death
- Crashes in widget handling
- Android Auto Backup works again
### Changed
- Added a tag filtering option to settings
- Comment count badge has been restored
- Navigation behavior has been improved to avoid too-deep hierarchies
- Widget clicks are handled reliably
- Widget UI has been touched up and a preview added for Android 16+
- User profile page is now scrollable
## [1.58.0] - 2025-12-26
### Fixed
- Avoid crash from trying to import invalid backups
- Avoid crash when posts move between pages
### Changed
- Widget update is more reliable
- Increased network timeouts
- Better network retries and rate limit avoidance
- Temporarily remove comment count to fix crashes
## [1.57.0] - 2025-12-07
### Changed
- Navigation rewritten for larger screens
- Comment expand/collapse now uses button UI
- Comment rendering rewritten for better performance
- Settings page redesigned
### Fixed
- Database concurrency improvements
- Upgrade to Compose 2025-12 alpha
- Swipe action trigger distance increased for Android 16 QPR1 Beta 1
- Deeply nested comment tree collapsing
- Post author indication in details
- Dark mode toggle crash
## [1.55.0] - 2025-02-13
### Fixed
- Adapt to lobsters API change
## [1.54.0] - 2025-01-31
### Fixed
- Upgrade to Compose 2025-01 releases
- Upgrade all dependencies
## [1.53.0] - 2024-11-30
### Fixed
- Tablet users can now navigate between hottest/newest/saved posts again
## [1.52.0] - 2024-11-26
### Added
- A brand new tablet-specific UI — Thanks [@ThanaReka](https://github.com/@ThanaReka)
- Usernames of story authors are highlighted in comments
### Fixed
- Add a workaround for rare crashes while loading SQLite with older devices
- Fix comments page resetting collapsed state while scrolling
- Images are cached for longer on disk
- Navigation bar now renders a solid background on API levels 31 and below
## [1.51.0] - 2024-09-22
### Fixed
- Pull to refresh indicator is no longer hiding behind the top app bar
- User profile page correctly renders contents below the top app bar
- Revert unread comment handling changes that caused false positives
## [1.50.0] - 2024-08-30
### Fixed
- Search bar draws under the status bar when hiding keyboard
- Bottom of the comments page is cut off by the navigation bar
- Rare crash in comments page
## [1.49.0] - 2024-08-29
### Added
- Opening posts you have previously seen will show the number of new comments since last visit
### Fixed
- Saving posts no longer triggers a page refresh that invalidates scroll position
### Changed
- Change submitter text to 'authored' when applicable
- Unread comments now have a brighter background rather than a text badge
- Bottom navigation bar has been redesigned
## [1.48.0] - 2024-06-05
### Added
- Swiping a post from left to right now offers a share action
### Fixed
- Try to work around an infrequent crash when scrolling hottest/newest posts
### Changed
- Upgrade to Kotlin 2.0.0
- Upgrade to Compose May beta releases
## [1.47.0] - 2024-05-14
### Changed
- Bring back dividers between posts (I regret my earlier choices)
- Upgrade to Compose May stable releases
### Fixed
- Navigating to user profiles now works when invoked from the search
results page
- Fix occasional crashes due to the app incorrectly trying to open
multiple database connections
## [1.46.0] - 2024-04-24
### Changed
- Remove dividers between posts
- Post titles in lists are now truncated to be a single line
### Fixed
- Disable logging of network errors to Sentry
- Add potential workaround for navigation-related crashes
## [1.45.0] - 2024-04-24
### Changed
- Upgrade to Compose April releases
- Story items are now more compact so you can see more items
on your screen.
### Fixed
- Fixed a crash when clicking an item on the bottom navigation bar
too quickly
- Removed buggy deep links
- Clicking a username now correctly navigates to the right page in-app
## [1.44.0] - 2024-03-19
### Fixed
- Fixed a bug in the database code that was triggering crashes for
a subset of upgrading users
## [1.43.0] - 2024-03-17
### Fixed
- Fix crash when trying to open a comments-only post from
the home screen widget
## [1.42.0] - 2024-03-16
### Fixed
- Adapt to changes in lobste.rs API
## [1.41.0] - 2024-03-07
### Fixed
- Downgrade dependency that was pulling in extraneous storage
permissions which the app does not require.
## [1.40.0] - 2024-03-07
### Changed
- Update to Compose March releases
- Improve performance of frequently invoked database queries
- Refactor UI data model to improve state handling
- Make saved posts update in the background more often
- Consolidate widget display logic
- Rework some theming logic to align with new Material guidelines
## [1.39.0] - 2024-01-23
### Changed
- Add HTML bookmarks as an export format
- Rework widget item layout for consistent touch targets
- Add attribution for libraries used in the app
- Redesign settings screen
- Update to Compose December releases
## [1.38.0] - 2023-11-20
### Changed
- Add brand new icon by dzuk
- Fix a bug that caused the app to fetch the same 20 posts over and over
- Significantly improve scrolling performance when there are a lot of saved posts
- Move search feature to its own screen
- Make top app and bottom system bars use the same color
- Reduce unnecessary API calls in search screen
- Upgrade to Compose November releases
- Enable logging of SQLite queries
## [1.37.0] - 2023-10-06
### Changed
- Fixed a crash that infrequently happened in the comments page
- Fixed a crash when trying to list saved posts
- Fixed a crash in home screen widget when user had less than 50 saved posts
- Upgraded to Compose 1.6.0-alpha07
## [1.36.0] - 2023-10-04
### Added
- Introduce an initial attempt at a home screen widget
### Changed
- Fixed a crash triggered when swiping items
- Adjusted pull to refresh component to match Material You theme
## [1.35.0] - 2023-09-19
### Changed
- Fixed missing vertical spacing between post tags
- Fixed user profile links not displaying correctly in-app
- Update to Compose October release
## [1.34.0] - 2023-08-30
### Added
- Add a swipe action on each story to open the comments page on `lobste.rs`
- Automatically mark posts as "read" and visually distinguish between them
### Changed
- Tweak list items to reduce vertical size and adjust colors
- Drop bogus workarounds for native library crashes
- Updated Jetpack Compose and SQLite
### Fixed
- Rework how comments are displayed to adapt to lobste.rs API change
## [1.33.0] - 2023-07-31
### Changed
- Yet another attempt at fixing native library crashes
- Rework how baseline profiles are generated
## [1.32.0] - 2023-07-27
### Changed
- Upgrade to AndroidX July 26 release
- Revert selectable text feature due to sporadic crashes
## [1.31.0] - 2023-07-25
### Added
- Added the ability to search for posts
- Text in the comments page is now selectable
### Changed
- Upgrade to Compose July release
- Upgrade to Kotlin 1.9.0
## [1.30.0] - 2023-07-02
### Changed
- Added another workaround for native library loading crash
## [1.29.0] - 2023-06-08
### Added
- Backup and restore options for saved posts
### Fixed
- Spamming the comments button no longer causes it to be opened multiple times
- Saved posts screen now has a visual indication when you have nothing saved
- Iconography has been updated across the board to be more consistent
## [1.28.0] - 2023-06-03
### Changed
- Navigation transitions have been slightly sped up
### Fixed
- Add workaround for a native library loading crash observed on some devices
## [1.27.0] - 2023-05-31
### Changed
- Small accessibility improvements
- Slightly tweak the layout for story items to take less vertical space
- Upgrade to Compose `1.5.0-beta01` release
- Set accessibility web URI for profile screen
- Directly use Material Icons from upstream artifacts
- Upgrade dependencies
- Add adaptive navigation support (thanks @Yash-Garg)
- Use latest SQLite for backing databases
## [1.26.0] - 2023-05-03
### Changed
- Upgrade to Compose May release
- Disable Sentry performance reporting
- Migrate to Compose Foundation `FlowRow` in `LobstersCard`
## [1.25.0] - 2023-04-18
### Changed
- Make the app draw edge to edge
- Simplify Top App Bar color scheme
- Rework how baseline profiles are generated
- Upgrade to Compose April release
## [1.24.0] - 2023-03-24
### Changed
- Rebuild app icon assets to align better with Material guidelines
- Adopt Slack's Compose lints and address issues found by it
- Upgrade to OkHttp 4.x
- Upgrade to Compose March release
- Start work on support for logging in with lobste.rs
## [1.23.0] - 2023-03-02
### Added
- Introduce [Sentry](https://sentry.io) for error reporting and performance monitoring
## [1.22.0] - 2023-03-02
### Changed
- In-app browser now respects the user's choice for dark mode
- Upgrade dependencies
## [1.21.0] - 2023-02-09
### Changed
- Fix bug that caused the app to crash on launch
## [1.20.0] - 2023-02-09
### Changed
- Upgrade dependencies
## [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 the newest posts tab actually showed the 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 comments by ~jcs
## [1.12.0] - 2022-12-09
### Changed
- Do not schedule post update job every time the app starts
- Improve favicon loading to reduce unnecessary redraws
- Fix post lists being reloaded unnecessarily
## [1.11.0] - 2022-12-07
### Changed
- Upgrade to Compose 2022.12.00 release
- Refresh packaged baseline profile
- Improve how saved state of posts is queried
## [1.10.0] - 2022-11-30
### Changed
- Configure Coil image loader with better caching settings
- Refresh packaged baseline profile
## [1.9.0] - 2022-11-28
### Changed
- A loading indicator is added to the bottom of the post list to identify when new posts are being fetched
## [1.8.0] - 2022-11-21
### Changed
- The release pipeline now automatically generates Play Store release notes from the changelog file
### Fixed
- Downgrade AGP to 8.0.0-alpha07 to fix Baseline Profiles not being packaged
## [1.7.0] - 2022-11-19
### Changed
- Switch to Compose Material's swipe refresh feature
- Change metadata extractor logic to no longer prefer canonical URLs. The submitted URL will be retained as-is now.
- Remove navigation transition animations
## [1.6.0] - 2022-11-14
### Changed
- Migrate dependency injection from Hilt to Anvil + Whetstone
## [1.5.0] - 2022-11-10
### Changed
- Upgrade to Compose 2022.11.00 release
## [1.4.0] - 2022-11-02
### Changed
- Misc fixes to link metadata extractor
## [1.3.0] - 2022-10-24
### Changed
- Prevent errors during link metadata retrieval from crashing the app
- Update Jetpack libraries to 2022-10-24 release
## [1.2.0] - 2022-10-17
### Changed
- Lazily load link metadata to improve comment page loading speed
- Upgrade to latest Compose release
- Refactor code to align with Twitter's public Compose guidelines
## [1.1.0] - 2022-09-30
### Added
- Add score and relative time to comments
### Changed
- Update Jetpack Compose
- Declare data backup and transfer rules
- Improve legibility of links in comments and user profiles
- Commonize and improve how network errors are displayed
## [1.0.0] - 2022-09-20
- Initial Play Store release
[Unreleased]: https://github.com/msfjarvis/compose-lobsters/compare/v1.64.0...HEAD
[1.64.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.63.0...v1.64.0
[1.63.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.62.0...v1.63.0
[1.62.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.61.0...v1.62.0
[1.61.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.60.0...v1.61.0
[1.60.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.59.0...v1.60.0
[1.59.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.58.0...v1.59.0
[1.58.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.57.0...v1.58.0
[1.57.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.55.0...v1.57.0
[1.55.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.54.0...v1.55.0
[1.54.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.53.0...v1.54.0
[1.53.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.52.0...v1.53.0
[1.52.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.51.0...v1.52.0
[1.51.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.50.0...v1.51.0
[1.50.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.49.0...v1.50.0
[1.49.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.48.0...v1.49.0
[1.48.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.47.0...v1.48.0
[1.47.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.46.0...v1.47.0
[1.46.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.45.0...v1.46.0
[1.45.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.44.0...v1.45.0
[1.44.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.43.0...v1.44.0
[1.43.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.42.0...v1.43.0
[1.42.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.41.0...v1.42.0
[1.41.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.40.0...v1.41.0
[1.40.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.39.0...v1.40.0
[1.39.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.38.0...v1.39.0
[1.38.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.37.0...v1.38.0
[1.37.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.36.0...v1.37.0
[1.36.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.35.0...v1.36.0
[1.35.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.34.0...v1.35.0
[1.34.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.33.0...v1.34.0
[1.33.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.32.0...v1.33.0
[1.32.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.31.0...v1.32.0
[1.31.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.30.0...v1.31.0
[1.30.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.29.0...v1.30.0
[1.29.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.28.0...v1.29.0
[1.28.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.27.0...v1.28.0
[1.27.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.26.0...v1.27.0
[1.26.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.25.0...v1.26.0
[1.25.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.24.0...v1.25.0
[1.24.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.23.0...v1.24.0
[1.23.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.22.0...v1.23.0
[1.22.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.21.0...v1.22.0
[1.21.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.20.0...v1.21.0
[1.20.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.19.0...v1.20.0
[1.19.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.18.0...v1.19.0
[1.18.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.17.0...v1.18.0
[1.17.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.16.0...v1.17.0
[1.16.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.15.0...v1.16.0
[1.15.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.14.0...v1.15.0
[1.14.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.13.0...v1.14.0
[1.13.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.12.0...v1.13.0
[1.12.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.11.0...v1.12.0
[1.11.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.10.0...v1.11.0
[1.10.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.9.0...v1.10.0
[1.9.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.8.0...v1.9.0
[1.8.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.7.0...1.8.0
[1.7.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.6.0...v1.7.0
[1.6.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.5.0...v1.6.0
[1.5.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.4.0...v1.5.0
[1.4.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.3.0...v1.4.0
[1.3.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.2.0...v1.3.0
[1.2.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/msfjarvis/compose-lobsters/compare/29c374859b17c5fcef03585b8a01c00070de9097...v1.0.0
+17
View File
@@ -0,0 +1,17 @@
# Hacking on compose-lobsters
This app is built as a very lightweight frontend for [lobste.rs](https://lobste.rs), mainly as an experiment in using [Jetpack Compose](https://d.android.com/jetpack/compose).
The codebase is fairly simple, and the package structure follows [Buffer's](https://buffer.com/resources/android-rethinking-package-structure/).
## Subproject breakdown
- `:api`: Retrofit-backed interface to the [lobste.rs] API
- `:app`: The guts of the Android client
- `:common`: Contains the abstractions we use for sharing strings and browser handling across desktop and mobile
- `:database`: Database layer implemented using SQLDelight to persist saved posts
- `:desktop`: Proof-of-concept desktop client that needs significantly more love than it has received so far
-23
View File
@@ -1,23 +0,0 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
-3
View File
@@ -1,3 +0,0 @@
# Privacy policy for Claw
The app does not collect any information about you and thus does not share anything with me or any other third-party. The only difference between using this app versus the [lobste.rs website](https://lobste.rs/) is the `User-Agent` header which is set to `Claw/$VERSION_NAME/msfjarvis` in the app.
+30 -24
View File
@@ -1,33 +1,39 @@
# Claw for [lobste.rs](https://lobste.rs) [![CI](https://github.com/msfjarvis/compose-lobsters/actions/workflows/ci.yml/badge.svg)](https://github.com/msfjarvis/compose-lobsters/actions/workflows/ci.yml)
# Claw for [lobste.rs](https://lobste.rs)
Unofficial Android app for read-only access to [lobste.rs](https://lobste.rs), built with [Jetpack Compose](https://developer.android.com/jetpack/compose).
Unofficial Android app for read-only access to [lobste.rs](https://lobste.rs) community, built with [Jetpack Compose](https://developer.android.com/jetpack/compose).
> [!WARNING]
> This app uses a JSON API that Lobsters does not expose by choice and [the maintainer has no interest in having](https://github.com/lobsters/lobsters/issues/1663#issuecomment-3074472781), so it may break in any manner at any point in time. File an issue [here](https://github.com/msfjarvis/compose-lobsters/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen) if you come across any bugs, do not bother the upstream developers about a feature they don't support.
Requires Android Studio 2020.3.1 Arctic Fox to work.
<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>
## Distribution
<a href="https://android.izzysoft.de/repo/apk/dev.msfjarvis.claw.android">
<img src="./.github/izzy-badge.webp"
alt="Get it on IzzyOnDroid"
height="80" />
</a>
The app will be published to Google Play soon, in the mean time snapshots from the development branch are available [here](https://dl.msfjarvis.dev/Claw)
> [!NOTE]
> Daily builds are published to the Google Play 'Open Testing' channel (if there are any changes), which can be opted into via this link: [[Android]](https://play.google.com/store/apps/details?id=dev.msfjarvis.claw.android) [[Web]](https://play.google.com/apps/testing/dev.msfjarvis.claw.android)
## Licence
<img src="https://github.com/msfjarvis/compose-lobsters/blob/main/.github/readme_feature_light.webp#gh-light-mode-only"
alt="A grid of screenshots from the app, in clockwise order: the main screen, the comments page, the search page and the saved posts page"
width="550" />
```
Copyright (c) 2020 Harsh Shandilya
<img src="https://github.com/msfjarvis/compose-lobsters/blob/main/.github/readme_feature_dark.webp#gh-dark-mode-only"
alt="A grid of screenshots from the app, in clockwise order: the main screen, the comments page, the search page and the saved posts page"
width="550" />
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
## License
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
See [LICENSE](LICENSE)
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
```
-2
View File
@@ -1,2 +0,0 @@
/apk/
/bundle/
-143
View File
@@ -1,143 +0,0 @@
/*
* Copyright © Harsh Shandilya.
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/
@file:Suppress("UnstableApiUsage")
import dev.msfjarvis.claw.gradle.addTestDependencies
import dev.zacsweers.metro.gradle.DiagnosticSeverity
plugins {
id("dev.msfjarvis.claw.android-application")
id("dev.msfjarvis.claw.rename-artifacts")
id("dev.msfjarvis.claw.kotlin-android")
id("dev.msfjarvis.claw.sentry")
id("dev.msfjarvis.claw.versioning-plugin")
id("kotlin-parcelize")
alias(libs.plugins.aboutlibraries)
alias(libs.plugins.modulegraphassert)
alias(libs.plugins.baselineprofile)
alias(libs.plugins.licensee)
alias(libs.plugins.kotlin.composeCompiler)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.dependencyAnalysis)
alias(libs.plugins.metro)
}
android {
namespace = "dev.msfjarvis.claw.android"
defaultConfig.applicationId = "dev.msfjarvis.claw.android"
defaultConfig.testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
buildFeatures.compose = true
buildTypes.create("internal") {
matchingFallbacks += "release"
signingConfig = signingConfigs["debug"]
applicationIdSuffix = ".internal"
isDebuggable = true
}
}
aboutLibraries.collect.gitHubApiToken = providers.environmentVariable("GITHUB_TOKEN").orNull
baselineProfile {
mergeIntoMain = true
saveInSrc = true
}
licensee {
allow("Apache-2.0")
allow("MIT")
allow("BSD-3-Clause")
ignoreDependencies("com.michael-bull.kotlin-result") { because("kotlin-result is ISC licensed") }
ignoreDependencies("org.commonmark") { because("Commonmark is BSD licensed") }
allowUrl("https://jsoup.org/license") { because("Jsoup is MIT licensed") }
allowUrl("https://opensource.org/license/MIT") { because("Ksoup is MIT licensed") }
}
metro { unusedGraphInputsSeverity = DiagnosticSeverity.ERROR }
moduleGraphAssert {
assertOnAnyBuild = true
maxHeight = 4
restricted = arrayOf(":core -X> :.*")
}
dependencies {
baselineProfile(projects.benchmark)
implementation(platform(libs.okhttp.bom))
implementation(libs.aboutLibraries.compose.core)
implementation(libs.aboutLibraries.core)
implementation(libs.aboutLibraries.m3)
implementation(libs.androidx.activity)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.compose.animation)
implementation(libs.androidx.compose.animation.core)
implementation(libs.androidx.compose.foundation)
implementation(libs.androidx.compose.foundation.layout)
implementation(libs.androidx.compose.glance)
implementation(libs.androidx.compose.glance.core)
implementation(libs.androidx.compose.glance.m3)
implementation(libs.androidx.compose.material.icons.core)
implementation(libs.androidx.compose.material.icons.extended)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.material3.adaptive)
implementation(libs.androidx.compose.material3.adaptive.layout)
implementation(libs.androidx.compose.material3.window.size)
implementation(libs.androidx.compose.runtime)
implementation(libs.androidx.compose.runtime.saveable)
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.ui.text)
implementation(libs.androidx.compose.ui.unit)
implementation(libs.androidx.annotation)
implementation(libs.androidx.collection)
implementation(libs.androidx.core)
implementation(libs.androidx.core.splashscreen)
implementation(libs.androidx.lifecycle.common)
implementation(libs.androidx.lifecycle.compose)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.lifecycle.viewmodel)
implementation(libs.androidx.material3.navigation3)
implementation(libs.androidx.navigation3.runtime)
implementation(libs.androidx.navigation3.ui)
implementation(libs.androidx.paging.common)
implementation(libs.androidx.paging.compose)
implementation(libs.androidx.work.runtime)
implementation(libs.eithernet)
implementation(libs.haze)
implementation(libs.haze.blur)
implementation(libs.kotlin.parcelize.runtime)
implementation(libs.kotlinx.collections.immutable)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.serialization.core)
implementation(libs.kotlinx.serialization.json)
implementation(libs.kotlin.stdlib)
implementation(libs.metrox.android)
implementation(libs.metrox.viewmodel)
implementation(libs.metrox.viewmodel.compose)
implementation(libs.okhttp.core)
implementation(libs.retrofit)
implementation(libs.sentry)
implementation(libs.sentry.android.core)
implementation(libs.sqldelight.extensions.coroutines)
implementation(libs.sqldelight.runtime)
implementation(libs.swipe)
implementation(libs.unfurl)
implementation(projects.api)
implementation(projects.common)
implementation(projects.core)
implementation(projects.database.core)
implementation(projects.database.impl)
implementation(projects.model)
compileOnly(libs.androidx.compose.glance.preview)
debugRuntimeOnly(libs.androidx.compose.glance)
runtimeOnly(libs.androidx.profileinstaller)
addTestDependencies(project)
}
-125
View File
@@ -1,125 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<issues format="6" by="lint 9.2.1" type="baseline" client="gradle" dependencies="true" name="AGP (9.2.1)" variant="all" version="9.2.1">
<issue
id="Instantiatable"
message="This class should provide a default constructor (a public constructor with no arguments) (`dev.msfjarvis.claw.android.MainActivity`)"
errorLine1=" android:name=&quot;.MainActivity&quot;"
errorLine2=" ~~~~~~~~~~~~~">
<location
file="src/main/AndroidManifest.xml"
line="28"
column="21"/>
</issue>
<issue
id="DeprecatedCall"
message="android.webkit.WebView.loadUrl is deprecated; consider using an alternative."
errorLine1=" webViewRef?.loadUrl(LOGIN_URL)"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/LoginScreen.kt"
line="69"
column="15"/>
</issue>
<issue
id="DeprecatedCall"
message="android.webkit.WebView.loadUrl is deprecated; consider using an alternative."
errorLine1=" webViewRef?.loadUrl(LOGIN_URL)"
errorLine2=" ~~~~~~~~~">
<location
file="src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/LoginScreen.kt"
line="69"
column="35"/>
</issue>
<issue
id="DeprecatedCall"
message="android.webkit.WebView.WebView is deprecated; consider using an alternative."
errorLine1=" WebView(context)"
errorLine2=" ~~~~~~~~~~~~~~~~">
<location
file="src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/LoginScreen.kt"
line="78"
column="13"/>
</issue>
<issue
id="DeprecatedCall"
message="android.webkit.WebView.WebView is deprecated; consider using an alternative."
errorLine1=" WebView(context)"
errorLine2=" ~~~~~~~">
<location
file="src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/LoginScreen.kt"
line="78"
column="21"/>
</issue>
<issue
id="DeprecatedCall"
message="android.webkit.WebView.getSettings is deprecated; consider using an alternative."
errorLine1=" settings.javaScriptEnabled = true"
errorLine2=" ~~~~~~~~">
<location
file="src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/LoginScreen.kt"
line="81"
column="17"/>
</issue>
<issue
id="DeprecatedCall"
message="android.webkit.WebView.setWebViewClient is deprecated; consider using an alternative."
errorLine1=" webViewClient ="
errorLine2=" ^">
<location
file="src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/LoginScreen.kt"
line="82"
column="17"/>
</issue>
<issue
id="DeprecatedCall"
message="android.webkit.WebView.setWebViewClient is deprecated; consider using an alternative."
errorLine1=" object : WebViewClient() {"
errorLine2=" ^">
<location
file="src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/LoginScreen.kt"
line="83"
column="19"/>
</issue>
<issue
id="DeprecatedCall"
message="android.webkit.WebView.loadUrl is deprecated; consider using an alternative."
errorLine1=" loadUrl(LOGIN_URL)"
errorLine2=" ~~~~~~~~~~~~~~~~~~">
<location
file="src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/LoginScreen.kt"
line="118"
column="17"/>
</issue>
<issue
id="DeprecatedCall"
message="android.webkit.WebView.loadUrl is deprecated; consider using an alternative."
errorLine1=" loadUrl(LOGIN_URL)"
errorLine2=" ~~~~~~~~~">
<location
file="src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/LoginScreen.kt"
line="118"
column="25"/>
</issue>
<issue
id="MemberExtensionConflict"
message="`KeepUntilTransitionsFinished` is defined both as a member in class `androidx.compose.animation.ExitTransition.Companion` and an extension in package `androidx.compose.animation`. The defined behavior for this is to use the member, but since the extension is explicitly imported into this file, there&apos;s a chance that this was not expected. (One common way this happens is for members to be added to a class after code was already written to use an extension)."
errorLine1=" ) togetherWith ExitTransition.KeepUntilTransitionsFinished"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src/main/kotlin/dev/msfjarvis/claw/android/ui/screens/LobstersPostsScreen.kt"
line="259"
column="49"/>
</issue>
</issues>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

-19
View File
@@ -1,19 +0,0 @@
-dontwarn androidx.window.extensions.WindowExtensions
-dontwarn androidx.window.extensions.WindowExtensionsProvider
-dontwarn androidx.window.extensions.area.ExtensionWindowAreaPresentation
-dontwarn androidx.window.extensions.core.util.function.Consumer
-dontwarn androidx.window.extensions.core.util.function.Function
-dontwarn androidx.window.extensions.core.util.function.Predicate
-dontwarn androidx.window.extensions.layout.DisplayFeature
-dontwarn androidx.window.extensions.layout.FoldingFeature
-dontwarn androidx.window.extensions.layout.WindowLayoutComponent
-dontwarn androidx.window.extensions.layout.WindowLayoutInfo
-dontwarn androidx.window.sidecar.SidecarDeviceState
-dontwarn androidx.window.sidecar.SidecarDisplayFeature
-dontwarn androidx.window.sidecar.SidecarInterface$SidecarCallback
-dontwarn androidx.window.sidecar.SidecarInterface
-dontwarn androidx.window.sidecar.SidecarProvider
-dontwarn androidx.window.sidecar.SidecarWindowLayoutInfo
-dontwarn io.mcarle.konvert.api.GeneratedKonverter
-dontwarn com.google.re2j.Matcher
-dontwarn com.google.re2j.Pattern
-2
View File
@@ -1,2 +0,0 @@
-dontobfuscate
-keepattributes SourceFile, LineNumberTable
@@ -1,13 +0,0 @@
<!--
~ Copyright © 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.
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Performing the heap growth analysis in process requires more heap. -->
<application
android:largeHeap="true"/>
</manifest>
-46
View File
@@ -1,46 +0,0 @@
<!--
~ Copyright © 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="claw-debug"
android:host="comments" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="claw-debug"
android:host="hottest" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="claw-debug"
android:host="newest" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="claw-debug"
android:host="saved" />
</intent-filter>
</activity>
</application>
</manifest>
-9
View File
@@ -1,9 +0,0 @@
<!--
~ Copyright © 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.
-->
<resources>
<string name="app_name">Claw β</string>
</resources>
@@ -1,9 +0,0 @@
<!--
~ Copyright © 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.
-->
<resources>
<string name="app_name">Claw 🤫</string>
</resources>
-122
View File
@@ -1,122 +0,0 @@
<!--
~ Copyright © 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".ClawApplication"
android:appComponentFactory="dev.msfjarvis.claw.android.BackupSafeAppComponentFactory"
android:dataExtractionRules="@xml/data_extraction_rules"
android:enableOnBackInvokedCallback="true"
android:fullBackupContent="@xml/full_backup_content"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/Theme.Claw.SplashScreen"
tools:replace="android:appComponentFactory"
tools:targetApi="tiramisu">
<profileable
android:shell="true"
tools:targetApi="29" />
<activity
android:name=".MainActivity"
android:configChanges="assetsPaths|colorMode|density|fontScale|fontWeightAdjustment|grammaticalGender|keyboard|keyboardHidden|layoutDirection|locale|mcc|mnc|navigation|orientation|resourcesUnused|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode"
android:launchMode="singleTask"
android:taskAffinity=""
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="claw" android:host="comments" android:pathPrefix="/" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="claw" android:host="hottest" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="claw" android:host="newest" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="claw" android:host="saved" />
</intent-filter>
</activity>
<activity
android:name=".glance.WidgetClickActivity"
android:exported="false"
android:theme="@android:style/Theme.NoDisplay"
android:excludeFromRecents="true"
android:taskAffinity="" />
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
<receiver
android:name=".glance.SavedPostsWidgetReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/saved_posts_widget_info" />
</receiver>
<receiver
android:name=".glance.HottestPostsWidgetReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/hottest_posts_widget_info" />
</receiver>
<!-- Required: set your sentry.io project identifier (DSN) -->
<meta-data
android:name="io.sentry.dsn"
android:value="${sentryDsn}" />
<!-- enable automatic breadcrumbs for user interactions (clicks, swipes, scrolls) -->
<meta-data
android:name="io.sentry.traces.user-interaction.enable"
android:value="true" />
<meta-data
android:name="io.sentry.breadcrumbs.user-interaction"
android:value="true" />
<!-- enable screenshot for crashes -->
<meta-data
android:name="io.sentry.attach-screenshot"
android:value="true" />
<!-- enable view hierarchy for crashes -->
<meta-data
android:name="io.sentry.attach-view-hierarchy"
android:value="true" />
<!-- set the environment for this build -->
<meta-data android:name="io.sentry.environment" android:value="${sentryEnvironment}" />
<meta-data android:name="io.sentry.auto-init" android:value="false" />
</application>
</manifest>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

@@ -1,35 +0,0 @@
/*
* Copyright © 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
import android.app.Application
import dev.zacsweers.metrox.android.MetroAppComponentFactory
/**
* Custom variant of [android.app.AppComponentFactory] that skips [ClawApplication] init when the
* app is running in a backup process for Android Auto Backup. In all normal cases, it will defer to
* [MetroAppComponentFactory].
*/
@Suppress("unused")
class BackupSafeAppComponentFactory : MetroAppComponentFactory() {
override fun instantiateApplicationCompat(cl: ClassLoader, className: String): Application {
if (isBackupAgentProcess()) {
return Application()
}
return super.instantiateApplicationCompat(cl, className)
}
private fun isBackupAgentProcess(): Boolean {
return try {
val processName = Application.getProcessName()
processName?.contains("backup") == true
} catch (_: Exception) {
false
}
}
}
@@ -1,68 +0,0 @@
/*
* Copyright © 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
import android.app.Application
import android.os.Build
import androidx.glance.appwidget.GlanceAppWidgetManager
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequestBuilder
import dev.msfjarvis.claw.android.glance.SavedPostsWidgetReceiver
import dev.msfjarvis.claw.android.injection.AppGraph
import dev.msfjarvis.claw.android.work.SavedPostUpdaterWorker
import dev.msfjarvis.claw.android.work.TagExpirationCleanupWorker
import dev.zacsweers.metro.createGraphFactory
import dev.zacsweers.metrox.android.MetroAppComponentProviders
import dev.zacsweers.metrox.android.MetroApplication
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class ClawApplication : Application(), MetroApplication {
val appGraph by lazy { createGraphFactory<AppGraph.Factory>().create(this) }
override val appComponentProviders: MetroAppComponentProviders
get() = appGraph
@OptIn(DelicateCoroutinesApi::class)
override fun onCreate() {
super.onCreate()
appGraph.plugins.forEach { plugin -> plugin.apply(this) }
val postUpdateWorkRequest =
PeriodicWorkRequestBuilder<SavedPostUpdaterWorker>(24, TimeUnit.HOURS)
.setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build())
.build()
appGraph.workManager.enqueueUniquePeriodicWork(
uniqueWorkName = "updateSavedPosts",
existingPeriodicWorkPolicy = ExistingPeriodicWorkPolicy.KEEP,
request = postUpdateWorkRequest,
)
val tagCleanupWorkRequest =
PeriodicWorkRequestBuilder<TagExpirationCleanupWorker>(24, TimeUnit.HOURS).build()
appGraph.workManager.enqueueUniquePeriodicWork(
uniqueWorkName = "cleanupExpiredTags",
existingPeriodicWorkPolicy = ExistingPeriodicWorkPolicy.KEEP,
request = tagCleanupWorkRequest,
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
GlobalScope.launch(appGraph.mainDispatcher) {
try {
GlanceAppWidgetManager(applicationContext)
.setWidgetPreviews(SavedPostsWidgetReceiver::class)
} catch (_: IllegalArgumentException) {
// Workaround for Motorola Android 15 bug where AppWidgetServiceImpl incorrectly
// reports registered widgets as invalid when setting previews.
// See: https://claw.sentry.io/share/issue/f8ee09821d8840b9b86293d3ffb627d3/
}
}
}
}
}
@@ -1,121 +0,0 @@
/*
* Copyright © 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
import android.app.Activity
import android.app.assist.AssistContent
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.platform.UriHandler
import androidx.core.net.toUri
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.ViewModelProvider
import androidx.navigation3.runtime.NavKey
import dev.msfjarvis.claw.android.ui.navigation.Comments
import dev.msfjarvis.claw.android.ui.navigation.Hottest
import dev.msfjarvis.claw.android.ui.navigation.Newest
import dev.msfjarvis.claw.android.ui.navigation.Saved
import dev.msfjarvis.claw.android.ui.screens.LobstersPostsScreen
import dev.msfjarvis.claw.common.theme.LobstersTheme
import dev.msfjarvis.claw.core.injection.InjectedViewModelFactory
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesIntoMap
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.binding
import dev.zacsweers.metrox.android.ActivityKey
import dev.zacsweers.metrox.viewmodel.LocalMetroViewModelFactory
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
@ContributesIntoMap(AppScope::class, binding<Activity>())
@ActivityKey
@Inject
class MainActivity(
private val uriHandler: UriHandler,
private val viewModelFactory: InjectedViewModelFactory,
) : ComponentActivity() {
override val defaultViewModelProviderFactory: ViewModelProvider.Factory
get() = viewModelFactory
var webUri: String? = null
var deepLinkDestination by mutableStateOf<NavKey?>(null)
private set
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
installSplashScreen()
enableEdgeToEdge(
statusBarStyle = SystemBarStyle.light(Color.TRANSPARENT, Color.TRANSPARENT)
// Don't set navigation bar style, the default matches the platform behavior.
)
handleIntent(intent)
setContent {
LobstersTheme(
dynamicColor = true,
providedValues =
arrayOf(
LocalUriHandler provides uriHandler,
LocalMetroViewModelFactory provides viewModelFactory,
),
) {
val windowSizeClass = calculateWindowSizeClass(this)
LobstersPostsScreen(
uriHandler = uriHandler,
windowSizeClass = windowSizeClass,
setWebUri = { url -> webUri = url },
deepLinkDestination = deepLinkDestination,
clearDeepLink = { deepLinkDestination = null },
)
}
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleIntent(intent)
}
private fun handleIntent(intent: Intent) {
val data = intent.data
if (data != null && data.scheme == BuildConfig.DEEPLINK_SCHEME) {
when (data.host) {
"comments" -> {
val postId = data.pathSegments.firstOrNull()
if (postId != null) {
deepLinkDestination = Comments(postId)
}
}
"newest" -> deepLinkDestination = Newest
"hottest" -> deepLinkDestination = Hottest
"saved" -> deepLinkDestination = Saved
}
}
}
override fun onProvideAssistContent(outContent: AssistContent?) {
super.onProvideAssistContent(outContent)
val uri = webUri
if (outContent != null) {
if (uri != null) {
outContent.webUri = uri.toUri()
} else {
outContent.webUri = null
}
}
}
}
@@ -1,157 +0,0 @@
/*
* Copyright © 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.glance
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.LocalContext
import androidx.glance.action.clickable
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.action.actionStartActivity
import androidx.glance.appwidget.cornerRadius
import androidx.glance.appwidget.lazy.items
import androidx.glance.appwidget.provideContent
import androidx.glance.background
import androidx.glance.layout.Alignment
import androidx.glance.layout.Box
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
import androidx.glance.text.Text
import androidx.glance.text.TextAlign
import androidx.glance.text.TextStyle
import com.slack.eithernet.ApiResult
import dev.msfjarvis.claw.android.BuildConfig
import dev.msfjarvis.claw.android.ClawApplication
import dev.msfjarvis.claw.android.MainActivity
import dev.msfjarvis.claw.android.R
import dev.msfjarvis.claw.database.local.CachedRemotePost
import dev.msfjarvis.claw.model.LobstersPost
import dev.msfjarvis.claw.model.UIPost
import dev.msfjarvis.claw.model.fromCachedRemotePost
import dev.msfjarvis.claw.model.toUIPost
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.first
class HottestPostsWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {
val appGraph = (context.applicationContext as ClawApplication).appGraph
val cachedRemotePostsRepository = appGraph.cachedRemotePostsRepository
val tagBlockRepository = appGraph.tagBlockRepository
val filteredTags =
try {
tagBlockRepository.getSavedTags().first()
} catch (_: Exception) {
emptySet()
}
val posts =
when (val postsResult = appGraph.lobstersApi.getHottestPosts(1)) {
is ApiResult.Success -> {
val uiPosts = postsResult.value.posts.map(LobstersPost::toUIPost)
// Cache the posts for future use when network is unavailable
// Using try-catch to prevent widget rendering failures if caching fails
try {
val cachedPosts = uiPosts.mapIndexed { index, post ->
CachedRemotePost(
shortId = post.shortId,
title = post.title,
url = post.url,
createdAt = post.createdAt,
commentCount = post.commentCount,
commentsUrl = post.commentsUrl,
submitterName = post.submitter,
tags = post.tags,
description = post.description,
userIsAuthor = post.userIsAuthor,
insertionOrder = index,
)
}
cachedRemotePostsRepository.savePosts(cachedPosts)
} catch (_: Exception) {
// Silently ignore caching failures - widget should still render
}
uiPosts.toImmutableList()
}
else -> {
// Fall back to cached posts when network fails
cachedRemotePostsRepository
.getCachedPosts()
.map(UIPost::fromCachedRemotePost)
.toImmutableList()
}
}
// Filter out posts with tags that are in the filtered tags set
val filteredPosts =
if (filteredTags.isEmpty()) {
posts
} else {
posts
.filter { post -> post.tags.none { tag -> filteredTags.contains(tag) } }
.toImmutableList()
}
provideContent { LobstersGlanceTheme { Content(context, filteredPosts) } }
}
override suspend fun providePreview(context: Context, widgetCategory: Int) {
provideContent { Content(context, samplePosts()) }
}
@SuppressLint("ComposeUnstableReceiver")
@Composable
private fun Content(
context: Context,
posts: ImmutableList<UIPost>,
modifier: GlanceModifier = GlanceModifier,
) {
WidgetContainer(
"Hottest posts",
listContent = {
items(posts) { post ->
Box(GlanceModifier.padding(horizontal = 16.dp, vertical = 4.dp)) { WidgetPostEntry(post) }
}
item {
Box(
contentAlignment = Alignment.Center,
modifier = GlanceModifier.fillMaxWidth().padding(16.dp),
) {
Box(
contentAlignment = Alignment.Center,
modifier =
GlanceModifier.background(GlanceTheme.colors.primary)
.cornerRadius(20.dp)
.padding(horizontal = 24.dp, vertical = 10.dp)
.clickable(
actionStartActivity(
Intent(Intent.ACTION_VIEW, "${BuildConfig.DEEPLINK_SCHEME}://hottest".toUri())
.apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
setClass(LocalContext.current, MainActivity::class.java)
}
)
),
) {
Text(
text = context.getString(R.string.see_more_posts),
style =
TextStyle(color = GlanceTheme.colors.onPrimary, textAlign = TextAlign.Center),
)
}
}
}
},
modifier = modifier,
)
}
}
@@ -1,31 +0,0 @@
/*
* Copyright © 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.glance
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesIntoMap
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.binding
import dev.zacsweers.metrox.android.BroadcastReceiverKey
@ContributesIntoMap(AppScope::class, binding<BroadcastReceiver>())
@BroadcastReceiverKey
@Inject
class HottestPostsWidgetReceiver() : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget
get() = HottestPostsWidget()
override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
}
}
@@ -1,111 +0,0 @@
/*
* Copyright © 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.glance
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.glance.GlanceComposable
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.LocalContext
import androidx.glance.action.clickable
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.action.actionStartActivity
import androidx.glance.appwidget.cornerRadius
import androidx.glance.appwidget.lazy.items
import androidx.glance.appwidget.provideContent
import androidx.glance.background
import androidx.glance.layout.Alignment
import androidx.glance.layout.Box
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
import androidx.glance.preview.ExperimentalGlancePreviewApi
import androidx.glance.preview.Preview
import androidx.glance.text.Text
import androidx.glance.text.TextAlign
import androidx.glance.text.TextStyle
import dev.msfjarvis.claw.android.BuildConfig
import dev.msfjarvis.claw.android.ClawApplication
import dev.msfjarvis.claw.android.MainActivity
import dev.msfjarvis.claw.android.R
import dev.msfjarvis.claw.model.UIPost
import dev.msfjarvis.claw.model.fromSavedPost
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.first
class SavedPostsWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {
val appGraph = (context.applicationContext as ClawApplication).appGraph
val posts = appGraph.savedPostsRepository.getRecentPosts(50)
val postWindow = posts.first().map(UIPost::fromSavedPost).toImmutableList()
provideContent { LobstersGlanceTheme { Content(postWindow) } }
}
override suspend fun providePreview(context: Context, widgetCategory: Int) {
provideContent { Content(samplePosts()) }
}
@SuppressLint("ComposeUnstableReceiver")
@Composable
private fun Content(posts: ImmutableList<UIPost>, modifier: GlanceModifier = GlanceModifier) {
val context = LocalContext.current
WidgetContainer(
title = context.getString(R.string.saved_posts),
listContent = {
items(posts) { post ->
Box(GlanceModifier.padding(horizontal = 16.dp, vertical = 4.dp)) { WidgetPostEntry(post) }
}
item {
Box(
contentAlignment = Alignment.Center,
modifier = GlanceModifier.fillMaxWidth().padding(16.dp),
) {
Box(
contentAlignment = Alignment.Center,
modifier =
GlanceModifier.background(GlanceTheme.colors.primary)
.cornerRadius(20.dp)
.padding(horizontal = 24.dp, vertical = 10.dp)
.clickable(
actionStartActivity(
Intent(Intent.ACTION_VIEW, "${BuildConfig.DEEPLINK_SCHEME}://saved".toUri())
.apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
setClass(LocalContext.current, MainActivity::class.java)
}
)
),
) {
Text(
text = context.getString(R.string.see_more_posts),
style =
TextStyle(color = GlanceTheme.colors.onPrimary, textAlign = TextAlign.Center),
)
}
}
}
},
modifier = modifier,
)
}
@SuppressLint("ComposeUnstableReceiver")
@OptIn(ExperimentalGlancePreviewApi::class)
@Preview
@GlanceComposable
@Composable
private fun Preview() {
Content(samplePosts())
}
}
@@ -1,31 +0,0 @@
/*
* Copyright © 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.glance
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesIntoMap
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.binding
import dev.zacsweers.metrox.android.BroadcastReceiverKey
@ContributesIntoMap(AppScope::class, binding<BroadcastReceiver>())
@BroadcastReceiverKey
@Inject
class SavedPostsWidgetReceiver() : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget
get() = SavedPostsWidget()
override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
}
}
@@ -1,96 +0,0 @@
/*
* Copyright © 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.glance
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.core.net.toUri
import dev.msfjarvis.claw.android.BuildConfig
import dev.msfjarvis.claw.android.MainActivity
import io.sentry.Breadcrumb
import io.sentry.Sentry
import io.sentry.SentryLevel
/**
* Trampoline activity for handling widget clicks with explicit components.
*
* This activity is required to fix COMPOSE-LOBSTERS-9X/9P/8M. Glance's LazyColumn requires all
* actionStartActivity intents to have an explicit component set. For external URLs, we route
* through this activity which then launches the appropriate intent (either internal deep link or
* external browser).
*
* @see:
* https://developer.android.com/reference/kotlin/androidx/glance/appwidget/action/package-summary#actionStartActivity(android.content.Intent,androidx.glance.action.ActionParameters)
*/
class WidgetClickActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val url = intent.getStringExtra(EXTRA_URL)
val shortId = intent.getStringExtra(EXTRA_SHORT_ID)
val action = intent.action
when {
// Handle comments deep link
action == ACTION_OPEN_COMMENTS && shortId != null -> {
Sentry.addBreadcrumb(
Breadcrumb().apply {
category = "widget_click_debug"
message = "Comment deeplink"
level = SentryLevel.INFO
}
)
val commentsIntent =
Intent(Intent.ACTION_VIEW, "${BuildConfig.DEEPLINK_SCHEME}://comments/$shortId".toUri())
.apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
setClass(this@WidgetClickActivity, MainActivity::class.java)
}
startActivity(commentsIntent)
}
// Handle external URL
url != null && (url.startsWith("http://") || url.startsWith("https://")) -> {
Sentry.addBreadcrumb(
Breadcrumb().apply {
category = "widget_click_debug"
message = "External URL"
level = SentryLevel.INFO
}
)
val urlIntent = Intent(Intent.ACTION_VIEW, url.toUri())
startActivity(urlIntent)
}
// Handle internal/relative URL as comments link
url != null && (url.startsWith('/') || url.isEmpty()) -> {
Sentry.addBreadcrumb(
Breadcrumb().apply {
category = "widget_click_debug"
message = "Relative URL, parsed as a comment link"
level = SentryLevel.INFO
}
)
// For internal URLs without shortId, extract it from the URL or use comments action
val commentsIntent =
Intent(Intent.ACTION_VIEW, "${BuildConfig.DEEPLINK_SCHEME}://comments/$shortId".toUri())
.apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
setClass(this@WidgetClickActivity, MainActivity::class.java)
}
startActivity(commentsIntent)
}
}
finish()
}
companion object {
const val ACTION_OPEN_COMMENTS = "dev.msfjarvis.claw.OPEN_COMMENTS"
const val EXTRA_URL = "dev.msfjarvis.claw.EXTRA_URL"
const val EXTRA_SHORT_ID = "dev.msfjarvis.claw.EXTRA_SHORT_ID"
}
}
@@ -1,97 +0,0 @@
/*
* Copyright © 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.glance
import android.annotation.SuppressLint
import android.content.Intent
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import androidx.glance.ColorFilter
import androidx.glance.GlanceComposable
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.Image
import androidx.glance.ImageProvider
import androidx.glance.LocalContext
import androidx.glance.action.clickable
import androidx.glance.appwidget.action.actionStartActivity
import androidx.glance.appwidget.cornerRadius
import androidx.glance.background
import androidx.glance.layout.Alignment
import androidx.glance.layout.Row
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
import androidx.glance.preview.ExperimentalGlancePreviewApi
import androidx.glance.preview.Preview
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
import dev.msfjarvis.claw.android.R
import dev.msfjarvis.claw.model.UIPost
@SuppressLint("ArgInFormattedQuantityStringRes")
@Composable
@GlanceComposable
fun WidgetPostEntry(post: UIPost, modifier: GlanceModifier = GlanceModifier) {
val titleStyle = MaterialTheme.typography.bodyMedium
val context = LocalContext.current
val commentsAction =
actionStartActivity(
Intent(context, WidgetClickActivity::class.java).apply {
action = WidgetClickActivity.ACTION_OPEN_COMMENTS
putExtra(WidgetClickActivity.EXTRA_SHORT_ID, post.shortId)
}
)
val postAction =
if (post.url.startsWith('/') || post.url.isEmpty()) commentsAction
else
actionStartActivity(
Intent(context, WidgetClickActivity::class.java).apply {
putExtra(WidgetClickActivity.EXTRA_URL, post.url)
putExtra(WidgetClickActivity.EXTRA_SHORT_ID, post.shortId)
}
)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier =
modifier.fillMaxWidth().cornerRadius(8.dp).background(GlanceTheme.colors.secondaryContainer),
) {
Text(
text = post.title,
modifier =
GlanceModifier.defaultWeight()
.clickable(postAction)
.padding(horizontal = 8.dp, vertical = 4.dp)
.padding(start = 4.dp),
maxLines = 1,
style =
TextStyle(color = GlanceTheme.colors.onSecondaryContainer, fontSize = titleStyle.fontSize),
)
Image(
provider = ImageProvider(R.drawable.ic_comment),
contentDescription =
context.resources.getQuantityString(
R.plurals.comments,
post.commentCount,
post.commentCount,
),
colorFilter = ColorFilter.tint(GlanceTheme.colors.onSecondaryContainer),
modifier =
GlanceModifier.clickable(commentsAction)
.padding(horizontal = 8.dp, vertical = 4.dp)
.padding(horizontal = 4.dp),
)
}
}
@OptIn(ExperimentalGlancePreviewApi::class)
@Preview
@GlanceComposable
@Composable
private fun WidgetPostEntryPreview() {
WidgetPostEntry(samplePosts(1).first())
}
@@ -1,72 +0,0 @@
/*
* Copyright © 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.glance
import android.annotation.SuppressLint
import android.os.Build
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.glance.GlanceComposable
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.appwidget.cornerRadius
import androidx.glance.appwidget.lazy.LazyColumn
import androidx.glance.appwidget.lazy.LazyListScope
import androidx.glance.background
import androidx.glance.layout.Alignment
import androidx.glance.layout.Column
import androidx.glance.layout.Row
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
import androidx.glance.material3.ColorProviders
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
import dev.msfjarvis.claw.common.posts.TEST_POST
import dev.msfjarvis.claw.common.theme.DarkThemeColors
import dev.msfjarvis.claw.common.theme.LightThemeColors
import dev.msfjarvis.claw.model.UIPost
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.toPersistentList
@Composable
fun LobstersGlanceTheme(content: @GlanceComposable @Composable () -> Unit) {
GlanceTheme(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) GlanceTheme.colors
else ColorProviders(light = LightThemeColors, dark = DarkThemeColors),
content,
)
}
@Composable
fun WidgetContainer(
title: String,
listContent: LazyListScope.() -> Unit,
modifier: GlanceModifier = GlanceModifier,
) {
Column(
modifier.fillMaxSize().background(GlanceTheme.colors.widgetBackground).cornerRadius(16.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = GlanceModifier.padding(16.dp).fillMaxWidth(),
) {
Text(text = title, style = TextStyle(fontSize = 18.sp, color = GlanceTheme.colors.onSurface))
}
LazyColumn(content = listContent)
}
}
@SuppressLint("VisibleForTests")
fun samplePosts(
count: Int = 5,
mutator: UIPost.(idx: Int) -> UIPost = { this },
): PersistentList<UIPost> {
return buildList(count) { repeat(count) { add(TEST_POST.mutator(it)) } }.toPersistentList()
}
@@ -1,61 +0,0 @@
/*
* Copyright © 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.content.Context
import androidx.work.ListenableWorker
import androidx.work.WorkManager
import dev.msfjarvis.claw.android.viewmodel.CachedRemotePostsRepository
import dev.msfjarvis.claw.android.viewmodel.SavedPostsRepository
import dev.msfjarvis.claw.api.LobstersApi
import dev.msfjarvis.claw.common.tags.TagBlockRepository
import dev.msfjarvis.claw.core.coroutines.MainDispatcher
import dev.msfjarvis.claw.core.injection.AppPlugin
import dev.msfjarvis.claw.core.injection.InjectedWorkerFactory
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.DependencyGraph
import dev.zacsweers.metro.Multibinds
import dev.zacsweers.metro.Provides
import dev.zacsweers.metro.SingleIn
import dev.zacsweers.metrox.android.MetroAppComponentProviders
import dev.zacsweers.metrox.viewmodel.ViewModelGraph
import kotlin.reflect.KClass
import kotlinx.coroutines.CoroutineDispatcher
@DependencyGraph(AppScope::class)
interface AppGraph : MetroAppComponentProviders, ViewModelGraph {
val plugins: Set<AppPlugin>
val workManager: WorkManager
@Provides
@SingleIn(AppScope::class)
fun providesWorkManager(application: Context): WorkManager {
return WorkManager.getInstance(application)
}
@Multibinds
val workerProviders:
Map<KClass<out ListenableWorker>, () -> InjectedWorkerFactory.WorkerInstanceFactory<*>>
val workerFactory: InjectedWorkerFactory
val savedPostsRepository: SavedPostsRepository
val cachedRemotePostsRepository: CachedRemotePostsRepository
val lobstersApi: LobstersApi
val tagBlockRepository: TagBlockRepository
@MainDispatcher val mainDispatcher: CoroutineDispatcher
@DependencyGraph.Factory
fun interface Factory {
fun create(@Provides context: Context): AppGraph
}
}
@@ -1,38 +0,0 @@
/*
* Copyright © 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.util.Log
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.BindingContainer
import dev.zacsweers.metro.ContributesTo
import dev.zacsweers.metro.Provides
import me.saket.unfurl.UnfurlLogger
import me.saket.unfurl.Unfurler
import okhttp3.OkHttpClient
@BindingContainer
@ContributesTo(AppScope::class)
object MetadataExtractorModule {
@Provides
fun provideUnfurlLogger(): UnfurlLogger {
return object : UnfurlLogger {
override fun log(message: String) {
Log.d("Unfurler", message)
}
override fun log(e: Throwable, message: String) {
Log.e("Unfurler", message, e)
}
}
}
@Provides
fun provideUnfurler(okHttpClient: OkHttpClient, logger: UnfurlLogger): Unfurler {
return Unfurler(httpClient = okHttpClient, logger = logger)
}
}
@@ -1,37 +0,0 @@
/*
* Copyright © 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 dev.msfjarvis.claw.api.LobstersApi
import dev.msfjarvis.claw.core.injection.AppPlugin
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesIntoSet
import dev.zacsweers.metro.Inject
import io.sentry.SentryOptions
import io.sentry.TypeCheckHint.OKHTTP_REQUEST
import io.sentry.android.core.SentryAndroid
import okhttp3.Request
@ContributesIntoSet(AppScope::class)
@Inject
class SentryConfigurationPlugin : AppPlugin {
override fun apply(application: Application) {
SentryAndroid.init(application) { options ->
options.beforeSend = SentryOptions.BeforeSendCallback { event, hint ->
val request = hint.getAs(OKHTTP_REQUEST, Request::class.java)
// Drop all OkHttp errors that are not about Lobsters specifically.
if (request != null && !LobstersApi.BASE_URL.contains(request.url.host)) {
null
} else {
event
}
}
}
}
}
@@ -1,31 +0,0 @@
/*
* Copyright © 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 dev.msfjarvis.claw.core.injection.AppPlugin
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesIntoSet
import dev.zacsweers.metro.Inject
@ContributesIntoSet(AppScope::class)
@Inject
class WorkManagerPlugin(private val workerFactory: WorkerFactory) : AppPlugin {
override fun apply(application: Application) {
WorkManager.initialize(
application,
Configuration.Builder()
.setWorkerFactory(workerFactory)
.setMinimumLoggingLevel(Log.DEBUG)
.build(),
)
}
}
@@ -1,62 +0,0 @@
/*
* Copyright © 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.paging
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.slack.eithernet.ApiResult.Failure
import com.slack.eithernet.ApiResult.Success
import dev.msfjarvis.claw.api.toError
import dev.msfjarvis.claw.core.coroutines.IODispatcher
import dev.msfjarvis.claw.model.LobstersPost
import dev.msfjarvis.claw.model.UIPost
import dev.msfjarvis.claw.model.toUIPost
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.AssistedInject
import java.io.IOException
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
@AssistedInject
class LobstersPagingSource(
@Assisted private val remoteFetcher: RemoteFetcher<LobstersPost>,
@param:IODispatcher private val ioDispatcher: CoroutineDispatcher,
) : PagingSource<Int, UIPost>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, UIPost> {
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.map(LobstersPost::toUIPost),
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)
is Failure.HttpFailure -> LoadResult.Error(result.toError())
is Failure.ApiFailure -> LoadResult.Error(IOException("API returned an invalid response"))
}
}
override fun getRefreshKey(state: PagingState<Int, UIPost>): 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,14 +0,0 @@
/*
* Copyright © 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.paging
import com.slack.eithernet.ApiResult
/** SAM interface to abstract over a remote API that fetches paginated content. */
fun interface RemoteFetcher<T> {
suspend fun getItemsAtPage(page: Int): ApiResult<List<T>, Unit>
}
@@ -1,77 +0,0 @@
/*
* Copyright © 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.paging
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.slack.eithernet.ApiResult
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.api.LobstersSearchApi
import dev.msfjarvis.claw.api.toError
import dev.msfjarvis.claw.core.coroutines.IODispatcher
import dev.msfjarvis.claw.model.LobstersPost
import dev.msfjarvis.claw.model.UIPost
import dev.msfjarvis.claw.model.toUIPost
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.AssistedInject
import java.io.IOException
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
/**
* Specialized variant of [LobstersPagingSource] to prevent hammering the Lobsters search endpoint
* with unnecessary requests. Rather than abstract out the API call this paging source takes in the
* relevant parameters to short-circuit when there is no query specified in order to avoid calling
* the API at all.
*/
@AssistedInject
class SearchPagingSource(
private val searchApi: LobstersSearchApi,
@Assisted private val queryProvider: () -> String,
@param:IODispatcher private val ioDispatcher: CoroutineDispatcher,
) : PagingSource<Int, UIPost>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, UIPost> {
val searchQuery = queryProvider()
// If there is no query, we don't need to call the API at all.
if (searchQuery.isEmpty()) {
return LoadResult.Page(itemsBefore = 0, data = emptyList(), prevKey = null, nextKey = null)
}
val page = params.key ?: STARTING_PAGE_INDEX
val result = withContext(ioDispatcher) { searchApi.searchPosts(searchQuery, page) }
return when (result) {
is ApiResult.Success -> {
// Optimization: prevent fetching more pages if we found no items, this means
// there is no active search query.
val nextKey = if (result.value.isEmpty()) null else page + 1
LoadResult.Page(
itemsBefore = (page - 1) * PAGE_SIZE,
data = result.value.map(LobstersPost::toUIPost),
prevKey = if (page == STARTING_PAGE_INDEX) null else page - 1,
nextKey = nextKey,
)
}
is ApiResult.Failure.NetworkFailure -> LoadResult.Error(result.error)
is ApiResult.Failure.UnknownFailure -> LoadResult.Error(result.error)
is ApiResult.Failure.HttpFailure -> LoadResult.Error(result.toError())
is ApiResult.Failure.ApiFailure ->
LoadResult.Error(IOException("API returned an invalid response"))
}
}
override fun getRefreshKey(state: PagingState<Int, UIPost>): Int? {
return state.anchorPosition?.let { anchorPosition ->
(anchorPosition / PAGE_SIZE).coerceAtLeast(STARTING_PAGE_INDEX)
}
}
@AssistedFactory
interface Factory {
fun create(queryProvider: () -> String): SearchPagingSource
}
}
@@ -1,81 +0,0 @@
/*
* Copyright © 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.decorations
import android.app.Activity
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Tune
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.navigation3.runtime.NavKey
import dev.msfjarvis.claw.android.R
import dev.msfjarvis.claw.android.ui.navigation.Search
import dev.msfjarvis.claw.android.ui.navigation.Settings
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ClawAppBar(
activity: Activity?,
isTopLevel: Boolean,
navigateTo: (NavKey) -> Unit,
popBackStack: () -> NavKey?,
modifier: Modifier = Modifier,
) {
TopAppBar(
modifier = modifier.shadow(8.dp),
navigationIcon = {
if (!isTopLevel) {
IconButton(onClick = { if (popBackStack() == null) activity?.finish() }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = stringResource(R.string.go_back_to_previous_screen),
)
}
} else {
Icon(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = stringResource(R.string.the_app_icon_for_claw),
modifier = Modifier.size(48.dp),
)
}
},
title = {
if (isTopLevel) {
Text(text = stringResource(R.string.app_name), fontWeight = FontWeight.Bold)
}
},
actions = {
if (isTopLevel) {
IconButton(onClick = { navigateTo(Search) }) {
Icon(
imageVector = Icons.Filled.Search,
contentDescription = stringResource(R.string.search_posts),
)
}
IconButton(onClick = { navigateTo(Settings) }) {
Icon(
imageVector = Icons.Filled.Tune,
contentDescription = stringResource(R.string.settings),
)
}
}
},
)
}
@@ -1,130 +0,0 @@
/*
* Copyright © 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.decorations
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.FastOutLinearInEasing
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import androidx.navigation3.runtime.NavKey
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.blur.HazeBlurDefaults
import dev.chrisbanes.haze.blur.blurEffect
import dev.chrisbanes.haze.hazeEffect
import dev.msfjarvis.claw.android.ui.navigation.AppDestinations
import dev.msfjarvis.claw.common.ui.FloatingNavigationBar
import kotlinx.collections.immutable.ImmutableList
const val AnimationDuration = 100
@Composable
fun ClawNavigationBar(
items: ImmutableList<NavigationItem>,
currentNavKey: NavKey?,
navigateTo: (NavKey) -> Unit,
isVisible: Boolean,
hazeState: HazeState,
modifier: Modifier = Modifier,
) {
AnimatedVisibility(
visible = isVisible,
enter =
slideInVertically(
// Enters by sliding up from offset 0 to fullHeight.
initialOffsetY = { fullHeight -> fullHeight },
animationSpec = tween(durationMillis = AnimationDuration, easing = LinearOutSlowInEasing),
),
exit =
slideOutVertically(
// Exits by sliding up from offset 0 to -fullHeight.
targetOffsetY = { fullHeight -> fullHeight },
animationSpec = tween(durationMillis = AnimationDuration, easing = FastOutLinearInEasing),
),
label = "",
content = {
val surfaceColor = MaterialTheme.colorScheme.surface
FloatingNavigationBar(
tonalElevation = 16.dp,
shape = MaterialTheme.shapes.extraLarge,
modifier =
modifier
.padding(horizontal = 16.dp)
.navigationBarsPadding()
.clip(MaterialTheme.shapes.extraLarge)
.hazeEffect(hazeState) {
blurEffect {
backgroundColor = surfaceColor
colorEffects = emptyList()
blurRadius = 24.dp
noiseFactor = 0f
}
},
containerColor = if (HazeBlurDefaults.blurEnabled()) Color.Transparent else surfaceColor,
) {
items.forEach { navItem ->
val isSelected = currentNavKey == navItem.navKey
NavigationBarItem(
icon = {
Crossfade(isSelected, label = "nav-label") {
Icon(
imageVector = if (it) navItem.selectedIcon else navItem.icon,
contentDescription = navItem.label.replaceFirstChar(Char::uppercase),
)
}
},
label = { Text(text = navItem.label) },
selected = isSelected,
onClick = {
if (isSelected) {
navItem.listStateResetCallback()
} else {
navigateTo(navItem.navKey)
}
},
modifier = Modifier.testTag(navItem.label.uppercase()),
)
}
}
},
)
}
class NavigationItem
private constructor(
val icon: ImageVector,
val label: String,
val navKey: NavKey,
val selectedIcon: ImageVector,
val listStateResetCallback: () -> Unit,
) {
constructor(
destination: AppDestinations,
listStateResetCallback: () -> Unit,
) : this(
destination.icon,
destination.label,
destination.navKey,
destination.selectedIcon,
listStateResetCallback,
) {}
}
@@ -1,79 +0,0 @@
/*
* Copyright © 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.decorations
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.FastOutLinearInEasing
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.layout.Spacer
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationRail
import androidx.compose.material3.NavigationRailItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.navigation3.runtime.NavKey
import kotlinx.collections.immutable.ImmutableList
@Composable
fun ClawNavigationRail(
items: ImmutableList<NavigationItem>,
currentNavKey: NavKey?,
navigateTo: (NavKey) -> Unit,
isVisible: Boolean,
modifier: Modifier = Modifier,
) {
AnimatedVisibility(
visible = isVisible,
enter =
slideInHorizontally(
// Enters by sliding in from offset -fullWidth to 0.
initialOffsetX = { fullWidth -> -fullWidth },
animationSpec = tween(durationMillis = AnimationDuration, easing = LinearOutSlowInEasing),
),
exit =
slideOutHorizontally(
// Exits by sliding out from offset 0 to -fullWidth.
targetOffsetX = { fullWidth -> -fullWidth },
animationSpec = tween(durationMillis = AnimationDuration, easing = FastOutLinearInEasing),
),
modifier = Modifier,
) {
NavigationRail(modifier = modifier) {
Spacer(Modifier.weight(1f))
items.forEach { navItem ->
val isSelected = currentNavKey == navItem.navKey
NavigationRailItem(
icon = {
Crossfade(isSelected, label = "nav-label") {
Icon(
imageVector = if (it) navItem.selectedIcon else navItem.icon,
contentDescription = navItem.label.replaceFirstChar(Char::uppercase),
)
}
},
label = { Text(text = navItem.label) },
selected = isSelected,
onClick = {
if (isSelected) {
navItem.listStateResetCallback()
} else {
navigateTo(navItem.navKey)
}
},
modifier = Modifier.testTag(navItem.label.uppercase()),
)
}
Spacer(Modifier.weight(1f))
}
}
}
@@ -1,62 +0,0 @@
/*
* Copyright © 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
import android.content.Context
import android.content.Intent
import androidx.compose.ui.platform.UriHandler
import dev.msfjarvis.claw.android.viewmodel.ClawViewModel
import dev.msfjarvis.claw.common.posts.PostActions
import dev.msfjarvis.claw.model.LinkMetadata
import dev.msfjarvis.claw.model.UIPost
fun PostActions(
context: Context,
uriHandler: UriHandler,
viewModel: ClawViewModel,
navigateToComments: (String) -> Unit,
): PostActions {
return object : PostActions {
override fun viewPost(postId: String, postUrl: String, commentsUrl: String) {
viewModel.markPostAsRead(postId)
uriHandler.openUri(postUrl.ifEmpty { commentsUrl })
}
override fun viewComments(postId: String) {
viewModel.markPostAsRead(postId)
navigateToComments(postId)
}
override fun viewCommentsPage(post: UIPost) {
uriHandler.openUri(post.commentsUrl)
}
override fun toggleSave(post: UIPost) {
viewModel.toggleSave(post)
}
override fun share(post: UIPost) {
val sendIntent: Intent =
Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, post.url.ifEmpty { post.commentsUrl })
putExtra(Intent.EXTRA_TITLE, post.title)
type = "text/plain"
}
val shareIntent = Intent.createChooser(sendIntent, null)
context.startActivity(shareIntent)
}
override fun isPostRead(post: UIPost): Boolean = viewModel.isPostRead(post)
override fun isPostSaved(post: UIPost): Boolean = viewModel.isPostSaved(post)
override suspend fun getLinkMetadata(url: String): LinkMetadata {
return viewModel.getLinkMetadata(url)
}
}
}
@@ -1,78 +0,0 @@
/*
* Copyright © 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.lists
import androidx.activity.compose.ReportDrawn
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Inbox
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import dev.msfjarvis.claw.android.R
import dev.msfjarvis.claw.common.posts.PostActions
import dev.msfjarvis.claw.common.ui.decorations.MonthHeader
import dev.msfjarvis.claw.model.UIPost
import kotlinx.collections.immutable.ImmutableMap
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun DatabasePosts(
items: ImmutableMap<String, List<UIPost>>,
listState: LazyListState,
postActions: PostActions,
contentPadding: PaddingValues,
modifier: Modifier = Modifier,
) {
ReportDrawn()
Box(modifier = modifier.fillMaxSize()) {
if (items.isEmpty()) {
Column(modifier = Modifier.align(Alignment.Center)) {
Icon(
imageVector = Icons.Filled.Inbox,
contentDescription = stringResource(R.string.empty_inbox_icon),
modifier = Modifier.align(Alignment.CenterHorizontally).size(36.dp),
)
Text(
text = stringResource(R.string.no_saved_posts),
style = MaterialTheme.typography.headlineSmall,
)
}
} else {
LazyColumn(state = listState, contentPadding = contentPadding) {
items.forEach { (month, posts) ->
stickyHeader(contentType = "month-header") { MonthHeader(label = month) }
items(items = posts, key = { it.shortId }, contentType = { "LobstersItem" }) { item ->
val isSaved = postActions.isPostSaved(item)
val isRead = postActions.isPostRead(item)
LobstersListItem(
item = item,
isSaved = isSaved,
isRead = isRead,
postActions = postActions,
)
HorizontalDivider()
}
}
}
}
}
}
@@ -1,74 +0,0 @@
/*
* Copyright © 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.lists
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Reply
import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.unit.dp
import dev.msfjarvis.claw.android.glance.samplePosts
import dev.msfjarvis.claw.common.posts.LobstersCard
import dev.msfjarvis.claw.common.posts.PostActions
import dev.msfjarvis.claw.common.posts.TEST_POST_ACTIONS
import dev.msfjarvis.claw.common.theme.LobstersTheme
import dev.msfjarvis.claw.common.ui.preview.ThemePreviews
import dev.msfjarvis.claw.model.UIPost
import me.saket.swipe.SwipeAction
import me.saket.swipe.SwipeableActionsBox
@Composable
fun LobstersListItem(
item: UIPost,
isSaved: Boolean,
isRead: Boolean,
postActions: PostActions,
modifier: Modifier = Modifier,
) {
val commentsAction =
SwipeAction(
icon = rememberVectorPainter(Icons.AutoMirrored.Filled.Reply),
background = MaterialTheme.colorScheme.tertiary,
onSwipe = { postActions.viewCommentsPage(item) },
)
val shareAction =
SwipeAction(
icon = rememberVectorPainter(Icons.Filled.Share),
background = MaterialTheme.colorScheme.tertiary,
onSwipe = { postActions.share(item) },
)
SwipeableActionsBox(
startActions = listOf(shareAction),
endActions = listOf(commentsAction),
swipeThreshold = 80.dp,
backgroundUntilSwipeThreshold = MaterialTheme.colorScheme.surfaceVariant,
) {
LobstersCard(
post = item,
isSaved = isSaved,
isRead = isRead,
postActions = postActions,
modifier = modifier,
)
}
}
@ThemePreviews
@Composable
private fun ItemPreview() {
LobstersTheme {
LobstersListItem(
item = samplePosts(1).first(),
isSaved = true,
isRead = true,
postActions = TEST_POST_ACTIONS,
)
}
}
@@ -1,131 +0,0 @@
/*
* Copyright © 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.lists
import androidx.activity.compose.ReportDrawnWhen
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.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.paging.LoadState
import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemContentType
import dev.msfjarvis.claw.android.glance.samplePosts
import dev.msfjarvis.claw.common.posts.PostActions
import dev.msfjarvis.claw.common.posts.TEST_POST_ACTIONS
import dev.msfjarvis.claw.common.theme.LobstersTheme
import dev.msfjarvis.claw.common.ui.NetworkError
import dev.msfjarvis.claw.common.ui.ProgressBar
import dev.msfjarvis.claw.common.ui.preview.DevicePreviews
import dev.msfjarvis.claw.model.UIPost
import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.persistentSetOf
import kotlinx.coroutines.flow.MutableStateFlow
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NetworkPosts(
lazyPagingItems: LazyPagingItems<UIPost>,
listState: LazyListState,
postActions: PostActions,
contentPadding: PaddingValues,
filteredTags: ImmutableSet<String>,
modifier: Modifier = Modifier,
) {
ReportDrawnWhen { lazyPagingItems.itemCount > 0 }
val refreshLoadState by rememberUpdatedState(lazyPagingItems.loadState.refresh)
val state = rememberPullToRefreshState()
PullToRefreshBox(
modifier = modifier.fillMaxSize(),
isRefreshing = refreshLoadState == LoadState.Loading,
state = state,
onRefresh = { lazyPagingItems.refresh() },
indicator = {
PullToRefreshDefaults.Indicator(
state = state,
isRefreshing = refreshLoadState == LoadState.Loading,
modifier = Modifier.align(Alignment.TopCenter).padding(contentPadding),
)
},
) {
if (lazyPagingItems.itemCount == 0 && refreshLoadState is LoadState.Error) {
NetworkError(
label = "Failed to load posts",
error = (refreshLoadState as LoadState.Error).error,
modifier = Modifier.align(Alignment.Center),
)
} else {
LazyColumn(contentPadding = contentPadding, state = listState) {
items(
count = lazyPagingItems.itemCount,
// Intentionally keyed on just the index to avoid crashes
key = { index -> index },
contentType = lazyPagingItems.itemContentType { "LobstersItem" },
) { index ->
val item = lazyPagingItems[index]
if (item != null) {
val shouldShowPost = item.tags.none { tag -> filteredTags.contains(tag) }
if (shouldShowPost) {
val isSaved = postActions.isPostSaved(item)
val isRead = postActions.isPostRead(item)
LobstersListItem(
item = item,
isSaved = isSaved,
isRead = isRead,
postActions = postActions,
)
HorizontalDivider()
}
}
}
if (lazyPagingItems.loadState.append == LoadState.Loading) {
item(key = "progressbar") {
ProgressBar(
modifier =
Modifier.fillMaxWidth()
.wrapContentWidth(Alignment.CenterHorizontally)
.padding(vertical = 16.dp)
)
}
}
}
}
}
}
@DevicePreviews
@Composable
private fun ListPreview() {
val items = samplePosts(20) { this.copy(shortId = "${this.shortId}${it}") }
val flow = MutableStateFlow(PagingData.from(items))
LobstersTheme {
NetworkPosts(
lazyPagingItems = flow.collectAsLazyPagingItems(),
listState = rememberLazyListState(),
postActions = TEST_POST_ACTIONS,
contentPadding = PaddingValues(),
filteredTags = persistentSetOf(),
)
}
}
@@ -1,54 +0,0 @@
/*
* Copyright © 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.lists
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import androidx.paging.compose.LazyPagingItems
import dev.msfjarvis.claw.common.posts.PostActions
import dev.msfjarvis.claw.common.ui.SearchBar
import dev.msfjarvis.claw.model.UIPost
import kotlinx.collections.immutable.ImmutableSet
@Composable
fun SearchList(
lazyPagingItems: LazyPagingItems<UIPost>,
listState: LazyListState,
postActions: PostActions,
searchQuery: String,
setSearchQuery: (String) -> Unit,
contentPadding: PaddingValues,
filteredTags: ImmutableSet<String>,
modifier: Modifier = Modifier,
) {
val triggerSearch = { query: String ->
setSearchQuery(query)
lazyPagingItems.refresh()
}
Column(modifier.padding(contentPadding).fillMaxWidth()) {
SearchBar(
value = searchQuery,
onValueChange = setSearchQuery,
onSearch = { triggerSearch(it) },
modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp).testTag("search_bar"),
)
NetworkPosts(
lazyPagingItems = lazyPagingItems,
listState = listState,
postActions = postActions,
contentPadding = PaddingValues(0.dp),
filteredTags = filteredTags,
)
}
}
@@ -1,70 +0,0 @@
/*
* Copyright © 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.navigation
import android.os.Parcelable
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.NewReleases
import androidx.compose.material.icons.filled.Whatshot
import androidx.compose.material.icons.outlined.FavoriteBorder
import androidx.compose.material.icons.outlined.NewReleases
import androidx.compose.material.icons.outlined.Whatshot
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.navigation3.runtime.NavKey
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
interface TopLevelDestination
interface NonStackable
@Parcelize @Serializable data object Hottest : NavKey, TopLevelDestination, Parcelable
@Parcelize @Serializable data object Newest : NavKey, TopLevelDestination, Parcelable
@Parcelize @Serializable data object Saved : NavKey, TopLevelDestination, Parcelable
@Parcelize @Serializable data class Comments(val postId: String) : NavKey, Parcelable, NonStackable
@Parcelize @Serializable data class User(val username: String) : NavKey, Parcelable, NonStackable
@Parcelize @Serializable data object Search : NavKey, Parcelable
@Parcelize @Serializable data object Settings : NavKey, Parcelable
@Parcelize @Serializable data object AboutLibraries : NavKey, Parcelable
@Parcelize @Serializable data object TagFiltering : NavKey, Parcelable
@Parcelize @Serializable data object Login : NavKey, Parcelable
enum class AppDestinations(
val icon: ImageVector,
val label: String,
val navKey: NavKey,
val selectedIcon: ImageVector,
) {
HOTTEST(
icon = Icons.Outlined.Whatshot,
label = "Hottest",
navKey = Hottest,
selectedIcon = Icons.Filled.Whatshot,
),
NEWEST(
icon = Icons.Outlined.NewReleases,
label = "Newest",
navKey = Newest,
selectedIcon = Icons.Filled.NewReleases,
),
SAVED(
icon = Icons.Outlined.FavoriteBorder,
label = "Saved",
navKey = Saved,
selectedIcon = Icons.Filled.Favorite,
),
}
@@ -1,24 +0,0 @@
/*
* Copyright © 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.navigation
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
enum class ClawNavigationType {
BOTTOM_NAVIGATION,
NAVIGATION_RAIL;
companion object {
fun fromSize(windowSize: WindowWidthSizeClass): ClawNavigationType {
return when (windowSize) {
WindowWidthSizeClass.Medium,
WindowWidthSizeClass.Expanded -> NAVIGATION_RAIL
else -> BOTTOM_NAVIGATION
}
}
}
}
@@ -1,241 +0,0 @@
/** Taken from https://github.com/getsentry/sentry-java/pull/5023 */
@file:Suppress("UnstableApiUsage") // Sentry internal APIs are used by this module
package dev.msfjarvis.claw.android.ui.navigation
import android.annotation.SuppressLint
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.snapshotFlow
import io.sentry.Breadcrumb
import io.sentry.BuildConfig
import io.sentry.Hint
import io.sentry.IScopes
import io.sentry.ITransaction
import io.sentry.ScopesAdapter
import io.sentry.SentryIntegrationPackageStorage
import io.sentry.SentryLevel
import io.sentry.SentryOptions
import io.sentry.SpanStatus
import io.sentry.TransactionContext
import io.sentry.TransactionOptions
import io.sentry.TypeCheckHint
import io.sentry.protocol.TransactionNameSource
import io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
private const val TRACE_ORIGIN = "auto.navigation.navigation3"
private const val NAVIGATION_OP = "navigation"
/** Holder for the active navigation transaction in a composition scope. */
internal class NavigationTransactionHolder {
internal var activeTransaction: ITransaction? = null
}
/**
* A Composable that observes a back stack [List] and captures a [Breadcrumb] and starts an
* [ITransaction] for each navigation event, sending them to Sentry.
*
* This integration is designed for Android Navigation 3 which uses a back stack-based approach with
* Compose state observation instead of traditional listeners.
*
* Each invocation of this composable maintains independent transaction state, allowing multiple
* navigation instances (e.g., in split-screen or multi-pane layouts) to coexist without
* interference.
*
* @param T The type of keys in the back stack
* @param backStack The current back stack to observe for navigation changes
* @param enableNavigationBreadcrumbs Whether the integration should capture breadcrumbs for
* navigation events.
* @param enableNavigationTracing Whether the integration should start a new idle [ITransaction]
* with [SentryOptions.idleTimeout] for navigation events.
* @param keyToRoute A function to extract a route name from a back stack key. Defaults to
* [Any.toString].
* @param scopes The [IScopes] instance to use for capturing events. Defaults to the singleton
* instance.
*/
@Composable
fun <T> SentryNavigation3Traced(
@SuppressLint("ComposeUnstableCollections") backStack: List<T>,
enableNavigationBreadcrumbs: Boolean = true,
enableNavigationTracing: Boolean = true,
keyToRoute: (T) -> String? = { it.toString() },
scopes: IScopes = ScopesAdapter.getInstance(),
) {
val backStackSnapshot by rememberUpdatedState(backStack)
val enableBreadcrumbsSnapshot by rememberUpdatedState(enableNavigationBreadcrumbs)
val enableTracingSnapshot by rememberUpdatedState(enableNavigationTracing)
val keyToRouteSnapshot by rememberUpdatedState(keyToRoute)
val scopesSnapshot by rememberUpdatedState(scopes)
val transactionHolder = remember { NavigationTransactionHolder() }
DisposableEffect(Unit) {
addIntegrationToSdkVersion("Navigation3")
val observer =
SentryBackStackObserver(
enableNavigationBreadcrumbs = enableBreadcrumbsSnapshot,
enableNavigationTracing = enableTracingSnapshot,
keyToRoute = keyToRouteSnapshot,
scopes = scopesSnapshot,
transactionHolder = transactionHolder,
)
@SuppressLint("RawDispatchersUse")
val scope =
kotlinx.coroutines.CoroutineScope(
kotlinx.coroutines.Dispatchers.Main + kotlinx.coroutines.SupervisorJob()
)
@SuppressLint("DenyListedApi")
val job =
snapshotFlow { backStackSnapshot }
.drop(1) // Skip initial state
.onEach { currentStack ->
val currentKey = currentStack.lastOrNull()
val previousKey = observer.previousKey
if (currentKey != null && currentKey != previousKey) {
observer.handleNavigation(currentKey, currentStack)
observer.previousKey = currentKey
}
}
.launchIn(scope)
onDispose { job.cancel() }
}
}
/** Internal observer that monitors back stack changes and creates Sentry events. */
internal class SentryBackStackObserver<T>(
private val enableNavigationBreadcrumbs: Boolean,
private val enableNavigationTracing: Boolean,
private val keyToRoute: (T) -> String?,
private val scopes: IScopes,
private val transactionHolder: NavigationTransactionHolder,
) {
internal var previousKey: T? = null
private val isPerformanceEnabled: Boolean
get() = scopes.options.isTracingEnabled && enableNavigationTracing
init {
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-android-navigation3", BuildConfig.VERSION_NAME)
}
internal fun handleNavigation(currentKey: T, currentStack: List<T>) {
val currentRoute = keyToRoute(currentKey)
if (currentRoute != null) {
addBreadcrumb(currentRoute, currentStack)
if (scopes.options.isEnableScreenTracking) {
scopes.configureScope { it.screen = currentRoute }
}
startTracing(currentRoute, currentStack)
}
}
private fun addBreadcrumb(toRoute: String, currentStack: List<T>) {
if (!enableNavigationBreadcrumbs) {
return
}
val breadcrumb =
Breadcrumb().apply {
type = NAVIGATION_OP
category = NAVIGATION_OP
val fromKey = previousKey
if (fromKey != null) {
val fromRoute = keyToRoute(fromKey)
if (fromRoute != null) {
data["from"] = fromRoute
}
}
data["to"] = toRoute
// Capture back stack keys as a list
val backStackKeys = currentStack.mapNotNull { keyToRoute(it) }
if (backStackKeys.isNotEmpty()) {
data["back_stack"] = backStackKeys
}
level = SentryLevel.INFO
}
val hint = Hint()
hint.set(TypeCheckHint.ANDROID_NAV_DESTINATION, toRoute)
scopes.addBreadcrumb(breadcrumb, hint)
}
private fun startTracing(routeName: String, currentStack: List<T>) {
if (!isPerformanceEnabled) {
io.sentry.util.TracingUtils.startNewTrace(scopes)
return
}
// Finish previous transaction in this navigation scope
if (transactionHolder.activeTransaction != null) {
stopTracing()
}
val transactionOptions =
TransactionOptions().also {
it.isWaitForChildren = true
it.idleTimeout = scopes.options.idleTimeout
// Set deadline timeout based on configured option
val deadlineTimeoutMillis = scopes.options.deadlineTimeout
// No deadline when zero or negative value is set
it.deadlineTimeout = if (deadlineTimeoutMillis <= 0) null else deadlineTimeoutMillis
it.isTrimEnd = true
}
val transaction =
scopes.startTransaction(
TransactionContext(routeName, TransactionNameSource.ROUTE, NAVIGATION_OP),
transactionOptions,
)
transaction.spanContext.origin = TRACE_ORIGIN
// Capture back stack keys as data
val backStackKeys = currentStack.mapNotNull { keyToRoute(it) }
if (backStackKeys.isNotEmpty()) {
transaction.setData("back_stack", backStackKeys)
}
scopes.configureScope { scope ->
scope.withTransaction { tx ->
if (tx == null) {
scope.transaction = transaction
}
}
}
transactionHolder.activeTransaction = transaction
}
private fun stopTracing() {
val status = transactionHolder.activeTransaction?.status ?: SpanStatus.OK
transactionHolder.activeTransaction?.finish(status)
// clear transaction from scope so others can bind to it
scopes.configureScope { scope ->
scope.withTransaction { tx ->
if (tx == transactionHolder.activeTransaction) {
scope.clearTransaction()
}
}
}
transactionHolder.activeTransaction = null
}
}
@@ -1,374 +0,0 @@
/*
* Copyright © 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.screens
import androidx.activity.compose.BackHandler
import androidx.activity.compose.LocalActivity
import androidx.annotation.VisibleForTesting
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.navigation3.ListDetailSceneStrategy
import androidx.compose.material3.adaptive.navigation3.rememberListDetailSceneStrategy
import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.UriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.runtime.rememberNavBackStack
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
import androidx.navigation3.ui.NavDisplay
import androidx.paging.compose.collectAsLazyPagingItems
import com.mikepenz.aboutlibraries.ui.compose.android.produceLibraries
import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.hazeSource
import dev.msfjarvis.claw.android.R
import dev.msfjarvis.claw.android.ui.PostActions
import dev.msfjarvis.claw.android.ui.decorations.ClawAppBar
import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationBar
import dev.msfjarvis.claw.android.ui.decorations.ClawNavigationRail
import dev.msfjarvis.claw.android.ui.decorations.NavigationItem
import dev.msfjarvis.claw.android.ui.lists.DatabasePosts
import dev.msfjarvis.claw.android.ui.lists.NetworkPosts
import dev.msfjarvis.claw.android.ui.navigation.AboutLibraries
import dev.msfjarvis.claw.android.ui.navigation.AppDestinations
import dev.msfjarvis.claw.android.ui.navigation.ClawNavigationType
import dev.msfjarvis.claw.android.ui.navigation.Comments
import dev.msfjarvis.claw.android.ui.navigation.Hottest
import dev.msfjarvis.claw.android.ui.navigation.Login
import dev.msfjarvis.claw.android.ui.navigation.Newest
import dev.msfjarvis.claw.android.ui.navigation.NonStackable
import dev.msfjarvis.claw.android.ui.navigation.Saved
import dev.msfjarvis.claw.android.ui.navigation.Search
import dev.msfjarvis.claw.android.ui.navigation.SentryNavigation3Traced
import dev.msfjarvis.claw.android.ui.navigation.Settings
import dev.msfjarvis.claw.android.ui.navigation.TagFiltering
import dev.msfjarvis.claw.android.ui.navigation.TopLevelDestination
import dev.msfjarvis.claw.android.ui.navigation.User
import dev.msfjarvis.claw.android.viewmodel.ClawViewModel
import dev.msfjarvis.claw.android.viewmodel.SettingsViewModel
import dev.msfjarvis.claw.common.comments.CommentsPage
import dev.msfjarvis.claw.common.tags.TagFilterViewModel
import dev.msfjarvis.claw.common.tags.TagList
import dev.msfjarvis.claw.common.user.UserProfile
import dev.zacsweers.metrox.viewmodel.metroViewModel
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.persistentSetOf
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun LobstersPostsScreen(
uriHandler: UriHandler,
windowSizeClass: WindowSizeClass,
setWebUri: (String?) -> Unit,
modifier: Modifier = Modifier,
deepLinkDestination: NavKey? = null,
clearDeepLink: () -> Unit = {},
viewModel: ClawViewModel = metroViewModel(),
settingsViewModel: SettingsViewModel = metroViewModel(),
tagFilterViewModel: TagFilterViewModel = metroViewModel(key = "tag_filter"),
) {
val navigationType = ClawNavigationType.fromSize(windowSizeClass.widthSizeClass)
val libraries by produceLibraries()
val backStack = rememberNavBackStack(Hottest)
val listDetailStrategy = rememberListDetailSceneStrategy<NavKey>()
// region Pain
val context = LocalContext.current
val activity = LocalActivity.current
val hottestListState = rememberLazyListState()
val newestListState = rememberLazyListState()
val savedListState = rememberLazyListState()
val coroutineScope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
val hazeState = remember { HazeState() }
val savedPosts by viewModel.savedPostsByMonth.collectAsStateWithLifecycle(persistentMapOf())
val savedPostsCount by viewModel.savedPostsCount.collectAsStateWithLifecycle(0L)
val filteredTags by tagFilterViewModel.filteredTags.collectAsStateWithLifecycle(persistentSetOf())
val isLoggedIn by settingsViewModel.isLoggedIn.collectAsStateWithLifecycle(false)
val username by settingsViewModel.username.collectAsStateWithLifecycle(null)
LaunchedEffect(deepLinkDestination) {
if (deepLinkDestination != null) {
navigateTo(backStack, deepLinkDestination, allowStacking = true)
clearDeepLink()
}
}
val navItems =
persistentListOf(
NavigationItem(AppDestinations.HOTTEST) {
coroutineScope.launch { hottestListState.animateScrollToItem(index = 0) }
},
NavigationItem(AppDestinations.NEWEST) {
coroutineScope.launch { newestListState.animateScrollToItem(index = 0) }
},
NavigationItem(AppDestinations.SAVED) {
coroutineScope.launch { if (savedPosts.isNotEmpty()) savedListState.scrollToItem(0) }
},
)
// endregion
val postActions = remember {
PostActions(context, uriHandler, viewModel) {
navigateTo(backStack, Comments(it), allowStacking = false)
}
}
BackHandler(enabled = backStack.size > 1) { popBackStack(backStack) }
Scaffold(
topBar = {
ClawAppBar(
activity = activity,
isTopLevel = backStack.lastOrNull() is TopLevelDestination,
navigateTo = { destination -> navigateTo(backStack, destination) },
popBackStack = { popBackStack(backStack) },
)
},
bottomBar = {
val currentDestination = backStack.lastOrNull()
AnimatedVisibility(visible = navigationType == ClawNavigationType.BOTTOM_NAVIGATION) {
ClawNavigationBar(
items = navItems,
currentNavKey = currentDestination,
navigateTo = { destination -> navigateTo(backStack, destination) },
isVisible = backStack.lastOrNull() is TopLevelDestination,
hazeState = hazeState,
)
}
},
snackbarHost = { SnackbarHost(snackbarHostState) },
modifier = Modifier.semantics { testTagsAsResourceId = true },
) { contentPadding ->
SentryNavigation3Traced(backStack = backStack)
Row {
AnimatedVisibility(visible = navigationType == ClawNavigationType.NAVIGATION_RAIL) {
val currentDestination = backStack.lastOrNull()
ClawNavigationRail(
items = navItems,
currentNavKey = currentDestination,
navigateTo = { destination -> navigateTo(backStack, destination) },
isVisible = backStack.lastOrNull() is TopLevelDestination,
)
}
NavDisplay(
backStack = backStack,
modifier = modifier.hazeSource(hazeState),
sceneStrategies = listOf(listDetailStrategy),
entryDecorators = listOf(rememberSaveableStateHolderNavEntryDecorator()),
onBack = {
if (popBackStack(backStack) == null) {
activity?.finish()
}
},
predictivePopTransitionSpec = {
slideInHorizontally(initialOffsetX = { -it }, animationSpec = tween(200)) togetherWith
slideOutHorizontally(targetOffsetX = { it }, animationSpec = tween(200))
},
entryProvider =
entryProvider {
entry<Hottest>(
metadata = ListDetailSceneStrategy.listPane(detailPlaceholder = { Placeholder() })
) {
setWebUri("https://lobste.rs/")
val hottestPosts = viewModel.hottestPosts.collectAsLazyPagingItems()
NetworkPosts(
lazyPagingItems = hottestPosts,
listState = hottestListState,
postActions = postActions,
contentPadding = contentPadding,
filteredTags = filteredTags,
)
}
entry<Newest>(
metadata = ListDetailSceneStrategy.listPane(detailPlaceholder = { Placeholder() })
) {
setWebUri("https://lobste.rs/")
val newestPosts = viewModel.newestPosts.collectAsLazyPagingItems()
NetworkPosts(
lazyPagingItems = newestPosts,
listState = newestListState,
postActions = postActions,
contentPadding = contentPadding,
filteredTags = filteredTags,
)
}
entry<Saved>(
metadata = ListDetailSceneStrategy.listPane(detailPlaceholder = { Placeholder() })
) {
setWebUri(null)
DatabasePosts(
items = savedPosts,
listState = savedListState,
postActions = postActions,
contentPadding = contentPadding,
)
}
entry<Comments>(metadata = ListDetailSceneStrategy.detailPane()) { dest ->
val commentsPostActions = remember {
PostActions(context, uriHandler, viewModel) {
navigateTo(backStack, Comments(it), allowStacking = true)
}
}
CommentsPage(
postId = dest.postId,
postActions = commentsPostActions,
contentPadding = contentPadding,
openUserProfile = { navigateTo(backStack, User(it)) },
)
}
entry<User>(
metadata =
NavDisplay.transitionSpec {
slideInHorizontally(
initialOffsetX = { it },
animationSpec = tween(200),
) togetherWith ExitTransition.KeepUntilTransitionsFinished
} +
NavDisplay.popTransitionSpec {
// Slide old content down, revealing the new content in place underneath
EnterTransition.None togetherWith
slideOutHorizontally(targetOffsetX = { it }, animationSpec = tween(200))
} +
ListDetailSceneStrategy.extraPane()
) { dest ->
UserProfile(
username = dest.username,
contentPadding = contentPadding,
openUserProfile = { navigateTo(backStack, User(it)) },
)
}
entry<Settings>(metadata = ListDetailSceneStrategy.extraPane()) {
SettingsScreen(
openInputStream = context.contentResolver::openInputStream,
openOutputStream = context.contentResolver::openOutputStream,
openLibrariesScreen = { navigateTo(backStack, AboutLibraries) },
openRepository = {
uriHandler.openUri("https://github.com/msfjarvis/compose-lobsters")
},
openTagFiltering = { navigateTo(backStack, TagFiltering) },
openLoginScreen = { navigateTo(backStack, Login) },
onLogout = { settingsViewModel.logout() },
isLoggedIn = isLoggedIn,
username = username,
importPosts = viewModel::importPosts,
exportPostsAsJson = viewModel::exportPostsAsJson,
exportPostsAsHtml = viewModel::exportPostsAsHtml,
savedPostsCount = savedPostsCount,
snackbarHostState = snackbarHostState,
contentPadding = contentPadding,
modifier = Modifier.fillMaxSize(),
)
}
entry<Login>(metadata = ListDetailSceneStrategy.extraPane()) {
LoginScreen(
onLoginSuccess = settingsViewModel::saveCookie,
popBackStack = { popBackStack(backStack) },
modifier = Modifier.fillMaxSize(),
)
}
entry<Search>(metadata = ListDetailSceneStrategy.extraPane()) {
setWebUri("https://lobste.rs/search")
SearchScreen(
viewModel = viewModel,
postActions = postActions,
contentPadding = contentPadding,
)
}
entry<AboutLibraries>(metadata = ListDetailSceneStrategy.extraPane()) {
LibrariesContainer(
libraries = libraries,
contentPadding = contentPadding,
modifier = Modifier.fillMaxSize(),
)
}
entry<TagFiltering> { TagList(contentPadding = contentPadding) }
},
)
}
}
}
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun navigateTo(
backStack: MutableList<NavKey>,
destination: NavKey,
allowStacking: Boolean = false,
) {
if (destination is TopLevelDestination) {
backStack.clear()
if (destination != Hottest) {
backStack.add(Hottest)
}
backStack.add(destination)
return
}
if (destination is NonStackable) {
if (allowStacking && destination is Comments) {
val lastItem = backStack.lastOrNull()
if (lastItem is Comments && lastItem.postId == destination.postId) {
return
}
backStack.add(destination)
return
}
val existingEntry = backStack.firstOrNull {
it is NonStackable && it::class.java.isAssignableFrom(destination::class.java)
}
if (existingEntry != null) {
backStack.remove(existingEntry)
}
}
backStack.add(destination)
}
private fun popBackStack(backStack: MutableList<NavKey>): NavKey? {
if (backStack.size <= 1) {
return null
}
return backStack.removeAt(backStack.lastIndex)
}
@Composable
private fun Placeholder(modifier: Modifier = Modifier) {
Text(text = stringResource(R.string.no_post_selected), modifier = modifier)
}
@@ -1,131 +0,0 @@
/*
* Copyright © 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.screens
import android.annotation.SuppressLint
import android.graphics.Bitmap
import android.webkit.CookieManager
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
private const val LOGIN_URL = "https://lobste.rs/login"
private const val LOBSTERS_URL = "https://lobste.rs"
internal fun parseAuthenticatedUsername(usernameResult: String): String? {
return usernameResult.removeSurrounding("\"").takeUnless { it.isBlank() || it == "null" }
}
@Composable
fun LoginScreen(
onLoginSuccess: (String, String) -> Unit,
popBackStack: () -> Unit,
modifier: Modifier = Modifier,
) {
var isLoading by remember { mutableStateOf(true) }
var hasError by remember { mutableStateOf(false) }
var webViewRef by remember { mutableStateOf<WebView?>(null) }
Scaffold(modifier = modifier) { contentPadding ->
Box(modifier = Modifier.padding(contentPadding).fillMaxSize()) {
if (hasError) {
Column(
modifier = Modifier.align(Alignment.Center),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text("Failed to load login page.")
Spacer(modifier = Modifier.height(12.dp))
Button(
onClick = {
hasError = false
isLoading = true
webViewRef?.loadUrl(LOGIN_URL)
}
) {
Text("Retry")
}
}
} else {
AndroidView(
factory = { context ->
WebView(context)
.apply {
@SuppressLint("SetJavaScriptEnabled")
settings.javaScriptEnabled = true
webViewClient =
object : WebViewClient() {
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
isLoading = true
}
override fun onPageFinished(view: WebView, url: String) {
isLoading = false
if (!url.contains("/login")) {
@SuppressLint("DeprecatedCall")
view.evaluateJavascript(
"document.body && document.body.getAttribute('data-username')"
) { usernameResult ->
val username = parseAuthenticatedUsername(usernameResult)
if (username != null) {
val cookie = CookieManager.getInstance().getCookie(LOBSTERS_URL)
if (cookie != null) {
onLoginSuccess(cookie, username)
popBackStack()
}
}
}
}
}
override fun onReceivedError(
view: WebView,
request: WebResourceRequest,
error: WebResourceError,
) {
if (request.isForMainFrame) {
isLoading = false
hasError = true
}
}
}
loadUrl(LOGIN_URL)
}
.also { webViewRef = it }
},
modifier = Modifier.fillMaxSize(),
)
}
if (isLoading && !hasError) {
LinearProgressIndicator(modifier = Modifier.fillMaxWidth().align(Alignment.TopCenter))
}
}
}
}
@@ -1,45 +0,0 @@
/*
* Copyright © 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.screens
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.compose.collectAsLazyPagingItems
import dev.msfjarvis.claw.android.ui.lists.SearchList
import dev.msfjarvis.claw.android.viewmodel.ClawViewModel
import dev.msfjarvis.claw.common.posts.PostActions
import dev.msfjarvis.claw.common.tags.TagFilterViewModel
import dev.zacsweers.metrox.viewmodel.metroViewModel
import kotlinx.collections.immutable.toPersistentSet
@Composable
fun SearchScreen(
postActions: PostActions,
contentPadding: PaddingValues,
modifier: Modifier = Modifier,
viewModel: ClawViewModel = metroViewModel(),
tagFilterViewModel: TagFilterViewModel = metroViewModel(key = "tag_filter"),
) {
val listState = rememberLazyListState()
val searchResults = viewModel.searchResults.collectAsLazyPagingItems()
val filteredTags by tagFilterViewModel.filteredTags.collectAsStateWithLifecycle(emptySet())
SearchList(
lazyPagingItems = searchResults,
listState = listState,
postActions = postActions,
searchQuery = viewModel.searchQuery,
contentPadding = contentPadding,
setSearchQuery = { query -> viewModel.searchQuery = query },
filteredTags = filteredTags.toPersistentSet(),
modifier = modifier,
)
}
@@ -1,404 +0,0 @@
/*
* Copyright © 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.screens
import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.LibraryBooks
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Bookmarks
import androidx.compose.material.icons.filled.Code
import androidx.compose.material.icons.filled.FilterList
import androidx.compose.material.icons.filled.ImportExport
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import dev.msfjarvis.claw.android.BuildConfig
import dev.msfjarvis.claw.android.R
import dev.msfjarvis.claw.common.theme.LobstersTheme
import dev.msfjarvis.claw.common.ui.preview.ThemePreviews
import io.sentry.Sentry
import java.io.InputStream
import java.io.OutputStream
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
private const val JSON_MIME_TYPE = "application/json"
private const val HTML_MIME_TYPE = "application/html"
internal fun loggedInAccountText(username: String?): String {
return if (username.isNullOrBlank()) {
"Sign out of your lobste.rs account"
} else {
"Logged in as $username"
}
}
@Composable
fun SettingsScreen(
openLibrariesScreen: () -> Unit,
openRepository: () -> Unit,
openTagFiltering: () -> Unit,
openLoginScreen: () -> Unit,
onLogout: () -> Unit,
isLoggedIn: Boolean,
username: String?,
snackbarHostState: SnackbarHostState,
openInputStream: (Uri) -> InputStream?,
openOutputStream: (Uri) -> OutputStream?,
importPosts: suspend (InputStream) -> Result<Unit>,
exportPostsAsJson: suspend (OutputStream) -> Unit,
exportPostsAsHtml: suspend (OutputStream) -> Unit,
contentPadding: PaddingValues,
savedPostsCount: Long,
modifier: Modifier = Modifier,
) {
val coroutineScope = rememberCoroutineScope()
Column(modifier.padding(contentPadding)) {
// Account Section
SectionHeader(title = "Account")
if (isLoggedIn) {
ListItem(
headlineContent = { Text("Log out") },
supportingContent = { Text(loggedInAccountText(username)) },
leadingContent = {
Icon(
imageVector = Icons.Filled.AccountCircle,
contentDescription = null,
modifier = Modifier.height(32.dp),
)
},
modifier = Modifier.clickable(onClick = onLogout),
)
} else {
ListItem(
headlineContent = { Text("Log in with lobste.rs") },
supportingContent = { Text("Sign in to your lobste.rs account") },
leadingContent = {
Icon(
imageVector = Icons.Filled.AccountCircle,
contentDescription = null,
modifier = Modifier.height(32.dp),
)
},
modifier = Modifier.clickable(onClick = openLoginScreen),
)
}
Spacer(modifier = Modifier.height(24.dp))
// Data Management Section
SectionHeader(title = stringResource(R.string.data_management))
ListItem(
headlineContent = { Text(stringResource(R.string.data_transfer)) },
supportingContent = { Text(stringResource(R.string.import_and_export_your_saved_posts)) },
leadingContent = {
Icon(
imageVector = Icons.Filled.ImportExport,
contentDescription = null,
modifier = Modifier.height(32.dp),
)
},
trailingContent = {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
ImportPosts(openInputStream, coroutineScope, snackbarHostState, importPosts)
ExportPosts(
coroutineScope,
snackbarHostState,
openOutputStream,
exportPostsAsJson,
exportPostsAsHtml,
)
}
},
)
Spacer(modifier = Modifier.height(24.dp))
// App Information Section
SectionHeader(title = stringResource(R.string.app_information))
ListItem(
headlineContent = { Text(stringResource(R.string.version)) },
supportingContent = {
Text(
stringResource(
R.string.version_placeholder,
BuildConfig.VERSION_NAME,
BuildConfig.VERSION_CODE,
)
)
},
leadingContent = {
Icon(
imageVector = Icons.Filled.Code,
contentDescription = null,
modifier = Modifier.height(32.dp),
)
},
)
ListItem(
headlineContent = { Text(stringResource(R.string.saved_posts)) },
supportingContent = { Text(stringResource(R.string.posts_saved_locally, savedPostsCount)) },
leadingContent = {
Icon(
imageVector = Icons.Filled.Bookmarks,
contentDescription = null,
modifier = Modifier.height(32.dp),
)
},
)
ListItem(
headlineContent = { Text(stringResource(R.string.tag_filtering)) },
supportingContent = { Text(stringResource(R.string.filter_posts_by_tags)) },
leadingContent = {
Icon(
imageVector = Icons.Filled.FilterList,
contentDescription = null,
modifier = Modifier.height(32.dp),
)
},
modifier = Modifier.clickable(onClick = openTagFiltering),
)
Spacer(modifier = Modifier.height(24.dp))
// About Section
SectionHeader(title = stringResource(R.string.about))
ListItem(
headlineContent = { Text(stringResource(R.string.libraries)) },
supportingContent = {
Text(stringResource(R.string.view_open_source_libraries_used_in_this_))
},
leadingContent = {
Icon(
imageVector = Icons.AutoMirrored.Filled.LibraryBooks,
contentDescription = null,
modifier = Modifier.height(32.dp),
)
},
modifier = Modifier.clickable(onClick = openLibrariesScreen),
)
ListItem(
headlineContent = { Text(stringResource(R.string.source_code)) },
supportingContent = { Text(stringResource(R.string.view_the_source_code_on_github)) },
leadingContent = {
Icon(
imageVector = Icons.Filled.Code,
contentDescription = null,
modifier = Modifier.height(32.dp),
)
},
modifier = Modifier.clickable(onClick = openRepository),
)
}
}
@Composable
private fun SectionHeader(title: String, modifier: Modifier = Modifier) {
Column(modifier = modifier) {
Text(
text = title,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
)
HorizontalDivider(
modifier = Modifier.padding(horizontal = 16.dp),
color = MaterialTheme.colorScheme.outlineVariant,
)
}
}
@Composable
private fun ImportPosts(
openInputStream: (Uri) -> InputStream?,
coroutineScope: CoroutineScope,
snackbarHostState: SnackbarHostState,
importPosts: suspend (InputStream) -> Result<Unit>,
modifier: Modifier = Modifier,
) {
val importAction =
rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri ->
if (uri == null) {
coroutineScope.launch { snackbarHostState.showSnackbarDismissing("No file selected") }
return@rememberLauncherForActivityResult
}
coroutineScope.launch {
try {
openInputStream(uri)?.use { stream ->
val result = importPosts(stream)
if (result.isSuccess) {
snackbarHostState.showSnackbarDismissing("Successfully imported posts")
} else {
snackbarHostState.showSnackbarDismissing("Failed to import posts")
}
} ?: snackbarHostState.showSnackbarDismissing("Unable to open selected file")
} catch (e: Exception) {
snackbarHostState.showSnackbarDismissing("Failed to open file: ${e.message}")
}
}
}
OutlinedButton(
onClick = {
importAction.launch(JSON_MIME_TYPE)
Sentry.metrics().count("import", 1.0)
},
shape = MaterialTheme.shapes.extraSmall,
modifier = modifier,
) {
Text(text = stringResource(R.string.import_action))
}
}
@Composable
private inline fun ExportPosts(
coroutineScope: CoroutineScope,
snackbarHostState: SnackbarHostState,
crossinline openOutputStream: (Uri) -> OutputStream?,
crossinline exportPostsAsJson: suspend (OutputStream) -> Unit,
crossinline exportPostsAsHtml: suspend (OutputStream) -> Unit,
modifier: Modifier = Modifier,
) {
val jsonExportAction =
rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument(JSON_MIME_TYPE)) { uri
->
if (uri == null) {
coroutineScope.launch { snackbarHostState.showSnackbarDismissing("No file selected") }
return@rememberLauncherForActivityResult
}
coroutineScope.launch {
try {
openOutputStream(uri)?.use { stream ->
exportPostsAsJson(stream)
snackbarHostState.showSnackbarDismissing("Successfully exported posts")
Sentry.metrics().count("json_export_success", 1.0)
}
?: run {
snackbarHostState.showSnackbarDismissing("Unable to open output file")
Sentry.metrics().count("json_export_failure", 1.0)
}
} catch (e: Exception) {
snackbarHostState.showSnackbarDismissing("Failed to export posts: ${e.message}")
Sentry.metrics().count("json_export_failure", 1.0)
}
}
}
val htmlExportAction =
rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument(HTML_MIME_TYPE)) { uri
->
if (uri == null) {
coroutineScope.launch { snackbarHostState.showSnackbarDismissing("No file selected") }
return@rememberLauncherForActivityResult
}
coroutineScope.launch {
try {
openOutputStream(uri)?.use { stream ->
exportPostsAsHtml(stream)
snackbarHostState.showSnackbarDismissing("Successfully exported posts")
Sentry.metrics().count("html_export_success", 1.0)
}
?: run {
snackbarHostState.showSnackbarDismissing("Unable to open output file")
Sentry.metrics().count("html_export_failure", 1.0)
}
} catch (e: Exception) {
snackbarHostState.showSnackbarDismissing("Failed to export posts: ${e.message}")
Sentry.metrics().count("html_export_failure", 1.0)
}
}
}
var expanded by remember { mutableStateOf(false) }
OutlinedButton(
onClick = { expanded = true },
shape = MaterialTheme.shapes.extraSmall,
modifier = modifier,
) {
Text(text = stringResource(R.string.export))
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
DropdownMenuItem(
text = { Text(stringResource(R.string.json)) },
onClick = {
expanded = false
jsonExportAction.launch("claw-export.json")
},
leadingIcon = {
Icon(
imageVector = Icons.Filled.Code,
contentDescription = stringResource(R.string.export_as_json),
)
},
)
DropdownMenuItem(
text = { Text(stringResource(R.string.bookmarks)) },
onClick = {
expanded = false
htmlExportAction.launch("claw-export.html")
},
leadingIcon = {
Icon(
imageVector = Icons.Filled.Bookmarks,
contentDescription = stringResource(R.string.export_as_browser_bookmarks),
)
},
)
}
}
}
/** Shows a Snackbar but dismisses any existing ones first. */
private suspend inline fun SnackbarHostState.showSnackbarDismissing(text: String) {
currentSnackbarData?.dismiss()
showSnackbar(text)
}
@ThemePreviews
@Composable
private fun SettingsScreenPreview() {
LobstersTheme {
SettingsScreen(
openLibrariesScreen = {},
openRepository = {},
openTagFiltering = {},
openLoginScreen = {},
onLogout = {},
isLoggedIn = false,
username = null,
snackbarHostState = SnackbarHostState(),
openInputStream = { null },
openOutputStream = { null },
importPosts = { Result.success(Unit) },
exportPostsAsJson = {},
exportPostsAsHtml = {},
contentPadding = PaddingValues(),
savedPostsCount = 42,
)
}
}
@@ -1,51 +0,0 @@
/*
* Copyright © 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.viewmodel
import android.util.Log
import app.cash.sqldelight.coroutines.asFlow
import app.cash.sqldelight.coroutines.mapToList
import dev.msfjarvis.claw.android.BuildConfig
import dev.msfjarvis.claw.core.coroutines.DatabaseReadDispatcher
import dev.msfjarvis.claw.core.coroutines.DatabaseWriteDispatcher
import dev.msfjarvis.claw.database.local.CachedRemotePost
import dev.msfjarvis.claw.database.local.CachedRemotePostQueries
import dev.zacsweers.metro.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
@Inject
class CachedRemotePostsRepository(
private val cachedRemotePostQueries: CachedRemotePostQueries,
@param:DatabaseReadDispatcher private val readDispatcher: CoroutineDispatcher,
@param:DatabaseWriteDispatcher private val writeDispatcher: CoroutineDispatcher,
) {
val cachedPosts = cachedRemotePostQueries.selectAllPosts().asFlow().mapToList(readDispatcher)
fun getRecentPosts(limit: Long) =
cachedRemotePostQueries.selectRecentPosts(limit).asFlow().mapToList(readDispatcher)
suspend fun getCachedPosts(): List<CachedRemotePost> {
return withContext(readDispatcher) { cachedRemotePostQueries.selectAllPosts().executeAsList() }
}
suspend fun savePosts(posts: List<CachedRemotePost>) {
if (BuildConfig.DEBUG) {
Log.d(TAG, "Caching remote posts: ${posts.joinToString(",") { it.shortId }}")
}
withContext(writeDispatcher) {
cachedRemotePostQueries.transaction {
cachedRemotePostQueries.deleteAllPosts()
posts.forEach { post -> cachedRemotePostQueries.insertOrReplacePost(post) }
}
}
}
private companion object {
private const val TAG = "CachedRemotePostsRepository"
}
}
@@ -1,179 +0,0 @@
/*
* Copyright © 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.viewmodel
import android.app.Application
import android.content.Context
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.text.capitalize
import androidx.compose.ui.text.intl.Locale
import androidx.glance.appwidget.updateAll
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.cachedIn
import dev.msfjarvis.claw.android.glance.SavedPostsWidget
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.paging.SearchPagingSource
import dev.msfjarvis.claw.api.LobstersApi
import dev.msfjarvis.claw.api.toPostsResult
import dev.msfjarvis.claw.core.coroutines.IODispatcher
import dev.msfjarvis.claw.core.coroutines.MainDispatcher
import dev.msfjarvis.claw.model.UIPost
import dev.msfjarvis.claw.model.fromSavedPost
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesIntoMap
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.binding
import dev.zacsweers.metrox.viewmodel.ViewModelKey
import java.io.InputStream
import java.io.OutputStream
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@Inject
@ViewModelKey
@ContributesIntoMap(scope = AppScope::class, binding = binding<ViewModel>())
class ClawViewModel(
context: Context,
private val api: LobstersApi,
private val readPostsRepository: ReadPostsRepository,
private val savedPostsRepository: SavedPostsRepository,
private val linkMetadataRepository: LinkMetadataRepository,
private val dataTransferRepository: DataTransferRepository,
private val pagingSourceFactory: LobstersPagingSource.Factory,
private val searchPagingSourceFactory: SearchPagingSource.Factory,
@param:IODispatcher private val ioDispatcher: CoroutineDispatcher,
@param:MainDispatcher private val mainDispatcher: CoroutineDispatcher,
) : AndroidViewModel(context as Application) {
private var _hottestPosts: Flow<PagingData<UIPost>>? = null
val hottestPosts: Flow<PagingData<UIPost>>
get() {
if (_hottestPosts == null) {
_hottestPosts =
Pager(
config = PagingConfig(pageSize = PAGE_SIZE),
initialKey = STARTING_PAGE_INDEX,
pagingSourceFactory = {
pagingSourceFactory.create { page -> api.getHottestPosts(page).toPostsResult() }
},
)
.flow
.cachedIn(viewModelScope)
}
return requireNotNull(_hottestPosts) { "_hottestPosts should be initialized here" }
}
private var _newestPosts: Flow<PagingData<UIPost>>? = null
val newestPosts: Flow<PagingData<UIPost>>
get() {
if (_newestPosts == null) {
_newestPosts =
Pager(
config = PagingConfig(pageSize = PAGE_SIZE),
initialKey = STARTING_PAGE_INDEX,
pagingSourceFactory = {
pagingSourceFactory.create { page -> api.getNewestPosts(page).toPostsResult() }
},
)
.flow
.cachedIn(viewModelScope)
}
return requireNotNull(_newestPosts) { "_newestPosts should be initialized here" }
}
val searchResults =
Pager(
PagingConfig(pageSize = PAGE_SIZE),
initialKey = STARTING_PAGE_INDEX,
pagingSourceFactory = { searchPagingSourceFactory.create { searchQuery } },
)
.flow
val savedPosts = savedPostsRepository.savedPosts.map { it.map(UIPost.Companion::fromSavedPost) }
val savedPostsCount = savedPostsRepository.savedPosts.map { it.size.toLong() }
val savedPostsByMonth
get() =
savedPostsRepository.savedPostsSortedByDate.map { posts ->
posts
.map(UIPost.Companion::fromSavedPost)
.groupBy { post ->
val time = post.createdAt.toLocalDateTime()
"${time.month.name.lowercase().capitalize(Locale.current)} ${time.year}"
}
.toImmutableMap()
}
var searchQuery by mutableStateOf("")
private var _readPosts by mutableStateOf(emptySet<String>())
private var _savedPosts by mutableStateOf(emptySet<String>())
init {
viewModelScope.launch {
savedPosts.collectLatest { _savedPosts = it.map(UIPost::shortId).toSet() }
}
viewModelScope.launch {
readPostsRepository.readPosts.collectLatest { _readPosts = it.toSet() }
}
}
fun toggleSave(post: UIPost) {
viewModelScope.launch {
savedPostsRepository.toggleSave(post)
withContext(mainDispatcher) { SavedPostsWidget().updateAll(getApplication()) }
}
}
fun isPostRead(post: UIPost): Boolean {
return _readPosts.contains(post.shortId)
}
fun isPostSaved(post: UIPost): Boolean {
return _savedPosts.contains(post.shortId)
}
suspend fun getLinkMetadata(url: String) =
withContext(ioDispatcher) { linkMetadataRepository.getLinkMetadata(url) }
suspend fun importPosts(input: InputStream): Result<Unit> =
dataTransferRepository.importPosts(input)
suspend fun exportPostsAsJson(output: OutputStream) =
dataTransferRepository.exportPostsAsJson(output)
suspend fun exportPostsAsHtml(output: OutputStream) =
dataTransferRepository.exportPostsAsHTML(output)
fun markPostAsRead(postId: String) {
viewModelScope.launch { readPostsRepository.markRead(postId) }
}
/**
* Parses a given [String] into a [LocalDateTime]. This method is only intended to be used for
* dates in the format returned by the Lobsters API, and is not a general purpose parsing
* solution.
*/
private fun String.toLocalDateTime(): LocalDateTime {
if (isEmpty()) return LocalDateTime.now(ZoneId.systemDefault())
return LocalDateTime.from(DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(this))
}
}
@@ -1,93 +0,0 @@
/*
* Copyright © 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.viewmodel
import dev.msfjarvis.claw.core.coroutines.IODispatcher
import dev.msfjarvis.claw.database.SavedPostSerializer
import dev.msfjarvis.claw.database.local.SavedPost
import dev.zacsweers.metro.Inject
import java.io.InputStream
import java.io.OutputStream
import java.time.Instant
import java.time.format.DateTimeFormatter
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream
@Inject
@OptIn(ExperimentalSerializationApi::class)
class DataTransferRepository(
private val json: Json,
private val savedPostsRepository: SavedPostsRepository,
@param:IODispatcher private val ioDispatcher: CoroutineDispatcher,
) {
private val serializer = ListSerializer(SavedPostSerializer)
suspend fun importPosts(input: InputStream): Result<Unit> {
return try {
val posts: List<SavedPost> =
withContext(ioDispatcher) { json.decodeFromStream(serializer, input) }
savedPostsRepository.savePosts(posts)
Result.success(Unit)
} catch (t: Throwable) {
Result.failure(t)
}
}
suspend fun exportPostsAsJson(output: OutputStream) {
val posts = savedPostsRepository.savedPosts.first()
withContext(ioDispatcher) { json.encodeToStream(serializer, posts, output) }
}
suspend fun exportPostsAsHTML(output: OutputStream) {
fun computeTimestamp(post: SavedPost): Long {
val temporal = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(post.createdAt)
val instant = Instant.from(temporal)
return instant.toEpochMilli()
}
val posts = savedPostsRepository.savedPosts.first()
val header =
"""
<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
It will be read and overwritten.
DO NOT EDIT! -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
"""
.trimIndent()
val html = buildString {
append(header)
append("<DD><p>\n")
for (post in posts) {
append(
"""
<DT><A HREF="${post.url.ifEmpty { post.commentsUrl }}" ADD_DATE="${computeTimestamp(post)}" PRIVATE="0" TAGS="${post.tags.joinToString(",")}">${post.title}</A>
<DD>${post.title}
"""
.trimIndent()
)
}
append(
"""
<DT><A HREF="https://example.com/" ADD_DATE="0" PRIVATE="0" TAGS="delete,me,pls">Padding post</A>
<DD>Linkding ignores the last entry so this pads the difference for imports
"""
.trimIndent()
)
append("</DD></p>\n")
}
withContext(ioDispatcher) { output.bufferedWriter().use { writer -> writer.write(html) } }
}
}
@@ -1,25 +0,0 @@
/*
* Copyright © 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.viewmodel
import dev.msfjarvis.claw.core.coroutines.IODispatcher
import dev.msfjarvis.claw.model.LinkMetadata
import dev.zacsweers.metro.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import me.saket.unfurl.Unfurler
@Inject
class LinkMetadataRepository(
private val unfurler: Unfurler,
@param:IODispatcher private val dispatcher: CoroutineDispatcher,
) {
suspend fun getLinkMetadata(url: String): LinkMetadata {
val result = withContext(dispatcher) { unfurler.unfurl(url) }
return LinkMetadata(url, result?.favicon?.toString())
}
}
@@ -1,29 +0,0 @@
/*
* Copyright © 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.viewmodel
import app.cash.sqldelight.coroutines.asFlow
import app.cash.sqldelight.coroutines.mapToList
import dev.msfjarvis.claw.core.coroutines.DatabaseReadDispatcher
import dev.msfjarvis.claw.core.coroutines.DatabaseWriteDispatcher
import dev.msfjarvis.claw.database.local.ReadPostsQueries
import dev.zacsweers.metro.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
@Inject
class ReadPostsRepository(
private val readPostsQueries: ReadPostsQueries,
@param:DatabaseReadDispatcher private val readDispatcher: CoroutineDispatcher,
@param:DatabaseWriteDispatcher private val writeDispatcher: CoroutineDispatcher,
) {
val readPosts = readPostsQueries.selectAllPosts().asFlow().mapToList(readDispatcher)
suspend fun markRead(postId: String) {
withContext(writeDispatcher) { readPostsQueries.markRead(postId) }
}
}
@@ -1,69 +0,0 @@
/*
* Copyright © 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.viewmodel
import android.util.Log
import app.cash.sqldelight.coroutines.asFlow
import app.cash.sqldelight.coroutines.mapToList
import dev.msfjarvis.claw.android.BuildConfig
import dev.msfjarvis.claw.core.coroutines.DatabaseReadDispatcher
import dev.msfjarvis.claw.core.coroutines.DatabaseWriteDispatcher
import dev.msfjarvis.claw.database.local.SavedPost
import dev.msfjarvis.claw.database.local.SavedPostQueries
import dev.msfjarvis.claw.model.UIPost
import dev.msfjarvis.claw.model.toSavedPost
import dev.zacsweers.metro.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
@Inject
class SavedPostsRepository(
private val savedPostQueries: SavedPostQueries,
@param:DatabaseReadDispatcher private val readDispatcher: CoroutineDispatcher,
@param:DatabaseWriteDispatcher private val writeDispatcher: CoroutineDispatcher,
) {
val savedPosts = savedPostQueries.selectAllPosts().asFlow().mapToList(readDispatcher)
val savedPostsSortedByDate =
savedPostQueries.selectAllPostsSortedByDate().asFlow().mapToList(readDispatcher)
fun getPostsFromLastNDays(days: Long) =
savedPostQueries.selectPostsFromLastNDays(days.toString()).asFlow().mapToList(readDispatcher)
fun getRecentPosts(limit: Long) =
savedPostQueries.selectRecentPosts(limit).asFlow().mapToList(readDispatcher)
suspend fun toggleSave(post: UIPost) {
val exists =
withContext(readDispatcher) { savedPostQueries.postExists(post.shortId).executeAsOne() }
if (exists) {
if (BuildConfig.DEBUG) {
Log.d(TAG, "Removing post: ${post.shortId}")
}
withContext(writeDispatcher) { savedPostQueries.deletePost(post.shortId) }
} else {
if (BuildConfig.DEBUG) {
Log.d(TAG, "Saving post: ${post.shortId}")
}
withContext(writeDispatcher) { savedPostQueries.insertOrReplacePost(post.toSavedPost()) }
}
}
suspend fun savePosts(posts: List<SavedPost>) {
if (BuildConfig.DEBUG) {
Log.d(TAG, "Saving posts: ${posts.joinToString(",") { it.shortId }}")
}
withContext(writeDispatcher) {
savedPostQueries.transaction {
posts.forEach { post -> savedPostQueries.insertOrReplacePost(post) }
}
}
}
private companion object {
private const val TAG = "SavedPostsRepository"
}
}
@@ -1,54 +0,0 @@
/*
* Copyright © 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.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dev.msfjarvis.claw.core.network.SessionCookieStore
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesIntoMap
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.binding
import dev.zacsweers.metrox.viewmodel.ViewModelKey
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
@Inject
@ViewModelKey
@ContributesIntoMap(scope = AppScope::class, binding = binding<ViewModel>())
class SettingsViewModel(
private val sessionCookieStore: SessionCookieStore,
private val webViewCookieStore: WebViewCookieStore,
) : ViewModel() {
val isLoggedIn =
sessionCookieStore
.isLoggedIn()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = false,
)
val username =
sessionCookieStore
.username()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = null,
)
fun logout() {
sessionCookieStore.clear()
webViewCookieStore.clearLobstersCookies()
}
fun saveCookie(cookie: String, username: String) {
sessionCookieStore.set(cookie, username)
}
}
@@ -1,25 +0,0 @@
/*
* Copyright © 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.viewmodel
import android.webkit.CookieManager
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import dev.zacsweers.metro.Inject
interface WebViewCookieStore {
fun clearLobstersCookies()
}
@Inject
@ContributesBinding(AppScope::class)
class AndroidWebViewCookieStore : WebViewCookieStore {
override fun clearLobstersCookies() {
CookieManager.getInstance().removeAllCookies(null)
CookieManager.getInstance().flush()
}
}
@@ -1,90 +0,0 @@
/*
* Copyright © 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.work
import android.content.Context
import androidx.glance.appwidget.updateAll
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.slack.eithernet.ApiResult.Success
import dev.msfjarvis.claw.android.glance.SavedPostsWidget
import dev.msfjarvis.claw.android.viewmodel.SavedPostsRepository
import dev.msfjarvis.claw.api.LobstersApi
import dev.msfjarvis.claw.core.injection.InjectedWorkerFactory
import dev.msfjarvis.claw.core.injection.WorkerKey
import dev.msfjarvis.claw.database.local.SavedPost
import dev.msfjarvis.claw.model.toSavedPost
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.AssistedInject
import dev.zacsweers.metro.ContributesIntoMap
import dev.zacsweers.metro.binding
import kotlin.random.Random
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
/**
* WorkManager-backed [CoroutineWorker] that gets all the posts from [SavedPostsRepository] that
* were created in the last 30 days, fetches their newest state using the [LobstersApi] and then
* writes them back to the database. API calls are staggered with a configurable delay to avoid rate
* limiting. This allows saved posts to stay up-to-date with current comment counts and other
* metadata changes.
*/
@AssistedInject
class SavedPostUpdaterWorker(
context: Context,
@Assisted params: WorkerParameters,
private val savedPostsRepository: SavedPostsRepository,
private val lobstersApi: LobstersApi,
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
val postsToUpdate = savedPostsRepository.getPostsFromLastNDays(DAYS_TO_UPDATE).first()
if (postsToUpdate.isEmpty()) {
return Result.success()
}
val updatedPosts = mutableListOf<SavedPost>()
for ((index, post) in postsToUpdate.withIndex()) {
when (val result = lobstersApi.getPostDetails(post.shortId)) {
is Success -> {
updatedPosts.add(result.value.toSavedPost())
}
else -> {}
}
if (index < postsToUpdate.lastIndex) {
// Add a random delay between API calls to avoid rate limiting
val delayMs = Random.nextLong(MIN_DELAY_MS, MAX_DELAY_MS)
delay(delayMs)
}
}
if (updatedPosts.isNotEmpty()) {
savedPostsRepository.savePosts(updatedPosts)
SavedPostsWidget().updateAll(applicationContext)
}
return Result.success()
}
private companion object {
const val DAYS_TO_UPDATE = 30L
const val MIN_DELAY_MS = 500L
const val MAX_DELAY_MS = 2000L
}
@WorkerKey(SavedPostUpdaterWorker::class)
@ContributesIntoMap(
AppScope::class,
binding = binding<InjectedWorkerFactory.WorkerInstanceFactory<*>>(),
)
@AssistedFactory
abstract class Factory : InjectedWorkerFactory.WorkerInstanceFactory<SavedPostUpdaterWorker>
}
@@ -1,40 +0,0 @@
/*
* Copyright © 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.work
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import dev.msfjarvis.claw.common.tags.TagBlockRepository
import dev.msfjarvis.claw.core.injection.InjectedWorkerFactory
import dev.msfjarvis.claw.core.injection.WorkerKey
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedFactory
import dev.zacsweers.metro.AssistedInject
import dev.zacsweers.metro.ContributesIntoMap
import dev.zacsweers.metro.binding
@AssistedInject
class TagExpirationCleanupWorker(
context: Context,
@Assisted params: WorkerParameters,
private val tagBlockRepository: TagBlockRepository,
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
tagBlockRepository.removeExpiredTags()
return Result.success()
}
@WorkerKey(TagExpirationCleanupWorker::class)
@ContributesIntoMap(
AppScope::class,
binding = binding<InjectedWorkerFactory.WorkerInstanceFactory<*>>(),
)
@AssistedFactory
abstract class Factory : InjectedWorkerFactory.WorkerInstanceFactory<TagExpirationCleanupWorker>
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

@@ -1,17 +0,0 @@
<!--
~ Copyright © 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?android:attr/colorControlNormal"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:pathData="M21.99,4c0,-1.1 -0.89,-2 -1.99,-2L4,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14l4,4 -0.01,-18zM20,4v13.17L18.83,16L4,16L4,4h16zM6,12h12v2L6,14zM6,9h12v2L6,11zM6,6h12v2L6,8z"/>
</vector>
@@ -1,20 +0,0 @@
<!--
~ Copyright © 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="1080"
android:viewportHeight="1080">
<group android:scaleX="0.7"
android:scaleY="0.7"
android:translateX="162"
android:translateY="162">
<path
android:pathData="M431.3,341.2C435.9,337 580.3,236 710.5,223.8C747.3,220.3 790.7,254.7 833.2,312.5C875.9,370.7 892.9,433.4 875.9,443C863.2,450.2 838.6,407.4 758.8,396.3C743.4,394.2 730.4,393.4 719.4,393.5C738.5,423.4 745.6,451.1 737.2,458C733.1,461.4 673.6,438.7 612.9,436.5C570.1,435 535.2,440.9 506.7,451.3C496.9,454.9 486,453.8 477.2,448.3C444.5,427.9 424.2,406.2 418.4,376.2C415.8,362.8 420.9,349.4 431.3,341.2ZM274.7,394.8C285.8,370.1 312.2,325.9 346.3,305.4C383.6,282.9 335,390.3 427.3,489.8C519.5,589.3 586.5,623.8 655.1,581.5C723.7,539.2 746.5,518.1 753,518.7C764,519.6 772.9,536.1 764.4,589.5C763,598.2 761.3,606.5 759.3,614.2C828.7,575 871.7,516.3 889,520C912,524.8 918.3,617.8 852.1,721C811.8,784 768.8,830.7 741.4,857.5C724.3,874 699.7,880 677,873.1C512,822.6 315.5,733.8 202.9,652C193.8,645.2 186.8,635.9 183,625.1C171.8,593.7 161.3,548.1 168.5,495.3C179.5,414.2 222.7,352.9 245.2,335.9C261.9,323.2 267.1,362.4 274.7,394.8Z"
android:fillColor="#FF4529"/>
</group>
</vector>
@@ -1,11 +0,0 @@
<!--
~ Copyright © 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.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>
@@ -1,9 +0,0 @@
<!--
~ Copyright © 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.
-->
<resources>
<color name="ic_launcher_background">#540C00</color>
</resources>
-44
View File
@@ -1,44 +0,0 @@
<!--
~ Copyright © 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.
-->
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name">Claw</string>
<string name="saved_posts_widget_description">View your saved Lobsters posts</string>
<string name="hottest_posts_widget_description">View the hottest posts on Lobsters</string>
<string name="saved_posts">Saved posts</string>
<string name="see_more_posts">See more posts</string>
<plurals name="comments">
<item quantity="one">%1$d comment</item>
<item quantity="other">%1$d comments</item>
</plurals>
<string name="no_post_selected">No post selected</string>
<string name="data_management">Data Management</string>
<string name="data_transfer">Data transfer</string>
<string name="import_and_export_your_saved_posts">Import and export your saved posts</string>
<string name="app_information">App Information</string>
<string name="version">Version</string>
<string name="version_placeholder">%1$s (%2$s)</string>
<string name="posts_saved_locally">%1$s posts saved locally</string>
<string name="tag_filtering">Tag filtering</string>
<string name="filter_posts_by_tags">Filter posts by tags</string>
<string name="about">About</string>
<string name="libraries">Libraries</string>
<string name="view_open_source_libraries_used_in_this_">View open source libraries used in this app</string>
<string name="source_code">Source code</string>
<string name="view_the_source_code_on_github">View the source code on GitHub</string>
<string name="import_action">Import</string>
<string name="export">Export</string>
<string name="json">JSON</string>
<string name="export_as_json">Export as JSON</string>
<string name="bookmarks">Bookmarks</string>
<string name="export_as_browser_bookmarks">Export as browser bookmarks</string>
<string name="go_back_to_previous_screen">Go back to previous screen</string>
<string name="the_app_icon_for_claw">The app icon for Claw</string>
<string name="search_posts">Search posts</string>
<string name="settings">Settings</string>
<string name="empty_inbox_icon">Empty inbox icon</string>
<string name="no_saved_posts">No saved posts</string>
</resources>
-17
View File
@@ -1,17 +0,0 @@
<!--
~ Copyright © 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.
-->
<resources>
<style name="Theme.Claw.SplashScreen" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/ic_launcher_background</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_launcher_foreground</item>
<item name="windowSplashScreenAnimationDuration">1000</item>
<item name="postSplashScreenTheme">@style/Theme.Claw</item>
</style>
<style name="Theme.Claw" parent="android:Theme.Material.NoActionBar" />
</resources>
@@ -1,19 +0,0 @@
<!--
~ Copyright © 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.
-->
<data-extraction-rules>
<cloud-backup>
<include
domain="database"
path="." />
</cloud-backup>
<device-transfer>
<include
domain="database"
path="." />
</device-transfer>
</data-extraction-rules>
@@ -1,11 +0,0 @@
<!--
~ Copyright © 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.
-->
<full-backup-content>
<include
domain="database"
path="." />
</full-backup-content>
@@ -1,17 +0,0 @@
<!--
~ Copyright © 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.
-->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/hottest_posts_widget_description"
android:previewImage="@drawable/hottest_posts_widget_preview"
android:initialLayout="@layout/glance_default_loading_layout"
android:minWidth="349dp"
android:minHeight="110dp"
android:minResizeWidth="349dp"
android:minResizeHeight="110dp"
android:resizeMode="vertical|horizontal"
android:updatePeriodMillis="1800000"
android:widgetCategory="home_screen" />
@@ -1,17 +0,0 @@
<!--
~ Copyright © 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.
-->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/saved_posts_widget_description"
android:previewImage="@drawable/saved_posts_widget_preview"
android:initialLayout="@layout/glance_default_loading_layout"
android:minWidth="349dp"
android:minHeight="110dp"
android:minResizeWidth="349dp"
android:minResizeHeight="110dp"
android:resizeMode="vertical|horizontal"
android:updatePeriodMillis="1800000"
android:widgetCategory="home_screen" />

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