Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a223470da5 | ||
|
|
1e5421fbc2 | ||
|
|
4087ce1941 | ||
|
|
63e8ca3df8 | ||
|
|
a9a304cd96 | ||
|
|
8dd15b8276 | ||
|
|
5c2793fff3 | ||
|
|
245437c739 | ||
|
|
2fdbff6f12 | ||
|
|
2bc86509cf | ||
|
|
1dfea689c6 | ||
|
|
a0276c4873 | ||
|
|
fbb13c5acf | ||
|
|
95b3030e19 | ||
|
|
0f2bb909d4 | ||
|
|
d2e1ae91da | ||
|
|
82a024baee | ||
|
|
3021b55fce | ||
|
|
7506dd5ca9 | ||
|
|
d3d4519daa | ||
|
|
83b58aa7fb | ||
|
|
aa2093424c | ||
|
|
501721fca9 | ||
|
|
aa088f5dd7 | ||
|
|
b0b47d98a5 | ||
|
|
2109bcf12c | ||
|
|
6bc6b13ea6 | ||
|
|
0f20366cf3 | ||
|
|
723ab0add3 | ||
|
|
77dd4496a5 | ||
|
|
1e8a93fde8 | ||
|
|
fe0e0ea10e | ||
|
|
d26245da17 | ||
|
|
aa60160378 | ||
|
|
943cddd8a4 | ||
|
|
d5a0bb256f | ||
|
|
1ebf610794 | ||
|
|
784dcf16f8 | ||
|
|
cc5b95e9c8 | ||
|
|
e317a1f4f3 | ||
|
|
ed4c573ee4 | ||
|
|
7037d55bf2 | ||
|
|
4ab76c60bb | ||
|
|
0fe83e8583 | ||
|
|
68bcae572b |
@@ -3,3 +3,4 @@
|
||||
**/clawicons/** linguist-generated
|
||||
android/src/main/baseline-prof.txt linguist-generated -diff
|
||||
android/src/main/generated/** linguist-generated -diff
|
||||
**/screenshotTest/**/*.png filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
+6
-11
@@ -53,6 +53,12 @@
|
||||
],
|
||||
"allowedVersions": "!/.*-1-8$/",
|
||||
},
|
||||
{
|
||||
"matchDepNames": [
|
||||
"androidx.compose:compose-bom",
|
||||
],
|
||||
"allowedVersions": "/.*-alpha$/",
|
||||
},
|
||||
{
|
||||
"matchPackagePatterns": [
|
||||
"^androidx.glance",
|
||||
@@ -72,16 +78,5 @@
|
||||
"depNameTemplate": "com.facebook:ktfmt",
|
||||
"registryUrlTemplate": "https://repo1.maven.org/maven2/",
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"gradle/libs.versions.toml"
|
||||
],
|
||||
"matchStrings": [
|
||||
"composeCompiler = \"(?<currentValue>.*)\""
|
||||
],
|
||||
"datasourceTemplate": "maven",
|
||||
"depNameTemplate": "androidx.compose.compiler:compiler",
|
||||
"registryUrlTemplate": "https://maven.google.com",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -25,15 +25,16 @@ runs:
|
||||
with:
|
||||
fetch-depth: ${{ inputs.fetch-depth }}
|
||||
token: ${{ inputs.token }}
|
||||
lfs: true
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2
|
||||
uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # v4.3.0
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 22
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4
|
||||
uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4
|
||||
with:
|
||||
gradle-home-cache-cleanup: true
|
||||
cache-read-only: ${{ inputs.cache-read-only }}
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
|
||||
- name: (Fail-only) Upload test report
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
||||
with:
|
||||
name: Test report
|
||||
path: '**/build/reports/tests/**'
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
cache-read-only: true
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
|
||||
uses: github/codeql-action/init@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8
|
||||
with:
|
||||
languages: java
|
||||
tools: latest
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
./gradlew assembleDebug assembleInternal
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
|
||||
uses: github/codeql-action/analyze@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8
|
||||
with:
|
||||
category: "/language:java"
|
||||
mobsfscan:
|
||||
@@ -62,6 +62,6 @@ jobs:
|
||||
args: . --sarif --output results.sarif || true
|
||||
|
||||
- name: Upload mobsfscan report
|
||||
uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
|
||||
uses: github/codeql-action/upload-sarif@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
ORG_GRADLE_PROJECT_VERSION_CODE: ${{ github.event.inputs.versionCode }}
|
||||
|
||||
- name: Upload app bundle
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
||||
with:
|
||||
name: release-aab
|
||||
path: android/build/outputs/bundle/release/android-release.aab
|
||||
|
||||
+10
-1
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.51.0] - 2024-09-22
|
||||
|
||||
### Fixed
|
||||
|
||||
- Pull to refresh indicator is no longer hiding behind the top app bar
|
||||
- User profile page correctly renders contents below the top app bar
|
||||
- Revert unread comment handling changes that caused false positives
|
||||
|
||||
## [1.50.0] - 2024-08-30
|
||||
|
||||
### Fixed
|
||||
@@ -462,7 +470,8 @@ 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/v1.50.0...HEAD
|
||||
[Unreleased]: https://github.com/msfjarvis/compose-lobsters/compare/v1.51.0...HEAD
|
||||
[1.51.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.50.0...v1.51.0
|
||||
[1.50.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.49.0...v1.50.0
|
||||
[1.49.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.48.0...v1.49.0
|
||||
[1.48.0]: https://github.com/msfjarvis/compose-lobsters/compare/v1.47.0...v1.48.0
|
||||
|
||||
@@ -26,6 +26,7 @@ plugins {
|
||||
alias(libs.plugins.kotlin.composeCompiler)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
alias(libs.plugins.navigation.safeargs)
|
||||
alias(libs.plugins.screenshot)
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -40,6 +41,7 @@ android {
|
||||
applicationIdSuffix = ".internal"
|
||||
debuggable(true)
|
||||
}
|
||||
experimentalProperties["android.experimental.enableScreenshotTest"] = true
|
||||
}
|
||||
|
||||
baselineProfile {
|
||||
@@ -119,6 +121,8 @@ dependencies {
|
||||
|
||||
kapt(libs.dagger.compiler)
|
||||
|
||||
screenshotTestImplementation(libs.androidx.compose.ui.tooling)
|
||||
|
||||
addTestDependencies(project)
|
||||
androidTestImplementation(libs.androidx.test.espresso.core)
|
||||
androidTestImplementation(libs.androidx.test.uiautomator)
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:10defb7f6abec5d8bd07e07625e30e5b68d3a330e87f157e8138688f8d7ef0aa
|
||||
size 214795
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:64dd5c002c021774469d7d9a15433678a014f720b6cde2281fbbf20daee98712
|
||||
size 216093
|
||||
@@ -63,7 +63,7 @@ fun NetworkPosts(
|
||||
PullToRefreshDefaults.Indicator(
|
||||
state = state,
|
||||
isRefreshing = refreshLoadState == LoadState.Loading,
|
||||
modifier = Modifier.align(Alignment.TopCenter),
|
||||
modifier = Modifier.align(Alignment.TopCenter).padding(contentPadding),
|
||||
)
|
||||
},
|
||||
) {
|
||||
|
||||
@@ -19,16 +19,14 @@ import androidx.compose.ui.semantics.isTraversalGroup
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import dev.msfjarvis.claw.common.posts.PostActions
|
||||
import dev.msfjarvis.claw.common.ui.SearchBar
|
||||
import dev.msfjarvis.claw.model.UIPost
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Composable
|
||||
fun SearchList(
|
||||
items: Flow<PagingData<UIPost>>,
|
||||
lazyPagingItems: LazyPagingItems<UIPost>,
|
||||
listState: LazyListState,
|
||||
postActions: PostActions,
|
||||
searchQuery: String,
|
||||
@@ -36,7 +34,6 @@ fun SearchList(
|
||||
contentPadding: PaddingValues,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val lazyPagingItems = items.collectAsLazyPagingItems()
|
||||
val triggerSearch = { query: String ->
|
||||
setSearchQuery(query)
|
||||
lazyPagingItems.refresh()
|
||||
|
||||
+5
-3
@@ -36,7 +36,6 @@ import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.windowsizeclass.WindowSizeClass
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
@@ -50,6 +49,7 @@ import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.semantics.testTagsAsResourceId
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
@@ -114,7 +114,7 @@ fun LobstersPostsScreen(
|
||||
|
||||
val hottestPosts = viewModel.hottestPosts.collectAsLazyPagingItems()
|
||||
val newestPosts = viewModel.newestPosts.collectAsLazyPagingItems()
|
||||
val savedPosts by viewModel.savedPostsByMonth.collectAsState(persistentMapOf())
|
||||
val savedPosts by viewModel.savedPostsByMonth.collectAsStateWithLifecycle(persistentMapOf())
|
||||
|
||||
val navigationType = ClawNavigationType.fromSize(windowSizeClass.widthSizeClass)
|
||||
|
||||
@@ -283,13 +283,15 @@ fun LobstersPostsScreen(
|
||||
}
|
||||
composable<Settings> {
|
||||
SettingsScreen(
|
||||
context = context,
|
||||
openInputStream = context.contentResolver::openInputStream,
|
||||
openOutputStream = context.contentResolver::openOutputStream,
|
||||
openLibrariesScreen = { navController.navigate(AboutLibraries) },
|
||||
importPosts = viewModel::importPosts,
|
||||
exportPostsAsJson = viewModel::exportPostsAsJson,
|
||||
exportPostsAsHtml = viewModel::exportPostsAsHtml,
|
||||
snackbarHostState = snackbarHostState,
|
||||
contentPadding = contentPadding,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
composable<AboutLibraries> {
|
||||
|
||||
@@ -15,6 +15,7 @@ import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.toRoute
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.deliveryhero.whetstone.compose.injectedViewModel
|
||||
import dev.msfjarvis.claw.android.ui.lists.SearchList
|
||||
import dev.msfjarvis.claw.android.ui.navigation.Comments
|
||||
@@ -38,12 +39,13 @@ fun SearchScreen(
|
||||
val navController = rememberNavController()
|
||||
val postActions = rememberPostActions(LocalContext.current, urlLauncher, navController, viewModel)
|
||||
val listState = rememberLazyListState()
|
||||
val searchResults = viewModel.searchResults.collectAsLazyPagingItems()
|
||||
Scaffold(modifier = modifier) { contentPadding ->
|
||||
NavHost(navController = navController, startDestination = Search) {
|
||||
composable<Search> {
|
||||
setWebUri("https://lobste.rs/search")
|
||||
SearchList(
|
||||
items = viewModel.searchResults,
|
||||
lazyPagingItems = searchResults,
|
||||
listState = listState,
|
||||
postActions = postActions,
|
||||
searchQuery = viewModel.searchQuery,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
package dev.msfjarvis.claw.android.ui.screens
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.clickable
|
||||
@@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -41,6 +40,8 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.msfjarvis.claw.common.theme.LobstersTheme
|
||||
import dev.msfjarvis.claw.common.ui.preview.ThemePreviews
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -51,9 +52,10 @@ private const val HTML_MIME_TYPE = "application/html"
|
||||
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
context: Context,
|
||||
openLibrariesScreen: () -> Unit,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
openInputStream: (Uri) -> InputStream?,
|
||||
openOutputStream: (Uri) -> OutputStream?,
|
||||
importPosts: suspend (InputStream) -> Unit,
|
||||
exportPostsAsJson: suspend (OutputStream) -> Unit,
|
||||
exportPostsAsHtml: suspend (OutputStream) -> Unit,
|
||||
@@ -61,7 +63,7 @@ fun SettingsScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
Box(modifier = modifier.padding(contentPadding).fillMaxSize()) {
|
||||
Box(modifier = modifier.padding(contentPadding)) {
|
||||
Column {
|
||||
ListItem(
|
||||
headlineContent = { Text("Data transfer") },
|
||||
@@ -77,11 +79,11 @@ fun SettingsScreen(
|
||||
horizontalArrangement = Arrangement.spacedBy(32.dp),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
ImportPosts(context, coroutineScope, snackbarHostState, importPosts)
|
||||
ImportPosts(openInputStream, coroutineScope, snackbarHostState, importPosts)
|
||||
ExportPosts(
|
||||
context,
|
||||
coroutineScope,
|
||||
snackbarHostState,
|
||||
openOutputStream,
|
||||
exportPostsAsJson,
|
||||
exportPostsAsHtml,
|
||||
)
|
||||
@@ -105,7 +107,7 @@ fun SettingsScreen(
|
||||
|
||||
@Composable
|
||||
private fun RowScope.ImportPosts(
|
||||
context: Context,
|
||||
openInputStream: (Uri) -> InputStream?,
|
||||
coroutineScope: CoroutineScope,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
importPosts: suspend (InputStream) -> Unit,
|
||||
@@ -118,7 +120,7 @@ private fun RowScope.ImportPosts(
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
coroutineScope.launch {
|
||||
context.contentResolver.openInputStream(uri)?.use { stream ->
|
||||
openInputStream(uri)?.use { stream ->
|
||||
importPosts(stream)
|
||||
snackbarHostState.showSnackbarDismissing("Successfully imported posts")
|
||||
}
|
||||
@@ -135,9 +137,9 @@ private fun RowScope.ImportPosts(
|
||||
|
||||
@Composable
|
||||
private fun RowScope.ExportPosts(
|
||||
context: Context,
|
||||
coroutineScope: CoroutineScope,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
openOutputStream: (Uri) -> OutputStream?,
|
||||
exportPostsAsJson: suspend (OutputStream) -> Unit,
|
||||
exportPostsAsHtml: suspend (OutputStream) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -150,7 +152,7 @@ private fun RowScope.ExportPosts(
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
coroutineScope.launch {
|
||||
context.contentResolver.openOutputStream(uri)?.use { stream ->
|
||||
openOutputStream(uri)?.use { stream ->
|
||||
exportPostsAsJson(stream)
|
||||
snackbarHostState.showSnackbarDismissing("Successfully exported posts")
|
||||
}
|
||||
@@ -164,7 +166,7 @@ private fun RowScope.ExportPosts(
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
coroutineScope.launch {
|
||||
context.contentResolver.openOutputStream(uri)?.use { stream ->
|
||||
openOutputStream(uri)?.use { stream ->
|
||||
exportPostsAsHtml(stream)
|
||||
snackbarHostState.showSnackbarDismissing("Successfully exported posts")
|
||||
}
|
||||
@@ -210,3 +212,11 @@ private suspend fun SnackbarHostState.showSnackbarDismissing(text: String) {
|
||||
currentSnackbarData?.dismiss()
|
||||
showSnackbar(text)
|
||||
}
|
||||
|
||||
@ThemePreviews
|
||||
@Composable
|
||||
private fun SettingsScreenPreview() {
|
||||
LobstersTheme {
|
||||
SettingsScreen({}, SnackbarHostState(), { null }, { null }, {}, {}, {}, PaddingValues())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,7 @@ constructor(
|
||||
) {
|
||||
|
||||
suspend fun getSeenComments(postId: String) =
|
||||
withContext(dbDispatcher) {
|
||||
postCommentsQueries.getCommentIds(postId).executeAsOneOrNull()
|
||||
?: PostComments(postId = postId, commentIds = emptyList())
|
||||
}
|
||||
withContext(dbDispatcher) { postCommentsQueries.getCommentIds(postId).executeAsOneOrNull() }
|
||||
|
||||
suspend fun markSeenComments(postId: String, comments: List<Comment>) {
|
||||
withContext(dbDispatcher) {
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright © 2024 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 android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import dev.msfjarvis.claw.common.posts.TEST_POST
|
||||
import dev.msfjarvis.claw.common.posts.TEST_POST_ACTIONS
|
||||
import dev.msfjarvis.claw.common.theme.LobstersTheme
|
||||
import dev.msfjarvis.claw.common.ui.preview.ThemePreviews
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
@SuppressLint("ComposePreviewPublic", "ComposeUnstableReceiver")
|
||||
class NetworkPostsTest {
|
||||
@ThemePreviews
|
||||
@Composable
|
||||
fun DefaultPreview() {
|
||||
val items = List(20) { TEST_POST.copy(shortId = "${TEST_POST.shortId}${it}") }
|
||||
val flow = MutableStateFlow(PagingData.from(items))
|
||||
LobstersTheme {
|
||||
NetworkPosts(
|
||||
lazyPagingItems = flow.collectAsLazyPagingItems(),
|
||||
listState = rememberLazyListState(),
|
||||
postActions = TEST_POST_ACTIONS,
|
||||
contentPadding = PaddingValues(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# This file was automatically generated by 'versioning-plugin'. DO NOT EDIT MANUALLY.
|
||||
#
|
||||
versioning-plugin.versionCode=15000
|
||||
versioning-plugin.versionName=1.50.0
|
||||
versioning-plugin.versionCode=15100
|
||||
versioning-plugin.versionName=1.51.0
|
||||
|
||||
@@ -25,21 +25,14 @@ import org.gradle.kotlin.dsl.findByType
|
||||
|
||||
class AndroidCommonPlugin : Plugin<Project> {
|
||||
|
||||
private companion object {
|
||||
const val COMPILE_SDK = 35
|
||||
const val MIN_SDK = 26
|
||||
const val TARGET_SDK = 34
|
||||
const val SLIM_TESTS_PROPERTY = "slimTests"
|
||||
}
|
||||
|
||||
override fun apply(project: Project) {
|
||||
project.configureSlimTests()
|
||||
project.pluginManager.apply(AndroidCacheFixPlugin::class)
|
||||
project.extensions.configure<BaseExtension> {
|
||||
compileSdkVersion(COMPILE_SDK)
|
||||
compileSdkVersion(35)
|
||||
defaultConfig {
|
||||
minSdk = MIN_SDK
|
||||
targetSdk = TARGET_SDK
|
||||
minSdk = 26
|
||||
targetSdk = 35
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
@@ -64,7 +57,7 @@ class AndroidCommonPlugin : Plugin<Project> {
|
||||
}
|
||||
|
||||
private fun Project.configureSlimTests() {
|
||||
if (!providers.gradleProperty(SLIM_TESTS_PROPERTY).isPresent) {
|
||||
if (!providers.gradleProperty("slimTests").isPresent) {
|
||||
return
|
||||
}
|
||||
// Disable unit test tasks on the release build type for Android Library projects
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.gradle.kotlin.dsl.exclude
|
||||
/** Extension function to configure JUnit5 dependencies with the Truth assertion library. */
|
||||
fun DependencyHandlerScope.addTestDependencies(project: Project) {
|
||||
val libs = project.extensions.getByName("libs") as LibrariesForLibs
|
||||
arrayOf("test", "androidTest")
|
||||
arrayOf("test", "androidTest", "screenshotTest")
|
||||
.filter { sourceSet -> project.configurations.findByName("${sourceSet}Implementation") != null }
|
||||
.forEach { sourceSet ->
|
||||
addProvider("${sourceSet}Implementation", libs.junit.jupiter.api)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
import com.android.build.api.variant.HasUnitTestBuilder
|
||||
import dev.msfjarvis.claw.gradle.addTestDependencies
|
||||
|
||||
plugins {
|
||||
id("dev.msfjarvis.claw.android-library")
|
||||
@@ -14,6 +15,7 @@ plugins {
|
||||
alias(libs.plugins.anvil)
|
||||
alias(libs.plugins.whetstone)
|
||||
alias(libs.plugins.kotlin.composeCompiler)
|
||||
alias(libs.plugins.screenshot)
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -23,6 +25,7 @@ android {
|
||||
}
|
||||
composeOptions { useLiveLiterals = false }
|
||||
namespace = "dev.msfjarvis.claw.common"
|
||||
experimentalProperties["android.experimental.enableScreenshotTest"] = true
|
||||
}
|
||||
|
||||
androidComponents { beforeVariants { (it as HasUnitTestBuilder).enableUnitTest = false } }
|
||||
@@ -57,7 +60,11 @@ dependencies {
|
||||
implementation(libs.kotlinResult.coroutines)
|
||||
implementation(libs.napier)
|
||||
|
||||
screenshotTestImplementation(libs.androidx.compose.ui.tooling)
|
||||
|
||||
compileOnly(libs.androidx.compose.ui.tooling.preview)
|
||||
|
||||
runtimeOnly(libs.androidx.compose.ui.tooling)
|
||||
|
||||
addTestDependencies(project)
|
||||
}
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9d9d4a162b7ba995f625b4feb683879de46ad1c583a37c5cc721030521cece11
|
||||
size 6633
|
||||
@@ -65,14 +65,10 @@ internal class CommentNode(
|
||||
|
||||
internal fun createListNode(
|
||||
comments: List<Comment>,
|
||||
commentState: PostComments,
|
||||
commentState: PostComments?,
|
||||
): MutableList<CommentNode> {
|
||||
// Only check unread state if there any comments read in the first place. This somewhat restores
|
||||
// the first view behavior from when `commentState` was nullable.
|
||||
val hasUnread = commentState.commentIds.isNotEmpty()
|
||||
val isUnread =
|
||||
if (hasUnread) ({ id: String -> !commentState.commentIds.contains(id) }) else { _ -> false }
|
||||
val commentNodes = mutableListOf<CommentNode>()
|
||||
val isUnread = { id: String -> commentState?.commentIds?.contains(id) == false }
|
||||
|
||||
for (i in comments.indices) {
|
||||
if (comments[i].parentComment == null) {
|
||||
|
||||
@@ -33,7 +33,7 @@ fun CommentsPage(
|
||||
postId: String,
|
||||
postActions: PostActions,
|
||||
htmlConverter: HTMLConverter,
|
||||
getSeenComments: suspend (String) -> PostComments,
|
||||
getSeenComments: suspend (String) -> PostComments?,
|
||||
markSeenComments: (String, List<Comment>) -> Unit,
|
||||
contentPadding: PaddingValues,
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -48,9 +48,7 @@ fun CommentsPage(
|
||||
)
|
||||
}
|
||||
val commentState by
|
||||
produceState(initialValue = PostComments(postId, emptyList())) {
|
||||
value = getSeenComments(postId)
|
||||
}
|
||||
produceState<PostComments?>(initialValue = null) { value = getSeenComments(postId) }
|
||||
|
||||
when (postDetails) {
|
||||
is Success<*> -> {
|
||||
|
||||
@@ -53,7 +53,7 @@ internal fun CommentsPageInternal(
|
||||
details: UIPost,
|
||||
postActions: PostActions,
|
||||
htmlConverter: HTMLConverter,
|
||||
commentState: PostComments,
|
||||
commentState: PostComments?,
|
||||
markSeenComments: (String, List<Comment>) -> Unit,
|
||||
openUserProfile: (String) -> Unit,
|
||||
contentPadding: PaddingValues,
|
||||
@@ -62,10 +62,14 @@ internal fun CommentsPageInternal(
|
||||
val context = LocalContext.current
|
||||
val commentNodes = createListNode(details.comments, commentState)
|
||||
LaunchedEffect(key1 = commentNodes) {
|
||||
if (details.comments.isNotEmpty() && commentState.commentIds.isNotEmpty()) {
|
||||
val unreadCount = details.comments.size - commentState.commentIds.size
|
||||
if (details.comments.isNotEmpty() && !commentState?.commentIds.isNullOrEmpty()) {
|
||||
val unreadCount = details.comments.size - (commentState?.commentIds?.size ?: 0)
|
||||
if (unreadCount > 0) {
|
||||
val text = "$unreadCount unread comments"
|
||||
val text =
|
||||
when (unreadCount) {
|
||||
1 -> "$unreadCount unread comment"
|
||||
else -> "$unreadCount unread comments"
|
||||
}
|
||||
Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ fun LobstersCard(post: UIPost, postActions: PostActions, modifier: Modifier = Mo
|
||||
.fillMaxWidth()
|
||||
.clickable { postActions.viewPost(post.shortId, post.url, post.commentsUrl) }
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(start = 8.dp, top = 2.dp, bottom = 2.dp)
|
||||
.padding(start = 8.dp, top = 4.dp, bottom = 4.dp)
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
|
||||
@@ -64,7 +64,7 @@ fun UserProfile(
|
||||
UserProfileInternal(
|
||||
user = (user as Success<User>).data,
|
||||
openUserProfile = openUserProfile,
|
||||
modifier = modifier,
|
||||
modifier = modifier.padding(contentPadding),
|
||||
)
|
||||
}
|
||||
is Error -> {
|
||||
@@ -78,7 +78,7 @@ fun UserProfile(
|
||||
}
|
||||
}
|
||||
Loading -> {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Box(modifier = Modifier.fillMaxSize().padding(contentPadding)) {
|
||||
ProgressBar(modifier = Modifier.align(Alignment.Center))
|
||||
}
|
||||
}
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright © 2024 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 android.annotation.SuppressLint
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import dev.msfjarvis.claw.common.theme.LobstersTheme
|
||||
|
||||
@SuppressLint("ComposePreviewPublic", "ComposeUnstableReceiver")
|
||||
class MonthHeaderTest {
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun DefaultPreview() {
|
||||
LobstersTheme { MonthHeader(label = "April 2023") }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2022-2023 Harsh Shandilya.
|
||||
* Copyright © 2022-2024 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
@@ -11,10 +11,8 @@ package dev.msfjarvis.claw.core.coroutines
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
||||
/** Interface to allow abstracting individual [CoroutineDispatcher]s out of class dependencies. */
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
interface DispatcherProvider {
|
||||
|
||||
fun main(): CoroutineDispatcher = Dispatchers.Main
|
||||
@@ -23,7 +21,8 @@ interface DispatcherProvider {
|
||||
|
||||
fun io(): CoroutineDispatcher = Dispatchers.IO
|
||||
|
||||
fun database(): CoroutineDispatcher = Dispatchers.IO.limitedParallelism(1)
|
||||
fun database(): CoroutineDispatcher =
|
||||
Dispatchers.IO.limitedParallelism(1, name = "DatabaseDispatcher")
|
||||
}
|
||||
|
||||
/** Concrete type for [DispatcherProvider] with all the defaults from the class. */
|
||||
|
||||
+4
-1
@@ -49,9 +49,12 @@ android.dependencyResolutionAtConfigurationTime.disallow=true
|
||||
android.disableEarlyManifestParsing=true
|
||||
|
||||
# Disable warnings about unsupported features
|
||||
android.suppressUnsupportedOptionWarnings=android.dependencyResolutionAtConfigurationTime.disallow,android.disableEarlyManifestParsing,android.suppressUnsupportedOptionWarnings
|
||||
android.suppressUnsupportedOptionWarnings=android.dependencyResolutionAtConfigurationTime.disallow,android.disableEarlyManifestParsing,android.experimental.enableScreenshotTest,android.suppressUnsupportedOptionWarnings
|
||||
android.suppressUnsupportedCompileSdk=35
|
||||
|
||||
# Enable screenshot tests
|
||||
android.experimental.enableScreenshotTest=true
|
||||
|
||||
# Make Spotless default to keeping license headers years in sync
|
||||
spotlessSetLicenseHeaderYearsFromGitHistory=true
|
||||
|
||||
|
||||
+20
-18
@@ -1,35 +1,36 @@
|
||||
[versions]
|
||||
aboutLibraries = "11.2.3"
|
||||
agp = "8.6.0"
|
||||
agp = "8.6.1"
|
||||
android-junit5 = "1.11.0.0"
|
||||
benchmark = "1.3.0"
|
||||
benchmark = "1.3.1"
|
||||
coil = "2.7.0"
|
||||
coroutines = "1.9.0-RC.2"
|
||||
coroutines = "1.9.0"
|
||||
dagger = "2.52"
|
||||
glance = "1.0.0"
|
||||
haze = "0.9.0-beta01"
|
||||
junit = "5.11.0"
|
||||
konvert = "3.2.2"
|
||||
kotlin = "2.0.20"
|
||||
kotlinResult = "2.0.0"
|
||||
leakcanary = "3.0-alpha-8"
|
||||
lifecycle = "2.8.4"
|
||||
navigation = "2.8.0-rc01"
|
||||
lifecycle = "2.8.6"
|
||||
navigation = "2.8.1"
|
||||
retrofit = "2.11.0"
|
||||
richtext = "1.0.0-alpha01"
|
||||
sentry-sdk = "7.14.0"
|
||||
serialization = "1.7.2"
|
||||
serialization = "1.7.3"
|
||||
store = "5.1.0-alpha04"
|
||||
sqldelight = "2.0.2"
|
||||
whetstone = "0.9.0-beta02"
|
||||
workmanager = "2.10.0-alpha02"
|
||||
workmanager = "2.10.0-alpha04"
|
||||
|
||||
[libraries]
|
||||
aboutLibraries-m3 = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutLibraries" }
|
||||
androidx-activity-compose = "androidx.activity:activity-compose:1.9.1"
|
||||
androidx-activity-compose = "androidx.activity:activity-compose:1.9.2"
|
||||
androidx-benchmark-macro-junit4 = { module = "androidx.benchmark:benchmark-macro-junit4", version.ref = "benchmark" }
|
||||
androidx-browser = "androidx.browser:browser:1.8.0"
|
||||
androidx-compose-animation = { module = "androidx.compose.animation:animation" }
|
||||
androidx-compose-bom = "dev.chrisbanes.compose:compose-bom:2024.08.00-alpha02"
|
||||
androidx-compose-bom = "androidx.compose:compose-bom:2024.09.00-alpha"
|
||||
androidx-compose-foundation = { module = "androidx.compose.foundation:foundation" }
|
||||
androidx-compose-glance = { module = "androidx.glance:glance-appwidget", version.ref = "glance" }
|
||||
androidx-compose-glance-m3 = { module = "androidx.glance:glance-material3", version.ref = "glance" }
|
||||
@@ -44,12 +45,12 @@ androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling
|
||||
androidx-compose-ui-unit = { module = "androidx.compose.ui:ui-unit" }
|
||||
androidx-compose-ui-util = { module = "androidx.compose.ui:ui-util" }
|
||||
androidx-core = "androidx.core:core:1.13.1"
|
||||
androidx-core-splashscreen = "androidx.core:core-splashscreen:1.2.0-alpha01"
|
||||
androidx-core-splashscreen = "androidx.core:core-splashscreen:1.2.0-alpha02"
|
||||
androidx-lifecycle-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycle" }
|
||||
androidx-lint = "androidx.lint:lint-gradle:1.0.0-alpha01"
|
||||
androidx-lint = "androidx.lint:lint-gradle:1.0.0-alpha02"
|
||||
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigation" }
|
||||
androidx-paging-compose = "androidx.paging:paging-compose:3.3.2"
|
||||
androidx-profileinstaller = "androidx.profileinstaller:profileinstaller:1.4.0-beta01"
|
||||
androidx-profileinstaller = "androidx.profileinstaller:profileinstaller:1.4.0"
|
||||
androidx-test-core = "androidx.test:core:1.6.1"
|
||||
androidx-test-espresso-core = "androidx.test.espresso:espresso-core:3.6.1"
|
||||
androidx-test-ext-junit = "androidx.test.ext:junit:1.2.1"
|
||||
@@ -73,8 +74,8 @@ copydown = "io.github.furstenheim:copy_down:1.1"
|
||||
dagger = { module = "com.google.dagger:dagger", version.ref = "dagger" }
|
||||
dagger-compiler = { module = "com.google.dagger:dagger-compiler", version.ref = "dagger" }
|
||||
eithernet = "com.slack.eithernet:eithernet:1.9.0"
|
||||
haze = "dev.chrisbanes.haze:haze:0.9.0-alpha08"
|
||||
haze-materials = "dev.chrisbanes.haze:haze-materials:0.9.0-alpha08"
|
||||
haze = { module = "dev.chrisbanes.haze:haze", version.ref = "haze" }
|
||||
haze-materials = { module = "dev.chrisbanes.haze:haze-materials", version.ref = "haze" }
|
||||
javax-inject = "javax.inject:javax.inject:1"
|
||||
jsoup = "org.jsoup:jsoup:1.18.1"
|
||||
junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" }
|
||||
@@ -85,7 +86,7 @@ konvert-api = { module = "io.mcarle:konvert-api", version.ref = "konvert" }
|
||||
konvert-processor = { module = "io.mcarle:konvert", version.ref = "konvert" }
|
||||
kotlinResult = { module = "com.michael-bull.kotlin-result:kotlin-result", version.ref = "kotlinResult" }
|
||||
kotlinResult-coroutines = { module = "com.michael-bull.kotlin-result:kotlin-result-coroutines", version.ref = "kotlinResult" }
|
||||
kotlinx-collections-immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7"
|
||||
kotlinx-collections-immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.8"
|
||||
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
|
||||
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
|
||||
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
|
||||
@@ -120,15 +121,16 @@ android-junit5 = { id = "de.mannodermaus.android-junit5", version.ref = "android
|
||||
android-test = { id = "com.android.test", version.ref = "agp" }
|
||||
anvil = "com.squareup.anvil:2.5.0-beta11"
|
||||
baselineprofile = { id = "androidx.baselineprofile", version.ref = "benchmark" }
|
||||
dependencyAnalysis = "com.autonomousapps.dependency-analysis:2.0.1"
|
||||
dependencyAnalysis = "com.autonomousapps.dependency-analysis:2.0.2"
|
||||
invert = "com.squareup.invert:0.0.1-dev"
|
||||
kotlin-composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||
ksp = "com.google.devtools.ksp:2.0.20-1.0.24"
|
||||
ksp = "com.google.devtools.ksp:2.0.20-1.0.25"
|
||||
licensee = "app.cash.licensee:1.11.0"
|
||||
modulegraphassert = "com.jraska.module.graph.assertion:2.7.1"
|
||||
navigation-safeargs = { id = "androidx.navigation.safeargs.kotlin", version.ref = "navigation" }
|
||||
poko = "dev.drewhamilton.poko:0.17.0"
|
||||
poko = "dev.drewhamilton.poko:0.17.1"
|
||||
screenshot = "com.android.compose.screenshot:0.0.1-alpha07"
|
||||
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
|
||||
tracelog = "dev.msfjarvis.tracelog:0.1.3"
|
||||
whetstone = { id = "dev.msfjarvis.whetstone", version.ref = "whetstone" }
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=5b9c5eb3f9fc2c94abaea57d90bd78747ca117ddbbf96c859d3741181a12bf2a
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
|
||||
distributionSha256Sum=1541fa36599e12857140465f3c91a97409b4512501c26f9631fb113e392c5bd1
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ pluginManagement {
|
||||
|
||||
plugins {
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
|
||||
id("com.gradle.develocity") version "3.18"
|
||||
id("com.gradle.develocity") version "3.18.1"
|
||||
}
|
||||
|
||||
develocity {
|
||||
|
||||
Reference in New Issue
Block a user