更新个人主页

This commit is contained in:
2024-09-29 23:17:59 +08:00
parent 2497698f27
commit 2c6af2ad94
18 changed files with 863 additions and 2483 deletions

View File

@@ -0,0 +1,18 @@
package com.aiosman.riderpro.data
import com.aiosman.riderpro.data.api.ApiClient
import com.aiosman.riderpro.data.api.DictItem
interface DictService {
/**
* 获取字典项
*/
suspend fun getDictByKey(key: String): DictItem
}
class DictServiceImpl : DictService {
override suspend fun getDictByKey(key: String): DictItem {
val resp = ApiClient.api.getDict(key)
return resp.body()?.data ?: throw Exception("failed to get dict")
}
}

View File

@@ -119,6 +119,15 @@ data class AppConfig(
val trtcAppId: Int, val trtcAppId: Int,
) )
data class DictItem(
@SerializedName("key")
val key: String,
@SerializedName("value")
val value: String,
@SerializedName("desc")
val desc: String,
)
interface RiderProAPI { interface RiderProAPI {
@POST("register") @POST("register")
suspend fun register(@Body body: RegisterRequestBody): Response<Unit> suspend fun register(@Body body: RegisterRequestBody): Response<Unit>
@@ -322,4 +331,9 @@ interface RiderProAPI {
@GET("app/info") @GET("app/info")
suspend fun getAppConfig(): Response<DataContainer<AppConfig>> suspend fun getAppConfig(): Response<DataContainer<AppConfig>>
@GET("dict")
suspend fun getDict(
@Query("key") key: String
): Response<DataContainer<DictItem>>
} }

View File

@@ -53,7 +53,7 @@ import com.aiosman.riderpro.ui.modification.EditModificationScreen
import com.aiosman.riderpro.ui.post.NewPostImageGridScreen import com.aiosman.riderpro.ui.post.NewPostImageGridScreen
import com.aiosman.riderpro.ui.post.NewPostScreen import com.aiosman.riderpro.ui.post.NewPostScreen
import com.aiosman.riderpro.ui.post.PostScreen import com.aiosman.riderpro.ui.post.PostScreen
import com.aiosman.riderpro.ui.profile.AccountProfile import com.aiosman.riderpro.ui.profile.AccountProfileV2
sealed class NavigationRoute( sealed class NavigationRoute(
val route: String, val route: String,
@@ -240,7 +240,7 @@ fun NavigationController(
CompositionLocalProvider( CompositionLocalProvider(
LocalAnimatedContentScope provides this, LocalAnimatedContentScope provides this,
) { ) {
AccountProfile(it.arguments?.getString("id")!!) AccountProfileV2(it.arguments?.getString("id")!!)
} }
} }
composable( composable(
@@ -363,7 +363,6 @@ fun NavigationController(
} }
} }
} }
@OptIn(ExperimentalSharedTransitionApi::class) @OptIn(ExperimentalSharedTransitionApi::class)
@@ -394,7 +393,7 @@ fun Navigation(
fun NavHostController.navigateToPost( fun NavHostController.navigateToPost(
id: Int, id: Int,
highlightCommentId: Int? = 0, highlightCommentId: Int? = 0,
initImagePagerIndex: Int? = null initImagePagerIndex: Int? = 0
) { ) {
navigate( navigate(
route = NavigationRoute.Post.route route = NavigationRoute.Post.route

View File

@@ -3,7 +3,6 @@ package com.aiosman.riderpro.ui.composables
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -13,15 +12,14 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.drawable.toDrawable
import coil.ImageLoader import coil.ImageLoader
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.compose.rememberAsyncImagePainter import coil.compose.rememberAsyncImagePainter
import coil.compose.rememberImagePainter import coil.compose.rememberImagePainter
import coil.request.ImageRequest import coil.request.ImageRequest
import coil.request.SuccessResult import coil.request.SuccessResult
import com.aiosman.riderpro.utils.BlurHashDecoder
import com.aiosman.riderpro.utils.Utils.getImageLoader import com.aiosman.riderpro.utils.Utils.getImageLoader
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext

View File

@@ -0,0 +1,144 @@
package com.aiosman.riderpro.ui.composables
import android.net.http.SslError
import android.webkit.SslErrorHandler
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
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.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import com.aiosman.riderpro.data.DictService
import com.aiosman.riderpro.data.DictServiceImpl
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
@Composable
fun PolicyCheckbox(
checked: Boolean = false,
error: Boolean = false,
onCheckedChange: (Boolean) -> Unit,
) {
var showModal by remember { mutableStateOf(false) }
var modalSheetState = androidx.compose.material3.rememberModalBottomSheetState(
skipPartiallyExpanded = true,
)
var scope = rememberCoroutineScope()
val dictService: DictService = DictServiceImpl()
var policyUrl by remember { mutableStateOf("") }
fun openPolicyModel() {
scope.launch {
try {
val resp = dictService.getDictByKey("private_policy")
policyUrl = resp.value
showModal = true
} catch (e: Exception) {
e.printStackTrace()
}
}
}
if (showModal) {
ModalBottomSheet(
onDismissRequest = {
showModal = false
},
sheetState = modalSheetState,
windowInsets = WindowInsets(0),
containerColor = Color.White,
) {
WebViewDisplay(
url = policyUrl
)
}
}
Row {
Checkbox(
checked = checked,
onCheckedChange = {
onCheckedChange(it)
},
size = 16
)
val text = buildAnnotatedString {
append("I agree to the ")
withStyle(style = SpanStyle(color = if (error) Color.Red else Color.Black)) {
append("terms and conditions")
}
addStyle(
style = SpanStyle(
color = Color.Blue,
textDecoration = TextDecoration.Underline
),
start = "I agree to the ".length,
end = "I agree to the terms and conditions".length
)
}
ClickableText(
text = text,
modifier = Modifier.padding(start = 8.dp),
onClick = {
openPolicyModel()
},
style = TextStyle(
fontSize = 12.sp,
color = if (error) Color.Red else Color.Black
)
)
}
}
@Composable
fun WebViewDisplay(modifier: Modifier = Modifier, url: String) {
LazyColumn(
modifier = modifier.fillMaxSize()
) {
item {
AndroidView(
factory = { context ->
WebView(context).apply {
webViewClient = object : WebViewClient() {
override fun onReceivedSslError(
view: WebView?,
handler: SslErrorHandler?,
error: SslError?
) {
handler?.proceed() // 忽略证书错误
}
}
settings.apply {
domStorageEnabled = true
mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
}
loadUrl(url)
}
},
modifier = modifier.fillMaxSize()
)
}
}
}

View File

@@ -33,9 +33,7 @@ import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.index.tabs.add.AddPage import com.aiosman.riderpro.ui.index.tabs.add.AddPage
import com.aiosman.riderpro.ui.index.tabs.message.NotificationsScreen import com.aiosman.riderpro.ui.index.tabs.message.NotificationsScreen
import com.aiosman.riderpro.ui.index.tabs.moment.MomentsList import com.aiosman.riderpro.ui.index.tabs.moment.MomentsList
import com.aiosman.riderpro.ui.index.tabs.profile.v2.Profile2 import com.aiosman.riderpro.ui.index.tabs.profile.ProfileWrap
import com.aiosman.riderpro.ui.index.tabs.profile.v2.Profile4
import com.aiosman.riderpro.ui.index.tabs.profile.v2.ProfilePage
import com.aiosman.riderpro.ui.index.tabs.search.DiscoverScreen import com.aiosman.riderpro.ui.index.tabs.search.DiscoverScreen
import com.aiosman.riderpro.ui.index.tabs.shorts.ShortVideo import com.aiosman.riderpro.ui.index.tabs.shorts.ShortVideo
import com.aiosman.riderpro.ui.index.tabs.street.StreetPage import com.aiosman.riderpro.ui.index.tabs.street.StreetPage
@@ -209,7 +207,7 @@ fun Profile() {
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Profile2() ProfileWrap()
} }
} }

View File

@@ -17,7 +17,6 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.LinearProgressIndicator
import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material.pullrefresh.rememberPullRefreshState
@@ -27,7 +26,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
@@ -37,21 +35,14 @@ import androidx.compose.ui.text.style.TextOverflow
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.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.paging.LoadState
import androidx.paging.compose.collectAsLazyPagingItems
import com.aiosman.riderpro.LocalNavController import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R import com.aiosman.riderpro.R
import com.aiosman.riderpro.entity.CommentEntity
import com.aiosman.riderpro.exp.timeAgo
import com.aiosman.riderpro.ui.NavigationRoute import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.composables.CustomAsyncImage import com.aiosman.riderpro.ui.composables.CustomAsyncImage
import com.aiosman.riderpro.ui.composables.StatusBarSpacer import com.aiosman.riderpro.ui.composables.StatusBarSpacer
import com.aiosman.riderpro.ui.favourite.FavouriteNoticeViewModel
import com.aiosman.riderpro.ui.follower.FollowerNoticeViewModel import com.aiosman.riderpro.ui.follower.FollowerNoticeViewModel
import com.aiosman.riderpro.ui.like.LikeNoticeViewModel import com.aiosman.riderpro.ui.like.LikeNoticeViewModel
import com.aiosman.riderpro.ui.modifiers.noRippleClickable import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import com.aiosman.riderpro.ui.navigateToChat
import com.aiosman.riderpro.ui.navigateToPost
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@@ -5,7 +5,6 @@ import android.net.Uri
import android.util.Log import android.util.Log
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@@ -19,7 +18,6 @@ import com.aiosman.riderpro.data.AccountService
import com.aiosman.riderpro.data.AccountServiceImpl import com.aiosman.riderpro.data.AccountServiceImpl
import com.aiosman.riderpro.data.MomentService import com.aiosman.riderpro.data.MomentService
import com.aiosman.riderpro.data.UploadImage import com.aiosman.riderpro.data.UploadImage
import com.aiosman.riderpro.data.UserServiceImpl
import com.aiosman.riderpro.entity.AccountProfileEntity import com.aiosman.riderpro.entity.AccountProfileEntity
import com.aiosman.riderpro.entity.MomentEntity import com.aiosman.riderpro.entity.MomentEntity
import com.aiosman.riderpro.entity.MomentPagingSource import com.aiosman.riderpro.entity.MomentPagingSource
@@ -35,23 +33,24 @@ object MyProfileViewModel : ViewModel() {
val accountService: AccountService = AccountServiceImpl() val accountService: AccountService = AccountServiceImpl()
val momentService: MomentService = MomentServiceImpl() val momentService: MomentService = MomentServiceImpl()
var profile by mutableStateOf<AccountProfileEntity?>(null) var profile by mutableStateOf<AccountProfileEntity?>(null)
private var _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty()) private var _sharedFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
var momentsFlow = _momentsFlow.asStateFlow() var sharedFlow = _sharedFlow.asStateFlow()
var refreshing by mutableStateOf(false) var refreshing by mutableStateOf(false)
var firstLoad = true var firstLoad = true
fun loadProfile(pullRefresh: Boolean = false) { fun loadProfile(pullRefresh: Boolean = false) {
// if (!firstLoad && !pullRefresh) { if (!firstLoad) return
// return
// }
viewModelScope.launch { viewModelScope.launch {
if (pullRefresh){ if (pullRefresh) {
refreshing = true refreshing = true
} }
firstLoad = false firstLoad = false
val profile = accountService.getMyAccountProfile() val profile = accountService.getMyAccountProfile()
this@MyProfileViewModel.profile = profile MyProfileViewModel.profile = profile
refreshing = false refreshing = false
try { try {
// Collect shared flow
Pager( Pager(
config = PagingConfig(pageSize = 5, enablePlaceholders = false), config = PagingConfig(pageSize = 5, enablePlaceholders = false),
pagingSourceFactory = { pagingSourceFactory = {
@@ -61,13 +60,11 @@ object MyProfileViewModel : ViewModel() {
) )
} }
).flow.cachedIn(viewModelScope).collectLatest { ).flow.cachedIn(viewModelScope).collectLatest {
_momentsFlow.value = it _sharedFlow.value = it
} }
}catch (e: Exception){ } catch (e: Exception) {
Log.e("MyProfileViewModel", "loadProfile: ", e) Log.e("MyProfileViewModel", "loadProfile: ", e)
} }
} }
} }
@@ -76,7 +73,6 @@ object MyProfileViewModel : ViewModel() {
token = null token = null
rememberMe = false rememberMe = false
saveData() saveData()
} }
AppState.ReloadAppState() AppState.ReloadAppState()
} }
@@ -115,8 +111,7 @@ object MyProfileViewModel : ViewModel() {
fun ResetModel() { fun ResetModel() {
profile = null profile = null
_momentsFlow.value = PagingData.empty() _sharedFlow.value = PagingData.empty()
firstLoad = true firstLoad = true
} }
} }

