整理代码

This commit is contained in:
2024-08-24 23:11:20 +08:00
parent 367d1c9f3a
commit b4004663cd
40 changed files with 898 additions and 801 deletions

View File

@@ -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<Image>,
val time: Date,
)
/**
* 消息关联资料
*/
data class NoticePost(
// 动态ID
@SerializedName("id")
val id: Int,
// 动态内容
@SerializedName("textContent")
// 动态图片
val textContent: String,
// 动态图片
@SerializedName("images")
val images: List<Image>,
// 动态时间
@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<Int, AccountLikeEntity>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AccountLikeEntity> {
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, AccountLikeEntity>): Int? {
return state.anchorPosition
}
}
class FavoriteItemPagingSource(
private val accountService: AccountService,
) : PagingSource<Int, AccountFavouriteEntity>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AccountFavouriteEntity> {
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, AccountFavouriteEntity>): Int? {
return state.anchorPosition
}
}
class FollowItemPagingSource(
private val accountService: AccountService,
) : PagingSource<Int, AccountFollow>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AccountFollow> {
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, AccountFollow>): 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<AccountLike>
/**
* 获取我的关注通知
* @param page 页码
* @param pageSize 每页数量
*/
suspend fun getMyFollowNotice(page: Int, pageSize: Int): ListContainer<AccountFollow>
/**
* 获取我的收藏通知
* @param page 页码
* @param pageSize 每页数量
*/
suspend fun getMyFavouriteNotice(page: Int, pageSize: Int): ListContainer<AccountFavourite>
/**
* 获取我的通知信息
*/
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())

View File

@@ -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<CommentEntity>
/**
* 创建评论
* @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<CommentEntity>,
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<Int, CommentEntity>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, CommentEntity> {
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, CommentEntity>): 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
}
}

View File

@@ -1,5 +1,8 @@
package com.aiosman.riderpro.data
/**
* 通用接口返回数据
*/
data class DataContainer<T>(
val data: T
)

View File

@@ -1,5 +1,8 @@
package com.aiosman.riderpro.data
/**
* 错误返回
*/
class ServiceException(
override val message: String,
val code: Int = 0,

View File

@@ -3,13 +3,20 @@ package com.aiosman.riderpro.data
import com.google.gson.annotations.SerializedName
/**
* 通用列表接口返回
*/
data class ListContainer<T>(
// 总数
@SerializedName("total")
val total: Int,
// 当前页
@SerializedName("page")
val page: Int,
// 每页数量
@SerializedName("pageSize")
val pageSize: Int,
// 列表
@SerializedName("list")
val list: List<T>
)

View File

@@ -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<MomentEntity>
/**
* 创建动态
* @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<Int, MomentEntity>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MomentEntity> {
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, MomentEntity>): Int? {
return state.anchorPosition
}
}
class MomentRemoteDataSource(
private val momentService: MomentService,
) {
suspend fun getMoments(
pageNumber: Int,
author: Int?,
timelineId: Int?,
contentSearch: String?
): ListContainer<MomentEntity> {
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<MomentEntity> {
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<UploadImage>,
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<MomentEntity> {
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<UploadImage>,
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)
}
}

View File

@@ -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<Int, AccountProfileEntity>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AccountProfileEntity> {
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, AccountProfileEntity>): 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")

View File

@@ -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

View File

@@ -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<Image>,
// 时间
val time: Date,
)
/**
* 消息关联的用户
*/
data class NoticeUserEntity(
// 用户ID
val id: Int,
// 昵称
val nickName: String,
// 头像
val avatar: String,
)
/**
* 用户点赞消息分页数据加载器
*/
class LikeItemPagingSource(
private val accountService: AccountService,
) : PagingSource<Int, AccountLikeEntity>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AccountLikeEntity> {
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, AccountLikeEntity>): Int? {
return state.anchorPosition
}
}
/**
* 用户收藏消息分页数据加载器
*/
class FavoriteItemPagingSource(
private val accountService: AccountService,
) : PagingSource<Int, AccountFavouriteEntity>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AccountFavouriteEntity> {
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, AccountFavouriteEntity>): Int? {
return state.anchorPosition
}
}
/**
* 用户关注消息分页数据加载器
*/
class FollowItemPagingSource(
private val accountService: AccountService,
) : PagingSource<Int, AccountFollow>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AccountFollow> {
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, AccountFollow>): Int? {
return state.anchorPosition
}
}

View File

@@ -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<CommentEntity>,
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<Int, CommentEntity>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, CommentEntity> {
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, CommentEntity>): Int? {
return state.anchorPosition
}
}

View File

