添加业务逻辑

This commit is contained in:
2024-07-29 00:01:09 +08:00
parent cdc0f6e38d
commit d23c5f5c7e
19 changed files with 764 additions and 321 deletions

View File

@@ -80,6 +80,8 @@ dependencies {
implementation(libs.androidx.animation) implementation(libs.androidx.animation)
implementation("io.coil-kt:coil-compose:2.7.0") implementation("io.coil-kt:coil-compose:2.7.0")
implementation("io.coil-kt:coil:2.7.0") implementation("io.coil-kt:coil:2.7.0")
implementation("com.google.android.gms:play-services-auth:21.2.0")
implementation("io.github.serpro69:kotlin-faker:2.0.0-rc.5")
} }

View File

@@ -0,0 +1,29 @@
package com.aiosman.riderpro.data
data class AccountProfile(
val id: Int,
val followerCount: Int,
val followingCount: Int,
val nickName: String,
val avatar: String,
val bio: String,
val country: String,
)
interface AccountService {
suspend fun getAccountProfile(): 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"
)
}
}

View File

@@ -1,8 +1,7 @@
package com.aiosman.riderpro.data.comment package com.aiosman.riderpro.data
import androidx.paging.PagingSource import androidx.paging.PagingSource
import androidx.paging.PagingState import androidx.paging.PagingState
import com.aiosman.riderpro.data.ListContainer
import java.io.IOException import java.io.IOException
import kotlin.random.Random import kotlin.random.Random

View File