View File

@@ -1,116 +0,0 @@
package com.aiosman.riderpro.ui.index.tabs.profile.v2
import android.content.Context
import android.net.Uri
import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.aiosman.riderpro.AppState
import com.aiosman.riderpro.AppStore
import com.aiosman.riderpro.data.AccountService
import com.aiosman.riderpro.data.AccountServiceImpl
import com.aiosman.riderpro.data.MomentService
import com.aiosman.riderpro.data.UploadImage
import com.aiosman.riderpro.entity.AccountProfileEntity
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
import kotlinx.coroutines.launch
object MyProfileViewModel2 : ViewModel() {
val accountService: AccountService = AccountServiceImpl()
val momentService: MomentService = MomentServiceImpl()
var profile by mutableStateOf<AccountProfileEntity?>(null)
private var _sharedFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
var sharedFlow = _sharedFlow.asStateFlow()
var refreshing by mutableStateOf(false)
var firstLoad = true
fun loadProfile(pullRefresh: Boolean = false) {
viewModelScope.launch {
if (pullRefresh) {
refreshing = true
}
firstLoad = false
val profile = accountService.getMyAccountProfile()
MyProfileViewModel2.profile = profile
refreshing = false
try {
// Collect shared flow
Pager(
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
pagingSourceFactory = {
MomentPagingSource(
MomentRemoteDataSource(momentService),
author = profile.id
)
}
).flow.cachedIn(viewModelScope).collectLatest {
_sharedFlow.value = it
}
} catch (e: Exception) {
Log.e("MyProfileViewModel", "loadProfile: ", e)
}
}
}
suspend fun logout() {
AppStore.apply {
token = null
rememberMe = false
saveData()
}
AppState.ReloadAppState()
}
fun updateUserProfileBanner(bannerImageUrl: Uri?, context: Context) {
viewModelScope.launch {
var newBanner = bannerImageUrl?.let {
val cursor = context.contentResolver.query(it, null, null, null, null)
var newBanner: UploadImage? = null
cursor?.use { cur ->
if (cur.moveToFirst()) {
val displayName = cur.getString(cur.getColumnIndex("_display_name"))
val extension = displayName.substringAfterLast(".")
Log.d("NewPost", "File name: $displayName, extension: $extension")
// read as file
val file = uriToFile(context, it)
Log.d("NewPost", "File size: ${file.length()}")
newBanner = UploadImage(file, displayName, it.toString(), extension)
}
}
newBanner
}
accountService.updateProfile(
banner = newBanner,
avatar = null,
nickName = null,
bio = null
)
profile = accountService.getMyAccountProfile()
}
}
val bio get() = profile?.bio ?: ""
val nickName get() = profile?.nickName ?: ""
val avatar get() = profile?.avatar
fun ResetModel() {
profile = null
_sharedFlow.value = PagingData.empty()
firstLoad = true
}
}

