This commit is contained in:
2024-08-11 17:15:17 +08:00
parent 2dc0ee3307
commit 19527f17c3
32 changed files with 1082 additions and 417 deletions

View File

@@ -1,8 +1,11 @@
package com.aiosman.riderpro.data
import com.aiosman.riderpro.data.api.ApiClient
import com.aiosman.riderpro.data.api.LoginUserRequestBody
import com.aiosman.riderpro.data.api.RegisterRequestBody
import com.aiosman.riderpro.test.TestDatabase
data class AccountProfile(
data class AccountProfileEntity(
val id: Int,
val followerCount: Int,
val followingCount: Int,
@@ -10,33 +13,72 @@ data class AccountProfile(
val avatar: String,
val bio: String,
val country: String,
val isFollowing: Boolean
)
//{
// "id": 1,
// "username": "root",
// "nickname": "rider_4351",
// "avatar": "/api/v1/public/default_avatar.jpeg",
// "followingCount": 1,
// "followerCount": 0
//}
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
)
}
}
interface AccountService {
suspend fun getMyAccountProfile(): AccountProfile
suspend fun getAccountProfileById(id: Int): AccountProfile
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(nickName: String, bio: String)
suspend fun registerUserWithPassword(loginName: String, password: String)
}
class TestAccountServiceImpl : AccountService {
override suspend fun getMyAccountProfile(): AccountProfile {
return TestDatabase.accountData.first { it.id == 1 }
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): AccountProfile {
return TestDatabase.accountData.first { it.id == id }
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 {
return UserAuth(1)
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 {
return UserAuth(1, "token")
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() {
@@ -52,6 +94,7 @@ class TestAccountServiceImpl : AccountService {
}
}
}
override suspend fun updateProfile(nickName: String, bio: String) {
TestDatabase.accountData = TestDatabase.accountData.map {
if (it.id == 1) {
@@ -61,4 +104,8 @@ class TestAccountServiceImpl : AccountService {
}
}
}
override suspend fun registerUserWithPassword(loginName: String, password: String) {
ApiClient.api.register(RegisterRequestBody(loginName, password))
}
}

View File

@@ -0,0 +1,2 @@
package com.aiosman.riderpro.data

View File

@@ -2,38 +2,81 @@ 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.CommentRequestBody
import com.aiosman.riderpro.test.TestDatabase
import com.google.gson.annotations.SerializedName
import java.io.IOException
import java.util.Calendar
import kotlin.math.min
import kotlin.random.Random
interface CommentService {
suspend fun getComments(pageNumber: Int, postId: Int? = null): ListContainer<Comment>
suspend fun createComment(postId: Int, content: String, authorId: Int): Comment
suspend fun getComments(pageNumber: Int, postId: Int? = null): ListContainer<CommentEntity>
suspend fun createComment(postId: Int, content: String)
suspend fun likeComment(commentId: Int)
suspend fun dislikeComment(commentId: Int)
}
//{
// "id": 2,
// "content": "123",
// "User": {
// "id": 1,
// "nickName": "",
// "avatar": "/api/v1/public/default_avatar.jpeg"
//},
// "likeCount": 1,
// "isLiked": true,
// "createdAt": "2024-08-05 02:53:48"
//}
data class Comment(
@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
) {
fun toCommentEntity(): CommentEntity {
return CommentEntity(
id = id,
name = user.nickName,
comment = content,
date = createdAt,
likes = likeCount,
replies = emptyList(),
postId = 0,
avatar = ApiClient.BASE_SERVER + user.avatar,
author = user.id,
liked = isLiked
)
}
}
data class CommentEntity(
val id: Int,
val name: String,
val comment: String,
val date: String,
val likes: Int,
val replies: List<Comment>,
val replies: List<CommentEntity>,
val postId: Int = 0,
val avatar: String,
val author: Int,
val author: Long,
var liked: Boolean,
)
class CommentPagingSource(
private val remoteDataSource: CommentRemoteDataSource,
private val postId: Int? = null
) : PagingSource<Int, Comment>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Comment> {
) : PagingSource<Int, CommentEntity>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, CommentEntity> {
return try {
val currentPage = params.key ?: 1
val comments = remoteDataSource.getComments(
@@ -50,7 +93,7 @@ class CommentPagingSource(
}
}
override fun getRefreshKey(state: PagingState<Int, Comment>): Int? {
override fun getRefreshKey(state: PagingState<Int, CommentEntity>): Int? {
return state.anchorPosition
}
@@ -59,87 +102,37 @@ class CommentPagingSource(
class CommentRemoteDataSource(
private val commentService: CommentService,
) {
suspend fun getComments(pageNumber: Int, postId: Int?): ListContainer<Comment> {
suspend fun getComments(pageNumber: Int, postId: Int?): ListContainer<CommentEntity> {
return commentService.getComments(pageNumber, postId)
}
}
class TestCommentServiceImpl : CommentService {
override suspend fun getComments(pageNumber: Int, postId: Int?): ListContainer<Comment> {
var rawList = TestDatabase.comment
if (postId != null) {
rawList = rawList.filter { it.postId == postId }
}
val from = (pageNumber - 1) * DataBatchSize
val to = (pageNumber) * DataBatchSize
rawList = rawList.sortedBy { -it.id }
if (from >= rawList.size) {
return ListContainer(
total = rawList.size,
page = pageNumber,
pageSize = DataBatchSize,
list = emptyList()
)
}
rawList = rawList.sortedBy { -it.id }
rawList.forEach {
val myLikeIdList = TestDatabase.likeCommentList.filter { it.second == 1 }.map { it.first }
if (myLikeIdList.contains(it.id)) {
it.liked = true
}
}
val currentSublist = rawList.subList(from, min(to, rawList.size))
override suspend fun getComments(pageNumber: Int, postId: Int?): ListContainer<CommentEntity> {
val resp = ApiClient.api.getComments(pageNumber, postId)
val body = resp.body() ?: throw ServiceException("Failed to get comments")
return ListContainer(
total = rawList.size,
page = pageNumber,
pageSize = DataBatchSize,
list = currentSublist
list = body.list.map { it.toCommentEntity() },
page = body.page,
total = body.total,
pageSize = body.pageSize
)
}
override suspend fun createComment(postId: Int, content: String, authorId: Int): Comment {
var author = TestDatabase.accountData.find { it.id == authorId }
if (author == null) {
author = TestDatabase.accountData.random()
}
TestDatabase.commentIdCounter += 1
val newComment = Comment(
name = author.nickName,
comment = content,
date = Calendar.getInstance().time.toString(),
likes = 0,
replies = emptyList(),
postId = postId,
avatar = author.avatar,
author = author.id,
id = TestDatabase.commentIdCounter,
liked = false
)
TestDatabase.comment += newComment
return newComment
override suspend fun createComment(postId: Int, content: String) {
val resp = ApiClient.api.createComment(postId, CommentRequestBody(content))
return
}
override suspend fun likeComment(commentId: Int) {
TestDatabase.comment = TestDatabase.comment.map {
if (it.id == commentId) {
it.copy(likes = it.likes + 1)
} else {
it
}
}
TestDatabase.likeCommentList += Pair(commentId, 1)
val resp = ApiClient.api.likeComment(commentId)
return
}
override suspend fun dislikeComment(commentId: Int) {
TestDatabase.comment = TestDatabase.comment.map {
if (it.id == commentId) {
it.copy(likes = it.likes - 1)
} else {
it
}
}
TestDatabase.likeCommentList = TestDatabase.likeCommentList.filter { it.first != commentId }
val resp = ApiClient.api.dislikeComment(commentId)
return
}
companion object {

View File

@@ -0,0 +1,5 @@
package com.aiosman.riderpro.data
data class DataContainer<T>(
val data: T
)

View File

@@ -0,0 +1,9 @@
package com.aiosman.riderpro.data
class ServiceException(
override val message: String,
val code: Int = 0,
val data: Any? = null
) : Exception(
message
)

View File

@@ -1,9 +1,15 @@
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

@@ -2,28 +2,109 @@ 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.model.MomentItem
import com.aiosman.riderpro.data.api.ApiClient
import com.aiosman.riderpro.model.MomentEntity
import com.aiosman.riderpro.test.TestDatabase
import java.io.IOException
import kotlin.math.min
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")
val id: Long,
@SerializedName("textContent")
val textContent: String,
@SerializedName("images")
val images: List<Image>,
@SerializedName("user")
val user: User,
@SerializedName("likeCount")
val likeCount: Long,
@SerializedName("isLiked")
val isLiked: Boolean,
@SerializedName("favoriteCount")
val favoriteCount: Long,
@SerializedName("isFavorite")
val isFavorite: Boolean,
@SerializedName("shareCount")
val isCommented: Boolean,
@SerializedName("commentCount")
val commentCount: Long,
@SerializedName("time")
val time: String
) {
fun toMomentItem(): MomentEntity {
return MomentEntity(
id = id.toInt(),
avatar = ApiClient.BASE_SERVER + user.avatar,
nickname = user.nickName,
location = "Worldwide",
time = time,
followStatus = false,
momentTextContent = textContent,
momentPicture = R.drawable.default_moment_img,
likeCount = likeCount.toInt(),
commentCount = commentCount.toInt(),
shareCount = 0,
favoriteCount = favoriteCount.toInt(),
images = images.map { ApiClient.BASE_SERVER + it.url + "?token=${AppStore.token}" },
authorId = user.id.toInt(),
liked = isLiked,
isFavorite = isFavorite
)
}
}
data class Image(
@SerializedName("id")
val id: Long,
@SerializedName("url")
val url: String,
@SerializedName("thumbnail")
val thumbnail: String
)
data class User(
@SerializedName("id")
val id: Long,
@SerializedName("nickName")
val nickName: String,
@SerializedName("avatar")
val avatar: String
)
data class UploadImage(
val file: File,
val filename: String,
val url: String,
val ext: String
)
interface MomentService {
suspend fun getMomentById(id: Int): MomentItem
suspend fun getMomentById(id: Int): MomentEntity
suspend fun likeMoment(id: Int)
suspend fun dislikeMoment(id: Int)
suspend fun getMoments(
pageNumber: Int,
author: Int? = null,
timelineId: Int? = null
): ListContainer<MomentItem>
): ListContainer<MomentEntity>
suspend fun createMoment(
content: String,
authorId: Int,
imageUriList: List<String>,
images: List<UploadImage>,
relPostId: Int? = null
): MomentItem
): MomentEntity
suspend fun favoriteMoment(id: Int)
suspend fun unfavoriteMoment(id: Int)
}
@@ -31,8 +112,8 @@ class MomentPagingSource(
private val remoteDataSource: MomentRemoteDataSource,
private val author: Int? = null,
private val timelineId: Int? = null
) : PagingSource<Int, MomentItem>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MomentItem> {
) : PagingSource<Int, MomentEntity>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MomentEntity> {
return try {
val currentPage = params.key ?: 1
val moments = remoteDataSource.getMoments(
@@ -51,7 +132,7 @@ class MomentPagingSource(
}
}
override fun getRefreshKey(state: PagingState<Int, MomentItem>): Int? {
override fun getRefreshKey(state: PagingState<Int, MomentEntity>): Int? {
return state.anchorPosition
}
@@ -64,7 +145,7 @@ class MomentRemoteDataSource(
pageNumber: Int,
author: Int?,
timelineId: Int?
): ListContainer<MomentItem> {
): ListContainer<MomentEntity> {
return momentService.getMoments(pageNumber, author, timelineId)
}
}
@@ -77,11 +158,11 @@ class TestMomentServiceImpl() : MomentService {
pageNumber: Int,
author: Int?,
timelineId: Int?
): ListContainer<MomentItem> {
): ListContainer<MomentEntity> {
return testMomentBackend.fetchMomentItems(pageNumber, author, timelineId)
}
override suspend fun getMomentById(id: Int): MomentItem {
override suspend fun getMomentById(id: Int): MomentEntity {
return testMomentBackend.getMomentById(id)
}
@@ -97,10 +178,18 @@ class TestMomentServiceImpl() : MomentService {
override suspend fun createMoment(
content: String,
authorId: Int,
imageUriList: List<String>,
images: List<UploadImage>,
relPostId: Int?
): MomentItem {
return testMomentBackend.createMoment(content, authorId, imageUriList, relPostId)
): 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)
}
}
@@ -113,110 +202,63 @@ class TestMomentBackend(
pageNumber: Int,
author: Int? = null,
timelineId: Int?
): ListContainer<MomentItem> {
var rawList = TestDatabase.momentData
rawList = rawList.sortedBy { it.id }.reversed()
if (author != null) {
rawList = rawList.filter { it.authorId == author }
}
if (timelineId != null) {
val followIdList = TestDatabase.followList.filter {
it.first == timelineId
}.map { it.second }
rawList = rawList.filter { it.authorId in followIdList || it.authorId == 1 }
}
val from = (pageNumber - 1) * DataBatchSize
val to = (pageNumber) * DataBatchSize
if (from >= rawList.size) {
return ListContainer(
total = rawList.size,
page = pageNumber,
pageSize = DataBatchSize,
list = emptyList()
)
}
val currentSublist = rawList.subList(from, min(to, rawList.size))
currentSublist.forEach {
val myLikeIdList =
TestDatabase.likeMomentList.filter { it.second == 1 }.map { it.first }
if (myLikeIdList.contains(it.id)) {
it.liked = true
}
if (it.relPostId != null) {
it.relMoment = rawList.first { it1 -> it1.id == it.relPostId }
}
}
// delay
kotlinx.coroutines.delay(loadDelay)
): ListContainer<MomentEntity> {
val resp = ApiClient.api.getPosts(
pageSize = DataBatchSize,
page = pageNumber,
timelineId = timelineId,
authorId = author
)
val body = resp.body() ?: throw ServiceException("Failed to get moments")
return ListContainer(
total = rawList.size,
total = body.total,
page = pageNumber,
pageSize = DataBatchSize,
list = currentSublist
list = body.list.map { it.toMomentItem() }
)
}
suspend fun getMomentById(id: Int): MomentItem {
var moment = TestDatabase.momentData.first {
it.id == id
}
val isLike = TestDatabase.likeMomentList.any {
it.first == id && it.second == 1
}
moment = moment.copy(liked = isLike)
return moment
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) {
val oldMoment = TestDatabase.momentData.first {
it.id == id
}
val newMoment = oldMoment.copy(likeCount = oldMoment.likeCount + 1)
TestDatabase.updateMomentById(id, newMoment)
TestDatabase.likeMomentList += Pair(id, 1)
ApiClient.api.likePost(id)
}
suspend fun dislikeMoment(id: Int) {
val oldMoment = TestDatabase.momentData.first {
it.id == id
}
val newMoment = oldMoment.copy(likeCount = oldMoment.likeCount - 1)
TestDatabase.updateMomentById(id, newMoment)
TestDatabase.likeMomentList = TestDatabase.likeMomentList.filter {
it.first != id
}
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<String>,
imageUriList: List<UploadImage>,
relPostId: Int?
): MomentItem {
TestDatabase.momentIdCounter += 1
val person = TestDatabase.accountData.first {
it.id == authorId
): MomentEntity {
val textContent = content.toRequestBody("text/plain".toMediaTypeOrNull())
val imageList = imageUriList.map { item ->
val file = item.file
createMultipartBody(file, "image")
}
val newMoment = MomentItem(
id = TestDatabase.momentIdCounter,
avatar = person.avatar,
nickname = person.nickName,
location = person.country,
time = "2023.02.02 11:23",
followStatus = false,
momentTextContent = content,
momentPicture = R.drawable.default_moment_img,
likeCount = 0,
commentCount = 0,
shareCount = 0,
favoriteCount = 0,
images = imageUriList,
authorId = person.id,
relPostId = relPostId
)
TestDatabase.momentData += newMoment
return newMoment
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,5 +1,6 @@
package com.aiosman.riderpro.data
import com.aiosman.riderpro.data.api.ApiClient
import com.aiosman.riderpro.test.TestDatabase
data class UserAuth(
@@ -8,17 +9,26 @@ data class UserAuth(
)
interface UserService {
suspend fun getUserProfile(id: String): AccountProfile
suspend fun getUserProfile(id: String): AccountProfileEntity
suspend fun followUser(id: String)
suspend fun unFollowUser(id: String)
}
class TestUserServiceImpl : UserService {
override suspend fun getUserProfile(id: String): AccountProfile {
TestDatabase.accountData.forEach {
if (it.id == id.toInt()) {
return it
}
}
return AccountProfile(0, 0, 0, "", "", "", "")
override suspend fun getUserProfile(id: String): AccountProfileEntity {
val resp = ApiClient.api.getAccountProfileById(id.toInt())
val body = resp.body() ?: throw ServiceException("Failed to get account")
return body.data.toAccountProfileEntity()
}
override suspend fun followUser(id: String) {
val resp = ApiClient.api.followUser(id.toInt())
return
}
override suspend fun unFollowUser(id: String) {
val resp = ApiClient.api.unfollowUser(id.toInt())
return
}
}

View File

@@ -0,0 +1,39 @@
package com.aiosman.riderpro.data.api
import com.aiosman.riderpro.AppStore
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class AuthInterceptor() : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val requestBuilder = chain.request().newBuilder()
requestBuilder.addHeader("Authorization", "Bearer ${AppStore.token}")
return chain.proceed(requestBuilder.build())
}
}
object ApiClient {
const val BASE_SERVER = "http://192.168.31.57:8088"
const val BASE_API_URL = "${BASE_SERVER}/api/v1"
const val RETROFIT_URL = "${BASE_API_URL}/"
private val okHttpClient: OkHttpClient by lazy {
OkHttpClient.Builder()
.addInterceptor(AuthInterceptor())
.build()
}
private val retrofit: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(RETROFIT_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
val api: RiderProAPI by lazy {
retrofit.create(RiderProAPI::class.java)
}
}

View File

@@ -0,0 +1,146 @@
package com.aiosman.riderpro.data.api
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
import com.aiosman.riderpro.data.Moment
import com.google.gson.annotations.SerializedName
import okhttp3.MultipartBody
import okhttp3.RequestBody
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.Part
import retrofit2.http.Path
import retrofit2.http.Query
data class RegisterRequestBody(
@SerializedName("username")
val username: String,
@SerializedName("password")
val password: String
)
data class LoginUserRequestBody(
@SerializedName("username")
val username: String,
@SerializedName("password")
val password: String
)
data class AuthResult(
@SerializedName("code")
val code: Int,
@SerializedName("expire")
val expire: String,
@SerializedName("token")
val token: String
)
data class ValidateTokenResult(
@SerializedName("id")
val id: Int,
)
data class CommentRequestBody(
@SerializedName("content")
val content: String
)
interface RiderProAPI {
@POST("register")
suspend fun register(@Body body: RegisterRequestBody): Response<Unit>
@POST("login")
suspend fun login(@Body body: LoginUserRequestBody): Response<AuthResult>
@GET("auth/token")
suspend fun checkToken(): Response<ValidateTokenResult>
@GET("posts")
suspend fun getPosts(
@Query("page") page: Int = 1,
@Query("pageSize") pageSize: Int = 20,
@Query("timelineId") timelineId: Int? = null,
@Query("authorId") authorId: Int? = null,
): Response<ListContainer<Moment>>
@Multipart
@POST("posts")
suspend fun createPost(
@Part image: List<MultipartBody.Part>,
@Part("textContent") textContent: RequestBody,
): Response<DataContainer<Moment>>
@GET("post/{id}")
suspend fun getPost(
@Path("id") id: Int
): Response<DataContainer<Moment>>
@POST("post/{id}/like")
suspend fun likePost(
@Path("id") id: Int
): Response<Unit>
@POST("post/{id}/dislike")
suspend fun dislikePost(
@Path("id") id: Int
): Response<Unit>
@POST("post/{id}/favorite")
suspend fun favoritePost(
@Path("id") id: Int
): Response<Unit>
@POST("post/{id}/unfavorite")
suspend fun unfavoritePost(
@Path("id") id: Int
): Response<Unit>
@POST("post/{id}/comment")
suspend fun createComment(
@Path("id") id: Int,
@Body body: CommentRequestBody
): Response<Unit>
@POST("comment/{id}/like")
suspend fun likeComment(
@Path("id") id: Int
): Response<Unit>
@POST("comment/{id}/dislike")
suspend fun dislikeComment(
@Path("id") id: Int
): Response<Unit>
@GET("comments")
suspend fun getComments(
@Query("page") page: Int = 1,
@Query("postId") postId: Int? = null,
@Query("pageSize") pageSize: Int = 20,
): Response<ListContainer<Comment>>
@GET("account/my")
suspend fun getMyAccount(): Response<DataContainer<AccountProfile>>
@GET("profile/{id}")
suspend fun getAccountProfileById(
@Path("id") id: Int
): Response<DataContainer<AccountProfile>>
@POST("user/{id}/follow")
suspend fun followUser(
@Path("id") id: Int
): Response<Unit>
@POST("user/{id}/unfollow")
suspend fun unfollowUser(
@Path("id") id: Int
): Response<Unit>
}