@@ -1,17 +1,15 @@
package com.aiosman.riderpro.data.moment package com.aiosman.riderpro.data
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.model.MomentItem import com.aiosman.riderpro.model.MomentItem
import com.aiosman.riderpro.test.TestDatabase
import java.io.IOException import java.io.IOException
import kotlin.random.Random
class MomentPagingSource( class MomentPagingSource(
private val remoteDataSource: MomentRemoteDataSource, private val remoteDataSource: MomentRemoteDataSource,
) : PagingSource<Int, MomentItem>() { ) : PagingSource<Int, MomentItem>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MomentItem> { override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MomentItem> {
return try { return try {
val currentPage = params.key ?: 1 val currentPage = params.key ?: 1
@@ -46,6 +44,7 @@ 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 suspend fun getMomentById(id: Int): MomentItem
suspend fun likeMoment(id: Int)
} }
class TestMomentServiceImpl() : MomentService { class TestMomentServiceImpl() : MomentService {
@@ -54,23 +53,7 @@ class TestMomentServiceImpl() : MomentService {
"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/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://img.freepik.com/free-photo/marketing-strategy-planning-strategy-concept_53876-42950.jpg"
) )
val mockData = (0..300).toList().mapIndexed { idx, _ -> var mockData = TestDatabase.momentData
MomentItem(
id = idx,
avatar = R.drawable.default_avatar,
nickname = "Onyama Limba",
location = "Japan",
time = "2023.02.02 11:23",
followStatus = false,
momentTextContent = "By strongarming Ducati into giving him the factory seat.Marquez effectively …",
momentPicture = R.drawable.default_moment_img,
likeCount = 21,
commentCount = 43,
shareCount = 33,
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)
@@ -79,6 +62,18 @@ class TestMomentServiceImpl() : MomentService {
override suspend fun getMomentById(id: Int): MomentItem { override suspend fun getMomentById(id: Int): MomentItem {
return mockData[id] return mockData[id]
} }
override suspend fun likeMoment(id: Int) {
// mockData = mockData.map {
// if (it.id == id) {
// it.copy(likeCount = it.likeCount + 1)
// } else {
// it
// }
// }
// mockData
}
} }
class TestMomentBackend( class TestMomentBackend(

View File

@@ -0,0 +1,18 @@
package com.aiosman.riderpro.data
import com.aiosman.riderpro.test.TestDatabase
interface UserService {
suspend fun getUserProfile(id:String): AccountProfile
}
class TestUserServiceImpl : UserService {
override suspend fun getUserProfile(id: String): AccountProfile {
TestDatabase.accountData.forEach {
if (it.id == id.toInt()) {
return it
}
}
return AccountProfile(0, 0, 0, "", "", "", "")
}
}

View File

@@ -5,7 +5,7 @@ import com.aiosman.riderpro.R
data class MomentItem( data class MomentItem(
val id: Int, val id: Int,
@DrawableRes val avatar: Int, val avatar: String,
val nickname: String, val nickname: String,
val location: String, val location: String,
val time: String, val time: String,
@@ -16,61 +16,6 @@ data class MomentItem(
val commentCount: Int, val commentCount: Int,
val shareCount: Int, val shareCount: Int,
val favoriteCount: Int, val favoriteCount: Int,
val images: List<String> = emptyList() val images: List<String> = emptyList(),
) val authorId: Int = 0
val momentTestItem = MomentItem(
id = 1,
avatar = R.drawable.default_avatar,
nickname = "Onyama Limba",
location = "Japan",
time = "2023.02.02 11:23",
followStatus = false,
momentTextContent = "By strongarming Ducati into giving him the factory seat.Marquez effectively …",
momentPicture = R.drawable.default_moment_img,
likeCount = 21,
commentCount = 43,
shareCount = 33,
favoriteCount = 211)
val profileMomentItems = listOf(
MomentItem(
id = 1,
avatar = R.drawable.default_avatar,
nickname = "Onyama Limba",
location = "Japan",
time = "2024.06.08 12:23",
followStatus = false,
momentTextContent = "Modifications that are made to make your motorbike more like you",
momentPicture = R.drawable.rider_pro_moment_demo_1,
likeCount = 2345,
commentCount = 12,
shareCount = 33,
favoriteCount = 211),
MomentItem(
id = 2,
avatar = R.drawable.default_avatar,
nickname = "Onyama Limba",
location = "Japan",
time = "2024.03.03 12:31",
followStatus = false,
momentTextContent = "At least 500 units will be made, to meet homologation requirements.",
momentPicture = R.drawable.rider_pro_moment_demo_2,
likeCount = 211,
commentCount = 33,
shareCount = 33,
favoriteCount = 211),
MomentItem(
id = 3,
avatar = R.drawable.default_avatar,
nickname = "Onyama Limba",
location = "Japan",
time = "2024.02.02 11:23",
followStatus = false,
momentTextContent = "The bike is already FIM legal (and soon-to-be MotoAmerica legal as well).",
momentPicture = R.drawable.rider_pro_moment_demo_3,
likeCount = 987,
commentCount = 21,
shareCount = 33,
favoriteCount = 211)
) )

View File

@@ -0,0 +1,92 @@
package com.aiosman.riderpro.test
import com.aiosman.riderpro.R
import com.aiosman.riderpro.data.AccountProfile
import com.aiosman.riderpro.model.MomentItem
import io.github.serpro69.kfaker.faker
object TestDatabase {
var momentData = emptyList<MomentItem>()
var accountData = emptyList<AccountProfile>()
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",
"https://t3.ftcdn.net/jpg/02/94/56/58/240_F_294565895_IOqZC2OpcHGEibWF04MPEP09KZaewEl5.jpg",
"https://t3.ftcdn.net/jpg/01/01/66/84/240_F_101668484_FopHBSMBq4t6BlvwI9awPMzUdi501sJ7.jpg",
"https://t3.ftcdn.net/jpg/05/65/11/60/240_F_565116019_oHbZ6Hc8VYCMcZWpexXF7Z5lOWeNNYtD.jpg",
"https://t3.ftcdn.net/jpg/03/52/21/48/240_F_352214843_dQ3JtTJrKyqrh2yd1emYCDPSrzrwqaNK.jpg",
"https://t3.ftcdn.net/jpg/07/22/47/16/240_F_722471661_T25r329RFRxgK88S6oBJ9dUksOC2arLl.jpg",
"https://t3.ftcdn.net/jpg/02/18/11/26/240_F_218112603_jBChzLJGuz8smPZsdFsy17wB0O0QF3Xo.jpg",
"https://t4.ftcdn.net/jpg/04/11/49/07/240_F_411490703_KRvV0aRyxHWYVUO8bGXxuQGo2mHblYnv.jpg",
"https://img.freepik.com/premium-photo/man-wearing-orange-helmet-white-background_466494-5539.jpg?ga=GA1.1.1334458544.1722150011&semt=sph",
"https://img.freepik.com/premium-photo/motorcycle-vehicle-3d-modelling_274824-502.jpg?ga=GA1.1.1334458544.1722150011&semt=sph",
"https://t3.ftcdn.net/jpg/01/68/26/06/240_F_168260687_UfaDjjs6TxcIB6BdsquSeCmYWEFmN1Sh.jpg",
"https://t3.ftcdn.net/jpg/03/48/50/34/240_F_348503435_On7Tt5Eqn7IP9QWYTQL0H1smubU8gvLv.jpg",
"https://t3.ftcdn.net/jpg/02/76/70/70/240_F_276707060_WpP9bwHWv0Wdqqn0pEgtSuIgXUvgkbs7.jpg",
"https://t3.ftcdn.net/jpg/02/65/43/04/240_F_265430460_DIHqnrziar7WL2rmW0qbDO07TbxjlPQo.jpg"
)
var followList = emptyList<Pair<Int, Int>>()
init {
val faker = faker {
this.fakerConfig {
locale = "en"
}
}
accountData = (0..300).toList().mapIndexed { idx, _ ->
AccountProfile(
id = idx,
followerCount = 0,
followingCount = 0,
nickName = faker.name.name(),
avatar = imageList.random(),
bio = "I am a software engineer",
country = faker.address.country()
)
}
// make a random follow rel
for (i in 0..10000) {
var person1 = accountData.random()
var persion2 = accountData.random()
followList += Pair(person1.id, persion2.id)
// update followerCount and followingCount
accountData = accountData.map {
if (it.id == person1.id) {
it.copy(followingCount = it.followingCount + 1)
} else if (it.id == persion2.id) {
it.copy(followerCount = it.followerCount + 1)
} else {
it
}
}
}
momentData = (0..300).toList().mapIndexed { idx, _ ->
val person = accountData.random()
MomentItem(
id = idx,
avatar = person.avatar,
nickname = person.nickName,
location = person.country,
time = "2023.02.02 11:23",
followStatus = false,
momentTextContent = "By strongarming Ducati into giving him the factory seat.Marquez effectively …",
momentPicture = R.drawable.default_moment_img,
likeCount = faker.random.nextInt(0, 100),
commentCount = faker.random.nextInt(0, 100),
shareCount = faker.random.nextInt(0, 100),
favoriteCount = faker.random.nextInt(0, 100),
images = imageList.shuffled().take(3),
authorId = person.id
)
}
}
}

View File

@@ -29,10 +29,12 @@ import com.aiosman.riderpro.ui.gallery.ProfileTimelineScreen
import com.aiosman.riderpro.ui.index.IndexScreen import com.aiosman.riderpro.ui.index.IndexScreen
import com.aiosman.riderpro.ui.like.LikeScreen import com.aiosman.riderpro.ui.like.LikeScreen
import com.aiosman.riderpro.ui.location.LocationDetailScreen import com.aiosman.riderpro.ui.location.LocationDetailScreen
import com.aiosman.riderpro.ui.login.LoginPage
import com.aiosman.riderpro.ui.message.NotificationsScreen import com.aiosman.riderpro.ui.message.NotificationsScreen
import com.aiosman.riderpro.ui.modification.EditModificationScreen import com.aiosman.riderpro.ui.modification.EditModificationScreen
import com.aiosman.riderpro.ui.post.NewPostScreen import com.aiosman.riderpro.ui.post.NewPostScreen
import com.aiosman.riderpro.ui.post.PostScreen import com.aiosman.riderpro.ui.post.PostScreen
import com.aiosman.riderpro.ui.profile.AccountProfile
sealed class NavigationRoute( sealed class NavigationRoute(
val route: String, val route: String,
@@ -50,6 +52,8 @@ sealed class NavigationRoute(
data object Followers : NavigationRoute("Followers") data object Followers : NavigationRoute("Followers")
data object NewPost : NavigationRoute("NewPost") data object NewPost : NavigationRoute("NewPost")
data object EditModification : NavigationRoute("EditModification") data object EditModification : NavigationRoute("EditModification")
data object Login : NavigationRoute("Login")
data object AccountProfile : NavigationRoute("AccountProfile/{id}")
} }
@@ -61,7 +65,7 @@ fun NavigationController(navController: NavHostController) {
NavHost( NavHost(
navController = navController, navController = navController,
startDestination = NavigationRoute.Index.route, startDestination = NavigationRoute.Login.route,
) { ) {
composable(route = NavigationRoute.Index.route) { composable(route = NavigationRoute.Index.route) {
CompositionLocalProvider( CompositionLocalProvider(
@@ -134,6 +138,16 @@ fun NavigationController(navController: NavHostController) {
EditModificationScreen() EditModificationScreen()
} }
} }
composable(route = NavigationRoute.Login.route) {
LoginPage()
}
composable(
route = NavigationRoute.AccountProfile.route,
arguments = listOf(navArgument("id") { type = NavType.StringType })
) {
AccountProfile(it.arguments?.getString("id")!!)
}
} }

View File

@@ -41,12 +41,10 @@ import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems 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
import com.aiosman.riderpro.data.comment.CommentPagingSource import com.aiosman.riderpro.data.CommentPagingSource
import com.aiosman.riderpro.data.comment.CommentRemoteDataSource import com.aiosman.riderpro.data.CommentRemoteDataSource
import com.aiosman.riderpro.data.comment.TestCommentServiceImpl import com.aiosman.riderpro.data.TestCommentServiceImpl
import com.aiosman.riderpro.model.MomentItem
import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel.momentListPagingSource
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow

View File

@@ -18,6 +18,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import com.aiosman.riderpro.R import com.aiosman.riderpro.R
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
@@ -50,7 +51,7 @@ fun AnimatedLikeIcon(
animationSpec = tween(100) animationSpec = tween(100)
) )
} }
Box(contentAlignment = Alignment.Center, modifier = Modifier.clickable { Box(contentAlignment = Alignment.Center, modifier = Modifier.noRippleClickable {
liked = !liked liked = !liked
onClick?.invoke() onClick?.invoke()
// Trigger shake animation // Trigger shake animation

View File

@@ -35,6 +35,7 @@ fun StatusBarMask(darkIcons: Boolean = true) {
fun StatusBarMaskLayout( fun StatusBarMaskLayout(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
darkIcons: Boolean = true, darkIcons: Boolean = true,
useNavigationBarMask: Boolean = true,
maskBoxBackgroundColor: Color = Color.Transparent, maskBoxBackgroundColor: Color = Color.Transparent,
content: @Composable ColumnScope.() -> Unit content: @Composable ColumnScope.() -> Unit
) { ) {
@@ -57,7 +58,7 @@ fun StatusBarMaskLayout(
} }
content() content()
if (navigationBarPaddings > 24.dp) { if (navigationBarPaddings > 24.dp && useNavigationBarMask) {
Box( Box(
modifier = Modifier modifier = Modifier
.height(navigationBarPaddings).fillMaxWidth().background(Color.White) .height(navigationBarPaddings).fillMaxWidth().background(Color.White)

View File

@@ -33,6 +33,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue 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
@@ -46,51 +47,43 @@ 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 coil.compose.AsyncImage
import com.aiosman.riderpro.LocalAnimatedContentScope
import com.aiosman.riderpro.LocalNavController import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.LocalSharedTransitionScope
import com.aiosman.riderpro.R import com.aiosman.riderpro.R
import com.aiosman.riderpro.model.MomentItem import com.aiosman.riderpro.model.MomentItem
import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.comment.CommentModalContent import com.aiosman.riderpro.ui.comment.CommentModalContent
import com.aiosman.riderpro.ui.composables.AnimatedCounter import com.aiosman.riderpro.ui.composables.AnimatedCounter
import com.aiosman.riderpro.ui.composables.AnimatedLikeIcon import com.aiosman.riderpro.ui.composables.AnimatedLikeIcon
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.launch
private val DATA = (0..60).toList().mapIndexed { idx, _ ->
MomentItem(
id = idx,
avatar = R.drawable.default_avatar,
nickname = "Onyama Limba",
location = "Japan",
time = "2023.02.02 11:23",
followStatus = false,
momentTextContent = "By strongarming Ducati into giving him the factory seat.Marquez effectively …",
momentPicture = R.drawable.default_moment_img,
likeCount = 21,
commentCount = 43,
shareCount = 33,
favoriteCount = 211
)
}
@OptIn(ExperimentalSharedTransitionApi::class) @OptIn(ExperimentalSharedTransitionApi::class)
@Composable @Composable
fun MomentsList() { fun MomentsList() {
val model = MomentViewModel val model = MomentViewModel
val moments = model.momentsFlow.collectAsLazyPagingItems() var dataFlow = model.momentsFlow
var moments = dataFlow.collectAsLazyPagingItems()
val scope = rememberCoroutineScope()
LazyColumn { LazyColumn {
items(moments.itemCount) { idx -> items(moments.itemCount) { idx ->
val momentItem = moments[idx] ?: return@items val momentItem = moments[idx] ?: return@items
MomentCard(momentItem = momentItem) MomentCard(momentItem = momentItem, onLikeClick = {
scope.launch {
model.likeMoment(momentItem.id)
moments.refresh()
}
})
} }
} }
} }
@OptIn(ExperimentalSharedTransitionApi::class)
@Composable @Composable
fun MomentCard( fun MomentCard(
momentItem: MomentItem, momentItem: MomentItem,
onLikeClick: () -> Unit
) { ) {
val navController = LocalNavController.current val navController = LocalNavController.current
Column( Column(
@@ -100,18 +93,17 @@ fun MomentCard(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { .noRippleClickable {
navController.navigate("Post/${momentItem.id}") navController.navigate("Post/${momentItem.id}")
} }
) { ) {
MomentContentGroup(momentItem = momentItem) MomentContentGroup(momentItem = momentItem)
} }
val momentOperateBtnBoxModifier = Modifier val momentOperateBtnBoxModifier = Modifier
.fillMaxHeight() .fillMaxHeight()
.weight(1f) .weight(1f)
ModificationListHeader() ModificationListHeader()
MomentBottomOperateRowGroup(momentOperateBtnBoxModifier, momentItem = momentItem) MomentBottomOperateRowGroup(momentOperateBtnBoxModifier, momentItem = momentItem, onLikeClick = onLikeClick)
} }
} }
@@ -213,14 +205,19 @@ fun MomentPostTime(time: String) {
@Composable @Composable
fun MomentTopRowGroup(momentItem: MomentItem) { fun MomentTopRowGroup(momentItem: MomentItem) {
val navController = LocalNavController.current
Row( Row(
modifier = Modifier modifier = Modifier
.height(40.dp) .height(40.dp)
.padding(top = 0.dp, bottom = 0.dp, start = 24.dp, end = 24.dp) .padding(top = 0.dp, bottom = 0.dp, start = 24.dp, end = 24.dp)
) { ) {
Image( AsyncImage(
painter = painterResource(id = momentItem.avatar), momentItem.avatar,
contentDescription = "" contentDescription = "",
modifier = Modifier.size(40.dp).noRippleClickable {
navController.navigate(NavigationRoute.AccountProfile.route.replace("{id}", momentItem.authorId.toString()))
},
contentScale = ContentScale.Crop
) )
Column( Column(
modifier = Modifier modifier = Modifier
@@ -254,8 +251,6 @@ fun MomentTopRowGroup(momentItem: MomentItem) {
fun MomentContentGroup( fun MomentContentGroup(
momentItem: MomentItem, momentItem: MomentItem,
) { ) {
// val sharedTransitionScope = LocalSharedTransitionScope.current
// val animatedContentScope = LocalAnimatedContentScope.current
val displayImageUrl = momentItem.images.firstOrNull() val displayImageUrl = momentItem.images.firstOrNull()
Text( Text(
text = momentItem.momentTextContent, text = momentItem.momentTextContent,
@@ -312,7 +307,11 @@ fun MomentOperateBtn(count: String, content: @Composable () -> Unit) {
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun MomentBottomOperateRowGroup(modifier: Modifier, momentItem: MomentItem) { fun MomentBottomOperateRowGroup(
modifier: Modifier,
onLikeClick: () -> Unit = {},
momentItem: MomentItem
) {
var systemUiController = rememberSystemUiController() var systemUiController = rememberSystemUiController()
var showCommentModal by remember { mutableStateOf(false) } var showCommentModal by remember { mutableStateOf(false) }
if (showCommentModal) { if (showCommentModal) {
@@ -340,10 +339,7 @@ fun MomentBottomOperateRowGroup(modifier: Modifier, momentItem: MomentItem) {
) { ) {
MomentOperateBtn(count = momentItem.likeCount.toString()) { MomentOperateBtn(count = momentItem.likeCount.toString()) {
AnimatedLikeIcon(modifier = Modifier.size(24.dp)) { AnimatedLikeIcon(modifier = Modifier.size(24.dp)) {
MomentViewModel.updateById( onLikeClick()
momentItem.id,
momentItem.copy(likeCount = momentItem.likeCount + 1)
)
} }
} }
} }
@@ -356,19 +352,19 @@ fun MomentBottomOperateRowGroup(modifier: Modifier, momentItem: MomentItem) {
}, },
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
MomentOperateBtn(icon = R.drawable.rider_pro_moment_comment, count = "43") MomentOperateBtn(icon = R.drawable.rider_pro_moment_comment, count = momentItem.commentCount.toString())
} }
Box( Box(
modifier = modifier, modifier = modifier,
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
MomentOperateBtn(icon = R.drawable.rider_pro_share, count = "33") MomentOperateBtn(icon = R.drawable.rider_pro_share, count = momentItem.shareCount.toString())
} }
Box( Box(
modifier = modifier, modifier = modifier,
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
MomentOperateBtn(icon = R.drawable.rider_pro_favoriate, count = "211") MomentOperateBtn(icon = R.drawable.rider_pro_favoriate, count = momentItem.favoriteCount.toString())
} }
} }
} }

View File

@@ -8,52 +8,32 @@ import androidx.lifecycle.ViewModel
import androidx.paging.Pager import androidx.paging.Pager
import androidx.paging.PagingConfig import androidx.paging.PagingConfig
import androidx.paging.PagingData import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.aiosman.riderpro.R import com.aiosman.riderpro.R
import com.aiosman.riderpro.data.moment.MomentPagingSource import com.aiosman.riderpro.data.MomentPagingSource
import com.aiosman.riderpro.data.moment.MomentRemoteDataSource import com.aiosman.riderpro.data.MomentRemoteDataSource
import com.aiosman.riderpro.data.moment.TestMomentServiceImpl import com.aiosman.riderpro.data.MomentService
import com.aiosman.riderpro.data.TestMomentServiceImpl
import com.aiosman.riderpro.model.MomentItem import com.aiosman.riderpro.model.MomentItem
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
private val DATA = (0..60).toList().mapIndexed { idx, _ ->
MomentItem(
id = idx,
avatar = R.drawable.default_avatar,
nickname = "Onyama Limba",
location = "Japan",
time = "2023.02.02 11:23",
followStatus = false,
momentTextContent = "By strongarming Ducati into giving him the factory seat.Marquez effectively …",
momentPicture = R.drawable.default_moment_img,
likeCount = 21,
commentCount = 43,
shareCount = 33,
favoriteCount = 211
)
}
object MomentViewModel : ViewModel() { object MomentViewModel : ViewModel() {
var momentList by mutableStateOf(DATA) val momentService: MomentService = TestMomentServiceImpl()
fun updateById(id: Int, momentItem: MomentItem) {
momentList = momentList.map {
if (it.id == id) {
momentItem
} else {
it
}
}.toMutableStateList()
}
var momentListPagingSource = MomentPagingSource( var momentListPagingSource = MomentPagingSource(
MomentRemoteDataSource(TestMomentServiceImpl()) MomentRemoteDataSource(momentService)
) )
var momentsFlow: Flow<PagingData<MomentItem>> = Pager(
val momentsFlow: Flow<PagingData<MomentItem>> = Pager(
config = PagingConfig(pageSize = 5, enablePlaceholders = false), config = PagingConfig(pageSize = 5, enablePlaceholders = false),
pagingSourceFactory = { momentListPagingSource } pagingSourceFactory = {
MomentPagingSource(
MomentRemoteDataSource(momentService)
)
}
).flow ).flow
suspend fun likeMoment(id: Int) {
momentService.likeMoment(id)
}
} }

View File

@@ -0,0 +1,39 @@
package com.aiosman.riderpro.ui.index.tabs.profile
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.aiosman.riderpro.data.AccountProfile
import com.aiosman.riderpro.data.AccountService
import com.aiosman.riderpro.data.TestAccountServiceImpl
import com.aiosman.riderpro.data.MomentPagingSource
import com.aiosman.riderpro.data.MomentRemoteDataSource
import com.aiosman.riderpro.data.TestMomentServiceImpl
import com.aiosman.riderpro.model.MomentItem
import kotlinx.coroutines.flow.Flow
object MyProfileViewModel {
val service: AccountService = TestAccountServiceImpl()
var profile by mutableStateOf<AccountProfile?>(null)
suspend fun loadProfile() {
profile = service.getAccountProfile()
}
var momentListPagingSource = MomentPagingSource(
MomentRemoteDataSource(TestMomentServiceImpl())
)
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 ?: ""
val nickName get() = profile?.nickName ?: ""
val avatar get() = profile?.avatar
}

View File

@@ -16,46 +16,66 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size 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.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
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
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 coil.compose.AsyncImage
import com.aiosman.riderpro.LocalNavController import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R import com.aiosman.riderpro.R
import com.aiosman.riderpro.data.AccountProfile
import com.aiosman.riderpro.model.MomentItem import com.aiosman.riderpro.model.MomentItem
import com.aiosman.riderpro.model.profileMomentItems
@Composable @Composable
fun ProfilePage(){ fun ProfilePage() {
Column ( val model = MyProfileViewModel
LaunchedEffect(Unit) {
model.loadProfile()
}
val profile = model.momentsFlow.collectAsLazyPagingItems()
LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(bottom = 16.dp), .padding(bottom = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top, verticalArrangement = Arrangement.Top,
){ ) {
CarGroup() item {
UserInformation() CarGroup()
RidingStyle() model.profile?.let {
UserMoment() UserInformation(accountProfile = it)
}
RidingStyle()
}
items(profile.itemCount) { idx ->
val momentItem = profile[idx] ?: return@items
MomentPostUnit(momentItem)
}
} }
} }
@Composable @Composable
fun CarGroup(){ fun CarGroup() {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -68,144 +88,271 @@ fun CarGroup(){
} }
@Composable @Composable
fun CarTopInformation(){ fun CarTopInformation() {
Row{ Row {
Text(text = "BMW", color = Color.Black, fontSize = 12.sp, style = TextStyle(fontWeight = FontWeight.Bold)) Text(
Text(modifier = Modifier.padding(start = 4.dp), text = "/", color = Color.Gray, fontSize = 12.sp) text = "BMW",
Text(modifier = Modifier.padding(start = 4.dp), text = "M1000RR", color = Color.Gray, fontSize = 12.sp) color = Color.Black,
fontSize = 12.sp,
style = TextStyle(fontWeight = FontWeight.Bold)
)
Text(
modifier = Modifier.padding(start = 4.dp),
text = "/",
color = Color.Gray,
fontSize = 12.sp
)
Text(
modifier = Modifier.padding(start = 4.dp),
text = "M1000RR",
color = Color.Gray,
fontSize = 12.sp
)
} }
} }
@Composable @Composable
fun CarTopPicture(){ fun CarTopPicture() {
Image( Image(
modifier = Modifier modifier = Modifier
.size(width = 336.dp, height = 224.dp) .size(width = 336.dp, height = 224.dp)
.padding(top = 42.dp), .padding(top = 42.dp),
painter = painterResource(id = R.drawable.default_profile_moto), contentDescription = "") painter = painterResource(id = R.drawable.default_profile_moto), contentDescription = ""
)
} }
@Composable @Composable
fun UserInformation(){ fun UserInformation(isSelf: Boolean = true,accountProfile: AccountProfile) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(top = 8.dp, start = 33.dp, end = 33.dp), .padding(top = 8.dp, start = 33.dp, end = 33.dp),
horizontalAlignment = Alignment.CenterHorizontally){ horizontalAlignment = Alignment.CenterHorizontally
Row (modifier = Modifier.fillMaxWidth()){ ) {
Row(modifier = Modifier.fillMaxWidth()) {
val userInfoModifier = Modifier.weight(1f) val userInfoModifier = Modifier.weight(1f)
UserInformationFollowers(userInfoModifier) UserInformationFollowers(userInfoModifier,accountProfile)
UserInformationBasic(userInfoModifier) UserInformationBasic(userInfoModifier,accountProfile)
UserInformationFollowing(userInfoModifier) UserInformationFollowing(userInfoModifier,accountProfile)
} }
UserInformationSlogan() UserInformationSlogan()
CommunicationOperatorGroup() CommunicationOperatorGroup(isSelf = isSelf)
} }
} }
@Composable @Composable
fun UserInformationFollowers(modifier: Modifier){ fun UserInformationFollowers(modifier: Modifier,accountProfile: AccountProfile) {
Column(modifier = modifier.padding(top = 31.dp)) { Column(modifier = modifier.padding(top = 31.dp)) {
Text(modifier = Modifier.padding(bottom = 5.dp),text = "183", fontSize = 24.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold)) Text(
modifier = Modifier.padding(bottom = 5.dp),
text = accountProfile.followerCount.toString(),
fontSize = 24.sp,
color = Color.Black,
style = TextStyle(fontWeight = FontWeight.Bold)
)
Spacer( Spacer(
modifier = Modifier modifier = Modifier
.size(width = 88.83.dp, height = 1.dp) .size(width = 88.83.dp, height = 1.dp)
.border(width = 1.dp, color = Color.Gray) .border(width = 1.dp, color = Color.Gray)
.padding(top = 5.dp, bottom = 5.dp) .padding(top = 5.dp, bottom = 5.dp)
) )
Text(modifier = Modifier.padding(top = 5.dp),text = "FOLLOWERS", fontSize = 12.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold)) Text(
modifier = Modifier.padding(top = 5.dp),
text = "FOLLOWERS",
fontSize = 12.sp,
color = Color.Black,
style = TextStyle(fontWeight = FontWeight.Bold)
)
} }
} }
@Composable @Composable
fun UserInformationBasic(modifier: Modifier){ fun UserInformationBasic(modifier: Modifier,accountProfile: AccountProfile) {
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally) { horizontalAlignment = Alignment.CenterHorizontally
Box(modifier = Modifier.size(width = 112.dp, height = 112.dp), ) {
Box(
modifier = Modifier.size(width = 112.dp, height = 112.dp),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
){ ) {
Image( Image(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
painter = painterResource(id = R.drawable.avatar_bold), contentDescription = "") painter = painterResource(id = R.drawable.avatar_bold), contentDescription = ""
Image( )
AsyncImage(
accountProfile.avatar,
modifier = Modifier modifier = Modifier
.size(width = 88.dp, height = 88.dp) .size(width = 88.dp, height = 88.dp)
.clip( .clip(
RoundedCornerShape(88.dp) RoundedCornerShape(88.dp)
), ),
painter = painterResource(id = R.drawable.default_avatar), contentDescription = "") contentDescription = "",
contentScale = ContentScale.Crop
)
} }
Text(modifier = Modifier.padding(top = 8.dp), text = "Atom", fontSize = 32.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold)) Text(
Text(modifier = Modifier.padding(top = 4.dp), text = "America", fontSize = 12.sp, color = Color.Gray) modifier = Modifier.widthIn(max =220.dp).padding(top = 8.dp),
Text(modifier = Modifier.padding(top = 4.dp), text = "Member since Jun 4.2019", fontSize = 12.sp, color = Color.Gray) text = accountProfile.nickName,
fontSize = 32.sp,
color = Color.Black,
style = TextStyle(fontWeight = FontWeight.Bold),
textAlign = TextAlign.Center
)
Text(
modifier = Modifier.padding(top = 4.dp),
text = accountProfile.country,
fontSize = 12.sp,
color = Color.Gray
)
Text(
modifier = Modifier.padding(top = 4.dp),
text = "Member since Jun 4.2019",
fontSize = 12.sp,
color = Color.Gray
)
} }
} }
@Composable @Composable
fun UserInformationFollowing(modifier: Modifier){ fun UserInformationFollowing(modifier: Modifier,accountProfile: AccountProfile) {
Column(modifier = modifier.padding(top = 6.dp), Column(
horizontalAlignment = Alignment.End) { modifier = modifier.padding(top = 6.dp),
Text(modifier = Modifier.padding(bottom = 5.dp), text = "306", fontSize = 24.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold)) horizontalAlignment = Alignment.End
) {
Text(
modifier = Modifier.padding(bottom = 5.dp),
text = accountProfile.followingCount.toString(),
fontSize = 24.sp,
color = Color.Black,
style = TextStyle(fontWeight = FontWeight.Bold)
)
Box( Box(
modifier = Modifier modifier = Modifier
.size(width = 88.83.dp, height = 1.dp) .size(width = 88.83.dp, height = 1.dp)
.border(width = 1.dp, color = Color.Gray) .border(width = 1.dp, color = Color.Gray)
) )
Text(modifier = Modifier.padding(top = 5.dp),text = "FOLLOWING", fontSize = 12.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold)) Text(
modifier = Modifier.padding(top = 5.dp),
text = "FOLLOWING",
fontSize = 12.sp,
color = Color.Black,
style = TextStyle(fontWeight = FontWeight.Bold)
)
} }
} }
@Composable @Composable
fun UserInformationSlogan(){ fun UserInformationSlogan() {
Text(modifier = Modifier.padding(top = 23.dp),text = "Ridering shooting fishing not much more", fontSize = 13.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold)) val model = MyProfileViewModel
Text(
modifier = Modifier.padding(top = 23.dp),
text = model.bio,
fontSize = 13.sp,
color = Color.Black,
style = TextStyle(fontWeight = FontWeight.Bold)
)
} }
@Composable @Composable
fun CommunicationOperatorGroup(){ fun CommunicationOperatorGroup(isSelf: Boolean = true) {
val navController = LocalNavController.current val navController = LocalNavController.current
Row (modifier = Modifier Row(
.fillMaxWidth() modifier = Modifier
.padding(top = 16.dp), horizontalArrangement = Arrangement.Center){ .fillMaxWidth()
Box(modifier = Modifier.size(width = 142.dp, height = 40.dp), .padding(top = 16.dp), horizontalArrangement = Arrangement.Center
contentAlignment = Alignment.Center){ ) {
Image(modifier = Modifier.fillMaxSize(), painter = painterResource(id = R.drawable.rider_pro_profile_follow), contentDescription = "") Box(
Text(text = "FOLLOW", fontSize = 16.sp, color = Color.White, style = TextStyle(fontWeight = FontWeight.Bold)) modifier = Modifier.size(width = 142.dp, height = 40.dp),
contentAlignment = Alignment.Center
) {
Image(
modifier = Modifier.fillMaxSize(),
painter = painterResource(id = R.drawable.rider_pro_profile_follow),
contentDescription = ""
)
Text(
text = "FOLLOW",
fontSize = 16.sp,
color = Color.White,
style = TextStyle(fontWeight = FontWeight.Bold)
)
} }
Box(modifier = Modifier if (isSelf) {
.size(width = 142.dp, height = 40.dp) Box(
.padding(start = 6.dp).clickable { modifier = Modifier
navController.navigate("MyMessage") .size(width = 142.dp, height = 40.dp)
}, .padding(start = 6.dp)
contentAlignment = Alignment.Center){ .clickable {
Image(modifier = Modifier.fillMaxSize(),painter = painterResource(id = R.drawable.rider_pro_profile_message), contentDescription = "") navController.navigate("MyMessage")
Text(text = "MESSAGE", fontSize = 16.sp, color = Color.White, style = TextStyle(fontWeight = FontWeight.Bold)) },
} contentAlignment = Alignment.Center
Box(modifier = Modifier.size(width = 142.dp, height = 40.dp).clickable { ) {
navController.navigate("ProfileTimeline") Image(
}, modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center){ painter = painterResource(id = R.drawable.rider_pro_profile_message),
Image(modifier = Modifier.fillMaxSize(), painter = painterResource(id = R.drawable.rider_pro_profile_follow), contentDescription = "") contentDescription = ""
Text(text = "GALLERY", fontSize = 16.sp, color = Color.White, style = TextStyle(fontWeight = FontWeight.Bold)) )
Text(
text = "MESSAGE",
fontSize = 16.sp,
color = Color.White,
style = TextStyle(fontWeight = FontWeight.Bold)
)
}
Box(
modifier = Modifier
.size(width = 142.dp, height = 40.dp)
.clickable {
navController.navigate("ProfileTimeline")
},
contentAlignment = Alignment.Center
) {
Image(
modifier = Modifier.fillMaxSize(),
painter = painterResource(id = R.drawable.rider_pro_profile_follow),
contentDescription = ""
)
Text(
text = "GALLERY",
fontSize = 16.sp,
color = Color.White,
style = TextStyle(fontWeight = FontWeight.Bold)
)
}
} }
} }
} }
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class)
@Composable @Composable
fun RidingStyle(){ fun RidingStyle() {
Column(modifier = Modifier Column(
.fillMaxWidth() modifier = Modifier
.padding(start = 24.dp, top = 40.dp, end = 24.dp),
horizontalAlignment = Alignment.Start) {
Text(text = "RIDING STYLES", fontSize = 18.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold))
Image(modifier = Modifier
.padding(top = 4.dp)
.height(8.dp),painter = painterResource(id = R.drawable.rider_pro_profile_line), contentDescription = "")
FlowRow (modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(top = 24.dp)){ .padding(start = 24.dp, top = 40.dp, end = 24.dp),
horizontalAlignment = Alignment.Start
) {
Text(
text = "RIDING STYLES",
fontSize = 18.sp,
color = Color.Black,
style = TextStyle(fontWeight = FontWeight.Bold)
)
Image(
modifier = Modifier
.padding(top = 4.dp)
.height(8.dp),
painter = painterResource(id = R.drawable.rider_pro_profile_line),
contentDescription = ""
)
FlowRow(
modifier = Modifier
.fillMaxWidth()
.padding(top = 24.dp)
) {
RidingStyleItem(styleContent = "Cruiser") RidingStyleItem(styleContent = "Cruiser")
RidingStyleItem(styleContent = "Bobber") RidingStyleItem(styleContent = "Bobber")
RidingStyleItem(styleContent = "Cafe") RidingStyleItem(styleContent = "Cafe")
@@ -219,100 +366,150 @@ fun RidingStyle(){
} }
@Composable @Composable
fun RidingStyleItem(styleContent: String){ fun RidingStyleItem(styleContent: String) {
Box(modifier = Modifier.padding(bottom = 8.dp, end = 8.dp), Box(
contentAlignment = Alignment.Center) { modifier = Modifier.padding(bottom = 8.dp, end = 8.dp),
Image(modifier = Modifier.shadow( contentAlignment = Alignment.Center
ambientColor = Color.Gray, ) {
spotColor = Color(0f,0f,0f,0.2f), Image(
elevation = 20.dp, modifier = Modifier.shadow(
), painter = painterResource(id = R.drawable.rider_pro_style_wrapper), contentDescription = "") ambientColor = Color.Gray,
Text(modifier = Modifier.padding(start = 5.dp, end = 5.dp), text = styleContent, fontSize = 12.sp, color = Color.Gray, style = TextStyle(fontWeight = FontWeight.Bold)) spotColor = Color(0f, 0f, 0f, 0.2f),
elevation = 20.dp,
),
painter = painterResource(id = R.drawable.rider_pro_style_wrapper),
contentDescription = ""
)
Text(
modifier = Modifier.padding(start = 5.dp, end = 5.dp),
text = styleContent,
fontSize = 12.sp,
color = Color.Gray,
style = TextStyle(fontWeight = FontWeight.Bold)
)
} }
} }
@Composable @Composable
fun UserMoment(){ fun UserMoment(scope: LazyListScope) {
profileMomentItems.forEach(
action = { MomentPostUnit(it) }
// LazyColumn(
// modifier = Modifier
// .fillMaxWidth()
// .weight(1f)
// ){
//
//
// }
}
@Composable
fun MomentPostUnit(momentItem: MomentItem) {
TimeGroup(momentItem.time)
MomentCard(
momentItem.momentTextContent,
momentItem.momentPicture,
momentItem.likeCount.toString(),
momentItem.commentCount.toString()
) )
} }
@Composable @Composable
fun MomentPostUnit(momentItem: MomentItem){ fun TimeGroup(time: String = "2024.06.08 12:23") {
TimeGroup(momentItem.time) Row(
MomentCard(momentItem.momentTextContent, modifier = Modifier
momentItem.momentPicture, .fillMaxWidth()
momentItem.likeCount.toString(), .padding(start = 24.dp, top = 40.dp, end = 24.dp),
momentItem.commentCount.toString())
}
@Composable
fun TimeGroup(time: String = "2024.06.08 12:23"){
Row(modifier = Modifier
.fillMaxWidth()
.padding(start = 24.dp, top = 40.dp, end = 24.dp),
horizontalArrangement = Arrangement.Start, horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically){ verticalAlignment = Alignment.CenterVertically
) {
Image( Image(
modifier = Modifier.padding(end = 12.dp), modifier = Modifier.padding(end = 12.dp),
painter = painterResource(id = R.drawable.rider_pro_moment_time_flag), contentDescription = "") painter = painterResource(id = R.drawable.rider_pro_moment_time_flag),
Text(text = time,fontSize = 22.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold)) contentDescription = ""
)
Text(
text = time,
fontSize = 22.sp,
color = Color.Black,
style = TextStyle(fontWeight = FontWeight.Bold)
)
} }
} }
@Composable @Composable
fun MomentCard(content: String,@DrawableRes picture: Int, like: String, comment: String){ fun MomentCard(content: String, @DrawableRes picture: Int, like: String, comment: String) {
Column(modifier = Modifier Column(
.fillMaxWidth() modifier = Modifier
.padding(start = 48.dp, top = 18.dp, end = 24.dp) .fillMaxWidth()
.border(width = 1.dp, color = Color(0f,0f,0f,0.1f), shape = RoundedCornerShape(6.dp)) .padding(start = 48.dp, top = 18.dp, end = 24.dp)
){ .border(width = 1.dp, color = Color(0f, 0f, 0f, 0.1f), shape = RoundedCornerShape(6.dp))
) {
MomentCardTopContent(content) MomentCardTopContent(content)
MomentCardPicture(picture) MomentCardPicture(picture)
MomentCardOperation(like,comment) MomentCardOperation(like, comment)
} }
} }
@Composable @Composable
fun MomentCardTopContent(content: String){ fun MomentCardTopContent(content: String) {
Row(modifier = Modifier Row(
.fillMaxWidth(), modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.Start, horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically){ verticalAlignment = Alignment.CenterVertically
) {
Text( Text(
modifier = Modifier.padding(16.dp), modifier = Modifier.padding(16.dp),
text = content,fontSize = 16.sp, color = Color.Black) text = content, fontSize = 16.sp, color = Color.Black
)
} }
} }
@Composable @Composable
fun MomentCardPicture(@DrawableRes drawable: Int){ fun MomentCardPicture(@DrawableRes drawable: Int) {
Image(modifier = Modifier Image(
.fillMaxSize() modifier = Modifier
.padding(16.dp), painter = painterResource(id = drawable), contentDescription = "") .fillMaxSize()
.padding(16.dp), painter = painterResource(id = drawable), contentDescription = ""
)
} }
@Composable @Composable
fun MomentCardOperation(like: String, comment: String){ fun MomentCardOperation(like: String, comment: String) {
Row(modifier = Modifier Row(
.fillMaxWidth() modifier = Modifier
.height(56.dp), .fillMaxWidth()
.height(56.dp),
horizontalArrangement = Arrangement.Start, horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically){ verticalAlignment = Alignment.CenterVertically
) {
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
MomentCardOperationItem(drawable = R.drawable.rider_pro_like, number = like, modifier = Modifier.weight(1f)) MomentCardOperationItem(
MomentCardOperationItem(drawable = R.drawable.rider_pro_moment_comment, number = comment, modifier = Modifier.weight(1f)) drawable = R.drawable.rider_pro_like,
number = like,
modifier = Modifier.weight(1f)
)
MomentCardOperationItem(
drawable = R.drawable.rider_pro_moment_comment,
number = comment,
modifier = Modifier.weight(1f)
)
} }
} }
@Composable @Composable
fun MomentCardOperationItem(@DrawableRes drawable: Int, number: String, modifier: Modifier){ fun MomentCardOperationItem(@DrawableRes drawable: Int, number: String, modifier: Modifier) {
Row(modifier = modifier, Row(
verticalAlignment = Alignment.CenterVertically){ modifier = modifier,
Image(modifier = Modifier.padding(start = 16.dp, end = 8.dp), verticalAlignment = Alignment.CenterVertically
painter = painterResource(id = drawable), contentDescription = "") ) {
Image(
modifier = Modifier.padding(start = 16.dp, end = 8.dp),
painter = painterResource(id = drawable), contentDescription = ""
)
Text(text = number) Text(text = number)
} }
} }

View File

@@ -0,0 +1,41 @@
package com.aiosman.riderpro.ui.login
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ElevatedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.ui.NavigationRoute
@Composable
fun LoginPage() {
val navController = LocalNavController.current
LaunchedEffect(Unit) {
navController.navigate(NavigationRoute.Index.route)
}
Scaffold { paddingValues ->
Column(
modifier = Modifier.padding(paddingValues).padding(horizontal = 16.dp).fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Login", fontSize = 24.sp)
ElevatedButton(
onClick = { /*TODO*/ },
modifier = Modifier.fillMaxWidth()
) {
Text("Login with Google")
}
}
}
}

View File

@@ -19,10 +19,10 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
@@ -64,18 +64,15 @@ import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.aiosman.riderpro.LocalAnimatedContentScope
import com.aiosman.riderpro.LocalNavController import com.aiosman.riderpro.LocalNavController
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
import com.aiosman.riderpro.data.comment.CommentPagingSource import com.aiosman.riderpro.data.CommentPagingSource
import com.aiosman.riderpro.data.comment.CommentRemoteDataSource import com.aiosman.riderpro.data.CommentRemoteDataSource
import com.aiosman.riderpro.data.comment.TestCommentServiceImpl import com.aiosman.riderpro.data.TestCommentServiceImpl
import com.aiosman.riderpro.data.moment.MomentService import com.aiosman.riderpro.data.MomentService
import com.aiosman.riderpro.data.moment.TestMomentServiceImpl import com.aiosman.riderpro.data.TestMomentServiceImpl
import com.aiosman.riderpro.model.MomentItem 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.composables.EditCommentBottomModal
@@ -122,7 +119,11 @@ fun PostScreen(
Column(modifier = Modifier.animateContentSize()) { Column(modifier = Modifier.animateContentSize()) {
AnimatedVisibility(visible = showCollapseContent) { AnimatedVisibility(visible = showCollapseContent) {
// collapse content // collapse content
Column { Column(
modifier = Modifier
.fillMaxWidth()
) {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -267,19 +268,26 @@ fun PostDetails(
postId: String, postId: String,
momentItem: MomentItem? momentItem: MomentItem?
) { ) {
momentItem?.let {
Column(modifier = Modifier.padding(16.dp)) {
Text( Column(
text = momentItem.momentTextContent, modifier = Modifier
fontSize = 16.sp, .padding(16.dp)
fontWeight = FontWeight.Bold, .fillMaxWidth()
) .wrapContentHeight()
Text(text = "12-11 发布") ) {
Spacer(modifier = Modifier.height(8.dp))
Text(text = "共231条评论") Text(
text = momentItem?.momentTextContent ?:"",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
)
Text(text = "12-11 发布")
Spacer(modifier = Modifier.height(8.dp))
Text(text = "共231条评论")
} }
}
} }

View File

@@ -0,0 +1,84 @@
package com.aiosman.riderpro.ui.profile
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
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.unit.dp
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import com.aiosman.riderpro.data.AccountProfile
import com.aiosman.riderpro.data.MomentPagingSource
import com.aiosman.riderpro.data.MomentRemoteDataSource
import com.aiosman.riderpro.data.TestMomentServiceImpl
import com.aiosman.riderpro.data.TestUserServiceImpl
import com.aiosman.riderpro.data.UserService
import com.aiosman.riderpro.model.MomentItem
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.aiosman.riderpro.ui.index.tabs.profile.CarGroup
import com.aiosman.riderpro.ui.index.tabs.profile.MomentPostUnit
import com.aiosman.riderpro.ui.index.tabs.profile.RidingStyle
import com.aiosman.riderpro.ui.index.tabs.profile.UserInformation
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.flow.Flow
@Composable
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
LaunchedEffect(Unit) {
userProfile = userService.getUserProfile(id)
}
val items = momentsFlow.collectAsLazyPagingItems()
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setNavigationBarColor(
color = androidx.compose.ui.graphics.Color.Transparent
)
}
StatusBarMaskLayout(
modifier = Modifier.fillMaxSize(),
useNavigationBarMask = false
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top,
) {
item {
CarGroup()
userProfile?.let {
UserInformation(isSelf = false, accountProfile = it)
}
RidingStyle()
}
items(items.itemCount) { idx ->
val momentItem = items[idx] ?: return@items
MomentPostUnit(momentItem)
}
}
}
}

View File

@@ -0,0 +1,4 @@
package com.aiosman.riderpro.ui.profile
object AccountProfileViewModel {
}