View File

@@ -1,542 +0,0 @@
package com.aiosman.riderpro.ui.index.tabs.profile.v2
import android.app.Activity
import android.content.Intent
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.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
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.navigationBars
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.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.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.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
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.navigation.NavController
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.ui.NavigationRoute
import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
import com.aiosman.riderpro.ui.composables.MenuItem
import com.aiosman.riderpro.ui.index.tabs.profile.MyProfileViewModel
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun Profile2() {
val model = MyProfileViewModel2
var expanded by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
MyProfileViewModel2.loadProfile()
}
val navController: NavController = LocalNavController.current
val scope = rememberCoroutineScope()
val state = rememberPullRefreshState(MyProfileViewModel2.refreshing, onRefresh = {
MyProfileViewModel2.loadProfile(pullRefresh = true)
})
val context = LocalContext.current
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
val pickBannerImageLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val uri = result.data?.data
uri?.let {
MyProfileViewModel2.updateUserProfileBanner(it, context = context)
}
}
}
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
// 处理滚动事件之前的逻辑
return Offset.Zero
}
override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset {
// 处理滚动事件之后的逻辑
return Offset.Zero
}
}
}
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF5F5F5))
.padding(bottom = with(LocalDensity.current) {
val da = WindowInsets.navigationBars
.getBottom(this)
.toDp() + 48.dp
da
})
.pullRefresh(state)
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.nestedScroll(nestedScrollConnection),
) {
item {
Column(
modifier = Modifier
.fillMaxWidth()
) {
// banner
Box(
modifier = Modifier
.fillMaxWidth()
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(500.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 = MyProfileViewModel2.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
)
}
}
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
)
}
com.aiosman.riderpro.ui.composables.DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
width = 250,
menuItems = listOf(
MenuItem(
stringResource(R.string.logout),
R.mipmap.rider_pro_logout
) {
expanded = false
scope.launch {
MyProfileViewModel2.logout()
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)
}
}
),
)
}
}
}
Spacer(modifier = Modifier.height(32.dp))
// 个人信息
Box(
modifier = Modifier.padding(horizontal = 16.dp)
) {
MyProfileViewModel2.profile?.let {
UserItem(it)
}
}
Spacer(modifier = Modifier.height(16.dp))
MyProfileViewModel2.profile?.let {
Box(
modifier = Modifier.padding(horizontal = 16.dp)
) {
SelfProfileAction {
navController.navigate(NavigationRoute.AccountEdit.route)
}
}
}
Spacer(modifier = Modifier.height(16.dp))
// 动态列表
}
UserContentTab(nestedScrollConnection)
}
}
PullRefreshIndicator(MyProfileViewModel2.refreshing, state, Modifier.align(Alignment.TopCenter))
}
}
@Composable
fun UserItem(accountProfileEntity: AccountProfileEntity) {
Column(
modifier = Modifier
.fillMaxWidth()
) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
// 头像
CustomAsyncImage(
LocalContext.current,
accountProfileEntity.avatar,
modifier = Modifier
.clip(CircleShape)
.size(48.dp),
contentDescription = "",
contentScale = ContentScale.Crop
)
Spacer(modifier = Modifier.width(32.dp))
//个人统计
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.weight(1f)
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.weight(1f)
) {
Text(
text = accountProfileEntity.followerCount.toString(),
fontWeight = FontWeight.W600,
fontSize = 16.sp
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(R.string.followers_upper),
)
}
Column(
verticalArrangement = Arrangement.Center,
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = accountProfileEntity.followingCount.toString(),
fontWeight = FontWeight.W600,
fontSize = 16.sp
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(R.string.following_upper),
)
}
Column(
verticalArrangement = Arrangement.Center,
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
}
}
}
Spacer(modifier = Modifier.height(12.dp))
// 昵称
Text(
text = accountProfileEntity.nickName,
fontWeight = FontWeight.W600,
fontSize = 16.sp,
)
Spacer(modifier = Modifier.height(4.dp))
// 个人简介
Text(
text = accountProfileEntity.bio,
fontSize = 14.sp,
color = Color.Gray
)
}
}
@Composable
fun SelfProfileAction(
onEditProfile: () -> Unit
) {
// 按钮
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.clip(RoundedCornerShape(32.dp))
.background(Color(0xffebebeb))
.padding(horizontal = 16.dp, vertical = 4.dp)
.noRippleClickable {
onEditProfile()
}
) {
Icon(
Icons.Default.Edit,
contentDescription = "",
modifier = Modifier.size(24.dp)
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = stringResource(R.string.edit_profile),
fontSize = 14.sp,
fontWeight = FontWeight.W600,
color = Color.Black,
modifier = Modifier.padding(8.dp)
)
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun UserContentTab(nestedScrollConnection: NestedScrollConnection) {
val pagerState = rememberPagerState(pageCount = { 2 })
val scope = rememberCoroutineScope()
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.clip(RoundedCornerShape(32.dp))
.background(if (pagerState.currentPage == 0) Color(0xFFFFFFFF) else Color.Transparent)
.padding(horizontal = 16.dp, vertical = 4.dp)
.noRippleClickable {
// switch to gallery
scope.launch {
pagerState.scrollToPage(0)
}
}
) {
Text(
text = "Gallery",
fontSize = 14.sp,
fontWeight = FontWeight.W600,
color = Color.Black,
modifier = Modifier.padding(8.dp)
)
}
Spacer(modifier = Modifier.width(4.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.clip(RoundedCornerShape(32.dp))
.background(if (pagerState.currentPage == 1) Color(0xFFFFFFFF) else Color.Transparent)
.padding(horizontal = 16.dp, vertical = 8.dp)
.noRippleClickable {
// switch to moments
scope.launch {
pagerState.scrollToPage(1)
}
}
) {
Text(
text = "Moments",
fontSize = 16.sp,
fontWeight = FontWeight.W600,
color = Color.Black,
modifier = Modifier.padding(8.dp)
)
}
}
Spacer(modifier = Modifier.height(16.dp))
HorizontalPager(
state = pagerState,
modifier = Modifier
.padding(0.dp)
.height(900.dp),
beyondBoundsPageCount = 2,
verticalAlignment = Alignment.Top
) { page ->
when (page) {
0 -> Gallery()
1 -> MomentsList(nestedScrollConnection)
}
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun Gallery() {
val moments = MyProfileViewModel2.sharedFlow.collectAsLazyPagingItems()
val screenWidth = LocalConfiguration.current.screenWidthDp.dp
val itemWidth = (screenWidth / 2) - 16.dp - 2.dp
FlowRow(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
maxItemsInEachRow = 2,
) {
for (idx in 0 until moments.itemCount) {
val moment = moments[idx]
moment?.let {
Box(
modifier = Modifier
.width(itemWidth)
.aspectRatio(1f)
.background(Color.Gray)
) {
CustomAsyncImage(
LocalContext.current,
it.images[0].thumbnail,
modifier = Modifier
.fillMaxSize(),
contentDescription = "",
contentScale = ContentScale.Crop
)
}
}
}
BottomNavigationPlaceholder()
}
}
@Composable
fun MomentsList(nestedScrollConnection: NestedScrollConnection) {
val moments = MyProfileViewModel2.sharedFlow.collectAsLazyPagingItems()
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.height(900.dp).nestedScroll(nestedScrollConnection),
) {
item {
for (idx in 0 until moments.itemCount) {
val moment = moments[idx]
moment?.let {
MomentPostUnit(it)
}
}
}
}
}

View File

@@ -1,134 +0,0 @@
package com.aiosman.riderpro.ui.index.tabs.profile.v2
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
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.setValue
import androidx.compose.ui.Modifier
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.platform.LocalConfiguration
import androidx.compose.ui.unit.dp
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable
fun Profile3() {
Scaffold(
) { it
val screenHeight = LocalConfiguration.current.screenHeightDp.dp
var headHeight by remember { mutableStateOf(400.dp) }
val speedFactor = 2 // 调整速度因子
// val animatedHeadHeight by animateDpAsState(
// targetValue = headHeight,
// animationSpec = tween(durationMillis = 0)
// )
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
val delta = available.y
val newHeight = (headHeight + delta.dp).coerceIn(0.dp, 400.dp)
headHeight = newHeight
return Offset.Zero
}
}
}
LazyColumn(
modifier = Modifier
.fillMaxSize()
.nestedScroll(nestedScrollConnection)
) {
item {
// head
Box(
modifier = Modifier
.fillMaxWidth()
.height(headHeight)
.background(Color.DarkGray)
)
}
item {
LazyColumn(
modifier = Modifier.fillMaxWidth().height(screenHeight),
) {
items(100) { idx ->
Box(
modifier = Modifier
.fillMaxWidth()
.height(64.dp)
.background(Color.Green)
) {
Text("Item $idx")
}
Spacer(modifier = Modifier.height(8.dp))
}
}
}
// item {
// HorizontalPager(
// state = rememberPagerState(pageCount = { 2 }),
// modifier = Modifier
// .fillMaxWidth()
// .height(screenHeight)
// ) { page ->
// when (page) {
// 0 -> {
// LazyVerticalGrid(
// columns = GridCells.Fixed(2),
// verticalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy(8.dp),
// horizontalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy(8.dp),
// modifier = Modifier.fillMaxSize(),
//// userScrollEnabled = headHeight < 2.dp
// ) {
// items(100) { idx ->
// Box(
// modifier = Modifier
// .fillMaxWidth()
// .height(64.dp)
// ) {
// Text("Item $idx")
// }
// }
// }
// }
// 1 -> {
// LazyColumn(
// modifier = Modifier.fillMaxSize(),
//// userScrollEnabled = headHeight < 2.dp
// ) {
// items(100) { idx ->
// Box(
// modifier = Modifier
// .fillMaxWidth()
// .height(64.dp)
// .background(Color.Green)
// ) {
// Text("Item $idx")
// }
// Spacer(modifier = Modifier.height(8.dp))
// }
// }
// }
// }
// }
// }
}
}
}

View File

@@ -1,134 +0,0 @@
package com.aiosman.riderpro.ui.index.tabs.profile.v2
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
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.setValue
import androidx.compose.ui.Modifier
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.platform.LocalConfiguration
import androidx.compose.ui.unit.dp
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable
fun Profile4() {
Scaffold(
) { it
val screenHeight = LocalConfiguration.current.screenHeightDp.dp
var headHeight by remember { mutableStateOf(400.dp) }
val speedFactor = 2 // 调整速度因子
// val animatedHeadHeight by animateDpAsState(
// targetValue = headHeight,
// animationSpec = tween(durationMillis = 0)
// )
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
val delta = available.y
val newHeight = (headHeight + delta.dp).coerceIn(0.dp, 400.dp)
headHeight = newHeight
return Offset.Zero
}
}
}
LazyColumn(
modifier = Modifier
.fillMaxSize()
.nestedScroll(nestedScrollConnection)
) {
item {
// head
Box(
modifier = Modifier
.fillMaxWidth()
.height(headHeight)
.background(Color.DarkGray)
)
}
item {
LazyColumn(
modifier = Modifier.fillMaxWidth().height(screenHeight),
) {
items(100) { idx ->
Box(
modifier = Modifier
.fillMaxWidth()
.height(64.dp)
.background(Color.Green)
) {
Text("Item $idx")
}
Spacer(modifier = Modifier.height(8.dp))
}
}
}
// item {
// HorizontalPager(
// state = rememberPagerState(pageCount = { 2 }),
// modifier = Modifier
// .fillMaxWidth()
// .height(screenHeight)
// ) { page ->
// when (page) {
// 0 -> {
// LazyVerticalGrid(
// columns = GridCells.Fixed(2),
// verticalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy(8.dp),
// horizontalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy(8.dp),
// modifier = Modifier.fillMaxSize(),
//// userScrollEnabled = headHeight < 2.dp
// ) {
// items(100) { idx ->
// Box(
// modifier = Modifier
// .fillMaxWidth()
// .height(64.dp)
// ) {
// Text("Item $idx")
// }
// }
// }
// }
// 1 -> {
// LazyColumn(
// modifier = Modifier.fillMaxSize(),
//// userScrollEnabled = headHeight < 2.dp
// ) {
// items(100) { idx ->
// Box(
// modifier = Modifier
// .fillMaxWidth()
// .height(64.dp)
// .background(Color.Green)
// ) {
// Text("Item $idx")
// }
// Spacer(modifier = Modifier.height(8.dp))
// }
// }
// }
// }
// }
// }
}
}
}

View File

@@ -1,879 +0,0 @@
package com.aiosman.riderpro.ui.index.tabs.profile.v2
import android.app.Activity
import android.content.Intent
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Canvas
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.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
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.navigationBars
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.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PathEffect
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import com.aiosman.riderpro.AppState
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.exp.formatPostTime2
import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
import com.aiosman.riderpro.ui.composables.MenuItem
import com.aiosman.riderpro.ui.index.tabs.profile.MyProfileViewModel
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import com.aiosman.riderpro.ui.navigateToChat
import com.aiosman.riderpro.ui.navigateToPost
import com.aiosman.riderpro.ui.post.NewPostViewModel
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ProfilePage() {
val model = MyProfileViewModel2
var expanded by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
MyProfileViewModel.loadProfile()
}
val navController: NavController = LocalNavController.current
val scope = rememberCoroutineScope()
val state = rememberPullRefreshState(MyProfileViewModel.refreshing, onRefresh = {
MyProfileViewModel.loadProfile(pullRefresh = true)
})
val context = LocalContext.current
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues()
val pickBannerImageLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val uri = result.data?.data
uri?.let {
MyProfileViewModel.updateUserProfileBanner(it, context = context)
}
}
}
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF5F5F5))
.padding(bottom = with(LocalDensity.current) {
val da = WindowInsets.navigationBars
.getBottom(this)
.toDp() + 48.dp
da
})
.pullRefresh(state)
) {
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item {
Box(
modifier = Modifier
.fillMaxWidth()
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(400.dp)
.noRippleClickable {
Intent(Intent.ACTION_PICK).apply {
type = "image/*"
pickBannerImageLauncher.launch(this)
}
}
) {
val banner = MyProfileViewModel.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
)
}
}
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
)
}
com.aiosman.riderpro.ui.composables.DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
width = 250,
menuItems = listOf(
MenuItem(
stringResource(R.string.logout),
R.mipmap.rider_pro_logout
) {
expanded = false
scope.launch {
MyProfileViewModel.logout()
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)
}
}
),
)
}
}
Spacer(modifier = Modifier.height(32.dp))
MyProfileViewModel.profile?.let {
UserInformation(
accountProfileEntity = it,
onEditProfileClick = {
navController.navigate(NavigationRoute.AccountEdit.route)
}
)
}
// if (moments.itemCount == 0) {
// EmptyMomentPostUnit()
// }
}
// items(moments.itemCount) { idx ->
// val momentItem = moments[idx] ?: return@items
// MomentPostUnit(momentItem)
// }
item {
Spacer(modifier = Modifier.height(48.dp))
}
}
PullRefreshIndicator(MyProfileViewModel.refreshing, state, Modifier.align(Alignment.TopCenter))
}
}
@Composable
fun CarGroup() {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(top = 54.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
CarTopInformation()
CarTopPicture()
}
}
@Composable
fun CarTopInformation() {
Row {
Text(
text = "BMW",
color = Color.Black,
fontSize = 12.sp,
style = TextStyle(fontWeight = FontWeight.Bold)
)
Text(
modifier = Modifier.padding(start = 4.dp),
text = "/",
color = Color.Gray,
fontSize = 12.sp
)
Text(
modifier = Modifier.padding(start = 4.dp),
text = "M1000RR",
color = Color.Gray,
fontSize = 12.sp
)
}
}
@Composable
fun CarTopPicture() {
Image(
modifier = Modifier
.size(width = 336.dp, height = 224.dp)
.padding(top = 42.dp),
painter = painterResource(id = R.drawable.default_profile_moto), contentDescription = ""
)
}
@Composable
fun UserInformation(
isSelf: Boolean = true,
accountProfileEntity: AccountProfileEntity,
onFollowClick: () -> Unit = {},
onEditProfileClick: () -> Unit = {}
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp, start = 33.dp, end = 33.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Row(modifier = Modifier.fillMaxWidth()) {
val userInfoModifier = Modifier.weight(1f)
UserInformationFollowers(userInfoModifier, accountProfileEntity)
UserInformationBasic(userInfoModifier, accountProfileEntity)
UserInformationFollowing(userInfoModifier, accountProfileEntity)
}
UserInformationSlogan(accountProfileEntity)
CommunicationOperatorGroup(
isSelf = isSelf,
isFollowing = accountProfileEntity.isFollowing,
onFollowClick = onFollowClick,
onEditProfileClick = onEditProfileClick,
accountProfileEntity = accountProfileEntity
)
}
}
@Composable
fun UserInformationFollowers(modifier: Modifier, accountProfileEntity: AccountProfileEntity) {
val navController = LocalNavController.current
Column(modifier = modifier.padding(top = 31.dp)) {
Text(
modifier = Modifier
.padding(bottom = 5.dp)
.noRippleClickable {
navController.navigate(
NavigationRoute.FollowerList.route.replace(
"{id}",
accountProfileEntity.id.toString()
)
)
},
text = accountProfileEntity.followerCount.toString(),
fontSize = 24.sp,
color = Color.Black,
style = TextStyle(fontStyle = FontStyle.Italic, fontWeight = FontWeight.Bold)
)
Canvas(
modifier = Modifier
.size(width = 88.83.dp, height = 1.dp)
.padding(top = 2.dp, bottom = 5.dp)
) {
drawLine(
color = Color(0xFFCCCCCC),
start = Offset(0f, 0f),
end = Offset(size.width, 0f),
strokeWidth = 1f,
pathEffect = PathEffect.dashPathEffect(floatArrayOf(20f, 20f), 0f)
)
}
Text(
modifier = Modifier.padding(top = 5.dp),
text = stringResource(R.string.followers_upper),
fontSize = 12.sp,
color = Color.Black,
style = TextStyle(fontWeight = FontWeight.Bold)
)
}
}
@Composable
fun UserInformationBasic(modifier: Modifier, accountProfileEntity: AccountProfileEntity) {
val context = LocalContext.current
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier.size(width = 112.dp, height = 112.dp),
contentAlignment = Alignment.Center
) {
Image(
modifier = Modifier.fillMaxSize(),
painter = painterResource(id = R.drawable.avatar_bold), contentDescription = ""
)
CustomAsyncImage(
context,
accountProfileEntity.avatar,
modifier = Modifier
.size(width = 88.dp, height = 88.dp)
.clip(
RoundedCornerShape(88.dp)
),
contentDescription = "",
contentScale = ContentScale.Crop
)
}
Text(
modifier = Modifier
.widthIn(max = 220.dp)
.padding(top = 8.dp),
text = accountProfileEntity.nickName,
fontSize = 32.sp,
color = Color.Black,
style = TextStyle(fontWeight = FontWeight.Bold),
textAlign = TextAlign.Center
)
Text(
modifier = Modifier.padding(top = 4.dp),
text = accountProfileEntity.country,
fontSize = 12.sp,
color = Color.Gray
)
}
}
@Composable
fun UserInformationFollowing(modifier: Modifier, accountProfileEntity: AccountProfileEntity) {
val navController = LocalNavController.current
Column(
modifier = modifier.padding(top = 6.dp),
horizontalAlignment = Alignment.End
) {
Text(
modifier = Modifier
.padding(bottom = 5.dp)
.noRippleClickable {
navController.navigate(
NavigationRoute.FollowingList.route.replace(
"{id}",
accountProfileEntity.id.toString()
)
)
},
text = accountProfileEntity.followingCount.toString(),
fontSize = 24.sp,
color = Color.Black,
style = TextStyle(fontStyle = FontStyle.Italic, fontWeight = FontWeight.Bold)
)
Canvas(
modifier = Modifier
.size(width = 88.83.dp, height = 1.dp)
.padding(top = 2.dp, bottom = 5.dp)
) {
drawLine(
color = Color(0xFFCCCCCC),
start = Offset(0f, 0f),
end = Offset(size.width, 0f),
strokeWidth = 1f,
pathEffect = PathEffect.dashPathEffect(floatArrayOf(20f, 20f), 0f)
)
}
Text(
modifier = Modifier.padding(top = 5.dp),
text = stringResource(R.string.following_upper),
fontSize = 12.sp,
color = Color.Black,
style = TextStyle(fontWeight = FontWeight.Bold)
)
}
}
@Composable
fun UserInformationSlogan(accountProfileEntity: AccountProfileEntity) {
Text(
modifier = Modifier.padding(top = 23.dp),
text = accountProfileEntity.bio,
fontSize = 13.sp,
color = Color.Black,
style = TextStyle(fontWeight = FontWeight.Bold)
)
}
@Composable
fun CommunicationOperatorGroup(
accountProfileEntity: AccountProfileEntity,
isSelf: Boolean = true,
isFollowing: Boolean = false,
onFollowClick: () -> Unit = {},
onEditProfileClick: () -> Unit = {}
) {
val navController = LocalNavController.current
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp), horizontalArrangement = Arrangement.Center
) {
if (!isSelf && AppState.UserId != accountProfileEntity.id) {
Box(
modifier = Modifier
.size(width = 142.dp, height = 40.dp)
.noRippleClickable {
onFollowClick()
},
contentAlignment = Alignment.Center
) {
Image(
modifier = Modifier.fillMaxSize(),
painter = if (isFollowing) painterResource(id = R.mipmap.rider_pro_follow_grey) else painterResource(
id = R.mipmap.rider_pro_follow_red
),
contentDescription = ""
)
Text(
text = if (isFollowing) stringResource(R.string.following_upper) else stringResource(
R.string.follow_upper
),
fontSize = 14.sp,
color = if (isFollowing) Color.Black else Color.White,
style = TextStyle(fontWeight = FontWeight.W600, fontStyle = FontStyle.Italic),
)
}
Spacer(modifier = Modifier.width(16.dp))
Box(
modifier = Modifier
.size(width = 142.dp, height = 40.dp)
.noRippleClickable {
navController.navigateToChat(accountProfileEntity.id.toString())
},
contentAlignment = Alignment.Center
) {
Image(
modifier = Modifier.fillMaxSize(),
painter = painterResource(id = R.mipmap.rider_pro_btn_bg_grey),
contentDescription = ""
)
Text(
text = "CHAT",
fontSize = 14.sp,
color = Color.Black,
fontWeight = FontWeight.Bold,
style = TextStyle(fontWeight = FontWeight.Bold, fontStyle = FontStyle.Italic)
)
}
}
if (isSelf) {
Box(
modifier = Modifier
.size(width = 142.dp, height = 40.dp)
.noRippleClickable {
onEditProfileClick()
},
contentAlignment = Alignment.Center
) {
Image(
modifier = Modifier.fillMaxSize(),
painter = painterResource(id = R.mipmap.rider_pro_btn_bg_grey),
contentDescription = ""
)
Text(
text = stringResource(R.string.edit_profile),
fontSize = 14.sp,
color = Color.Black,
fontWeight = FontWeight.Bold,
style = TextStyle(fontWeight = FontWeight.Bold, fontStyle = FontStyle.Italic)
)
}
}
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun RidingStyle() {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(start = 24.dp, top = 40.dp, end = 24.dp),
horizontalAlignment = Alignment.Start
) {
Text(
text = "RIDING STYLES",
fontSize = 18.sp,
color = Color.Black,
style = TextStyle(fontWeight = FontWeight.Bold)
)
Image(
modifier = Modifier
.padding(top = 4.dp)
.height(8.dp),
painter = painterResource(id = R.drawable.rider_pro_profile_line),
contentDescription = ""
)
FlowRow(
modifier = Modifier
.fillMaxWidth()
.padding(top = 24.dp)
) {
RidingStyleItem(styleContent = "Cruiser")
RidingStyleItem(styleContent = "Bobber")
RidingStyleItem(styleContent = "Cafe")
RidingStyleItem(styleContent = "Chopper")
RidingStyleItem(styleContent = "Sport")
RidingStyleItem(styleContent = "Vintage")
RidingStyleItem(styleContent = "Trike")
RidingStyleItem(styleContent = "Touring")
}
}
}
@Composable
fun RidingStyleItem(styleContent: String) {
Box(
modifier = Modifier.padding(bottom = 8.dp, end = 8.dp),
contentAlignment = Alignment.Center
) {
Image(
modifier = Modifier.shadow(
ambientColor = Color.Gray,
spotColor = Color(0f, 0f, 0f, 0.2f),
elevation = 20.dp,
),
painter = painterResource(id = R.drawable.rider_pro_style_wrapper),
contentDescription = ""
)
Text(
modifier = Modifier.padding(start = 5.dp, end = 5.dp),
text = styleContent,
fontSize = 12.sp,
color = Color.Gray,
style = TextStyle(fontWeight = FontWeight.Bold)
)
}
}
@Composable
fun EmptyMomentPostUnit() {
TimeGroup(stringResource(R.string.empty_my_post_title))
ProfileEmptyMomentCard()
}
@Composable
fun ProfileEmptyMomentCard(
) {
var columnHeight by remember { mutableStateOf(0) }
val navController = LocalNavController.current
Column(
modifier = Modifier
.fillMaxWidth()
.padding(start = 24.dp, top = 18.dp, end = 24.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
) {
Canvas(
modifier = Modifier
.height(with(LocalDensity.current) { columnHeight.toDp() })
.width(14.dp)
) {
drawLine(
color = Color(0xff899DA9),
start = Offset(0f, 0f),
end = Offset(0f, size.height),
strokeWidth = 4f,
pathEffect = PathEffect.dashPathEffect(floatArrayOf(20f, 20f), 0f)
)
}
Spacer(modifier = Modifier.width(10.dp))
Column(
modifier = Modifier
.weight(1f)
.onGloballyPositioned { coordinates ->
columnHeight = coordinates.size.height
}
) {
Text(stringResource(R.string.empty_my_post_content), fontSize = 16.sp)
Spacer(modifier = Modifier.height(24.dp))
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(3f / 2f)
.background(Color.White)
.padding(16.dp)
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF5F5F5))
.noRippleClickable {
NewPostViewModel.asNewPost()
navController.navigate(NavigationRoute.NewPost.route)
}
) {
Icon(
Icons.Default.Add,
tint = Color(0xFFD8D8D8),
contentDescription = "New post",
modifier = Modifier
.size(32.dp)
.align(Alignment.Center)
)
}
}
}
}
}
}
@Composable
fun MomentPostUnit(momentEntity: MomentEntity) {
TimeGroup(momentEntity.time.formatPostTime2())
ProfileMomentCard(
momentEntity.momentTextContent,
momentEntity.images[0].thumbnail,
momentEntity.likeCount.toString(),
momentEntity.commentCount.toString(),
momentEntity = momentEntity
)
}
@Composable
fun TimeGroup(time: String = "2024.06.08 12:23") {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 24.dp, top = 40.dp, end = 24.dp),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
Image(
modifier = Modifier
.height(16.dp)
.width(14.dp),
painter = painterResource(id = R.drawable.rider_pro_moment_time_flag),
contentDescription = ""
)
Spacer(modifier = Modifier.width(12.dp))
Text(
text = time,
fontSize = 16.sp,
color = Color.Black,
style = TextStyle(fontWeight = FontWeight.W600)
)
}
}
@Composable
fun ProfileMomentCard(
content: String,
imageUrl: String,
like: String,
comment: String,
momentEntity: MomentEntity
) {
var columnHeight by remember { mutableStateOf(0) }
Column(
modifier = Modifier
.fillMaxWidth()
.padding(start = 24.dp, top = 18.dp, end = 24.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
) {
Canvas(
modifier = Modifier
.height(with(LocalDensity.current) { columnHeight.toDp() })
.width(14.dp)
) {
drawLine(
color = Color(0xff899DA9),
start = Offset(0f, 0f),
end = Offset(0f, size.height),
strokeWidth = 4f,
pathEffect = PathEffect.dashPathEffect(floatArrayOf(20f, 20f), 0f)
)
}
Spacer(modifier = Modifier.width(10.dp))
Column(
modifier = Modifier
.background(Color.White)
.weight(1f)
.onGloballyPositioned { coordinates ->
columnHeight = coordinates.size.height
}
) {
if (content.isNotEmpty()) {
MomentCardTopContent(content)
}
MomentCardPicture(imageUrl, momentEntity = momentEntity)
MomentCardOperation(like, comment)
}
}
}
}
@Composable
fun MomentCardTopContent(content: String) {
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
Text(
modifier = Modifier.padding(top = 16.dp, bottom = 0.dp, start = 16.dp, end = 16.dp),
text = content, fontSize = 16.sp, color = Color.Black
)
}
}
@Composable
fun MomentCardPicture(imageUrl: String, momentEntity: MomentEntity) {
val navController = LocalNavController.current
val context = LocalContext.current
CustomAsyncImage(
context,
imageUrl,
modifier = Modifier
.fillMaxSize()
.aspectRatio(3f / 2f)
.padding(top = 16.dp)
.noRippleClickable {
navController.navigateToPost(
id = momentEntity.id,
highlightCommentId = 0,
initImagePagerIndex = 0
)
},
contentDescription = "",
contentScale = ContentScale.Crop
)
}
@Composable
fun MomentCardOperation(like: String, comment: String) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(48.dp),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically
) {
// Spacer(modifier = Modifier.weight(1f))
MomentCardOperationItem(
drawable = R.drawable.rider_pro_like,
number = like,
modifier = Modifier.padding(end = 32.dp)
)
MomentCardOperationItem(
drawable = R.drawable.rider_pro_moment_comment,
number = comment,
modifier = Modifier.padding(end = 32.dp)
)
}
}
@Composable
fun MomentCardOperationItem(@DrawableRes drawable: Int, number: String, modifier: Modifier) {
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically
) {
Image(
modifier = Modifier.padding(start = 16.dp, end = 8.dp),
painter = painterResource(id = drawable), contentDescription = ""
)
Text(text = number)
}
}

View File

@@ -38,6 +38,7 @@ import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
import com.aiosman.riderpro.ui.composables.ActionButton import com.aiosman.riderpro.ui.composables.ActionButton
import com.aiosman.riderpro.ui.composables.CheckboxWithLabel import com.aiosman.riderpro.ui.composables.CheckboxWithLabel
import com.aiosman.riderpro.ui.composables.PolicyCheckbox
import com.aiosman.riderpro.ui.composables.StatusBarSpacer import com.aiosman.riderpro.ui.composables.StatusBarSpacer
import com.aiosman.riderpro.ui.composables.TextInputField import com.aiosman.riderpro.ui.composables.TextInputField
import com.aiosman.riderpro.utils.Utils import com.aiosman.riderpro.utils.Utils
@@ -241,15 +242,21 @@ fun EmailSignupScreen() {
rememberMe = it rememberMe = it
} }
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
CheckboxWithLabel( PolicyCheckbox(
checked = acceptTerms, checked = acceptTerms,
checkSize = 16,
fontSize = 12,
label = stringResource(R.string.agree_terms_of_service),
error = termsError error = termsError
) { ) {
acceptTerms = it acceptTerms = it
} }
// CheckboxWithLabel(
// checked = acceptTerms,
// checkSize = 16,
// fontSize = 12,
// label = stringResource(R.string.agree_terms_of_service),
// error = termsError
// ) {
// acceptTerms = it
// }
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
CheckboxWithLabel( CheckboxWithLabel(
checked = acceptPromotions, checked = acceptPromotions,
@@ -263,6 +270,10 @@ fun EmailSignupScreen() {
} }
Spacer(modifier = Modifier.height(64.dp)) Spacer(modifier = Modifier.height(64.dp))
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
ActionButton( ActionButton(
modifier = Modifier modifier = Modifier
.width(345.dp) .width(345.dp)
@@ -278,4 +289,6 @@ fun EmailSignupScreen() {
} }
}
} }

