From b4004663cd44ab403cbce7acc617821c0949716a Mon Sep 17 00:00:00 2001 From: AllenTom Date: Sat, 24 Aug 2024 23:11:20 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B4=E7=90=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aiosman/riderpro/data/AccountService.kt | 264 ++++++++---------- .../java/com/aiosman/riderpro/data/Base.kt | 0 .../aiosman/riderpro/data/CommentService.kt | 106 ++++--- .../aiosman/riderpro/data/DataContainer.kt | 3 + .../com/aiosman/riderpro/data/Exception.kt | 3 + .../aiosman/riderpro/data/ListContainer.kt | 7 + .../aiosman/riderpro/data/MomentService.kt | 216 +++----------- .../com/aiosman/riderpro/data/UserService.kt | 61 ++-- .../aiosman/riderpro/data/api/RiderProAPI.kt | 1 - .../com/aiosman/riderpro/entity/Account.kt | 176 ++++++++++++ .../com/aiosman/riderpro/entity/Comment.kt | 54 ++++ .../com/aiosman/riderpro/entity/Moment.kt | 236 ++++++++++++++++ .../java/com/aiosman/riderpro/entity/User.kt | 36 +++ .../java/com/aiosman/riderpro/exp/Date.kt | 6 + .../aiosman/riderpro/model/MomentEntity.kt | 32 --- .../aiosman/riderpro/test/MockDataSource.kt | 31 -- .../com/aiosman/riderpro/test/SimpleUtils.kt | 51 ---- .../com/aiosman/riderpro/test/TestDatabase.kt | 163 ----------- .../riderpro/ui/account/changepassword.kt | 19 +- .../com/aiosman/riderpro/ui/account/edit.kt | 28 +- .../riderpro/ui/comment/CommentModal.kt | 22 +- .../riderpro/ui/composables/RelPostCard.kt | 2 +- .../ui/favourite/FavouritePageViewModel.kt | 8 +- .../riderpro/ui/favourite/FavouriteScreen.kt | 9 +- .../riderpro/ui/follower/FollowerPage.kt | 4 +- .../riderpro/ui/follower/FollowerViewModel.kt | 9 +- .../ui/imageviewer/ImageViewerViewModel.kt | 2 +- .../com/aiosman/riderpro/ui/index/Index.kt | 2 +- .../ui/index/tabs/message/MessageList.kt | 7 +- .../tabs/message/MessageListViewModel.kt | 8 +- .../riderpro/ui/index/tabs/moment/Moment.kt | 27 +- .../ui/index/tabs/moment/MomentViewModel.kt | 10 +- .../index/tabs/profile/MyProfileViewModel.kt | 16 +- .../riderpro/ui/index/tabs/profile/Profile.kt | 6 +- .../ui/index/tabs/search/SearchScreen.kt | 2 +- .../ui/index/tabs/search/SearchViewModel.kt | 20 +- .../riderpro/ui/like/LikePageViewModel.kt | 4 +- .../riderpro/ui/post/NewPostViewModel.kt | 6 +- .../java/com/aiosman/riderpro/ui/post/Post.kt | 26 +- .../riderpro/ui/profile/AccountProfile.kt | 16 +- 40 files changed, 898 insertions(+), 801 deletions(-) delete mode 100644 app/src/main/java/com/aiosman/riderpro/data/Base.kt create mode 100644 app/src/main/java/com/aiosman/riderpro/entity/Account.kt create mode 100644 app/src/main/java/com/aiosman/riderpro/entity/Comment.kt create mode 100644 app/src/main/java/com/aiosman/riderpro/entity/Moment.kt create mode 100644 app/src/main/java/com/aiosman/riderpro/entity/User.kt delete mode 100644 app/src/main/java/com/aiosman/riderpro/model/MomentEntity.kt delete mode 100644 app/src/main/java/com/aiosman/riderpro/test/MockDataSource.kt delete mode 100644 app/src/main/java/com/aiosman/riderpro/test/SimpleUtils.kt delete mode 100644 app/src/main/java/com/aiosman/riderpro/test/TestDatabase.kt 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 74975c1..41df8e2 100644 --- a/app/src/main/java/com/aiosman/riderpro/data/AccountService.kt +++ b/app/src/main/java/com/aiosman/riderpro/data/AccountService.kt @@ -1,14 +1,16 @@ package com.aiosman.riderpro.data -import androidx.paging.PagingSource -import androidx.paging.PagingState import com.aiosman.riderpro.data.api.ApiClient import com.aiosman.riderpro.data.api.ChangePasswordRequestBody import com.aiosman.riderpro.data.api.GoogleRegisterRequestBody import com.aiosman.riderpro.data.api.LoginUserRequestBody import com.aiosman.riderpro.data.api.RegisterRequestBody import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody -import com.aiosman.riderpro.test.TestDatabase +import com.aiosman.riderpro.entity.AccountFavouriteEntity +import com.aiosman.riderpro.entity.AccountLikeEntity +import com.aiosman.riderpro.entity.AccountProfileEntity +import com.aiosman.riderpro.entity.NoticePostEntity +import com.aiosman.riderpro.entity.NoticeUserEntity import com.google.gson.annotations.SerializedName import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody @@ -16,41 +18,29 @@ import okhttp3.RequestBody import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.toRequestBody import java.io.File -import java.io.IOException -import java.util.Date - -data class AccountLikeEntity( - val post: NoticePostEntity, - val user: NoticeUserEntity, - val likeTime: Date, -) - -data class AccountFavouriteEntity( - val post: NoticePostEntity, - val user: NoticeUserEntity, - val favoriteTime: Date, -) - -data class AccountProfileEntity( - val id: Int, - val followerCount: Int, - val followingCount: Int, - val nickName: String, - val avatar: String, - val bio: String, - val country: String, - val isFollowing: Boolean -) +/** + * 用户资料 + */ data class AccountProfile( + // 用户ID val id: Int, + // 用户名 val username: String, + // 昵称 val nickname: String, + // 头像 val avatar: String, + // 关注数 val followingCount: Int, + // 粉丝数 val followerCount: Int, + // 是否关注 val isFollowing: Boolean ) { + /** + * 转换为Entity + */ fun toAccountProfileEntity(): AccountProfileEntity { return AccountProfileEntity( id = id, @@ -65,23 +55,27 @@ data class AccountProfile( } } -data class NoticePostEntity( - val id: Int, - val textContent: String, - val images: List, - val time: Date, -) - +/** + * 消息关联资料 + */ data class NoticePost( + // 动态ID @SerializedName("id") val id: Int, + // 动态内容 @SerializedName("textContent") + // 动态图片 val textContent: String, + // 动态图片 @SerializedName("images") val images: List, + // 动态时间 @SerializedName("time") val time: String, ) { + /** + * 转换为Entity + */ fun toNoticePostEntity(): NoticePostEntity { return NoticePostEntity( id = id, @@ -97,20 +91,23 @@ data class NoticePost( } } -data class NoticeUserEntity( - val id: Int, - val nickName: String, - val avatar: String, -) - +/** + * 消息关联用户 + */ data class NoticeUser( + // 用户ID @SerializedName("id") val id: Int, + // 昵称 @SerializedName("nickName") val nickName: String, + // 头像 @SerializedName("avatar") val avatar: String, ) { + /** + * 转换为Entity + */ fun toNoticeUserEntity(): NoticeUserEntity { return NoticeUserEntity( id = id, @@ -120,13 +117,20 @@ data class NoticeUser( } } +/** + * 点赞消息通知 + */ data class AccountLike( + // 是否未读 @SerializedName("isUnread") val isUnread: Boolean, + // 动态 @SerializedName("post") val post: NoticePost, + // 点赞用户 @SerializedName("user") val user: NoticeUser, + // 点赞时间 @SerializedName("likeTime") val likeTime: String, ) { @@ -192,108 +196,93 @@ data class AccountNotice( ) -class LikeItemPagingSource( - private val accountService: AccountService, -) : PagingSource() { - override suspend fun load(params: LoadParams): LoadResult { - return try { - val currentPage = params.key ?: 1 - val likes = accountService.getMyLikeNotice( - page = currentPage, - pageSize = 20, - ) - - LoadResult.Page( - data = likes.list.map { - it.toAccountLikeEntity() - }, - prevKey = if (currentPage == 1) null else currentPage - 1, - nextKey = if (likes.list.isEmpty()) null else likes.page + 1 - ) - } catch (exception: IOException) { - return LoadResult.Error(exception) - } - } - - override fun getRefreshKey(state: PagingState): Int? { - return state.anchorPosition - } -} - -class FavoriteItemPagingSource( - private val accountService: AccountService, -) : PagingSource() { - override suspend fun load(params: LoadParams): LoadResult { - return try { - val currentPage = params.key ?: 1 - val favouriteListContainer = accountService.getMyFavouriteNotice( - page = currentPage, - pageSize = 20, - ) - LoadResult.Page( - data = favouriteListContainer.list.map { - it.toAccountFavouriteEntity() - }, - prevKey = if (currentPage == 1) null else currentPage - 1, - nextKey = if (favouriteListContainer.list.isEmpty()) null else favouriteListContainer.page + 1 - ) - } catch (exception: IOException) { - return LoadResult.Error(exception) - } - } - - override fun getRefreshKey(state: PagingState): Int? { - return state.anchorPosition - } -} - -class FollowItemPagingSource( - private val accountService: AccountService, -) : PagingSource() { - override suspend fun load(params: LoadParams): LoadResult { - return try { - val currentPage = params.key ?: 1 - val followListContainer = accountService.getMyFollowNotice( - page = currentPage, - pageSize = 20, - ) - - LoadResult.Page( - data = followListContainer.list.map { - it.copy( - avatar = "${ApiClient.BASE_SERVER}${it.avatar}", - ) - }, - prevKey = if (currentPage == 1) null else currentPage - 1, - nextKey = if (followListContainer.list.isEmpty()) null else followListContainer.page + 1 - ) - } catch (exception: IOException) { - return LoadResult.Error(exception) - } - } - - override fun getRefreshKey(state: PagingState): Int? { - return state.anchorPosition - } -} - - interface AccountService { + /** + * 获取登录当前用户的资料 + */ suspend fun getMyAccountProfile(): AccountProfileEntity - suspend fun getAccountProfileById(id: Int): AccountProfileEntity + + /** + * 获取登录的用户认证信息 + */ suspend fun getMyAccount(): UserAuth + + /** + * 使用用户名密码登录 + * @param loginName 用户名 + * @param password 密码 + */ suspend fun loginUserWithPassword(loginName: String, password: String): UserAuth + + /** + * 使用google登录 + * @param googleId googleId + */ suspend fun loginUserWithGoogle(googleId: String): UserAuth + + /** + * 退出登录 + */ suspend fun logout() - suspend fun updateAvatar(uri: String) + + /** + * 更新用户资料 + * @param avatar 头像 + * @param nickName 昵称 + * @param bio 简介 + */ suspend fun updateProfile(avatar: UploadImage?, nickName: String?, bio: String?) + + /** + * 注册用户 + * @param loginName 用户名 + * @param password 密码 + */ suspend fun registerUserWithPassword(loginName: String, password: String) + + /** + * 使用google账号注册 + * @param idToken googleIdToken + */ suspend fun regiterUserWithGoogleAccount(idToken: String) + + /** + * 修改密码 + * @param oldPassword 旧密码 + * @param newPassword 新密码 + */ suspend fun changeAccountPassword(oldPassword: String, newPassword: String) + + /** + * 获取我的点赞通知 + * @param page 页码 + * @param pageSize 每页数量 + */ suspend fun getMyLikeNotice(page: Int, pageSize: Int): ListContainer + + /** + * 获取我的关注通知 + * @param page 页码 + * @param pageSize 每页数量 + */ suspend fun getMyFollowNotice(page: Int, pageSize: Int): ListContainer + + /** + * 获取我的收藏通知 + * @param page 页码 + * @param pageSize 每页数量 + */ suspend fun getMyFavouriteNotice(page: Int, pageSize: Int): ListContainer + + /** + * 获取我的通知信息 + */ suspend fun getMyNoticeInfo(): AccountNotice + + /** + * 更新通知信息,更新最后一次查看时间 + * @param payload 通知信息 + */ suspend fun updateNotice(payload: UpdateNoticeRequestBody) } @@ -304,12 +293,6 @@ class AccountServiceImpl : AccountService { return body.data.toAccountProfileEntity() } - override suspend fun getAccountProfileById(id: Int): AccountProfileEntity { - val resp = ApiClient.api.getAccountProfileById(id) - val body = resp.body() ?: throw ServiceException("Failed to get account") - return body.data.toAccountProfileEntity() - } - override suspend fun getMyAccount(): UserAuth { val resp = ApiClient.api.checkToken() val body = resp.body() ?: throw ServiceException("Failed to get account") @@ -323,7 +306,7 @@ class AccountServiceImpl : AccountService { } override suspend fun loginUserWithGoogle(googleId: String): UserAuth { - val resp = ApiClient.api.login(LoginUserRequestBody(googleId=googleId)) + val resp = ApiClient.api.login(LoginUserRequestBody(googleId = googleId)) val body = resp.body() ?: throw ServiceException("Failed to login") return UserAuth(0, body.token) } @@ -339,15 +322,6 @@ class AccountServiceImpl : AccountService { // do nothing } - override suspend fun updateAvatar(uri: String) { - TestDatabase.accountData = TestDatabase.accountData.map { - if (it.id == 1) { - it.copy(avatar = uri) - } else { - it - } - } - } fun createMultipartBody(file: File, filename: String, name: String): MultipartBody.Part { val requestFile = file.asRequestBody("image/*".toMediaTypeOrNull()) diff --git a/app/src/main/java/com/aiosman/riderpro/data/Base.kt b/app/src/main/java/com/aiosman/riderpro/data/Base.kt deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/main/java/com/aiosman/riderpro/data/CommentService.kt b/app/src/main/java/com/aiosman/riderpro/data/CommentService.kt index 6eb080c..3a17b56 100644 --- a/app/src/main/java/com/aiosman/riderpro/data/CommentService.kt +++ b/app/src/main/java/com/aiosman/riderpro/data/CommentService.kt @@ -1,18 +1,22 @@ package com.aiosman.riderpro.data -import androidx.paging.PagingSource -import androidx.paging.PagingState -import com.aiosman.riderpro.AppStore import com.aiosman.riderpro.data.api.ApiClient import com.aiosman.riderpro.data.api.CommentRequestBody -import com.aiosman.riderpro.test.TestDatabase +import com.aiosman.riderpro.entity.CommentEntity import com.google.gson.annotations.SerializedName -import java.io.IOException -import java.util.Calendar -import java.util.Date -import kotlin.math.min +/** + * 评论相关 Service + */ interface CommentService { + /** + * 获取动态 + * @param pageNumber 页码 + * @param postId 动态ID,过滤条件 + * @param postUser 动态作者ID,获取某个用户所有动态下的评论 + * @param selfNotice 是否是自己的通知 + * @return 评论列表 + */ suspend fun getComments( pageNumber: Int, postId: Int? = null, @@ -20,32 +24,67 @@ interface CommentService { selfNotice: Boolean? = null ): ListContainer + /** + * 创建评论 + * @param postId 动态ID + * @param content 评论内容 + */ suspend fun createComment(postId: Int, content: String) + + /** + * 点赞评论 + * @param commentId 评论ID + */ suspend fun likeComment(commentId: Int) + + /** + * 取消点赞评论 + * @param commentId 评论ID + */ suspend fun dislikeComment(commentId: Int) + + /** + * 更新评论已读状态 + * @param commentId 评论ID + */ suspend fun updateReadStatus(commentId: Int) } +/** + * 评论 + */ data class Comment( + // 评论ID @SerializedName("id") val id: Int, + // 评论内容 @SerializedName("content") val content: String, + // 评论用户 @SerializedName("user") val user: User, + // 点赞数 @SerializedName("likeCount") val likeCount: Int, + // 是否点赞 @SerializedName("isLiked") val isLiked: Boolean, + // 创建时间 @SerializedName("createdAt") val createdAt: String, + // 动态ID @SerializedName("postId") val postId: Int, + // 动态 @SerializedName("post") val post: NoticePost?, + // 是否未读 @SerializedName("isUnread") val isUnread: Boolean ) { + /** + * 转换为Entity + */ fun toCommentEntity(): CommentEntity { return CommentEntity( id = id, @@ -73,52 +112,6 @@ data class Comment( } } -data class CommentEntity( - val id: Int, - val name: String, - val comment: String, - val date: Date, - val likes: Int, - val replies: List, - val postId: Int = 0, - val avatar: String, - val author: Long, - var liked: Boolean, - var unread: Boolean = false, - var post: NoticePost? -) - -class CommentPagingSource( - private val remoteDataSource: CommentRemoteDataSource, - private val postId: Int? = null, - private val postUser: Int? = null, - private val selfNotice: Boolean? = null -) : PagingSource() { - override suspend fun load(params: LoadParams): LoadResult { - return try { - val currentPage = params.key ?: 1 - val comments = remoteDataSource.getComments( - pageNumber = currentPage, - postId = postId, - postUser = postUser, - selfNotice = selfNotice - ) - LoadResult.Page( - data = comments.list, - prevKey = if (currentPage == 1) null else currentPage - 1, - nextKey = if (comments.list.isEmpty()) null else comments.page + 1 - ) - } catch (exception: IOException) { - return LoadResult.Error(exception) - } - } - - override fun getRefreshKey(state: PagingState): Int? { - return state.anchorPosition - } - -} - class CommentRemoteDataSource( private val commentService: CommentService, ) { @@ -138,7 +131,7 @@ class CommentRemoteDataSource( } -class TestCommentServiceImpl : CommentService { +class CommentServiceImpl : CommentService { override suspend fun getComments( pageNumber: Int, postId: Int?, @@ -182,7 +175,4 @@ class TestCommentServiceImpl : CommentService { return } - companion object { - const val DataBatchSize = 5 - } } \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/data/DataContainer.kt b/app/src/main/java/com/aiosman/riderpro/data/DataContainer.kt index 545e97b..74f7340 100644 --- a/app/src/main/java/com/aiosman/riderpro/data/DataContainer.kt +++ b/app/src/main/java/com/aiosman/riderpro/data/DataContainer.kt @@ -1,5 +1,8 @@ package com.aiosman.riderpro.data +/** + * 通用接口返回数据 + */ data class DataContainer( val data: T ) diff --git a/app/src/main/java/com/aiosman/riderpro/data/Exception.kt b/app/src/main/java/com/aiosman/riderpro/data/Exception.kt index a27803f..7bdb711 100644 --- a/app/src/main/java/com/aiosman/riderpro/data/Exception.kt +++ b/app/src/main/java/com/aiosman/riderpro/data/Exception.kt @@ -1,5 +1,8 @@ package com.aiosman.riderpro.data +/** + * 错误返回 + */ class ServiceException( override val message: String, val code: Int = 0, diff --git a/app/src/main/java/com/aiosman/riderpro/data/ListContainer.kt b/app/src/main/java/com/aiosman/riderpro/data/ListContainer.kt index ae7037f..722c36b 100644 --- a/app/src/main/java/com/aiosman/riderpro/data/ListContainer.kt +++ b/app/src/main/java/com/aiosman/riderpro/data/ListContainer.kt @@ -3,13 +3,20 @@ package com.aiosman.riderpro.data import com.google.gson.annotations.SerializedName +/** + * 通用列表接口返回 + */ data class ListContainer( + // 总数 @SerializedName("total") val total: Int, + // 当前页 @SerializedName("page") val page: Int, + // 每页数量 @SerializedName("pageSize") val pageSize: Int, + // 列表 @SerializedName("list") val list: List ) \ No newline at end of file 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 f55e973..eec939c 100644 --- a/app/src/main/java/com/aiosman/riderpro/data/MomentService.kt +++ b/app/src/main/java/com/aiosman/riderpro/data/MomentService.kt @@ -1,23 +1,11 @@ package com.aiosman.riderpro.data -import androidx.paging.PagingSource -import androidx.paging.PagingState -import com.aiosman.riderpro.AppStore import com.aiosman.riderpro.R import com.aiosman.riderpro.data.api.ApiClient -import com.aiosman.riderpro.model.MomentEntity -import com.aiosman.riderpro.model.MomentImageEntity -import com.aiosman.riderpro.test.TestDatabase +import com.aiosman.riderpro.entity.MomentEntity +import com.aiosman.riderpro.entity.MomentImageEntity import com.google.gson.annotations.SerializedName - -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.MultipartBody -import okhttp3.RequestBody -import okhttp3.RequestBody.Companion.toRequestBody import java.io.File -import java.io.FileOutputStream -import java.io.IOException -import java.net.URL data class Moment( @SerializedName("id") @@ -100,9 +88,32 @@ data class UploadImage( ) interface MomentService { + /** + * 获取动态详情 + * @param id 动态ID + */ suspend fun getMomentById(id: Int): MomentEntity + + /** + * 点赞动态 + * @param id 动态ID + */ suspend fun likeMoment(id: Int) + + /** + * 取消点赞动态 + * @param id 动态ID + */ suspend fun dislikeMoment(id: Int) + + /** + * 获取动态列表 + * @param pageNumber 页码 + * @param author 作者ID,过滤条件 + * @param timelineId 用户时间线ID,指定用户 ID 的时间线 + * @param contentSearch 内容搜索,过滤条件 + * @return 动态列表 + */ suspend fun getMoments( pageNumber: Int, author: Int? = null, @@ -110,6 +121,13 @@ interface MomentService { contentSearch: String? = null ): ListContainer + /** + * 创建动态 + * @param content 动态内容 + * @param authorId 作者ID + * @param images 图片列表 + * @param relPostId 关联动态ID + */ suspend fun createMoment( content: String, authorId: Int, @@ -117,169 +135,17 @@ interface MomentService { relPostId: Int? = null ): MomentEntity + /** + * 收藏动态 + * @param id 动态ID + */ suspend fun favoriteMoment(id: Int) + + /** + * 取消收藏动态 + * @param id 动态ID + */ suspend fun unfavoriteMoment(id: Int) } -class MomentPagingSource( - private val remoteDataSource: MomentRemoteDataSource, - private val author: Int? = null, - private val timelineId: Int? = null, - private val contentSearch: String? = null -) : PagingSource() { - override suspend fun load(params: LoadParams): LoadResult { - return try { - val currentPage = params.key ?: 1 - val moments = remoteDataSource.getMoments( - pageNumber = currentPage, - author = author, - timelineId = timelineId, - contentSearch = contentSearch - ) - - LoadResult.Page( - data = moments.list, - prevKey = if (currentPage == 1) null else currentPage - 1, - nextKey = if (moments.list.isEmpty()) null else moments.page + 1 - ) - } catch (exception: IOException) { - return LoadResult.Error(exception) - } - } - - override fun getRefreshKey(state: PagingState): Int? { - return state.anchorPosition - } - -} - -class MomentRemoteDataSource( - private val momentService: MomentService, -) { - suspend fun getMoments( - pageNumber: Int, - author: Int?, - timelineId: Int?, - contentSearch: String? - ): ListContainer { - return momentService.getMoments(pageNumber, author, timelineId, contentSearch) - } -} - - -class TestMomentServiceImpl() : MomentService { - val testMomentBackend = TestMomentBackend() - - override suspend fun getMoments( - pageNumber: Int, - author: Int?, - timelineId: Int?, - contentSearch: String? - ): ListContainer { - return testMomentBackend.fetchMomentItems(pageNumber, author, timelineId, contentSearch) - } - - override suspend fun getMomentById(id: Int): MomentEntity { - return testMomentBackend.getMomentById(id) - } - - - override suspend fun likeMoment(id: Int) { - testMomentBackend.likeMoment(id) - } - - override suspend fun dislikeMoment(id: Int) { - testMomentBackend.dislikeMoment(id) - } - - override suspend fun createMoment( - content: String, - authorId: Int, - images: List, - relPostId: Int? - ): MomentEntity { - return testMomentBackend.createMoment(content, authorId, images, relPostId) - } - - override suspend fun favoriteMoment(id: Int) { - testMomentBackend.favoriteMoment(id) - } - - override suspend fun unfavoriteMoment(id: Int) { - testMomentBackend.unfavoriteMoment(id) - } - -} - -class TestMomentBackend( - private val loadDelay: Long = 500, -) { - val DataBatchSize = 5 - suspend fun fetchMomentItems( - pageNumber: Int, - author: Int? = null, - timelineId: Int?, - contentSearch: String? - ): ListContainer { - val resp = ApiClient.api.getPosts( - pageSize = DataBatchSize, - page = pageNumber, - timelineId = timelineId, - authorId = author, - contentSearch = contentSearch - ) - val body = resp.body() ?: throw ServiceException("Failed to get moments") - return ListContainer( - total = body.total, - page = pageNumber, - pageSize = DataBatchSize, - list = body.list.map { it.toMomentItem() } - ) - } - - suspend fun getMomentById(id: Int): MomentEntity { - var resp = ApiClient.api.getPost(id) - var body = resp.body()?.data ?: throw ServiceException("Failed to get moment") - return body.toMomentItem() - } - - suspend fun likeMoment(id: Int) { - ApiClient.api.likePost(id) - } - - suspend fun dislikeMoment(id: Int) { - ApiClient.api.dislikePost(id) - } - - fun createMultipartBody(file: File, name: String): MultipartBody.Part { - val requestFile = RequestBody.create("image/*".toMediaTypeOrNull(), file) - return MultipartBody.Part.createFormData(name, file.name, requestFile) - } - - suspend fun createMoment( - content: String, - authorId: Int, - imageUriList: List, - relPostId: Int? - ): MomentEntity { - val textContent = content.toRequestBody("text/plain".toMediaTypeOrNull()) - val imageList = imageUriList.map { item -> - val file = item.file - createMultipartBody(file, "image") - } - val response = ApiClient.api.createPost(imageList, textContent = textContent) - val body = response.body()?.data ?: throw ServiceException("Failed to create moment") - return body.toMomentItem() - - } - - suspend fun favoriteMoment(id: Int) { - ApiClient.api.favoritePost(id) - } - - suspend fun unfavoriteMoment(id: Int) { - ApiClient.api.unfavoritePost(id) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/data/UserService.kt b/app/src/main/java/com/aiosman/riderpro/data/UserService.kt index caeff8e..6452ffd 100644 --- a/app/src/main/java/com/aiosman/riderpro/data/UserService.kt +++ b/app/src/main/java/com/aiosman/riderpro/data/UserService.kt @@ -1,48 +1,43 @@ package com.aiosman.riderpro.data -import androidx.paging.PagingSource -import androidx.paging.PagingState import com.aiosman.riderpro.data.api.ApiClient -import com.aiosman.riderpro.model.MomentEntity -import com.aiosman.riderpro.test.TestDatabase -import java.io.IOException +import com.aiosman.riderpro.entity.AccountProfileEntity data class UserAuth( val id: Int, val token: String? = null ) -class AccountPagingSource( - private val userService: UserService, - private val nickname: String? = null -) : PagingSource() { - override suspend fun load(params: LoadParams): LoadResult { - return try { - val currentPage = params.key ?: 1 - val users = userService.getUsers( - page = currentPage, - nickname = nickname - ) - LoadResult.Page( - data = users.list, - prevKey = if (currentPage == 1) null else currentPage - 1, - nextKey = if (users.list.isEmpty()) null else users.page + 1 - ) - } catch (exception: IOException) { - return LoadResult.Error(exception) - } - } - - override fun getRefreshKey(state: PagingState): Int? { - return state.anchorPosition - } - -} - +/** + * 用户相关 Service + */ interface UserService { + /** + * 获取用户信息 + * @param id 用户ID + * @return 用户信息 + */ suspend fun getUserProfile(id: String): AccountProfileEntity + + /** + * 关注用户 + * @param id 用户ID + */ suspend fun followUser(id: String) + + /** + * 取消关注用户 + * @param id 用户ID + */ suspend fun unFollowUser(id: String) + + /** + * 获取用户列表 + * @param pageSize 分页大小 + * @param page 页码 + * @param nickname 昵称搜索 + * @return 用户列表 + */ suspend fun getUsers( pageSize: Int = 20, page: Int = 1, @@ -51,7 +46,7 @@ interface UserService { } -class TestUserServiceImpl : UserService { +class UserServiceImpl : UserService { override suspend fun getUserProfile(id: String): AccountProfileEntity { val resp = ApiClient.api.getAccountProfileById(id.toInt()) val body = resp.body() ?: throw ServiceException("Failed to get account") diff --git a/app/src/main/java/com/aiosman/riderpro/data/api/RiderProAPI.kt b/app/src/main/java/com/aiosman/riderpro/data/api/RiderProAPI.kt index 2b3fb28..7186909 100644 --- a/app/src/main/java/com/aiosman/riderpro/data/api/RiderProAPI.kt +++ b/app/src/main/java/com/aiosman/riderpro/data/api/RiderProAPI.kt @@ -5,7 +5,6 @@ import com.aiosman.riderpro.data.AccountFollow import com.aiosman.riderpro.data.AccountLike import com.aiosman.riderpro.data.AccountNotice import com.aiosman.riderpro.data.AccountProfile -import com.aiosman.riderpro.data.AccountProfileEntity import com.aiosman.riderpro.data.Comment import com.aiosman.riderpro.data.DataContainer import com.aiosman.riderpro.data.ListContainer diff --git a/app/src/main/java/com/aiosman/riderpro/entity/Account.kt b/app/src/main/java/com/aiosman/riderpro/entity/Account.kt new file mode 100644 index 0000000..1a5088c --- /dev/null +++ b/app/src/main/java/com/aiosman/riderpro/entity/Account.kt @@ -0,0 +1,176 @@ +package com.aiosman.riderpro.entity + +import androidx.paging.PagingSource +import androidx.paging.PagingState +import com.aiosman.riderpro.data.AccountFollow +import com.aiosman.riderpro.data.AccountService +import com.aiosman.riderpro.data.Image +import com.aiosman.riderpro.data.api.ApiClient +import java.io.IOException +import java.util.Date + +/** + * 用户点赞 + */ +data class AccountLikeEntity( + // 动态 + val post: NoticePostEntity, + // 点赞用户 + val user: NoticeUserEntity, + // 点赞时间 + val likeTime: Date, +) + +/** + * 用户收藏 + */ +data class AccountFavouriteEntity( + // 动态 + val post: NoticePostEntity, + // 收藏用户 + val user: NoticeUserEntity, + // 收藏时间 + val favoriteTime: Date, +) + +/** + * 用户信息 + */ +data class AccountProfileEntity( + // 用户ID + val id: Int, + // 粉丝数 + val followerCount: Int, + // 关注数 + val followingCount: Int, + // 昵称 + val nickName: String, + // 头像 + val avatar: String, + // 个人简介 + val bio: String, + // 国家 + val country: String, + // 是否关注,针对当前登录用户 + val isFollowing: Boolean +) + +/** + * 消息关联的动态 + */ +data class NoticePostEntity( + // 动态ID + val id: Int, + // 动态内容 + val textContent: String, + // 动态图片 + val images: List, + // 时间 + val time: Date, +) + +/** + * 消息关联的用户 + */ +data class NoticeUserEntity( + // 用户ID + val id: Int, + // 昵称 + val nickName: String, + // 头像 + val avatar: String, +) + +/** + * 用户点赞消息分页数据加载器 + */ +class LikeItemPagingSource( + private val accountService: AccountService, +) : PagingSource() { + override suspend fun load(params: LoadParams): LoadResult { + return try { + val currentPage = params.key ?: 1 + val likes = accountService.getMyLikeNotice( + page = currentPage, + pageSize = 20, + ) + + LoadResult.Page( + data = likes.list.map { + it.toAccountLikeEntity() + }, + prevKey = if (currentPage == 1) null else currentPage - 1, + nextKey = if (likes.list.isEmpty()) null else likes.page + 1 + ) + } catch (exception: IOException) { + return LoadResult.Error(exception) + } + } + + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition + } +} + +/** + * 用户收藏消息分页数据加载器 + */ +class FavoriteItemPagingSource( + private val accountService: AccountService, +) : PagingSource() { + override suspend fun load(params: LoadParams): LoadResult { + return try { + val currentPage = params.key ?: 1 + val favouriteListContainer = accountService.getMyFavouriteNotice( + page = currentPage, + pageSize = 20, + ) + LoadResult.Page( + data = favouriteListContainer.list.map { + it.toAccountFavouriteEntity() + }, + prevKey = if (currentPage == 1) null else currentPage - 1, + nextKey = if (favouriteListContainer.list.isEmpty()) null else favouriteListContainer.page + 1 + ) + } catch (exception: IOException) { + return LoadResult.Error(exception) + } + } + + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition + } +} + +/** + * 用户关注消息分页数据加载器 + */ +class FollowItemPagingSource( + private val accountService: AccountService, +) : PagingSource() { + override suspend fun load(params: LoadParams): LoadResult { + return try { + val currentPage = params.key ?: 1 + val followListContainer = accountService.getMyFollowNotice( + page = currentPage, + pageSize = 20, + ) + + LoadResult.Page( + data = followListContainer.list.map { + it.copy( + avatar = "${ApiClient.BASE_SERVER}${it.avatar}", + ) + }, + prevKey = if (currentPage == 1) null else currentPage - 1, + nextKey = if (followListContainer.list.isEmpty()) null else followListContainer.page + 1 + ) + } catch (exception: IOException) { + return LoadResult.Error(exception) + } + } + + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition + } +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/entity/Comment.kt b/app/src/main/java/com/aiosman/riderpro/entity/Comment.kt new file mode 100644 index 0000000..338dd5d --- /dev/null +++ b/app/src/main/java/com/aiosman/riderpro/entity/Comment.kt @@ -0,0 +1,54 @@ +package com.aiosman.riderpro.entity + +import androidx.paging.PagingSource +import androidx.paging.PagingState +import com.aiosman.riderpro.data.CommentRemoteDataSource +import com.aiosman.riderpro.data.NoticePost +import java.io.IOException +import java.util.Date + +data class CommentEntity( + val id: Int, + val name: String, + val comment: String, + val date: Date, + val likes: Int, + val replies: List, + val postId: Int = 0, + val avatar: String, + val author: Long, + var liked: Boolean, + var unread: Boolean = false, + var post: NoticePost? +) + +class CommentPagingSource( + private val remoteDataSource: CommentRemoteDataSource, + private val postId: Int? = null, + private val postUser: Int? = null, + private val selfNotice: Boolean? = null +) : PagingSource() { + override suspend fun load(params: LoadParams): LoadResult { + return try { + val currentPage = params.key ?: 1 + val comments = remoteDataSource.getComments( + pageNumber = currentPage, + postId = postId, + postUser = postUser, + selfNotice = selfNotice + ) + LoadResult.Page( + data = comments.list, + prevKey = if (currentPage == 1) null else currentPage - 1, + nextKey = if (comments.list.isEmpty()) null else comments.page + 1 + ) + } catch (exception: IOException) { + return LoadResult.Error(exception) + } + } + + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/entity/Moment.kt b/app/src/main/java/com/aiosman/riderpro/entity/Moment.kt new file mode 100644 index 0000000..f5309d6 --- /dev/null +++ b/app/src/main/java/com/aiosman/riderpro/entity/Moment.kt @@ -0,0 +1,236 @@ +package com.aiosman.riderpro.entity + +import androidx.annotation.DrawableRes +import androidx.paging.PagingSource +import androidx.paging.PagingState +import com.aiosman.riderpro.data.ListContainer +import com.aiosman.riderpro.data.MomentService +import com.aiosman.riderpro.data.ServiceException +import com.aiosman.riderpro.data.UploadImage +import com.aiosman.riderpro.data.api.ApiClient +import com.aiosman.riderpro.entity.MomentEntity +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import java.io.File +import java.io.IOException +import java.util.Date + +/** + * 动态分页加载器 + */ +class MomentPagingSource( + private val remoteDataSource: MomentRemoteDataSource, + private val author: Int? = null, + private val timelineId: Int? = null, + private val contentSearch: String? = null +) : PagingSource() { + override suspend fun load(params: LoadParams): LoadResult { + return try { + val currentPage = params.key ?: 1 + val moments = remoteDataSource.getMoments( + pageNumber = currentPage, + author = author, + timelineId = timelineId, + contentSearch = contentSearch + ) + + LoadResult.Page( + data = moments.list, + prevKey = if (currentPage == 1) null else currentPage - 1, + nextKey = if (moments.list.isEmpty()) null else moments.page + 1 + ) + } catch (exception: IOException) { + return LoadResult.Error(exception) + } + } + + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition + } + +} + +class MomentRemoteDataSource( + private val momentService: MomentService, +) { + suspend fun getMoments( + pageNumber: Int, + author: Int?, + timelineId: Int?, + contentSearch: String? + ): ListContainer { + return momentService.getMoments(pageNumber, author, timelineId, contentSearch) + } +} + +class MomentServiceImpl() : MomentService { + val momentBackend = MomentBackend() + + override suspend fun getMoments( + pageNumber: Int, + author: Int?, + timelineId: Int?, + contentSearch: String? + ): ListContainer { + return momentBackend.fetchMomentItems(pageNumber, author, timelineId, contentSearch) + } + + override suspend fun getMomentById(id: Int): MomentEntity { + return momentBackend.getMomentById(id) + } + + + override suspend fun likeMoment(id: Int) { + momentBackend.likeMoment(id) + } + + override suspend fun dislikeMoment(id: Int) { + momentBackend.dislikeMoment(id) + } + + override suspend fun createMoment( + content: String, + authorId: Int, + images: List, + relPostId: Int? + ): MomentEntity { + return momentBackend.createMoment(content, authorId, images, relPostId) + } + + override suspend fun favoriteMoment(id: Int) { + momentBackend.favoriteMoment(id) + } + + override suspend fun unfavoriteMoment(id: Int) { + momentBackend.unfavoriteMoment(id) + } + +} + +class MomentBackend { + val DataBatchSize = 20 + suspend fun fetchMomentItems( + pageNumber: Int, + author: Int? = null, + timelineId: Int?, + contentSearch: String? + ): ListContainer { + val resp = ApiClient.api.getPosts( + pageSize = DataBatchSize, + page = pageNumber, + timelineId = timelineId, + authorId = author, + contentSearch = contentSearch + ) + val body = resp.body() ?: throw ServiceException("Failed to get moments") + return ListContainer( + total = body.total, + page = pageNumber, + pageSize = DataBatchSize, + list = body.list.map { it.toMomentItem() } + ) + } + + suspend fun getMomentById(id: Int): MomentEntity { + var resp = ApiClient.api.getPost(id) + var body = resp.body()?.data ?: throw ServiceException("Failed to get moment") + return body.toMomentItem() + } + + suspend fun likeMoment(id: Int) { + ApiClient.api.likePost(id) + } + + suspend fun dislikeMoment(id: Int) { + ApiClient.api.dislikePost(id) + } + + fun createMultipartBody(file: File, name: String): MultipartBody.Part { + val requestFile = RequestBody.create("image/*".toMediaTypeOrNull(), file) + return MultipartBody.Part.createFormData(name, file.name, requestFile) + } + + suspend fun createMoment( + content: String, + authorId: Int, + imageUriList: List, + relPostId: Int? + ): MomentEntity { + val textContent = content.toRequestBody("text/plain".toMediaTypeOrNull()) + val imageList = imageUriList.map { item -> + val file = item.file + createMultipartBody(file, "image") + } + val response = ApiClient.api.createPost(imageList, textContent = textContent) + val body = response.body()?.data ?: throw ServiceException("Failed to create moment") + return body.toMomentItem() + + } + + suspend fun favoriteMoment(id: Int) { + ApiClient.api.favoritePost(id) + } + + suspend fun unfavoriteMoment(id: Int) { + ApiClient.api.unfavoritePost(id) + } + +} + +/** + * 动态图片 + */ +data class MomentImageEntity( + // 图片ID + val id: Long, + // 图片URL + val url: String, + // 缩略图URL + val thumbnail: String, + // 图片BlurHash + val blurHash: String? = null +) + +/** + * 动态 + */ +data class MomentEntity( + // 动态ID + val id: Int, + // 作者头像 + val avatar: String, + // 作者昵称 + val nickname: String, + // 区域 + val location: String, + // 动态时间 + val time: Date, + // 是否关注 + val followStatus: Boolean, + // 动态内容 + val momentTextContent: String, + // 动态图片 + @DrawableRes val momentPicture: Int, + // 点赞数 + val likeCount: Int, + // 评论数 + val commentCount: Int, + // 分享数 + val shareCount: Int, + // 收藏数 + val favoriteCount: Int, + // 动态图片列表 + val images: List = emptyList(), + // 作者ID + val authorId: Int = 0, + // 是否点赞 + var liked: Boolean = false, + // 关联动态ID + var relPostId: Int? = null, + // 关联动态 + var relMoment: MomentEntity? = null, + // 是否收藏 + var isFavorite: Boolean = false +) \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/entity/User.kt b/app/src/main/java/com/aiosman/riderpro/entity/User.kt new file mode 100644 index 0000000..e7859e7 --- /dev/null +++ b/app/src/main/java/com/aiosman/riderpro/entity/User.kt @@ -0,0 +1,36 @@ +package com.aiosman.riderpro.entity + +import androidx.paging.PagingSource +import androidx.paging.PagingState +import com.aiosman.riderpro.data.UserService +import java.io.IOException + +/** + * 用户信息分页加载器 + */ +class AccountPagingSource( + private val userService: UserService, + private val nickname: String? = null +) : PagingSource() { + override suspend fun load(params: LoadParams): LoadResult { + return try { + val currentPage = params.key ?: 1 + val users = userService.getUsers( + page = currentPage, + nickname = nickname + ) + LoadResult.Page( + data = users.list, + prevKey = if (currentPage == 1) null else currentPage - 1, + nextKey = if (users.list.isEmpty()) null else users.page + 1 + ) + } catch (exception: IOException) { + return LoadResult.Error(exception) + } + } + + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/exp/Date.kt b/app/src/main/java/com/aiosman/riderpro/exp/Date.kt index 09aea8e..78dc73a 100644 --- a/app/src/main/java/com/aiosman/riderpro/exp/Date.kt +++ b/app/src/main/java/com/aiosman/riderpro/exp/Date.kt @@ -6,6 +6,9 @@ import com.aiosman.riderpro.data.api.ApiClient import java.util.Date import java.util.Locale +/** + * 格式化时间为 xx 前 + */ fun Date.timeAgo(): String { val now = Date() val diffInMillis = now.time - this.time @@ -25,6 +28,9 @@ fun Date.timeAgo(): String { } } +/** + * 格式化时间为 xx-xx + */ fun Date.formatPostTime(): String { val now = Calendar.getInstance() val calendar = Calendar.getInstance() diff --git a/app/src/main/java/com/aiosman/riderpro/model/MomentEntity.kt b/app/src/main/java/com/aiosman/riderpro/model/MomentEntity.kt deleted file mode 100644 index f5fcb27..0000000 --- a/app/src/main/java/com/aiosman/riderpro/model/MomentEntity.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.aiosman.riderpro.model - -import androidx.annotation.DrawableRes -import java.util.Date - -data class MomentImageEntity( - val id: Long, - val url: String, - val thumbnail: String, - val blurHash: String? = null -) - -data class MomentEntity( - val id: Int, - val avatar: String, - val nickname: String, - val location: String, - val time: Date, - val followStatus: Boolean, - val momentTextContent: String, - @DrawableRes val momentPicture: Int, - val likeCount: Int, - val commentCount: Int, - val shareCount: Int, - val favoriteCount: Int, - val images: List = emptyList(), - val authorId: Int = 0, - var liked: Boolean = false, - var relPostId: Int? = null, - var relMoment: MomentEntity? = null, - var isFavorite: Boolean = false -) diff --git a/app/src/main/java/com/aiosman/riderpro/test/MockDataSource.kt b/app/src/main/java/com/aiosman/riderpro/test/MockDataSource.kt deleted file mode 100644 index 81e15ae..0000000 --- a/app/src/main/java/com/aiosman/riderpro/test/MockDataSource.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.aiosman.riderpro.test - -import kotlin.math.min - -class MockDataContainer( - val success: Boolean, - val data: T? -) { - -} - -class MockListContainer( - val total: Int, - val page: Int, - val pageSize: Int, - val list: List -) { - -} - -abstract class MockDataSource { - var list = mutableListOf() - suspend fun fetchData(page: Int, pageSize: Int): MockDataContainer> { - // over page return empty - if (page * pageSize > list.size) { - return MockDataContainer(false, MockListContainer(0, page, pageSize, emptyList())) - } - val backData = list.subList((page - 1) * pageSize, min(page * pageSize, list.size)) - return MockDataContainer(true, MockListContainer(list.size, page, pageSize, backData)) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/test/SimpleUtils.kt b/app/src/main/java/com/aiosman/riderpro/test/SimpleUtils.kt deleted file mode 100644 index b4d37ea..0000000 --- a/app/src/main/java/com/aiosman/riderpro/test/SimpleUtils.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.aiosman.riderpro.test - -import androidx.paging.PagingSource -import androidx.paging.PagingState -import com.aiosman.riderpro.model.MomentEntity -import kotlinx.coroutines.delay -import kotlin.math.ceil - -class TestBackend( - private val backendDataList: List, - private val loadDelay: Long = 500, -) { - val DataBatchSize = 5 - class DesiredLoadResultPageResponse(val data: List) - /** Returns [DataBatchSize] items for a key */ - fun searchItemsByKey(key: Int): DesiredLoadResultPageResponse { - val maxKey = ceil(backendDataList.size.toFloat() / DataBatchSize).toInt() - if (key >= maxKey) { - return DesiredLoadResultPageResponse(emptyList()) - } - val from = key * DataBatchSize - val to = minOf((key + 1) * DataBatchSize, backendDataList.size) - val currentSublist = backendDataList.subList(from, to) - return DesiredLoadResultPageResponse(currentSublist) - } - fun getAllData() = TestPagingSource(this, loadDelay) -} -class TestPagingSource( - private val backend: TestBackend, - private val loadDelay: Long, -) : PagingSource() { - override suspend fun load(params: LoadParams): LoadResult { - // Simulate latency - delay(loadDelay) - val pageNumber = params.key ?: 0 - val response = backend.searchItemsByKey(pageNumber) - // Since 0 is the lowest page number, return null to signify no more pages should - // be loaded before it. - val prevKey = if (pageNumber > 0) pageNumber - 1 else null - // This API defines that it's out of data when a page returns empty. When out of - // data, we return `null` to signify no more pages should be loaded - val nextKey = if (response.data.isNotEmpty()) pageNumber + 1 else null - return LoadResult.Page(data = response.data, prevKey = prevKey, nextKey = nextKey) - } - override fun getRefreshKey(state: PagingState): Int? { - return state.anchorPosition?.let { - state.closestPageToPosition(it)?.prevKey?.plus(1) - ?: state.closestPageToPosition(it)?.nextKey?.minus(1) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/test/TestDatabase.kt b/app/src/main/java/com/aiosman/riderpro/test/TestDatabase.kt deleted file mode 100644 index d740378..0000000 --- a/app/src/main/java/com/aiosman/riderpro/test/TestDatabase.kt +++ /dev/null @@ -1,163 +0,0 @@ -package com.aiosman.riderpro.test - -import com.aiosman.riderpro.R -import com.aiosman.riderpro.data.AccountProfileEntity -import com.aiosman.riderpro.data.CommentEntity -import com.aiosman.riderpro.model.MomentEntity -import com.aiosman.riderpro.model.MomentImageEntity -import com.google.gson.Gson -import com.google.gson.GsonBuilder -import io.github.serpro69.kfaker.faker -import java.io.File -import java.util.Calendar -import java.util.Date - -object TestDatabase { - var momentData = emptyList() - var accountData = emptyList() - var commentEntity = emptyList() - var commentIdCounter = 0 - var momentIdCounter = 0 - var selfId = 1 - var imageList = listOf( - "https://img.freepik.com/free-photo/white-billboard-template_23-2147726635.jpg?t=st=1722150015~exp=1722153615~hmac=5540620196d7898215d822be26353c87a63d51bbfb2b814e032626e1948a1583&w=740", - "https://img.freepik.com/free-photo/minimal-clothing-label-fashion-brands_53876-111053.jpg?w=1060&t=st=1722150122~exp=1722150722~hmac=67f8a2b6abfe3d08714cf0cc0085485c3221e1ba00dda14378b03753dce39153", - "https://img.freepik.com/free-photo/marketing-strategy-planning-strategy-concept_53876-42950.jpg", - "https://t4.ftcdn.net/jpg/02/27/00/89/240_F_227008949_5O7yXuEqTwUgs3BGqdcvrNutM5MSxs1t.jpg", - "https://t4.ftcdn.net/jpg/01/86/86/49/240_F_186864971_NixcoDg1zBjjN7soUNhpEVraI4vdzOFD.jpg", - "https://t3.ftcdn.net/jpg/00/84/01/30/240_F_84013057_fsOdzBgskSFUyWyD6YKjIAdtKdBPiKRD.jpg", - "https://t4.ftcdn.net/jpg/00/93/89/23/240_F_93892312_SNyGGruVaWKpJQiVG314gIQmS4EAghdy.jpg", - "https://t3.ftcdn.net/jpg/02/94/56/58/240_F_294565895_IOqZC2OpcHGEibWF04MPEP09KZaewEl5.jpg", - "https://t3.ftcdn.net/jpg/01/01/66/84/240_F_101668484_FopHBSMBq4t6BlvwI9awPMzUdi501sJ7.jpg", - "https://t3.ftcdn.net/jpg/05/65/11/60/240_F_565116019_oHbZ6Hc8VYCMcZWpexXF7Z5lOWeNNYtD.jpg", - "https://t3.ftcdn.net/jpg/03/52/21/48/240_F_352214843_dQ3JtTJrKyqrh2yd1emYCDPSrzrwqaNK.jpg", - "https://t3.ftcdn.net/jpg/07/22/47/16/240_F_722471661_T25r329RFRxgK88S6oBJ9dUksOC2arLl.jpg", - "https://t3.ftcdn.net/jpg/02/18/11/26/240_F_218112603_jBChzLJGuz8smPZsdFsy17wB0O0QF3Xo.jpg", - "https://t4.ftcdn.net/jpg/04/11/49/07/240_F_411490703_KRvV0aRyxHWYVUO8bGXxuQGo2mHblYnv.jpg", - "https://img.freepik.com/premium-photo/man-wearing-orange-helmet-white-background_466494-5539.jpg?ga=GA1.1.1334458544.1722150011&semt=sph", - "https://img.freepik.com/premium-photo/motorcycle-vehicle-3d-modelling_274824-502.jpg?ga=GA1.1.1334458544.1722150011&semt=sph", - "https://t3.ftcdn.net/jpg/01/68/26/06/240_F_168260687_UfaDjjs6TxcIB6BdsquSeCmYWEFmN1Sh.jpg", - "https://t3.ftcdn.net/jpg/03/48/50/34/240_F_348503435_On7Tt5Eqn7IP9QWYTQL0H1smubU8gvLv.jpg", - "https://t3.ftcdn.net/jpg/02/76/70/70/240_F_276707060_WpP9bwHWv0Wdqqn0pEgtSuIgXUvgkbs7.jpg", - "https://t3.ftcdn.net/jpg/02/65/43/04/240_F_265430460_DIHqnrziar7WL2rmW0qbDO07TbxjlPQo.jpg" - ) - var followList = emptyList>() - var likeCommentList = emptyList>() - var likeMomentList = emptyList>() - - init { - val faker = faker { - this.fakerConfig { - locale = "en" - } - } - accountData = (0..20).toList().mapIndexed { idx, _ -> - AccountProfileEntity( - id = idx, - followerCount = 0, - followingCount = 0, - nickName = faker.name.name(), - avatar = imageList.random(), - bio = "I am a software engineer", - country = faker.address.country(), - isFollowing = false - - ) - } - // make a random follow rel - for (i in 0..100) { - var person1 = accountData.random() - var persion2 = accountData.random() - followList += Pair(person1.id, persion2.id) - // update followerCount and followingCount - accountData = accountData.map { - if (it.id == person1.id) { - it.copy(followingCount = it.followingCount + 1) - } else if (it.id == persion2.id) { - it.copy(followerCount = it.followerCount + 1) - } else { - it - } - } - } - - momentData = (0..60).toList().mapIndexed { idx, _ -> - momentIdCounter += 1 - val person = accountData.random() - // make fake comment - val commentCount = faker.random.nextInt(0, 50) - for (i in 0..commentCount) { - commentIdCounter += 1 - val commentPerson = accountData.random() - var newCommentEntity = CommentEntity( - name = commentPerson.nickName, - comment = "this is comment ${commentIdCounter}", - date = Calendar.getInstance().time, - likes = 0, - replies = emptyList(), - postId = momentIdCounter, - avatar = commentPerson.avatar, - author = commentPerson.id.toLong(), - id = commentIdCounter, - liked = false, - unread = false, - post = null - ) - // generate like comment list - for (likeIdx in 0..faker.random.nextInt(0, 5)) { - val likePerson = accountData.random() - likeCommentList += Pair(commentIdCounter, likePerson.id) - newCommentEntity = newCommentEntity.copy(likes = newCommentEntity.likes + 1) - } - commentEntity += newCommentEntity - } - val likeCount = faker.random.nextInt(0, 5) - for (i in 0..likeCount) { - val likePerson = accountData.random() - likeMomentList += Pair(momentIdCounter, likePerson.id) - } - MomentEntity( - id = momentIdCounter, - avatar = person.avatar, - nickname = person.nickName, - location = person.country, - time = Date(), - followStatus = false, - momentTextContent = "By strongarming Ducati into giving him the factory seat.Marquez effectively …", - momentPicture = R.drawable.default_moment_img, - likeCount = likeCount, - commentCount = commentCount + 1, - shareCount = faker.random.nextInt(0, 100), - favoriteCount = faker.random.nextInt(0, 100), - images = imageList.shuffled().take(3).map { - MomentImageEntity( - id = faker.random.nextLong(), - url = it, - thumbnail = it - ) - }, - authorId = person.id - ) - } - } - - - fun updateMomentById(id: Int, momentEntity: MomentEntity) { - momentData = momentData.map { - if (it.id == id) { - momentEntity - } else { - it - } - } - } - - fun saveResultToJsonFile() { - val gson: Gson = GsonBuilder().setPrettyPrinting().create() - - // save accountData to json file - File("accountData.json").writeText(accountData.toString()) - // save momentData to json file - // save comment to json file - } -} \ No newline at end of file diff --git a/app/src/main/java/com/aiosman/riderpro/ui/account/changepassword.kt b/app/src/main/java/com/aiosman/riderpro/ui/account/changepassword.kt index 80db0c2..1f3a277 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/account/changepassword.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/account/changepassword.kt @@ -9,16 +9,27 @@ import com.aiosman.riderpro.data.AccountService import com.aiosman.riderpro.data.AccountServiceImpl import kotlinx.coroutines.launch +/** + * 修改密码页面的 ViewModel + */ class ChangePasswordViewModel { - val accountService :AccountService = AccountServiceImpl() + val accountService: AccountService = AccountServiceImpl() + + /** + * 修改密码 + * @param currentPassword 当前密码 + * @param newPassword 新密码 + */ suspend fun changePassword(currentPassword: String, newPassword: String) { accountService.changeAccountPassword(currentPassword, newPassword) } } -@Composable -fun ChangePasswordScreen( -) { +/** + * 修改密码页面 + */ +@Composable +fun ChangePasswordScreen() { val viewModel = remember { ChangePasswordViewModel() } var currentPassword by remember { mutableStateOf("") } var newPassword by remember { mutableStateOf("") } diff --git a/app/src/main/java/com/aiosman/riderpro/ui/account/edit.kt b/app/src/main/java/com/aiosman/riderpro/ui/account/edit.kt index e147274..02de7ad 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/account/edit.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/account/edit.kt @@ -33,10 +33,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp -import com.aiosman.riderpro.data.AccountProfileEntity +import com.aiosman.riderpro.entity.AccountProfileEntity import com.aiosman.riderpro.data.AccountService import com.aiosman.riderpro.data.AccountServiceImpl -import com.aiosman.riderpro.data.TestUserServiceImpl +import com.aiosman.riderpro.data.UserServiceImpl import com.aiosman.riderpro.data.UploadImage import com.aiosman.riderpro.data.UserService import com.aiosman.riderpro.ui.composables.CustomAsyncImage @@ -44,10 +44,12 @@ import com.aiosman.riderpro.ui.modifiers.noRippleClickable import com.aiosman.riderpro.ui.post.NewPostViewModel.uriToFile import kotlinx.coroutines.launch +/** + * 编辑用户资料界面 + */ @OptIn(ExperimentalMaterial3Api::class) @Composable fun AccountEditScreen() { - val userService: UserService = TestUserServiceImpl() val accountService: AccountService = AccountServiceImpl() var name by remember { mutableStateOf("") } var bio by remember { mutableStateOf("") } @@ -59,8 +61,11 @@ fun AccountEditScreen() { } val scope = rememberCoroutineScope() val context = LocalContext.current - suspend fun reloadProfile() { + /** + * 加载用户资料 + */ + suspend fun reloadProfile() { accountService.getMyAccountProfile().let { profile = it name = it.nickName @@ -70,19 +75,12 @@ fun AccountEditScreen() { } - fun updateUserAvatar(uri: String) { - scope.launch { - accountService.updateAvatar(uri) - reloadProfile() - } - - } fun updateUserProfile() { scope.launch { val newAvatar = imageUrl?.let { val cursor = context.contentResolver.query(it, null, null, null, null) var newAvatar: UploadImage? = null - cursor?.use {cur -> + cursor?.use { cur -> if (cur.moveToFirst()) { val displayName = cur.getString(cur.getColumnIndex("_display_name")) val extension = displayName.substringAfterLast(".") @@ -135,13 +133,13 @@ fun AccountEditScreen() { ) } } - ) { - padding -> + ) { padding -> profile?.let { Column( modifier = Modifier .fillMaxSize() - .padding(padding).padding(horizontal = 24.dp), + .padding(padding) + .padding(horizontal = 24.dp), horizontalAlignment = Alignment.CenterHorizontally ) { CustomAsyncImage( diff --git a/app/src/main/java/com/aiosman/riderpro/ui/comment/CommentModal.kt b/app/src/main/java/com/aiosman/riderpro/ui/comment/CommentModal.kt index a005aec..a04072f 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/comment/CommentModal.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/comment/CommentModal.kt @@ -49,19 +49,22 @@ import androidx.paging.cachedIn import androidx.paging.compose.collectAsLazyPagingItems import com.aiosman.riderpro.ui.post.CommentsSection import com.aiosman.riderpro.R -import com.aiosman.riderpro.data.CommentEntity -import com.aiosman.riderpro.data.CommentPagingSource +import com.aiosman.riderpro.entity.CommentEntity +import com.aiosman.riderpro.entity.CommentPagingSource import com.aiosman.riderpro.data.CommentRemoteDataSource import com.aiosman.riderpro.data.CommentService -import com.aiosman.riderpro.data.TestCommentServiceImpl +import com.aiosman.riderpro.data.CommentServiceImpl import com.aiosman.riderpro.ui.modifiers.noRippleClickable import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch +/** + * 评论弹窗的 ViewModel + */ class CommentModalViewModel( val postId: Int? ) : ViewModel() { - val commentService: CommentService = TestCommentServiceImpl() + val commentService: CommentService = CommentServiceImpl() val commentsFlow: Flow> = Pager( config = PagingConfig(pageSize = 20, enablePlaceholders = false), pagingSourceFactory = { @@ -72,6 +75,9 @@ class CommentModalViewModel( } ).flow.cachedIn(viewModelScope) + /** + * 创建评论 + */ suspend fun createComment(content: String) { postId?.let { commentService.createComment(postId, content) @@ -79,7 +85,13 @@ class CommentModalViewModel( } } -@Preview + +/** + * 评论弹窗 + * @param postId 帖子ID + * @param onCommentAdded 评论添加回调 + * @param onDismiss 关闭回调 + */ @Composable fun CommentModalContent( postId: Int? = null, diff --git a/app/src/main/java/com/aiosman/riderpro/ui/composables/RelPostCard.kt b/app/src/main/java/com/aiosman/riderpro/ui/composables/RelPostCard.kt index 4c9172d..59e941a 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/composables/RelPostCard.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/composables/RelPostCard.kt @@ -10,7 +10,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import coil.compose.AsyncImage -import com.aiosman.riderpro.model.MomentEntity +import com.aiosman.riderpro.entity.MomentEntity import com.aiosman.riderpro.ui.index.tabs.moment.MomentTopRowGroup @Composable diff --git a/app/src/main/java/com/aiosman/riderpro/ui/favourite/FavouritePageViewModel.kt b/app/src/main/java/com/aiosman/riderpro/ui/favourite/FavouritePageViewModel.kt index 6871e3b..1915ccb 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/favourite/FavouritePageViewModel.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/favourite/FavouritePageViewModel.kt @@ -7,9 +7,9 @@ import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData import androidx.paging.cachedIn -import com.aiosman.riderpro.data.AccountFavouriteEntity +import com.aiosman.riderpro.entity.AccountFavouriteEntity import com.aiosman.riderpro.data.AccountService -import com.aiosman.riderpro.data.FavoriteItemPagingSource +import com.aiosman.riderpro.entity.FavoriteItemPagingSource import com.aiosman.riderpro.data.AccountServiceImpl import com.aiosman.riderpro.data.api.ApiClient import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody @@ -18,6 +18,9 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +/** + * 收藏消息列表的 ViewModel + */ object FavouritePageViewModel : ViewModel() { private val accountService: AccountService = AccountServiceImpl() private val _favouriteItemsFlow = @@ -38,6 +41,7 @@ object FavouritePageViewModel : ViewModel() { } } } + // 更新收藏消息的查看时间 suspend fun updateNotice() { var now = Calendar.getInstance().time accountService.updateNotice( diff --git a/app/src/main/java/com/aiosman/riderpro/ui/favourite/FavouriteScreen.kt b/app/src/main/java/com/aiosman/riderpro/ui/favourite/FavouriteScreen.kt index 9c5985c..41af4fb 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/favourite/FavouriteScreen.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/favourite/FavouriteScreen.kt @@ -37,11 +37,12 @@ import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder import com.aiosman.riderpro.ui.like.ActionNoticeItem import com.aiosman.riderpro.ui.like.LikePageViewModel -@Preview +/** + * 收藏消息界面 + */ @Composable fun FavouriteScreen() { val model = FavouritePageViewModel - val coroutineScope = rememberCoroutineScope() val listState = rememberLazyListState() var dataFlow = model.favouriteItemsFlow var favourites = dataFlow.collectAsLazyPagingItems() @@ -59,7 +60,9 @@ fun FavouriteScreen() { .padding(horizontal = 16.dp) ) { Box( - modifier = Modifier.fillMaxWidth().padding(vertical = 16.dp) + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp) ) { NoticeScreenHeader( "FAVOURITE", diff --git a/app/src/main/java/com/aiosman/riderpro/ui/follower/FollowerPage.kt b/app/src/main/java/com/aiosman/riderpro/ui/follower/FollowerPage.kt index ef17eb0..55a13a3 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/follower/FollowerPage.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/follower/FollowerPage.kt @@ -35,7 +35,9 @@ import com.aiosman.riderpro.ui.composables.CustomAsyncImage import com.aiosman.riderpro.ui.modifiers.noRippleClickable import kotlinx.coroutines.launch -@Preview +/** + * 关注消息列表 + */ @Composable fun FollowerScreen() { val scope = rememberCoroutineScope() diff --git a/app/src/main/java/com/aiosman/riderpro/ui/follower/FollowerViewModel.kt b/app/src/main/java/com/aiosman/riderpro/ui/follower/FollowerViewModel.kt index 620c6da..af2c506 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/follower/FollowerViewModel.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/follower/FollowerViewModel.kt @@ -10,9 +10,9 @@ import androidx.paging.cachedIn import androidx.paging.map import com.aiosman.riderpro.data.AccountFollow import com.aiosman.riderpro.data.AccountService -import com.aiosman.riderpro.data.FollowItemPagingSource +import com.aiosman.riderpro.entity.FollowItemPagingSource import com.aiosman.riderpro.data.AccountServiceImpl -import com.aiosman.riderpro.data.TestUserServiceImpl +import com.aiosman.riderpro.data.UserServiceImpl import com.aiosman.riderpro.data.UserService import com.aiosman.riderpro.data.api.ApiClient import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody @@ -21,9 +21,12 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +/** + * 关注消息列表的 ViewModel + */ object FollowerViewModel : ViewModel() { private val accountService: AccountService = AccountServiceImpl() - private val userService: UserService = TestUserServiceImpl() + private val userService: UserService = UserServiceImpl() private val _followerItemsFlow = MutableStateFlow>(PagingData.empty()) val followerItemsFlow = _followerItemsFlow.asStateFlow() diff --git a/app/src/main/java/com/aiosman/riderpro/ui/imageviewer/ImageViewerViewModel.kt b/app/src/main/java/com/aiosman/riderpro/ui/imageviewer/ImageViewerViewModel.kt index 5d34b01..efba76b 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/imageviewer/ImageViewerViewModel.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/imageviewer/ImageViewerViewModel.kt @@ -1,6 +1,6 @@ package com.aiosman.riderpro.ui.imageviewer -import com.aiosman.riderpro.model.MomentImageEntity +import com.aiosman.riderpro.entity.MomentImageEntity object ImageViewerViewModel { var imageList = mutableListOf() diff --git a/app/src/main/java/com/aiosman/riderpro/ui/index/Index.kt b/app/src/main/java/com/aiosman/riderpro/ui/index/Index.kt index 7941f52..8ac02ae 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/index/Index.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/index/Index.kt @@ -60,7 +60,7 @@ fun IndexScreen() { val systemUiController = rememberSystemUiController() LaunchedEffect(Unit) { -// systemUiController.setNavigationBarColor(Color.Transparent) + systemUiController.setNavigationBarColor(Color.Transparent) } Scaffold( bottomBar = { diff --git a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/message/MessageList.kt b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/message/MessageList.kt index 2e65eee..028eadd 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/message/MessageList.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/message/MessageList.kt @@ -32,17 +32,18 @@ import androidx.compose.ui.unit.sp import androidx.paging.compose.collectAsLazyPagingItems import com.aiosman.riderpro.LocalNavController import com.aiosman.riderpro.R -import com.aiosman.riderpro.data.CommentEntity +import com.aiosman.riderpro.entity.CommentEntity import com.aiosman.riderpro.exp.timeAgo import com.aiosman.riderpro.ui.NavigationRoute -import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder import com.aiosman.riderpro.ui.composables.CustomAsyncImage import com.aiosman.riderpro.ui.modifiers.noRippleClickable import com.google.accompanist.systemuicontroller.rememberSystemUiController -@Preview(showBackground = true) +/** + * 消息列表界面 + */ @Composable fun NotificationsScreen() { val model = MessageListViewModel diff --git a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/message/MessageListViewModel.kt b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/message/MessageListViewModel.kt index 5e74970..be9b233 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/message/MessageListViewModel.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/message/MessageListViewModel.kt @@ -12,12 +12,12 @@ import androidx.paging.cachedIn import androidx.paging.map import com.aiosman.riderpro.data.AccountNotice import com.aiosman.riderpro.data.AccountService -import com.aiosman.riderpro.data.CommentEntity -import com.aiosman.riderpro.data.CommentPagingSource +import com.aiosman.riderpro.entity.CommentEntity +import com.aiosman.riderpro.entity.CommentPagingSource import com.aiosman.riderpro.data.CommentRemoteDataSource import com.aiosman.riderpro.data.CommentService import com.aiosman.riderpro.data.AccountServiceImpl -import com.aiosman.riderpro.data.TestCommentServiceImpl +import com.aiosman.riderpro.data.CommentServiceImpl import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collectLatest @@ -27,7 +27,7 @@ object MessageListViewModel : ViewModel() { val accountService: AccountService = AccountServiceImpl() var noticeInfo by mutableStateOf(null) - private val commentService: CommentService = TestCommentServiceImpl() + private val commentService: CommentService = CommentServiceImpl() private val _commentItemsFlow = MutableStateFlow>(PagingData.empty()) val commentItemsFlow = _commentItemsFlow.asStateFlow() diff --git a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/moment/Moment.kt b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/moment/Moment.kt index 2d02b8f..e0bf6b6 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/moment/Moment.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/moment/Moment.kt @@ -42,7 +42,6 @@ import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.key import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -65,9 +64,9 @@ import com.aiosman.riderpro.LocalAnimatedContentScope import com.aiosman.riderpro.LocalNavController import com.aiosman.riderpro.LocalSharedTransitionScope import com.aiosman.riderpro.R +import com.aiosman.riderpro.entity.MomentEntity +import com.aiosman.riderpro.entity.MomentImageEntity import com.aiosman.riderpro.exp.timeAgo -import com.aiosman.riderpro.model.MomentEntity -import com.aiosman.riderpro.model.MomentImageEntity import com.aiosman.riderpro.ui.NavigationRoute import com.aiosman.riderpro.ui.comment.CommentModalContent import com.aiosman.riderpro.ui.composables.AnimatedCounter @@ -78,9 +77,11 @@ import com.aiosman.riderpro.ui.composables.RelPostCard import com.aiosman.riderpro.ui.modifiers.noRippleClickable import com.aiosman.riderpro.ui.post.NewPostViewModel import com.aiosman.riderpro.ui.post.PostViewModel -import com.google.accompanist.systemuicontroller.rememberSystemUiController import kotlinx.coroutines.launch +/** + * 动态列表 + */ @OptIn(ExperimentalMaterialApi::class) @Composable fun MomentsList() { @@ -370,21 +371,21 @@ fun PostImageView( // ) // .fillMaxSize() // ) - CustomAsyncImage( - context, - image.thumbnail, - contentDescription = "Image", - blurHash = image.blurHash, - contentScale = ContentScale.Crop, - modifier = Modifier - .fillMaxSize() + CustomAsyncImage( + context, + image.thumbnail, + contentDescription = "Image", + blurHash = image.blurHash, + contentScale = ContentScale.Crop, + modifier = Modifier + .fillMaxSize() // .noRippleClickable { // ImageViewerViewModel.asNew(images, page) // navController.navigate( // NavigationRoute.ImageViewer.route // ) // } - ) + ) } diff --git a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/moment/MomentViewModel.kt b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/moment/MomentViewModel.kt index 6188a23..9fc321f 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/moment/MomentViewModel.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/moment/MomentViewModel.kt @@ -8,12 +8,12 @@ import androidx.paging.PagingData import androidx.paging.cachedIn import androidx.paging.map import com.aiosman.riderpro.data.AccountService -import com.aiosman.riderpro.data.MomentPagingSource -import com.aiosman.riderpro.data.MomentRemoteDataSource +import com.aiosman.riderpro.entity.MomentPagingSource +import com.aiosman.riderpro.entity.MomentRemoteDataSource import com.aiosman.riderpro.data.MomentService import com.aiosman.riderpro.data.AccountServiceImpl -import com.aiosman.riderpro.data.TestMomentServiceImpl -import com.aiosman.riderpro.model.MomentEntity +import com.aiosman.riderpro.entity.MomentServiceImpl +import com.aiosman.riderpro.entity.MomentEntity import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collectLatest @@ -21,7 +21,7 @@ import kotlinx.coroutines.launch object MomentViewModel : ViewModel() { - private val momentService: MomentService = TestMomentServiceImpl() + private val momentService: MomentService = MomentServiceImpl() private val _momentsFlow = MutableStateFlow>(PagingData.empty()) val momentsFlow = _momentsFlow.asStateFlow() val accountService: AccountService = AccountServiceImpl() diff --git a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/MyProfileViewModel.kt b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/MyProfileViewModel.kt index be1dc26..8dc845f 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/MyProfileViewModel.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/MyProfileViewModel.kt @@ -7,19 +7,19 @@ import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData import com.aiosman.riderpro.AppStore -import com.aiosman.riderpro.data.AccountProfileEntity +import com.aiosman.riderpro.entity.AccountProfileEntity import com.aiosman.riderpro.data.AccountService import com.aiosman.riderpro.data.AccountServiceImpl -import com.aiosman.riderpro.data.MomentPagingSource -import com.aiosman.riderpro.data.MomentRemoteDataSource -import com.aiosman.riderpro.data.TestMomentServiceImpl -import com.aiosman.riderpro.data.TestUserServiceImpl -import com.aiosman.riderpro.model.MomentEntity +import com.aiosman.riderpro.entity.MomentPagingSource +import com.aiosman.riderpro.entity.MomentRemoteDataSource +import com.aiosman.riderpro.entity.MomentServiceImpl +import com.aiosman.riderpro.data.UserServiceImpl +import com.aiosman.riderpro.entity.MomentEntity import kotlinx.coroutines.flow.Flow object MyProfileViewModel { val service: AccountService = AccountServiceImpl() - val userService = TestUserServiceImpl() + val userService = UserServiceImpl() var profile by mutableStateOf(null) var momentsFlow by mutableStateOf>?>(null) suspend fun loadProfile() { @@ -28,7 +28,7 @@ object MyProfileViewModel { config = PagingConfig(pageSize = 5, enablePlaceholders = false), pagingSourceFactory = { MomentPagingSource( - MomentRemoteDataSource(TestMomentServiceImpl()), + MomentRemoteDataSource(MomentServiceImpl()), author = profile?.id ?: 0, ) diff --git a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/Profile.kt b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/Profile.kt index 28e1cd0..dee1f3f 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/Profile.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/index/tabs/profile/Profile.kt @@ -4,7 +4,6 @@ import androidx.annotation.DrawableRes import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.foundation.Image import androidx.compose.foundation.border -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -47,14 +46,13 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import androidx.paging.compose.collectAsLazyPagingItems -import coil.compose.AsyncImage import com.aiosman.riderpro.LocalAnimatedContentScope import com.aiosman.riderpro.LocalNavController import com.aiosman.riderpro.LocalSharedTransitionScope import com.aiosman.riderpro.R -import com.aiosman.riderpro.data.AccountProfileEntity +import com.aiosman.riderpro.entity.AccountProfileEntity import com.aiosman.riderpro.exp.formatPostTime -import com.aiosman.riderpro.model.MomentEntity +import com.aiosman.riderpro.entity.MomentEntity import com.aiosman.riderpro.ui.NavigationRoute import com.aiosman.riderpro.ui.composables.CustomAsyncImage import com.aiosman.riderpro.ui.modifiers.noRippleClickable 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 98b7d0a..269e243 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 @@ -44,7 +44,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.paging.compose.collectAsLazyPagingItems import com.aiosman.riderpro.LocalNavController -import com.aiosman.riderpro.data.AccountProfileEntity +import com.aiosman.riderpro.entity.AccountProfileEntity import com.aiosman.riderpro.ui.composables.CustomAsyncImage import com.aiosman.riderpro.ui.index.tabs.moment.MomentCard import com.aiosman.riderpro.ui.modifiers.noRippleClickable 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 8013089..9631f68 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,16 +9,14 @@ import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData import androidx.paging.cachedIn -import com.aiosman.riderpro.data.AccountPagingSource -import com.aiosman.riderpro.data.AccountProfileEntity -import com.aiosman.riderpro.data.MomentPagingSource -import com.aiosman.riderpro.data.MomentRemoteDataSource +import com.aiosman.riderpro.entity.AccountPagingSource +import com.aiosman.riderpro.entity.AccountProfileEntity +import com.aiosman.riderpro.entity.MomentPagingSource +import com.aiosman.riderpro.entity.MomentRemoteDataSource import com.aiosman.riderpro.data.MomentService -import com.aiosman.riderpro.data.TestMomentServiceImpl -import com.aiosman.riderpro.data.TestUserServiceImpl -import com.aiosman.riderpro.model.MomentEntity -import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel -import com.aiosman.riderpro.ui.index.tabs.moment.MomentViewModel.accountService +import com.aiosman.riderpro.entity.MomentServiceImpl +import com.aiosman.riderpro.data.UserServiceImpl +import com.aiosman.riderpro.entity.MomentEntity import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collectLatest @@ -26,11 +24,11 @@ import kotlinx.coroutines.launch object SearchViewModel : ViewModel() { var searchText by mutableStateOf("") - private val momentService: MomentService = TestMomentServiceImpl() + private val momentService: MomentService = MomentServiceImpl() private val _momentsFlow = MutableStateFlow>(PagingData.empty()) val momentsFlow = _momentsFlow.asStateFlow() - private val userService = TestUserServiceImpl() + private val userService = UserServiceImpl() private val _usersFlow = MutableStateFlow>(PagingData.empty()) val usersFlow = _usersFlow.asStateFlow() var showResult by mutableStateOf(false) diff --git a/app/src/main/java/com/aiosman/riderpro/ui/like/LikePageViewModel.kt b/app/src/main/java/com/aiosman/riderpro/ui/like/LikePageViewModel.kt index e35aae4..8d3206e 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/like/LikePageViewModel.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/like/LikePageViewModel.kt @@ -7,9 +7,9 @@ import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData import androidx.paging.cachedIn -import com.aiosman.riderpro.data.AccountLikeEntity +import com.aiosman.riderpro.entity.AccountLikeEntity import com.aiosman.riderpro.data.AccountService -import com.aiosman.riderpro.data.LikeItemPagingSource +import com.aiosman.riderpro.entity.LikeItemPagingSource import com.aiosman.riderpro.data.AccountServiceImpl import com.aiosman.riderpro.data.api.ApiClient import com.aiosman.riderpro.data.api.UpdateNoticeRequestBody diff --git a/app/src/main/java/com/aiosman/riderpro/ui/post/NewPostViewModel.kt b/app/src/main/java/com/aiosman/riderpro/ui/post/NewPostViewModel.kt index bc121b3..1df5a7f 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/post/NewPostViewModel.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/post/NewPostViewModel.kt @@ -8,9 +8,9 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import com.aiosman.riderpro.data.MomentService -import com.aiosman.riderpro.data.TestMomentServiceImpl +import com.aiosman.riderpro.entity.MomentServiceImpl import com.aiosman.riderpro.data.UploadImage -import com.aiosman.riderpro.model.MomentEntity +import com.aiosman.riderpro.entity.MomentEntity import com.aiosman.riderpro.ui.modification.Modification import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -20,7 +20,7 @@ import java.io.InputStream object NewPostViewModel : ViewModel() { - var momentService: MomentService = TestMomentServiceImpl() + var momentService: MomentService = MomentServiceImpl() var textContent by mutableStateOf("") var searchPlaceAddressResult by mutableStateOf(null) var modificationList by mutableStateOf>(listOf()) diff --git a/app/src/main/java/com/aiosman/riderpro/ui/post/Post.kt b/app/src/main/java/com/aiosman/riderpro/ui/post/Post.kt index bee2db6..b5a294b 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/post/Post.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/post/Post.kt @@ -61,9 +61,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.compose.viewModel import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData @@ -75,22 +73,22 @@ import com.aiosman.riderpro.LocalAnimatedContentScope import com.aiosman.riderpro.LocalNavController import com.aiosman.riderpro.LocalSharedTransitionScope import com.aiosman.riderpro.R -import com.aiosman.riderpro.data.AccountProfileEntity +import com.aiosman.riderpro.entity.AccountProfileEntity import com.aiosman.riderpro.data.AccountService -import com.aiosman.riderpro.data.CommentEntity -import com.aiosman.riderpro.data.CommentPagingSource +import com.aiosman.riderpro.entity.CommentEntity +import com.aiosman.riderpro.entity.CommentPagingSource import com.aiosman.riderpro.data.CommentRemoteDataSource import com.aiosman.riderpro.data.CommentService -import com.aiosman.riderpro.data.TestCommentServiceImpl +import com.aiosman.riderpro.data.CommentServiceImpl import com.aiosman.riderpro.data.MomentService import com.aiosman.riderpro.data.AccountServiceImpl -import com.aiosman.riderpro.data.TestMomentServiceImpl -import com.aiosman.riderpro.data.TestUserServiceImpl +import com.aiosman.riderpro.entity.MomentServiceImpl +import com.aiosman.riderpro.data.UserServiceImpl import com.aiosman.riderpro.data.UserService import com.aiosman.riderpro.exp.formatPostTime import com.aiosman.riderpro.exp.timeAgo -import com.aiosman.riderpro.model.MomentEntity -import com.aiosman.riderpro.model.MomentImageEntity +import com.aiosman.riderpro.entity.MomentEntity +import com.aiosman.riderpro.entity.MomentImageEntity import com.aiosman.riderpro.ui.NavigationRoute import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout import com.aiosman.riderpro.ui.composables.BottomNavigationPlaceholder @@ -106,9 +104,9 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch object PostViewModel : ViewModel() { - var service: MomentService = TestMomentServiceImpl() - var commentService: CommentService = TestCommentServiceImpl() - var userService: UserService = TestUserServiceImpl() + var service: MomentService = MomentServiceImpl() + var commentService: CommentService = CommentServiceImpl() + var userService: UserService = UserServiceImpl() private var _commentsFlow = MutableStateFlow>(PagingData.empty()) val commentsFlow = _commentsFlow.asStateFlow() var postId: String = "" @@ -146,7 +144,7 @@ object PostViewModel : ViewModel() { suspend fun initData() { moment = service.getMomentById(postId.toInt()) moment?.let { - accountProfileEntity = accountService.getAccountProfileById(it.authorId) + accountProfileEntity = userService.getUserProfile(it.authorId.toString()) } } diff --git a/app/src/main/java/com/aiosman/riderpro/ui/profile/AccountProfile.kt b/app/src/main/java/com/aiosman/riderpro/ui/profile/AccountProfile.kt index 3a27996..cbb3e59 100644 --- a/app/src/main/java/com/aiosman/riderpro/ui/profile/AccountProfile.kt +++ b/app/src/main/java/com/aiosman/riderpro/ui/profile/AccountProfile.kt @@ -18,13 +18,13 @@ import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData import androidx.paging.compose.collectAsLazyPagingItems -import com.aiosman.riderpro.data.AccountProfileEntity -import com.aiosman.riderpro.data.MomentPagingSource -import com.aiosman.riderpro.data.MomentRemoteDataSource -import com.aiosman.riderpro.data.TestMomentServiceImpl -import com.aiosman.riderpro.data.TestUserServiceImpl +import com.aiosman.riderpro.entity.AccountProfileEntity +import com.aiosman.riderpro.entity.MomentPagingSource +import com.aiosman.riderpro.entity.MomentRemoteDataSource +import com.aiosman.riderpro.entity.MomentServiceImpl +import com.aiosman.riderpro.data.UserServiceImpl import com.aiosman.riderpro.data.UserService -import com.aiosman.riderpro.model.MomentEntity +import com.aiosman.riderpro.entity.MomentEntity import com.aiosman.riderpro.ui.composables.StatusBarMaskLayout import com.aiosman.riderpro.ui.index.tabs.profile.CarGroup import com.aiosman.riderpro.ui.index.tabs.profile.MomentPostUnit @@ -36,9 +36,9 @@ import kotlinx.coroutines.launch @Composable fun AccountProfile(id:String) { - val userService: UserService = TestUserServiceImpl() + val userService: UserService = UserServiceImpl() var userProfile by remember { mutableStateOf(null) } - val momentService = TestMomentServiceImpl() + val momentService = MomentServiceImpl() var momentsFlow by remember { mutableStateOf>?>(null) } val scope = rememberCoroutineScope() LaunchedEffect(Unit) {