Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0312c3f431 | ||
|
|
7d324ddb8c | ||
|
|
c7b7a786f3 | ||
|
|
26f69c2027 | ||
|
|
450601f26b | ||
|
|
2ab69de686 | ||
|
|
0083aa69b5 | ||
|
|
18d82a5eb3 | ||
|
|
0d668ac3aa | ||
|
|
b1a32496ba | ||
|
|
4db59db7a1 | ||
|
|
0843352514 | ||
|
|
aa50d6449d | ||
|
|
6a41b966c5 | ||
|
|
943b9bd091 | ||
|
|
010f69031a | ||
|
|
168271a8f4 | ||
|
|
ea92d64972 | ||
|
|
766f14cd22 | ||
|
|
33210319bb | ||
|
|
7ef0a8fc51 | ||
|
|
1f850435b1 | ||
|
|
9d8fd77a08 | ||
|
|
e2c760e7f3 | ||
|
|
51a8538d6a | ||
|
|
4b9b148519 | ||
|
|
ced16c3794 | ||
|
|
e824294ea0 | ||
|
|
ffce7c2b3e | ||
|
|
f0d615bd44 | ||
|
|
df546947bb | ||
|
|
3e0544279c | ||
|
|
25bfed3c71 | ||
|
|
6c88ecf5d5 | ||
|
|
4f60db29bd | ||
|
|
e6eaadbaa1 | ||
|
|
30fa04db56 | ||
|
|
74684fe115 | ||
|
|
d36bcd6b26 | ||
|
|
a2f3faad0f | ||
|
|
9b25cd0fea | ||
|
|
db75d1edfe | ||
|
|
ac98ebda3f | ||
|
|
c8d03c515a | ||
|
|
a77f098822 | ||
|
|
608720ac35 | ||
|
|
f1b68b238c | ||
|
|
977e3cbc8e | ||
|
|
fe2c3f630b | ||
|
|
4fac861656 | ||
|
|
0ec03fc979 | ||
|
|
46a9658dcd | ||
|
|
387881181a | ||
|
|
cb619ca6a9 | ||
|
|
37fc528053 | ||
|
|
b19617f8c7 | ||
|
|
3829abeb8f | ||
|
|
bd5cf0f167 | ||
|
|
a8ba0ed3d9 | ||
|
|
1267cf81e3 | ||
|
|
f48ef0206f | ||
|
|
7b31dc740b | ||
|
|
69c76a06c2 | ||
|
|
83e01c7cbc |
@@ -0,0 +1,93 @@
|
||||
# This .editorconfig section approximates ktfmt's formatting rules. You can include it in an
|
||||
# existing .editorconfig file or use it standalone by copying it to <project root>/.editorconfig
|
||||
# and making sure your editor is set to read settings from .editorconfig files.
|
||||
#
|
||||
# It includes editor-specific config options for IntelliJ IDEA.
|
||||
#
|
||||
# If any option is wrong, PR are welcome
|
||||
|
||||
[{*.kt,*.kts}]
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
max_line_length = 100
|
||||
indent_size = 2
|
||||
ij_continuation_indent_size = 2
|
||||
ij_java_names_count_to_use_import_on_demand = 9999
|
||||
ij_kotlin_align_in_columns_case_branch = false
|
||||
ij_kotlin_align_multiline_binary_operation = false
|
||||
ij_kotlin_align_multiline_extends_list = false
|
||||
ij_kotlin_align_multiline_method_parentheses = false
|
||||
ij_kotlin_align_multiline_parameters = true
|
||||
ij_kotlin_align_multiline_parameters_in_calls = false
|
||||
ij_kotlin_allow_trailing_comma = true
|
||||
ij_kotlin_allow_trailing_comma_on_call_site = true
|
||||
ij_kotlin_assignment_wrap = normal
|
||||
ij_kotlin_blank_lines_after_class_header = 0
|
||||
ij_kotlin_blank_lines_around_block_when_branches = 0
|
||||
ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
|
||||
ij_kotlin_block_comment_at_first_column = true
|
||||
ij_kotlin_call_parameters_new_line_after_left_paren = true
|
||||
ij_kotlin_call_parameters_right_paren_on_new_line = false
|
||||
ij_kotlin_call_parameters_wrap = on_every_item
|
||||
ij_kotlin_catch_on_new_line = false
|
||||
ij_kotlin_class_annotation_wrap = split_into_lines
|
||||
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
|
||||
ij_kotlin_continuation_indent_for_chained_calls = true
|
||||
ij_kotlin_continuation_indent_for_expression_bodies = true
|
||||
ij_kotlin_continuation_indent_in_argument_lists = true
|
||||
ij_kotlin_continuation_indent_in_elvis = false
|
||||
ij_kotlin_continuation_indent_in_if_conditions = false
|
||||
ij_kotlin_continuation_indent_in_parameter_lists = false
|
||||
ij_kotlin_continuation_indent_in_supertype_lists = false
|
||||
ij_kotlin_else_on_new_line = false
|
||||
ij_kotlin_enum_constants_wrap = off
|
||||
ij_kotlin_extends_list_wrap = normal
|
||||
ij_kotlin_field_annotation_wrap = split_into_lines
|
||||
ij_kotlin_finally_on_new_line = false
|
||||
ij_kotlin_if_rparen_on_new_line = false
|
||||
ij_kotlin_import_nested_classes = false
|
||||
ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
|
||||
ij_kotlin_keep_blank_lines_before_right_brace = 2
|
||||
ij_kotlin_keep_blank_lines_in_code = 2
|
||||
ij_kotlin_keep_blank_lines_in_declarations = 2
|
||||
ij_kotlin_keep_first_column_comment = true
|
||||
ij_kotlin_keep_indents_on_empty_lines = false
|
||||
ij_kotlin_keep_line_breaks = true
|
||||
ij_kotlin_lbrace_on_next_line = false
|
||||
ij_kotlin_line_comment_add_space = false
|
||||
ij_kotlin_line_comment_at_first_column = true
|
||||
ij_kotlin_method_annotation_wrap = split_into_lines
|
||||
ij_kotlin_method_call_chain_wrap = normal
|
||||
ij_kotlin_method_parameters_new_line_after_left_paren = true
|
||||
ij_kotlin_method_parameters_right_paren_on_new_line = true
|
||||
ij_kotlin_method_parameters_wrap = on_every_item
|
||||
ij_kotlin_name_count_to_use_star_import = 9999
|
||||
ij_kotlin_name_count_to_use_star_import_for_members = 9999
|
||||
ij_kotlin_parameter_annotation_wrap = off
|
||||
ij_kotlin_space_after_comma = true
|
||||
ij_kotlin_space_after_extend_colon = true
|
||||
ij_kotlin_space_after_type_colon = true
|
||||
ij_kotlin_space_before_catch_parentheses = true
|
||||
ij_kotlin_space_before_comma = false
|
||||
ij_kotlin_space_before_extend_colon = true
|
||||
ij_kotlin_space_before_for_parentheses = true
|
||||
ij_kotlin_space_before_if_parentheses = true
|
||||
ij_kotlin_space_before_lambda_arrow = true
|
||||
ij_kotlin_space_before_type_colon = false
|
||||
ij_kotlin_space_before_when_parentheses = true
|
||||
ij_kotlin_space_before_while_parentheses = true
|
||||
ij_kotlin_spaces_around_additive_operators = true
|
||||
ij_kotlin_spaces_around_assignment_operators = true
|
||||
ij_kotlin_spaces_around_equality_operators = true
|
||||
ij_kotlin_spaces_around_function_type_arrow = true
|
||||
ij_kotlin_spaces_around_logical_operators = true
|
||||
ij_kotlin_spaces_around_multiplicative_operators = true
|
||||
ij_kotlin_spaces_around_range = false
|
||||
ij_kotlin_spaces_around_relational_operators = true
|
||||
ij_kotlin_spaces_around_unary_operator = false
|
||||
ij_kotlin_spaces_around_when_arrow = true
|
||||
ij_kotlin_variable_annotation_wrap = off
|
||||
ij_kotlin_while_on_new_line = false
|
||||
ij_kotlin_wrap_elvis_expressions = 1
|
||||
ij_kotlin_wrap_expression_body_functions = 1
|
||||
ij_kotlin_wrap_first_method_in_call_chain = false
|
||||
@@ -73,6 +73,16 @@
|
||||
],
|
||||
"datasourceTemplate": "maven",
|
||||
"depNameTemplate": "androidx.compose.compiler:compiler"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"qodana.yaml"
|
||||
],
|
||||
"matchStrings": [
|
||||
"jetbrains/qodana-jvm-community:(?<currentValue>.*)"
|
||||
],
|
||||
"datasourceTemplate": "docker",
|
||||
"depNameTemplate": "jetbrains/qodana-jvm-community",
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
name: Baseline profile generation
|
||||
|
||||
on:
|
||||
# every day at 00:43
|
||||
schedule:
|
||||
- cron: '43 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
baseline-profile:
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 45
|
||||
env:
|
||||
TERM: dumb
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3.1.0
|
||||
with:
|
||||
token: ${{ secrets.POST_RELEASE_GH_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@c3ac5dd0ed8db40fedb61c32fbe677e6b355e94c # v3.8.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
|
||||
|
||||
- name: Decrypt secrets
|
||||
run: |
|
||||
./scripts/setup-age.sh
|
||||
./scripts/signing-setup.sh "$ENCRYPT_KEY"
|
||||
env:
|
||||
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
|
||||
|
||||
# This allows us to build most of what we need without the emulator running
|
||||
# and using resources
|
||||
- name: Build app and benchmark
|
||||
run: ./gradlew :benchmark:assembleBenchmark :android:assembleBenchmark
|
||||
|
||||
# Now use reactivecircus/android-emulator-runner to spin up an emulator and run our
|
||||
# baseline profile generator. We need to manually pull the baseline profiles off the
|
||||
# emulator when using the GA runner
|
||||
- name: Run benchmark
|
||||
uses: reactivecircus/android-emulator-runner@50986b1464923454c95e261820bc626f38490ec0 # v2
|
||||
with:
|
||||
api-level: 33
|
||||
target: google_apis
|
||||
arch: x86_64
|
||||
profile: Galaxy Nexus
|
||||
script: |
|
||||
# Run our benchmark, enabling only tests using BaselineProfile
|
||||
./gradlew connectedBenchmarkAndroidTest -Pandroid.testInstrumentationRunnerArguments.size=large
|
||||
# Need to manually pull the generated profiles from the emulator
|
||||
adb pull /sdcard/Android/media/dev.msfjarvis.claw.benchmark benchmark/build/outputs/baseline-prof/
|
||||
|
||||
# If we're on main branch, copy over the baseline profile and
|
||||
# commit it to the repository (if changed)
|
||||
- name: Commit baseline profile into main
|
||||
if: github.ref == 'refs/heads/main'
|
||||
run: |
|
||||
# Pull down any changes which may have been committed while this workflow has been running
|
||||
git pull
|
||||
# Sort the baseline profile, saving it to app/
|
||||
sort -o app/src/main/baseline-prof.txt benchmark/build/outputs/baseline-prof/BaselineProfileGenerator_startup-baseline-prof.txt
|
||||
# If the baseline profile has changed, commit it
|
||||
if [[ $(git diff --stat app/src/main/baseline-prof.txt) != '' ]]; then
|
||||
git add app/src/main/baseline-prof.txt
|
||||
git commit -m "chore(android): refresh baseline profile" && git push
|
||||
fi
|
||||
|
||||
# Upload the entire generated folder and attach it to the CI run
|
||||
- name: Attach baseline profile
|
||||
uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # v3.1.1
|
||||
with:
|
||||
name: Baseline profile output
|
||||
path: benchmark/build/outputs/baseline-prof
|
||||
|
||||
- name: Clean secrets
|
||||
if: always()
|
||||
run: scripts/signing-cleanup.sh
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3.1.0
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3.6.0
|
||||
uses: actions/setup-java@c3ac5dd0ed8db40fedb61c32fbe677e6b355e94c # v3.8.0
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 18
|
||||
@@ -56,13 +56,15 @@ jobs:
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3.1.0
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3.6.0
|
||||
uses: actions/setup-java@c3ac5dd0ed8db40fedb61c32fbe677e6b355e94c # v3.8.0
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 18
|
||||
|
||||
- name: Decrypt secrets
|
||||
run: scripts/signing-setup.sh "$ENCRYPT_KEY"
|
||||
run: |
|
||||
./scripts/setup-age.sh
|
||||
./scripts/signing-setup.sh "$ENCRYPT_KEY"
|
||||
env:
|
||||
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
schedule:
|
||||
- cron: "20 3 * * 0"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2
|
||||
with:
|
||||
languages: java
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@a669cc5936cc5e1b6a362ec1ff9e410dc570d190 # v2
|
||||
with:
|
||||
category: "/language:java"
|
||||
@@ -0,0 +1,19 @@
|
||||
name: Qodana
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
qodana:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: 'Qodana Scan'
|
||||
uses: JetBrains/qodana-action@3fdeefa830a7634a5c04f1cd70fcfb69956137a2 # v2022.3.0
|
||||
env:
|
||||
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@de1bb2b0c5634f0fc4438d7aa9944e68f9bf86cc # tag=v3.6.0
|
||||
uses: actions/setup-java@c3ac5dd0ed8db40fedb61c32fbe677e6b355e94c # v3.8.0
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 18
|
||||
@@ -31,7 +31,9 @@ jobs:
|
||||
cache-read-only: true
|
||||
|
||||
- name: Decrypt secrets
|
||||
run: scripts/signing-setup.sh "$ENCRYPT_KEY"
|
||||
run: |
|
||||
./scripts/setup-age.sh
|
||||
./scripts/signing-setup.sh "$ENCRYPT_KEY"
|
||||
env:
|
||||
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
|
||||
|
||||
@@ -45,7 +47,8 @@ jobs:
|
||||
- name: Update changelog
|
||||
uses: thomaseizinger/keep-a-changelog-new-release@5bc232893483441c5d6cd6c9dcb1e48abf9a2bae # 1.3.0
|
||||
with:
|
||||
tag: ${{ env.VERSION }}
|
||||
tag: v${{ env.VERSION }}
|
||||
version: ${{ env.VERSION }}
|
||||
|
||||
- name: Commit changes
|
||||
shell: bash
|
||||
|
||||
+40
-3
@@ -7,6 +7,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.14.0] - 2022-12-12
|
||||
|
||||
### Changed
|
||||
|
||||
- Rework how often saved posts are updated
|
||||
- Collapsing a comment now collapses all comments under it similar to how it works on the website
|
||||
|
||||
## [1.13.0] - 2022-12-10
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix crash when viewing [jcs](https://lobste.rs/u/jcs)' comments
|
||||
|
||||
## [1.12.0] - 2022-12-09
|
||||
|
||||
### Changed
|
||||
|
||||
- Do not schedule post update job every time the app starts
|
||||
- Improve favicon loading to reduce unnecessary redraws
|
||||
- Fix post lists being reloaded unnecessarily
|
||||
|
||||
## [1.11.0] - 2022-12-07
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade to Compose 2022.12.00 release
|
||||
- Refresh packaged baseline profile
|
||||
- Improve how saved state of posts is queried
|
||||
|
||||
## [1.10.0] - 2022-11-30
|
||||
|
||||
### Changed
|
||||
@@ -88,11 +117,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
- Initial Play Store release
|
||||
|
||||
[Unreleased]: https://github.com/msfjarvis/compose-lobsters/compare/1.10.0...HEAD
|
||||
[Unreleased]: https://github.com/msfjarvis/compose-lobsters/compare/v1.14.0...HEAD
|
||||
|
||||
[1.10.0]: https://github.com/msfjarvis/compose-lobsters/compare/1.9.0...1.10.0
|
||||
[1.14.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.13.0...v1.14.0
|
||||
|
||||
[1.9.0]: https://github.com/msfjarvis/compose-lobsters/compare/1.8.0...1.9.0
|
||||
[1.13.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.12.0...v1.13.0
|
||||
|
||||
[1.12.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.11.0...v1.12.0
|
||||
|
||||
[1.11.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.10.0...v1.11.0
|
||||
|
||||
[1.10.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.9.0...v1.10.0
|
||||
|
||||
[1.9.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.8.0...v1.9.0
|
||||
|
||||
[1.8.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.7.0...1.8.0
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
*/
|
||||
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
|
||||
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
id("dev.msfjarvis.claw.android-application")
|
||||
id("dev.msfjarvis.claw.rename-artifacts")
|
||||
@@ -23,6 +26,22 @@ whetstone {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs +=
|
||||
listOf(
|
||||
"-P",
|
||||
"plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
|
||||
rootProject.buildDir.absolutePath +
|
||||
"/compose_metrics/",
|
||||
"-P",
|
||||
"plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
|
||||
rootProject.buildDir.absolutePath +
|
||||
"/compose_metrics/",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "dev.msfjarvis.claw.android"
|
||||
defaultConfig { applicationId = "dev.msfjarvis.claw.android" }
|
||||
|
||||
@@ -149,6 +149,19 @@
|
||||
android:name="androidx.work.WorkManagerInitializer"
|
||||
android:value="androidx.startup"
|
||||
tools:node="remove" />
|
||||
<meta-data
|
||||
android:name="com.deliveryhero.whetstone.worker.WhetstoneWorkerInitializer"
|
||||
android:value="androidx.startup"
|
||||
tools:node="remove" />
|
||||
</provider>
|
||||
<!-- https://issuetracker.google.com/issues/258619948 -->
|
||||
<receiver
|
||||
android:name="androidx.profileinstaller.ProfileInstallReceiver"
|
||||
android:permission="android.permission.DUMP"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="androidx.profileinstaller.action.BENCHMARK_OPERATION" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
Generated
+12168
-11504
File diff suppressed because it is too large
Load Diff
@@ -7,10 +7,17 @@
|
||||
package dev.msfjarvis.claw.android
|
||||
|
||||
import android.app.Application
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.ExistingPeriodicWorkPolicy
|
||||
import androidx.work.NetworkType
|
||||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import com.deliveryhero.whetstone.Whetstone
|
||||
import com.deliveryhero.whetstone.app.ApplicationComponentOwner
|
||||
import com.deliveryhero.whetstone.app.ContributesAppInjector
|
||||
import dev.msfjarvis.claw.android.work.SavedPostUpdaterWorker
|
||||
import dev.msfjarvis.claw.core.injection.AppPlugin
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesAppInjector(generateAppComponent = true)
|
||||
@@ -25,5 +32,26 @@ class ClawApplication : Application(), ApplicationComponentOwner {
|
||||
Whetstone.inject(this)
|
||||
super.onCreate()
|
||||
plugins.forEach { plugin -> plugin.apply(this) }
|
||||
val postUpdateWorkRequest =
|
||||
PeriodicWorkRequestBuilder<SavedPostUpdaterWorker>(POST_REFRESH_PERIOD, TimeUnit.HOURS)
|
||||
.setConstraints(
|
||||
Constraints.Builder()
|
||||
.setRequiresCharging(false)
|
||||
.setRequiresBatteryNotLow(true)
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.setRequiresDeviceIdle(true)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
WorkManager.getInstance(this)
|
||||
.enqueueUniquePeriodicWork(
|
||||
"updateSavedPosts",
|
||||
ExistingPeriodicWorkPolicy.UPDATE,
|
||||
postUpdateWorkRequest,
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val POST_REFRESH_PERIOD = 24L // 24 hours
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,18 +12,11 @@ import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.ExistingPeriodicWorkPolicy
|
||||
import androidx.work.NetworkType
|
||||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import com.deliveryhero.whetstone.Whetstone
|
||||
import com.deliveryhero.whetstone.activity.ContributesActivityInjector
|
||||
import dev.msfjarvis.claw.android.ui.LobstersApp
|
||||
import dev.msfjarvis.claw.android.work.SavedPostUpdaterWorker
|
||||
import dev.msfjarvis.claw.common.comments.HTMLConverter
|
||||
import dev.msfjarvis.claw.common.urllauncher.UrlLauncher
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesActivityInjector
|
||||
@@ -44,23 +37,6 @@ class MainActivity : ComponentActivity() {
|
||||
setWebUri = { url -> webUri = url },
|
||||
)
|
||||
}
|
||||
val postUpdateWorkRequest =
|
||||
PeriodicWorkRequestBuilder<SavedPostUpdaterWorker>(POST_REFRESH_PERIOD, TimeUnit.HOURS)
|
||||
.setConstraints(
|
||||
Constraints.Builder()
|
||||
.setRequiresCharging(false)
|
||||
.setRequiresBatteryNotLow(true)
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.setRequiresDeviceIdle(true)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
WorkManager.getInstance(this@MainActivity)
|
||||
.enqueueUniquePeriodicWork(
|
||||
"updateSavedPosts",
|
||||
ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
|
||||
postUpdateWorkRequest,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onProvideAssistContent(outContent: AssistContent?) {
|
||||
@@ -73,8 +49,4 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val POST_REFRESH_PERIOD = 24L // 24 hours
|
||||
}
|
||||
}
|
||||
|
||||
+4
-13
@@ -8,22 +8,13 @@ package dev.msfjarvis.claw.android.injection
|
||||
|
||||
import com.deliveryhero.whetstone.app.ApplicationScope
|
||||
import com.squareup.anvil.annotations.ContributesTo
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dev.msfjarvis.claw.android.ui.util.HTMLConverterImpl
|
||||
import dev.msfjarvis.claw.common.comments.HTMLConverter
|
||||
import io.github.furstenheim.CopyDown
|
||||
|
||||
@Module
|
||||
@ContributesTo(ApplicationScope::class)
|
||||
object HTMLConverterModule {
|
||||
|
||||
@Provides
|
||||
fun provideHTMLConverter() =
|
||||
object : HTMLConverter {
|
||||
private val copydown = CopyDown()
|
||||
|
||||
override fun convertHTMLToMarkdown(html: String): String {
|
||||
return copydown.convert(html)
|
||||
}
|
||||
}
|
||||
interface HTMLConverterModule {
|
||||
@Binds fun HTMLConverterImpl.bind(): HTMLConverter
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.injection
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import androidx.work.Configuration
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkerFactory
|
||||
import com.deliveryhero.whetstone.app.ApplicationScope
|
||||
import com.squareup.anvil.annotations.ContributesMultibinding
|
||||
import dev.msfjarvis.claw.core.injection.AppPlugin
|
||||
import javax.inject.Inject
|
||||
|
||||
@ContributesMultibinding(ApplicationScope::class)
|
||||
class WorkManagerPlugin
|
||||
@Inject
|
||||
constructor(
|
||||
private val workerFactory: WorkerFactory,
|
||||
) : AppPlugin {
|
||||
override fun apply(application: Application) {
|
||||
WorkManager.initialize(
|
||||
application,
|
||||
Configuration.Builder()
|
||||
.setWorkerFactory(workerFactory)
|
||||
.setMinimumLoggingLevel(Log.DEBUG)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.ui.util
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import dev.msfjarvis.claw.common.comments.HTMLConverter
|
||||
import io.github.furstenheim.CopyDown
|
||||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
class HTMLConverterImpl @Inject constructor() : HTMLConverter {
|
||||
private val copydown = CopyDown()
|
||||
|
||||
override fun convertHTMLToMarkdown(html: String): String {
|
||||
return copydown.convert(html)
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,15 @@ constructor(
|
||||
withContext(dbDispatcher) { savedPostQueries.insertOrReplacePost(post) }
|
||||
}
|
||||
|
||||
suspend fun savePosts(posts: List<SavedPost>) {
|
||||
Napier.d(tag = TAG) { "Saving posts: ${posts.joinToString(",") { it.shortId }}" }
|
||||
withContext(dbDispatcher) {
|
||||
savedPostQueries.transaction {
|
||||
posts.forEach { post -> savedPostQueries.insertOrReplacePost(post) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun removePost(post: SavedPost) {
|
||||
Napier.d(tag = TAG) { "Removing post: ${post.shortId}" }
|
||||
withContext(dbDispatcher) { savedPostQueries.deletePost(post.shortId) }
|
||||
|
||||
@@ -12,15 +12,13 @@ import androidx.work.WorkerParameters
|
||||
import com.deliveryhero.whetstone.ForScope
|
||||
import com.deliveryhero.whetstone.worker.ContributesWorker
|
||||
import com.deliveryhero.whetstone.worker.WorkerScope
|
||||
import com.slack.eithernet.ApiResult
|
||||
import com.slack.eithernet.ApiResult.Success
|
||||
import dev.msfjarvis.claw.android.viewmodel.SavedPostsRepository
|
||||
import dev.msfjarvis.claw.api.LobstersApi
|
||||
import dev.msfjarvis.claw.common.posts.toDbModel
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
|
||||
/**
|
||||
* WorkManager-backed [CoroutineWorker] that gets all the posts from [SavedPostsRepository], fetches
|
||||
@@ -38,19 +36,13 @@ constructor(
|
||||
private val lobstersApi: LobstersApi,
|
||||
) : CoroutineWorker(appContext, workerParams) {
|
||||
override suspend fun doWork(): Result {
|
||||
val posts = savedPostsRepository.savedPosts.first()
|
||||
supervisorScope {
|
||||
posts
|
||||
.map { post ->
|
||||
async {
|
||||
val details = lobstersApi.getPostDetails(post.shortId)
|
||||
if (details is ApiResult.Success) {
|
||||
savedPostsRepository.savePost(details.value.toDbModel())
|
||||
}
|
||||
}
|
||||
}
|
||||
.awaitAll()
|
||||
}
|
||||
savedPostsRepository.savedPosts
|
||||
.first()
|
||||
.map { post -> lobstersApi.getPostDetails(post.shortId) }
|
||||
.filterIsInstance<Success<LobstersPostDetails>>()
|
||||
.map { result -> result.value.toDbModel() }
|
||||
.toList()
|
||||
.let { savedPostsRepository.savePosts(it) }
|
||||
return Result.success()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# This file was automatically generated by 'versioning-plugin'. DO NOT EDIT MANUALLY.
|
||||
#
|
||||
versioning-plugin.versionCode=11000
|
||||
versioning-plugin.versionName=1.10.0
|
||||
versioning-plugin.versionCode=11400
|
||||
versioning-plugin.versionName=1.14.0
|
||||
|
||||
@@ -15,14 +15,10 @@ import androidx.test.filters.LargeTest
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
@LargeTest
|
||||
@Ignore(
|
||||
"These tests fail saying that ProfileInstaller 1.3.0-alpha02 should be used which we already are"
|
||||
)
|
||||
class BaselineProfileBenchmark {
|
||||
@get:Rule val benchmarkRule = MacrobenchmarkRule()
|
||||
private lateinit var device: UiDevice
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
package dev.msfjarvis.claw.benchmark
|
||||
|
||||
import androidx.benchmark.macro.ExperimentalStableBaselineProfilesApi
|
||||
import androidx.benchmark.macro.junit4.BaselineProfileRule
|
||||
import androidx.test.filters.MediumTest
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
@@ -14,6 +15,7 @@ import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
@ExperimentalStableBaselineProfilesApi
|
||||
@MediumTest
|
||||
class BaselineProfileGenerator {
|
||||
@get:Rule val baselineProfileRule = BaselineProfileRule()
|
||||
@@ -27,5 +29,10 @@ class BaselineProfileGenerator {
|
||||
|
||||
@Test
|
||||
fun startup() =
|
||||
baselineProfileRule.collectBaselineProfile(packageName = PACKAGE_NAME) { exploreUI(device) }
|
||||
baselineProfileRule.collectStableBaselineProfile(
|
||||
packageName = PACKAGE_NAME,
|
||||
maxIterations = 5,
|
||||
) {
|
||||
exploreUI(device)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,6 @@ private fun UiDevice.returnToHottestScreen() {
|
||||
}
|
||||
|
||||
private fun UiDevice.openCommentsScreen() {
|
||||
findObjects(By.desc(COMMENT_BUTTON_DESC)).first().click()
|
||||
waitForSubmitterName()
|
||||
findObjects(By.desc(COMMENT_BUTTON_DESC)).first().click()
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
/build
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2022 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins { `kotlin-dsl` }
|
||||
|
||||
afterEvaluate {
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
sourceCompatibility = JavaVersion.VERSION_11.toString()
|
||||
targetCompatibility = JavaVersion.VERSION_11.toString()
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||
freeCompilerArgs = freeCompilerArgs + "-Xsam-conversions=class"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
register("android-application") {
|
||||
id = "dev.msfjarvis.claw.android-application"
|
||||
implementationClass = "dev.msfjarvis.aps.gradle.ApplicationPlugin"
|
||||
}
|
||||
register("android-common") {
|
||||
id = "dev.msfjarvis.claw.android-common"
|
||||
implementationClass = "dev.msfjarvis.aps.gradle.AndroidCommonPlugin"
|
||||
}
|
||||
register("android-library") {
|
||||
id = "dev.msfjarvis.claw.android-library"
|
||||
implementationClass = "dev.msfjarvis.aps.gradle.LibraryPlugin"
|
||||
}
|
||||
register("rename-artifacts") {
|
||||
id = "dev.msfjarvis.claw.rename-artifacts"
|
||||
implementationClass = "dev.msfjarvis.aps.gradle.RenameArtifactsPlugin"
|
||||
}
|
||||
register("versioning") {
|
||||
id = "dev.msfjarvis.claw.versioning-plugin"
|
||||
implementationClass = "dev.msfjarvis.aps.gradle.versioning.VersioningPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.build.agp)
|
||||
implementation(libs.build.cachefix)
|
||||
implementation(libs.build.semver)
|
||||
}
|
||||
@@ -27,38 +27,60 @@ afterEvaluate {
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
register("android-application") {
|
||||
id = "dev.msfjarvis.claw.android-application"
|
||||
implementationClass = "dev.msfjarvis.claw.gradle.ApplicationPlugin"
|
||||
}
|
||||
register("android-common") {
|
||||
id = "dev.msfjarvis.claw.android-common"
|
||||
implementationClass = "dev.msfjarvis.claw.gradle.AndroidCommonPlugin"
|
||||
}
|
||||
register("android-library") {
|
||||
id = "dev.msfjarvis.claw.android-library"
|
||||
implementationClass = "dev.msfjarvis.claw.gradle.LibraryPlugin"
|
||||
}
|
||||
register("kotlin-android") {
|
||||
id = "dev.msfjarvis.claw.kotlin-android"
|
||||
implementationClass = "dev.msfjarvis.aps.gradle.KotlinAndroidPlugin"
|
||||
implementationClass = "dev.msfjarvis.claw.gradle.KotlinAndroidPlugin"
|
||||
}
|
||||
register("kotlin-common") {
|
||||
id = "dev.msfjarvis.claw.kotlin-common"
|
||||
implementationClass = "dev.msfjarvis.aps.gradle.KotlinCommonPlugin"
|
||||
implementationClass = "dev.msfjarvis.claw.gradle.KotlinCommonPlugin"
|
||||
}
|
||||
register("kotlin-kapt") {
|
||||
id = "dev.msfjarvis.claw.kotlin-kapt"
|
||||
implementationClass = "dev.msfjarvis.aps.gradle.KotlinKaptPlugin"
|
||||
implementationClass = "dev.msfjarvis.claw.gradle.KotlinKaptPlugin"
|
||||
}
|
||||
register("kotlin-library") {
|
||||
id = "dev.msfjarvis.claw.kotlin-library"
|
||||
implementationClass = "dev.msfjarvis.aps.gradle.KotlinLibraryPlugin"
|
||||
implementationClass = "dev.msfjarvis.claw.gradle.KotlinLibraryPlugin"
|
||||
}
|
||||
register("rename-artifacts") {
|
||||
id = "dev.msfjarvis.claw.rename-artifacts"
|
||||
implementationClass = "dev.msfjarvis.claw.gradle.RenameArtifactsPlugin"
|
||||
}
|
||||
register("spotless") {
|
||||
id = "dev.msfjarvis.claw.spotless"
|
||||
implementationClass = "dev.msfjarvis.aps.gradle.SpotlessPlugin"
|
||||
implementationClass = "dev.msfjarvis.claw.gradle.SpotlessPlugin"
|
||||
}
|
||||
register("versioning") {
|
||||
id = "dev.msfjarvis.claw.versioning-plugin"
|
||||
implementationClass = "dev.msfjarvis.claw.gradle.versioning.VersioningPlugin"
|
||||
}
|
||||
register("versions") {
|
||||
id = "dev.msfjarvis.claw.versions"
|
||||
implementationClass = "dev.msfjarvis.aps.gradle.DependencyUpdatesPlugin"
|
||||
implementationClass = "dev.msfjarvis.claw.gradle.DependencyUpdatesPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.build.agp)
|
||||
implementation(libs.build.cachefix)
|
||||
implementation(libs.build.detekt)
|
||||
implementation(libs.build.kotlin.gradle)
|
||||
implementation(libs.build.kotlin.serialization)
|
||||
implementation(libs.build.semver)
|
||||
implementation(libs.build.spotless)
|
||||
implementation(libs.build.vcu)
|
||||
implementation(libs.build.versions)
|
||||
@@ -39,7 +39,3 @@ dependencyResolutionManagement {
|
||||
}
|
||||
versionCatalogs { create("libs") { from(files("../gradle/libs.versions.toml")) } }
|
||||
}
|
||||
|
||||
include("android-plugins")
|
||||
|
||||
include("kotlin-plugins")
|
||||
|
||||
+5
-4
@@ -6,19 +6,20 @@
|
||||
*/
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
package dev.msfjarvis.aps.gradle
|
||||
package dev.msfjarvis.claw.gradle
|
||||
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import com.android.build.api.dsl.LibraryExtension
|
||||
import com.android.build.api.dsl.Lint
|
||||
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
||||
import com.android.build.api.variant.LibraryAndroidComponentsExtension
|
||||
import com.android.build.gradle.BaseExtension
|
||||
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
|
||||
import org.gradle.android.AndroidCacheFixPlugin
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.findByType
|
||||
|
||||
private const val SLIM_TESTS_PROPERTY = "slimTests"
|
||||
@@ -34,7 +35,7 @@ class AndroidCommonPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
project.configureSlimTests()
|
||||
project.pluginManager.apply(AndroidCacheFixPlugin::class)
|
||||
project.extensions.findByType<BaseExtension>()?.run {
|
||||
project.extensions.configure<BaseExtension> {
|
||||
compileSdkVersion(COMPILE_SDK)
|
||||
defaultConfig {
|
||||
minSdk = MIN_SDK
|
||||
@@ -60,7 +61,7 @@ class AndroidCommonPlugin : Plugin<Project> {
|
||||
unitTests.isReturnDefaultValues = true
|
||||
}
|
||||
}
|
||||
project.extensions.findByType<BaseAppModuleExtension>()?.run { lint.configureLint(project) }
|
||||
project.extensions.findByType<ApplicationExtension>()?.run { lint.configureLint(project) }
|
||||
project.extensions.findByType<LibraryExtension>()?.run { lint.configureLint(project) }
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -6,15 +6,15 @@
|
||||
*/
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
package dev.msfjarvis.aps.gradle
|
||||
package dev.msfjarvis.claw.gradle
|
||||
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import com.android.build.gradle.AppPlugin
|
||||
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
|
||||
import dev.msfjarvis.aps.gradle.signing.configureBuildSigning
|
||||
import dev.msfjarvis.claw.gradle.signing.configureBuildSigning
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
|
||||
@Suppress("Unused")
|
||||
class ApplicationPlugin : Plugin<Project> {
|
||||
@@ -22,8 +22,8 @@ class ApplicationPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
project.pluginManager.apply(AppPlugin::class)
|
||||
project.pluginManager.apply(AndroidCommonPlugin::class)
|
||||
project.extensions.getByType<BaseAppModuleExtension>().run {
|
||||
adbOptions.installOptions("--user 0")
|
||||
project.extensions.configure<ApplicationExtension> {
|
||||
installation.installOptions("--user 0")
|
||||
|
||||
dependenciesInfo {
|
||||
includeInBundle = false
|
||||
+3
-3
@@ -4,7 +4,7 @@
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle
|
||||
package dev.msfjarvis.claw.gradle
|
||||
|
||||
import com.github.benmanes.gradle.versions.VersionsPlugin
|
||||
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
|
||||
@@ -13,7 +13,7 @@ import nl.littlerobots.vcu.plugin.VersionCatalogUpdatePlugin
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
|
||||
@Suppress("Unused")
|
||||
@@ -31,7 +31,7 @@ class DependencyUpdatesPlugin : Plugin<Project> {
|
||||
}
|
||||
checkForGradleUpdate = true
|
||||
}
|
||||
project.extensions.getByType<VersionCatalogUpdateExtension>().run {
|
||||
project.extensions.configure<VersionCatalogUpdateExtension> {
|
||||
keep.keepUnusedLibraries.set(true)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle
|
||||
package dev.msfjarvis.claw.gradle
|
||||
|
||||
import io.gitlab.arturbosch.detekt.DetektPlugin
|
||||
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle
|
||||
package dev.msfjarvis.claw.gradle
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle
|
||||
package dev.msfjarvis.claw.gradle
|
||||
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Plugin
|
||||
+3
-3
@@ -4,12 +4,12 @@
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle
|
||||
package dev.msfjarvis.claw.gradle
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.jetbrains.kotlin.gradle.internal.Kapt3GradleSubplugin
|
||||
import org.jetbrains.kotlin.gradle.plugin.KaptExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPluginWrapper
|
||||
@@ -23,7 +23,7 @@ class KotlinKaptPlugin : Plugin<Project> {
|
||||
apply(Kapt3GradleSubplugin::class)
|
||||
}
|
||||
project.afterEvaluate {
|
||||
project.extensions.getByType<KaptExtension>().run {
|
||||
project.extensions.configure<KaptExtension> {
|
||||
javacOptions {
|
||||
if (hasDaggerCompilerDependency()) {
|
||||
// https://dagger.dev/dev-guide/compiler-options#fastinit-mode
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle
|
||||
package dev.msfjarvis.claw.gradle
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle
|
||||
package dev.msfjarvis.claw.gradle
|
||||
|
||||
import com.android.build.gradle.LibraryPlugin
|
||||
import org.gradle.api.Plugin
|
||||
+5
-5
@@ -4,17 +4,17 @@
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle
|
||||
package dev.msfjarvis.claw.gradle
|
||||
|
||||
import com.android.build.api.artifact.SingleArtifact
|
||||
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
||||
import com.android.build.api.variant.VariantOutputConfiguration
|
||||
import dev.msfjarvis.aps.gradle.artifacts.CollectApksTask
|
||||
import dev.msfjarvis.aps.gradle.artifacts.CollectBundleTask
|
||||
import dev.msfjarvis.claw.gradle.artifacts.CollectApksTask
|
||||
import dev.msfjarvis.claw.gradle.artifacts.CollectBundleTask
|
||||
import java.util.Locale
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.register
|
||||
|
||||
@Suppress("Unused")
|
||||
@@ -22,7 +22,7 @@ class RenameArtifactsPlugin : Plugin<Project> {
|
||||
|
||||
override fun apply(project: Project) {
|
||||
project.pluginManager.withPlugin("com.android.application") {
|
||||
project.extensions.getByType<ApplicationAndroidComponentsExtension>().run {
|
||||
project.extensions.configure<ApplicationAndroidComponentsExtension> {
|
||||
onVariants { variant ->
|
||||
val taskPrefix = "collect${variant.name.capitalize(Locale.ROOT)}"
|
||||
project.tasks.register<CollectApksTask>("${taskPrefix}Apks") {
|
||||
+3
-3
@@ -4,7 +4,7 @@
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle
|
||||
package dev.msfjarvis.claw.gradle
|
||||
|
||||
import com.diffplug.gradle.spotless.SpotlessExtension
|
||||
import com.diffplug.gradle.spotless.SpotlessPlugin
|
||||
@@ -12,7 +12,7 @@ import org.gradle.api.GradleException
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
|
||||
@Suppress("Unused")
|
||||
class SpotlessPlugin : Plugin<Project> {
|
||||
@@ -22,7 +22,7 @@ class SpotlessPlugin : Plugin<Project> {
|
||||
throw GradleException("Spotless plugin must only be applied to the root project.")
|
||||
}
|
||||
project.pluginManager.apply(SpotlessPlugin::class)
|
||||
project.extensions.getByType<SpotlessExtension>().run {
|
||||
project.extensions.configure<SpotlessExtension> {
|
||||
kotlin {
|
||||
ktfmt(KTFMT_VERSION).googleStyle()
|
||||
target("**/*.kt")
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle.artifacts
|
||||
package dev.msfjarvis.claw.gradle.artifacts
|
||||
|
||||
import com.android.build.api.variant.BuiltArtifactsLoader
|
||||
import java.io.File
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle.artifacts
|
||||
package dev.msfjarvis.claw.gradle.artifacts
|
||||
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle.signing
|
||||
package dev.msfjarvis.claw.gradle.signing
|
||||
|
||||
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
|
||||
import java.util.Properties
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle.versioning
|
||||
package dev.msfjarvis.claw.gradle.versioning
|
||||
|
||||
const val VERSIONING_PROP_FILE = "version.properties"
|
||||
const val VERSIONING_PROP_VERSION_NAME = "versioning-plugin.versionName"
|
||||
+11
-7
@@ -4,7 +4,7 @@
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle.versioning
|
||||
package dev.msfjarvis.claw.gradle.versioning
|
||||
|
||||
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
|
||||
import com.android.build.api.variant.VariantOutputConfiguration
|
||||
@@ -14,7 +14,7 @@ import java.util.Properties
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.register
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
|
||||
@@ -46,11 +46,15 @@ class VersioningPlugin : Plugin<Project> {
|
||||
}
|
||||
project.plugins.withType<AppPlugin> {
|
||||
androidAppPluginApplied.set(true)
|
||||
extensions.getByType<ApplicationAndroidComponentsExtension>().onVariants { variant ->
|
||||
val mainOutput =
|
||||
variant.outputs.single { it.outputType == VariantOutputConfiguration.OutputType.SINGLE }
|
||||
mainOutput.versionName.set(versionName)
|
||||
mainOutput.versionCode.set(versionCode)
|
||||
extensions.configure<ApplicationAndroidComponentsExtension> {
|
||||
onVariants { variant ->
|
||||
val mainOutput =
|
||||
variant.outputs.single {
|
||||
it.outputType == VariantOutputConfiguration.OutputType.SINGLE
|
||||
}
|
||||
mainOutput.versionName.set(versionName)
|
||||
mainOutput.versionCode.set(versionCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
val version = Semver(versionName)
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.aps.gradle.versioning
|
||||
package dev.msfjarvis.claw.gradle.versioning
|
||||
|
||||
import com.vdurmont.semver4j.Semver
|
||||
import org.gradle.api.DefaultTask
|
||||
+20
-2
@@ -6,9 +6,11 @@
|
||||
*/
|
||||
@file:Suppress("DSL_SCOPE_VIOLATION", "UnstableApiUsage")
|
||||
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("android")
|
||||
id("dev.msfjarvis.claw.kotlin-common")
|
||||
id("dev.msfjarvis.claw.kotlin-android")
|
||||
id("dev.msfjarvis.claw.android-library")
|
||||
alias(libs.plugins.anvil)
|
||||
alias(libs.plugins.whetstone)
|
||||
@@ -18,6 +20,22 @@ anvil { generateDaggerFactories.set(true) }
|
||||
|
||||
androidComponents { beforeVariants { it.enableUnitTest = false } }
|
||||
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs +=
|
||||
listOf(
|
||||
"-P",
|
||||
"plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
|
||||
rootProject.buildDir.absolutePath +
|
||||
"/compose_metrics/",
|
||||
"-P",
|
||||
"plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
|
||||
rootProject.buildDir.absolutePath +
|
||||
"/compose_metrics/",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
api(libs.napier)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
package dev.msfjarvis.claw.common
|
||||
|
||||
sealed class NetworkState {
|
||||
internal sealed class NetworkState {
|
||||
class Success<T>(val data: T) : NetworkState()
|
||||
class Error(val error: Throwable, val description: String) : NetworkState()
|
||||
object Loading : NetworkState()
|
||||
|
||||
@@ -20,20 +20,15 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
@@ -46,14 +41,13 @@ import dev.msfjarvis.claw.common.posts.TagRow
|
||||
import dev.msfjarvis.claw.common.res.ClawIcons
|
||||
import dev.msfjarvis.claw.common.ui.NetworkImage
|
||||
import dev.msfjarvis.claw.common.ui.ThemedRichText
|
||||
import dev.msfjarvis.claw.model.Comment
|
||||
import dev.msfjarvis.claw.model.LinkMetadata
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
import java.time.Instant
|
||||
import java.time.temporal.TemporalAccessor
|
||||
|
||||
@Composable
|
||||
fun CommentsHeader(
|
||||
internal fun CommentsHeader(
|
||||
postDetails: LobstersPostDetails,
|
||||
postActions: PostActions,
|
||||
htmlConverter: HTMLConverter,
|
||||
@@ -104,7 +98,7 @@ fun CommentsHeader(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PostLink(
|
||||
private fun PostLink(
|
||||
linkMetadata: LinkMetadata,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@@ -114,22 +108,13 @@ fun PostLink(
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
)
|
||||
) {
|
||||
Row(modifier = Modifier.padding(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
if (linkMetadata.faviconUrl != null) {
|
||||
NetworkImage(
|
||||
url = linkMetadata.faviconUrl!!,
|
||||
placeholder = ClawIcons.Web,
|
||||
contentDescription = "",
|
||||
modifier = Modifier.size(24.dp),
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
painter = ClawIcons.Web,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onSecondary,
|
||||
modifier = Modifier.clip(CircleShape),
|
||||
)
|
||||
}
|
||||
Row(modifier = Modifier.padding(16.dp), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
NetworkImage(
|
||||
url = linkMetadata.faviconUrl,
|
||||
placeholder = ClawIcons.Web,
|
||||
contentDescription = "",
|
||||
modifier = Modifier.size(24.dp),
|
||||
)
|
||||
Text(
|
||||
text = linkMetadata.url,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
@@ -145,18 +130,19 @@ private val CommentEntryPadding = 16f.dp
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
fun CommentEntry(
|
||||
comment: Comment,
|
||||
internal fun CommentEntry(
|
||||
commentNode: CommentNode,
|
||||
htmlConverter: HTMLConverter,
|
||||
toggleExpanded: (CommentNode) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var expanded by remember(comment) { mutableStateOf(true) }
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val comment = commentNode.comment
|
||||
Box(
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { expanded = !expanded }
|
||||
.clickable { toggleExpanded(commentNode) }
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(
|
||||
start = CommentEntryPadding * comment.indentLevel,
|
||||
@@ -179,7 +165,7 @@ fun CommentEntry(
|
||||
modifier =
|
||||
Modifier.clickable { uriHandler.openUri("https://lobste.rs/u/${comment.user.username}") },
|
||||
)
|
||||
AnimatedContent(targetState = expanded) { expandedState ->
|
||||
AnimatedContent(targetState = commentNode.isExpanded) { expandedState ->
|
||||
if (expandedState) {
|
||||
ThemedRichText(
|
||||
text = htmlConverter.convertHTMLToMarkdown(comment.comment),
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.comments
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.material3.Divider
|
||||
import dev.msfjarvis.claw.model.Comment
|
||||
|
||||
internal data class CommentNode(
|
||||
val comment: Comment,
|
||||
var parent: CommentNode? = null,
|
||||
val children: MutableList<CommentNode> = mutableListOf(),
|
||||
var isExpanded: Boolean = true
|
||||
) {
|
||||
fun addChild(child: CommentNode) {
|
||||
if (comment.indentLevel + 1 == child.comment.indentLevel) {
|
||||
children.add(child)
|
||||
child.parent = this
|
||||
} else {
|
||||
children.last().addChild(child)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun createListNode(comments: List<Comment>): MutableList<CommentNode> {
|
||||
val commentNodes = mutableListOf<CommentNode>()
|
||||
|
||||
for (i in comments.indices) {
|
||||
if (comments[i].indentLevel == 1) {
|
||||
commentNodes.add(CommentNode(comment = comments[i]))
|
||||
} else {
|
||||
commentNodes.last().addChild(CommentNode(comment = comments[i]))
|
||||
}
|
||||
}
|
||||
|
||||
return commentNodes
|
||||
}
|
||||
|
||||
internal fun toggleAllExpanded(commentNode: CommentNode): CommentNode {
|
||||
commentNode.isExpanded = !commentNode.isExpanded
|
||||
|
||||
if (commentNode.children.isNotEmpty()) {
|
||||
commentNode.children.forEach { toggleAllExpanded(it) }
|
||||
}
|
||||
return commentNode
|
||||
}
|
||||
|
||||
internal fun findTopMostParent(node: CommentNode): CommentNode {
|
||||
val parent = node.parent
|
||||
return if (parent != null) {
|
||||
findTopMostParent(parent)
|
||||
} else {
|
||||
node
|
||||
}
|
||||
}
|
||||
|
||||
internal fun LazyListScope.nodes(
|
||||
nodes: List<CommentNode>,
|
||||
htmlConverter: HTMLConverter,
|
||||
toggleExpanded: (CommentNode) -> Unit,
|
||||
) {
|
||||
nodes.forEach { node ->
|
||||
node(
|
||||
node = node,
|
||||
htmlConverter = htmlConverter,
|
||||
toggleExpanded = toggleExpanded,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.node(
|
||||
node: CommentNode,
|
||||
htmlConverter: HTMLConverter,
|
||||
toggleExpanded: (CommentNode) -> Unit,
|
||||
) {
|
||||
// Skip the node if neither the node nor its parent is expanded
|
||||
if (!node.isExpanded && node.parent?.isExpanded == false) {
|
||||
return
|
||||
}
|
||||
item {
|
||||
CommentEntry(
|
||||
commentNode = node,
|
||||
htmlConverter = htmlConverter,
|
||||
toggleExpanded = toggleExpanded,
|
||||
)
|
||||
Divider()
|
||||
}
|
||||
if (node.children.isNotEmpty()) {
|
||||
nodes(
|
||||
node.children,
|
||||
htmlConverter = htmlConverter,
|
||||
toggleExpanded = toggleExpanded,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -11,14 +11,13 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@@ -39,6 +38,8 @@ private fun CommentsPageInternal(
|
||||
htmlConverter: HTMLConverter,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val commentNodes = createListNode(details.comments).toMutableStateList()
|
||||
|
||||
Surface(color = MaterialTheme.colorScheme.surfaceVariant) {
|
||||
LazyColumn(modifier = modifier, contentPadding = PaddingValues(bottom = 24.dp)) {
|
||||
item {
|
||||
@@ -49,7 +50,7 @@ private fun CommentsPageInternal(
|
||||
)
|
||||
}
|
||||
|
||||
if (details.comments.isNotEmpty()) {
|
||||
if (commentNodes.isNotEmpty()) {
|
||||
item {
|
||||
Text(
|
||||
text = "Comments",
|
||||
@@ -58,12 +59,20 @@ private fun CommentsPageInternal(
|
||||
)
|
||||
}
|
||||
|
||||
itemsIndexed(details.comments) { index, item ->
|
||||
if (index != 0) {
|
||||
Divider()
|
||||
}
|
||||
CommentEntry(comment = item, htmlConverter = htmlConverter)
|
||||
}
|
||||
nodes(
|
||||
nodes = commentNodes,
|
||||
htmlConverter = htmlConverter,
|
||||
toggleExpanded = { node ->
|
||||
val newNode = toggleAllExpanded(node)
|
||||
val parent = findTopMostParent(newNode)
|
||||
val index =
|
||||
commentNodes.indexOf(commentNodes.find { it.comment.url == parent.comment.url })
|
||||
if (index != -1) {
|
||||
commentNodes.removeAt(index)
|
||||
commentNodes.add(index, parent)
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
item {
|
||||
Text(
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.comments
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
|
||||
/** Defines a contract to convert strings of HTML to Markdown. */
|
||||
@Stable
|
||||
fun interface HTMLConverter {
|
||||
fun convertHTMLToMarkdown(html: String): String
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ fun PostDetails(post: SavedPost, modifier: Modifier = Modifier) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PostTitle(
|
||||
internal fun PostTitle(
|
||||
title: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@@ -128,7 +128,7 @@ fun PostTitle(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Submitter(
|
||||
internal fun Submitter(
|
||||
text: AnnotatedString,
|
||||
avatarUrl: String,
|
||||
contentDescription: String,
|
||||
@@ -151,7 +151,7 @@ fun Submitter(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SaveButton(
|
||||
private fun SaveButton(
|
||||
isSaved: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@@ -169,7 +169,7 @@ fun SaveButton(
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
fun CommentsButton(
|
||||
private fun CommentsButton(
|
||||
commentCount: Int?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@@ -200,7 +200,7 @@ fun CommentsButton(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TagRow(
|
||||
internal fun TagRow(
|
||||
tags: List<String>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@@ -214,7 +214,7 @@ fun TagRow(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TagText(
|
||||
private fun TagText(
|
||||
tag: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
*/
|
||||
package dev.msfjarvis.claw.common.posts
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
import dev.msfjarvis.claw.model.LinkMetadata
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
|
||||
@Stable
|
||||
interface PostActions {
|
||||
fun viewPost(postUrl: String, commentsUrl: String)
|
||||
fun viewComments(postId: String)
|
||||
|
||||
@@ -21,6 +21,7 @@ fun LobstersPost.toDbModel(): SavedPost {
|
||||
submitterName = submitter.username,
|
||||
submitterAvatarUrl = submitter.avatarUrl,
|
||||
tags = tags,
|
||||
description = description,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,5 +36,6 @@ fun LobstersPostDetails.toDbModel(): SavedPost {
|
||||
submitterName = submitter.username,
|
||||
submitterAvatarUrl = submitter.avatarUrl,
|
||||
tags = tags,
|
||||
description = description,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import androidx.compose.runtime.ProvidedValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.halilibo.richtext.ui.material3.SetupMaterial3RichText
|
||||
|
||||
internal val LightThemeColors =
|
||||
private val LightThemeColors =
|
||||
lightColorScheme(
|
||||
primary = md_theme_light_primary,
|
||||
onPrimary = md_theme_light_onPrimary,
|
||||
@@ -48,7 +48,7 @@ internal val LightThemeColors =
|
||||
inverseSurface = md_theme_light_inverseSurface,
|
||||
)
|
||||
|
||||
internal val DarkThemeColors =
|
||||
private val DarkThemeColors =
|
||||
darkColorScheme(
|
||||
primary = md_theme_dark_primary,
|
||||
onPrimary = md_theme_dark_onPrimary,
|
||||
|
||||
@@ -23,7 +23,7 @@ import dev.msfjarvis.claw.common.theme.LobstersTheme
|
||||
import dev.msfjarvis.claw.common.ui.preview.ThemePreviews
|
||||
|
||||
@Composable
|
||||
fun ThemedRichText(
|
||||
internal fun ThemedRichText(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
@@ -50,7 +50,7 @@ fun ThemedRichText(
|
||||
|
||||
@ThemePreviews
|
||||
@Composable
|
||||
fun ThemedRichTextPreview() {
|
||||
internal fun ThemedRichTextPreview() {
|
||||
val text =
|
||||
"""
|
||||
### Heading
|
||||
|
||||
@@ -72,6 +72,6 @@ fun NetworkError(
|
||||
|
||||
@ThemePreviews
|
||||
@Composable
|
||||
fun NetworkErrorPreview() {
|
||||
internal fun NetworkErrorPreview() {
|
||||
LobstersTheme { NetworkError(label = "Failed to load posts", error = Throwable("Preview")) }
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import androidx.compose.ui.graphics.painter.Painter
|
||||
import coil.compose.AsyncImage
|
||||
|
||||
@Composable
|
||||
fun NetworkImage(
|
||||
url: String,
|
||||
internal fun NetworkImage(
|
||||
url: String?,
|
||||
placeholder: Painter,
|
||||
contentDescription: String,
|
||||
modifier: Modifier = Modifier,
|
||||
|
||||
@@ -11,9 +11,11 @@ import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.platform.UriHandler
|
||||
import io.github.aakira.napier.Napier
|
||||
|
||||
@Stable
|
||||
class UrlLauncher(private val context: Context) : UriHandler {
|
||||
override fun openUri(uri: String) {
|
||||
val customTabsIntent =
|
||||
|
||||
@@ -90,9 +90,11 @@ private fun UserProfileInternal(
|
||||
ThemedRichText(
|
||||
text = user.about,
|
||||
)
|
||||
ThemedRichText(
|
||||
text = "Invited by [${user.invitedBy}](https://lobste.rs/u/${user.invitedBy})",
|
||||
)
|
||||
user.invitedBy?.let { invitedBy ->
|
||||
ThemedRichText(
|
||||
text = "Invited by [${invitedBy}](https://lobste.rs/u/${user.invitedBy})",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE SavedPost
|
||||
ADD COLUMN description TEXT NOT NULL DEFAULT "";
|
||||
@@ -11,7 +11,8 @@ CREATE TABLE IF NOT EXISTS SavedPost(
|
||||
commentsUrl TEXT NOT NULL,
|
||||
submitterName TEXT NOT NULL,
|
||||
submitterAvatarUrl TEXT NOT NULL,
|
||||
tags TEXT AS List<String> NOT NULL
|
||||
tags TEXT AS List<String> NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT ""
|
||||
);
|
||||
|
||||
insertOrReplacePost:
|
||||
|
||||
@@ -131,6 +131,7 @@ class SqlDelightQueriesTest : FunSpec() {
|
||||
submitterName = "test_user_$i",
|
||||
submitterAvatarUrl = "test_avatar_url",
|
||||
tags = listOf(),
|
||||
description = "",
|
||||
)
|
||||
|
||||
posts.add(post)
|
||||
|
||||
@@ -13,20 +13,20 @@ kotlin = "1.7.21"
|
||||
# @pin Needs to be aligned with Retrofit
|
||||
okhttp = "3.14.9"
|
||||
retrofit = "2.9.0"
|
||||
richtext = "0.15.0"
|
||||
richtext = "0.16.0"
|
||||
serialization = "1.4.1"
|
||||
sqldelight = "2.0.0-alpha04"
|
||||
whetstone = "0.5.0-SNAPSHOT"
|
||||
workmanager = "2.8.0-beta02"
|
||||
workmanager = "2.8.0-rc01"
|
||||
|
||||
[libraries]
|
||||
accompanist-flowlayout = { module = "com.google.accompanist:accompanist-flowlayout", version.ref = "accompanist" }
|
||||
accompanist-sysuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" }
|
||||
androidx-activity-compose = "androidx.activity:activity-compose:1.7.0-alpha02"
|
||||
androidx-benchmark-macro-junit4 = "androidx.benchmark:benchmark-macro-junit4:1.2.0-alpha07"
|
||||
androidx-browser = "androidx.browser:browser:1.5.0-alpha01"
|
||||
androidx-benchmark-macro-junit4 = "androidx.benchmark:benchmark-macro-junit4:1.2.0-alpha08"
|
||||
androidx-browser = "androidx.browser:browser:1.5.0-alpha02"
|
||||
androidx-compose-animation = { module = "androidx.compose.animation:animation" }
|
||||
androidx-compose-bom = "androidx.compose:compose-bom:2022.11.00"
|
||||
androidx-compose-bom = "androidx.compose:compose-bom:2022.12.00"
|
||||
androidx-compose-foundation = { module = "androidx.compose.foundation:foundation" }
|
||||
androidx-compose-material = { module = "androidx.compose.material:material" }
|
||||
androidx-compose-material3 = { module = "androidx.compose.material3:material3" }
|
||||
|
||||
@@ -15,7 +15,7 @@ import kotlinx.serialization.Serializable
|
||||
class User(
|
||||
val username: String,
|
||||
val about: String,
|
||||
@SerialName("invited_by_user") val invitedBy: String,
|
||||
@SerialName("invited_by_user") val invitedBy: String?,
|
||||
@SerialName("avatar_url") val avatarUrl: String,
|
||||
@SerialName("created_at") val createdAt: String,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
version: "1.0"
|
||||
linter: jetbrains/qodana-jvm-community:2022.3
|
||||
exclude:
|
||||
- name: KotlinAnnotator
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
TEMP_DIR="$(mktemp -d)"
|
||||
BIN_DIR=""
|
||||
AGE_VERSION="v1.0.0"
|
||||
AGE_FILE=""
|
||||
|
||||
case "$(uname)" in
|
||||
Linux)
|
||||
AGE_FILE="age-${AGE_VERSION}-linux-amd64.tar.gz"
|
||||
BIN_DIR="${HOME}/.local/bin"
|
||||
;;
|
||||
Darwin)
|
||||
AGE_FILE="age-${AGE_VERSION}-darwin-amd64.tar.gz"
|
||||
BIN_DIR="${HOME}/bin"
|
||||
;;
|
||||
*) echo "Unsupported system: $(uname)"; exit 1 ;;
|
||||
esac
|
||||
|
||||
pushd "${TEMP_DIR}"
|
||||
|
||||
curl -L --silent --show-error --retry 3 --fail -o age.tar.gz "https://github.com/FiloSottile/age/releases/download/v1.0.0/${AGE_FILE:?}"
|
||||
tar xvf age.tar.gz
|
||||
rm age/LICENSE
|
||||
mkdir -p "${BIN_DIR}"
|
||||
mv -v age/age "${BIN_DIR}" && chmod +x "${BIN_DIR}/age"
|
||||
mv -v age/age-keygen "${BIN_DIR}" && chmod +x "${BIN_DIR}/age-keygen"
|
||||
|
||||
popd
|
||||
+17
-11
@@ -2,16 +2,22 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ENCRYPT_KEY="${1:-}"
|
||||
ENCRYPT_KEY="${1}"
|
||||
TEMP_KEY="$(mktemp)"
|
||||
|
||||
declare -A SECRETS
|
||||
SECRETS[secrets/keystore.cipher]=keystore.jks
|
||||
SECRETS[secrets/props.cipher]=keystore.properties
|
||||
trap "rm -rf ${TEMP_KEY} 2>/dev/null" INT TERM EXIT
|
||||
|
||||
if [[ -n "$ENCRYPT_KEY" ]]; then
|
||||
for src in "${!SECRETS[@]}"; do
|
||||
openssl enc -aes-256-cbc -md sha256 -pbkdf2 -d -in "${src}" -out "${SECRETS[${src}]}" -k "${ENCRYPT_KEY}"
|
||||
done
|
||||
else
|
||||
echo "Usage: ./signing-setup.sh <encryption key>"
|
||||
fi
|
||||
echo "${ENCRYPT_KEY:?}" > "${TEMP_KEY}"
|
||||
|
||||
function decrypt() {
|
||||
if ! command -v age 1>/dev/null; then
|
||||
echo "age not installed"
|
||||
exit 1
|
||||
fi
|
||||
SRC="${1}"
|
||||
DST="${2}"
|
||||
age --decrypt -i "${TEMP_KEY}" -o "${DST:?}" "${SRC:?}"
|
||||
}
|
||||
|
||||
decrypt secrets/keystore.cipher keystore.jks
|
||||
decrypt secrets/props.cipher keystore.properties
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -125,13 +125,6 @@ dependencyResolutionManagement {
|
||||
includeGroup("org.jetbrains.skiko")
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository { gradlePluginPortal() }
|
||||
filter {
|
||||
includeModule("org.gradle.android.cache-fix", "org.gradle.android.cache-fix.gradle.plugin")
|
||||
includeModule("gradle.plugin.org.gradle.android", "android-cache-fix-gradle-plugin")
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository { maven("https://oss.sonatype.org/content/repositories/snapshots/") }
|
||||
filter { includeGroup("dev.msfjarvis.whetstone") }
|
||||
|
||||
Reference in New Issue
Block a user