View File

@@ -1,155 +0,0 @@
package com.aiosman.riderpro.ui.profile
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.Row
import androidx.compose.foundation.layout.Spacer
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.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.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.paging.compose.collectAsLazyPagingItems
import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.R
import com.aiosman.riderpro.exp.viewModelFactory
import com.aiosman.riderpro.ui.composables.CustomAsyncImage
import com.aiosman.riderpro.ui.composables.StatusBarSpacer
import com.aiosman.riderpro.ui.index.tabs.profile.v2.MomentPostUnit
import com.aiosman.riderpro.ui.index.tabs.profile.v2.UserInformation
import com.aiosman.riderpro.ui.modifiers.noRippleClickable
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun AccountProfile(id: String) {
val model: AccountProfileViewModel = viewModel(factory = viewModelFactory {
AccountProfileViewModel()
}, key = "viewModel_${id}")
val scope = rememberCoroutineScope()
val items = model.momentsFlow.collectAsLazyPagingItems()
val state = rememberPullRefreshState(model.refreshing, onRefresh = {
model.loadProfile(id, pullRefresh = true)
})
val navController = LocalNavController.current
LaunchedEffect(Unit) {
model.loadProfile(id)
}
Box(
modifier = Modifier
.fillMaxSize()
.pullRefresh(state)
.background(Color(0xFFF5F5F5))
) {
LazyColumn(
modifier = Modifier
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top,
) {
item {
Box(
modifier = Modifier
.fillMaxWidth()
.height(400.dp)
) {
if (model.profile != null) {
val banner = model.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
)
}
}
Box(
modifier = Modifier.fillMaxWidth()
) {
Column(
modifier = Modifier.fillMaxWidth()
) {
StatusBarSpacer()
Row(
modifier = Modifier.padding(vertical = 10.dp, horizontal = 18.dp)
) {
Image(
painter = painterResource(id = R.drawable.rider_pro_nav_back),
contentDescription = "",
modifier = Modifier
.size(24.dp)
.noRippleClickable {
navController.popBackStack()
},
colorFilter = ColorFilter.tint(Color.White)
)
}
}
}
}
Spacer(modifier = Modifier.height(32.dp))
model.profile?.let {
UserInformation(
isSelf = false,
accountProfileEntity = it,
onFollowClick = {
scope.launch {
if (it.isFollowing) {
model.unFollowUser(id)
} else {
model.followUser(id)
}
}
},
)
}
// RidingStyle()
}
items(items.itemCount) { idx ->
val momentItem = items[idx] ?: return@items
MomentPostUnit(momentItem)
}
item {
Spacer(modifier = Modifier.height(32.dp))
}
}
PullRefreshIndicator(model.refreshing, state, Modifier.align(Alignment.TopCenter))
}
}

View File

@@ -0,0 +1,39 @@
package com.aiosman.riderpro.ui.profile
import androidx.compose.runtime.Composable
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.navigateToChat
@Composable
fun AccountProfileV2(id: String){
val model: AccountProfileViewModel = viewModel(factory = viewModelFactory {
AccountProfileViewModel()
}, key = "viewModel_${id}")
val navController = LocalNavController.current
LaunchedEffect(Unit) {
model.loadProfile(id)
}
Profile(
sharedFlow = model.momentsFlow,
profile = model.profile,
isSelf = false,
onChatClick = {
model.profile?.let {
navController.navigateToChat(it.id.toString())
}
},
onFollowClick = {
model.profile?.let {
if (it.isFollowing) {
model.unFollowUser(id)
} else {
model.followUser(id)
}
}
}
)
}

View File

@@ -23,6 +23,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class AccountProfileViewModel : ViewModel() { class AccountProfileViewModel : ViewModel() {
var profileId by mutableStateOf(0) var profileId by mutableStateOf(0)
val accountService: AccountService = AccountServiceImpl() val accountService: AccountService = AccountServiceImpl()