更改目录结构

This commit is contained in:
2024-07-23 15:25:00 +08:00
parent 82f58d4b9c
commit dfbc151e4e
47 changed files with 1007 additions and 533 deletions

View File

@@ -1,24 +0,0 @@
package com.aiosman.riderpro
import androidx.lifecycle.ViewModel
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 HomeViewModel : ViewModel() {
val momentList = DATA
}

View File

@@ -1,22 +1,16 @@
package com.aiosman.riderpro package com.aiosman.riderpro
import ModificationListScreen
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.compose.animation.AnimatedContentScope import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.animation.SharedTransitionScope import androidx.compose.animation.SharedTransitionScope
import androidx.compose.animation.animateColorAsState import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -27,27 +21,18 @@ import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.NavigationBarItemColors import androidx.compose.material3.NavigationBarItemColors
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import com.aiosman.riderpro.ui.Navigation
import androidx.navigation.navArgument import com.aiosman.riderpro.ui.index.NavigationItem
import com.aiosman.riderpro.ui.theme.RiderProTheme
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.google.android.libraries.places.api.Places import com.google.android.libraries.places.api.Places
@@ -79,98 +64,6 @@ val LocalAnimatedContentScope = compositionLocalOf<AnimatedContentScope> {
error("AnimatedContentScope not provided") error("AnimatedContentScope not provided")
} }
@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
fun NavigationController(navController: NavHostController) {
val navigationBarHeight = with(LocalDensity.current) {
WindowInsets.navigationBars.getBottom(this).toDp()
}
NavHost(
navController = navController,
startDestination = "Index",
) {
composable(route = "Index") {
CompositionLocalProvider(
LocalAnimatedContentScope provides this,
) {
ScaffoldWithNavigationBar2()
}
}
composable(route = "ProfileTimeline") {
GalleryPage()
}
composable(route = "LocationDetail") {
Box(
modifier = Modifier.padding(bottom = navigationBarHeight)
) {
LocationDetail()
}
}
composable(route = "OfficialPhoto") {
OfficialGalleryPage()
}
composable(route = "OfficialPhotographer") {
OfficialPhotographer()
}
composable(
route = "Post/{id}",
arguments = listOf(navArgument("id") { type = NavType.StringType })
) { backStackEntry ->
CompositionLocalProvider(
LocalAnimatedContentScope provides this,
) {
val id = backStackEntry.arguments?.getString("id")
PostPage(
id!!
)
}
}
composable(route = "ModificationList") {
ModificationListScreen()
}
composable(route = "MyMessage") {
NotificationsScreen()
}
composable(route = "Comments") {
CommentsScreen()
}
composable(route = "Likes") {
LikePage()
}
composable(route = "Followers") {
FollowerPage()
}
composable(route = "NewPost") {
NewPostScreen()
}
composable(route = "EditModification") {
Box(
modifier = Modifier.padding(top = 64.dp)
) {
EditModification()
}
}
}
}
@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
fun Navigation() {
val navController = rememberNavController()
SharedTransitionLayout {
CompositionLocalProvider(
LocalNavController provides navController,
LocalSharedTransitionScope provides this@SharedTransitionLayout,
) {
Box {
NavigationController(navController = navController)
}
}
}
}
// 用于带导航栏的路由的可复用 composable // 用于带导航栏的路由的可复用 composable
@Composable @Composable
@@ -266,204 +159,3 @@ fun ScaffoldWithNavigationBar(
} }
} }
@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
fun ScaffoldWithNavigationBar2() {
val model = IndexViewModel
val navigationBarHeight = with(LocalDensity.current) {
WindowInsets.navigationBars.getBottom(this).toDp()
}
val item = listOf(
NavigationItem.Home,
NavigationItem.Street,
NavigationItem.Add,
NavigationItem.Message,
NavigationItem.Profile
)
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setNavigationBarColor(Color.Black)
}
Scaffold(
bottomBar = {
NavigationBar(
modifier = Modifier.height(56.dp + navigationBarHeight),
containerColor = Color.Black
) {
item.forEachIndexed { idx, it ->
val isSelected = model.tabIndex == idx
val iconTint by animateColorAsState(
targetValue = if (isSelected) Color.Red else Color.White,
animationSpec = tween(durationMillis = 250), label = ""
)
NavigationBarItem(
selected = isSelected,
onClick = {
model.tabIndex = idx
// if (it.route == NavigationItem.Add.route || it.route == NavigationItem.Message.route) {
// systemUiController.setStatusBarColor(Color.Black, darkIcons = false)
// } else {
// systemUiController.setStatusBarColor(
// Color.Transparent,
// darkIcons = true
// )
// }
},
colors = NavigationBarItemColors(
selectedTextColor = Color.Red,
selectedIndicatorColor = Color.Black,
unselectedTextColor = Color.Red,
disabledIconColor = Color.Red,
disabledTextColor = Color.Red,
selectedIconColor = iconTint,
unselectedIconColor = iconTint,
),
icon = {
Icon(
modifier = Modifier.size(24.dp),
imageVector = if (isSelected) it.selectedIcon() else it.icon(),
contentDescription = null,
tint = iconTint
)
}
)
}
}
}
) { innerPadding ->
Box(
modifier = Modifier
) {
when (model.tabIndex) {
0 -> Box(
modifier = Modifier.padding(innerPadding)
) {
Home()
}
1 -> Street()
2 -> Box(
modifier = Modifier.padding(innerPadding)
) { Add() }
3 -> Box(
modifier = Modifier.padding(innerPadding)
) { Video() }
4 -> Box(
modifier = Modifier.padding(innerPadding)
) { Profile() }
}
}
}
}
@Composable
fun Home() {
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Transparent, darkIcons = true)
}
Column(
modifier = Modifier
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
PagingBackendSample()
}
}
@Composable
fun Street() {
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Transparent, darkIcons = true)
}
Column(
modifier = Modifier
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
StreetPage()
}
}
@Composable
fun Add() {
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Black, darkIcons = false)
}
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.Black),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
AddPage()
}
}
@Composable
fun Video() {
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Black, darkIcons = false)
}
Column(
modifier = Modifier
.fillMaxSize(),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally,
) {
ShortVideo()
}
}
@Composable
fun Message() {
val navigationBarHeight = with(LocalDensity.current) {
WindowInsets.navigationBars.getBottom(this).toDp()
}
Column(
modifier = Modifier
.fillMaxSize(),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally,
) {
MessagePage()
}
}
@Composable
fun Profile() {
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Transparent, darkIcons = true)
}
Column(
modifier = Modifier
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
ProfilePage()
}
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
RiderProTheme {
Surface(modifier = Modifier.fillMaxSize(), color = Color.White) {
Navigation()
}
}
}

View File

@@ -0,0 +1,9 @@
package com.aiosman.riderpro.data
data class ListContainer<T>(
val total: Int,
val page: Int,
val pageSize: Int,
val list: List<T>
)

View File

@@ -0,0 +1,92 @@
package com.aiosman.riderpro.data.moment
import android.net.http.HttpException
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 java.io.IOException
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
val moments = remoteDataSource.getMoments(
pageNumber = currentPage
)
LoadResult.Page(
data = moments.list,
prevKey = if (currentPage == 1) null else currentPage - 1,
nextKey = if (moments.list.isEmpty()) null else moments.page + 1
)
} catch (exception: IOException) {
return LoadResult.Error(exception)
}
}
override fun getRefreshKey(state: PagingState<Int, MomentItem>): Int? {
return state.anchorPosition
}
}
class MomentRemoteDataSource(
private val momentService: MomentService,
) {
suspend fun getMoments(pageNumber: Int): ListContainer<MomentItem> {
return momentService.getMoments(pageNumber)
}
}
interface MomentService {
suspend fun getMoments(pageNumber: Int): ListContainer<MomentItem>
}
class TestMomentServiceImpl() : MomentService {
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
)
}
val testMomentBackend = TestMomentBackend(mockData)
override suspend fun getMoments(pageNumber: Int): ListContainer<MomentItem> {
return testMomentBackend.fetchMomentItems(pageNumber)
}
}
class TestMomentBackend(
private val mockData: List<MomentItem>,
private val loadDelay: Long = 500,
) {
val DataBatchSize = 5
suspend fun fetchMomentItems(pageNumber: Int): ListContainer<MomentItem> {
val from = pageNumber * DataBatchSize
val to = (pageNumber + 1) * DataBatchSize
val currentSublist = mockData.subList(from, to)
// delay
kotlinx.coroutines.delay(loadDelay)
return ListContainer(
total = mockData.size,
page = pageNumber,
pageSize = DataBatchSize,
list = currentSublist
)
}
}

View File

@@ -1,21 +1,16 @@
package com.aiosman.riderpro package com.aiosman.riderpro.exp
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Build import android.os.Build
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import android.widget.RelativeLayout
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
//import androidx.appcompat.app.AppCompatActivity //import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
private const val COLOR_TRANSPARENT = 0 private const val COLOR_TRANSPARENT = 0

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.model
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.model
import androidx.paging.PagingSource import androidx.paging.PagingSource
import androidx.paging.PagingState import androidx.paging.PagingState

View File

@@ -1,6 +1,7 @@
package com.aiosman.riderpro package com.aiosman.riderpro.model
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import com.aiosman.riderpro.R
data class MomentItem( data class MomentItem(
val id: Int, val id: Int,

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.test
import kotlin.math.min import kotlin.math.min

View File

@@ -1,7 +1,8 @@
package com.aiosman.riderpro package com.aiosman.riderpro.test
import androidx.paging.PagingSource import androidx.paging.PagingSource
import androidx.paging.PagingState import androidx.paging.PagingState
import com.aiosman.riderpro.model.MomentItem
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlin.math.ceil import kotlin.math.ceil

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.test
data class StreetPosition( data class StreetPosition(
val name:String, val name:String,

View File

@@ -0,0 +1,156 @@
package com.aiosman.riderpro.ui
import ModificationListScreen
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.aiosman.riderpro.LocalAnimatedContentScope
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.LocalSharedTransitionScope
import com.aiosman.riderpro.ui.comment.CommentsScreen
import com.aiosman.riderpro.ui.follower.FollowerScreen
import com.aiosman.riderpro.ui.gallery.OfficialGalleryScreen
import com.aiosman.riderpro.ui.gallery.OfficialPhotographerScreen
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.message.NotificationsScreen
import com.aiosman.riderpro.ui.modification.EditModificationScreen
import com.aiosman.riderpro.ui.post.NewPostScreen
import com.aiosman.riderpro.ui.post.PostScreen
sealed class NavigationRoute(
val route: String,
) {
data object Index : NavigationRoute("Index")
data object ProfileTimeline : NavigationRoute("ProfileTimeline")
data object LocationDetail : NavigationRoute("LocationDetail/{x}/{y}")
data object OfficialPhoto : NavigationRoute("OfficialPhoto")
data object OfficialPhotographer : NavigationRoute("OfficialPhotographer")
data object Post : NavigationRoute("Post/{id}")
data object ModificationList : NavigationRoute("ModificationList")
data object MyMessage : NavigationRoute("MyMessage")
data object Comments : NavigationRoute("Comments")
data object Likes : NavigationRoute("Likes")
data object Followers : NavigationRoute("Followers")
data object NewPost : NavigationRoute("NewPost")
data object EditModification : NavigationRoute("EditModification")
}
@Composable
fun NavigationController(navController: NavHostController) {
val navigationBarHeight = with(LocalDensity.current) {
WindowInsets.navigationBars.getBottom(this).toDp()
}
NavHost(
navController = navController,
startDestination = NavigationRoute.Index.route,
) {
composable(route = NavigationRoute.Index.route) {
CompositionLocalProvider(
LocalAnimatedContentScope provides this,
) {
IndexScreen()
}
}
composable(route = NavigationRoute.ProfileTimeline.route) {
ProfileTimelineScreen()
}
composable(
route = NavigationRoute.LocationDetail.route,
arguments = listOf(
navArgument("x") { type = NavType.FloatType },
navArgument("y") { type = NavType.FloatType }
)
) {
Box(
modifier = Modifier.padding(bottom = navigationBarHeight)
) {
val x = it.arguments?.getFloat("x") ?: 0f
val y = it.arguments?.getFloat("y") ?: 0f
LocationDetailScreen(
x, y
)
}
}
composable(route = NavigationRoute.OfficialPhoto.route) {
OfficialGalleryScreen()
}
composable(route = NavigationRoute.OfficialPhotographer.route) {
OfficialPhotographerScreen()
}
composable(
route = NavigationRoute.Post.route,
arguments = listOf(navArgument("id") { type = NavType.StringType })
) { backStackEntry ->
CompositionLocalProvider(
LocalAnimatedContentScope provides this,
) {
val id = backStackEntry.arguments?.getString("id")
PostScreen(
id!!
)
}
}
composable(route = NavigationRoute.ModificationList.route) {
ModificationListScreen()
}
composable(route = NavigationRoute.MyMessage.route) {
NotificationsScreen()
}
composable(route = NavigationRoute.Comments.route) {
CommentsScreen()
}
composable(route = NavigationRoute.Likes.route) {
LikeScreen()
}
composable(route = NavigationRoute.Followers.route) {
FollowerScreen()
}
composable(route = NavigationRoute.NewPost.route) {
NewPostScreen()
}
composable(route = NavigationRoute.EditModification.route) {
Box(
modifier = Modifier.padding(top = 64.dp)
) {
EditModificationScreen()
}
}
}
}
@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
fun Navigation() {
val navController = rememberNavController()
SharedTransitionLayout {
CompositionLocalProvider(
LocalNavController provides navController,
LocalSharedTransitionScope provides this@SharedTransitionLayout,
) {
Box {
NavigationController(navController = navController)
}
}
}
}

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.comment
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
@@ -35,6 +35,8 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.ui.post.CommentsSection
import com.aiosman.riderpro.R
@Preview @Preview
@@ -53,7 +55,7 @@ fun CommentModalContent(onDismiss: () -> Unit = {}) {
} }
Column( Column(
modifier = Modifier.height(500.dp) modifier = Modifier
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.comment
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
@@ -25,6 +25,10 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
@Preview @Preview
@Composable @Composable

View File

@@ -0,0 +1,41 @@
package com.aiosman.riderpro.ui.composables
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.animation.with
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.sp
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun AnimatedCounter(count: Int, modifier: Modifier = Modifier, fontSize: Int = 24) {
AnimatedContent(
targetState = count,
transitionSpec = {
// Compare the incoming number with the previous number.
if (targetState > initialState) {
// If the target number is larger, it slides up and fades in
// while the initial (smaller) number slides up and fades out.
(slideInVertically { height -> height } + fadeIn()).togetherWith(slideOutVertically { height -> -height } + fadeOut())
} else {
// If the target number is smaller, it slides down and fades in
// while the initial number slides down and fades out.
(slideInVertically { height -> -height } + fadeIn()).togetherWith(slideOutVertically { height -> height } + fadeOut())
}.using(
// Disable clipping since the faded slide-in/out should
// be displayed out of bounds.
SizeTransform(clip = false)
)
}
) { targetCount ->
Text(text = "$targetCount", modifier = modifier, fontSize = fontSize.sp)
}
}

View File

@@ -0,0 +1,71 @@
package com.aiosman.riderpro.ui.composables
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
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
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.painterResource
import com.aiosman.riderpro.R
import kotlinx.coroutines.launch
@Composable
fun AnimatedLikeIcon(
modifier: Modifier = Modifier,
onClick: (() -> Unit)? = null
) {
var liked by remember { mutableStateOf(false) }
val animatableRotation = remember { Animatable(0f) }
val animatedColor by animateColorAsState(targetValue = if (liked) Color(0xFFd83737) else Color.Black)
val scope = rememberCoroutineScope()
suspend fun shake() {
repeat(2) {
animatableRotation.animateTo(
targetValue = 10f,
animationSpec = tween(100)
) {
}
animatableRotation.animateTo(
targetValue = -10f,
animationSpec = tween(100)
) {
}
}
animatableRotation.animateTo(
targetValue = 0f,
animationSpec = tween(100)
)
}
Box(contentAlignment = Alignment.Center, modifier = Modifier.clickable {
liked = !liked
onClick?.invoke()
// Trigger shake animation
scope.launch {
shake()
}
}) {
Image(
painter = painterResource(id = R.drawable.rider_pro_like),
contentDescription = "Like",
modifier = modifier.graphicsLayer {
rotationZ = animatableRotation.value
},
colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(animatedColor)
)
}
}

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.composables
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box

View File

@@ -1,13 +1,11 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.composables
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.areNavigationBarsVisible
import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.follower
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@@ -21,10 +21,13 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.R
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
@Preview @Preview
@Composable @Composable
fun FollowerPage() { fun FollowerScreen() {
StatusBarMaskLayout( StatusBarMaskLayout(
modifier = Modifier.padding(horizontal = 16.dp) modifier = Modifier.padding(horizontal = 16.dp)
) { ) {

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.gallery
import androidx.compose.foundation.Canvas import androidx.compose.foundation.Canvas
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
@@ -44,12 +44,13 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.R
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable @Composable
fun GalleryPage() { fun ProfileTimelineScreen() {
val pagerState = rememberPagerState(pageCount = { 2 }) val pagerState = rememberPagerState(pageCount = { 2 })
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val systemUiController = rememberSystemUiController() val systemUiController = rememberSystemUiController()

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.gallery
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
@@ -32,10 +32,14 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
@Preview @Preview
@Composable @Composable
fun OfficialGalleryPage() { fun OfficialGalleryScreen() {
StatusBarMaskLayout { StatusBarMaskLayout {
Column( Column(
modifier = Modifier modifier = Modifier

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.gallery
import android.util.Log import android.util.Log
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
@@ -24,7 +24,6 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
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.runtime.LaunchedEffect
@@ -38,12 +37,16 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.R
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
data class ArtWork( data class ArtWork(
val id: Int, val id: Int,
@@ -69,7 +72,7 @@ fun GenerateMockArtWorks(): List<ArtWork> {
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class)
@Preview @Preview
@Composable @Composable
fun OfficialPhotographer() { fun OfficialPhotographerScreen() {
val lazyListState = rememberLazyListState() val lazyListState = rememberLazyListState()
var artWorks by remember { mutableStateOf<List<ArtWork>>(emptyList()) } var artWorks by remember { mutableStateOf<List<ArtWork>>(emptyList()) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
@@ -135,7 +138,7 @@ fun OfficialPhotographer() {
) )
) )
.padding(16.dp) .padding(16.dp)
.align(alignment = androidx.compose.ui.Alignment.BottomCenter) .align(alignment = Alignment.BottomCenter)
) { ) {
Column( Column(
@@ -257,11 +260,11 @@ fun OfficialPhotographer() {
) { ) {
Row( Row(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
verticalAlignment = androidx.compose.ui.Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Image( Image(
painter = painterResource(id = R.drawable.rider_pro_nav_back), painter = painterResource(id = R.drawable.rider_pro_nav_back),
colorFilter = androidx.compose.ui.graphics.ColorFilter.tint(Color.White), colorFilter = ColorFilter.tint(Color.White),
contentDescription = "", contentDescription = "",
modifier = Modifier modifier = Modifier
.size(32.dp) .size(32.dp)

View File

@@ -0,0 +1,209 @@
package com.aiosman.riderpro.ui.index
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.NavigationBarItemColors
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import com.aiosman.riderpro.ui.index.tabs.add.AddPage
import com.aiosman.riderpro.ui.index.tabs.moment.MomentsList
import com.aiosman.riderpro.ui.index.tabs.profile.ProfilePage
import com.aiosman.riderpro.ui.index.tabs.shorts.ShortVideo
import com.aiosman.riderpro.ui.index.tabs.street.StreetPage
import com.google.accompanist.systemuicontroller.rememberSystemUiController
@Composable
fun IndexScreen() {
val model = IndexViewModel
val navigationBarHeight = with(LocalDensity.current) {
WindowInsets.navigationBars.getBottom(this).toDp()
}
val item = listOf(
NavigationItem.Home,
NavigationItem.Street,
NavigationItem.Add,
NavigationItem.Message,
NavigationItem.Profile
)
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setNavigationBarColor(Color.Black)
}
Scaffold(
bottomBar = {
NavigationBar(
modifier = Modifier.height(56.dp + navigationBarHeight),
containerColor = Color.Black
) {
item.forEachIndexed { idx, it ->
val isSelected = model.tabIndex == idx
val iconTint by animateColorAsState(
targetValue = if (isSelected) Color.Red else Color.White,
animationSpec = tween(durationMillis = 250), label = ""
)
NavigationBarItem(
selected = isSelected,
onClick = {
model.tabIndex = idx
// if (it.route == NavigationItem.Add.route || it.route == NavigationItem.Message.route) {
// systemUiController.setStatusBarColor(Color.Black, darkIcons = false)
// } else {
// systemUiController.setStatusBarColor(
// Color.Transparent,
// darkIcons = true
// )
// }
},
colors = NavigationBarItemColors(
selectedTextColor = Color.Red,
selectedIndicatorColor = Color.Black,
unselectedTextColor = Color.Red,
disabledIconColor = Color.Red,
disabledTextColor = Color.Red,
selectedIconColor = iconTint,
unselectedIconColor = iconTint,
),
icon = {
Icon(
modifier = Modifier.size(24.dp),
imageVector = if (isSelected) it.selectedIcon() else it.icon(),
contentDescription = null,
tint = iconTint
)
}
)
}
}
}
) { innerPadding ->
Box(
modifier = Modifier
) {
when (model.tabIndex) {
0 -> Box(
modifier = Modifier.padding(innerPadding)
) {
Home()
}
1 -> Street()
2 -> Box(
modifier = Modifier.padding(innerPadding)
) { Add() }
3 -> Box(
modifier = Modifier.padding(innerPadding)
) { Video() }
4 -> Box(
modifier = Modifier.padding(innerPadding)
) { Profile() }
}
}
}
}
@Composable
fun Home() {
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Transparent, darkIcons = true)
}
Column(
modifier = Modifier
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
MomentsList()
}
}
@Composable
fun Street() {
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Transparent, darkIcons = true)
}
Column(
modifier = Modifier
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
StreetPage()
}
}
@Composable
fun Add() {
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Black, darkIcons = false)
}
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.Black),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
AddPage()
}
}
@Composable
fun Video() {
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Black, darkIcons = false)
}
Column(
modifier = Modifier
.fillMaxSize(),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally,
) {
ShortVideo()
}
}
@Composable
fun Profile() {
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
systemUiController.setStatusBarColor(Color.Transparent, darkIcons = true)
}
Column(
modifier = Modifier
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
ProfilePage()
}
}

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.index
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf

View File

@@ -1,13 +1,9 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.index
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Email
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Place
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource import androidx.compose.ui.res.vectorResource
import com.aiosman.riderpro.R
sealed class NavigationItem( sealed class NavigationItem(
val route: String, val route: String,

View File

@@ -1,9 +1,8 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.index.tabs.add
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@@ -11,19 +10,19 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
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.material.icons.Icons
import androidx.compose.material.icons.filled.LocationOn
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.focusModifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
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.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.ui.post.NewPostViewModel
import com.aiosman.riderpro.R
@Composable @Composable
fun AddPage(){ fun AddPage(){

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.index.tabs.moment
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.ExperimentalSharedTransitionApi
@@ -42,6 +42,15 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign 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 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.comment.CommentModalContent
import com.aiosman.riderpro.ui.composables.AnimatedCounter
import com.aiosman.riderpro.ui.composables.AnimatedLikeIcon
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
private val DATA = (0..60).toList().mapIndexed { idx, _ -> private val DATA = (0..60).toList().mapIndexed { idx, _ ->
@@ -63,41 +72,16 @@ private val DATA = (0..60).toList().mapIndexed { idx, _ ->
@OptIn(ExperimentalSharedTransitionApi::class) @OptIn(ExperimentalSharedTransitionApi::class)
@Composable @Composable
fun PagingBackendSample() { fun MomentsList() {
val model = MomentViewModel
// val myBackend = remember { TestBackend(DATA) } val moments = model.momentsFlow.collectAsLazyPagingItems()
// val pager = remember {
// Pager(
// PagingConfig(
// pageSize = myBackend.DataBatchSize,
// enablePlaceholders = true,
// maxSize = 200
// )
// ) {
// myBackend.getAllData()
// }
// }
val model = HomeViewModel
// val lazyPagingItems = model.pager.collectAsLazyPagingItems()
LazyColumn { LazyColumn {
// if (lazyPagingItems.loadState.refresh == LoadState.Loading) { items(moments.itemCount) { idx ->
// item { val momentItem = moments[idx] ?: return@items
// MomentListLoading() MomentCard(momentItem = momentItem)
// }
// }
items(count = model.momentList.size) { index ->
val item = model.momentList.getOrNull(index)
if (item != null) {
MomentCard(item)
} }
} }
// if (lazyPagingItems.loadState.append == LoadState.Loading) {
// item {
// MomentListLoading()
// }
// }
}
} }
@OptIn(ExperimentalSharedTransitionApi::class) @OptIn(ExperimentalSharedTransitionApi::class)
@@ -124,7 +108,7 @@ fun MomentCard(
.fillMaxHeight() .fillMaxHeight()
.weight(1f) .weight(1f)
ModificationListHeader() ModificationListHeader()
MomentBottomOperateRowGroup(momentOperateBtnBoxModifier) MomentBottomOperateRowGroup(momentOperateBtnBoxModifier, momentItem = momentItem)
} }
} }
@@ -312,9 +296,24 @@ fun MomentOperateBtn(@DrawableRes icon: Int, count: String) {
} }
} }
@Composable
fun MomentOperateBtn(count: String, content: @Composable () -> Unit) {
Row(
modifier = Modifier,
verticalAlignment = Alignment.CenterVertically
) {
content()
AnimatedCounter(
count = count.toInt(),
fontSize = 14,
modifier = Modifier.padding(start = 7.dp)
)
}
}
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun MomentBottomOperateRowGroup(modifier: Modifier) { fun MomentBottomOperateRowGroup(modifier: Modifier, 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,7 +339,14 @@ fun MomentBottomOperateRowGroup(modifier: Modifier) {
modifier = modifier, modifier = modifier,
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
MomentOperateBtn(icon = R.drawable.rider_pro_like, count = "21") MomentOperateBtn(count = momentItem.likeCount.toString()) {
AnimatedLikeIcon(modifier = Modifier.size(24.dp)) {
MomentViewModel.updateById(
momentItem.id,
momentItem.copy(likeCount = momentItem.likeCount + 1)
)
}
}
} }
Box( Box(
modifier = modifier.clickable( modifier = modifier.clickable(

View File

@@ -0,0 +1,59 @@
package com.aiosman.riderpro.ui.index.tabs.moment
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.toMutableStateList
import androidx.lifecycle.ViewModel
import androidx.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.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()
}
var momentListPagingSource = MomentPagingSource(
MomentRemoteDataSource(TestMomentServiceImpl())
)
val momentsFlow: Flow<PagingData<MomentItem>> = Pager(
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
pagingSourceFactory = { momentListPagingSource }
).flow
}

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.index.tabs.profile
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
@@ -31,6 +31,10 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
import com.aiosman.riderpro.model.MomentItem
import com.aiosman.riderpro.model.profileMomentItems
@Composable @Composable

View File

@@ -1,11 +1,10 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.index.tabs.shorts
import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.Animatable
import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.draggable import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.gestures.rememberDraggableState import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier

View File

@@ -1,10 +1,8 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.index.tabs.shorts
import android.app.Activity
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import com.aiosman.riderpro.ShortViewCompose
import com.aiosman.riderpro.ui.theme.RiderProTheme import com.aiosman.riderpro.ui.theme.RiderProTheme
val videoUrls = listOf( val videoUrls = listOf(

View File

@@ -1,4 +1,6 @@
package com.aiosman.riderpro @file:kotlin.OptIn(ExperimentalMaterial3Api::class)
package com.aiosman.riderpro.ui.index.tabs.shorts
import android.net.Uri import android.net.Uri
import android.view.Gravity import android.view.Gravity
@@ -9,6 +11,7 @@ import androidx.annotation.OptIn
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@@ -21,8 +24,11 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@@ -59,22 +65,25 @@ import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.source.ProgressiveMediaSource import androidx.media3.exoplayer.source.ProgressiveMediaSource
import androidx.media3.ui.AspectRatioFrameLayout import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerView import androidx.media3.ui.PlayerView
import com.aiosman.riderpro.R
import com.aiosman.riderpro.ui.comment.CommentModalContent
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
fun ShortViewCompose( fun ShortViewCompose(
videoItemsUrl:List<String>, videoItemsUrl: List<String>,
clickItemPosition:Int = 0, clickItemPosition: Int = 0,
videoHeader:@Composable () -> Unit = {}, videoHeader: @Composable () -> Unit = {},
videoBottom:@Composable () -> Unit = {} videoBottom: @Composable () -> Unit = {}
) { ) {
val pagerState: PagerState = run { val pagerState: PagerState = run {
remember { remember {
PagerState(clickItemPosition, 0, videoItemsUrl.size - 1) PagerState(clickItemPosition, 0, videoItemsUrl.size - 1)
} }
} }
val initialLayout= remember { val initialLayout = remember {
mutableStateOf(true) mutableStateOf(true)
} }
val pauseIconVisibleState = remember { val pauseIconVisibleState = remember {
@@ -85,19 +94,21 @@ fun ShortViewCompose(
orientation = Orientation.Vertical, orientation = Orientation.Vertical,
offscreenLimit = 1 offscreenLimit = 1
) { ) {
pauseIconVisibleState.value=false pauseIconVisibleState.value = false
SingleVideoItemContent(videoItemsUrl[page], SingleVideoItemContent(
videoItemsUrl[page],
pagerState, pagerState,
page, page,
initialLayout, initialLayout,
pauseIconVisibleState, pauseIconVisibleState,
videoHeader, videoHeader,
videoBottom) videoBottom
)
} }
LaunchedEffect(clickItemPosition){ LaunchedEffect(clickItemPosition) {
delay(300) delay(300)
initialLayout.value=false initialLayout.value = false
} }
} }
@@ -112,16 +123,18 @@ private fun SingleVideoItemContent(
VideoHeader: @Composable() () -> Unit, VideoHeader: @Composable() () -> Unit,
VideoBottom: @Composable() () -> Unit, VideoBottom: @Composable() () -> Unit,
) { ) {
Box(modifier = Modifier.fillMaxSize()){ Box(modifier = Modifier.fillMaxSize()) {
VideoPlayer(videoUrl,pagerState,pager,pauseIconVisibleState) VideoPlayer(videoUrl, pagerState, pager, pauseIconVisibleState)
VideoHeader.invoke() VideoHeader.invoke()
Box(modifier = Modifier.align(Alignment.BottomStart)){ Box(modifier = Modifier.align(Alignment.BottomStart)) {
VideoBottom.invoke() VideoBottom.invoke()
} }
if (initialLayout.value) { if (initialLayout.value) {
Box(modifier = Modifier Box(
modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(color = Color.Black)) .background(color = Color.Black)
)
} }
} }
} }
@@ -135,9 +148,12 @@ fun VideoPlayer(
pauseIconVisibleState: MutableState<Boolean>, pauseIconVisibleState: MutableState<Boolean>,
) { ) {
val context = LocalContext.current val context = LocalContext.current
val scope= rememberCoroutineScope() val scope = rememberCoroutineScope()
val lifecycleOwner = LocalLifecycleOwner.current val lifecycleOwner = LocalLifecycleOwner.current
var showCommentModal by remember { mutableStateOf(false) }
var sheetState = rememberModalBottomSheetState(
skipPartiallyExpanded = true
)
val exoPlayer = remember { val exoPlayer = remember {
ExoPlayer.Builder(context) ExoPlayer.Builder(context)
.build() .build()
@@ -160,8 +176,12 @@ fun VideoPlayer(
} }
exoPlayer.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT exoPlayer.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT
exoPlayer.repeatMode = Player.REPEAT_MODE_ONE exoPlayer.repeatMode = Player.REPEAT_MODE_ONE
Box(modifier = Modifier.fillMaxSize(), // player box
contentAlignment = Alignment.TopCenter) { Box(
modifier = Modifier
.fillMaxSize(),
contentAlignment = Alignment.TopCenter
) {
var playerView by remember { mutableStateOf<PlayerView?>(null) } // Store reference to PlayerView var playerView by remember { mutableStateOf<PlayerView?>(null) } // Store reference to PlayerView
AndroidView( AndroidView(
@@ -176,7 +196,8 @@ fun VideoPlayer(
hideController() hideController()
useController = false useController = false
player = exoPlayer player = exoPlayer
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIXED_HEIGHT // 或 RESIZE_MODE_ZOOM resizeMode =
AspectRatioFrameLayout.RESIZE_MODE_FIXED_HEIGHT // 或 RESIZE_MODE_ZOOM
layoutParams = FrameLayout.LayoutParams( layoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@@ -226,11 +247,13 @@ fun VideoPlayer(
Lifecycle.Event.ON_PAUSE -> { Lifecycle.Event.ON_PAUSE -> {
exoPlayer.pause() // 应用进入后台时暂停 exoPlayer.pause() // 应用进入后台时暂停
} }
Lifecycle.Event.ON_RESUME -> { Lifecycle.Event.ON_RESUME -> {
if (pager == pagerState.currentPage) { if (pager == pagerState.currentPage) {
exoPlayer.play() // 返回前台且为当前页面时恢复播放 exoPlayer.play() // 返回前台且为当前页面时恢复播放
} }
} }
else -> {} else -> {}
} }
} }
@@ -243,29 +266,58 @@ fun VideoPlayer(
} }
Box(modifier = Modifier.fillMaxSize(), // action buttons
contentAlignment = Alignment.BottomEnd){ Box(
Column (modifier = Modifier.padding(bottom = 72.dp, end = 12.dp), modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally){ contentAlignment = Alignment.BottomEnd
) {
Column(
modifier = Modifier.padding(bottom = 72.dp, end = 12.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
UserAvatar() UserAvatar()
VideoBtn(icon = R.drawable.rider_pro_video_like, text = "975.9k") VideoBtn(icon = R.drawable.rider_pro_video_like, text = "975.9k")
VideoBtn(icon = R.drawable.rider_pro_video_comment, text = "1896") VideoBtn(icon = R.drawable.rider_pro_video_comment, text = "1896") {
showCommentModal = true
}
VideoBtn(icon = R.drawable.rider_pro_video_favor, text = "234") VideoBtn(icon = R.drawable.rider_pro_video_favor, text = "234")
VideoBtn(icon = R.drawable.rider_pro_video_share, text = "677k") VideoBtn(icon = R.drawable.rider_pro_video_share, text = "677k")
} }
} }
// info
Box(modifier = Modifier.fillMaxSize(), Box(
contentAlignment = Alignment.BottomStart){ modifier = Modifier.fillMaxSize(),
Column (modifier = Modifier.padding(start = 16.dp, bottom = 16.dp)){ contentAlignment = Alignment.BottomStart
Row (modifier = Modifier.padding(bottom = 8.dp).background(color = Color.Gray), ) {
Column(modifier = Modifier.padding(start = 16.dp, bottom = 16.dp)) {
Row(
modifier = Modifier
.padding(bottom = 8.dp)
.background(color = Color.Gray),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start, horizontalArrangement = Arrangement.Start,
){ ) {
Image(modifier = Modifier.size(20.dp).padding(start = 4.dp, end = 6.dp), painter = painterResource(id = R.drawable.rider_pro_video_location), contentDescription = "") Image(
Text(modifier = Modifier.padding(end = 4.dp),text = "USA",fontSize = 12.sp, color = Color.White, style = TextStyle(fontWeight = FontWeight.Bold)) modifier = Modifier
.size(20.dp)
.padding(start = 4.dp, end = 6.dp),
painter = painterResource(id = R.drawable.rider_pro_video_location),
contentDescription = ""
)
Text(
modifier = Modifier.padding(end = 4.dp),
text = "USA",
fontSize = 12.sp,
color = Color.White,
style = TextStyle(fontWeight = FontWeight.Bold)
)
} }
Text(text = "@Kevinlinpr",fontSize = 16.sp, color = Color.White, style = TextStyle(fontWeight = FontWeight.Bold)) Text(
text = "@Kevinlinpr",
fontSize = 16.sp,
color = Color.White,
style = TextStyle(fontWeight = FontWeight.Bold)
)
Text( Text(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -279,23 +331,54 @@ fun VideoPlayer(
) )
} }
} }
if (showCommentModal) {
ModalBottomSheet(
onDismissRequest = { showCommentModal = false },
containerColor = Color.White,
sheetState = sheetState
) {
CommentModalContent() {
}
}
}
} }
@Composable @Composable
fun UserAvatar(){ fun UserAvatar() {
Image(modifier = Modifier.padding(bottom = 16.dp).size(40.dp).border(width = 3.dp, color = Color.White, shape = RoundedCornerShape(40.dp)).clip( Image(
modifier = Modifier
.padding(bottom = 16.dp)
.size(40.dp)
.border(width = 3.dp, color = Color.White, shape = RoundedCornerShape(40.dp))
.clip(
RoundedCornerShape(40.dp) RoundedCornerShape(40.dp)
), painter = painterResource(id = R.drawable.default_avatar), contentDescription = "") ), painter = painterResource(id = R.drawable.default_avatar), contentDescription = ""
)
} }
@Composable @Composable
fun VideoBtn(@DrawableRes icon: Int, text: String){ fun VideoBtn(@DrawableRes icon: Int, text: String, onClick: (() -> Unit)? = null) {
Column ( Column(
modifier = Modifier.padding(bottom = 16.dp), modifier = Modifier
.padding(bottom = 16.dp)
.clickable {
onClick?.invoke()
},
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
){ ) {
Image(modifier = Modifier.size(36.dp), painter = painterResource(id = icon), contentDescription = "") Image(
Text(text = text,fontSize = 11.sp, color = Color.White, style = TextStyle(fontWeight = FontWeight.Bold)) modifier = Modifier.size(36.dp),
painter = painterResource(id = icon),
contentDescription = ""
)
Text(
text = text,
fontSize = 11.sp,
color = Color.White,
style = TextStyle(fontWeight = FontWeight.Bold)
)
} }
} }

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.index.tabs.street
import android.content.pm.PackageManager import android.content.pm.PackageManager
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
@@ -41,6 +41,10 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.navigation.NavOptions
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
import com.aiosman.riderpro.test.countries
import com.google.android.gms.location.FusedLocationProviderClient import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices import com.google.android.gms.location.LocationServices
import com.google.android.gms.maps.model.CameraPosition import com.google.android.gms.maps.model.CameraPosition
@@ -80,6 +84,7 @@ fun StreetPage() {
} }
} }
) )
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
when (PackageManager.PERMISSION_GRANTED) { when (PackageManager.PERMISSION_GRANTED) {
ContextCompat.checkSelfPermission( ContextCompat.checkSelfPermission(
@@ -88,7 +93,7 @@ fun StreetPage() {
) -> { ) -> {
fusedLocationClient.lastLocation.addOnSuccessListener { location -> fusedLocationClient.lastLocation.addOnSuccessListener { location ->
if (location != null) { if (location != null) {
currentLocation = LatLng(location.latitude, location.longitude) // currentLocation = LatLng(location.latitude, location.longitude)
} }
} }
hasLocationPermission = true hasLocationPermission = true
@@ -100,11 +105,20 @@ fun StreetPage() {
} }
} }
LaunchedEffect(currentLocation) { LaunchedEffect(currentLocation) {
cameraPositionState.position = cameraPositionState.position =
CameraPosition.fromLatLngZoom(currentLocation ?: LatLng(0.0, 0.0), 10f) CameraPosition.fromLatLngZoom(
currentLocation ?: LatLng(
countries[0].lat,
countries[0].lng
), 5f
)
} }
Box( Box(
modifier = Modifier.fillMaxSize().padding(bottom = 56.dp + navigationBarHeight) modifier = Modifier
.fillMaxSize()
.padding(bottom = 56.dp + navigationBarHeight)
) { ) {
GoogleMap( GoogleMap(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
@@ -118,14 +132,24 @@ fun StreetPage() {
zoomControlsEnabled = false zoomControlsEnabled = false
) )
) { ) {
// pins
countries.forEach { position -> countries.forEach { position ->
MarkerComposable( MarkerComposable(
state = MarkerState(position = LatLng(position.lat, position.lng)), state = MarkerState(position = LatLng(position.lat, position.lng)),
onClick = { it -> onClick = {
navController.navigate("LocationDetail") val screenLocation =
cameraPositionState.projection?.toScreenLocation(it.position)
val x = screenLocation?.x ?: 0
val y = screenLocation?.y ?: 0
navController.navigate("LocationDetail/${x}/${y}",NavOptions.Builder()
.setEnterAnim(0)
.setExitAnim(0)
.setPopEnterAnim(0)
.setPopExitAnim(0)
.build())
true true
} },
) { ) {
Image( Image(
painter = painterResource(id = R.drawable.rider_pro_map_mark), painter = painterResource(id = R.drawable.rider_pro_map_mark),
@@ -135,19 +159,20 @@ fun StreetPage() {
} }
} }
Image( Image(
painter = painterResource(id = R.drawable.rider_pro_my_location), painter = painterResource(id = R.drawable.rider_pro_my_location),
contentDescription = "", contentDescription = "",
modifier = Modifier modifier = Modifier
.align(Alignment.BottomStart) .align(Alignment.BottomStart)
.padding(start = 16.dp, bottom = 16.dp + navigationBarHeight) .clickable( .padding(start = 16.dp, bottom = 16.dp + navigationBarHeight)
.clickable(
indication = null, indication = null,
interactionSource = remember { MutableInteractionSource() } interactionSource = remember { MutableInteractionSource() }
) { ) {
currentLocation?.let { currentLocation?.let {
cameraPositionState.position = CameraPosition.fromLatLngZoom(it, cameraPositionState.position.zoom) cameraPositionState.position =
CameraPosition.fromLatLngZoom(it, cameraPositionState.position.zoom)
} }
} }
) )
@@ -167,7 +192,8 @@ fun StreetPage() {
painter = painterResource(id = R.drawable.rider_pro_new_post_add_pic), painter = painterResource(id = R.drawable.rider_pro_new_post_add_pic),
contentDescription = "", contentDescription = "",
modifier = Modifier modifier = Modifier
.align(Alignment.Center).size(36.dp), .align(Alignment.Center)
.size(36.dp),
colorFilter = ColorFilter.tint(Color.White) colorFilter = ColorFilter.tint(Color.White)
) )
@@ -178,10 +204,12 @@ fun StreetPage() {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.align(Alignment.TopCenter) .align(Alignment.TopCenter)
.padding(top = 64.dp, start = 16.dp,end=16.dp) .padding(top = 64.dp, start = 16.dp, end = 16.dp)
) { ) {
Box( Box(
modifier = Modifier.background(Color.White).padding(16.dp), modifier = Modifier
.background(Color.White)
.padding(16.dp),
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
@@ -205,7 +233,7 @@ fun StreetPage() {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
) { ) {
if (searchText.isEmpty()){ if (searchText.isEmpty()) {
Text( Text(
text = "Please enter a search location", text = "Please enter a search location",
color = Color(0xffc6c6c6), color = Color(0xffc6c6c6),

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.like
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
@@ -29,10 +29,14 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.R
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
@Preview @Preview
@Composable @Composable
fun LikePage() { fun LikeScreen() {
val model = LikePageViewModel val model = LikePageViewModel
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val listState = rememberLazyListState() val listState = rememberLazyListState()
@@ -45,10 +49,10 @@ fun LikePage() {
} }
} }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
model.loader.loadData() LikePageViewModel.loader.loadData()
} }
LaunchedEffect(reachedBottom) { LaunchedEffect(reachedBottom) {
if (reachedBottom) model.loader.loadMore() if (reachedBottom) LikePageViewModel.loader.loadMore()
} }
StatusBarMaskLayout( StatusBarMaskLayout(
darkIcons = true, darkIcons = true,
@@ -67,7 +71,7 @@ fun LikePage() {
state = listState, state = listState,
) { ) {
items(model.loader.list, key = { it.id }) { items(LikePageViewModel.loader.list, key = { it.id }) {
LikeItem(it) LikeItem(it)
} }
item { item {

View File

@@ -1,9 +1,12 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.like
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.aiosman.riderpro.test.MockDataContainer
import com.aiosman.riderpro.test.MockDataSource
import com.aiosman.riderpro.test.MockListContainer
class LikeDataSource : MockDataSource<LikeItemData>() { class LikeDataSource : MockDataSource<LikeItemData>() {
init { init {

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.location
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
@@ -53,12 +53,14 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
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 com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
data class OfficialGalleryItem( data class OfficialGalleryItem(
val id: Int, val id: Int,
@@ -109,9 +111,8 @@ fun getFeedItems(): List<FeedItem> {
} }
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Preview
@Composable @Composable
fun LocationDetail() { fun LocationDetailScreen(x: Float, y: Float) {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val scaffoldState = rememberBottomSheetScaffoldState( val scaffoldState = rememberBottomSheetScaffoldState(
SheetState( SheetState(
@@ -137,6 +138,25 @@ fun LocationDetail() {
val peekHeight = (screenHeight * 1 / 3).dp val peekHeight = (screenHeight * 1 / 3).dp
return peekHeight return peekHeight
} }
val view = LocalView.current
// LaunchedEffect(key1 = Unit) {
// val locationOnScreen = IntArray(2).apply {
// view.getLocationOnScreen(this)
// }
// val startX = x - locationOnScreen[0]
// val startY = y - locationOnScreen[1]
// val radius = hypot(view.width.toDouble(), view.height.toDouble()).toFloat()
//
// val anim = ViewAnimationUtils.createCircularReveal(view, startX.toInt(), startY.toInt(), 0f, radius).apply {
// duration = 600
// start()
// }
//
// }
val staggeredGridState = rememberLazyStaggeredGridState() val staggeredGridState = rememberLazyStaggeredGridState()
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
@@ -150,7 +170,7 @@ fun LocationDetail() {
} }
Box( Box(
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize().background(Color.Transparent)
) { ) {
Image( Image(
painter = painterResource(id = R.drawable.default_moment_img), painter = painterResource(id = R.drawable.default_moment_img),
@@ -433,3 +453,4 @@ fun GalleryAndInfo(showGalleryAndInfo: Boolean) {
} }
} }
} }

View File

@@ -1,6 +1,5 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.message
import android.view.RoundedCorner
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
@@ -8,8 +7,6 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@@ -18,8 +15,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Person
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -29,9 +24,7 @@ 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.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.vectorResource
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.unit.dp import androidx.compose.ui.unit.dp
@@ -40,6 +33,10 @@ import androidx.paging.LoadState
import androidx.paging.Pager import androidx.paging.Pager
import androidx.paging.PagingConfig import androidx.paging.PagingConfig
import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems
import com.aiosman.riderpro.ui.index.tabs.moment.MomentListLoading
import com.aiosman.riderpro.R
import com.aiosman.riderpro.model.ChatNotificationData
import com.aiosman.riderpro.model.TestChatBackend
val chatNotificationData = ChatNotificationData( val chatNotificationData = ChatNotificationData(
R.drawable.default_avatar, R.drawable.default_avatar,
@@ -49,7 +46,7 @@ val chatNotificationData = ChatNotificationData(
6 6
) )
private val ChatData = (0..10).toList().map { chatNotificationData} private val ChatData = (0..10).toList().map { chatNotificationData }
@Composable @Composable
fun MessagePage(){ fun MessagePage(){

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.message
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
@@ -8,12 +8,9 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
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
@@ -27,12 +24,15 @@ 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.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.modification
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
@@ -44,10 +44,14 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.ui.post.NewPostViewModel
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
import com.aiosman.riderpro.R
import com.aiosman.riderpro.utils.Utils
@Preview @Preview
@Composable @Composable
fun EditModification() { fun EditModificationScreen() {
val model = NewPostViewModel val model = NewPostViewModel
Column( Column(
modifier = Modifier modifier = Modifier
@@ -62,9 +66,9 @@ fun EditModification() {
LazyColumn( LazyColumn(
modifier = Modifier.padding(start = 24.dp, end = 24.dp, top = 16.dp) modifier = Modifier.padding(start = 24.dp, end = 24.dp, top = 16.dp)
) { ) {
items(model.modificationList) { mod -> items(NewPostViewModel.modificationList) { mod ->
AddModificationItem(mod) { updatedMod -> AddModificationItem(mod) { updatedMod ->
model.modificationList = model.modificationList.map { existingMod -> NewPostViewModel.modificationList = NewPostViewModel.modificationList.map { existingMod ->
if (existingMod.key == updatedMod.key) updatedMod else existingMod if (existingMod.key == updatedMod.key) updatedMod else existingMod
}.toMutableList() }.toMutableList()
} }
@@ -72,7 +76,7 @@ fun EditModification() {
} }
item { item {
AddModificationButton { AddModificationButton {
model.modificationList += Modification( NewPostViewModel.modificationList += Modification(
key = Utils.generateRandomString(4), key = Utils.generateRandomString(4),
name = "", name = "",
price = "0.0" price = "0.0"

View File

@@ -17,9 +17,9 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.BottomNavigationPlaceholder import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
import com.aiosman.riderpro.NoticeScreenHeader import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
import com.aiosman.riderpro.StatusBarMaskLayout import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
@Preview @Preview

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.modifiers
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.post
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
@@ -43,6 +43,9 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
@@ -62,8 +65,8 @@ fun NewPostScreen() {
.fillMaxSize() .fillMaxSize()
) { ) {
NewPostTopBar() NewPostTopBar()
NewPostTextField("Share your adventure…", model.textContent) { NewPostTextField("Share your adventure…", NewPostViewModel.textContent) {
model.textContent = it NewPostViewModel.textContent = it
} }
AddImageGrid() AddImageGrid()
AdditionalPostItem() AdditionalPostItem()
@@ -223,7 +226,7 @@ fun AdditionalPostItem() {
} }
) { ) {
isShowLocationModal = false isShowLocationModal = false
model.searchPlaceAddressResult = it NewPostViewModel.searchPlaceAddressResult = it
} }
} }
} }
@@ -241,9 +244,9 @@ fun AdditionalPostItem() {
onSelectLocationClick() onSelectLocationClick()
} }
) { ) {
model.searchPlaceAddressResult?.let { NewPostViewModel.searchPlaceAddressResult?.let {
SelectedLocation(it) { SelectedLocation(it) {
model.searchPlaceAddressResult = null NewPostViewModel.searchPlaceAddressResult = null
} }
} ?: Row( } ?: Row(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically

View File

@@ -1,9 +1,10 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.post
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.aiosman.riderpro.ui.modification.Modification
object NewPostViewModel : ViewModel() { object NewPostViewModel : ViewModel() {

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.post
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.ExperimentalSharedTransitionApi
@@ -56,6 +56,11 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.aiosman.riderpro.LocalAnimatedContentScope
import com.aiosman.riderpro.LocalSharedTransitionScope
import com.aiosman.riderpro.R
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
fun makeMockImages(): List<PostImage> { fun makeMockImages(): List<PostImage> {
@@ -69,7 +74,7 @@ fun makeMockImages(): List<PostImage> {
@OptIn(ExperimentalSharedTransitionApi::class) @OptIn(ExperimentalSharedTransitionApi::class)
@Composable @Composable
fun PostPage( fun PostScreen(
id: String, id: String,
) { ) {
var showCollapseContent by remember { mutableStateOf(true) } var showCollapseContent by remember { mutableStateOf(true) }

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.ui.post
import android.util.Log import android.util.Log
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
@@ -33,10 +33,9 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
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 com.aiosman.riderpro.R
import com.google.android.gms.common.api.ApiException import com.google.android.gms.common.api.ApiException
import com.google.android.libraries.places.api.Places import com.google.android.libraries.places.api.Places
import com.google.android.libraries.places.api.model.Place import com.google.android.libraries.places.api.model.Place
@@ -49,7 +48,10 @@ data class SearchPlaceAddressResult(
) )
@Composable @Composable
fun SelectLocationModal(onClose:() -> Unit,onSelectedLocation: (SearchPlaceAddressResult) -> Unit) { fun SelectLocationModal(
onClose: () -> Unit,
onSelectedLocation: (SearchPlaceAddressResult) -> Unit
) {
val context = LocalContext.current val context = LocalContext.current
var queryString by remember { mutableStateOf("") } var queryString by remember { mutableStateOf("") }
var searchPlaceAddressResults by remember { var searchPlaceAddressResults by remember {
@@ -94,7 +96,9 @@ fun SelectLocationModal(onClose:() -> Unit,onSelectedLocation: (SearchPlaceAddre
) )
Text( Text(
"Cancel", "Cancel",
modifier = Modifier.align(Alignment.CenterEnd).clickable { modifier = Modifier
.align(Alignment.CenterEnd)
.clickable {
onClose() onClose()
}, },
fontSize = 16.sp fontSize = 16.sp

View File

@@ -1,4 +1,4 @@
package com.aiosman.riderpro package com.aiosman.riderpro.utils
object Utils { object Utils {
fun generateRandomString(length: Int): String { fun generateRandomString(length: Int): String {

View File

@@ -21,3 +21,5 @@ kotlin.code.style=official
# resources declared in the library itself and none from the library's dependencies, # resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library # thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
org.gradle.daemon=true
ja