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.ChangePasswordRequestBody 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.google.gson.annotations.SerializedName import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody 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( val id: Int, val username: String, val nickname: String, val avatar: String, val followingCount: Int, val followerCount: Int, val isFollowing: Boolean ) { fun toAccountProfileEntity(): AccountProfileEntity { return AccountProfileEntity( id = id, followerCount = followerCount, followingCount = followingCount, nickName = nickname, avatar = "${ApiClient.BASE_SERVER}$avatar", bio = "", country = "Worldwide", isFollowing = isFollowing ) } } data class NoticePostEntity( val id: Int, val textContent: String, val images: List, val time: Date, ) data class NoticePost( @SerializedName("id") val id: Int, @SerializedName("textContent") val textContent: String, @SerializedName("images") val images: List, @SerializedName("time") val time: String, ) { fun toNoticePostEntity(): NoticePostEntity { return NoticePostEntity( id = id, textContent = textContent, images = images.map { it.copy( url = "${ApiClient.BASE_SERVER}${it.url}", thumbnail = "${ApiClient.BASE_SERVER}${it.thumbnail}", ) }, time = ApiClient.dateFromApiString(time) ) } } data class NoticeUserEntity( val id: Int, val nickName: String, val avatar: String, ) data class NoticeUser( @SerializedName("id") val id: Int, @SerializedName("nickName") val nickName: String, @SerializedName("avatar") val avatar: String, ) { fun toNoticeUserEntity(): NoticeUserEntity { return NoticeUserEntity( id = id, nickName = nickName, avatar = "${ApiClient.BASE_SERVER}$avatar", ) } } data class AccountLike( @SerializedName("isUnread") val isUnread: Boolean, @SerializedName("post") val post: NoticePost, @SerializedName("user") val user: NoticeUser, @SerializedName("likeTime") val likeTime: String, ) { fun toAccountLikeEntity(): AccountLikeEntity { return AccountLikeEntity( post = post.toNoticePostEntity(), user = user.toNoticeUserEntity(), likeTime = ApiClient.dateFromApiString(likeTime) ) } } data class AccountFavourite( @SerializedName("isUnread") val isUnread: Boolean, @SerializedName("post") val post: NoticePost, @SerializedName("user") val user: NoticeUser, @SerializedName("favoriteTime") val favouriteTime: String, ) { fun toAccountFavouriteEntity(): AccountFavouriteEntity { return AccountFavouriteEntity( post = post.toNoticePostEntity(), user = user.toNoticeUserEntity(), favoriteTime = ApiClient.dateFromApiString(favouriteTime) ) } } data class AccountFollow( @SerializedName("id") val id: Int, @SerializedName("username") val username: String, @SerializedName("nickname") val nickname: String, @SerializedName("avatar") val avatar: String, @SerializedName("isUnread") val isUnread: Boolean, @SerializedName("userId") val userId: Int, @SerializedName("isFollowing") val isFollowing: Boolean, ) //{ // "likeCount": 0, // "followCount": 0, // "favoriteCount": 0 //} data class AccountNotice( @SerializedName("likeCount") val likeCount: Int, @SerializedName("followCount") val followCount: Int, @SerializedName("favoriteCount") val favoriteCount: Int, @SerializedName("commentCount") val commentCount: Int, ) 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 suspend fun loginUserWithPassword(loginName: String, password: String): UserAuth suspend fun logout() suspend fun updateAvatar(uri: String) suspend fun updateProfile(avatar: UploadImage?, nickName: String?, bio: String?) suspend fun registerUserWithPassword(loginName: String, password: String) suspend fun changeAccountPassword(oldPassword: String, newPassword: String) suspend fun getMyLikeNotice(page: Int, pageSize: Int): ListContainer suspend fun getMyFollowNotice(page: Int, pageSize: Int): ListContainer suspend fun getMyFavouriteNotice(page: Int, pageSize: Int): ListContainer suspend fun getMyNoticeInfo(): AccountNotice suspend fun updateNotice(payload: UpdateNoticeRequestBody) } class TestAccountServiceImpl : AccountService { override suspend fun getMyAccountProfile(): AccountProfileEntity { val resp = ApiClient.api.getMyAccount() val body = resp.body() ?: throw ServiceException("Failed to get account") 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") return UserAuth(body.id) } override suspend fun loginUserWithPassword(loginName: String, password: String): UserAuth { val resp = ApiClient.api.login(LoginUserRequestBody(loginName, password)) val body = resp.body() ?: throw ServiceException("Failed to login") return UserAuth(0, body.token) } override suspend fun logout() { // 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()) return MultipartBody.Part.createFormData(name, filename, requestFile) } override suspend fun updateProfile(avatar: UploadImage?, nickName: String?, bio: String?) { val nicknameField: RequestBody? = nickName?.toRequestBody("text/plain".toMediaTypeOrNull()) val avatarField: MultipartBody.Part? = avatar?.let { createMultipartBody(it.file, it.filename, "avatar") } ApiClient.api.updateProfile(avatarField, nicknameField) } override suspend fun registerUserWithPassword(loginName: String, password: String) { ApiClient.api.register(RegisterRequestBody(loginName, password)) } override suspend fun changeAccountPassword(oldPassword: String, newPassword: String) { ApiClient.api.changePassword(ChangePasswordRequestBody(oldPassword, newPassword)) } override suspend fun getMyLikeNotice(page: Int, pageSize: Int): ListContainer { val resp = ApiClient.api.getMyLikeNotices(page, pageSize) val body = resp.body() ?: throw ServiceException("Failed to get account") return body } override suspend fun getMyFollowNotice(page: Int, pageSize: Int): ListContainer { val resp = ApiClient.api.getMyFollowNotices(page, pageSize) val body = resp.body() ?: throw ServiceException("Failed to get account") return body } override suspend fun getMyFavouriteNotice( page: Int, pageSize: Int ): ListContainer { val resp = ApiClient.api.getMyFavouriteNotices(page, pageSize) val body = resp.body() ?: throw ServiceException("Failed to get account") return body } override suspend fun getMyNoticeInfo(): AccountNotice { val resp = ApiClient.api.getMyNoticeInfo() val body = resp.body() ?: throw ServiceException("Failed to get account") return body.data } override suspend fun updateNotice(payload: UpdateNoticeRequestBody) { ApiClient.api.updateNoticeInfo(payload) } }