新增动态
This commit is contained in:
@@ -2,6 +2,7 @@ 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
|
||||
@@ -16,6 +17,12 @@ interface MomentService {
|
||||
author: Int? = null,
|
||||
timelineId: Int? = null
|
||||
): ListContainer<MomentItem>
|
||||
|
||||
suspend fun createMoment(
|
||||
content: String,
|
||||
authorId: Int,
|
||||
imageUriList: List<String>
|
||||
): MomentItem
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +93,14 @@ class TestMomentServiceImpl() : MomentService {
|
||||
testMomentBackend.dislikeMoment(id)
|
||||
}
|
||||
|
||||
override suspend fun createMoment(
|
||||
content: String,
|
||||
authorId: Int,
|
||||
imageUriList: List<String>
|
||||
): MomentItem {
|
||||
return testMomentBackend.createMoment(content, authorId, imageUriList)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestMomentBackend(
|
||||
@@ -98,6 +113,7 @@ class TestMomentBackend(
|
||||
timelineId: Int?
|
||||
): ListContainer<MomentItem> {
|
||||
var rawList = TestDatabase.momentData
|
||||
rawList = rawList.sortedBy { it.id }.reversed()
|
||||
if (author != null) {
|
||||
rawList = rawList.filter { it.authorId == author }
|
||||
}
|
||||
@@ -105,7 +121,7 @@ class TestMomentBackend(
|
||||
val followIdList = TestDatabase.followList.filter {
|
||||
it.first == timelineId
|
||||
}.map { it.second }
|
||||
rawList = rawList.filter { it.authorId in followIdList }
|
||||
rawList = rawList.filter { it.authorId in followIdList || it.authorId == 1 }
|
||||
}
|
||||
val from = (pageNumber - 1) * DataBatchSize
|
||||
val to = (pageNumber) * DataBatchSize
|
||||
@@ -119,11 +135,13 @@ class TestMomentBackend(
|
||||
}
|
||||
val currentSublist = rawList.subList(from, min(to, rawList.size))
|
||||
currentSublist.forEach {
|
||||
val myLikeIdList = TestDatabase.likeMomentList.filter { it.second == 1 }.map { it.first }
|
||||
val myLikeIdList =
|
||||
TestDatabase.likeMomentList.filter { it.second == 1 }.map { it.first }
|
||||
if (myLikeIdList.contains(it.id)) {
|
||||
it.liked = true
|
||||
}
|
||||
}
|
||||
|
||||
// delay
|
||||
kotlinx.coroutines.delay(loadDelay)
|
||||
return ListContainer(
|
||||
@@ -153,6 +171,7 @@ class TestMomentBackend(
|
||||
TestDatabase.updateMomentById(id, newMoment)
|
||||
TestDatabase.likeMomentList += Pair(id, 1)
|
||||
}
|
||||
|
||||
suspend fun dislikeMoment(id: Int) {
|
||||
val oldMoment = TestDatabase.momentData.first {
|
||||
it.id == id
|
||||
@@ -164,4 +183,33 @@ class TestMomentBackend(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun createMoment(
|
||||
content: String,
|
||||
authorId: Int,
|
||||
imageUriList: List<String>
|
||||
): MomentItem {
|
||||
TestDatabase.momentIdCounter += 1
|
||||
val person = TestDatabase.accountData.first {
|
||||
it.id == authorId
|
||||
}
|
||||
val newMoment = MomentItem(
|
||||
id = TestDatabase.momentIdCounter,
|
||||
avatar = person.avatar,
|
||||
nickname = person.nickName,
|
||||
location = person.country,
|
||||
time = "2023.02.02 11:23",
|
||||
followStatus = false,
|
||||
momentTextContent = content,
|
||||
momentPicture = R.drawable.default_moment_img,
|
||||
likeCount = 0,
|
||||
commentCount = 0,
|
||||
shareCount = 0,
|
||||
favoriteCount = 0,
|
||||
images = imageUriList,
|
||||
authorId = person.id
|
||||
)
|
||||
TestDatabase.momentData += newMoment
|
||||
return newMoment
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,6 +14,7 @@ object TestDatabase {
|
||||
var accountData = emptyList<AccountProfile>()
|
||||
var comment = emptyList<Comment>()
|
||||
var commentIdCounter = 0
|
||||
var momentIdCounter = 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",
|
||||
@@ -76,6 +77,7 @@ object TestDatabase {
|
||||
}
|
||||
|
||||
momentData = (0..60).toList().mapIndexed { idx, _ ->
|
||||
momentIdCounter += 1
|
||||
val person = accountData.random()
|
||||
// make fake comment
|
||||
val commentCount = faker.random.nextInt(0, 50)
|
||||
@@ -88,7 +90,7 @@ object TestDatabase {
|
||||
date = "2023-02-02 11:23",
|
||||
likes = 0,
|
||||
replies = emptyList(),
|
||||
postId = idx,
|
||||
postId = momentIdCounter,
|
||||
avatar = commentPerson.avatar,
|
||||
author = commentPerson.id,
|
||||
id = commentIdCounter,
|
||||
@@ -105,10 +107,10 @@ object TestDatabase {
|
||||
val likeCount = faker.random.nextInt(0, 5)
|
||||
for (i in 0..likeCount) {
|
||||
val likePerson = accountData.random()
|
||||
likeMomentList += Pair(idx, likePerson.id)
|
||||
likeMomentList += Pair(momentIdCounter, likePerson.id)
|
||||
}
|
||||
MomentItem(
|
||||
id = idx,
|
||||
id = momentIdCounter,
|
||||
avatar = person.avatar,
|
||||
nickname = person.nickName,
|
||||
location = person.country,
|
||||
|
||||
@@ -21,8 +21,12 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Build
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -30,6 +34,7 @@ import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
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
|
||||
@@ -45,6 +50,7 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import coil.compose.AsyncImage
|
||||
import com.aiosman.riderpro.LocalNavController
|
||||
@@ -56,33 +62,53 @@ import com.aiosman.riderpro.ui.composables.AnimatedCounter
|
||||
import com.aiosman.riderpro.ui.composables.AnimatedLikeIcon
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun MomentsList() {
|
||||
|
||||
val model = MomentViewModel
|
||||
var dataFlow = model.momentsFlow
|
||||
var moments = dataFlow.collectAsLazyPagingItems()
|
||||
val scope = rememberCoroutineScope()
|
||||
LazyColumn {
|
||||
items(moments.itemCount) { idx ->
|
||||
val momentItem = moments[idx] ?: return@items
|
||||
MomentCard(momentItem = momentItem,
|
||||
onAddComment = {
|
||||
scope.launch {
|
||||
model.onAddComment(momentItem.id)
|
||||
}
|
||||
},
|
||||
onLikeClick = {
|
||||
scope.launch {
|
||||
if (momentItem.liked) {
|
||||
model.dislikeMoment(momentItem.id)
|
||||
} else {
|
||||
model.likeMoment(momentItem.id)
|
||||
var refreshing by remember { mutableStateOf(false) }
|
||||
moments.loadState
|
||||
val state = rememberPullRefreshState(refreshing, onRefresh = {
|
||||
model.refreshPager()
|
||||
})
|
||||
LaunchedEffect(moments.loadState) {
|
||||
if (moments.loadState.refresh is LoadState.Loading) {
|
||||
refreshing = true
|
||||
} else {
|
||||
refreshing = false
|
||||
}
|
||||
}
|
||||
|
||||
Box(Modifier.pullRefresh(state)) {
|
||||
LazyColumn {
|
||||
items(moments.itemCount) { idx ->
|
||||
val momentItem = moments[idx] ?: return@items
|
||||
MomentCard(momentItem = momentItem,
|
||||
onAddComment = {
|
||||
scope.launch {
|
||||
model.onAddComment(momentItem.id)
|
||||
}
|
||||
},
|
||||
onLikeClick = {
|
||||
scope.launch {
|
||||
if (momentItem.liked) {
|
||||
model.dislikeMoment(momentItem.id)
|
||||
} else {
|
||||
model.likeMoment(momentItem.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
PullRefreshIndicator(refreshing, state, Modifier.align(Alignment.TopCenter))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -366,10 +392,7 @@ fun MomentBottomOperateRowGroup(
|
||||
modifier = Modifier.size(24.dp),
|
||||
liked = momentItem.liked
|
||||
) {
|
||||
|
||||
onLikeClick()
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ object MomentViewModel : ViewModel() {
|
||||
private val _momentsFlow = MutableStateFlow<PagingData<MomentItem>>(PagingData.empty())
|
||||
val momentsFlow = _momentsFlow.asStateFlow()
|
||||
val accountService: AccountService = TestAccountServiceImpl()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val profile = accountService.getMyAccountProfile()
|
||||
@@ -42,6 +41,22 @@ object MomentViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
}
|
||||
fun refreshPager() {
|
||||
viewModelScope.launch {
|
||||
val profile = accountService.getMyAccountProfile()
|
||||
Pager(
|
||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||
pagingSourceFactory = {
|
||||
MomentPagingSource(
|
||||
MomentRemoteDataSource(momentService),
|
||||
timelineId = profile.id
|
||||
)
|
||||
}
|
||||
).flow.cachedIn(viewModelScope).collectLatest {
|
||||
_momentsFlow.value = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateLikeCount(id: Int) {
|
||||
val currentPagingData = _momentsFlow.value
|
||||
|
||||
@@ -38,15 +38,20 @@ import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.PathEffect
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import coil.compose.AsyncImage
|
||||
import com.aiosman.riderpro.LocalNavController
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
@Preview
|
||||
@@ -54,6 +59,7 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
fun NewPostScreen() {
|
||||
val model = NewPostViewModel
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val navController = LocalNavController.current
|
||||
LaunchedEffect(Unit) {
|
||||
systemUiController.setNavigationBarColor(color = Color.Transparent)
|
||||
}
|
||||
@@ -64,7 +70,12 @@ fun NewPostScreen() {
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
NewPostTopBar()
|
||||
NewPostTopBar {
|
||||
model.viewModelScope.launch {
|
||||
model.createMoment()
|
||||
navController.popBackStack()
|
||||
}
|
||||
}
|
||||
NewPostTextField("Share your adventure…", NewPostViewModel.textContent) {
|
||||
NewPostViewModel.textContent = it
|
||||
}
|
||||
@@ -72,11 +83,10 @@ fun NewPostScreen() {
|
||||
AdditionalPostItem()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NewPostTopBar() {
|
||||
fun NewPostTopBar(onSendClick: () -> Unit = {}) {
|
||||
val navController = LocalNavController.current
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -89,18 +99,21 @@ fun NewPostTopBar() {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_close),
|
||||
contentDescription = "Back",
|
||||
modifier = Modifier.size(24.dp).clickable(
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
navController.popBackStack()
|
||||
}
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.noRippleClickable {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_send_post),
|
||||
contentDescription = "Send",
|
||||
modifier = Modifier.size(24.dp)
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.noRippleClickable {
|
||||
onSendClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -134,14 +147,15 @@ fun NewPostTextField(hint: String, value: String, onValueChange: (String) -> Uni
|
||||
@Composable
|
||||
fun AddImageGrid() {
|
||||
val context = LocalContext.current
|
||||
var imageUriList by remember { mutableStateOf(listOf<String>()) }
|
||||
val model = NewPostViewModel
|
||||
|
||||
val pickImageLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
val uri = result.data?.data
|
||||
if (uri != null) {
|
||||
imageUriList = imageUriList + uri.toString()
|
||||
model.imageUriList += uri.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,16 +173,17 @@ fun AddImageGrid() {
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
imageUriList.forEach {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_new_post_add_pic),
|
||||
contentDescription = "Add Image",
|
||||
model.imageUriList.forEach {
|
||||
AsyncImage(
|
||||
it,
|
||||
contentDescription = "Image",
|
||||
modifier = Modifier
|
||||
.size(110.dp)
|
||||
.background(Color(0xFFFFFFFF))
|
||||
.drawBehind {
|
||||
drawRoundRect(color = Color(0xFF999999), style = stroke)
|
||||
}
|
||||
},
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
}
|
||||
Box(
|
||||
|
||||
@@ -2,19 +2,33 @@ package com.aiosman.riderpro.ui.post
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.aiosman.riderpro.data.MomentService
|
||||
import com.aiosman.riderpro.data.TestMomentServiceImpl
|
||||
import com.aiosman.riderpro.ui.modification.Modification
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
object NewPostViewModel : ViewModel() {
|
||||
var momentService: MomentService = TestMomentServiceImpl()
|
||||
var textContent by mutableStateOf("")
|
||||
var searchPlaceAddressResult by mutableStateOf<SearchPlaceAddressResult?>(null)
|
||||
var modificationList by mutableStateOf<List<Modification>>(listOf())
|
||||
|
||||
var imageUriList by mutableStateOf(listOf<String>())
|
||||
fun asNewPost() {
|
||||
textContent = ""
|
||||
searchPlaceAddressResult = null
|
||||
modificationList = listOf()
|
||||
}
|
||||
|
||||
suspend fun createMoment() {
|
||||
momentService.createMoment(
|
||||
content = textContent,
|
||||
authorId = 1,
|
||||
imageUriList = imageUriList
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user