新增关注和粉丝列表

- 新增关注和粉丝列表页面
- 新增关注和粉丝列表ViewModel
- 更新UserService以支持获取关注和粉丝列表

- 更新RiderProAPI以支持获取关注和粉丝列表
- 更新Profile页面以支持跳转到关注和粉丝列表页面
- 更新Navi以支持关注和粉丝列表页面导航
- 更新UserInformationFollowers和UserInformationFollowing以支持跳转到关注和粉丝列表页面
- 更新MyProfileViewModel
以支持更新用户资料横幅
This commit is contained in:
2024-09-06 01:55:12 +08:00
parent 37dcd19227
commit e936f9cb77
12 changed files with 412 additions and 46 deletions

View File

@@ -36,12 +36,16 @@ interface UserService {
* @param pageSize 分页大小 * @param pageSize 分页大小
* @param page 页码 * @param page 页码
* @param nickname 昵称搜索 * @param nickname 昵称搜索
* @param followerId 粉丝ID,账号粉丝
* @param followingId 关注ID,账号关注
* @return 用户列表 * @return 用户列表
*/ */
suspend fun getUsers( suspend fun getUsers(
pageSize: Int = 20, pageSize: Int = 20,
page: Int = 1, page: Int = 1,
nickname: String? = null nickname: String? = null,
followerId: Int? = null,
followingId: Int? = null
): ListContainer<AccountProfileEntity> ): ListContainer<AccountProfileEntity>
} }
@@ -66,15 +70,23 @@ class UserServiceImpl : UserService {
override suspend fun getUsers( override suspend fun getUsers(
pageSize: Int, pageSize: Int,
page: Int, page: Int,
nickname: String? nickname: String?,
followerId: Int?,
followingId: Int?
): ListContainer<AccountProfileEntity> { ): ListContainer<AccountProfileEntity> {
val resp = ApiClient.api.getUsers(page, pageSize, nickname) val resp = ApiClient.api.getUsers(
page = page,
pageSize = pageSize,
search = nickname,
followerId = followerId,
followingId = followingId
)
val body = resp.body() ?: throw ServiceException("Failed to get account") val body = resp.body() ?: throw ServiceException("Failed to get account")
return ListContainer<AccountProfileEntity>( return ListContainer<AccountProfileEntity>(
list = body.list.map { it.toAccountProfileEntity() }, list = body.list.map { it.toAccountProfileEntity() },
page = body.page, page = body.page,
total = body.total, total = body.total,
pageSize = body.pageSize pageSize = body.pageSize,
) )
} }
} }

View File

@@ -85,6 +85,7 @@ data class RegisterMessageChannelRequestBody(
@SerializedName("identifier") @SerializedName("identifier")
val identifier: String, val identifier: 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>
@@ -243,6 +244,8 @@ interface RiderProAPI {
@Query("page") page: Int = 1, @Query("page") page: Int = 1,
@Query("pageSize") pageSize: Int = 20, @Query("pageSize") pageSize: Int = 20,
@Query("nickname") search: String? = null, @Query("nickname") search: String? = null,
@Query("followerId") followerId: Int? = null,
@Query("followingId") followingId: Int? = null,
): Response<ListContainer<AccountProfile>> ): Response<ListContainer<AccountProfile>>
@POST("register/google") @POST("register/google")

View File

@@ -10,14 +10,18 @@ import java.io.IOException
*/ */
class AccountPagingSource( class AccountPagingSource(
private val userService: UserService, private val userService: UserService,
private val nickname: String? = null private val nickname: String? = null,
private val followerId: Int? = null,
private val followingId: Int? = null
) : PagingSource<Int, AccountProfileEntity>() { ) : PagingSource<Int, AccountProfileEntity>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AccountProfileEntity> { override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AccountProfileEntity> {
return try { return try {
val currentPage = params.key ?: 1 val currentPage = params.key ?: 1
val users = userService.getUsers( val users = userService.getUsers(
page = currentPage, page = currentPage,
nickname = nickname nickname = nickname,
followerId = followerId,
followingId = followingId
) )
LoadResult.Page( LoadResult.Page(
data = users.list, data = users.list,

View File

@@ -27,11 +27,12 @@ import androidx.navigation.navArgument
import com.aiosman.riderpro.LocalAnimatedContentScope import com.aiosman.riderpro.LocalAnimatedContentScope
import com.aiosman.riderpro.LocalNavController import com.aiosman.riderpro.LocalNavController
import com.aiosman.riderpro.LocalSharedTransitionScope import com.aiosman.riderpro.LocalSharedTransitionScope
import com.aiosman.riderpro.ui.account.AccountEditScreen
import com.aiosman.riderpro.ui.account.AccountEditScreen2 import com.aiosman.riderpro.ui.account.AccountEditScreen2
import com.aiosman.riderpro.ui.comment.CommentsScreen import com.aiosman.riderpro.ui.comment.CommentsScreen
import com.aiosman.riderpro.ui.favourite.FavouriteScreen import com.aiosman.riderpro.ui.favourite.FavouriteScreen
import com.aiosman.riderpro.ui.follower.FollowerScreen import com.aiosman.riderpro.ui.follower.FollowerListScreen
import com.aiosman.riderpro.ui.follower.FollowerNotificationScreen
import com.aiosman.riderpro.ui.follower.FollowingListScreen
import com.aiosman.riderpro.ui.gallery.OfficialGalleryScreen import com.aiosman.riderpro.ui.gallery.OfficialGalleryScreen
import com.aiosman.riderpro.ui.gallery.OfficialPhotographerScreen import com.aiosman.riderpro.ui.gallery.OfficialPhotographerScreen
import com.aiosman.riderpro.ui.gallery.ProfileTimelineScreen import com.aiosman.riderpro.ui.gallery.ProfileTimelineScreen
@@ -77,6 +78,8 @@ sealed class NavigationRoute(
data object FavouritesScreen : NavigationRoute("FavouritesScreen") data object FavouritesScreen : NavigationRoute("FavouritesScreen")
data object NewPostImageGrid : NavigationRoute("NewPostImageGrid") data object NewPostImageGrid : NavigationRoute("NewPostImageGrid")
data object Search : NavigationRoute("Search") data object Search : NavigationRoute("Search")
data object FollowerList : NavigationRoute("FollowerList/{id}")
data object FollowingList : NavigationRoute("FollowingList/{id}")
} }
@@ -158,7 +161,7 @@ fun NavigationController(
LikeScreen() LikeScreen()
} }
composable(route = NavigationRoute.Followers.route) { composable(route = NavigationRoute.Followers.route) {
FollowerScreen() FollowerNotificationScreen()
} }
composable( composable(
route = NavigationRoute.NewPost.route, route = NavigationRoute.NewPost.route,
@@ -228,6 +231,27 @@ fun NavigationController(
SearchScreen() SearchScreen()
} }
} }
composable(
route = NavigationRoute.FollowerList.route,
arguments = listOf(navArgument("id") { type = NavType.IntType })
) {
CompositionLocalProvider(
LocalAnimatedContentScope provides this,
) {
FollowerListScreen(it.arguments?.getInt("id")!!)
}
}
composable(
route = NavigationRoute.FollowingList.route,
arguments = listOf(navArgument("id") { type = NavType.IntType })
) {
CompositionLocalProvider(
LocalAnimatedContentScope provides this,
) {
FollowingListScreen(it.arguments?.getInt("id")!!)
}
}
} }

View File

@@ -0,0 +1,57 @@
package com.aiosman.riderpro.ui.follower
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.paging.compose.collectAsLazyPagingItems
import com.aiosman.riderpro.R
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import kotlinx.coroutines.launch
@Composable
fun FollowerListScreen(userId: Int) {
val model = FollowerListViewModel
val scope = rememberCoroutineScope()
LaunchedEffect(Unit) {
model.loadData(userId)
}
StatusBarMaskLayout(
modifier = Modifier.padding(horizontal = 16.dp)
) {
var dataFlow = model.usersFlow
var users = dataFlow.collectAsLazyPagingItems()
Box(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp)
) {
NoticeScreenHeader(stringResource(R.string.followers_upper), moreIcon = false)
}
LazyColumn(
modifier = Modifier.weight(1f)
) {
items(users.itemCount) { index ->
users[index]?.let { user ->
FollowItem(
avatar = user.avatar,
nickname = user.nickName,
userId = user.id,
isFollowing = user.isFollowing
) {
scope.launch {
model.followUser(user.id)
}
}
}
}
}
}
}

View File

@@ -0,0 +1,63 @@
package com.aiosman.riderpro.ui.follower
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 androidx.paging.map
import com.aiosman.riderpro.data.UserServiceImpl
import com.aiosman.riderpro.entity.AccountPagingSource
import com.aiosman.riderpro.entity.AccountProfileEntity
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
object FollowerListViewModel : ViewModel() {
private val userService = UserServiceImpl()
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) {
return
}
userId = id
viewModelScope.launch {
Pager(
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
pagingSourceFactory = {
AccountPagingSource(
userService,
followerId = id
)
}
).flow.cachedIn(viewModelScope).collectLatest {
_usersFlow.value = it
}
}
}
private fun updateIsFollow(id: Int) {
val currentPagingData = usersFlow.value
val updatedPagingData = currentPagingData.map { user ->
if (user.id == id) {
user.copy(isFollowing = true)
} else {
user
}
}
_usersFlow.value = updatedPagingData
}
suspend fun followUser(userId: Int) {
userService.followUser(userId.toString())
updateIsFollow(userId)
}
}

View File

@@ -24,7 +24,7 @@ import kotlinx.coroutines.launch
/** /**
* 关注消息列表的 ViewModel * 关注消息列表的 ViewModel
*/ */
object FollowerViewModel : ViewModel() { object FollowerNoticeViewModel : ViewModel() {
private val accountService: AccountService = AccountServiceImpl() private val accountService: AccountService = AccountServiceImpl()
private val userService: UserService = UserServiceImpl() private val userService: UserService = UserServiceImpl()
private val _followerItemsFlow = private val _followerItemsFlow =

View File

@@ -22,13 +22,11 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.paging.compose.collectAsLazyPagingItems 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.data.AccountFollow
import com.aiosman.riderpro.ui.NavigationRoute import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
@@ -40,19 +38,18 @@ import kotlinx.coroutines.launch
* 关注消息列表 * 关注消息列表
*/ */
@Composable @Composable
fun FollowerScreen() { fun FollowerNotificationScreen() {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
StatusBarMaskLayout( StatusBarMaskLayout(
modifier = Modifier.padding(horizontal = 16.dp) modifier = Modifier.padding(horizontal = 16.dp)
) { ) {
val model = FollowerViewModel val model = FollowerNoticeViewModel
var dataFlow = model.followerItemsFlow var dataFlow = model.followerItemsFlow
var followers = dataFlow.collectAsLazyPagingItems() var followers = dataFlow.collectAsLazyPagingItems()
Box( Box(
modifier = Modifier.fillMaxWidth().padding(vertical = 16.dp) modifier = Modifier.fillMaxWidth().padding(vertical = 16.dp)
) { ) {
NoticeScreenHeader(stringResource(R.string.followers_upper), moreIcon = false) NoticeScreenHeader(stringResource(R.string.followers_upper), moreIcon = false)
} }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
model.updateNotice() model.updateNotice()
@@ -62,7 +59,12 @@ fun FollowerScreen() {
) { ) {
items(followers.itemCount) { index -> items(followers.itemCount) { index ->
followers[index]?.let { follower -> followers[index]?.let { follower ->
FollowerItem(follower) { FollowItem(
avatar = follower.avatar,
nickname = follower.nickname,
userId = follower.userId,
isFollowing = follower.isFollowing
) {
scope.launch { scope.launch {
model.followUser(follower.userId) model.followUser(follower.userId)
} }
@@ -75,8 +77,11 @@ fun FollowerScreen() {
@Composable @Composable
fun FollowerItem( fun FollowItem(
item: AccountFollow, avatar: String,
nickname: String,
userId: Int,
isFollowing: Boolean,
onFollow: () -> Unit = {} onFollow: () -> Unit = {}
) { ) {
val context = LocalContext.current val context = LocalContext.current
@@ -90,15 +95,15 @@ fun FollowerItem(
) { ) {
CustomAsyncImage( CustomAsyncImage(
context = context, context = context,
imageUrl = item.avatar, imageUrl = avatar,
contentDescription = item.nickname, contentDescription = nickname,
modifier = Modifier modifier = Modifier
.size(40.dp) .size(40.dp)
.noRippleClickable { .noRippleClickable {
navController.navigate( navController.navigate(
NavigationRoute.AccountProfile.route.replace( NavigationRoute.AccountProfile.route.replace(
"{id}", "{id}",
item.userId.toString() userId.toString()
) )
) )
} }
@@ -107,9 +112,9 @@ fun FollowerItem(
Column( Column(
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) { ) {
Text(item.nickname, fontWeight = FontWeight.Bold, fontSize = 16.sp) Text(nickname, fontWeight = FontWeight.Bold, fontSize = 16.sp)
} }
if (!item.isFollowing) { if (!isFollowing) {
Box( Box(
modifier = Modifier.noRippleClickable { modifier = Modifier.noRippleClickable {
onFollow() onFollow()

View File

@@ -0,0 +1,57 @@
package com.aiosman.riderpro.ui.follower
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.paging.compose.collectAsLazyPagingItems
import com.aiosman.riderpro.R
import com.aiosman.riderpro.ui.comment.NoticeScreenHeader
import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout
import kotlinx.coroutines.launch
@Composable
fun FollowingListScreen(userId: Int) {
val model = FollowerListViewModel
val scope = rememberCoroutineScope()
LaunchedEffect(Unit) {
model.loadData(userId)
}
StatusBarMaskLayout(
modifier = Modifier.padding(horizontal = 16.dp)
) {
var dataFlow = model.usersFlow
var users = dataFlow.collectAsLazyPagingItems()
Box(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp)
) {
NoticeScreenHeader(stringResource(R.string.following_upper), moreIcon = false)
}
LazyColumn(
modifier = Modifier.weight(1f)
) {
items(users.itemCount) { index ->
users[index]?.let { user ->
FollowItem(
avatar = user.avatar,
nickname = user.nickName,
userId = user.id,
isFollowing = user.isFollowing
) {
scope.launch {
model.followUser(user.id)
}
}
}
}
}
}
}

View File

@@ -0,0 +1,63 @@
package com.aiosman.riderpro.ui.follower
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 androidx.paging.map
import com.aiosman.riderpro.data.UserServiceImpl
import com.aiosman.riderpro.entity.AccountPagingSource
import com.aiosman.riderpro.entity.AccountProfileEntity
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
object FollowingListViewModel : ViewModel() {
private val userService = UserServiceImpl()
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) {
return
}
userId = id
viewModelScope.launch {
Pager(
config = PagingConfig(pageSize = 5, enablePlaceholders = false),
pagingSourceFactory = {
AccountPagingSource(
userService,
followerId = id
)
}
).flow.cachedIn(viewModelScope).collectLatest {
_usersFlow.value = it
}
}
}
private fun updateIsFollow(id: Int) {
val currentPagingData = usersFlow.value
val updatedPagingData = currentPagingData.map { user ->
if (user.id == id) {
user.copy(isFollowing = true)
} else {
user
}
}
_usersFlow.value = updatedPagingData
}
suspend fun followUser(userId: Int) {
userService.followUser(userId.toString())
updateIsFollow(userId)
}
}

View File

@@ -1,5 +1,8 @@
package com.aiosman.riderpro.ui.index.tabs.profile package com.aiosman.riderpro.ui.index.tabs.profile
import android.content.Context
import android.net.Uri
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.setValue import androidx.compose.runtime.setValue
@@ -13,12 +16,14 @@ import com.aiosman.riderpro.AppStore
import com.aiosman.riderpro.data.AccountService 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.UserServiceImpl 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
import com.aiosman.riderpro.entity.MomentRemoteDataSource import com.aiosman.riderpro.entity.MomentRemoteDataSource
import com.aiosman.riderpro.entity.MomentServiceImpl import com.aiosman.riderpro.entity.MomentServiceImpl
import com.aiosman.riderpro.ui.post.NewPostViewModel.uriToFile
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
@@ -31,7 +36,7 @@ object MyProfileViewModel : ViewModel() {
var profile by mutableStateOf<AccountProfileEntity?>(null) var profile by mutableStateOf<AccountProfileEntity?>(null)
private var _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty()) private var _momentsFlow = MutableStateFlow<PagingData<MomentEntity>>(PagingData.empty())
var momentsFlow = _momentsFlow.asStateFlow() var momentsFlow = _momentsFlow.asStateFlow()
fun loadProfile(){ fun loadProfile() {
viewModelScope.launch { viewModelScope.launch {
profile = accountService.getMyAccountProfile() profile = accountService.getMyAccountProfile()
val profile = accountService.getMyAccountProfile() val profile = accountService.getMyAccountProfile()
@@ -59,6 +64,34 @@ object MyProfileViewModel : ViewModel() {
} }
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 followerCount get() = profile?.followerCount ?: 0 val followerCount get() = profile?.followerCount ?: 0
val followingCount get() = profile?.followingCount ?: 0 val followingCount get() = profile?.followingCount ?: 0
val bio get() = profile?.bio ?: "" val bio get() = profile?.bio ?: ""

View File

@@ -1,6 +1,10 @@
package com.aiosman.riderpro.ui.index.tabs.profile package com.aiosman.riderpro.ui.index.tabs.profile
import android.app.Activity
import android.content.Intent
import android.util.Log import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.Animatable
@@ -92,7 +96,18 @@ fun ProfilePage() {
val moments = model.momentsFlow.collectAsLazyPagingItems() val moments = model.momentsFlow.collectAsLazyPagingItems()
val navController: NavController = LocalNavController.current val navController: NavController = LocalNavController.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val context = LocalContext.current
val statusBarPaddingValues = WindowInsets.systemBars.asPaddingValues() 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 {
model.updateUserProfileBanner(it, context = context)
}
}
}
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@@ -110,28 +125,40 @@ fun ProfilePage() {
.fillMaxWidth() .fillMaxWidth()
) { ) {
val banner = model.profile?.banner Box(
if (banner != null) { modifier = Modifier
CustomAsyncImage( .fillMaxWidth()
LocalContext.current, .height(400.dp)
banner, .noRippleClickable {
modifier = Modifier Intent(Intent.ACTION_PICK).apply {
.fillMaxWidth() type = "image/*"
.height(400.dp), pickBannerImageLauncher.launch(this)
contentDescription = "", }
contentScale = ContentScale.Crop }
) ) {
} else { val banner = model.profile?.banner
Image(
painter = painterResource(id = R.drawable.rider_pro_moment_demo_2), if (banner != null) {
modifier = Modifier CustomAsyncImage(
.fillMaxWidth() LocalContext.current,
.height(400.dp), banner,
contentDescription = "", modifier = Modifier
contentScale = ContentScale.Crop .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( Box(
modifier = Modifier modifier = Modifier
.align(Alignment.TopEnd) .align(Alignment.TopEnd)
@@ -313,9 +340,19 @@ fun UserInformation(
@Composable @Composable
fun UserInformationFollowers(modifier: Modifier, accountProfileEntity: AccountProfileEntity) { fun UserInformationFollowers(modifier: Modifier, accountProfileEntity: AccountProfileEntity) {
val navController = LocalNavController.current
Column(modifier = modifier.padding(top = 31.dp)) { Column(modifier = modifier.padding(top = 31.dp)) {
Text( Text(
modifier = Modifier.padding(bottom = 5.dp), modifier = Modifier
.padding(bottom = 5.dp)
.noRippleClickable {
navController.navigate(
NavigationRoute.FollowerList.route.replace(
"{id}",
accountProfileEntity.id.toString()
)
)
},
text = accountProfileEntity.followerCount.toString(), text = accountProfileEntity.followerCount.toString(),
fontSize = 24.sp, fontSize = 24.sp,
color = Color.Black, color = Color.Black,
@@ -391,12 +428,20 @@ fun UserInformationBasic(modifier: Modifier, accountProfileEntity: AccountProfil
@Composable @Composable
fun UserInformationFollowing(modifier: Modifier, accountProfileEntity: AccountProfileEntity) { fun UserInformationFollowing(modifier: Modifier, accountProfileEntity: AccountProfileEntity) {
val navController = LocalNavController.current
Column( Column(
modifier = modifier.padding(top = 6.dp), modifier = modifier.padding(top = 6.dp),
horizontalAlignment = Alignment.End horizontalAlignment = Alignment.End
) { ) {
Text( Text(
modifier = Modifier.padding(bottom = 5.dp), modifier = Modifier.padding(bottom = 5.dp).noRippleClickable {
navController.navigate(
NavigationRoute.FollowingList.route.replace(
"{id}",
accountProfileEntity.id.toString()
)
)
},
text = accountProfileEntity.followingCount.toString(), text = accountProfileEntity.followingCount.toString(),
fontSize = 24.sp, fontSize = 24.sp,
color = Color.Black, color = Color.Black,