Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2eb0a6a31c | ||
|
|
2d44ad2055 | ||
|
|
8af4bddfe1 | ||
|
|
0be0952e04 | ||
|
|
8b2ba790bc | ||
|
|
6ed377af94 | ||
|
|
cfc479a9ee | ||
|
|
7552fb1c36 | ||
|
|
bcd8a412b5 | ||
|
|
defe4914df | ||
|
|
621e93939b | ||
|
|
247949f92e | ||
|
|
5700b2150e | ||
|
|
530ad44b27 | ||
|
|
51c7306cfa | ||
|
|
79778466b9 | ||
|
|
7d7989d5c0 | ||
|
|
923361ee1b | ||
|
|
6adf339e08 | ||
|
|
9e37246810 | ||
|
|
7be1157f22 | ||
|
|
cd7d82d378 | ||
|
|
b30425d97d | ||
|
|
c89589adcb | ||
|
|
6523b85f07 | ||
|
|
33f8b73938 | ||
|
|
bb47119c84 | ||
|
|
6adb45ff7c | ||
|
|
a87b1cf38c | ||
|
|
b112585533 | ||
|
|
5750bbebd3 | ||
|
|
067b762a31 | ||
|
|
658a2f36fd | ||
|
|
6a4d0c2d3a | ||
|
|
5319669a8e | ||
|
|
720f946f41 | ||
|
|
a1390a0f05 | ||
|
|
655b20e496 | ||
|
|
8508a8646f | ||
|
|
790874e1a7 | ||
|
|
0ac7c9876e | ||
|
|
daba852e6d | ||
|
|
47f7f97aa2 | ||
|
|
80bfb34e5b | ||
|
|
42310a6a33 | ||
|
|
c554459191 | ||
|
|
1560574e15 | ||
|
|
a1f3950042 | ||
|
|
d7bafbddbc | ||
|
|
ef5e4a952e | ||
|
|
5711bab007 | ||
|
|
b1fca935a8 | ||
|
|
2780f7b204 | ||
|
|
22231606cb | ||
|
|
c797853055 | ||
|
|
b003f0c283 | ||
|
|
cc55687292 | ||
|
|
531234e036 | ||
|
|
ed5bbdd571 | ||
|
|
23cd91a97a | ||
|
|
6761659c7f | ||
|
|
6bd3dd81f0 | ||
|
|
765a7f6f0b | ||
|
|
46f4d9a397 | ||
|
|
2cc9e38e77 | ||
|
|
dc954c7ba5 | ||
|
|
15dc9c3dc7 | ||
|
|
a4be2d4bf2 | ||
|
|
5329b6e3aa | ||
|
|
204a3c0bb5 | ||
|
|
69eed8a23a | ||
|
|
3c741a7fe1 | ||
|
|
ccfcd48438 | ||
|
|
79aba9a187 | ||
|
|
4337a19abc | ||
|
|
a53e9f0065 | ||
|
|
e6ca8365ca | ||
|
|
f1805396e5 | ||
|
|
6d70d997a3 | ||
|
|
4e872bfa4a | ||
|
|
fdc9ae8968 | ||
|
|
f31debc685 | ||
|
|
74c265538d | ||
|
|
4607a68de0 | ||
|
|
8652d4ceaa | ||
|
|
484fac5779 | ||
|
|
0d7a7a0e22 | ||
|
|
8470e95f6b | ||
|
|
ad72c80130 | ||
|
|
be7840125f | ||
|
|
cf3549f09a | ||
|
|
6ee00f6a27 | ||
|
|
d963fd8ae4 | ||
|
|
32ca4f2115 | ||
|
|
95cbe8dfe4 | ||
|
|
7e7e00f06e | ||
|
|
8eee42ae0f | ||
|
|
8060c347cc | ||
|
|
8851071d58 | ||
|
|
54c4b01411 | ||
|
|
176bc9b022 | ||
|
|
245130ef20 | ||
|
|
394558d634 | ||
|
|
0904740980 | ||
|
|
4212ca41cf | ||
|
|
33b8503f19 | ||
|
|
eb8be2f909 | ||
|
|
e1d16e9f36 | ||
|
|
dd76c7bed7 | ||
|
|
a1ab32c43e | ||
|
|
069d7e5e05 | ||
|
|
8f927613f3 | ||
|
|
f5de95af03 | ||
|
|
535c8a5808 | ||
|
|
02416a3606 | ||
|
|
293d12454f | ||
|
|
d178bebebd | ||
|
|
38f1fdb6a3 | ||
|
|
46bfafd681 | ||
|
|
999f2dfb01 | ||
|
|
52ac8346b3 | ||
|
|
1d761b1f6e | ||
|
|
16404e6654 | ||
|
|
0f684a3728 | ||
|
|
2685111c70 | ||
|
|
b867804bdb | ||
|
|
37bb6e1d36 | ||
|
|
78c321d269 | ||
|
|
cab3f74ce0 | ||
|
|
f0cff549b4 | ||
|
|
5ab0774689 | ||
|
|
8835e07ac6 | ||
|
|
85de847692 | ||
|
|
ccc14dee4f |
+12
-1
@@ -2,6 +2,7 @@
|
||||
"extends": [
|
||||
"config:base",
|
||||
":dependencyDashboard",
|
||||
"helpers:pinGitHubActionDigests",
|
||||
"github>msfjarvis/shared-workflows//renovate/automerge"
|
||||
],
|
||||
"packageRules": [
|
||||
@@ -19,13 +20,23 @@
|
||||
"regexManagers": [
|
||||
{
|
||||
"fileMatch": [
|
||||
".kt$"
|
||||
"build-logic/kotlin-plugins/src/main/kotlin/dev/msfjarvis/aps/gradle/SpotlessPlugin.kt"
|
||||
],
|
||||
"matchStrings": [
|
||||
"KTFMT_VERSION = \"(?<currentValue>.*)\""
|
||||
],
|
||||
"datasourceTemplate": "maven",
|
||||
"depNameTemplate": "com.facebook:ktfmt"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"build-logic/kotlin-plugins/src/main/kotlin/dev/msfjarvis/aps/gradle/Detekt.kt"
|
||||
],
|
||||
"matchStrings": [
|
||||
"TWITTER_RULES_VERSION = \"(?<currentValue>.*)\""
|
||||
],
|
||||
"datasourceTemplate": "maven",
|
||||
"depNameTemplate": "com.twitter.compose.rules:detekt"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -15,23 +15,31 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3.0.2
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3.1.0
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v3.5.1
|
||||
uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3.6.0
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 18
|
||||
|
||||
- name: Run unit tests
|
||||
uses: gradle/gradle-build-action@v2.3.2
|
||||
uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2.3.3
|
||||
with:
|
||||
arguments: check -PslimTests
|
||||
gradle-home-cache-cleanup: true
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
- name: Run Detekt
|
||||
uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2.3.3
|
||||
with:
|
||||
arguments: detektMain
|
||||
gradle-home-cache-cleanup: true
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
- name: (Fail-only) Upload test report
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v3.1.0
|
||||
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3.1.1
|
||||
with:
|
||||
name: Test report
|
||||
path: android/build/reports
|
||||
@@ -42,10 +50,10 @@ jobs:
|
||||
needs: [ "check" ]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3.0.2
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3.1.0
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v3.5.1
|
||||
uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3.6.0
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 18
|
||||
@@ -56,10 +64,11 @@ jobs:
|
||||
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
|
||||
|
||||
- name: Build release app
|
||||
uses: gradle/gradle-build-action@v2.3.2
|
||||
uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2.3.3
|
||||
with:
|
||||
arguments: collectReleaseApks
|
||||
gradle-home-cache-cleanup: true
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
- name: Clean secrets
|
||||
run: scripts/signing-cleanup.sh
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
name: Release to Google Play
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish-google-play-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3.1.0
|
||||
with:
|
||||
depth: 0
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3.6.0
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 18
|
||||
|
||||
- name: Set up Git author
|
||||
shell: bash
|
||||
run: |
|
||||
git config user.name "GitHub Actions"
|
||||
git config user.email noreply@github.com
|
||||
|
||||
- name: Setup Gradle caching
|
||||
uses: gradle/gradle-build-action@3fbe033aaae657f011f88f29be9e65ed26bd29ef # tag=v2.3.3
|
||||
with:
|
||||
gradle-home-cache-cleanup: true
|
||||
cache-read-only: true
|
||||
|
||||
- name: Decrypt secrets
|
||||
run: scripts/signing-setup.sh "$ENCRYPT_KEY"
|
||||
env:
|
||||
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
|
||||
|
||||
- name: Prepare release assets
|
||||
shell: bash
|
||||
run: |
|
||||
# Remove the `-SNAPSHOT` suffix
|
||||
./gradlew -q clearPreRelease
|
||||
git commit -am 'feat(release): bump version'
|
||||
|
||||
# Build the release assets
|
||||
./gradlew collectReleaseApks collectReleaseBundle
|
||||
|
||||
- name: Clean secrets
|
||||
run: scripts/signing-cleanup.sh
|
||||
|
||||
- name: Publish bundle to Google Play
|
||||
uses: r0adkll/upload-google-play@78c9e796b1035c94169c101d8e46cb880194bfc3 # tag=v1.0.19
|
||||
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
|
||||
|
||||
- name: Post-release work
|
||||
shell: bash
|
||||
run: |
|
||||
# Tag the current version
|
||||
VERSION="$(tail -n1 android/version.properties | cut -d = -f 2)"
|
||||
git tag "v${VERSION}" -F distribution/whatsnew/whatsnew-en-GB
|
||||
|
||||
# Push everything to GitHub
|
||||
git push origin --all
|
||||
|
||||
# Create a GitHub release
|
||||
gh release create "v${VERSION}" -F distribution/whatsnew/whatsnew-en-GB --title "v${VERSION}" ./android/apk/*.apk ./android/bundle/*.aab
|
||||
|
||||
# Start the next development iteration
|
||||
./gradlew -q bumpSnapshot
|
||||
truncate -s 0 distribution/whatsnew/whatsnew-en-GB # Clear changelog
|
||||
git commit -am 'feat(release): start next development iteration'
|
||||
git push origin main
|
||||
@@ -0,0 +1,23 @@
|
||||
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.
|
||||
@@ -16,30 +16,4 @@ Snapshots from the development branch can be obtained [here](https://github.com/
|
||||
|
||||
## License
|
||||
|
||||
```
|
||||
Copyright (c) 2021 Harsh Shandilya
|
||||
|
||||
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.
|
||||
```
|
||||
See [LICENSE](LICENSE)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
/apk/
|
||||
/bundle/
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
|
||||
|
||||
import dagger.hilt.android.plugin.HiltExtension
|
||||
@@ -39,6 +45,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
annotationProcessor(libs.androidx.hilt.compiler)
|
||||
annotationProcessor(libs.dagger.hilt.compiler)
|
||||
implementation(projects.api)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
}
|
||||
|
||||
-keepclassmembers class * implements android.os.Parcelable {
|
||||
public static final ** CREATOR;
|
||||
public static final ** CREATOR;
|
||||
}
|
||||
|
||||
-keep class androidx.annotation.Keep
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Claw.SplashScreen"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
tools:targetApi="s">
|
||||
<profileable
|
||||
android:shell="true"
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android
|
||||
|
||||
import android.app.Application
|
||||
@@ -34,15 +40,20 @@ class ClawApplication : Application(), Configuration.Provider, ImageLoaderFactor
|
||||
|
||||
override fun newImageLoader(): ImageLoader {
|
||||
return ImageLoader.Builder(this)
|
||||
.memoryCache { MemoryCache.Builder(this).maxSizePercent(0.25).build() }
|
||||
.memoryCache { MemoryCache.Builder(this).maxSizePercent(MEMORY_CACHE_RATIO).build() }
|
||||
.diskCache {
|
||||
DiskCache.Builder()
|
||||
.directory(cacheDir.resolve("image_cache"))
|
||||
.maxSizeBytes(25L * 1024 * 1024) // 25 MB
|
||||
.maxSizeBytes(DISK_CACHE_MAX_SIZE)
|
||||
.build()
|
||||
}
|
||||
// Show a short crossfade when loading images asynchronously.
|
||||
.crossfade(true)
|
||||
.build()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val MEMORY_CACHE_RATIO = 0.25
|
||||
private const val DISK_CACHE_MAX_SIZE = 25L * 1024 * 1024 // 25 MB
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android
|
||||
|
||||
import android.app.assist.AssistContent
|
||||
@@ -30,10 +36,14 @@ class MainActivity : ComponentActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
installSplashScreen()
|
||||
setContent {
|
||||
LobstersApp(urlLauncher = urlLauncher, htmlConverter = htmlConverter) { url -> webUri = url }
|
||||
LobstersApp(
|
||||
urlLauncher = urlLauncher,
|
||||
htmlConverter = htmlConverter,
|
||||
setWebUri = { url -> webUri = url },
|
||||
)
|
||||
}
|
||||
val postUpdateWorkRequest =
|
||||
PeriodicWorkRequestBuilder<SavedPostUpdaterWorker>(24, TimeUnit.HOURS)
|
||||
PeriodicWorkRequestBuilder<SavedPostUpdaterWorker>(POST_REFRESH_PERIOD, TimeUnit.HOURS)
|
||||
.setConstraints(
|
||||
Constraints.Builder()
|
||||
.setRequiresCharging(false)
|
||||
@@ -61,4 +71,8 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val POST_REFRESH_PERIOD = 24L // 24 hours
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.injection
|
||||
|
||||
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||
import com.slack.eithernet.ApiResultCallAdapterFactory
|
||||
import com.slack.eithernet.ApiResultConverterFactory
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
@@ -26,7 +34,9 @@ object ApiModule {
|
||||
return Retrofit.Builder()
|
||||
.client(client)
|
||||
.baseUrl(LobstersApi.BASE_URL)
|
||||
.addConverterFactory(ApiResultConverterFactory)
|
||||
.addConverterFactory(json.asConverterFactory(contentType))
|
||||
.addCallAdapterFactory(ApiResultCallAdapterFactory)
|
||||
.build()
|
||||
}
|
||||
|
||||
|
||||
+8
-9
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.injection
|
||||
|
||||
import dagger.Binds
|
||||
@@ -7,14 +13,13 @@ import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import dev.msfjarvis.claw.util.coroutines.DefaultDispatcherProvider
|
||||
import dev.msfjarvis.claw.util.coroutines.DispatcherProvider
|
||||
import javax.inject.Qualifier
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
abstract class CoroutineDispatcherModule {
|
||||
interface CoroutineDispatcherModule {
|
||||
|
||||
@Binds abstract fun DefaultDispatcherProvider.bind(): DispatcherProvider
|
||||
@Binds fun DefaultDispatcherProvider.bind(): DispatcherProvider
|
||||
|
||||
companion object {
|
||||
@[Provides IODispatcher]
|
||||
@@ -33,9 +38,3 @@ abstract class CoroutineDispatcherModule {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class DatabaseDispatcher
|
||||
|
||||
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class MainDispatcher
|
||||
|
||||
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class IODispatcher
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.injection
|
||||
|
||||
import android.content.Context
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021 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 dagger.Module
|
||||
|
||||
+6
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.injection
|
||||
|
||||
import com.chimbori.crux.Crux
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.injection
|
||||
|
||||
import android.content.Context
|
||||
@@ -21,24 +27,26 @@ import okhttp3.logging.HttpLoggingInterceptor
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
abstract class OkHttpModule {
|
||||
interface OkHttpModule {
|
||||
|
||||
@Binds abstract fun NapierLogger.bindLogger(): HttpLoggingInterceptor.Logger
|
||||
@Binds fun NapierLogger.bindLogger(): HttpLoggingInterceptor.Logger
|
||||
|
||||
@Binds @IntoSet abstract fun UserAgentInterceptor.bindUAInterceptor(): Interceptor
|
||||
@Binds @IntoSet fun UserAgentInterceptor.bindUAInterceptor(): Interceptor
|
||||
|
||||
companion object {
|
||||
private const val CACHE_SIZE_MB = 10L * 1024 * 1024
|
||||
private const val THREAD_STATS_TAG = 0x000090000
|
||||
|
||||
@Provides
|
||||
fun provideCache(@ApplicationContext context: Context): Cache {
|
||||
return Cache(context.cacheDir, 10 * 1024 * 1024)
|
||||
return Cache(context.cacheDir, CACHE_SIZE_MB)
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideSocketFactory(): SocketFactory {
|
||||
return object : DelegatingSocketFactory(getDefault()) {
|
||||
override fun configureSocket(socket: Socket): Socket {
|
||||
TrafficStats.setThreadStatsTag(0x000090000)
|
||||
TrafficStats.setThreadStatsTag(THREAD_STATS_TAG)
|
||||
return super.configureSocket(socket)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021 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
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.injection
|
||||
|
||||
import javax.inject.Qualifier
|
||||
|
||||
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class DatabaseDispatcher
|
||||
|
||||
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class MainDispatcher
|
||||
|
||||
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class IODispatcher
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.network
|
||||
|
||||
import java.net.InetAddress
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.network
|
||||
|
||||
import io.github.aakira.napier.Napier
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.network
|
||||
|
||||
import dev.msfjarvis.claw.android.BuildConfig
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.paging
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import com.slack.eithernet.ApiResult.Failure
|
||||
import com.slack.eithernet.ApiResult.Success
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import dev.msfjarvis.claw.android.injection.IODispatcher
|
||||
import dev.msfjarvis.claw.model.LobstersPost
|
||||
import java.io.IOException
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@@ -18,17 +27,18 @@ constructor(
|
||||
) : PagingSource<Int, LobstersPost>() {
|
||||
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, LobstersPost> {
|
||||
return try {
|
||||
val page = params.key ?: 1
|
||||
val posts = withContext(ioDispatcher) { remoteFetcher.getItemsAtPage(page) }
|
||||
|
||||
LoadResult.Page(
|
||||
data = posts,
|
||||
prevKey = if (page == 1) null else page - 1,
|
||||
nextKey = page.plus(1)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
LoadResult.Error(e)
|
||||
val page = params.key ?: 1
|
||||
return when (val result = withContext(ioDispatcher) { remoteFetcher.getItemsAtPage(page) }) {
|
||||
is Success ->
|
||||
LoadResult.Page(
|
||||
data = result.value,
|
||||
prevKey = if (page == 1) null else page - 1,
|
||||
nextKey = page.plus(1)
|
||||
)
|
||||
is Failure.NetworkFailure -> LoadResult.Error(result.error)
|
||||
is Failure.UnknownFailure -> LoadResult.Error(result.error)
|
||||
is Failure.HttpFailure,
|
||||
is Failure.ApiFailure -> LoadResult.Error(IOException("API returned an invalid response"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.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): List<T>
|
||||
suspend fun getItemsAtPage(page: Int): ApiResult<List<T>, Unit>
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.ui
|
||||
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
@@ -38,7 +44,6 @@ import dev.msfjarvis.claw.android.viewmodel.ClawViewModel
|
||||
import dev.msfjarvis.claw.api.LobstersApi
|
||||
import dev.msfjarvis.claw.common.comments.CommentsPage
|
||||
import dev.msfjarvis.claw.common.comments.HTMLConverter
|
||||
import dev.msfjarvis.claw.common.comments.LocalHTMLConverter
|
||||
import dev.msfjarvis.claw.common.res.ClawIcons
|
||||
import dev.msfjarvis.claw.common.theme.LobstersTheme
|
||||
import dev.msfjarvis.claw.common.ui.decorations.ClawAppBar
|
||||
@@ -50,13 +55,14 @@ import soup.compose.material.motion.navigation.MaterialMotionNavHost
|
||||
import soup.compose.material.motion.navigation.composable
|
||||
import soup.compose.material.motion.navigation.rememberMaterialMotionNavController
|
||||
|
||||
@Suppress("ModifierMissing") // Top-level composable, will never have a modifier supplied.
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
fun LobstersApp(
|
||||
viewModel: ClawViewModel = viewModel(),
|
||||
urlLauncher: UrlLauncher,
|
||||
htmlConverter: HTMLConverter,
|
||||
setWebUri: (String?) -> Unit,
|
||||
viewModel: ClawViewModel = viewModel(),
|
||||
) {
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val hottestListState = rememberLazyListState()
|
||||
@@ -75,11 +81,7 @@ fun LobstersApp(
|
||||
|
||||
LobstersTheme(
|
||||
dynamicColor = true,
|
||||
providedValues =
|
||||
arrayOf(
|
||||
LocalUriHandler provides urlLauncher,
|
||||
LocalHTMLConverter provides htmlConverter,
|
||||
),
|
||||
providedValues = arrayOf(LocalUriHandler provides urlLauncher),
|
||||
) {
|
||||
val currentUiMode = LocalConfiguration.current.uiMode
|
||||
val systemBarsColor = MaterialTheme.colorScheme.surfaceColorAtNavigationBarElevation()
|
||||
@@ -88,7 +90,7 @@ fun LobstersApp(
|
||||
listOf(
|
||||
NavigationItem(
|
||||
label = "Hottest",
|
||||
route = Destinations.Hottest.getRoute(),
|
||||
route = Destinations.Hottest.route,
|
||||
icon = ClawIcons.Flame,
|
||||
selectedIcon = ClawIcons.FlameFilled,
|
||||
) {
|
||||
@@ -96,7 +98,7 @@ fun LobstersApp(
|
||||
},
|
||||
NavigationItem(
|
||||
label = "Newest",
|
||||
route = Destinations.Newest.getRoute(),
|
||||
route = Destinations.Newest.route,
|
||||
icon = ClawIcons.New,
|
||||
selectedIcon = ClawIcons.NewFilled,
|
||||
) {
|
||||
@@ -104,7 +106,7 @@ fun LobstersApp(
|
||||
},
|
||||
NavigationItem(
|
||||
label = "Saved",
|
||||
route = Destinations.Saved.getRoute(),
|
||||
route = Destinations.Saved.route,
|
||||
icon = ClawIcons.HeartBorder,
|
||||
selectedIcon = ClawIcons.Heart,
|
||||
) {
|
||||
@@ -158,12 +160,12 @@ fun LobstersApp(
|
||||
) { paddingValues ->
|
||||
MaterialMotionNavHost(
|
||||
navController = navController,
|
||||
startDestination = Destinations.startDestination.getRoute(),
|
||||
startDestination = Destinations.startDestination.route,
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
) {
|
||||
val uri = LobstersApi.BASE_URL
|
||||
composable(
|
||||
route = Destinations.Hottest.getRoute(),
|
||||
route = Destinations.Hottest.route,
|
||||
deepLinks =
|
||||
listOf(navDeepLink { uriPattern = uri }, navDeepLink { uriPattern = "$uri/" }),
|
||||
) {
|
||||
@@ -177,7 +179,7 @@ fun LobstersApp(
|
||||
)
|
||||
}
|
||||
composable(
|
||||
route = Destinations.Newest.getRoute(),
|
||||
route = Destinations.Newest.route,
|
||||
) {
|
||||
setWebUri("https://lobste.rs/")
|
||||
NetworkPosts(
|
||||
@@ -188,7 +190,7 @@ fun LobstersApp(
|
||||
postActions = postActions,
|
||||
)
|
||||
}
|
||||
composable(Destinations.Saved.getRoute()) {
|
||||
composable(Destinations.Saved.route) {
|
||||
setWebUri(null)
|
||||
DatabasePosts(
|
||||
items = savedPosts,
|
||||
@@ -197,26 +199,27 @@ fun LobstersApp(
|
||||
)
|
||||
}
|
||||
composable(
|
||||
route = Destinations.Comments.getRoute("{postId}"),
|
||||
route = Destinations.Comments.route,
|
||||
arguments = listOf(navArgument("postId") { type = NavType.StringType }),
|
||||
deepLinks =
|
||||
listOf(
|
||||
navDeepLink { uriPattern = "$uri/s/{postId}/.*" },
|
||||
navDeepLink { uriPattern = "$uri/s/{postId}" },
|
||||
navDeepLink { uriPattern = "$uri/s/${Destinations.Comments.placeholder}/.*" },
|
||||
navDeepLink { uriPattern = "$uri/s/${Destinations.Comments.placeholder}" },
|
||||
),
|
||||
) { backStackEntry ->
|
||||
val postId = requireNotNull(backStackEntry.arguments?.getString("postId"))
|
||||
setWebUri("https://lobste.rs/s/$postId")
|
||||
CommentsPage(
|
||||
postId = postId,
|
||||
getDetails = viewModel::getPostComments,
|
||||
postActions = postActions,
|
||||
htmlConverter = htmlConverter,
|
||||
)
|
||||
}
|
||||
composable(
|
||||
route = Destinations.User.getRoute("{username}"),
|
||||
route = Destinations.User.route,
|
||||
arguments = listOf(navArgument("username") { type = NavType.StringType }),
|
||||
deepLinks = listOf(navDeepLink { uriPattern = "$uri/u/{username}" }),
|
||||
deepLinks =
|
||||
listOf(navDeepLink { uriPattern = "$uri/u/${Destinations.User.placeholder}" }),
|
||||
) { backStackEntry ->
|
||||
val username = requireNotNull(backStackEntry.arguments?.getString("username"))
|
||||
UserProfile(
|
||||
|
||||
+10
-2
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.ui.decorations
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
@@ -61,8 +67,10 @@ fun ClawNavigationBar(
|
||||
navItem.listStateResetCallback()
|
||||
return@NavigationBarItem
|
||||
}
|
||||
navController.popBackStack(navController.graph.startDestinationRoute!!, false)
|
||||
if (navItem.route != Destinations.startDestination.getRoute()) {
|
||||
navController.graph.startDestinationRoute?.let { startDestination ->
|
||||
navController.popBackStack(startDestination, false)
|
||||
}
|
||||
if (navItem.route != Destinations.startDestination.route) {
|
||||
navController.navigate(navItem.route)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.ui
|
||||
|
||||
import android.content.Context
|
||||
@@ -11,6 +17,8 @@ import dev.msfjarvis.claw.android.viewmodel.ClawViewModel
|
||||
import dev.msfjarvis.claw.common.posts.PostActions
|
||||
import dev.msfjarvis.claw.common.urllauncher.UrlLauncher
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
import dev.msfjarvis.claw.model.LinkMetadata
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
@@ -43,7 +51,9 @@ fun rememberPostActions(
|
||||
}
|
||||
|
||||
override fun viewComments(postId: String) {
|
||||
navController.navigate(Destinations.Comments.getRoute(postId))
|
||||
navController.navigate(
|
||||
Destinations.Comments.route.replace(Destinations.Comments.placeholder, postId)
|
||||
)
|
||||
}
|
||||
|
||||
override fun viewCommentsPage(commentsUrl: String) {
|
||||
@@ -59,6 +69,14 @@ fun rememberPostActions(
|
||||
override fun toggleSave(post: SavedPost) {
|
||||
viewModel.toggleSave(post)
|
||||
}
|
||||
|
||||
override suspend fun getComments(postId: String): LobstersPostDetails {
|
||||
return viewModel.getPostComments(postId)
|
||||
}
|
||||
|
||||
override suspend fun getLinkMetadata(url: String): LinkMetadata {
|
||||
return viewModel.getLinkMetadata(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.ui.lists
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.ui.lists
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.ui.lists
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
|
||||
@@ -1,24 +1,34 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.ui.navigation
|
||||
|
||||
sealed class Destinations {
|
||||
abstract val route: String
|
||||
|
||||
object Hottest : Destinations() {
|
||||
fun getRoute() = "hottest"
|
||||
override val route = "hottest"
|
||||
}
|
||||
|
||||
object Newest : Destinations() {
|
||||
fun getRoute() = "newest"
|
||||
override val route = "newest"
|
||||
}
|
||||
|
||||
object Saved : Destinations() {
|
||||
fun getRoute() = "saved"
|
||||
override val route = "saved"
|
||||
}
|
||||
|
||||
object Comments : Destinations() {
|
||||
fun getRoute(postId: String) = "comments/$postId"
|
||||
const val placeholder = "{postId}"
|
||||
override val route = "comments/$placeholder"
|
||||
}
|
||||
|
||||
object User : Destinations() {
|
||||
fun getRoute(username: String) = "user/$username"
|
||||
const val placeholder = "{username}"
|
||||
override val route = "user/$placeholder"
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import com.slack.eithernet.ApiResult.Failure
|
||||
import com.slack.eithernet.ApiResult.Success
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dev.msfjarvis.claw.android.injection.IODispatcher
|
||||
import dev.msfjarvis.claw.android.paging.LobstersPagingSource
|
||||
import dev.msfjarvis.claw.android.ui.toLocalDateTime
|
||||
import dev.msfjarvis.claw.api.LobstersApi
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
import java.io.IOException
|
||||
import java.time.Month
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
@@ -24,7 +33,7 @@ class ClawViewModel
|
||||
constructor(
|
||||
private val api: LobstersApi,
|
||||
private val savedPostsRepository: SavedPostsRepository,
|
||||
private val postDetailsRepository: PostDetailsRepository,
|
||||
private val linkMetadataRepository: LinkMetadataRepository,
|
||||
private val pagingSourceFactory: LobstersPagingSource.Factory,
|
||||
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
|
||||
) : ViewModel() {
|
||||
@@ -73,12 +82,28 @@ constructor(
|
||||
|
||||
suspend fun getPostComments(postId: String) =
|
||||
withContext(ioDispatcher) {
|
||||
val details = api.getPostDetails(postId)
|
||||
val extendedDetails = postDetailsRepository.getExtendedDetails(details)
|
||||
extendedDetails
|
||||
when (val result = api.getPostDetails(postId)) {
|
||||
is Success -> result.value
|
||||
is Failure.NetworkFailure -> throw result.error
|
||||
is Failure.UnknownFailure -> throw result.error
|
||||
is Failure.HttpFailure,
|
||||
is Failure.ApiFailure -> throw IOException("API returned an invalid response")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getUserProfile(username: String) = withContext(ioDispatcher) { api.getUser(username) }
|
||||
suspend fun getLinkMetadata(url: String) =
|
||||
withContext(ioDispatcher) { linkMetadataRepository.getLinkMetadata(url) }
|
||||
|
||||
suspend fun getUserProfile(username: String) =
|
||||
withContext(ioDispatcher) {
|
||||
when (val result = api.getUser(username)) {
|
||||
is Success -> result.value
|
||||
is Failure.NetworkFailure -> throw result.error
|
||||
is Failure.UnknownFailure -> throw result.error
|
||||
is Failure.HttpFailure,
|
||||
is Failure.ApiFailure -> throw IOException("API returned an invalid response")
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshHottestPosts() {
|
||||
hottestPostsPagingSource?.invalidate()
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.viewmodel
|
||||
|
||||
import dev.msfjarvis.claw.metadata.MetadataExtractor
|
||||
import dev.msfjarvis.claw.model.LinkMetadata
|
||||
import javax.inject.Inject
|
||||
|
||||
class LinkMetadataRepository
|
||||
@Inject
|
||||
constructor(
|
||||
private val metadataExtractor: MetadataExtractor,
|
||||
) {
|
||||
suspend fun getLinkMetadata(url: String): LinkMetadata {
|
||||
return metadataExtractor.getExtractedMetadata(url)
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package dev.msfjarvis.claw.android.viewmodel
|
||||
|
||||
import dev.msfjarvis.claw.metadata.MetadataExtractor
|
||||
import dev.msfjarvis.claw.model.ExtendedPostDetails
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
import javax.inject.Inject
|
||||
|
||||
class PostDetailsRepository
|
||||
@Inject
|
||||
constructor(
|
||||
private val metadataExtractor: MetadataExtractor,
|
||||
) {
|
||||
suspend fun getExtendedDetails(details: LobstersPostDetails): ExtendedPostDetails {
|
||||
val metadata = metadataExtractor.getExtractedMetadata(details.url)
|
||||
return ExtendedPostDetails(
|
||||
title = details.title,
|
||||
linkMetadata = metadata,
|
||||
description = details.description,
|
||||
submitter = details.submitter,
|
||||
tags = details.tags,
|
||||
comments = details.comments,
|
||||
commentsUrl = details.commentsUrl,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.viewmodel
|
||||
|
||||
import app.cash.sqldelight.coroutines.asFlow
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.work
|
||||
|
||||
import android.content.Context
|
||||
import androidx.hilt.work.HiltWorker
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.WorkerParameters
|
||||
import com.slack.eithernet.ApiResult
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import dev.msfjarvis.claw.android.viewmodel.SavedPostsRepository
|
||||
import dev.msfjarvis.claw.api.LobstersApi
|
||||
import dev.msfjarvis.claw.common.posts.toDbModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
@@ -23,7 +29,6 @@ import kotlinx.coroutines.flow.first
|
||||
* and for new-enough posts that are still getting comments to have an accurate one.
|
||||
*/
|
||||
@Suppress("DEPRECATION") // We're being nasty
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@HiltWorker
|
||||
class SavedPostUpdaterWorker
|
||||
@AssistedInject
|
||||
@@ -39,8 +44,8 @@ constructor(
|
||||
.map { post ->
|
||||
CoroutineScope(coroutineContext + Job()).async {
|
||||
val details = runCatching { lobstersApi.getPostDetails(post.shortId) }.getOrNull()
|
||||
if (details != null) {
|
||||
savedPostsRepository.savePost(details.toDbModel())
|
||||
if (details is ApiResult.Success) {
|
||||
savedPostsRepository.savePost(details.value.toDbModel())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# This file was automatically generated by 'versioning-plugin'. DO NOT EDIT MANUALLY.
|
||||
#
|
||||
versioning-plugin.versionCode=10100
|
||||
versioning-plugin.versionName=1.1.0
|
||||
versioning-plugin.versionCode=10500
|
||||
versioning-plugin.versionName=1.5.0
|
||||
|
||||
+10
-4
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
|
||||
|
||||
plugins {
|
||||
@@ -7,11 +13,11 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
api(projects.model)
|
||||
api(libs.retrofit.lib)
|
||||
implementation(libs.kotlinx.serialization.core)
|
||||
testImplementation(libs.kotlinx.coroutines.core)
|
||||
api(libs.retrofit)
|
||||
api(libs.eithernet)
|
||||
testImplementation(testFixtures(libs.eithernet))
|
||||
testImplementation(kotlin("test-junit"))
|
||||
testImplementation(libs.kotlinx.coroutines.core)
|
||||
testImplementation(libs.kotlinx.serialization.json)
|
||||
testImplementation(libs.retrofit.kotlinxSerializationConverter)
|
||||
testImplementation(libs.retrofit.mock)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.api
|
||||
|
||||
import com.slack.eithernet.ApiResult
|
||||
import dev.msfjarvis.claw.model.LobstersPost
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
import dev.msfjarvis.claw.model.User
|
||||
@@ -10,14 +17,17 @@ import retrofit2.http.Query
|
||||
/** Simple interface defining an API for lobste.rs */
|
||||
interface LobstersApi {
|
||||
|
||||
@GET("hottest.json") suspend fun getHottestPosts(@Query("page") page: Int): List<LobstersPost>
|
||||
@GET("hottest.json")
|
||||
suspend fun getHottestPosts(@Query("page") page: Int): ApiResult<List<LobstersPost>, Unit>
|
||||
|
||||
@GET("newest.json") suspend fun getNewestPosts(@Query("page") page: Int): List<LobstersPost>
|
||||
@GET("newest.json")
|
||||
suspend fun getNewestPosts(@Query("page") page: Int): ApiResult<List<LobstersPost>, Unit>
|
||||
|
||||
@GET("s/{postId}.json")
|
||||
suspend fun getPostDetails(@Path("postId") postId: String): LobstersPostDetails
|
||||
suspend fun getPostDetails(@Path("postId") postId: String): ApiResult<LobstersPostDetails, Unit>
|
||||
|
||||
@GET("u/{username}.json") suspend fun getUser(@Path("username") username: String): User
|
||||
@GET("u/{username}.json")
|
||||
suspend fun getUser(@Path("username") username: String): ApiResult<User, Unit>
|
||||
|
||||
companion object {
|
||||
const val BASE_URL = "https://lobste.rs"
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.api
|
||||
|
||||
import com.slack.eithernet.ApiResult.Success
|
||||
import com.slack.eithernet.test.newEitherNetController
|
||||
import dev.msfjarvis.claw.model.LobstersPost
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
import dev.msfjarvis.claw.model.User
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertIs
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.mock.MockRetrofit
|
||||
import retrofit2.mock.NetworkBehavior
|
||||
import retrofit2.mock.create
|
||||
|
||||
class ApiTest {
|
||||
private val retrofit = Retrofit.Builder().baseUrl(LobstersApi.BASE_URL).build()
|
||||
private val networkBehaviour = createNetworkBehaviour()
|
||||
private val api =
|
||||
MockRetrofit.Builder(retrofit)
|
||||
.networkBehavior(networkBehaviour)
|
||||
.build()
|
||||
.create<LobstersApi>()
|
||||
.let(::FakeApi)
|
||||
|
||||
private fun createNetworkBehaviour(): NetworkBehavior {
|
||||
val behaviour = NetworkBehavior.create()
|
||||
behaviour.setFailurePercent(0)
|
||||
behaviour.setErrorPercent(0)
|
||||
return behaviour
|
||||
}
|
||||
private val wrapper = ApiWrapper(newEitherNetController())
|
||||
private val api
|
||||
get() = wrapper.api
|
||||
|
||||
@Test
|
||||
fun `api gets correct number of items`() = runBlocking {
|
||||
val posts = api.getHottestPosts(1)
|
||||
assertEquals(25, posts.size)
|
||||
assertIs<Success<List<LobstersPost>>>(posts)
|
||||
assertEquals(25, posts.value.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `posts with no urls`() = runBlocking {
|
||||
val posts = api.getHottestPosts(1)
|
||||
val commentsOnlyPosts = posts.asSequence().filter { it.url.isEmpty() }.toSet()
|
||||
assertIs<Success<List<LobstersPost>>>(posts)
|
||||
val commentsOnlyPosts = posts.value.asSequence().filter { it.url.isEmpty() }.toSet()
|
||||
assertEquals(2, commentsOnlyPosts.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `post details with comments`() = runBlocking {
|
||||
val postDetails = api.getPostDetails("tdfoqh")
|
||||
assertEquals(7, postDetails.comments.size)
|
||||
assertIs<Success<LobstersPostDetails>>(postDetails)
|
||||
assertEquals(7, postDetails.value.comments.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `get user details`() = runBlocking {
|
||||
val user = api.getUser("msfjarvis")
|
||||
assertEquals("msfjarvis", user.username)
|
||||
assertIs<Success<User>>(user)
|
||||
assertEquals("msfjarvis", user.value.username)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.api
|
||||
|
||||
import com.slack.eithernet.ApiResult.Companion.success
|
||||
import com.slack.eithernet.test.EitherNetController
|
||||
import com.slack.eithernet.test.enqueue
|
||||
import dev.msfjarvis.claw.model.LobstersPost
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
import dev.msfjarvis.claw.model.User
|
||||
import dev.msfjarvis.claw.util.TestUtils.getJson
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
class ApiWrapper(controller: EitherNetController<LobstersApi>) {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
private val hottest: List<LobstersPost> = json.decodeFromString(getJson("hottest.json"))
|
||||
private val postDetails: LobstersPostDetails =
|
||||
json.decodeFromString(getJson("post_details_tdfoqh.json"))
|
||||
private val user: User = json.decodeFromString(getJson("msfjarvis.json"))
|
||||
|
||||
val api = controller.api
|
||||
|
||||
init {
|
||||
controller.enqueue(LobstersApi::getHottestPosts) { success(hottest) }
|
||||
controller.enqueue(LobstersApi::getNewestPosts) { success(hottest) }
|
||||
controller.enqueue(LobstersApi::getPostDetails) { success(postDetails) }
|
||||
controller.enqueue(LobstersApi::getUser) { success(user) }
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package dev.msfjarvis.claw.api
|
||||
|
||||
import dev.msfjarvis.claw.model.LobstersPost
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
import dev.msfjarvis.claw.model.User
|
||||
import dev.msfjarvis.claw.util.TestUtils.getJson
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import retrofit2.mock.BehaviorDelegate
|
||||
|
||||
class FakeApi(private val delegate: BehaviorDelegate<LobstersApi>) : LobstersApi {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
private val hottest: List<LobstersPost> = json.decodeFromString(getJson("hottest.json"))
|
||||
private val postDetails: LobstersPostDetails =
|
||||
json.decodeFromString(getJson("post_details_tdfoqh.json"))
|
||||
private val user: User = json.decodeFromString(getJson("msfjarvis.json"))
|
||||
|
||||
override suspend fun getHottestPosts(page: Int): List<LobstersPost> {
|
||||
return delegate.returningResponse(hottest).getHottestPosts(page)
|
||||
}
|
||||
|
||||
override suspend fun getNewestPosts(page: Int): List<LobstersPost> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun getPostDetails(postId: String): LobstersPostDetails {
|
||||
return delegate.returningResponse(postDetails).getPostDetails(postId)
|
||||
}
|
||||
|
||||
override suspend fun getUser(username: String): User {
|
||||
return delegate.returningResponse(user).getUser(username)
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021 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.util
|
||||
|
||||
import java.io.File
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
|
||||
|
||||
plugins {
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.benchmark
|
||||
|
||||
import androidx.benchmark.macro.BaselineProfileMode
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.benchmark
|
||||
|
||||
import androidx.benchmark.macro.ExperimentalBaselineProfilesApi
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.benchmark
|
||||
|
||||
import androidx.benchmark.macro.MacrobenchmarkScope
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
|
||||
+6
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle
|
||||
|
||||
import com.android.build.api.dsl.TestExtension
|
||||
|
||||
+6
-8
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
package dev.msfjarvis.aps.gradle
|
||||
@@ -50,12 +56,4 @@ class ApplicationPlugin : Plugin<Project> {
|
||||
project.configureBuildSigning()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.isSnapshot(): Boolean {
|
||||
with(project.providers) {
|
||||
val workflow = environmentVariable("GITHUB_WORKFLOW")
|
||||
val snapshot = environmentVariable("SNAPSHOT")
|
||||
return workflow.isPresent || snapshot.isPresent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle
|
||||
|
||||
import com.android.build.gradle.LibraryPlugin
|
||||
|
||||
+14
-4
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle
|
||||
|
||||
import com.android.build.api.artifact.SingleArtifact
|
||||
@@ -5,6 +11,7 @@ import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
||||
import com.android.build.api.variant.VariantOutputConfiguration
|
||||
import dev.msfjarvis.aps.gradle.artifacts.CollectApksTask
|
||||
import dev.msfjarvis.aps.gradle.artifacts.CollectBundleTask
|
||||
import java.util.Locale
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
@@ -17,21 +24,24 @@ class RenameArtifactsPlugin : Plugin<Project> {
|
||||
project.pluginManager.withPlugin("com.android.application") {
|
||||
project.extensions.getByType<ApplicationAndroidComponentsExtension>().run {
|
||||
onVariants { variant ->
|
||||
project.tasks.register<CollectApksTask>("collect${variant.name.capitalize()}Apks") {
|
||||
val taskPrefix = "collect${variant.name.capitalize(Locale.ROOT)}"
|
||||
project.tasks.register<CollectApksTask>("${taskPrefix}Apks") {
|
||||
variantName.set(variant.name)
|
||||
apkFolder.set(variant.artifacts.get(SingleArtifact.APK))
|
||||
mappingFile.set(variant.artifacts.get(SingleArtifact.OBFUSCATION_MAPPING_FILE))
|
||||
builtArtifactsLoader.set(variant.artifacts.getBuiltArtifactsLoader())
|
||||
outputDirectory.set(project.layout.projectDirectory.dir("outputs"))
|
||||
outputDirectory.set(project.layout.projectDirectory.dir("apk"))
|
||||
}
|
||||
project.tasks.register<CollectBundleTask>("collect${variant.name.capitalize()}Bundle") {
|
||||
project.tasks.register<CollectBundleTask>("${taskPrefix}Bundle") {
|
||||
val mainOutput =
|
||||
variant.outputs.single {
|
||||
it.outputType == VariantOutputConfiguration.OutputType.SINGLE
|
||||
}
|
||||
variantName.set(variant.name)
|
||||
versionName.set(mainOutput.versionName)
|
||||
mappingFile.set(variant.artifacts.get(SingleArtifact.OBFUSCATION_MAPPING_FILE))
|
||||
bundleFile.set(variant.artifacts.get(SingleArtifact.BUNDLE))
|
||||
outputDirectory.set(project.layout.projectDirectory.dir("outputs"))
|
||||
outputDirectory.set(project.layout.projectDirectory.dir("bundle"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+24
-1
@@ -1,14 +1,24 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle.artifacts
|
||||
|
||||
import com.android.build.api.variant.BuiltArtifactsLoader
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.nio.file.StandardCopyOption
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
@@ -21,6 +31,10 @@ import org.gradle.api.tasks.TaskAction
|
||||
abstract class CollectApksTask : DefaultTask() {
|
||||
@get:InputFiles @get:PathSensitive(PathSensitivity.NONE) abstract val apkFolder: DirectoryProperty
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val mappingFile: RegularFileProperty
|
||||
|
||||
@get:Input abstract val variantName: Property<String>
|
||||
|
||||
@get:Internal abstract val builtArtifactsLoader: Property<BuiltArtifactsLoader>
|
||||
@@ -30,9 +44,13 @@ abstract class CollectApksTask : DefaultTask() {
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val outputDir = outputDirectory.asFile.get()
|
||||
val outputDirStream =
|
||||
Files.walk(outputDir.toPath()).sorted(Comparator.reverseOrder()).map(Path::toFile)
|
||||
outputDirStream.forEach(File::delete)
|
||||
outputDirStream.close()
|
||||
outputDir.mkdirs()
|
||||
val builtArtifacts =
|
||||
builtArtifactsLoader.get().load(apkFolder.get()) ?: throw RuntimeException("Cannot load APKs")
|
||||
builtArtifactsLoader.get().load(apkFolder.get()) ?: error("Cannot load APKs")
|
||||
builtArtifacts.elements.forEach { artifact ->
|
||||
Files.copy(
|
||||
Paths.get(artifact.outputFile),
|
||||
@@ -40,5 +58,10 @@ abstract class CollectApksTask : DefaultTask() {
|
||||
StandardCopyOption.REPLACE_EXISTING,
|
||||
)
|
||||
}
|
||||
Files.copy(
|
||||
mappingFile.get().asFile.toPath(),
|
||||
outputDir.resolve("mapping.txt").toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+19
@@ -1,6 +1,14 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle.artifacts
|
||||
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardCopyOption
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
@@ -14,6 +22,8 @@ import org.gradle.api.tasks.TaskAction
|
||||
abstract class CollectBundleTask : DefaultTask() {
|
||||
@get:InputFile abstract val bundleFile: RegularFileProperty
|
||||
|
||||
@get:InputFile abstract val mappingFile: RegularFileProperty
|
||||
|
||||
@get:Input abstract val variantName: Property<String>
|
||||
|
||||
@get:Input abstract val versionName: Property<String>
|
||||
@@ -23,11 +33,20 @@ abstract class CollectBundleTask : DefaultTask() {
|
||||
@TaskAction
|
||||
fun taskAction() {
|
||||
val outputDir = outputDirectory.asFile.get()
|
||||
val outputDirStream =
|
||||
Files.walk(outputDir.toPath()).sorted(Comparator.reverseOrder()).map(Path::toFile)
|
||||
outputDirStream.forEach(File::delete)
|
||||
outputDirStream.close()
|
||||
outputDir.mkdirs()
|
||||
Files.copy(
|
||||
bundleFile.get().asFile.toPath(),
|
||||
outputDir.resolve("Claw-${variantName.get()}-${versionName.get()}.aab").toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING,
|
||||
)
|
||||
Files.copy(
|
||||
mappingFile.get().asFile.toPath(),
|
||||
outputDir.resolve("mapping.txt").toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package dev.msfjarvis.aps.gradle.signing
|
||||
|
||||
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
|
||||
|
||||
+6
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle.versioning
|
||||
|
||||
const val VERSIONING_PROP_FILE = "version.properties"
|
||||
|
||||
+6
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle.versioning
|
||||
|
||||
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
||||
|
||||
+6
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle.versioning
|
||||
|
||||
import com.vdurmont.semver4j.Semver
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
@@ -51,6 +52,7 @@ gradlePlugin {
|
||||
|
||||
dependencies {
|
||||
implementation(libs.build.agp)
|
||||
implementation(libs.build.detekt)
|
||||
implementation(libs.build.kotlin.gradle)
|
||||
implementation(libs.build.kotlin.serialization)
|
||||
implementation(libs.build.spotless)
|
||||
|
||||
+6
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle
|
||||
|
||||
import com.github.benmanes.gradle.versions.VersionsPlugin
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle
|
||||
|
||||
import io.gitlab.arturbosch.detekt.DetektPlugin
|
||||
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
|
||||
object Detekt {
|
||||
private const val TWITTER_RULES_VERSION = "0.0.23"
|
||||
|
||||
fun apply(project: Project) {
|
||||
project.pluginManager.apply(DetektPlugin::class.java)
|
||||
project.extensions.configure<DetektExtension> {
|
||||
debug = project.providers.gradleProperty("debugDetekt").isPresent
|
||||
parallel = true
|
||||
ignoredBuildTypes = listOf("benchmark", "release")
|
||||
basePath = project.layout.projectDirectory.toString()
|
||||
baseline =
|
||||
project.rootProject.layout.projectDirectory
|
||||
.dir("detekt-baselines")
|
||||
.file("${project.name}.xml")
|
||||
.asFile
|
||||
}
|
||||
project.dependencies.add(
|
||||
"detektPlugins",
|
||||
"com.twitter.compose.rules:detekt:$TWITTER_RULES_VERSION",
|
||||
)
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package dev.msfjarvis.aps.gradle
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
|
||||
+7
-8
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package dev.msfjarvis.aps.gradle
|
||||
|
||||
import org.gradle.api.JavaVersion
|
||||
@@ -18,6 +19,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
class KotlinCommonPlugin : Plugin<Project> {
|
||||
|
||||
override fun apply(project: Project) {
|
||||
Detekt.apply(project)
|
||||
project.tasks.run {
|
||||
withType<JavaCompile>().configureEach {
|
||||
sourceCompatibility = JavaVersion.VERSION_11.toString()
|
||||
@@ -25,16 +27,15 @@ class KotlinCommonPlugin : Plugin<Project> {
|
||||
}
|
||||
withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors = false
|
||||
allWarningsAsErrors = project.providers.environmentVariable("GITHUB_WORKFLOW").isPresent
|
||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||
freeCompilerArgs = freeCompilerArgs + ADDITIONAL_COMPILER_ARGS
|
||||
languageVersion = "1.5"
|
||||
languageVersion = "1.7"
|
||||
}
|
||||
}
|
||||
withType<Test>().configureEach {
|
||||
maxParallelForks = Runtime.getRuntime().availableProcessors() * 2
|
||||
testLogging { events(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED) }
|
||||
doNotTrackState("We want tests to always run even if Gradle thinks otherwise")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,8 +44,6 @@ class KotlinCommonPlugin : Plugin<Project> {
|
||||
private val ADDITIONAL_COMPILER_ARGS =
|
||||
listOf(
|
||||
"-opt-in=kotlin.RequiresOptIn",
|
||||
"-P",
|
||||
"plugin:androidx.compose.compiler.plugins.kotlin:suppressKotlinVersionCompatibilityCheck=1.7.20",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package dev.msfjarvis.aps.gradle
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
|
||||
+11
-4
@@ -1,12 +1,14 @@
|
||||
/*
|
||||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
package dev.msfjarvis.aps.gradle
|
||||
|
||||
import com.diffplug.gradle.spotless.SpotlessExtension
|
||||
import com.diffplug.gradle.spotless.SpotlessPlugin
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
@@ -16,17 +18,22 @@ import org.gradle.kotlin.dsl.getByType
|
||||
class SpotlessPlugin : Plugin<Project> {
|
||||
|
||||
override fun apply(project: Project) {
|
||||
if (project.rootProject != project) {
|
||||
throw GradleException("Spotless plugin must only be applied to the root project.")
|
||||
}
|
||||
project.pluginManager.apply(SpotlessPlugin::class)
|
||||
project.extensions.getByType<SpotlessExtension>().run {
|
||||
kotlin {
|
||||
ktfmt(KTFMT_VERSION).googleStyle()
|
||||
target("**/*.kt")
|
||||
targetExclude("**/build/", "**/gen/")
|
||||
targetExclude("**/build/", "/spotless/")
|
||||
licenseHeaderFile(project.file("spotless/license.kt"))
|
||||
}
|
||||
kotlinGradle {
|
||||
ktfmt(KTFMT_VERSION).googleStyle()
|
||||
target("**/*.kts")
|
||||
targetExclude("**/build/")
|
||||
licenseHeaderFile(project.file("spotless/license.kt"), "import|plugins|@file")
|
||||
}
|
||||
format("xml") {
|
||||
target("**/*.xml")
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/*
|
||||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress("DSL_SCOPE_VIOLATION")
|
||||
|
||||
plugins {
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
|
||||
|
||||
plugins {
|
||||
@@ -9,6 +15,7 @@ plugins {
|
||||
androidComponents { beforeVariants { it.enableUnitTest = false } }
|
||||
|
||||
dependencies {
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
api(libs.napier)
|
||||
implementation(projects.database)
|
||||
implementation(projects.model)
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common
|
||||
|
||||
sealed class NetworkState {
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.comments
|
||||
|
||||
import android.text.format.DateUtils
|
||||
@@ -23,6 +29,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -40,20 +47,28 @@ import dev.msfjarvis.claw.common.res.ClawIcons
|
||||
import dev.msfjarvis.claw.common.ui.NetworkImage
|
||||
import dev.msfjarvis.claw.common.ui.ThemedRichText
|
||||
import dev.msfjarvis.claw.model.Comment
|
||||
import dev.msfjarvis.claw.model.ExtendedPostDetails
|
||||
import dev.msfjarvis.claw.model.LinkMetadata
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
import java.time.Instant
|
||||
import java.time.temporal.TemporalAccessor
|
||||
|
||||
@Composable
|
||||
fun CommentsHeader(
|
||||
postDetails: ExtendedPostDetails,
|
||||
postDetails: LobstersPostDetails,
|
||||
postActions: PostActions,
|
||||
htmlConverter: HTMLConverter,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val htmlConverter = LocalHTMLConverter.current
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val linkMetadata by
|
||||
produceState(
|
||||
initialValue = LinkMetadata(postDetails.url, null, null),
|
||||
) {
|
||||
runCatching { postActions.getLinkMetadata(postDetails.url) }
|
||||
.onSuccess { metadata -> value = metadata }
|
||||
}
|
||||
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
Surface(color = MaterialTheme.colorScheme.background, modifier = modifier) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp).fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
@@ -62,13 +77,11 @@ fun CommentsHeader(
|
||||
TagRow(tags = postDetails.tags)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
|
||||
if (postDetails.linkMetadata.url.isNotBlank()) {
|
||||
if (linkMetadata.url.isNotBlank()) {
|
||||
PostLink(
|
||||
linkMetadata = postDetails.linkMetadata,
|
||||
linkMetadata = linkMetadata,
|
||||
modifier =
|
||||
Modifier.clickable {
|
||||
postActions.viewPost(postDetails.linkMetadata.url, postDetails.commentsUrl)
|
||||
},
|
||||
Modifier.clickable { postActions.viewPost(linkMetadata.url, postDetails.commentsUrl) },
|
||||
)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
}
|
||||
@@ -128,20 +141,29 @@ fun PostLink(
|
||||
}
|
||||
}
|
||||
|
||||
private val CommentEntryPadding = 16f.dp
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
fun CommentEntry(
|
||||
comment: Comment,
|
||||
htmlConverter: HTMLConverter,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var expanded by remember(comment) { mutableStateOf(true) }
|
||||
val htmlConverter = LocalHTMLConverter.current
|
||||
val uriHandler = LocalUriHandler.current
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { expanded = !expanded }
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(start = (comment.indentLevel * 16).dp, end = 16.dp, top = 16.dp, bottom = 16.dp),
|
||||
.padding(
|
||||
start = CommentEntryPadding * comment.indentLevel,
|
||||
end = CommentEntryPadding,
|
||||
top = CommentEntryPadding,
|
||||
bottom = CommentEntryPadding,
|
||||
),
|
||||
) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Submitter(
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.comments
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
@@ -24,17 +30,24 @@ import dev.msfjarvis.claw.common.NetworkState.Success
|
||||
import dev.msfjarvis.claw.common.posts.PostActions
|
||||
import dev.msfjarvis.claw.common.ui.NetworkError
|
||||
import dev.msfjarvis.claw.common.ui.ProgressBar
|
||||
import dev.msfjarvis.claw.model.ExtendedPostDetails
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
|
||||
@Composable
|
||||
private fun CommentsPageInternal(
|
||||
details: ExtendedPostDetails,
|
||||
details: LobstersPostDetails,
|
||||
postActions: PostActions,
|
||||
htmlConverter: HTMLConverter,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Surface(color = MaterialTheme.colorScheme.surfaceVariant) {
|
||||
LazyColumn(modifier = modifier, contentPadding = PaddingValues(bottom = 24.dp)) {
|
||||
item { CommentsHeader(postDetails = details, postActions = postActions) }
|
||||
item {
|
||||
CommentsHeader(
|
||||
postDetails = details,
|
||||
postActions = postActions,
|
||||
htmlConverter = htmlConverter,
|
||||
)
|
||||
}
|
||||
|
||||
if (details.comments.isNotEmpty()) {
|
||||
item {
|
||||
@@ -49,7 +62,7 @@ private fun CommentsPageInternal(
|
||||
if (index != 0) {
|
||||
Divider()
|
||||
}
|
||||
CommentEntry(item)
|
||||
CommentEntry(comment = item, htmlConverter = htmlConverter)
|
||||
}
|
||||
} else {
|
||||
item {
|
||||
@@ -70,13 +83,13 @@ private fun CommentsPageInternal(
|
||||
@Composable
|
||||
fun CommentsPage(
|
||||
postId: String,
|
||||
getDetails: suspend (String) -> ExtendedPostDetails,
|
||||
postActions: PostActions,
|
||||
htmlConverter: HTMLConverter,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val postDetails by
|
||||
produceState<NetworkState>(Loading) {
|
||||
runCatching { getDetails(postId) }
|
||||
runCatching { postActions.getComments(postId) }
|
||||
.fold(
|
||||
onSuccess = { details -> value = Success(details) },
|
||||
onFailure = { value = Error(error = it, description = "Failed to load comments") }
|
||||
@@ -86,9 +99,10 @@ fun CommentsPage(
|
||||
when (postDetails) {
|
||||
is Success<*> -> {
|
||||
CommentsPageInternal(
|
||||
(postDetails as Success<ExtendedPostDetails>).data,
|
||||
postActions,
|
||||
modifier.fillMaxSize(),
|
||||
details = (postDetails as Success<LobstersPostDetails>).data,
|
||||
postActions = postActions,
|
||||
htmlConverter = htmlConverter,
|
||||
modifier = modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
is Error -> {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.comments
|
||||
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
|
||||
/** Defines a contract to convert strings of HTML to Markdown. */
|
||||
fun interface HTMLConverter {
|
||||
fun convertHTMLToMarkdown(html: String): String
|
||||
}
|
||||
|
||||
val LocalHTMLConverter = staticCompositionLocalOf<HTMLConverter> { error("To be provided") }
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.posts
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
@@ -19,7 +25,6 @@ import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.Badge
|
||||
import androidx.compose.material3.BadgedBox
|
||||
@@ -146,10 +151,10 @@ fun Submitter(
|
||||
url = avatarUrl,
|
||||
placeholder = ClawIcons.Account,
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier.requiredSize(24.dp).clip(CircleShape),
|
||||
modifier = Modifier.requiredSize(24.dp).clip(CircleShape),
|
||||
)
|
||||
|
||||
Text(text = text, modifier = modifier, style = MaterialTheme.typography.bodyMedium)
|
||||
Text(text = text, style = MaterialTheme.typography.bodyMedium)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +186,7 @@ fun CommentsButton(
|
||||
badge = {
|
||||
if (commentCount != null) {
|
||||
Badge(
|
||||
modifier = Modifier.absoluteOffset(x = (-8).dp),
|
||||
modifier = Modifier.absoluteOffset(x = -(8.dp)),
|
||||
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
|
||||
) {
|
||||
Text(
|
||||
@@ -224,9 +229,9 @@ fun TagText(
|
||||
Text(
|
||||
text = tag,
|
||||
modifier =
|
||||
Modifier.background(MaterialTheme.colorScheme.tertiaryContainer, RoundedCornerShape(50))
|
||||
.padding(vertical = 4.dp, horizontal = 12.dp)
|
||||
.then(modifier),
|
||||
modifier
|
||||
.background(MaterialTheme.colorScheme.tertiaryContainer, CircleShape)
|
||||
.padding(vertical = 4.dp, horizontal = 12.dp),
|
||||
color = MaterialTheme.colorScheme.onTertiaryContainer,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.posts
|
||||
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
import dev.msfjarvis.claw.model.LinkMetadata
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
|
||||
interface PostActions {
|
||||
fun viewPost(postUrl: String, commentsUrl: String)
|
||||
fun viewComments(postId: String)
|
||||
fun viewCommentsPage(commentsUrl: String)
|
||||
fun toggleSave(post: SavedPost)
|
||||
suspend fun getComments(postId: String): LobstersPostDetails
|
||||
suspend fun getLinkMetadata(url: String): LinkMetadata
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.posts
|
||||
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.res
|
||||
|
||||
import dev.msfjarvis.claw.common.res.clawicons.account_circle_black_24dp
|
||||
|
||||
Generated
+15
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
|
||||
+15
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
|
||||
+15
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
|
||||
+15
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
|
||||
Generated
+15
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
|
||||
+15
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
|
||||
Generated
+15
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
|
||||
+15
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
|
||||
+15
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
|
||||
Generated
+15
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress(
|
||||
"ClassNaming",
|
||||
"FunctionNaming",
|
||||
"FunctionOnlyReturningConstant",
|
||||
"LongMethod",
|
||||
"MagicNumber",
|
||||
"WildcardImport",
|
||||
)
|
||||
|
||||
package dev.msfjarvis.claw.common.res.clawicons
|
||||
|
||||
import androidx.compose.ui.geometry.*
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.theme
|
||||
|
||||
import android.os.Build
|
||||
@@ -71,6 +77,7 @@ internal val DarkThemeColors =
|
||||
inverseSurface = md_theme_dark_inverseSurface,
|
||||
)
|
||||
|
||||
@Suppress("SpreadOperator")
|
||||
@Composable
|
||||
fun LobstersTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
@@ -80,7 +87,7 @@ fun LobstersTheme(
|
||||
) {
|
||||
val colorScheme =
|
||||
when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= 31 -> {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) {
|
||||
dynamicDarkColorScheme(context)
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.ui
|
||||
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.ui
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
@@ -67,5 +73,5 @@ fun NetworkError(
|
||||
@ThemePreviews
|
||||
@Composable
|
||||
fun NetworkErrorPreview() {
|
||||
LobstersTheme { NetworkError(label = "Failed to load posts", error = Throwable()) }
|
||||
LobstersTheme { NetworkError(label = "Failed to load posts", error = Throwable("Preview")) }
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.ui.decorations
|
||||
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -7,8 +13,10 @@ import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.msfjarvis.claw.common.theme.LobstersTheme
|
||||
import dev.msfjarvis.claw.common.ui.preview.DevicePreviews
|
||||
import dev.msfjarvis.claw.common.ui.preview.ThemePreviews
|
||||
@@ -24,7 +32,7 @@ fun ClawAppBar(
|
||||
) {
|
||||
TopAppBar(
|
||||
title = title,
|
||||
modifier = modifier,
|
||||
modifier = modifier.shadow(8.dp),
|
||||
colors = TopAppBarDefaults.smallTopAppBarColors(containerColor = backgroundColor),
|
||||
navigationIcon = navigationIcon,
|
||||
)
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.ui.decorations
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
@@ -19,9 +25,13 @@ import dev.msfjarvis.claw.common.ui.surfaceColorAtNavigationBarElevation
|
||||
import java.time.Month
|
||||
|
||||
@Composable
|
||||
fun MonthHeader(month: Month) {
|
||||
fun MonthHeader(
|
||||
month: Month,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
Modifier.fillMaxWidth()
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.background(MaterialTheme.colorScheme.surfaceColorAtNavigationBarElevation())
|
||||
) {
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.ui
|
||||
|
||||
import androidx.compose.material3.ColorScheme
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
@file:Suppress("NON_SOURCE_REPEATED_ANNOTATION") // IDE false-positive
|
||||
|
||||
package dev.msfjarvis.claw.common.ui.preview
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2021-2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.urllauncher
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
@@ -20,7 +26,7 @@ class UrlLauncher(private val context: Context) : UriHandler {
|
||||
customTabsIntent.launchUrl(context, Uri.parse(uri))
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
val error = "Failed to open URL: $uri"
|
||||
Napier.d(tag = "UrlLauncher") { error }
|
||||
Napier.d(throwable = e, tag = "UrlLauncher") { error }
|
||||
Toast.makeText(context, error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.user
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -50,7 +56,7 @@ fun UserProfile(
|
||||
}
|
||||
when (user) {
|
||||
is Success<*> -> {
|
||||
UserProfileInternal((user as Success<User>).data)
|
||||
UserProfileInternal(user = (user as Success<User>).data, modifier = modifier)
|
||||
}
|
||||
is Error -> {
|
||||
val error = user as Error
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
package dev.msfjarvis.claw.common
|
||||
|
||||
fun <T> unsafeLazy(initializer: () -> T) = lazy(LazyThreadSafetyMode.NONE, initializer)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user