mirror of
https://github.com/msfjarvis/compose-lobsters
synced 2025-08-14 16:27:06 +05:30
Merge pull request #258 from msfjarvis/comments-screen
This commit is contained in:
commit
aa1bd18411
16 changed files with 318 additions and 87 deletions
|
@ -20,7 +20,10 @@ dependencies {
|
|||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.androidx.coreKtx)
|
||||
implementation(libs.androidx.lifecycle.compose)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
implementation(libs.androidx.paging.compose)
|
||||
implementation(libs.multiplatform.markdown.android)
|
||||
implementation(libs.copydown)
|
||||
implementation(libs.dagger.hilt.android)
|
||||
implementation(libs.sqldelight.extensions.coroutines)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
package dev.msfjarvis.claw.android.ext
|
||||
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
import dev.msfjarvis.claw.model.LobstersPost
|
||||
|
||||
/** Convert a [LobstersPost] object returned by the API into a [SavedPost] for persistence. */
|
||||
fun LobstersPost.toDbModel(): SavedPost {
|
||||
return SavedPost(
|
||||
shortId = shortId,
|
||||
title = title,
|
||||
url = url,
|
||||
createdAt = createdAt,
|
||||
commentsUrl = commentsUrl,
|
||||
submitterName = submitter.username,
|
||||
submitterAvatarUrl = submitter.avatarUrl,
|
||||
tags = tags,
|
||||
)
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package dev.msfjarvis.claw.android.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import com.google.accompanist.swiperefresh.SwipeRefresh
|
||||
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
import dev.msfjarvis.claw.model.LobstersPost
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun HottestPosts(
|
||||
items: LazyPagingItems<LobstersPost>,
|
||||
listState: LazyListState,
|
||||
isPostSaved: suspend (SavedPost) -> Boolean,
|
||||
toggleSave: suspend (SavedPost) -> Unit,
|
||||
reloadPosts: () -> Unit,
|
||||
launchUrl: (String) -> Unit,
|
||||
viewComments: (String) -> Unit,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val isRefreshing = items.loadState.refresh == LoadState.Loading
|
||||
SwipeRefresh(
|
||||
state = rememberSwipeRefreshState(isRefreshing),
|
||||
onRefresh = reloadPosts,
|
||||
) {
|
||||
if (items.itemCount == 0) {
|
||||
Box(modifier = Modifier.fillMaxSize())
|
||||
} else {
|
||||
NetworkPosts(
|
||||
items = items,
|
||||
launchUrl = launchUrl,
|
||||
listState = listState,
|
||||
isSaved = isPostSaved,
|
||||
viewComments = viewComments,
|
||||
toggleSave = { coroutineScope.launch { toggleSave(it) } },
|
||||
modifier = Modifier.padding(top = 16.dp).then(modifier),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,9 +2,6 @@ package dev.msfjarvis.claw.android.ui
|
|||
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Scaffold
|
||||
|
@ -15,7 +12,6 @@ import androidx.compose.runtime.SideEffect
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
|
@ -23,20 +19,21 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.paging.LoadState
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.google.accompanist.insets.ProvideWindowInsets
|
||||
import com.google.accompanist.insets.navigationBarsPadding
|
||||
import com.google.accompanist.insets.statusBarsPadding
|
||||
import com.google.accompanist.swiperefresh.SwipeRefresh
|
||||
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.mikepenz.markdown.Markdown
|
||||
import dev.msfjarvis.claw.android.viewmodel.ClawViewModel
|
||||
import dev.msfjarvis.claw.common.comments.CommentsPage
|
||||
import dev.msfjarvis.claw.common.theme.LobstersTheme
|
||||
import dev.msfjarvis.claw.common.urllauncher.UrlLauncher
|
||||
import kotlinx.coroutines.launch
|
||||
import io.github.furstenheim.CopyDown
|
||||
|
||||
private const val ScrollDelta = 50
|
||||
|
||||
|
@ -46,10 +43,11 @@ fun LobstersApp(
|
|||
viewModel: ClawViewModel = viewModel(),
|
||||
urlLauncher: UrlLauncher,
|
||||
) {
|
||||
val copydown = remember { CopyDown() }
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val scaffoldState = rememberScaffoldState()
|
||||
val listState = rememberLazyListState()
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val navController = rememberNavController()
|
||||
var isFabVisible by remember { mutableStateOf(true) }
|
||||
val nestedScrollConnection = remember {
|
||||
object : NestedScrollConnection {
|
||||
|
@ -77,32 +75,40 @@ fun LobstersApp(
|
|||
systemUiController.setNavigationBarColor(color = Color.Transparent)
|
||||
}
|
||||
val items = viewModel.pagerFlow.collectAsLazyPagingItems()
|
||||
|
||||
Scaffold(
|
||||
scaffoldState = scaffoldState,
|
||||
topBar = { ClawAppBar(modifier = Modifier.statusBarsPadding()) },
|
||||
floatingActionButton = {
|
||||
ClawFab(
|
||||
isFabVisible = isFabVisible,
|
||||
isFabVisible = isFabVisible && navController.currentDestination?.route == "hottest",
|
||||
listState = listState,
|
||||
modifier = Modifier.navigationBarsPadding(),
|
||||
)
|
||||
},
|
||||
) {
|
||||
val isRefreshing = items.loadState.refresh == LoadState.Loading
|
||||
SwipeRefresh(
|
||||
state = rememberSwipeRefreshState(isRefreshing),
|
||||
onRefresh = viewModel::reloadPosts,
|
||||
) {
|
||||
if (items.itemCount == 0) {
|
||||
Box(modifier = Modifier.fillMaxSize())
|
||||
} else {
|
||||
NetworkPosts(
|
||||
items = items,
|
||||
launchUrl = urlLauncher::launch,
|
||||
listState = listState,
|
||||
isSaved = viewModel::isPostSaved,
|
||||
toggleSave = { coroutineScope.launch { viewModel.toggleSave(it) } },
|
||||
modifier = Modifier.padding(top = 16.dp).nestedScroll(nestedScrollConnection),
|
||||
) { paddingValues ->
|
||||
NavHost(navController, startDestination = "hottest") {
|
||||
composable("hottest") {
|
||||
HottestPosts(
|
||||
items,
|
||||
listState,
|
||||
viewModel::isPostSaved,
|
||||
viewModel::toggleSave,
|
||||
viewModel::reloadPosts,
|
||||
urlLauncher::launch,
|
||||
{ navController.navigate("comments/$it") },
|
||||
Modifier.nestedScroll(nestedScrollConnection),
|
||||
)
|
||||
}
|
||||
composable("comments/{postId}") { backStackEntry ->
|
||||
CommentsPage(
|
||||
postId = requireNotNull(backStackEntry.arguments?.getString("postId")),
|
||||
getDetails = viewModel::getPostComments,
|
||||
renderMarkdown = { source, modifier ->
|
||||
val markdown = copydown.convert(source)
|
||||
Markdown(markdown, modifier = modifier)
|
||||
},
|
||||
paddingValues = paddingValues,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import androidx.paging.compose.items
|
||||
import dev.msfjarvis.claw.android.ext.toDbModel
|
||||
import dev.msfjarvis.claw.common.posts.LobstersCard
|
||||
import dev.msfjarvis.claw.common.posts.toDbModel
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
import dev.msfjarvis.claw.model.LobstersPost
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -26,6 +26,7 @@ fun NetworkPosts(
|
|||
launchUrl: (String) -> Unit,
|
||||
isSaved: suspend (SavedPost) -> Boolean,
|
||||
toggleSave: (SavedPost) -> Unit,
|
||||
viewComments: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
@ -42,7 +43,7 @@ fun NetworkPosts(
|
|||
post = dbModel,
|
||||
isSaved = saved,
|
||||
viewPost = { launchUrl(item.url.ifEmpty { item.commentsUrl }) },
|
||||
viewComments = { launchUrl(item.commentsUrl) },
|
||||
viewComments = { viewComments(item.shortId) },
|
||||
toggleSave = { toggleSave(dbModel) },
|
||||
modifier = Modifier.padding(bottom = 16.dp, start = 16.dp, end = 16.dp),
|
||||
)
|
||||
|
|
|
@ -7,19 +7,22 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||
import dev.msfjarvis.claw.android.paging.LobstersPagingSource
|
||||
import dev.msfjarvis.claw.api.LobstersApi
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.last
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@HiltViewModel
|
||||
class ClawViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
api: LobstersApi,
|
||||
private val api: LobstersApi,
|
||||
private val repository: SavedPostsRepository,
|
||||
) : ViewModel() {
|
||||
var lastPagingSource: LobstersPagingSource? = null
|
||||
|
@ -46,6 +49,11 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun getPostComments(postId: String): LobstersPostDetails =
|
||||
withContext(Dispatchers.IO) {
|
||||
return@withContext api.getPostDetails(postId)
|
||||
}
|
||||
|
||||
fun reloadPosts() {
|
||||
lastPagingSource?.invalidate()
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ buildscript {
|
|||
classpath("com.android.tools:r8:3.1.17-dev")
|
||||
classpath(kotlin("gradle-plugin", version = kotlinVersion))
|
||||
classpath(kotlin("serialization", version = kotlinVersion))
|
||||
classpath("com.android.tools.build:gradle:7.1.0-alpha12")
|
||||
classpath("com.android.tools.build:gradle:7.1.0-alpha13")
|
||||
classpath("com.diffplug.spotless:spotless-plugin-gradle:5.15.0")
|
||||
classpath("com.google.dagger:hilt-android-gradle-plugin:2.39")
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ kotlin {
|
|||
api(compose.foundation)
|
||||
api(compose.material)
|
||||
api(projects.database)
|
||||
api(projects.model)
|
||||
}
|
||||
}
|
||||
sourceSets["androidMain"].apply {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package dev.msfjarvis.lobsters.ui.comments
|
||||
|
||||
sealed class NetworkState {
|
||||
class Success<T>(val data: T) : NetworkState()
|
||||
class Error(val message: String) : NetworkState()
|
||||
object Loading : NetworkState()
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package dev.msfjarvis.claw.common.comments
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.msfjarvis.claw.common.posts.PostDetails
|
||||
import dev.msfjarvis.claw.common.posts.SubmitterName
|
||||
import dev.msfjarvis.claw.common.posts.toDbModel
|
||||
import dev.msfjarvis.claw.model.Comment
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
|
||||
@Composable
|
||||
fun CommentsHeader(
|
||||
postDetails: LobstersPostDetails,
|
||||
) {
|
||||
Surface {
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp).fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
PostDetails(
|
||||
post = postDetails.toDbModel(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CommentEntry(
|
||||
comment: Comment,
|
||||
renderMarkdown: @Composable (comment: String, modifier: Modifier) -> Unit,
|
||||
) {
|
||||
val indentLevel = comment.indentLevel.toInt() - 1
|
||||
|
||||
Divider(color = Color.Gray.copy(0.4f))
|
||||
Row(modifier = Modifier.height(IntrinsicSize.Min)) {
|
||||
CommentTreeColors(indentLevel = indentLevel)
|
||||
Column(modifier = Modifier.padding(start = 12.dp, end = 8.dp, top = 4.dp, bottom = 4.dp)) {
|
||||
SubmitterName(
|
||||
text = "Submitted by ${comment.user.username}",
|
||||
avatarUrl = "https://lobste.rs/${comment.user.avatarUrl}",
|
||||
contentDescription = "Submitted by ${comment.user.username}",
|
||||
)
|
||||
renderMarkdown(comment.comment, Modifier.padding(top = 8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CommentTreeColors(
|
||||
indentLevel: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(modifier = modifier) {
|
||||
for (level in 1..indentLevel) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.padding(start = (12 + ((level - 1) * 10)).dp)
|
||||
.fillMaxHeight()
|
||||
.width(1.dp)
|
||||
.background(CommentTreeColor[level])
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ object CommentTreeColor {
|
|||
Color(0xFFFF0097),
|
||||
Color(0xFF1E7145),
|
||||
)
|
||||
private val size = colors.size
|
||||
val size = colors.size
|
||||
|
||||
operator fun get(idx: Int): Color = colors[idx % size]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package dev.msfjarvis.claw.common.comments
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.CircularProgressIndicator
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
import dev.msfjarvis.lobsters.ui.comments.NetworkState
|
||||
|
||||
@Composable
|
||||
private fun CommentsPageInternal(
|
||||
details: LobstersPostDetails,
|
||||
renderMarkdown: @Composable (comment: String, modifier: Modifier) -> Unit,
|
||||
bottomPadding: Dp,
|
||||
) {
|
||||
LazyColumn(Modifier.padding(bottom = bottomPadding)) {
|
||||
item { CommentsHeader(postDetails = details) }
|
||||
|
||||
item { Spacer(modifier = Modifier.height(8.dp)) }
|
||||
|
||||
items(details.comments) { item -> CommentEntry(item, renderMarkdown) }
|
||||
|
||||
item { Divider(color = Color.Gray.copy(0.4f)) }
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@Composable
|
||||
fun CommentsPage(
|
||||
postId: String,
|
||||
getDetails: suspend (String) -> LobstersPostDetails,
|
||||
renderMarkdown: @Composable (comment: String, modifier: Modifier) -> Unit,
|
||||
paddingValues: PaddingValues,
|
||||
) {
|
||||
var postDetails: NetworkState by remember { mutableStateOf(NetworkState.Loading) }
|
||||
|
||||
LaunchedEffect(postId) { postDetails = NetworkState.Success(getDetails(postId)) }
|
||||
|
||||
when (postDetails) {
|
||||
is NetworkState.Success<*> -> {
|
||||
CommentsPageInternal(
|
||||
(postDetails as NetworkState.Success<LobstersPostDetails>).data,
|
||||
renderMarkdown,
|
||||
paddingValues.calculateBottomPadding(),
|
||||
)
|
||||
}
|
||||
is NetworkState.Error -> TODO("Handle no network scenario")
|
||||
NetworkState.Loading -> ProgressBar(paddingValues.calculateBottomPadding())
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ProgressBar(bottomPadding: Dp) {
|
||||
Box(
|
||||
modifier = Modifier.padding(bottom = bottomPadding).fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) { CircularProgressIndicator(color = MaterialTheme.colors.secondary) }
|
||||
}
|
|
@ -37,18 +37,6 @@ import dev.msfjarvis.claw.common.theme.titleColor
|
|||
import dev.msfjarvis.claw.common.ui.NetworkImage
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
|
||||
val TEST_POST =
|
||||
SavedPost(
|
||||
shortId = "zqyydb",
|
||||
title = "k2k20 hackathon report: Bob Beck on LibreSSL progress",
|
||||
url = "https://undeadly.org/cgi?action=article;sid=20200921105847",
|
||||
createdAt = "2020-09-21T07:11:14.000-05:00",
|
||||
commentsUrl = "https://lobste.rs/s/zqyydb/k2k20_hackathon_report_bob_beck_on",
|
||||
submitterName = "Vigdis",
|
||||
submitterAvatarUrl = "https://avatars.githubusercontent.com/u/13348378?v=4",
|
||||
tags = listOf("openbsd", "linux", "containers", "hack the planet", "no thanks"),
|
||||
)
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
fun LobstersCard(
|
||||
|
@ -67,16 +55,8 @@ fun LobstersCard(
|
|||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp).fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
PostTitle(
|
||||
title = post.title,
|
||||
)
|
||||
TagRow(
|
||||
tags = post.tags,
|
||||
)
|
||||
SubmitterName(
|
||||
text = "Submitted by ${post.submitterName}",
|
||||
avatarUrl = "https://lobste.rs/${post.submitterAvatarUrl}",
|
||||
contentDescription = "Submitted by ${post.submitterName}",
|
||||
PostDetails(
|
||||
post = post,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp),
|
||||
|
@ -98,6 +78,23 @@ fun LobstersCard(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PostDetails(
|
||||
post: SavedPost,
|
||||
) {
|
||||
PostTitle(
|
||||
title = post.title,
|
||||
)
|
||||
TagRow(
|
||||
tags = post.tags,
|
||||
)
|
||||
SubmitterName(
|
||||
text = "Submitted by ${post.submitterName}",
|
||||
avatarUrl = "https://lobste.rs/${post.submitterAvatarUrl}",
|
||||
contentDescription = "Submitted by ${post.submitterName}",
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PostTitle(
|
||||
title: String,
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package dev.msfjarvis.claw.common.posts
|
||||
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
import dev.msfjarvis.claw.model.LobstersPost
|
||||
import dev.msfjarvis.claw.model.LobstersPostDetails
|
||||
|
||||
fun LobstersPost.toDbModel(): SavedPost {
|
||||
return SavedPost(
|
||||
shortId = shortId,
|
||||
title = title,
|
||||
url = url,
|
||||
createdAt = createdAt,
|
||||
commentsUrl = commentsUrl,
|
||||
submitterName = submitter.username,
|
||||
submitterAvatarUrl = submitter.avatarUrl,
|
||||
tags = tags,
|
||||
)
|
||||
}
|
||||
|
||||
fun LobstersPostDetails.toDbModel(): SavedPost {
|
||||
return SavedPost(
|
||||
shortId = shortId,
|
||||
title = title,
|
||||
url = url,
|
||||
createdAt = createdAt,
|
||||
commentsUrl = commentsUrl,
|
||||
submitterName = submitter.username,
|
||||
submitterAvatarUrl = submitter.avatarUrl,
|
||||
tags = tags,
|
||||
)
|
||||
}
|
|
@ -14,27 +14,13 @@ import androidx.compose.ui.window.WindowPosition
|
|||
import androidx.compose.ui.window.application
|
||||
import androidx.compose.ui.window.rememberWindowState
|
||||
import dev.msfjarvis.claw.common.posts.LobstersCard
|
||||
import dev.msfjarvis.claw.common.posts.toDbModel
|
||||
import dev.msfjarvis.claw.common.theme.LobstersTheme
|
||||
import dev.msfjarvis.claw.common.urllauncher.UrlLauncher
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
import dev.msfjarvis.claw.model.LobstersPost
|
||||
import org.pushingpixels.aurora.component.AuroraVerticalScrollbar
|
||||
import org.pushingpixels.aurora.skin.ceruleanSkin
|
||||
import org.pushingpixels.aurora.window.AuroraWindow
|
||||
|
||||
fun LobstersPost.toDbModel(): SavedPost {
|
||||
return SavedPost(
|
||||
shortId = shortId,
|
||||
title = title,
|
||||
url = url,
|
||||
createdAt = createdAt,
|
||||
commentsUrl = commentsUrl,
|
||||
submitterName = submitter.username,
|
||||
submitterAvatarUrl = submitter.avatarUrl,
|
||||
tags = tags,
|
||||
)
|
||||
}
|
||||
|
||||
fun main() = application {
|
||||
val paging = Paging(rememberCoroutineScope())
|
||||
val items = paging.pagingData.collectAsLazyPagingItems()
|
||||
|
|
|
@ -23,6 +23,7 @@ androidx-appcompat = "androidx.appcompat:appcompat:1.4.0-beta01"
|
|||
androidx-browser = "androidx.browser:browser:1.4.0-beta01"
|
||||
androidx-coreKtx = "androidx.core:core-ktx:1.7.0-beta02"
|
||||
androidx-lifecycle-compose = "androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0-rc01"
|
||||
androidx-navigation-compose = "androidx.navigation:navigation-compose:2.4.0-alpha10"
|
||||
androidx-paging-compose = "androidx.paging:paging-compose:1.0.0-alpha13"
|
||||
|
||||
aurora-component = { module = "org.pushing-pixels:aurora-component", version.ref = "aurora" }
|
||||
|
@ -36,6 +37,9 @@ dagger-hilt-android = { module = "com.google.dagger:hilt-android", version.ref =
|
|||
dagger-hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" }
|
||||
dagger-hilt-core = { module = "com.google.dagger:hilt-core", version.ref = "hilt" }
|
||||
|
||||
multiplatform-markdown-android = "com.mikepenz:multiplatform-markdown-renderer-android:0.0.1"
|
||||
copydown = "io.github.furstenheim:copy_down:1.0"
|
||||
|
||||
multiplatform-paging = "dev.msfjarvis.paging:multiplatform-paging:0.4.5-SNAPSHOT"
|
||||
|
||||
retrofit-lib = "com.squareup.retrofit2:retrofit:2.9.0"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue