更新代码
This commit is contained in:
@@ -154,7 +154,7 @@ fun ClickCaptchaDialog(
|
||||
onDismissRequest()
|
||||
},
|
||||
title = {
|
||||
Text("Captcha")
|
||||
Text(stringResource(R.string.captcha))
|
||||
},
|
||||
text = {
|
||||
Column {
|
||||
@@ -163,14 +163,14 @@ fun ClickCaptchaDialog(
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
ClickCaptchaView(
|
||||
captchaData = captchaData!!,
|
||||
captchaData = captchaData,
|
||||
onPositionClicked = onPositionClicked
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
ActionButton(
|
||||
text = "Refresh",
|
||||
text = stringResource(R.string.refresh),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
|
||||
@@ -96,7 +96,7 @@ fun CollapsingToolbarScaffold(
|
||||
toolbarModifier: Modifier = Modifier,
|
||||
toolbarClipToBounds: Boolean = true,
|
||||
toolbarScrollable: Boolean = false,
|
||||
toolbar: @Composable CollapsingToolbarScope.() -> Unit,
|
||||
toolbar: @Composable CollapsingToolbarScope.(ScrollState) -> Unit,
|
||||
body: @Composable CollapsingToolbarScaffoldScope.() -> Unit
|
||||
) {
|
||||
val flingBehavior = ScrollableDefaults.flingBehavior()
|
||||
@@ -122,7 +122,7 @@ fun CollapsingToolbarScaffold(
|
||||
toolbarState,
|
||||
toolbarScrollState
|
||||
)
|
||||
toolbar()
|
||||
toolbar(toolbarScrollState)
|
||||
}
|
||||
|
||||
CollapsingToolbarScaffoldScopeInstance.body()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.aiosman.riderpro.ui.composables.toolbar
|
||||
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.SubcomposeLayout
|
||||
@@ -14,7 +15,7 @@ fun ToolbarWithFabScaffold(
|
||||
scrollStrategy: ScrollStrategy,
|
||||
toolbarModifier: Modifier = Modifier,
|
||||
toolbarClipToBounds: Boolean = true,
|
||||
toolbar: @Composable CollapsingToolbarScope.() -> Unit,
|
||||
toolbar: @Composable CollapsingToolbarScope.(ScrollState) -> Unit,
|
||||
toolbarScrollable: Boolean = false,
|
||||
fab: @Composable () -> Unit,
|
||||
fabPosition: FabPosition = FabPosition.End,
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
package com.aiosman.riderpro.ui.follower
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -16,15 +23,21 @@ import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun FollowerListScreen(userId: Int) {
|
||||
val model = FollowerListViewModel
|
||||
val scope = rememberCoroutineScope()
|
||||
val refreshState = rememberPullRefreshState(model.isLoading, onRefresh = {
|
||||
model.loadData(userId, true)
|
||||
})
|
||||
LaunchedEffect(Unit) {
|
||||
model.loadData(userId)
|
||||
}
|
||||
|
||||
StatusBarMaskLayout(
|
||||
modifier = Modifier.padding(horizontal = 16.dp)
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
) {
|
||||
var dataFlow = model.usersFlow
|
||||
var users = dataFlow.collectAsLazyPagingItems()
|
||||
@@ -35,8 +48,14 @@ fun FollowerListScreen(userId: Int) {
|
||||
) {
|
||||
NoticeScreenHeader(stringResource(R.string.followers_upper), moreIcon = false)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
.pullRefresh(refreshState)
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.weight(1f)
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
items(users.itemCount) { index ->
|
||||
users[index]?.let { user ->
|
||||
@@ -57,5 +76,11 @@ fun FollowerListScreen(userId: Int) {
|
||||
}
|
||||
}
|
||||
}
|
||||
PullRefreshIndicator(
|
||||
refreshing = model.isLoading,
|
||||
state = refreshState,
|
||||
modifier = Modifier.align(Alignment.TopCenter)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,10 +23,12 @@ object FollowerListViewModel : ViewModel() {
|
||||
private val _usersFlow = MutableStateFlow<PagingData<AccountProfileEntity>>(PagingData.empty())
|
||||
val usersFlow = _usersFlow.asStateFlow()
|
||||
private var userId by mutableStateOf<Int?>(null)
|
||||
fun loadData(id: Int) {
|
||||
if (userId == id) {
|
||||
var isLoading by mutableStateOf(false)
|
||||
fun loadData(id: Int,force : Boolean = false) {
|
||||
if (userId == id && !force) {
|
||||
return
|
||||
}
|
||||
isLoading = true
|
||||
userId = id
|
||||
viewModelScope.launch {
|
||||
Pager(
|
||||
@@ -41,6 +43,7 @@ object FollowerListViewModel : ViewModel() {
|
||||
_usersFlow.value = it
|
||||
}
|
||||
}
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
private fun updateIsFollow(id: Int, isFollow: Boolean = true) {
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package com.aiosman.riderpro.ui.follower
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -16,10 +22,14 @@ import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
|
||||
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun FollowingListScreen(userId: Int) {
|
||||
val model = FollowingListViewModel
|
||||
val scope = rememberCoroutineScope()
|
||||
val refreshState = rememberPullRefreshState(model.isLoading, onRefresh = {
|
||||
model.loadData(userId, true)
|
||||
})
|
||||
LaunchedEffect(Unit) {
|
||||
model.loadData(userId)
|
||||
}
|
||||
@@ -35,8 +45,14 @@ fun FollowingListScreen(userId: Int) {
|
||||
) {
|
||||
NoticeScreenHeader(stringResource(R.string.following_upper), moreIcon = false)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
.pullRefresh(refreshState)
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.weight(1f)
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
items(users.itemCount) { index ->
|
||||
users[index]?.let { user ->
|
||||
@@ -57,5 +73,11 @@ fun FollowingListScreen(userId: Int) {
|
||||
}
|
||||
}
|
||||
}
|
||||
PullRefreshIndicator(
|
||||
refreshing = model.isLoading,
|
||||
state = refreshState,
|
||||
modifier = Modifier.align(Alignment.TopCenter)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,14 @@ import kotlinx.coroutines.launch
|
||||
object FollowingListViewModel : ViewModel() {
|
||||
private val userService = UserServiceImpl()
|
||||
private val _usersFlow = MutableStateFlow<PagingData<AccountProfileEntity>>(PagingData.empty())
|
||||
var isLoading by mutableStateOf(false)
|
||||
val usersFlow = _usersFlow.asStateFlow()
|
||||
private var userId by mutableStateOf<Int?>(null)
|
||||
fun loadData(id: Int) {
|
||||
fun loadData(id: Int, force: Boolean = false) {
|
||||
if (userId == id && !force) {
|
||||
return
|
||||
}
|
||||
isLoading = true
|
||||
userId = id
|
||||
viewModelScope.launch {
|
||||
Pager(
|
||||
@@ -38,6 +43,7 @@ object FollowingListViewModel : ViewModel() {
|
||||
_usersFlow.value = it
|
||||
}
|
||||
}
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
private fun updateIsFollow(id: Int, isFollow: Boolean = true) {
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.aiosman.riderpro.entity.MomentEntity
|
||||
import com.aiosman.riderpro.entity.MomentPagingSource
|
||||
import com.aiosman.riderpro.entity.MomentRemoteDataSource
|
||||
import com.aiosman.riderpro.entity.MomentServiceImpl
|
||||
import com.aiosman.riderpro.ui.post.NewPostViewModel.uriToFile
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
@@ -43,7 +42,6 @@ object MyProfileViewModel : ViewModel() {
|
||||
val profile = accountService.getMyAccountProfile()
|
||||
MyProfileViewModel.profile = profile
|
||||
}
|
||||
|
||||
fun loadProfile(pullRefresh: Boolean = false) {
|
||||
if (!firstLoad && !pullRefresh) return
|
||||
viewModelScope.launch {
|
||||
|
||||
@@ -1,483 +0,0 @@
|
||||
package com.aiosman.riderpro.ui.index.tabs.profile
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
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.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
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.systemBars
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
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.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.aiosman.riderpro.LocalNavController
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.entity.AccountProfileEntity
|
||||
import com.aiosman.riderpro.entity.MomentEntity
|
||||
import com.aiosman.riderpro.ui.NavigationRoute
|
||||
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
||||
import com.aiosman.riderpro.ui.composables.DropdownMenu
|
||||
import com.aiosman.riderpro.ui.composables.MenuItem
|
||||
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.composable.EmptyMomentPostUnit
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.composable.GalleryItem
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.composable.MomentPostUnit
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.composable.OtherProfileAction
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.composable.SelfProfileAction
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.composable.UserContentPageIndicator
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.composable.UserItem
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
import com.aiosman.riderpro.ui.post.NewPostViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun Profile(
|
||||
onUpdateBanner: ((Uri, Context) -> Unit)? = null,
|
||||
profile: AccountProfileEntity? = null,
|
||||
onLogout: () -> Unit = {},
|
||||
onFollowClick: () -> Unit = {},
|
||||
onChatClick: () -> Unit = {},
|
||||
sharedFlow: SharedFlow<PagingData<MomentEntity>> = MutableStateFlow<PagingData<MomentEntity>>(
|
||||
PagingData.empty()
|
||||
).asStateFlow(),
|
||||
isSelf: Boolean = true
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.background(Color(0xffFFFFFF))
|
||||
) {
|
||||
val userHeight: Int = 187
|
||||
val bannerHeight = 500
|
||||
var headerBannerMaxHeight: Int = userHeight + bannerHeight
|
||||
val headerBannerMinHeight = 100
|
||||
val speedFactor = 0.75f
|
||||
var currentHeaderHeight by rememberSaveable { mutableStateOf<Int>(headerBannerMaxHeight) }
|
||||
var scrollState = rememberLazyListState()
|
||||
var gridScrollState = rememberLazyStaggeredGridState()
|
||||
var pagerState = rememberPagerState(pageCount = { 2 })
|
||||
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
|
||||
val context = LocalContext.current
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
val scope = rememberCoroutineScope()
|
||||
val navController = LocalNavController.current
|
||||
val moments = sharedFlow.collectAsLazyPagingItems()
|
||||
val pickBannerImageLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
val uri = result.data?.data
|
||||
uri?.let {
|
||||
onUpdateBanner?.invoke(it, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
val parentScrollConnection = remember {
|
||||
object : NestedScrollConnection {
|
||||
override fun onPreScroll(
|
||||
available: Offset,
|
||||
source: NestedScrollSource
|
||||
): Offset {
|
||||
val delta = (available.y * speedFactor).toInt()
|
||||
// 如果是向下滑动,未滑动到列表顶部,则不展开头部
|
||||
if (pagerState.currentPage == 0) {
|
||||
if (delta > 0 && (gridScrollState.firstVisibleItemIndex > 0 || gridScrollState.firstVisibleItemScrollOffset > 0)) {
|
||||
return Offset.Zero
|
||||
}
|
||||
}
|
||||
if (pagerState.currentPage == 1) {
|
||||
if (delta > 0 && (scrollState.firstVisibleItemIndex > 0 || scrollState.firstVisibleItemScrollOffset > 0)) {
|
||||
return Offset.Zero
|
||||
}
|
||||
}
|
||||
|
||||
// 计算新的高度
|
||||
val newHeight = currentHeaderHeight + delta
|
||||
val previousHeight = currentHeaderHeight
|
||||
val newCurrentHeaderHeight =
|
||||
newHeight.coerceIn(headerBannerMinHeight, headerBannerMaxHeight)
|
||||
val consumedHeader = newCurrentHeaderHeight - previousHeight
|
||||
|
||||
currentHeaderHeight = newCurrentHeaderHeight
|
||||
|
||||
return Offset(x = 0f, y = consumedHeader / speedFactor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(parentScrollConnection)
|
||||
.verticalScroll(
|
||||
state = rememberScrollState()
|
||||
)
|
||||
.background(Color(0xfff8f8f8))
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(currentHeaderHeight.dp)
|
||||
|
||||
) {
|
||||
// banner
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(currentHeaderHeight.dp - userHeight.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.noRippleClickable {
|
||||
Intent(Intent.ACTION_PICK).apply {
|
||||
type = "image/*"
|
||||
pickBannerImageLauncher.launch(this)
|
||||
}
|
||||
}
|
||||
.shadow(
|
||||
elevation = 4.dp,
|
||||
shape = RoundedCornerShape(
|
||||
bottomStart = 32.dp,
|
||||
bottomEnd = 32.dp
|
||||
)
|
||||
)
|
||||
) {
|
||||
val banner = profile?.banner
|
||||
|
||||
if (banner != null) {
|
||||
CustomAsyncImage(
|
||||
LocalContext.current,
|
||||
banner,
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
contentDescription = "",
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
} else {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_moment_demo_2),
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
contentDescription = "",
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
}
|
||||
}
|
||||
if (isSelf) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(
|
||||
top = statusBarPaddingValues.calculateTopPadding(),
|
||||
start = 8.dp,
|
||||
end = 8.dp
|
||||
)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.shadow(
|
||||
elevation = 20.dp
|
||||
)
|
||||
.background(Color.White.copy(alpha = 0.7f))
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.rider_pro_more_horizon),
|
||||
contentDescription = "",
|
||||
modifier = Modifier.noRippleClickable {
|
||||
expanded = true
|
||||
},
|
||||
tint = Color.Black
|
||||
)
|
||||
}
|
||||
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false },
|
||||
width = 250,
|
||||
menuItems = listOf(
|
||||
MenuItem(
|
||||
stringResource(R.string.logout),
|
||||
R.mipmap.rider_pro_logout
|
||||
) {
|
||||
expanded = false
|
||||
scope.launch {
|
||||
onLogout()
|
||||
navController.navigate(NavigationRoute.Login.route) {
|
||||
popUpTo(NavigationRoute.Index.route) {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
MenuItem(
|
||||
stringResource(R.string.change_password),
|
||||
R.mipmap.rider_pro_change_password
|
||||
) {
|
||||
expanded = false
|
||||
scope.launch {
|
||||
navController.navigate(NavigationRoute.ChangePasswordScreen.route)
|
||||
}
|
||||
},
|
||||
MenuItem(
|
||||
stringResource(R.string.favourites),
|
||||
R.drawable.rider_pro_favourite
|
||||
) {
|
||||
expanded = false
|
||||
scope.launch {
|
||||
navController.navigate(NavigationRoute.FavouriteList.route)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
// user info
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(if (currentHeaderHeight.dp > bannerHeight.dp) userHeight.dp else currentHeaderHeight.dp)
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
// 个人信息
|
||||
Box(
|
||||
modifier = Modifier.padding(horizontal = 16.dp)
|
||||
) {
|
||||
profile?.let {
|
||||
UserItem(it)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
profile?.let {
|
||||
Box(
|
||||
modifier = Modifier.padding(horizontal = 16.dp)
|
||||
) {
|
||||
if (isSelf) {
|
||||
SelfProfileAction {
|
||||
navController.navigate(NavigationRoute.AccountEdit.route)
|
||||
}
|
||||
} else {
|
||||
OtherProfileAction(
|
||||
it,
|
||||
onFollow = {
|
||||
onFollowClick()
|
||||
},
|
||||
onChat = {
|
||||
onChatClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// collapsed bar
|
||||
|
||||
|
||||
}
|
||||
val thresholdHeight = 200 // 设置阈值高度
|
||||
val startChangeHeight = headerBannerMinHeight + thresholdHeight
|
||||
val alpha = if (currentHeaderHeight < startChangeHeight) {
|
||||
((currentHeaderHeight - headerBannerMinHeight).toFloat() / thresholdHeight.toFloat()).coerceIn(
|
||||
0f,
|
||||
1f
|
||||
)
|
||||
} else {
|
||||
1f // 高度大于阈值时,alpha 为 0
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
// 保持在最低高度和当前高度之间
|
||||
.alpha(1 - alpha)
|
||||
.background(Color(0xfff8f8f8))
|
||||
.padding(horizontal = 16.dp)
|
||||
) {
|
||||
StatusBarSpacer()
|
||||
|
||||
Row(
|
||||
modifier = Modifier.height(headerBannerMinHeight.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
CustomAsyncImage(
|
||||
LocalContext.current,
|
||||
profile?.avatar,
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(CircleShape),
|
||||
contentDescription = "",
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
Text(
|
||||
text = profile?.nickName ?: "",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.W600,
|
||||
color = Color.Black
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(currentHeaderHeight.dp - headerBannerMinHeight.dp))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
UserContentPageIndicator(pagerState)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
HorizontalPager(
|
||||
state = pagerState,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
) { page ->
|
||||
when (page) {
|
||||
0 -> {
|
||||
LazyVerticalStaggeredGrid(
|
||||
columns = StaggeredGridCells.Fixed(2),
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
8.dp
|
||||
),
|
||||
verticalItemSpacing = 8.dp,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = gridScrollState,
|
||||
contentPadding = PaddingValues(8.dp)
|
||||
) {
|
||||
items(1) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(0.75f)
|
||||
.clip(
|
||||
RoundedCornerShape(8.dp)
|
||||
)
|
||||
.background(Color.White)
|
||||
.padding(8.dp)
|
||||
.noRippleClickable {
|
||||
NewPostViewModel.asNewPost()
|
||||
navController.navigate(NavigationRoute.NewPost.route)
|
||||
}
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clip(
|
||||
RoundedCornerShape(8.dp)
|
||||
)
|
||||
.background(Color(0xfff5f5f5))
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Add,
|
||||
contentDescription = "",
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
items(moments.itemCount) { idx ->
|
||||
val moment = moments[idx] ?: return@items
|
||||
GalleryItem(moment)
|
||||
}
|
||||
items(2) {
|
||||
Spacer(modifier = Modifier.height(120.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1 -> {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = scrollState
|
||||
) {
|
||||
if (moments.itemCount == 0) {
|
||||
item {
|
||||
EmptyMomentPostUnit()
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
for (idx in 0 until moments.itemCount) {
|
||||
val moment = moments[idx]
|
||||
moment?.let {
|
||||
MomentPostUnit(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(120.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
@@ -136,13 +137,14 @@ fun ProfileV3(
|
||||
scrollStrategy = ScrollStrategy.ExitUntilCollapsed,
|
||||
toolbarScrollable = true,
|
||||
enabled = enabled,
|
||||
toolbar = {
|
||||
toolbar = { toolbarScrollState ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
// 保持在最低高度和当前高度之间
|
||||
.background(Color(0xfff8f8f8))
|
||||
.padding(horizontal = 16.dp)
|
||||
|
||||
) {
|
||||
StatusBarSpacer()
|
||||
|
||||
@@ -177,8 +179,7 @@ fun ProfileV3(
|
||||
.graphicsLayer {
|
||||
// change alpha of Image as the toolbar expands
|
||||
alpha = state.toolbarState.progress
|
||||
},
|
||||
|
||||
}.verticalScroll(toolbarScrollState)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
||||
@@ -1,476 +0,0 @@
|
||||
package com.aiosman.riderpro.ui.index.tabs.profile
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.scrollBy
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
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.systemBars
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
|
||||
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
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.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.aiosman.riderpro.LocalNavController
|
||||
import com.aiosman.riderpro.R
|
||||
import com.aiosman.riderpro.entity.AccountProfileEntity
|
||||
import com.aiosman.riderpro.entity.MomentEntity
|
||||
import com.aiosman.riderpro.ui.NavigationRoute
|
||||
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
|
||||
import com.aiosman.riderpro.ui.composables.DropdownMenu
|
||||
import com.aiosman.riderpro.ui.composables.MenuItem
|
||||
import com.aiosman.riderpro.ui.composables.pickupAndCompressLauncher
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.composable.EmptyMomentPostUnit
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.composable.GalleryItem
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.composable.MomentPostUnit
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.composable.OtherProfileAction
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.composable.SelfProfileAction
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.composable.UserContentPageIndicator
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.composable.UserItem
|
||||
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
|
||||
import com.aiosman.riderpro.ui.post.NewPostViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun ProfileV2(
|
||||
onUpdateBanner: ((Uri, File, Context) -> Unit)? = null,
|
||||
profile: AccountProfileEntity? = null,
|
||||
onLogout: () -> Unit = {},
|
||||
onFollowClick: () -> Unit = {},
|
||||
onChatClick: () -> Unit = {},
|
||||
sharedFlow: SharedFlow<PagingData<MomentEntity>> = MutableStateFlow<PagingData<MomentEntity>>(
|
||||
PagingData.empty()
|
||||
).asStateFlow(),
|
||||
isSelf: Boolean = true
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.background(Color(0xffFFFFFF))
|
||||
) {
|
||||
var parentScrollThreshold by remember { mutableStateOf(0) }
|
||||
var remainScrollThreshold by remember { mutableStateOf(0) }
|
||||
val bannerHeight = 500
|
||||
var scrollState = rememberLazyListState()
|
||||
var gridScrollState = rememberLazyStaggeredGridState()
|
||||
var pagerState = rememberPagerState(pageCount = { 2 })
|
||||
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
|
||||
val context = LocalContext.current
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
val scope = rememberCoroutineScope()
|
||||
val navController = LocalNavController.current
|
||||
val moments = sharedFlow.collectAsLazyPagingItems()
|
||||
val rootScrollState = rememberScrollState()
|
||||
val pickBannerImageLauncher = pickupAndCompressLauncher(
|
||||
context,
|
||||
scope
|
||||
) { uri, file ->
|
||||
onUpdateBanner?.invoke(uri, file, context)
|
||||
}
|
||||
val parentScrollConnection = remember {
|
||||
object : NestedScrollConnection {
|
||||
override fun onPreScroll(
|
||||
available: Offset,
|
||||
source: NestedScrollSource
|
||||
): Offset {
|
||||
Log.d("ProfileV2", "onPreScroll: $available")
|
||||
val delta = available.y.toInt()
|
||||
if (delta < 0 && rootScrollState.value < parentScrollThreshold - remainScrollThreshold) {
|
||||
val scrollAmount = minOf(
|
||||
-delta,
|
||||
parentScrollThreshold - remainScrollThreshold - rootScrollState.value
|
||||
)
|
||||
scope.launch {
|
||||
Log.d("ProfileV2", "scrollBy: $scrollAmount")
|
||||
rootScrollState.scrollBy(scrollAmount.toFloat())
|
||||
}
|
||||
return Offset(0f, -scrollAmount.toFloat())
|
||||
}
|
||||
return Offset.Zero
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(parentScrollConnection)
|
||||
.verticalScroll(
|
||||
state = rootScrollState
|
||||
)
|
||||
.background(Color(0xfff8f8f8))
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.onGloballyPositioned {
|
||||
parentScrollThreshold = it.size.height
|
||||
}
|
||||
|
||||
) {
|
||||
// banner
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(bannerHeight.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.noRippleClickable {
|
||||
Intent(Intent.ACTION_PICK).apply {
|
||||
type = "image/*"
|
||||
pickBannerImageLauncher.launch(this)
|
||||
}
|
||||
}
|
||||
.shadow(
|
||||
elevation = 4.dp,
|
||||
shape = RoundedCornerShape(
|
||||
bottomStart = 32.dp,
|
||||
bottomEnd = 32.dp
|
||||
)
|
||||
)
|
||||
) {
|
||||
val banner = profile?.banner
|
||||
|
||||
if (banner != null) {
|
||||
CustomAsyncImage(
|
||||
LocalContext.current,
|
||||
banner,
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
contentDescription = "",
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
} else {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.rider_pro_moment_demo_2),
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
contentDescription = "",
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
}
|
||||
}
|
||||
if (isSelf) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(
|
||||
top = statusBarPaddingValues.calculateTopPadding(),
|
||||
start = 8.dp,
|
||||
end = 8.dp
|
||||
)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.shadow(
|
||||
elevation = 20.dp
|
||||
)
|
||||
.background(Color.White.copy(alpha = 0.7f))
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.rider_pro_more_horizon),
|
||||
contentDescription = "",
|
||||
modifier = Modifier.noRippleClickable {
|
||||
expanded = true
|
||||
},
|
||||
tint = Color.Black
|
||||
)
|
||||
}
|
||||
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false },
|
||||
width = 250,
|
||||
menuItems = listOf(
|
||||
MenuItem(
|
||||
stringResource(R.string.logout),
|
||||
R.mipmap.rider_pro_logout
|
||||
) {
|
||||
expanded = false
|
||||
scope.launch {
|
||||
onLogout()
|
||||
navController.navigate(NavigationRoute.Login.route) {
|
||||
popUpTo(NavigationRoute.Index.route) {
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
MenuItem(
|
||||
stringResource(R.string.change_password),
|
||||
R.mipmap.rider_pro_change_password
|
||||
) {
|
||||
expanded = false
|
||||
scope.launch {
|
||||
navController.navigate(NavigationRoute.ChangePasswordScreen.route)
|
||||
}
|
||||
},
|
||||
MenuItem(
|
||||
stringResource(R.string.favourites),
|
||||
R.drawable.rider_pro_favourite
|
||||
) {
|
||||
expanded = false
|
||||
scope.launch {
|
||||
navController.navigate(NavigationRoute.FavouriteList.route)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
// user info
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
// 个人信息
|
||||
Box(
|
||||
modifier = Modifier.padding(horizontal = 16.dp)
|
||||
) {
|
||||
profile?.let {
|
||||
UserItem(it)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
profile?.let {
|
||||
Box(
|
||||
modifier = Modifier.padding(horizontal = 16.dp)
|
||||
) {
|
||||
if (isSelf) {
|
||||
SelfProfileAction {
|
||||
navController.navigate(NavigationRoute.AccountEdit.route)
|
||||
}
|
||||
} else {
|
||||
OtherProfileAction(
|
||||
it,
|
||||
onFollow = {
|
||||
onFollowClick()
|
||||
},
|
||||
onChat = {
|
||||
onChatClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// collapsed bar
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
val miniBarAlpha =
|
||||
if (rootScrollState.value >= parentScrollThreshold - remainScrollThreshold) {
|
||||
1f
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.alpha(miniBarAlpha)
|
||||
// 保持在最低高度和当前高度之间
|
||||
.background(Color(0xfff8f8f8))
|
||||
.padding(horizontal = 16.dp)
|
||||
.onGloballyPositioned {
|
||||
remainScrollThreshold = it.size.height
|
||||
}
|
||||
.align(Alignment.BottomCenter)
|
||||
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(48.dp))
|
||||
Row(
|
||||
modifier = Modifier,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
CustomAsyncImage(
|
||||
LocalContext.current,
|
||||
profile?.avatar,
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(CircleShape),
|
||||
contentDescription = "",
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
Text(
|
||||
text = profile?.nickName ?: "",
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.W600,
|
||||
color = Color.Black
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 页面指示器
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
UserContentPageIndicator(pagerState)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
HorizontalPager(
|
||||
state = pagerState,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(900.dp)
|
||||
) { page ->
|
||||
when (page) {
|
||||
0 -> {
|
||||
LazyVerticalStaggeredGrid(
|
||||
columns = StaggeredGridCells.Fixed(2),
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
8.dp
|
||||
),
|
||||
verticalItemSpacing = 8.dp,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = gridScrollState,
|
||||
contentPadding = PaddingValues(8.dp)
|
||||
) {
|
||||
items(1) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(0.75f)
|
||||
.clip(
|
||||
RoundedCornerShape(8.dp)
|
||||
)
|
||||
.background(Color.White)
|
||||
.padding(8.dp)
|
||||
.noRippleClickable {
|
||||
NewPostViewModel.asNewPost()
|
||||
navController.navigate(NavigationRoute.NewPost.route)
|
||||
}
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clip(
|
||||
RoundedCornerShape(8.dp)
|
||||
)
|
||||
.background(Color(0xfff5f5f5))
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Add,
|
||||
contentDescription = "",
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
items(moments.itemCount) { idx ->
|
||||
val moment = moments[idx] ?: return@items
|
||||
GalleryItem(moment)
|
||||
}
|
||||
items(2) {
|
||||
Spacer(modifier = Modifier.height(120.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1 -> {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxHeight(),
|
||||
state = scrollState
|
||||
) {
|
||||
if (moments.itemCount == 0) {
|
||||
item {
|
||||
EmptyMomentPostUnit()
|
||||
}
|
||||
}
|
||||
item {
|
||||
for (idx in 0 until moments.itemCount) {
|
||||
val moment = moments[idx]
|
||||
moment?.let {
|
||||
MomentPostUnit(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(120.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ fun UserAuthScreen() {
|
||||
loadLoginCaptcha()
|
||||
Toast.makeText(
|
||||
context,
|
||||
"incorrect captcha,please try again",
|
||||
context.getString(R.string.incorrect_captcha_please_try_again),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
@@ -197,8 +197,7 @@ fun UserAuthScreen() {
|
||||
onLogin(captchaInfo)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
Column(
|
||||
|
||||
@@ -93,7 +93,7 @@ object NewPostViewModel : ViewModel() {
|
||||
}
|
||||
momentService.createMoment(textContent, 1, uploadImageList, relPostId)
|
||||
// 刷新个人动态
|
||||
MyProfileViewModel.loadProfile()
|
||||
MyProfileViewModel.loadProfile(pullRefresh = true)
|
||||
MomentViewModel.refreshPager()
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +114,7 @@ class PostViewModel(
|
||||
moment?.let {
|
||||
userService.followUser(it.authorId.toString())
|
||||
moment = moment?.copy(followStatus = true)
|
||||
// 更新我的关注页面的关注数
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +122,7 @@ class PostViewModel(
|
||||
moment?.let {
|
||||
userService.unFollowUser(it.authorId.toString())
|
||||
moment = moment?.copy(followStatus = false)
|
||||
// 更新我的关注页面的关注数
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.aiosman.riderpro.LocalNavController
|
||||
import com.aiosman.riderpro.exp.viewModelFactory
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.Profile
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.ProfileV2
|
||||
import com.aiosman.riderpro.ui.index.tabs.profile.ProfileV3
|
||||
import com.aiosman.riderpro.ui.navigateToChat
|
||||
|
||||
|
||||
@@ -82,4 +82,8 @@
|
||||
<string name="resend">重新发送 %s</string>
|
||||
<string name="error_40002_user_not_exist">用户不存在</string>
|
||||
<string name="captcha_hint">请依次点击图片中的元素</string>
|
||||
<string name="captcha">验证码</string>
|
||||
<string name="refresh">刷新</string>
|
||||
<string name="clear">清除</string>
|
||||
<string name="incorrect_captcha_please_try_again">验证码错误,请重试</string>
|
||||
</resources>
|
||||
@@ -81,4 +81,8 @@
|
||||
<string name="resend">Resend in %s</string>
|
||||
<string name="error_40002_user_not_exist">user not exist</string>
|
||||
<string name="captcha_hint">Please click on the dots in the image in the correct order.</string>
|
||||
<string name="captcha">Chaptcha</string>
|
||||
<string name="refresh">Refresh</string>
|
||||
<string name="clear">Clear</string>
|
||||
<string name="incorrect_captcha_please_try_again">incorrect captcha,please try again</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user