Skip to content

Commit

Permalink
[1.224.*] Pre-release merge (#696)
Browse files Browse the repository at this point in the history
  • Loading branch information
tramline-github[bot] authored Oct 27, 2024
2 parents 0943931 + 8303530 commit 93d2e8f
Show file tree
Hide file tree
Showing 17 changed files with 380 additions and 26 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- A brand new tablet-specific UI — Thanks [@ThanaReka](https://github.com/@ThanaReka)

### Fixes

- Add a workaround for rare crashes while loading SQLite with older devices
Expand Down
4 changes: 4 additions & 0 deletions android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ plugins {
id("dev.msfjarvis.claw.kotlin-kapt")
id("dev.msfjarvis.claw.sentry")
id("dev.msfjarvis.claw.versioning-plugin")
id("kotlin-parcelize")
alias(libs.plugins.aboutlibraries)
alias(libs.plugins.android.junit5)
alias(libs.plugins.anvil)
Expand Down Expand Up @@ -89,6 +90,9 @@ dependencies {
implementation(libs.androidx.compose.material.icons.extended)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.material3.window.size)
implementation(libs.androidx.compose.material3.adaptive)
implementation(libs.androidx.compose.material3.adaptive.layout)
implementation(libs.androidx.compose.material3.adaptive.navigation)
implementation(libs.androidx.compose.runtime)
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.text)
Expand Down
29 changes: 23 additions & 6 deletions android/src/main/kotlin/dev/msfjarvis/claw/android/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
*/
package dev.msfjarvis.claw.android

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import com.deliveryhero.whetstone.activity.ContributesActivityInjector
import dev.msfjarvis.claw.android.ui.screens.LobstersPostsScreen
import dev.msfjarvis.claw.android.ui.screens.TwoPaneLayout

@ContributesActivityInjector
class MainActivity : BaseActivity() {
Expand All @@ -20,12 +24,25 @@ class MainActivity : BaseActivity() {
@Composable
override fun Content() {
val windowSizeClass = calculateWindowSizeClass(this)
LobstersPostsScreen(
urlLauncher = urlLauncher,
htmlConverter = htmlConverter,
windowSizeClass = windowSizeClass,
setWebUri = { url -> webUri = url },
)

when (windowSizeClass.widthSizeClass) {
WindowWidthSizeClass.Compact -> {
LobstersPostsScreen(
urlLauncher = urlLauncher,
htmlConverter = htmlConverter,
windowSizeClass = windowSizeClass,
setWebUri = { url -> webUri = url },
)
}

else -> {
TwoPaneLayout(
urlLauncher = urlLauncher,
htmlConverter = htmlConverter,
modifier = Modifier.fillMaxSize(),
)
}
}
}

override fun preLaunch() {
Expand Down
51 changes: 51 additions & 0 deletions android/src/main/kotlin/dev/msfjarvis/claw/android/ui/ext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,57 @@ fun PostActions(
}
}

fun TwoPaneLayoutPostActions(
context: Context,
urlLauncher: UrlLauncher,
viewModel: ClawViewModel,
setSelectedPost: (String) -> Unit,
): PostActions {
return object : PostActions {
override fun viewPost(postId: String, postUrl: String, commentsUrl: String) {
viewModel.markPostAsRead(postId)
urlLauncher.openUri(postUrl.ifEmpty { commentsUrl })
}

override fun viewComments(postId: String) {
viewModel.markPostAsRead(postId)
setSelectedPost(postId) // Update selectedPostId
}

override fun viewCommentsPage(post: UIPost) {
urlLauncher.openUri(post.commentsUrl)
}

override fun toggleSave(post: UIPost) {
viewModel.toggleSave(post)
}

override fun share(post: UIPost) {
val sendIntent =
Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, post.url.ifEmpty { post.commentsUrl })
putExtra(Intent.EXTRA_TITLE, post.title)
type = "text/plain"
}
val shareIntent = Intent.createChooser(sendIntent, null)
context.startActivity(shareIntent)
}

override fun isPostRead(post: UIPost): Boolean = viewModel.isPostRead(post)

override fun isPostSaved(post: UIPost): Boolean = viewModel.isPostSaved(post)

override suspend fun getComments(postId: String): UIPost {
return viewModel.getPostComments(postId)
}

override suspend fun getLinkMetadata(url: String): LinkMetadata {
return viewModel.getLinkMetadata(url)
}
}
}

/**
* Convert an [ApiResult.Failure.HttpFailure] to a scoped down error with a more useful user-facing
* message.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package dev.msfjarvis.claw.android.ui.lists

import androidx.activity.compose.ReportDrawnWhen
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
Expand Down Expand Up @@ -50,6 +51,7 @@ fun NetworkPosts(
postActions: PostActions,
contentPadding: PaddingValues,
modifier: Modifier = Modifier,
onPostClick: (String) -> Unit,
) {
ReportDrawnWhen { lazyPagingItems.itemCount > 0 }
val refreshLoadState by rememberUpdatedState(lazyPagingItems.loadState.refresh)
Expand Down Expand Up @@ -82,7 +84,14 @@ fun NetworkPosts(
) { index ->
val item = lazyPagingItems[index]
if (item != null) {
LobstersListItem(item = item, postActions = postActions)
LobstersListItem(
item = item,
postActions = postActions,
modifier =
Modifier.clickable {
onPostClick(item.shortId) // Trigger the click listener
},
)
HorizontalDivider()
}
}
Expand Down Expand Up @@ -112,6 +121,7 @@ private fun ListPreview() {
listState = rememberLazyListState(),
postActions = TEST_POST_ACTIONS,
contentPadding = PaddingValues(),
onPostClick = {},
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright © 2021-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 androidx.activity.compose.ReportDrawnWhen
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.paging.LoadState
import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemContentType
import androidx.paging.compose.itemKey
import dev.msfjarvis.claw.common.posts.PostActions
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.NetworkError
import dev.msfjarvis.claw.common.ui.ProgressBar
import dev.msfjarvis.claw.common.ui.preview.DevicePreviews
import dev.msfjarvis.claw.model.UIPost
import kotlinx.coroutines.flow.MutableStateFlow

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NetworkPostsForTwoPaneLayout(
lazyPagingItems: LazyPagingItems<UIPost>,
listState: LazyListState,
postActions: PostActions,
contentPadding: PaddingValues,
modifier: Modifier = Modifier,
onPostClick: (String) -> Unit,
) {
ReportDrawnWhen { lazyPagingItems.itemCount > 0 }
val refreshLoadState by rememberUpdatedState(lazyPagingItems.loadState.refresh)
val state = rememberPullToRefreshState()
PullToRefreshBox(
modifier = modifier.fillMaxSize(),
isRefreshing = refreshLoadState == LoadState.Loading,
state = state,
onRefresh = { lazyPagingItems.refresh() },
indicator = {
PullToRefreshDefaults.Indicator(
state = state,
isRefreshing = refreshLoadState == LoadState.Loading,
modifier = Modifier.align(Alignment.TopCenter).padding(contentPadding),
)
},
) {
if (lazyPagingItems.itemCount == 0 && refreshLoadState is LoadState.Error) {
NetworkError(
label = "Failed to load posts",
error = (refreshLoadState as LoadState.Error).error,
modifier = Modifier.align(Alignment.Center),
)
} else {
LazyColumn(contentPadding = contentPadding, state = listState) {
items(
count = lazyPagingItems.itemCount,
key = lazyPagingItems.itemKey { it.shortId },
contentType = lazyPagingItems.itemContentType { "LobstersItem" },
) { index ->
val item = lazyPagingItems[index]
if (item != null) {
LobstersListItem(
item = item,
postActions = postActions,
modifier =
Modifier.clickable {
onPostClick(item.shortId) // Trigger the click listener)
},
)
HorizontalDivider()
}
}
if (lazyPagingItems.loadState.append == LoadState.Loading) {
item(key = "progressbar") {
ProgressBar(
modifier =
Modifier.fillMaxWidth()
.wrapContentWidth(Alignment.CenterHorizontally)
.padding(vertical = 16.dp)
)
}
}
}
}
}
}

@DevicePreviews
@Composable
private fun ListPreview() {
val items = List(20) { TEST_POST.copy(shortId = "${TEST_POST.shortId}${it}") }
val flow = MutableStateFlow(PagingData.from(items))
LobstersTheme {
NetworkPostsForTwoPaneLayout(
lazyPagingItems = flow.collectAsLazyPagingItems(),
listState = rememberLazyListState(),
postActions = TEST_POST_ACTIONS,
contentPadding = PaddingValues(),
onPostClick = {},
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ fun SearchList(
setSearchQuery: (String) -> Unit,
contentPadding: PaddingValues,
modifier: Modifier = Modifier,
onPostClick: (String) -> Unit,
) {
val triggerSearch = { query: String ->
setSearchQuery(query)
Expand All @@ -53,6 +54,7 @@ fun SearchList(
listState = listState,
postActions = postActions,
contentPadding = contentPadding,
onPostClick = onPostClick,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,24 @@
*/
package dev.msfjarvis.claw.android.ui.navigation

import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable

sealed interface Destination
sealed interface Destination : Parcelable

@Serializable data object Hottest : Destination
@Parcelize @Serializable data object Hottest : Destination

@Serializable data object Newest : Destination
@Parcelize @Serializable data object Newest : Destination

@Serializable data object Saved : Destination
@Parcelize @Serializable data object Saved : Destination

@Serializable data class Comments(val postId: String) : Destination
@Parcelize @Serializable data class Comments(val postId: String) : Destination

@Serializable data class User(val username: String) : Destination
@Parcelize @Serializable data class User(val username: String) : Destination

@Serializable data object Search : Destination
@Parcelize @Serializable data object Search : Destination

@Serializable data object Settings : Destination
@Parcelize @Serializable data object Settings : Destination

@Serializable data object AboutLibraries : Destination
@Parcelize @Serializable data object AboutLibraries : Destination
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ fun LobstersPostsScreen(
navController.previousBackStackEntry != null && currentDestination.none(navDestinations)
) {
IconButton(
onClick = { if (!navController.navigateUp()) context.getActivity()?.finish() }
onClick = { if (!navController.popBackStack()) context.getActivity()?.finish() }
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
Expand Down Expand Up @@ -238,6 +238,7 @@ fun LobstersPostsScreen(
listState = hottestListState,
postActions = postActions,
contentPadding = contentPadding,
onPostClick = { postId -> navController.navigate(Comments(postId)) },
)
}
composable<Newest> {
Expand All @@ -247,6 +248,7 @@ fun LobstersPostsScreen(
listState = newestListState,
postActions = postActions,
contentPadding = contentPadding,
onPostClick = { postId -> navController.navigate(Comments(postId)) },
)
}
composable<Saved> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ fun SearchScreen(
val postActions = remember { PostActions(context, urlLauncher, navController, viewModel) }
val listState = rememberLazyListState()
val searchResults = viewModel.searchResults.collectAsLazyPagingItems()

val onPostClick: (String) -> Unit = { postId -> navController.navigate("comments/$postId") }
Scaffold(modifier = modifier) { contentPadding ->
NavHost(navController = navController, startDestination = Search) {
composable<Search> {
Expand All @@ -53,6 +55,7 @@ fun SearchScreen(
searchQuery = viewModel.searchQuery,
contentPadding = contentPadding,
setSearchQuery = { query -> viewModel.searchQuery = query },
onPostClick = onPostClick,
)
}
composable<Comments> { backStackEntry ->
Expand Down
Loading

0 comments on commit 93d2e8f

Please sign in to comment.