From 43c27b7e0b25c194ae8ae96d0244e3650e362d86 Mon Sep 17 00:00:00 2001 From: AllenTom Date: Tue, 8 Oct 2024 17:25:27 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/aiosman/riderpro/MainActivity.kt | 7 -- .../aiosman/riderpro/data/AccountService.kt | 3 + .../aiosman/riderpro/data/MomentService.kt | 12 ++- .../com/aiosman/riderpro/entity/Moment.kt | 6 +- .../ui/index/tabs/profile/ProfileV3.kt | 1 - .../tabs/profile/composable/GalleryItem.kt | 18 ++++- .../ui/index/tabs/search/DiscoverScreen.kt | 5 +- .../ui/index/tabs/search/SearchScreen.kt | 74 +++++++++++++++++-- .../ui/index/tabs/search/SearchViewModel.kt | 32 ++++++++ app/src/main/res/values-zh/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 11 files changed, 134 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/com/aiosman/riderpro/MainActivity.kt b/app/src/main/java/com/aiosman/riderpro/MainActivity.kt index 612d963..ff6439e 100644 --- a/app/src/main/java/com/aiosman/riderpro/MainActivity.kt +++ b/app/src/main/java/com/aiosman/riderpro/MainActivity.kt @@ -6,8 +6,6 @@ import android.app.NotificationManager import android.content.Context import android.content.Intent import android.content.pm.PackageManager -import android.icu.util.Calendar -import android.icu.util.TimeZone import android.net.Uri import android.os.Build import android.os.Bundle @@ -32,12 +30,7 @@ import com.aiosman.riderpro.ui.Navigation import com.aiosman.riderpro.ui.NavigationRoute import com.aiosman.riderpro.ui.navigateToPost import com.aiosman.riderpro.ui.post.NewPostViewModel -import com.aiosman.riderpro.utils.Utils import com.google.firebase.analytics.FirebaseAnalytics -import com.tencent.imsdk.v2.V2TIMCallback -import com.tencent.imsdk.v2.V2TIMLogListener -import com.tencent.imsdk.v2.V2TIMManager -import com.tencent.imsdk.v2.V2TIMSDKConfig import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch diff --git a/app/src/main/java/com/aiosman/riderpro/data/AccountService.kt b/app/src/main/java/com/aiosman/riderpro/data/AccountService.kt index 88cbc8b..94961e0 100644 --- a/app/src/main/java/com/aiosman/riderpro/data/AccountService.kt +++ b/app/src/main/java/com/aiosman/riderpro/data/AccountService.kt @@ -105,6 +105,9 @@ data class NoticePost( it.copy( url = "${ApiClient.BASE_SERVER}${it.url}", thumbnail = "${ApiClient.BASE_SERVER}${it.thumbnail}", + blurHash = it.blurHash, + width = it.width, + height = it.height ) }, time = ApiClient.dateFromApiString(time) diff --git a/app/src/main/java/com/aiosman/riderpro/data/MomentService.kt b/app/src/main/java/com/aiosman/riderpro/data/MomentService.kt index 627aeef..d588bed 100644 --- a/app/src/main/java/com/aiosman/riderpro/data/MomentService.kt +++ b/app/src/main/java/com/aiosman/riderpro/data/MomentService.kt @@ -52,12 +52,14 @@ data class Moment( url = "${ApiClient.BASE_SERVER}${it.url}", thumbnail = "${ApiClient.BASE_SERVER}${it.thumbnail}", id = it.id, - blurHash = it.blurHash + blurHash = it.blurHash, + width = it.width, + height = it.height ) }, authorId = user.id.toInt(), liked = isLiked, - isFavorite = isFavorite + isFavorite = isFavorite, ) } } @@ -70,7 +72,11 @@ data class Image( @SerializedName("thumbnail") val thumbnail: String, @SerializedName("blurHash") - val blurHash: String? + val blurHash: String?, + @SerializedName("width") + val width: Int?, + @SerializedName("height") + val height: Int? ) data class User( diff --git a/app/src/main/java/com/aiosman/riderpro/entity/Moment.kt b/app/src/main/java/com/aiosman/riderpro/entity/Moment.kt index 04a8a28..13b9fc4 100644 --- a/app/src/main/java/com/aiosman/riderpro/entity/Moment.kt +++ b/app/src/main/java/com/aiosman/riderpro/entity/Moment.kt @@ -230,7 +230,11 @@ data class MomentImageEntity( // 缩略图URL val thumbnail: String, // 图片BlurHash - val blurHash: String? = null + val blurHash: String? = null, + // 宽度 + var width: Int? = null, + // 高度 + var height: Int? = null ) /** diff --git a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/ProfileV3.kt b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/ProfileV3.kt index 6f59efa..1d7bdbe 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/ProfileV3.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/ProfileV3.kt @@ -501,7 +501,6 @@ fun ProfileV3( Spacer(modifier = Modifier.height(120.dp)) } } - 1 -> LazyColumn( modifier = Modifier diff --git a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/composable/GalleryItem.kt b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/composable/GalleryItem.kt index fe148c2..d0ba63c 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/composable/GalleryItem.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/composable/GalleryItem.kt @@ -21,15 +21,25 @@ import com.aiosman.riderpro.ui.navigateToPost fun GalleryItem( moment: MomentEntity, idx: Int = 0 -){ +) { val navController = LocalNavController.current Box( modifier = Modifier .fillMaxWidth() - .aspectRatio( - if (idx % 3 == 0) 1.5f else 1f - ) + .let { + val firstImage = moment.images.firstOrNull() + if (firstImage?.width != null && + firstImage.height != null && + firstImage.width!! > 0 && + firstImage.height!! > 0 + ) { + val ratio = firstImage.width!!.toFloat() / firstImage.height!!.toFloat() + return@let it.aspectRatio(ratio.coerceIn(0.7f, 1.5f)) + } else { + return@let it.aspectRatio(if (idx % 3 == 0) 1.5f else 1f) + } + } .clip(RoundedCornerShape(8.dp)) .noRippleClickable { navController.navigateToPost( diff --git a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/search/DiscoverScreen.kt b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/search/DiscoverScreen.kt index 9192b68..c50092a 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/search/DiscoverScreen.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/search/DiscoverScreen.kt @@ -1,6 +1,5 @@ package com.aiosman.riderpro.ui.index.tabs.search -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -50,7 +49,7 @@ import com.aiosman.riderpro.ui.modifiers.noRippleClickable import com.aiosman.riderpro.ui.navigateToPost -@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterialApi::class) +@OptIn( ExperimentalMaterialApi::class) @Preview @Composable fun DiscoverScreen() { @@ -82,6 +81,7 @@ fun DiscoverScreen() { .fillMaxWidth() .padding(top = 16.dp, start = 24.dp, end = 24.dp), ) { + SearchViewModel.requestFocus = true navController.navigate(NavigationRoute.Search.route) } } @@ -150,7 +150,6 @@ fun DiscoverView() { .fillMaxWidth() .aspectRatio(1f) .padding(2.dp) - .noRippleClickable { navController.navigateToPost( id = momentItem.id, diff --git a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/search/SearchScreen.kt b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/search/SearchScreen.kt index 1e46655..677979c 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/search/SearchScreen.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/search/SearchScreen.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets @@ -51,9 +52,11 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.paging.compose.collectAsLazyPagingItems +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.ui.composables.ActionButton import com.aiosman.riderpro.ui.composables.CustomAsyncImage import com.aiosman.riderpro.ui.index.tabs.moment.MomentCard import com.aiosman.riderpro.ui.modifiers.noRippleClickable @@ -82,7 +85,10 @@ fun SearchScreen() { systemUiController.setStatusBarColor(Color.Transparent, darkIcons = true) } LaunchedEffect(Unit) { - focusRequester.requestFocus() + if (model.requestFocus) { + focusRequester.requestFocus() + model.requestFocus = false + } } Column( @@ -246,7 +252,8 @@ fun MomentResultTab() { val momentItem = moments[idx] ?: return@items Box( modifier = Modifier - .fillMaxWidth().background(Color.White) + .fillMaxWidth() + .background(Color.White) ) { MomentCard(momentEntity = momentItem, hideAction = true) } @@ -260,6 +267,7 @@ fun MomentResultTab() { fun UserResultTab() { val model = SearchViewModel val users = model.usersFlow.collectAsLazyPagingItems() + val scope = rememberCoroutineScope() Box( modifier = Modifier.fillMaxSize() ) { @@ -268,14 +276,25 @@ fun UserResultTab() { ) { items(users.itemCount) { idx -> val userItem = users[idx] ?: return@items - UserItem(userItem) + UserItem(userItem) { + scope.launch { + if (userItem.isFollowing) { + model.unfollowUser(userItem.id) + } else { + model.followUser(userItem.id) + } + } + } } } } } @Composable -fun UserItem(accountProfile: AccountProfileEntity) { +fun UserItem( + accountProfile: AccountProfileEntity, + onFollow: (AccountProfileEntity) -> Unit = {}, +) { val context = LocalContext.current val navController = LocalNavController.current Row( @@ -291,11 +310,52 @@ fun UserItem(accountProfile: AccountProfileEntity) { context, imageUrl = accountProfile.avatar, modifier = Modifier - .size(64.dp) + .size(48.dp) .clip(CircleShape), contentDescription = null ) - Spacer(modifier = Modifier.padding(16.dp)) - Text(text = accountProfile.nickName, fontSize = 18.sp, fontWeight = FontWeight.Bold) + Spacer(modifier = Modifier.padding(8.dp)) + Row( + modifier = Modifier.fillMaxWidth() + ) { + Column( + modifier = Modifier.weight(1f) + ) { + Text(text = accountProfile.nickName, fontSize = 16.sp, fontWeight = FontWeight.Bold) + Spacer(modifier = Modifier.padding(2.dp)) + Text( + text = stringResource( + R.string.search_user_item_follower_count, + accountProfile.followerCount + ), fontSize = 14.sp, color = Color(0xFF9E9E9E) + ) + } + Spacer(modifier = Modifier.padding(8.dp)) + if (accountProfile.id != AppState.UserId) { + if (accountProfile.isFollowing) { + ActionButton( + text = stringResource(R.string.following_upper), + backgroundColor = Color(0xFF9E9E9E), + contentPadding = PaddingValues(vertical = 4.dp, horizontal = 8.dp), + color = Color.White + ) { + onFollow(accountProfile) + } + } else { + ActionButton( + text = stringResource(R.string.follow_upper), + backgroundColor = Color(0xffda3832), + contentPadding = PaddingValues(vertical = 4.dp, horizontal = 8.dp), + color = Color.White + ) { + onFollow(accountProfile) + } + } + } + + + } + + } } \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/search/SearchViewModel.kt b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/search/SearchViewModel.kt index ad48e87..61a2b05 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/search/SearchViewModel.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/search/SearchViewModel.kt @@ -9,6 +9,7 @@ import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData import androidx.paging.cachedIn +import androidx.paging.map import com.aiosman.riderpro.entity.AccountPagingSource import com.aiosman.riderpro.entity.AccountProfileEntity import com.aiosman.riderpro.entity.MomentPagingSource @@ -17,6 +18,7 @@ import com.aiosman.riderpro.data.MomentService import com.aiosman.riderpro.entity.MomentServiceImpl import com.aiosman.riderpro.data.UserServiceImpl import com.aiosman.riderpro.entity.MomentEntity +import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collectLatest @@ -32,7 +34,11 @@ object SearchViewModel : ViewModel() { private val _usersFlow = MutableStateFlow>(PagingData.empty()) val usersFlow = _usersFlow.asStateFlow() var showResult by mutableStateOf(false) + var requestFocus by mutableStateOf(false) fun search() { + if (searchText.isEmpty()) { + return + } viewModelScope.launch { Pager( config = PagingConfig(pageSize = 5, enablePlaceholders = false), @@ -62,6 +68,32 @@ object SearchViewModel : ViewModel() { showResult = true } + suspend fun followUser(id:Int){ + userService.followUser(id.toString()) + val currentPagingData = _usersFlow.value + val updatedPagingData = currentPagingData.map { userItem -> + if (userItem.id == id) { + userItem.copy(isFollowing = true, followerCount = userItem.followerCount + 1) + } else { + userItem + } + } + _usersFlow.value = updatedPagingData + } + + suspend fun unfollowUser(id:Int){ + userService.unFollowUser(id.toString()) + val currentPagingData = _usersFlow.value + val updatedPagingData = currentPagingData.map { userItem -> + if (userItem.id == id) { + userItem.copy(isFollowing = false, followerCount = userItem.followerCount - 1) + } else { + userItem + } + } + _usersFlow.value = updatedPagingData + } + fun ResetModel(){ _momentsFlow.value = PagingData.empty() _usersFlow.value = PagingData.empty() diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index ab3dcf0..2780300 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -86,4 +86,5 @@ 刷新 清除 验证码错误,请重试 + %d 粉丝 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6d1f545..3ccc879 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -85,4 +85,5 @@ Refresh Clear incorrect captcha,please try again + %d followers \ No newline at end of file