添加更新数据
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
package com.aiosman.riderpro.data
|
||||
|
||||
import com.aiosman.riderpro.test.TestDatabase
|
||||
|
||||
data class AccountProfile(
|
||||
val id: Int,
|
||||
val followerCount: Int,
|
||||
@@ -11,19 +13,16 @@ data class AccountProfile(
|
||||
)
|
||||
|
||||
interface AccountService {
|
||||
suspend fun getAccountProfile(): AccountProfile
|
||||
suspend fun getMyAccountProfile(): AccountProfile
|
||||
suspend fun getAccountProfileById(id: Int): AccountProfile
|
||||
}
|
||||
|
||||
class TestAccountServiceImpl : AccountService {
|
||||
override suspend fun getAccountProfile(): AccountProfile {
|
||||
return AccountProfile(
|
||||
id = 1,
|
||||
followerCount = 100,
|
||||
followingCount = 200,
|
||||
nickName = "Aiosman",
|
||||
avatar = "https://img.freepik.com/free-photo/white-billboard-template_23-2147726635.jpg?t=st=1722150015~exp=1722153615~hmac=5540620196d7898215d822be26353c87a63d51bbfb2b814e032626e1948a1583&w=740",
|
||||
bio = "I am a software engineer",
|
||||
country = "Nigeria"
|
||||
)
|
||||
override suspend fun getMyAccountProfile(): AccountProfile {
|
||||
return TestDatabase.accountData.first { it.id == 0 }
|
||||
}
|
||||
|
||||
override suspend fun getAccountProfileById(id: Int): AccountProfile {
|
||||
return TestDatabase.accountData.first { it.id == id }
|
||||
}
|
||||
}
|
||||
@@ -2,28 +2,43 @@ package com.aiosman.riderpro.data
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import com.aiosman.riderpro.test.TestDatabase
|
||||
import java.io.IOException
|
||||
import java.util.Calendar
|
||||
import kotlin.math.min
|
||||
import kotlin.random.Random
|
||||
|
||||
interface CommentService {
|
||||
suspend fun getComments(pageNumber: Int, postId: Int? = null): ListContainer<Comment>
|
||||
suspend fun createComment(postId: Int, content: String, authorId: Int): Comment
|
||||
suspend fun likeComment(commentId: Int)
|
||||
}
|
||||
|
||||
|
||||
data class Comment(
|
||||
val id: Int,
|
||||
val name: String,
|
||||
val comment: String,
|
||||
val date: String,
|
||||
val likes: Int,
|
||||
val replies: List<Comment>
|
||||
val replies: List<Comment>,
|
||||
val postId: Int = 0,
|
||||
val avatar: String,
|
||||
val author: Int,
|
||||
val liked: Boolean,
|
||||
)
|
||||
|
||||
class CommentPagingSource(
|
||||
private val remoteDataSource: CommentRemoteDataSource,
|
||||
private val postId: Int? = null
|
||||
) : 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
|
||||
pageNumber = currentPage,
|
||||
postId = postId
|
||||
)
|
||||
|
||||
LoadResult.Page(
|
||||
data = comments.list,
|
||||
prevKey = if (currentPage == 1) null else currentPage - 1,
|
||||
@@ -43,52 +58,70 @@ class CommentPagingSource(
|
||||
class CommentRemoteDataSource(
|
||||
private val commentService: CommentService,
|
||||
) {
|
||||
suspend fun getComments(pageNumber: Int): ListContainer<Comment> {
|
||||
return commentService.getComments(pageNumber)
|
||||
suspend fun getComments(pageNumber: Int, postId: Int?): ListContainer<Comment> {
|
||||
return commentService.getComments(pageNumber, postId)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
override suspend fun getComments(pageNumber: Int, postId: Int?): ListContainer<Comment> {
|
||||
var rawList = TestDatabase.comment
|
||||
if (postId != null) {
|
||||
rawList = rawList.filter { it.postId == postId }
|
||||
}
|
||||
val from = (pageNumber - 1) * DataBatchSize
|
||||
val to = (pageNumber) * DataBatchSize
|
||||
rawList = rawList.sortedBy { -it.id }
|
||||
if (from >= rawList.size) {
|
||||
return ListContainer(
|
||||
total = mockData.size,
|
||||
total = rawList.size,
|
||||
page = pageNumber,
|
||||
pageSize = DataBatchSize,
|
||||
list = emptyList()
|
||||
)
|
||||
}
|
||||
rawList = rawList.sortedBy { -it.id }
|
||||
val currentSublist = rawList.subList(from, min(to, rawList.size))
|
||||
return ListContainer(
|
||||
total = rawList.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()
|
||||
override suspend fun createComment(postId: Int, content: String, authorId: Int): Comment {
|
||||
var author = TestDatabase.accountData.find { it.id == authorId }
|
||||
if (author == null) {
|
||||
author = TestDatabase.accountData.random()
|
||||
}
|
||||
TestDatabase.commentIdCounter += 1
|
||||
val newComment = Comment(
|
||||
name = author.nickName,
|
||||
comment = content,
|
||||
date = Calendar.getInstance().time.toString(),
|
||||
likes = 0,
|
||||
replies = emptyList(),
|
||||
postId = postId,
|
||||
avatar = author.avatar,
|
||||
author = author.id,
|
||||
id = TestDatabase.commentIdCounter,
|
||||
liked = false
|
||||
)
|
||||
TestDatabase.comment += newComment
|
||||
return newComment
|
||||
}
|
||||
|
||||
override suspend fun likeComment(commentId: Int) {
|
||||
TestDatabase.comment = TestDatabase.comment.map {
|
||||
if (it.id == commentId) {
|
||||
it.copy(likes = it.likes + 1)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -2,19 +2,34 @@ package com.aiosman.riderpro.data
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.model.MomentItem
|
||||
import com.aiosman.riderpro.test.TestDatabase
|
||||
import java.io.IOException
|
||||
import kotlin.math.min
|
||||
|
||||
interface MomentService {
|
||||
suspend fun getMomentById(id: Int): MomentItem
|
||||
suspend fun likeMoment(id: Int)
|
||||
suspend fun getMoments(
|
||||
pageNumber: Int,
|
||||
author: Int? = null,
|
||||
timelineId: Int? = null
|
||||
): ListContainer<MomentItem>
|
||||
}
|
||||
|
||||
|
||||
class MomentPagingSource(
|
||||
private val remoteDataSource: MomentRemoteDataSource,
|
||||
private val author: Int? = null,
|
||||
private val timelineId: Int? = null
|
||||
) : PagingSource<Int, MomentItem>() {
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MomentItem> {
|
||||
return try {
|
||||
val currentPage = params.key ?: 1
|
||||
val moments = remoteDataSource.getMoments(
|
||||
pageNumber = currentPage
|
||||
pageNumber = currentPage,
|
||||
author = author,
|
||||
timelineId = timelineId
|
||||
)
|
||||
|
||||
LoadResult.Page(
|
||||
@@ -36,63 +51,88 @@ class MomentPagingSource(
|
||||
class MomentRemoteDataSource(
|
||||
private val momentService: MomentService,
|
||||
) {
|
||||
suspend fun getMoments(pageNumber: Int): ListContainer<MomentItem> {
|
||||
return momentService.getMoments(pageNumber)
|
||||
suspend fun getMoments(
|
||||
pageNumber: Int,
|
||||
author: Int?,
|
||||
timelineId: Int?
|
||||
): ListContainer<MomentItem> {
|
||||
return momentService.getMoments(pageNumber, author, timelineId)
|
||||
}
|
||||
}
|
||||
|
||||
interface MomentService {
|
||||
suspend fun getMoments(pageNumber: Int): ListContainer<MomentItem>
|
||||
suspend fun getMomentById(id: Int): MomentItem
|
||||
suspend fun likeMoment(id: Int)
|
||||
}
|
||||
|
||||
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"
|
||||
)
|
||||
var mockData = TestDatabase.momentData
|
||||
val testMomentBackend = TestMomentBackend(mockData)
|
||||
override suspend fun getMoments(pageNumber: Int): ListContainer<MomentItem> {
|
||||
return testMomentBackend.fetchMomentItems(pageNumber)
|
||||
val testMomentBackend = TestMomentBackend()
|
||||
|
||||
override suspend fun getMoments(
|
||||
pageNumber: Int,
|
||||
author: Int?,
|
||||
timelineId: Int?
|
||||
): ListContainer<MomentItem> {
|
||||
return testMomentBackend.fetchMomentItems(pageNumber, author, timelineId)
|
||||
}
|
||||
|
||||
override suspend fun getMomentById(id: Int): MomentItem {
|
||||
return mockData[id]
|
||||
return testMomentBackend.getMomentById(id)
|
||||
}
|
||||
|
||||
|
||||
override suspend fun likeMoment(id: Int) {
|
||||
// mockData = mockData.map {
|
||||
// if (it.id == id) {
|
||||
// it.copy(likeCount = it.likeCount + 1)
|
||||
// } else {
|
||||
// it
|
||||
// }
|
||||
// }
|
||||
// mockData
|
||||
testMomentBackend.likeMoment(id)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestMomentBackend(
|
||||
private val mockData: List<MomentItem>,
|
||||
private val loadDelay: Long = 500,
|
||||
) {
|
||||
val DataBatchSize = 5
|
||||
suspend fun fetchMomentItems(pageNumber: Int): ListContainer<MomentItem> {
|
||||
val from = pageNumber * DataBatchSize
|
||||
val to = (pageNumber + 1) * DataBatchSize
|
||||
val currentSublist = mockData.subList(from, to)
|
||||
suspend fun fetchMomentItems(
|
||||
pageNumber: Int,
|
||||
author: Int? = null,
|
||||
timelineId: Int?
|
||||
): ListContainer<MomentItem> {
|
||||
var rawList = TestDatabase.momentData
|
||||
if (author != null) {
|
||||
rawList = rawList.filter { it.authorId == author }
|
||||
}
|
||||
if (timelineId != null) {
|
||||
val followIdList = TestDatabase.followList.filter {
|
||||
it.first == timelineId
|
||||
}.map { it.second }
|
||||
rawList = rawList.filter { it.authorId in followIdList }
|
||||
}
|
||||
val from = (pageNumber - 1) * DataBatchSize
|
||||
val to = (pageNumber) * DataBatchSize
|
||||
if (from >= rawList.size) {
|
||||
return ListContainer(
|
||||
total = rawList.size,
|
||||
page = pageNumber,
|
||||
pageSize = DataBatchSize,
|
||||
list = emptyList()
|
||||
)
|
||||
}
|
||||
val currentSublist = rawList.subList(from, min(to, rawList.size))
|
||||
// delay
|
||||
kotlinx.coroutines.delay(loadDelay)
|
||||
return ListContainer(
|
||||
total = mockData.size,
|
||||
total = rawList.size,
|
||||
page = pageNumber,
|
||||
pageSize = DataBatchSize,
|
||||
list = currentSublist
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun getMomentById(id: Int): MomentItem {
|
||||
return TestDatabase.momentData[id]
|
||||
}
|
||||
|
||||
suspend fun likeMoment(id: Int) {
|
||||
val oldMoment = TestDatabase.momentData.first {
|
||||
it.id == id
|
||||
}
|
||||
val newMoment = oldMoment.copy(likeCount = oldMoment.likeCount + 1)
|
||||
TestDatabase.updateMomentById(id, newMoment)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,18 +2,24 @@ package com.aiosman.riderpro.test
|
||||
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.data.AccountProfile
|
||||
import com.aiosman.riderpro.data.Comment
|
||||
import com.aiosman.riderpro.model.MomentItem
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import io.github.serpro69.kfaker.faker
|
||||
import java.io.File
|
||||
|
||||
object TestDatabase {
|
||||
var momentData = emptyList<MomentItem>()
|
||||
var accountData = emptyList<AccountProfile>()
|
||||
var comment = emptyList<Comment>()
|
||||
var commentIdCounter = 0
|
||||
var selfId = 1
|
||||
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",
|
||||
"https://t4.ftcdn.net/jpg/02/27/00/89/240_F_227008949_5O7yXuEqTwUgs3BGqdcvrNutM5MSxs1t.jpg",
|
||||
|
||||
"https://t4.ftcdn.net/jpg/01/86/86/49/240_F_186864971_NixcoDg1zBjjN7soUNhpEVraI4vdzOFD.jpg",
|
||||
"https://t3.ftcdn.net/jpg/00/84/01/30/240_F_84013057_fsOdzBgskSFUyWyD6YKjIAdtKdBPiKRD.jpg",
|
||||
"https://t4.ftcdn.net/jpg/00/93/89/23/240_F_93892312_SNyGGruVaWKpJQiVG314gIQmS4EAghdy.jpg",
|
||||
@@ -32,14 +38,14 @@ object TestDatabase {
|
||||
"https://t3.ftcdn.net/jpg/02/65/43/04/240_F_265430460_DIHqnrziar7WL2rmW0qbDO07TbxjlPQo.jpg"
|
||||
)
|
||||
var followList = emptyList<Pair<Int, Int>>()
|
||||
|
||||
var likeCommentList = emptyList<Pair<Int, Int>>()
|
||||
init {
|
||||
val faker = faker {
|
||||
this.fakerConfig {
|
||||
locale = "en"
|
||||
}
|
||||
}
|
||||
accountData = (0..300).toList().mapIndexed { idx, _ ->
|
||||
accountData = (0..100).toList().mapIndexed { idx, _ ->
|
||||
AccountProfile(
|
||||
id = idx,
|
||||
followerCount = 0,
|
||||
@@ -52,7 +58,7 @@ object TestDatabase {
|
||||
)
|
||||
}
|
||||
// make a random follow rel
|
||||
for (i in 0..10000) {
|
||||
for (i in 0..500) {
|
||||
var person1 = accountData.random()
|
||||
var persion2 = accountData.random()
|
||||
followList += Pair(person1.id, persion2.id)
|
||||
@@ -66,11 +72,34 @@ object TestDatabase {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
momentData = (0..300).toList().mapIndexed { idx, _ ->
|
||||
momentData = (0..200).toList().mapIndexed { idx, _ ->
|
||||
val person = accountData.random()
|
||||
// make fake comment
|
||||
for (i in 0..faker.random.nextInt(0, 5)) {
|
||||
commentIdCounter += 1
|
||||
val commentPerson = accountData.random()
|
||||
var newComment = Comment(
|
||||
name = commentPerson.nickName,
|
||||
comment = "this is comment ${commentIdCounter}",
|
||||
date = "2023-02-02 11:23",
|
||||
likes = 0,
|
||||
replies = emptyList(),
|
||||
postId = idx,
|
||||
avatar = commentPerson.avatar,
|
||||
author = commentPerson.id,
|
||||
id = commentIdCounter,
|
||||
liked = false
|
||||
)
|
||||
// generate like comment list
|
||||
for (likeIdx in 0..faker.random.nextInt(0, 5)) {
|
||||
val likePerson = accountData.random()
|
||||
likeCommentList += Pair(commentIdCounter, likePerson.id)
|
||||
newComment = newComment.copy(likes = newComment.likes + 1)
|
||||
}
|
||||
comment += newComment
|
||||
}
|
||||
MomentItem(
|
||||
id = idx,
|
||||
avatar = person.avatar,
|
||||
@@ -89,4 +118,24 @@ object TestDatabase {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun updateMomentById(id: Int, momentItem: MomentItem) {
|
||||
momentData = momentData.map {
|
||||
if (it.id == id) {
|
||||
momentItem
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun saveResultToJsonFile() {
|
||||
val gson: Gson = GsonBuilder().setPrettyPrinting().create()
|
||||
|
||||
// save accountData to json file
|
||||
File("accountData.json").writeText(accountData.toString())
|
||||
// save momentData to json file
|
||||
// save comment to json file
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
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.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -35,30 +36,54 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.cachedIn
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.aiosman.riderpro.ui.post.CommentsSection
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.data.Comment
|
||||
import com.aiosman.riderpro.data.CommentPagingSource
|
||||
import com.aiosman.riderpro.data.CommentRemoteDataSource
|
||||
import com.aiosman.riderpro.data.CommentService
|
||||
import com.aiosman.riderpro.data.TestCommentServiceImpl
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class CommentModalViewModel(
|
||||
postId: Int?
|
||||
):ViewModel(){
|
||||
val commentService:CommentService = TestCommentServiceImpl()
|
||||
val commentsFlow: Flow<PagingData<Comment>> = Pager(
|
||||
config = PagingConfig(pageSize = 20, enablePlaceholders = false),
|
||||
pagingSourceFactory = {
|
||||
CommentPagingSource(
|
||||
CommentRemoteDataSource(commentService),
|
||||
postId
|
||||
)
|
||||
}
|
||||
).flow.cachedIn(viewModelScope)
|
||||
}
|
||||
@Preview
|
||||
@Composable
|
||||
fun CommentModalContent(onDismiss: () -> Unit = {}) {
|
||||
var commentSource = CommentPagingSource(
|
||||
CommentRemoteDataSource(TestCommentServiceImpl())
|
||||
fun CommentModalContent(postId: Int? = null, onDismiss: () -> Unit = {}) {
|
||||
val model = viewModel<CommentModalViewModel>(
|
||||
factory = object : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return CommentModalViewModel(postId) as T
|
||||
}
|
||||
}
|
||||
)
|
||||
val commentsFlow: Flow<PagingData<Comment>> = Pager(
|
||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||
pagingSourceFactory = { commentSource }
|
||||
).flow
|
||||
val comments = commentsFlow.collectAsLazyPagingItems()
|
||||
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
val comments = model.commentsFlow.collectAsLazyPagingItems()
|
||||
val insets = WindowInsets
|
||||
val imePadding = insets.ime.getBottom(density = LocalDensity.current)
|
||||
var bottomPadding by remember { mutableStateOf(0.dp) }
|
||||
@@ -70,7 +95,14 @@ fun CommentModalContent(onDismiss: () -> Unit = {}) {
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
|
||||
var commentText by remember { mutableStateOf("") }
|
||||
suspend fun sendComment() {
|
||||
if (commentText.isNotEmpty()) {
|
||||
model.commentService.createComment(postId!!, commentText, 1)
|
||||
commentText = ""
|
||||
}
|
||||
comments.refresh()
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
) {
|
||||
@@ -97,8 +129,13 @@ fun CommentModalContent(onDismiss: () -> Unit = {}) {
|
||||
.padding(horizontal = 16.dp)
|
||||
.weight(1f)
|
||||
) {
|
||||
|
||||
CommentsSection(lazyPagingItems = comments) {
|
||||
CommentsSection(lazyPagingItems = comments, onLike = {
|
||||
comment: Comment ->
|
||||
scope.launch {
|
||||
model.commentService.likeComment(comment.id)
|
||||
comments.refresh()
|
||||
}
|
||||
}) {
|
||||
|
||||
}
|
||||
|
||||
@@ -127,8 +164,8 @@ fun CommentModalContent(onDismiss: () -> Unit = {}) {
|
||||
|
||||
) {
|
||||
BasicTextField(
|
||||
value = "",
|
||||
onValueChange = { },
|
||||
value = commentText,
|
||||
onValueChange = { text -> commentText = text },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
textStyle = TextStyle(
|
||||
@@ -144,7 +181,11 @@ fun CommentModalContent(onDismiss: () -> Unit = {}) {
|
||||
contentDescription = "Send",
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
|
||||
.noRippleClickable {
|
||||
scope.launch {
|
||||
sendComment()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ fun MomentsList() {
|
||||
MomentCard(momentItem = momentItem, onLikeClick = {
|
||||
scope.launch {
|
||||
model.likeMoment(momentItem.id)
|
||||
moments.refresh()
|
||||
// moments.refresh()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -253,7 +253,7 @@ fun MomentContentGroup(
|
||||
) {
|
||||
val displayImageUrl = momentItem.images.firstOrNull()
|
||||
Text(
|
||||
text = momentItem.momentTextContent,
|
||||
text = "${momentItem.id} ${momentItem.momentTextContent}",
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 22.dp, bottom = 16.dp, start = 24.dp, end = 24.dp),
|
||||
@@ -323,7 +323,7 @@ fun MomentBottomOperateRowGroup(
|
||||
)
|
||||
) {
|
||||
systemUiController.setNavigationBarColor(Color(0xfff7f7f7))
|
||||
CommentModalContent() {
|
||||
CommentModalContent(postId = momentItem.id) {
|
||||
systemUiController.setNavigationBarColor(Color.Black)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +1,57 @@
|
||||
package com.aiosman.riderpro.ui.index.tabs.moment
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import com.aiosman.riderpro.R
|
||||
import androidx.paging.cachedIn
|
||||
import androidx.paging.map
|
||||
import com.aiosman.riderpro.data.AccountService
|
||||
import com.aiosman.riderpro.data.MomentPagingSource
|
||||
import com.aiosman.riderpro.data.MomentRemoteDataSource
|
||||
import com.aiosman.riderpro.data.MomentService
|
||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
||||
import com.aiosman.riderpro.data.TestMomentServiceImpl
|
||||
import com.aiosman.riderpro.model.MomentItem
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
object MomentViewModel : ViewModel() {
|
||||
val momentService: MomentService = TestMomentServiceImpl()
|
||||
var momentListPagingSource = MomentPagingSource(
|
||||
MomentRemoteDataSource(momentService)
|
||||
)
|
||||
var momentsFlow: Flow<PagingData<MomentItem>> = Pager(
|
||||
private val momentService: MomentService = TestMomentServiceImpl()
|
||||
private val _momentsFlow = MutableStateFlow<PagingData<MomentItem>>(PagingData.empty())
|
||||
val momentsFlow = _momentsFlow.asStateFlow()
|
||||
val accountService: AccountService = TestAccountServiceImpl()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val profile = accountService.getMyAccountProfile()
|
||||
Pager(
|
||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||
pagingSourceFactory = {
|
||||
MomentPagingSource(
|
||||
MomentRemoteDataSource(momentService)
|
||||
)
|
||||
pagingSourceFactory = { MomentPagingSource(
|
||||
MomentRemoteDataSource(momentService),
|
||||
timelineId = profile.id
|
||||
) }
|
||||
).flow.cachedIn(viewModelScope).collectLatest {
|
||||
_momentsFlow.value = it
|
||||
}
|
||||
}
|
||||
}
|
||||
).flow
|
||||
|
||||
suspend fun likeMoment(id: Int) {
|
||||
momentService.likeMoment(id)
|
||||
val currentPagingData = _momentsFlow.value
|
||||
val updatedPagingData = currentPagingData.map { momentItem ->
|
||||
if (momentItem.id == id) {
|
||||
momentItem.copy(likeCount = momentItem.likeCount + 1)
|
||||
} else {
|
||||
momentItem
|
||||
}
|
||||
}
|
||||
_momentsFlow.value = updatedPagingData
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -18,18 +18,20 @@ import kotlinx.coroutines.flow.Flow
|
||||
object MyProfileViewModel {
|
||||
val service: AccountService = TestAccountServiceImpl()
|
||||
var profile by mutableStateOf<AccountProfile?>(null)
|
||||
var momentsFlow by mutableStateOf<Flow<PagingData<MomentItem>>?>(null)
|
||||
suspend fun loadProfile() {
|
||||
profile = service.getAccountProfile()
|
||||
}
|
||||
var momentListPagingSource = MomentPagingSource(
|
||||
MomentRemoteDataSource(TestMomentServiceImpl())
|
||||
profile = service.getMyAccountProfile()
|
||||
momentsFlow = Pager(
|
||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||
pagingSourceFactory = {
|
||||
MomentPagingSource(
|
||||
MomentRemoteDataSource(TestMomentServiceImpl()),
|
||||
author = profile?.id ?: 0,
|
||||
|
||||
)
|
||||
|
||||
val momentsFlow: Flow<PagingData<MomentItem>> = Pager(
|
||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||
pagingSourceFactory = { momentListPagingSource }
|
||||
}
|
||||
).flow
|
||||
}
|
||||
val followerCount get() = profile?.followerCount ?: 0
|
||||
val followingCount get() = profile?.followingCount ?: 0
|
||||
val bio get() = profile?.bio ?: ""
|
||||
|
||||
@@ -19,9 +19,7 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -51,7 +49,7 @@ fun ProfilePage() {
|
||||
LaunchedEffect(Unit) {
|
||||
model.loadProfile()
|
||||
}
|
||||
val profile = model.momentsFlow.collectAsLazyPagingItems()
|
||||
val moments = model.momentsFlow?.collectAsLazyPagingItems()
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -67,11 +65,14 @@ fun ProfilePage() {
|
||||
|
||||
RidingStyle()
|
||||
}
|
||||
items(profile.itemCount) { idx ->
|
||||
val momentItem = profile[idx] ?: return@items
|
||||
moments?.let {
|
||||
items(it.itemCount) { idx ->
|
||||
val momentItem = it[idx] ?: return@items
|
||||
MomentPostUnit(momentItem)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -409,7 +410,7 @@ fun MomentPostUnit(momentItem: MomentItem) {
|
||||
TimeGroup(momentItem.time)
|
||||
MomentCard(
|
||||
momentItem.momentTextContent,
|
||||
momentItem.momentPicture,
|
||||
momentItem.images[0],
|
||||
momentItem.likeCount.toString(),
|
||||
momentItem.commentCount.toString()
|
||||
)
|
||||
@@ -439,7 +440,7 @@ fun TimeGroup(time: String = "2024.06.08 12:23") {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentCard(content: String, @DrawableRes picture: Int, like: String, comment: String) {
|
||||
fun MomentCard(content: String, imageUrl: String, like: String, comment: String) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -447,7 +448,7 @@ fun MomentCard(content: String, @DrawableRes picture: Int, like: String, comment
|
||||
.border(width = 1.dp, color = Color(0f, 0f, 0f, 0.1f), shape = RoundedCornerShape(6.dp))
|
||||
) {
|
||||
MomentCardTopContent(content)
|
||||
MomentCardPicture(picture)
|
||||
MomentCardPicture(imageUrl)
|
||||
MomentCardOperation(like, comment)
|
||||
}
|
||||
}
|
||||
@@ -468,13 +469,15 @@ fun MomentCardTopContent(content: String) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentCardPicture(@DrawableRes drawable: Int) {
|
||||
Image(
|
||||
fun MomentCardPicture(imageUrl:String) {
|
||||
AsyncImage(
|
||||
imageUrl,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp), painter = painterResource(id = drawable), contentDescription = ""
|
||||
.padding(16.dp),
|
||||
contentDescription = "",
|
||||
contentScale = ContentScale.FillWidth
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -66,11 +66,14 @@ import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import coil.compose.AsyncImage
|
||||
import com.aiosman.riderpro.LocalNavController
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.data.AccountProfile
|
||||
import com.aiosman.riderpro.data.AccountService
|
||||
import com.aiosman.riderpro.data.Comment
|
||||
import com.aiosman.riderpro.data.CommentPagingSource
|
||||
import com.aiosman.riderpro.data.CommentRemoteDataSource
|
||||
import com.aiosman.riderpro.data.TestCommentServiceImpl
|
||||
import com.aiosman.riderpro.data.MomentService
|
||||
import com.aiosman.riderpro.data.TestAccountServiceImpl
|
||||
import com.aiosman.riderpro.data.TestMomentServiceImpl
|
||||
import com.aiosman.riderpro.model.MomentItem
|
||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||
@@ -101,9 +104,14 @@ fun PostScreen(
|
||||
val scrollState = rememberLazyListState()
|
||||
val uiController = rememberSystemUiController()
|
||||
var moment by remember { mutableStateOf<MomentItem?>(null) }
|
||||
var accountProfile by remember { mutableStateOf<AccountProfile?>(null) }
|
||||
var accountService: AccountService = TestAccountServiceImpl()
|
||||
LaunchedEffect(Unit) {
|
||||
uiController.setNavigationBarColor(Color.White)
|
||||
moment = service.getMomentById(id.toInt())
|
||||
moment?.let {
|
||||
accountProfile = accountService.getAccountProfileById(it.authorId)
|
||||
}
|
||||
}
|
||||
StatusBarMaskLayout {
|
||||
Scaffold(
|
||||
@@ -115,7 +123,7 @@ fun PostScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Header()
|
||||
Header(accountProfile)
|
||||
Column(modifier = Modifier.animateContentSize()) {
|
||||
AnimatedVisibility(visible = showCollapseContent) {
|
||||
// collapse content
|
||||
@@ -148,7 +156,7 @@ fun PostScreen(
|
||||
.fillMaxWidth()
|
||||
|
||||
) {
|
||||
CommentsSection(lazyPagingItems = lazyPagingItems, scrollState) {
|
||||
CommentsSection(lazyPagingItems = lazyPagingItems, scrollState, onLike = {}) {
|
||||
showCollapseContent = it
|
||||
}
|
||||
}
|
||||
@@ -158,7 +166,7 @@ fun PostScreen(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Header() {
|
||||
fun Header(accountProfile: AccountProfile?) {
|
||||
val navController = LocalNavController.current
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@@ -177,15 +185,22 @@ fun Header() {
|
||||
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.default_avatar), // Replace with your image resource
|
||||
accountProfile?.let {
|
||||
AsyncImage(
|
||||
accountProfile.avatar,
|
||||
contentDescription = "Profile Picture",
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clip(CircleShape)
|
||||
.clip(CircleShape),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = "Diego Morata", fontWeight = FontWeight.Bold)
|
||||
accountProfile?.let {
|
||||
Text(text = accountProfile.nickName, fontWeight = FontWeight.Bold)
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(20.dp)
|
||||
@@ -288,14 +303,13 @@ fun PostDetails(
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CommentsSection(
|
||||
lazyPagingItems: LazyPagingItems<Comment>,
|
||||
scrollState: LazyListState = rememberLazyListState(),
|
||||
onLike: (Comment) -> Unit,
|
||||
onWillCollapse: (Boolean) -> Unit
|
||||
) {
|
||||
LazyColumn(
|
||||
@@ -305,7 +319,9 @@ fun CommentsSection(
|
||||
) {
|
||||
items(lazyPagingItems.itemCount) { idx ->
|
||||
val item = lazyPagingItems[idx] ?: return@items
|
||||
CommentItem(item)
|
||||
CommentItem(item,onLike={
|
||||
onLike(item)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,15 +339,16 @@ fun CommentsSection(
|
||||
|
||||
|
||||
@Composable
|
||||
fun CommentItem(comment: Comment) {
|
||||
fun CommentItem(comment: Comment,onLike:()->Unit = {}) {
|
||||
Column {
|
||||
Row(modifier = Modifier.padding(vertical = 8.dp)) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.default_avatar), // Replace with your image resource
|
||||
AsyncImage(
|
||||
comment.avatar,
|
||||
contentDescription = "Comment Profile Picture",
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clip(CircleShape)
|
||||
.clip(CircleShape),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Column {
|
||||
@@ -341,7 +358,9 @@ fun CommentItem(comment: Comment) {
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
IconButton(onClick = { /*TODO*/ }) {
|
||||
IconButton(onClick = {
|
||||
onLike()
|
||||
}) {
|
||||
Icon(Icons.Filled.Favorite, contentDescription = "Like")
|
||||
}
|
||||
Text(text = comment.likes.toString())
|
||||
|
||||
@@ -37,18 +37,22 @@ fun AccountProfile(id:String) {
|
||||
// val model = MyProfileViewModel
|
||||
val userService: UserService = TestUserServiceImpl()
|
||||
var userProfile by remember { mutableStateOf<AccountProfile?>(null) }
|
||||
var momentListPagingSource = MomentPagingSource(
|
||||
MomentRemoteDataSource(TestMomentServiceImpl())
|
||||
)
|
||||
val momentsFlow: Flow<PagingData<MomentItem>> = Pager(
|
||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||
pagingSourceFactory = { momentListPagingSource }
|
||||
).flow
|
||||
val momentService = TestMomentServiceImpl()
|
||||
var momentsFlow by remember { mutableStateOf<Flow<PagingData<MomentItem>>?>(null) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
userProfile = userService.getUserProfile(id)
|
||||
momentsFlow = Pager(
|
||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||
pagingSourceFactory = {
|
||||
MomentPagingSource(
|
||||
MomentRemoteDataSource(momentService),
|
||||
author = id.toInt()
|
||||
)
|
||||
}
|
||||
val items = momentsFlow.collectAsLazyPagingItems()
|
||||
).flow
|
||||
}
|
||||
val items = momentsFlow?.collectAsLazyPagingItems()
|
||||
val systemUiController = rememberSystemUiController()
|
||||
LaunchedEffect(Unit) {
|
||||
systemUiController.setNavigationBarColor(
|
||||
@@ -70,15 +74,16 @@ fun AccountProfile(id:String) {
|
||||
CarGroup()
|
||||
userProfile?.let {
|
||||
UserInformation(isSelf = false, accountProfile = it)
|
||||
|
||||
}
|
||||
RidingStyle()
|
||||
}
|
||||
if (items != null) {
|
||||
items(items.itemCount) { idx ->
|
||||
val momentItem = items[idx] ?: return@items
|
||||
MomentPostUnit(momentItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user