添加业务逻辑
This commit is contained in:
@@ -80,6 +80,8 @@ dependencies {
|
||||
implementation(libs.androidx.animation)
|
||||
implementation("io.coil-kt:coil-compose: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")
|
||||
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
package com.aiosman.riderpro.data.comment
|
||||
package com.aiosman.riderpro.data
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import com.aiosman.riderpro.data.ListContainer
|
||||
import java.io.IOException
|
||||
import kotlin.random.Random
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
package com.aiosman.riderpro.data.moment
|
||||
package com.aiosman.riderpro.data
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.data.ListContainer
|
||||
import com.aiosman.riderpro.model.MomentItem
|
||||
import com.aiosman.riderpro.test.TestDatabase
|
||||
import java.io.IOException
|
||||
import kotlin.random.Random
|
||||
|
||||
class MomentPagingSource(
|
||||
private val remoteDataSource: MomentRemoteDataSource,
|
||||
) : PagingSource<Int, MomentItem>() {
|
||||
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MomentItem> {
|
||||
return try {
|
||||
val currentPage = params.key ?: 1
|
||||
@@ -46,6 +44,7 @@ class MomentRemoteDataSource(
|
||||
interface MomentService {
|
||||
suspend fun getMoments(pageNumber: Int): ListContainer<MomentItem>
|
||||
suspend fun getMomentById(id: Int): MomentItem
|
||||
suspend fun likeMoment(id: Int)
|
||||
}
|
||||
|
||||
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/marketing-strategy-planning-strategy-concept_53876-42950.jpg"
|
||||
)
|
||||
val mockData = (0..300).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,
|
||||
images = imageList.shuffled().take(3),
|
||||
)
|
||||
}
|
||||
var mockData = TestDatabase.momentData
|
||||
val testMomentBackend = TestMomentBackend(mockData)
|
||||
override suspend fun getMoments(pageNumber: Int): ListContainer<MomentItem> {
|
||||
return testMomentBackend.fetchMomentItems(pageNumber)
|
||||
@@ -79,6 +62,18 @@ class TestMomentServiceImpl() : MomentService {
|
||||
override suspend fun getMomentById(id: Int): MomentItem {
|
||||
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(
|
||||
18
app/src/main/java/com/aiosman/riderpro/data/UserService.kt
Normal file
18
app/src/main/java/com/aiosman/riderpro/data/UserService.kt
Normal 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, "", "", "", "")
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import com.aiosman.riderpro.R
|
||||
|
||||
data class MomentItem(
|
||||
val id: Int,
|
||||
@DrawableRes val avatar: Int,
|
||||
val avatar: String,
|
||||
val nickname: String,
|
||||
val location: String,
|
||||
val time: String,
|
||||
@@ -16,61 +16,6 @@ data class MomentItem(
|
||||
val commentCount: Int,
|
||||
val shareCount: Int,
|
||||
val favoriteCount: Int,
|
||||
val images: List<String> = emptyList()
|
||||
)
|
||||
|
||||
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)
|
||||
val images: List<String> = emptyList(),
|
||||
val authorId: Int = 0
|
||||
)
|
||||
|
||||
92
app/src/main/java/com/aiosman/riderpro/test/TestDatabase.kt
Normal file
92
app/src/main/java/com/aiosman/riderpro/test/TestDatabase.kt
Normal 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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,10 +29,12 @@ import com.aiosman.riderpro.ui.gallery.ProfileTimelineScreen
|
||||
import com.aiosman.riderpro.ui.index.IndexScreen
|
||||
import com.aiosman.riderpro.ui.like.LikeScreen
|
||||
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.modification.EditModificationScreen
|
||||
import com.aiosman.riderpro.ui.post.NewPostScreen
|
||||
import com.aiosman.riderpro.ui.post.PostScreen
|
||||
import com.aiosman.riderpro.ui.profile.AccountProfile
|
||||
|
||||
sealed class NavigationRoute(
|
||||
val route: String,
|
||||
@@ -50,6 +52,8 @@ sealed class NavigationRoute(
|
||||
data object Followers : NavigationRoute("Followers")
|
||||
data object NewPost : NavigationRoute("NewPost")
|
||||
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(
|
||||
navController = navController,
|
||||
startDestination = NavigationRoute.Index.route,
|
||||
startDestination = NavigationRoute.Login.route,
|
||||
) {
|
||||
composable(route = NavigationRoute.Index.route) {
|
||||
CompositionLocalProvider(
|
||||
@@ -134,6 +138,16 @@ fun NavigationController(navController: NavHostController) {
|
||||
EditModificationScreen()
|
||||
}
|
||||
}
|
||||
composable(route = NavigationRoute.Login.route) {
|
||||
LoginPage()
|
||||
|
||||
}
|
||||
composable(
|
||||
route = NavigationRoute.AccountProfile.route,
|
||||
arguments = listOf(navArgument("id") { type = NavType.StringType })
|
||||
) {
|
||||
AccountProfile(it.arguments?.getString("id")!!)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -41,12 +41,10 @@ import androidx.paging.PagingData
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.aiosman.riderpro.ui.post.CommentsSection
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.data.comment.Comment
|
||||
import com.aiosman.riderpro.data.comment.CommentPagingSource
|
||||
import com.aiosman.riderpro.data.comment.CommentRemoteDataSource
|
||||
import com.aiosman.riderpro.data.comment.TestCommentServiceImpl
|
||||
import com.aiosman.riderpro.model.MomentItem
|
||||
import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel.momentListPagingSource
|
||||
import com.aiosman.riderpro.data.Comment
|
||||
import com.aiosman.riderpro.data.CommentPagingSource
|
||||
import com.aiosman.riderpro.data.CommentRemoteDataSource
|
||||
import com.aiosman.riderpro.data.TestCommentServiceImpl
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
@@ -50,7 +51,7 @@ fun AnimatedLikeIcon(
|
||||
animationSpec = tween(100)
|
||||
)
|
||||
}
|
||||
Box(contentAlignment = Alignment.Center, modifier = Modifier.clickable {
|
||||
Box(contentAlignment = Alignment.Center, modifier = Modifier.noRippleClickable {
|
||||
liked = !liked
|
||||
onClick?.invoke()
|
||||
// Trigger shake animation
|
||||
|
||||
@@ -35,6 +35,7 @@ fun StatusBarMask(darkIcons: Boolean = true) {
|
||||
fun StatusBarMaskLayout(
|
||||
modifier: Modifier = Modifier,
|
||||
darkIcons: Boolean = true,
|
||||
useNavigationBarMask: Boolean = true,
|
||||
maskBoxBackgroundColor: Color = Color.Transparent,
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
) {
|
||||
@@ -57,7 +58,7 @@ fun StatusBarMaskLayout(
|
||||
|
||||
}
|
||||
content()
|
||||
if (navigationBarPaddings > 24.dp) {
|
||||
if (navigationBarPaddings > 24.dp && useNavigationBarMask) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(navigationBarPaddings).fillMaxWidth().background(Color.White)
|
||||
|
||||
@@ -33,6 +33,7 @@ import androidx.compose.runtime.Composable
|
||||
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
|
||||
@@ -46,51 +47,43 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import coil.compose.AsyncImage
|
||||
import com.aiosman.riderpro.LocalAnimatedContentScope
|
||||
import com.aiosman.riderpro.LocalNavController
|
||||
import com.aiosman.riderpro.LocalSharedTransitionScope
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.model.MomentItem
|
||||
import com.aiosman.riderpro.ui.NavigationRoute
|
||||
import com.aiosman.riderpro.ui.comment.CommentModalContent
|
||||
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
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||
@Composable
|
||||
fun MomentsList() {
|
||||
val model = MomentViewModel
|
||||
val moments = model.momentsFlow.collectAsLazyPagingItems()
|
||||
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)
|
||||
MomentCard(momentItem = momentItem, onLikeClick = {
|
||||
scope.launch {
|
||||
model.likeMoment(momentItem.id)
|
||||
moments.refresh()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||
@Composable
|
||||
fun MomentCard(
|
||||
momentItem: MomentItem,
|
||||
onLikeClick: () -> Unit
|
||||
) {
|
||||
val navController = LocalNavController.current
|
||||
Column(
|
||||
@@ -100,18 +93,17 @@ fun MomentCard(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
.noRippleClickable {
|
||||
navController.navigate("Post/${momentItem.id}")
|
||||
}
|
||||
) {
|
||||
MomentContentGroup(momentItem = momentItem)
|
||||
}
|
||||
|
||||
val momentOperateBtnBoxModifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.weight(1f)
|
||||
ModificationListHeader()
|
||||
MomentBottomOperateRowGroup(momentOperateBtnBoxModifier, momentItem = momentItem)
|
||||
MomentBottomOperateRowGroup(momentOperateBtnBoxModifier, momentItem = momentItem, onLikeClick = onLikeClick)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,14 +205,19 @@ fun MomentPostTime(time: String) {
|
||||
|
||||
@Composable
|
||||
fun MomentTopRowGroup(momentItem: MomentItem) {
|
||||
val navController = LocalNavController.current
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.height(40.dp)
|
||||
.padding(top = 0.dp, bottom = 0.dp, start = 24.dp, end = 24.dp)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = momentItem.avatar),
|
||||
contentDescription = ""
|
||||
AsyncImage(
|
||||
momentItem.avatar,
|
||||
contentDescription = "",
|
||||
modifier = Modifier.size(40.dp).noRippleClickable {
|
||||
navController.navigate(NavigationRoute.AccountProfile.route.replace("{id}", momentItem.authorId.toString()))
|
||||
},
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -254,8 +251,6 @@ fun MomentTopRowGroup(momentItem: MomentItem) {
|
||||
fun MomentContentGroup(
|
||||
momentItem: MomentItem,
|
||||
) {
|
||||
// val sharedTransitionScope = LocalSharedTransitionScope.current
|
||||
// val animatedContentScope = LocalAnimatedContentScope.current
|
||||
val displayImageUrl = momentItem.images.firstOrNull()
|
||||
Text(
|
||||
text = momentItem.momentTextContent,
|
||||
@@ -312,7 +307,11 @@ fun MomentOperateBtn(count: String, content: @Composable () -> Unit) {
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MomentBottomOperateRowGroup(modifier: Modifier, momentItem: MomentItem) {
|
||||
fun MomentBottomOperateRowGroup(
|
||||
modifier: Modifier,
|
||||
onLikeClick: () -> Unit = {},
|
||||
momentItem: MomentItem
|
||||
) {
|
||||
var systemUiController = rememberSystemUiController()
|
||||
var showCommentModal by remember { mutableStateOf(false) }
|
||||
if (showCommentModal) {
|
||||
@@ -340,10 +339,7 @@ fun MomentBottomOperateRowGroup(modifier: Modifier, momentItem: MomentItem) {
|
||||
) {
|
||||
MomentOperateBtn(count = momentItem.likeCount.toString()) {
|
||||
AnimatedLikeIcon(modifier = Modifier.size(24.dp)) {
|
||||
MomentViewModel.updateById(
|
||||
momentItem.id,
|
||||
momentItem.copy(likeCount = momentItem.likeCount + 1)
|
||||
)
|
||||
onLikeClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -356,19 +352,19 @@ fun MomentBottomOperateRowGroup(modifier: Modifier, momentItem: MomentItem) {
|
||||
},
|
||||
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(
|
||||
modifier = modifier,
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
MomentOperateBtn(icon = R.drawable.rider_pro_share, count = "33")
|
||||
MomentOperateBtn(icon = R.drawable.rider_pro_share, count = momentItem.shareCount.toString())
|
||||
}
|
||||
Box(
|
||||
modifier = modifier,
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
MomentOperateBtn(icon = R.drawable.rider_pro_favoriate, count = "211")
|
||||
MomentOperateBtn(icon = R.drawable.rider_pro_favoriate, count = momentItem.favoriteCount.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,52 +8,32 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.cachedIn
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.data.moment.MomentPagingSource
|
||||
import com.aiosman.riderpro.data.moment.MomentRemoteDataSource
|
||||
import com.aiosman.riderpro.data.moment.TestMomentServiceImpl
|
||||
import com.aiosman.riderpro.data.MomentPagingSource
|
||||
import com.aiosman.riderpro.data.MomentRemoteDataSource
|
||||
import com.aiosman.riderpro.data.MomentService
|
||||
import com.aiosman.riderpro.data.TestMomentServiceImpl
|
||||
import com.aiosman.riderpro.model.MomentItem
|
||||
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() {
|
||||
var momentList by mutableStateOf(DATA)
|
||||
|
||||
fun updateById(id: Int, momentItem: MomentItem) {
|
||||
momentList = momentList.map {
|
||||
if (it.id == id) {
|
||||
momentItem
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}.toMutableStateList()
|
||||
}
|
||||
|
||||
val momentService: MomentService = TestMomentServiceImpl()
|
||||
var momentListPagingSource = MomentPagingSource(
|
||||
MomentRemoteDataSource(TestMomentServiceImpl())
|
||||
|
||||
MomentRemoteDataSource(momentService)
|
||||
)
|
||||
|
||||
val momentsFlow: Flow<PagingData<MomentItem>> = Pager(
|
||||
var momentsFlow: Flow<PagingData<MomentItem>> = Pager(
|
||||
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
|
||||
pagingSourceFactory = { momentListPagingSource }
|
||||
pagingSourceFactory = {
|
||||
MomentPagingSource(
|
||||
MomentRemoteDataSource(momentService)
|
||||
)
|
||||
}
|
||||
).flow
|
||||
|
||||
suspend fun likeMoment(id: Int) {
|
||||
momentService.likeMoment(id)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -16,46 +16,66 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.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
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
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.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.model.MomentItem
|
||||
import com.aiosman.riderpro.model.profileMomentItems
|
||||
|
||||
|
||||
@Composable
|
||||
fun ProfilePage(){
|
||||
Column (
|
||||
fun ProfilePage() {
|
||||
val model = MyProfileViewModel
|
||||
LaunchedEffect(Unit) {
|
||||
model.loadProfile()
|
||||
}
|
||||
val profile = model.momentsFlow.collectAsLazyPagingItems()
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(bottom = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Top,
|
||||
){
|
||||
CarGroup()
|
||||
UserInformation()
|
||||
RidingStyle()
|
||||
UserMoment()
|
||||
) {
|
||||
item {
|
||||
CarGroup()
|
||||
model.profile?.let {
|
||||
UserInformation(accountProfile = it)
|
||||
}
|
||||
|
||||
RidingStyle()
|
||||
}
|
||||
items(profile.itemCount) { idx ->
|
||||
val momentItem = profile[idx] ?: return@items
|
||||
MomentPostUnit(momentItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CarGroup(){
|
||||
fun CarGroup() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -68,144 +88,271 @@ fun CarGroup(){
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CarTopInformation(){
|
||||
Row{
|
||||
Text(text = "BMW", 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)
|
||||
fun CarTopInformation() {
|
||||
Row {
|
||||
Text(
|
||||
text = "BMW",
|
||||
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
|
||||
fun CarTopPicture(){
|
||||
fun CarTopPicture() {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.size(width = 336.dp, height = 224.dp)
|
||||
.padding(top = 42.dp),
|
||||
painter = painterResource(id = R.drawable.default_profile_moto), contentDescription = "")
|
||||
painter = painterResource(id = R.drawable.default_profile_moto), contentDescription = ""
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserInformation(){
|
||||
fun UserInformation(isSelf: Boolean = true,accountProfile: AccountProfile) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 8.dp, start = 33.dp, end = 33.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally){
|
||||
Row (modifier = Modifier.fillMaxWidth()){
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
val userInfoModifier = Modifier.weight(1f)
|
||||
UserInformationFollowers(userInfoModifier)
|
||||
UserInformationBasic(userInfoModifier)
|
||||
UserInformationFollowing(userInfoModifier)
|
||||
UserInformationFollowers(userInfoModifier,accountProfile)
|
||||
UserInformationBasic(userInfoModifier,accountProfile)
|
||||
UserInformationFollowing(userInfoModifier,accountProfile)
|
||||
}
|
||||
UserInformationSlogan()
|
||||
CommunicationOperatorGroup()
|
||||
CommunicationOperatorGroup(isSelf = isSelf)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserInformationFollowers(modifier: Modifier){
|
||||
fun UserInformationFollowers(modifier: Modifier,accountProfile: AccountProfile) {
|
||||
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(
|
||||
modifier = Modifier
|
||||
.size(width = 88.83.dp, height = 1.dp)
|
||||
.border(width = 1.dp, color = Color.Gray)
|
||||
.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
|
||||
fun UserInformationBasic(modifier: Modifier){
|
||||
fun UserInformationBasic(modifier: Modifier,accountProfile: AccountProfile) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Box(modifier = Modifier.size(width = 112.dp, height = 112.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.size(width = 112.dp, height = 112.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
){
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
painter = painterResource(id = R.drawable.avatar_bold), contentDescription = "")
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.avatar_bold), contentDescription = ""
|
||||
)
|
||||
AsyncImage(
|
||||
accountProfile.avatar,
|
||||
modifier = Modifier
|
||||
.size(width = 88.dp, height = 88.dp)
|
||||
.clip(
|
||||
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(modifier = Modifier.padding(top = 4.dp), text = "America", 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)
|
||||
Text(
|
||||
modifier = Modifier.widthIn(max =220.dp).padding(top = 8.dp),
|
||||
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
|
||||
fun UserInformationFollowing(modifier: Modifier){
|
||||
Column(modifier = modifier.padding(top = 6.dp),
|
||||
horizontalAlignment = Alignment.End) {
|
||||
Text(modifier = Modifier.padding(bottom = 5.dp), text = "306", fontSize = 24.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
fun UserInformationFollowing(modifier: Modifier,accountProfile: AccountProfile) {
|
||||
Column(
|
||||
modifier = modifier.padding(top = 6.dp),
|
||||
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(
|
||||
modifier = Modifier
|
||||
.size(width = 88.83.dp, height = 1.dp)
|
||||
.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
|
||||
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))
|
||||
fun UserInformationSlogan() {
|
||||
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
|
||||
fun CommunicationOperatorGroup(){
|
||||
fun CommunicationOperatorGroup(isSelf: Boolean = true) {
|
||||
val navController = LocalNavController.current
|
||||
Row (modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp), horizontalArrangement = Arrangement.Center){
|
||||
Box(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))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp), horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Box(
|
||||
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
|
||||
.size(width = 142.dp, height = 40.dp)
|
||||
.padding(start = 6.dp).clickable {
|
||||
navController.navigate("MyMessage")
|
||||
},
|
||||
contentAlignment = Alignment.Center){
|
||||
Image(modifier = Modifier.fillMaxSize(),painter = painterResource(id = R.drawable.rider_pro_profile_message), contentDescription = "")
|
||||
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))
|
||||
if (isSelf) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(width = 142.dp, height = 40.dp)
|
||||
.padding(start = 6.dp)
|
||||
.clickable {
|
||||
navController.navigate("MyMessage")
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
painter = painterResource(id = R.drawable.rider_pro_profile_message),
|
||||
contentDescription = ""
|
||||
)
|
||||
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)
|
||||
@Composable
|
||||
fun RidingStyle(){
|
||||
Column(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.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
|
||||
fun RidingStyle() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.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 = "Bobber")
|
||||
RidingStyleItem(styleContent = "Cafe")
|
||||
@@ -219,100 +366,150 @@ fun RidingStyle(){
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RidingStyleItem(styleContent: String){
|
||||
Box(modifier = Modifier.padding(bottom = 8.dp, end = 8.dp),
|
||||
contentAlignment = Alignment.Center) {
|
||||
Image(modifier = Modifier.shadow(
|
||||
ambientColor = Color.Gray,
|
||||
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))
|
||||
fun RidingStyleItem(styleContent: String) {
|
||||
Box(
|
||||
modifier = Modifier.padding(bottom = 8.dp, end = 8.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.shadow(
|
||||
ambientColor = Color.Gray,
|
||||
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
|
||||
fun UserMoment(){
|
||||
profileMomentItems.forEach(
|
||||
action = { MomentPostUnit(it) }
|
||||
fun UserMoment(scope: LazyListScope) {
|
||||
|
||||
|
||||
// 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
|
||||
fun MomentPostUnit(momentItem: MomentItem){
|
||||
TimeGroup(momentItem.time)
|
||||
MomentCard(momentItem.momentTextContent,
|
||||
momentItem.momentPicture,
|
||||
momentItem.likeCount.toString(),
|
||||
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),
|
||||
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,
|
||||
verticalAlignment = Alignment.CenterVertically){
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.padding(end = 12.dp),
|
||||
painter = painterResource(id = R.drawable.rider_pro_moment_time_flag), contentDescription = "")
|
||||
Text(text = time,fontSize = 22.sp, color = Color.Black, style = TextStyle(fontWeight = FontWeight.Bold))
|
||||
painter = painterResource(id = R.drawable.rider_pro_moment_time_flag),
|
||||
contentDescription = ""
|
||||
)
|
||||
Text(
|
||||
text = time,
|
||||
fontSize = 22.sp,
|
||||
color = Color.Black,
|
||||
style = TextStyle(fontWeight = FontWeight.Bold)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentCard(content: String,@DrawableRes picture: Int, like: String, comment: String){
|
||||
Column(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.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))
|
||||
){
|
||||
fun MomentCard(content: String, @DrawableRes picture: Int, like: String, comment: String) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.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)
|
||||
MomentCardPicture(picture)
|
||||
MomentCardOperation(like,comment)
|
||||
MomentCardOperation(like, comment)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentCardTopContent(content: String){
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
fun MomentCardTopContent(content: String) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically){
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
text = content,fontSize = 16.sp, color = Color.Black)
|
||||
text = content, fontSize = 16.sp, color = Color.Black
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentCardPicture(@DrawableRes drawable: Int){
|
||||
Image(modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp), painter = painterResource(id = drawable), contentDescription = "")
|
||||
fun MomentCardPicture(@DrawableRes drawable: Int) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp), painter = painterResource(id = drawable), contentDescription = ""
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MomentCardOperation(like: String, comment: String){
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp),
|
||||
fun MomentCardOperation(like: String, comment: String) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically){
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
MomentCardOperationItem(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))
|
||||
MomentCardOperationItem(
|
||||
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
|
||||
fun MomentCardOperationItem(@DrawableRes drawable: Int, number: String, modifier: Modifier){
|
||||
Row(modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically){
|
||||
Image(modifier = Modifier.padding(start = 16.dp, end = 8.dp),
|
||||
painter = painterResource(id = drawable), contentDescription = "")
|
||||
fun MomentCardOperationItem(@DrawableRes drawable: Int, number: String, modifier: Modifier) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.padding(start = 16.dp, end = 8.dp),
|
||||
painter = painterResource(id = drawable), contentDescription = ""
|
||||
)
|
||||
Text(text = number)
|
||||
}
|
||||
}
|
||||
41
app/src/main/java/com/aiosman/riderpro/ui/login/login.kt
Normal file
41
app/src/main/java/com/aiosman/riderpro/ui/login/login.kt
Normal 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,10 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
@@ -64,18 +64,15 @@ import androidx.paging.PagingData
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import coil.compose.AsyncImage
|
||||
import com.aiosman.riderpro.LocalAnimatedContentScope
|
||||
import com.aiosman.riderpro.LocalNavController
|
||||
import com.aiosman.riderpro.LocalSharedTransitionScope
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.data.comment.Comment
|
||||
import com.aiosman.riderpro.data.comment.CommentPagingSource
|
||||
import com.aiosman.riderpro.data.comment.CommentRemoteDataSource
|
||||
import com.aiosman.riderpro.data.comment.TestCommentServiceImpl
|
||||
import com.aiosman.riderpro.data.moment.MomentService
|
||||
import com.aiosman.riderpro.data.moment.TestMomentServiceImpl
|
||||
import com.aiosman.riderpro.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.TestMomentServiceImpl
|
||||
import com.aiosman.riderpro.model.MomentItem
|
||||
import com.aiosman.riderpro.ui.comment.CommentModalContent
|
||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
|
||||
import com.aiosman.riderpro.ui.composables.EditCommentBottomModal
|
||||
@@ -122,7 +119,11 @@ fun PostScreen(
|
||||
Column(modifier = Modifier.animateContentSize()) {
|
||||
AnimatedVisibility(visible = showCollapseContent) {
|
||||
// collapse content
|
||||
Column {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -267,19 +268,26 @@ fun PostDetails(
|
||||
postId: String,
|
||||
momentItem: MomentItem?
|
||||
) {
|
||||
momentItem?.let {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
|
||||
Text(
|
||||
text = momentItem.momentTextContent,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
Text(text = "12-11 发布")
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(text = "共231条评论")
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = momentItem?.momentTextContent ?:"",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
Text(text = "12-11 发布")
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(text = "共231条评论")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.aiosman.riderpro.ui.profile
|
||||
|
||||
object AccountProfileViewModel {
|
||||
}
|
||||
Reference in New Issue
Block a user