添加业务逻辑
This commit is contained in:
@@ -78,6 +78,8 @@ dependencies {
|
|||||||
debugImplementation(libs.androidx.ui.test.manifest)
|
debugImplementation(libs.androidx.ui.test.manifest)
|
||||||
implementation (libs.places)
|
implementation (libs.places)
|
||||||
implementation(libs.androidx.animation)
|
implementation(libs.androidx.animation)
|
||||||
|
implementation("io.coil-kt:coil-compose:2.7.0")
|
||||||
|
implementation("io.coil-kt:coil:2.7.0")
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package com.aiosman.riderpro.data.comment
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import androidx.paging.PagingState
|
||||||
|
import com.aiosman.riderpro.data.ListContainer
|
||||||
|
import java.io.IOException
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
data class Comment(
|
||||||
|
val name: String,
|
||||||
|
val comment: String,
|
||||||
|
val date: String,
|
||||||
|
val likes: Int,
|
||||||
|
val replies: List<Comment>
|
||||||
|
)
|
||||||
|
|
||||||
|
class CommentPagingSource(
|
||||||
|
private val remoteDataSource: CommentRemoteDataSource,
|
||||||
|
) : PagingSource<Int, Comment>() {
|
||||||
|
|
||||||
|
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Comment> {
|
||||||
|
return try {
|
||||||
|
val currentPage = params.key ?: 1
|
||||||
|
val comments = remoteDataSource.getComments(
|
||||||
|
pageNumber = currentPage
|
||||||
|
)
|
||||||
|
|
||||||
|
LoadResult.Page(
|
||||||
|
data = comments.list,
|
||||||
|
prevKey = if (currentPage == 1) null else currentPage - 1,
|
||||||
|
nextKey = if (comments.list.isEmpty()) null else comments.page + 1
|
||||||
|
)
|
||||||
|
} catch (exception: IOException) {
|
||||||
|
return LoadResult.Error(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRefreshKey(state: PagingState<Int, Comment>): Int? {
|
||||||
|
return state.anchorPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class CommentRemoteDataSource(
|
||||||
|
private val commentService: CommentService,
|
||||||
|
) {
|
||||||
|
suspend fun getComments(pageNumber: Int): ListContainer<Comment> {
|
||||||
|
return commentService.getComments(pageNumber)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CommentService {
|
||||||
|
suspend fun getComments(pageNumber: Int): ListContainer<Comment>
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestCommentServiceImpl : CommentService {
|
||||||
|
private val mockData = generateMockComments(100)
|
||||||
|
override suspend fun getComments(pageNumber: Int): ListContainer<Comment> {
|
||||||
|
val from = pageNumber * DataBatchSize
|
||||||
|
val to = (pageNumber + 1) * DataBatchSize
|
||||||
|
val currentSublist = mockData.subList(from, to)
|
||||||
|
return ListContainer(
|
||||||
|
total = mockData.size,
|
||||||
|
page = pageNumber,
|
||||||
|
pageSize = DataBatchSize,
|
||||||
|
list = currentSublist
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateMockComments(count: Int): List<Comment> {
|
||||||
|
return (0 until count).map {
|
||||||
|
Comment(
|
||||||
|
name = "User $it",
|
||||||
|
comment = "This is comment $it",
|
||||||
|
date = "2023-02-02 11:23",
|
||||||
|
likes = Random.nextInt(0, 100),
|
||||||
|
replies = generateMockReplies()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateMockReplies(): List<Comment> {
|
||||||
|
val replyCount = Random.nextInt(0, 6)
|
||||||
|
return (0 until replyCount).map {
|
||||||
|
Comment(
|
||||||
|
name = "Reply User $it",
|
||||||
|
comment = "This is reply $it",
|
||||||
|
date = "2023-02-02 11:23",
|
||||||
|
likes = Random.nextInt(0, 100),
|
||||||
|
replies = emptyList()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val DataBatchSize = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.aiosman.riderpro.data.moment
|
package com.aiosman.riderpro.data.moment
|
||||||
|
|
||||||
import android.net.http.HttpException
|
|
||||||
import androidx.paging.PagingSource
|
import androidx.paging.PagingSource
|
||||||
import androidx.paging.PagingState
|
import androidx.paging.PagingState
|
||||||
import com.aiosman.riderpro.R
|
import com.aiosman.riderpro.R
|
||||||
import com.aiosman.riderpro.data.ListContainer
|
import com.aiosman.riderpro.data.ListContainer
|
||||||
import com.aiosman.riderpro.model.MomentItem
|
import com.aiosman.riderpro.model.MomentItem
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
class MomentPagingSource(
|
class MomentPagingSource(
|
||||||
private val remoteDataSource: MomentRemoteDataSource,
|
private val remoteDataSource: MomentRemoteDataSource,
|
||||||
@@ -45,9 +45,15 @@ class MomentRemoteDataSource(
|
|||||||
|
|
||||||
interface MomentService {
|
interface MomentService {
|
||||||
suspend fun getMoments(pageNumber: Int): ListContainer<MomentItem>
|
suspend fun getMoments(pageNumber: Int): ListContainer<MomentItem>
|
||||||
|
suspend fun getMomentById(id: Int): MomentItem
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestMomentServiceImpl() : MomentService {
|
class TestMomentServiceImpl() : MomentService {
|
||||||
|
var imageList = listOf(
|
||||||
|
"https://img.freepik.com/free-photo/white-billboard-template_23-2147726635.jpg?t=st=1722150015~exp=1722153615~hmac=5540620196d7898215d822be26353c87a63d51bbfb2b814e032626e1948a1583&w=740",
|
||||||
|
"https://img.freepik.com/free-photo/minimal-clothing-label-fashion-brands_53876-111053.jpg?w=1060&t=st=1722150122~exp=1722150722~hmac=67f8a2b6abfe3d08714cf0cc0085485c3221e1ba00dda14378b03753dce39153",
|
||||||
|
"https://img.freepik.com/free-photo/marketing-strategy-planning-strategy-concept_53876-42950.jpg"
|
||||||
|
)
|
||||||
val mockData = (0..300).toList().mapIndexed { idx, _ ->
|
val mockData = (0..300).toList().mapIndexed { idx, _ ->
|
||||||
MomentItem(
|
MomentItem(
|
||||||
id = idx,
|
id = idx,
|
||||||
@@ -61,13 +67,18 @@ class TestMomentServiceImpl() : MomentService {
|
|||||||
likeCount = 21,
|
likeCount = 21,
|
||||||
commentCount = 43,
|
commentCount = 43,
|
||||||
shareCount = 33,
|
shareCount = 33,
|
||||||
favoriteCount = 211
|
favoriteCount = 211,
|
||||||
|
images = imageList.shuffled().take(3),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val testMomentBackend = TestMomentBackend(mockData)
|
val testMomentBackend = TestMomentBackend(mockData)
|
||||||
override suspend fun getMoments(pageNumber: Int): ListContainer<MomentItem> {
|
override suspend fun getMoments(pageNumber: Int): ListContainer<MomentItem> {
|
||||||
return testMomentBackend.fetchMomentItems(pageNumber)
|
return testMomentBackend.fetchMomentItems(pageNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getMomentById(id: Int): MomentItem {
|
||||||
|
return mockData[id]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestMomentBackend(
|
class TestMomentBackend(
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ data class MomentItem(
|
|||||||
val likeCount: Int,
|
val likeCount: Int,
|
||||||
val commentCount: Int,
|
val commentCount: Int,
|
||||||
val shareCount: Int,
|
val shareCount: Int,
|
||||||
val favoriteCount: Int
|
val favoriteCount: Int,
|
||||||
|
val images: List<String> = emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
val momentTestItem = MomentItem(
|
val momentTestItem = MomentItem(
|
||||||
|
|||||||
@@ -35,13 +35,32 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
import com.aiosman.riderpro.ui.post.CommentsSection
|
import com.aiosman.riderpro.ui.post.CommentsSection
|
||||||
import com.aiosman.riderpro.R
|
import com.aiosman.riderpro.R
|
||||||
|
import com.aiosman.riderpro.data.comment.Comment
|
||||||
|
import com.aiosman.riderpro.data.comment.CommentPagingSource
|
||||||
|
import com.aiosman.riderpro.data.comment.CommentRemoteDataSource
|
||||||
|
import com.aiosman.riderpro.data.comment.TestCommentServiceImpl
|
||||||
|
import com.aiosman.riderpro.model.MomentItem
|
||||||
|
import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel.momentListPagingSource
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun CommentModalContent(onDismiss: () -> Unit = {}) {
|
fun CommentModalContent(onDismiss: () -> Unit = {}) {
|
||||||
|
var commentSource = CommentPagingSource(
|
||||||
|
CommentRemoteDataSource(TestCommentServiceImpl())
|
||||||
|
)
|
||||||
|
val commentsFlow: Flow<PagingData<Comment>> = Pager(
|
||||||
|
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||||
|
pagingSourceFactory = { commentSource }
|
||||||
|
).flow
|
||||||
|
val comments = commentsFlow.collectAsLazyPagingItems()
|
||||||
val insets = WindowInsets
|
val insets = WindowInsets
|
||||||
val imePadding = insets.ime.getBottom(density = LocalDensity.current)
|
val imePadding = insets.ime.getBottom(density = LocalDensity.current)
|
||||||
var bottomPadding by remember { mutableStateOf(0.dp) }
|
var bottomPadding by remember { mutableStateOf(0.dp) }
|
||||||
@@ -80,7 +99,11 @@ fun CommentModalContent(onDismiss: () -> Unit = {}) {
|
|||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
) {
|
) {
|
||||||
CommentsSection{}
|
|
||||||
|
CommentsSection(lazyPagingItems = comments) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -131,7 +154,6 @@ fun CommentModalContent(onDismiss: () -> Unit = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package com.aiosman.riderpro.ui.composables
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
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.layout.width
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.aiosman.riderpro.R
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun EditCommentBottomModal() {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(Color(0xfff7f7f7))
|
||||||
|
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
|
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.align(Alignment.Center),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
// rounded
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.weight(1f)
|
||||||
|
.clip(RoundedCornerShape(20.dp))
|
||||||
|
.background(Color(0xffe5e5e5))
|
||||||
|
.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||||
|
|
||||||
|
) {
|
||||||
|
BasicTextField(
|
||||||
|
value = "",
|
||||||
|
onValueChange = { },
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
textStyle = TextStyle(
|
||||||
|
color = Color.Black,
|
||||||
|
fontWeight = FontWeight.Normal
|
||||||
|
),
|
||||||
|
minLines = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
|
Image(
|
||||||
|
painter = painterResource(id = R.drawable.rider_pro_send),
|
||||||
|
contentDescription = "Send",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(32.dp)
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Box
|
|||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
import androidx.compose.foundation.layout.defaultMinSize
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -36,6 +37,7 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
@@ -43,6 +45,7 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
|
import coil.compose.AsyncImage
|
||||||
import com.aiosman.riderpro.LocalAnimatedContentScope
|
import com.aiosman.riderpro.LocalAnimatedContentScope
|
||||||
import com.aiosman.riderpro.LocalNavController
|
import com.aiosman.riderpro.LocalNavController
|
||||||
import com.aiosman.riderpro.LocalSharedTransitionScope
|
import com.aiosman.riderpro.LocalSharedTransitionScope
|
||||||
@@ -251,34 +254,30 @@ fun MomentTopRowGroup(momentItem: MomentItem) {
|
|||||||
fun MomentContentGroup(
|
fun MomentContentGroup(
|
||||||
momentItem: MomentItem,
|
momentItem: MomentItem,
|
||||||
) {
|
) {
|
||||||
val sharedTransitionScope = LocalSharedTransitionScope.current
|
// val sharedTransitionScope = LocalSharedTransitionScope.current
|
||||||
val animatedContentScope = LocalAnimatedContentScope.current
|
// val animatedContentScope = LocalAnimatedContentScope.current
|
||||||
with(sharedTransitionScope) {
|
val displayImageUrl = momentItem.images.firstOrNull()
|
||||||
Text(
|
Text(
|
||||||
text = momentItem.momentTextContent,
|
text = momentItem.momentTextContent,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 22.dp, bottom = 16.dp, start = 24.dp, end = 24.dp),
|
||||||
|
fontSize = 16.sp
|
||||||
|
)
|
||||||
|
displayImageUrl?.let {
|
||||||
|
AsyncImage(
|
||||||
|
it,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.sharedElement(
|
|
||||||
sharedTransitionScope.rememberSharedContentState(key = "text-${momentItem.id}"),
|
|
||||||
animatedVisibilityScope = animatedContentScope
|
|
||||||
)
|
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(top = 22.dp, bottom = 16.dp, start = 24.dp, end = 24.dp),
|
.aspectRatio(1f),
|
||||||
fontSize = 16.sp
|
contentScale = ContentScale.Crop,
|
||||||
)
|
|
||||||
Image(
|
|
||||||
modifier = Modifier
|
|
||||||
.sharedElement(
|
|
||||||
sharedTransitionScope.rememberSharedContentState(key = "image-${momentItem.id}"),
|
|
||||||
animatedVisibilityScope = animatedContentScope
|
|
||||||
)
|
|
||||||
.fillMaxWidth(),
|
|
||||||
painter = painterResource(id = momentItem.momentPicture),
|
|
||||||
contentDescription = ""
|
contentDescription = ""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MomentOperateBtn(@DrawableRes icon: Int, count: String) {
|
fun MomentOperateBtn(@DrawableRes icon: Int, count: String) {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
|||||||
@@ -36,8 +36,10 @@ import androidx.compose.material.icons.filled.Star
|
|||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -56,36 +58,66 @@ import androidx.compose.ui.text.TextStyle
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import androidx.paging.compose.LazyPagingItems
|
||||||
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
|
import coil.compose.AsyncImage
|
||||||
import com.aiosman.riderpro.LocalAnimatedContentScope
|
import com.aiosman.riderpro.LocalAnimatedContentScope
|
||||||
|
import com.aiosman.riderpro.LocalNavController
|
||||||
import com.aiosman.riderpro.LocalSharedTransitionScope
|
import com.aiosman.riderpro.LocalSharedTransitionScope
|
||||||
import com.aiosman.riderpro.R
|
import com.aiosman.riderpro.R
|
||||||
|
import com.aiosman.riderpro.data.comment.Comment
|
||||||
|
import com.aiosman.riderpro.data.comment.CommentPagingSource
|
||||||
|
import com.aiosman.riderpro.data.comment.CommentRemoteDataSource
|
||||||
|
import com.aiosman.riderpro.data.comment.TestCommentServiceImpl
|
||||||
|
import com.aiosman.riderpro.data.moment.MomentService
|
||||||
|
import com.aiosman.riderpro.data.moment.TestMomentServiceImpl
|
||||||
|
import com.aiosman.riderpro.model.MomentItem
|
||||||
|
import com.aiosman.riderpro.ui.comment.CommentModalContent
|
||||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||||
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
|
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
|
||||||
|
import com.aiosman.riderpro.ui.composables.EditCommentBottomModal
|
||||||
|
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||||
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
fun makeMockImages(): List<PostImage> {
|
|
||||||
return listOf(
|
|
||||||
PostImage(R.drawable.default_moment_img, "Image 1"),
|
|
||||||
PostImage(R.drawable.default_avatar, "Image 2"),
|
|
||||||
PostImage(R.drawable.rider_pro_moment_demo_1, "Image 3")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalSharedTransitionApi::class)
|
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun PostScreen(
|
fun PostScreen(
|
||||||
id: String,
|
id: String,
|
||||||
) {
|
) {
|
||||||
|
var service: MomentService = TestMomentServiceImpl()
|
||||||
|
|
||||||
|
var commentSource = CommentPagingSource(
|
||||||
|
CommentRemoteDataSource(TestCommentServiceImpl())
|
||||||
|
)
|
||||||
|
val commentsFlow: Flow<PagingData<Comment>> = Pager(
|
||||||
|
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||||
|
pagingSourceFactory = { commentSource }
|
||||||
|
).flow
|
||||||
|
val lazyPagingItems = commentsFlow.collectAsLazyPagingItems()
|
||||||
var showCollapseContent by remember { mutableStateOf(true) }
|
var showCollapseContent by remember { mutableStateOf(true) }
|
||||||
val scrollState = rememberLazyListState()
|
val scrollState = rememberLazyListState()
|
||||||
|
val uiController = rememberSystemUiController()
|
||||||
|
var moment by remember { mutableStateOf<MomentItem?>(null) }
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
uiController.setNavigationBarColor(Color.White)
|
||||||
|
moment = service.getMomentById(id.toInt())
|
||||||
|
}
|
||||||
StatusBarMaskLayout {
|
StatusBarMaskLayout {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
bottomBar = { BottomNavigationBar() }
|
bottomBar = { BottomNavigationBar() }
|
||||||
) {
|
) {
|
||||||
it
|
it
|
||||||
Column(modifier = Modifier.fillMaxSize()) {
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
) {
|
||||||
Header()
|
Header()
|
||||||
Column(modifier = Modifier.animateContentSize()) {
|
Column(modifier = Modifier.animateContentSize()) {
|
||||||
AnimatedVisibility(visible = showCollapseContent) {
|
AnimatedVisibility(visible = showCollapseContent) {
|
||||||
@@ -99,12 +131,13 @@ fun PostScreen(
|
|||||||
) {
|
) {
|
||||||
PostImageView(
|
PostImageView(
|
||||||
id,
|
id,
|
||||||
makeMockImages(),
|
moment?.images ?: emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
PostDetails(
|
PostDetails(
|
||||||
id,
|
id,
|
||||||
|
moment
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,18 +147,18 @@ fun PostScreen(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|
||||||
) {
|
) {
|
||||||
CommentsSection(scrollState) {
|
CommentsSection(lazyPagingItems = lazyPagingItems, scrollState) {
|
||||||
showCollapseContent = it
|
showCollapseContent = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Header() {
|
fun Header() {
|
||||||
|
val navController = LocalNavController.current
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -136,6 +169,9 @@ fun Header() {
|
|||||||
painter = painterResource(id = R.drawable.rider_pro_nav_back), // Replace with your image resource
|
painter = painterResource(id = R.drawable.rider_pro_nav_back), // Replace with your image resource
|
||||||
contentDescription = "Back",
|
contentDescription = "Back",
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.noRippleClickable {
|
||||||
|
navController.popBackStack()
|
||||||
|
}
|
||||||
.size(32.dp)
|
.size(32.dp)
|
||||||
|
|
||||||
)
|
)
|
||||||
@@ -171,257 +207,97 @@ fun Header() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class PostImage(
|
|
||||||
val imgRes: Int,
|
|
||||||
val description: String
|
|
||||||
)
|
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalSharedTransitionApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun PostImageView(
|
fun PostImageView(
|
||||||
postId: String,
|
postId: String,
|
||||||
images: List<PostImage>,
|
images: List<String>,
|
||||||
) {
|
) {
|
||||||
val sharedTransitionScope = LocalSharedTransitionScope.current
|
|
||||||
val animatedContentScope = LocalAnimatedContentScope.current
|
|
||||||
val pagerState = rememberPagerState(pageCount = { images.size })
|
val pagerState = rememberPagerState(pageCount = { images.size })
|
||||||
with(sharedTransitionScope) {
|
Column {
|
||||||
Column {
|
HorizontalPager(
|
||||||
HorizontalPager(
|
state = pagerState,
|
||||||
state = pagerState,
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
) { page ->
|
||||||
|
val image = images[page]
|
||||||
|
AsyncImage(
|
||||||
|
image,
|
||||||
|
contentDescription = "Image",
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.fillMaxSize()
|
||||||
.fillMaxWidth()
|
)
|
||||||
) { page ->
|
}
|
||||||
Image(
|
|
||||||
painter = painterResource(id = images[page].imgRes),
|
|
||||||
contentDescription = images[page].description,
|
|
||||||
contentScale = ContentScale.Crop,
|
|
||||||
modifier = Modifier.Companion
|
|
||||||
.sharedElement(
|
|
||||||
sharedTransitionScope.rememberSharedContentState(key = "image-$postId"),
|
|
||||||
animatedVisibilityScope = animatedContentScope
|
|
||||||
)
|
|
||||||
.fillMaxSize()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Indicator container
|
// Indicator container
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.Center
|
horizontalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
images.forEachIndexed { index, _ ->
|
images.forEachIndexed { index, _ ->
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(8.dp)
|
.size(8.dp)
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
|
|
||||||
.background(
|
.background(
|
||||||
if (pagerState.currentPage == index) Color.Red else Color.Gray.copy(
|
if (pagerState.currentPage == index) Color.Red else Color.Gray.copy(
|
||||||
alpha = 0.5f
|
alpha = 0.5f
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.padding(4.dp)
|
)
|
||||||
|
.padding(4.dp)
|
||||||
|
|
||||||
|
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalSharedTransitionApi::class)
|
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun PostDetails(
|
fun PostDetails(
|
||||||
postId: String,
|
postId: String,
|
||||||
|
momentItem: MomentItem?
|
||||||
) {
|
) {
|
||||||
val sharedTransitionScope = LocalSharedTransitionScope.current
|
momentItem?.let {
|
||||||
val animatedContentScope = LocalAnimatedContentScope.current
|
Column(modifier = Modifier.padding(16.dp)) {
|
||||||
with(sharedTransitionScope) {
|
|
||||||
Column(modifier = Modifier.padding(16.dp)) {
|
|
||||||
|
|
||||||
Text(text = "By strongarming Ducati into giving him the factory seat.Marquez effectively …", fontSize = 16.sp, fontWeight = FontWeight.Bold, modifier = Modifier.Companion.sharedElement(
|
Text(
|
||||||
sharedTransitionScope.rememberSharedContentState(key = "text-$postId"),
|
text = momentItem.momentTextContent,
|
||||||
animatedVisibilityScope = animatedContentScope
|
fontSize = 16.sp,
|
||||||
))
|
fontWeight = FontWeight.Bold,
|
||||||
Text(text = "12-11 发布")
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
Text(text = "共231条评论")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun MakeMockComments(): List<Comment> {
|
|
||||||
return listOf(
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = listOf(
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
),
|
Text(text = "12-11 发布")
|
||||||
Comment(
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
name = "Diego Morata",
|
Text(text = "共231条评论")
|
||||||
comment = "这里太适合骑行了",
|
}
|
||||||
date = "3 days ago",
|
}
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
),
|
|
||||||
Comment(
|
|
||||||
name = "Diego Morata",
|
|
||||||
comment = "这里太适合骑行了",
|
|
||||||
date = "3 days ago",
|
|
||||||
likes = 200,
|
|
||||||
replies = emptyList()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CommentsSection(
|
fun CommentsSection(
|
||||||
|
lazyPagingItems: LazyPagingItems<Comment>,
|
||||||
scrollState: LazyListState = rememberLazyListState(),
|
scrollState: LazyListState = rememberLazyListState(),
|
||||||
onWillCollapse: (Boolean) -> Unit
|
onWillCollapse: (Boolean) -> Unit
|
||||||
) {
|
) {
|
||||||
val items = MakeMockComments()
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
state = scrollState, modifier = Modifier
|
state = scrollState, modifier = Modifier
|
||||||
.padding(start = 16.dp, end = 16.dp)
|
.padding(start = 16.dp, end = 16.dp)
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
) {
|
) {
|
||||||
items(items) { comment ->
|
items(lazyPagingItems.itemCount) { idx ->
|
||||||
CommentItem(comment)
|
val item = lazyPagingItems[idx] ?: return@items
|
||||||
|
CommentItem(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,14 +313,6 @@ fun CommentsSection(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Comment(
|
|
||||||
val name: String,
|
|
||||||
val comment: String,
|
|
||||||
val date: String,
|
|
||||||
val likes: Int,
|
|
||||||
val replies: List<Comment>
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CommentItem(comment: Comment) {
|
fun CommentItem(comment: Comment) {
|
||||||
@@ -485,6 +353,21 @@ fun CommentItem(comment: Comment) {
|
|||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun BottomNavigationBar() {
|
fun BottomNavigationBar() {
|
||||||
|
val systemUiController = rememberSystemUiController()
|
||||||
|
var showCommentModal by remember { mutableStateOf(false) }
|
||||||
|
if (showCommentModal) {
|
||||||
|
ModalBottomSheet(
|
||||||
|
onDismissRequest = { showCommentModal = false },
|
||||||
|
containerColor = Color.White,
|
||||||
|
sheetState = rememberModalBottomSheetState(
|
||||||
|
skipPartiallyExpanded = true
|
||||||
|
),
|
||||||
|
dragHandle = {},
|
||||||
|
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp),
|
||||||
|
) {
|
||||||
|
EditCommentBottomModal()
|
||||||
|
}
|
||||||
|
}
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.background(Color.White)
|
modifier = Modifier.background(Color.White)
|
||||||
) {
|
) {
|
||||||
@@ -503,6 +386,9 @@ fun BottomNavigationBar() {
|
|||||||
.weight(1f)
|
.weight(1f)
|
||||||
.height(31.dp)
|
.height(31.dp)
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
|
.noRippleClickable {
|
||||||
|
showCommentModal = true
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
|||||||
Reference in New Issue
Block a user