@@ -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<Int, MomentEntity>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MomentEntity> {
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, MomentEntity>): Int? {
return state.anchorPosition
}
}
class MomentRemoteDataSource(
private val momentService: MomentService,
) {
suspend fun getMoments(
pageNumber: Int,
author: Int?,
timelineId: Int?,
contentSearch: String?
): ListContainer<MomentEntity> {
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<MomentEntity> {
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<UploadImage>,
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<MomentEntity> {
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<UploadImage>,
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<MomentImageEntity> = emptyList(),
// 作者ID
val authorId: Int = 0,
// 是否点赞
var liked: Boolean = false,
// 关联动态ID
var relPostId: Int? = null,
// 关联动态
var relMoment: MomentEntity? = null,
// 是否收藏
var isFavorite: Boolean = false
)

View File

@@ -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<Int, AccountProfileEntity>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AccountProfileEntity> {
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, AccountProfileEntity>): Int? {
return state.anchorPosition
}
}

View File

@@ -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()

View File

@@ -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<MomentImageEntity> = emptyList(),
val authorId: Int = 0,
var liked: Boolean = false,
var relPostId: Int? = null,
var relMoment: MomentEntity? = null,
var isFavorite: Boolean = false
)

View File

@@ -1,31 +0,0 @@
package com.aiosman.riderpro.test
import kotlin.math.min
class MockDataContainer<T>(
val success: Boolean,
val data: T?
) {
}
class MockListContainer<T>(
val total: Int,
val page: Int,
val pageSize: Int,
val list: List<T>
) {
}
abstract class MockDataSource<T> {
var list = mutableListOf<T>()
suspend fun fetchData(page: Int, pageSize: Int): MockDataContainer<MockListContainer<T>> {
// 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))
}
}

View File

@@ -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<MomentEntity>,
private val loadDelay: Long = 500,
) {
val DataBatchSize = 5
class DesiredLoadResultPageResponse(val data: List<MomentEntity>)
/** 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<Int, MomentEntity>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MomentEntity> {
// 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, MomentEntity>): Int? {
return state.anchorPosition?.let {
state.closestPageToPosition(it)?.prevKey?.plus(1)
?: state.closestPageToPosition(it)?.nextKey?.minus(1)
}
}
}

View File

@@ -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<MomentEntity>()
var accountData = emptyList<AccountProfileEntity>()
var commentEntity = emptyList<CommentEntity>()
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<Pair<Int, Int>>()
var likeCommentList = emptyList<Pair<Int, Int>>()
var likeMomentList = emptyList<Pair<Int, Int>>()
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
}
}

View File

@@ -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("") }

View File

@@ -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(

View File

@@ -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<PagingData<CommentEntity>> = 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,

View File

@@ -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

View File

@@ -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(

View File

@@ -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",

View File

@@ -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()

View File

@@ -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<AccountFollow>>(PagingData.empty())
val followerItemsFlow = _followerItemsFlow.asStateFlow()

View File

@@ -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<MomentImageEntity>()

View File

@@ -60,7 +60,7 @@ fun IndexScreen() {
val systemUiController = rememberSystemUiController()
LaunchedEffect(Unit) {
// systemUiController.setNavigationBarColor(Color.Transparent)
systemUiController.setNavigationBarColor(Color.Transparent)
}
Scaffold(
bottomBar = {

View File

@@ -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

View File

@@ -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<AccountNotice?>(null)
private val commentService: CommentService = TestCommentServiceImpl()
private val commentService: CommentService = CommentServiceImpl()
private val _commentItemsFlow = MutableStateFlow<PagingData<CommentEntity>>(PagingData.empty())
val commentItemsFlow = _commentItemsFlow.asStateFlow()

View File

@@ -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
// )
// }
)
)
}

View File

@@ -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<MomentEntity>>(PagingData.empty())
val momentsFlow = _momentsFlow.asStateFlow()
val accountService: AccountService = AccountServiceImpl()

View File

@@ -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<AccountProfileEntity?>(null)
var momentsFlow by mutableStateOf<Flow<PagingData<MomentEntity>>?>(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,
)

View File

@@ -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

View File

@@ -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

View File

@@ -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<MomentEntity>>(PagingData.empty())
val momentsFlow = _momentsFlow.asStateFlow()
private val userService = TestUserServiceImpl()
private val userService = UserServiceImpl()
private val _usersFlow = MutableStateFlow<PagingData<AccountProfileEntity>>(PagingData.empty())
val usersFlow = _usersFlow.asStateFlow()
var showResult by mutableStateOf(false)

View File

@@ -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

View File

@@ -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<SearchPlaceAddressResult?>(null)
var modificationList by mutableStateOf<List<Modification>>(listOf())

View File

@@ -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<CommentEntity>>(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())
}
}

View File

@@ -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<AccountProfileEntity?>(null) }
val momentService = TestMomentServiceImpl()
val momentService = MomentServiceImpl()
var momentsFlow by remember { mutableStateOf<Flow<PagingData<MomentEntity>>?>(null) }
val scope = rememberCoroutineScope()
LaunchedEffect(Unit) {