改包名com.aiosman.ravenow
This commit is contained in:
140
app/src/main/java/com/aiosman/ravenow/data/api/ApiClient.kt
Normal file
140
app/src/main/java/com/aiosman/ravenow/data/api/ApiClient.kt
Normal file
@@ -0,0 +1,140 @@
|
||||
package com.aiosman.ravenow.data.api
|
||||
|
||||
import android.icu.text.SimpleDateFormat
|
||||
import android.icu.util.TimeZone
|
||||
import com.aiosman.ravenow.AppStore
|
||||
import com.aiosman.ravenow.ConstVars
|
||||
import com.auth0.android.jwt.JWT
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Response
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import java.security.cert.CertificateException
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.TrustManager
|
||||
import javax.net.ssl.X509TrustManager
|
||||
|
||||
fun getUnsafeOkHttpClient(
|
||||
authInterceptor: AuthInterceptor? = null
|
||||
): OkHttpClient {
|
||||
return try {
|
||||
// Create a trust manager that does not validate certificate chains
|
||||
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
|
||||
@Throws(CertificateException::class)
|
||||
override fun checkClientTrusted(
|
||||
chain: Array<java.security.cert.X509Certificate>,
|
||||
authType: String
|
||||
) {
|
||||
}
|
||||
|
||||
@Throws(CertificateException::class)
|
||||
override fun checkServerTrusted(
|
||||
chain: Array<java.security.cert.X509Certificate>,
|
||||
authType: String
|
||||
) {
|
||||
}
|
||||
|
||||
override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> = arrayOf()
|
||||
})
|
||||
|
||||
// Install the all-trusting trust manager
|
||||
val sslContext = SSLContext.getInstance("SSL")
|
||||
sslContext.init(null, trustAllCerts, java.security.SecureRandom())
|
||||
// Create an ssl socket factory with our all-trusting manager
|
||||
val sslSocketFactory = sslContext.socketFactory
|
||||
|
||||
OkHttpClient.Builder()
|
||||
.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
|
||||
.hostnameVerifier { _, _ -> true }
|
||||
.apply {
|
||||
authInterceptor?.let {
|
||||
addInterceptor(it)
|
||||
}
|
||||
}
|
||||
.build()
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
|
||||
class AuthInterceptor() : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val requestBuilder = chain.request().newBuilder()
|
||||
val token = AppStore.token
|
||||
token?.let {
|
||||
val jwt = JWT(token)
|
||||
val expiresAt = jwt.expiresAt?.time?.minus(3000)
|
||||
val currentTime = System.currentTimeMillis()
|
||||
val isExpired = expiresAt != null && currentTime > expiresAt
|
||||
if (isExpired) {
|
||||
runBlocking {
|
||||
val newToken = refreshToken()
|
||||
if (newToken != null) {
|
||||
AppStore.token = newToken
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requestBuilder.addHeader("Authorization", "Bearer ${AppStore.token}")
|
||||
|
||||
val response = chain.proceed(requestBuilder.build())
|
||||
return response
|
||||
}
|
||||
|
||||
private suspend fun refreshToken(): String? {
|
||||
val client = Retrofit.Builder()
|
||||
.baseUrl(ApiClient.RETROFIT_URL)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.client(getUnsafeOkHttpClient())
|
||||
.build()
|
||||
.create(RiderProAPI::class.java)
|
||||
|
||||
val resp = client.refreshToken(AppStore.token ?: "")
|
||||
val newToken = resp.body()?.token
|
||||
if (newToken != null) {
|
||||
AppStore.token = newToken
|
||||
}
|
||||
return newToken
|
||||
}
|
||||
}
|
||||
|
||||
object ApiClient {
|
||||
const val BASE_SERVER = ConstVars.BASE_SERVER
|
||||
const val BASE_API_URL = "${BASE_SERVER}/api/v1"
|
||||
const val RETROFIT_URL = "${BASE_API_URL}/"
|
||||
const val TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"
|
||||
private val okHttpClient: OkHttpClient by lazy {
|
||||
getUnsafeOkHttpClient(authInterceptor = AuthInterceptor())
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
fun formatTime(date: Date): String {
|
||||
val dateFormat = SimpleDateFormat(TIME_FORMAT, Locale.getDefault())
|
||||
return dateFormat.format(date)
|
||||
}
|
||||
|
||||
fun dateFromApiString(apiString: String): Date {
|
||||
val timeFormat = TIME_FORMAT
|
||||
val simpleDateFormat = SimpleDateFormat(timeFormat, Locale.getDefault())
|
||||
simpleDateFormat.timeZone = TimeZone.getTimeZone("UTC")
|
||||
val date = simpleDateFormat.parse(apiString)
|
||||
|
||||
simpleDateFormat.timeZone = TimeZone.getDefault()
|
||||
val localDateString = simpleDateFormat.format(date)
|
||||
return simpleDateFormat.parse(localDateString)
|
||||
}
|
||||
}
|
||||
42
app/src/main/java/com/aiosman/ravenow/data/api/Error.kt
Normal file
42
app/src/main/java/com/aiosman/ravenow/data/api/Error.kt
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.aiosman.ravenow.data.api
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import com.aiosman.ravenow.R
|
||||
|
||||
//
|
||||
enum class ErrorCode(val code: Int) {
|
||||
USER_EXIST(40001),
|
||||
USER_NOT_EXIST(40002),
|
||||
InvalidateCaptcha(40004),
|
||||
IncorrectOldPassword(40005),
|
||||
// 未知错误
|
||||
UNKNOWN(99999)
|
||||
}
|
||||
|
||||
fun ErrorCode.toErrorMessage(context: Context): String {
|
||||
return context.getErrorMessageCode(code)
|
||||
}
|
||||
|
||||
fun ErrorCode.showToast(context: Context) {
|
||||
Toast.makeText(context, toErrorMessage(context), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
// code to ErrorCode
|
||||
fun Int.toErrorCode(): ErrorCode {
|
||||
return when (this) {
|
||||
40001 -> ErrorCode.USER_EXIST
|
||||
40002 -> ErrorCode.USER_NOT_EXIST
|
||||
40004 -> ErrorCode.InvalidateCaptcha
|
||||
40005 -> ErrorCode.IncorrectOldPassword
|
||||
else -> ErrorCode.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.getErrorMessageCode(code: Int?): String {
|
||||
return when (code) {
|
||||
40001 -> getString(R.string.error_10001_user_exist)
|
||||
ErrorCode.IncorrectOldPassword.code -> getString(R.string.error_incorrect_old_password)
|
||||
else -> getString(R.string.error_unknown)
|
||||
}
|
||||
}
|
||||
428
app/src/main/java/com/aiosman/ravenow/data/api/RiderProAPI.kt
Normal file
428
app/src/main/java/com/aiosman/ravenow/data/api/RiderProAPI.kt
Normal file
@@ -0,0 +1,428 @@
|
||||
package com.aiosman.ravenow.data.api
|
||||
|
||||
import com.aiosman.ravenow.data.AccountFavourite
|
||||
import com.aiosman.ravenow.data.AccountFollow
|
||||
import com.aiosman.ravenow.data.AccountLike
|
||||
import com.aiosman.ravenow.data.AccountNotice
|
||||
import com.aiosman.ravenow.data.AccountProfile
|
||||
import com.aiosman.ravenow.data.Comment
|
||||
import com.aiosman.ravenow.data.DataContainer
|
||||
import com.aiosman.ravenow.data.ListContainer
|
||||
import com.aiosman.ravenow.data.Moment
|
||||
import com.aiosman.ravenow.entity.ChatNotification
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.DELETE
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Multipart
|
||||
import retrofit2.http.PATCH
|
||||
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? = null,
|
||||
@SerializedName("password")
|
||||
val password: String? = null,
|
||||
@SerializedName("googleId")
|
||||
val googleId: String? = null,
|
||||
@SerializedName("captcha")
|
||||
val captcha: CaptchaInfo? = null,
|
||||
)
|
||||
|
||||
data class GoogleRegisterRequestBody(
|
||||
@SerializedName("idToken")
|
||||
val idToken: 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,
|
||||
@SerializedName("parentCommentId")
|
||||
val parentCommentId: Int? = null,
|
||||
@SerializedName("replyUserId")
|
||||
val replyUserId: Int? = null,
|
||||
@SerializedName("replyCommentId")
|
||||
val replyCommentId: Int? = null,
|
||||
)
|
||||
|
||||
data class ChangePasswordRequestBody(
|
||||
@SerializedName("currentPassword")
|
||||
val oldPassword: String = "",
|
||||
@SerializedName("newPassword")
|
||||
val newPassword: String = ""
|
||||
)
|
||||
|
||||
data class UpdateNoticeRequestBody(
|
||||
@SerializedName("lastLookLikeTime")
|
||||
val lastLookLikeTime: String? = null,
|
||||
@SerializedName("lastLookFollowTime")
|
||||
val lastLookFollowTime: String? = null,
|
||||
@SerializedName("lastLookFavoriteTime")
|
||||
val lastLookFavouriteTime: String? = null
|
||||
)
|
||||
|
||||
data class RegisterMessageChannelRequestBody(
|
||||
@SerializedName("client")
|
||||
val client: String,
|
||||
@SerializedName("identifier")
|
||||
val identifier: String,
|
||||
)
|
||||
|
||||
data class UnRegisterMessageChannelRequestBody(
|
||||
@SerializedName("client")
|
||||
val client: String,
|
||||
@SerializedName("identifier")
|
||||
val identifier: String,
|
||||
)
|
||||
data class ResetPasswordRequestBody(
|
||||
@SerializedName("username")
|
||||
val username: String,
|
||||
)
|
||||
|
||||
data class UpdateUserLangRequestBody(
|
||||
@SerializedName("language")
|
||||
val lang: String,
|
||||
@SerializedName("timeOffset")
|
||||
val timeOffset: Int,
|
||||
@SerializedName("timezone")
|
||||
val timezone: String,
|
||||
)
|
||||
|
||||
data class TrtcSignResponseBody(
|
||||
@SerializedName("sig")
|
||||
val sig: String,
|
||||
@SerializedName("userId")
|
||||
val userId: String,
|
||||
)
|
||||
|
||||
data class AppConfig(
|
||||
@SerializedName("trtcAppId")
|
||||
val trtcAppId: Int,
|
||||
)
|
||||
|
||||
data class DictItem(
|
||||
@SerializedName("key")
|
||||
val key: String,
|
||||
@SerializedName("value")
|
||||
val value: String,
|
||||
@SerializedName("desc")
|
||||
val desc: String,
|
||||
)
|
||||
|
||||
data class CaptchaRequestBody(
|
||||
@SerializedName("source")
|
||||
val source: String,
|
||||
)
|
||||
|
||||
data class CaptchaResponseBody(
|
||||
@SerializedName("id")
|
||||
val id: Int,
|
||||
@SerializedName("thumb_base64")
|
||||
val thumbBase64: String,
|
||||
@SerializedName("master_base64")
|
||||
val masterBase64: String,
|
||||
@SerializedName("count")
|
||||
val count: Int,
|
||||
)
|
||||
|
||||
data class CheckLoginCaptchaRequestBody(
|
||||
@SerializedName("username")
|
||||
val username: String,
|
||||
)
|
||||
|
||||
data class GenerateLoginCaptchaRequestBody(
|
||||
@SerializedName("username")
|
||||
val username: String,
|
||||
)
|
||||
|
||||
data class DotPosition(
|
||||
@SerializedName("index")
|
||||
val index: Int,
|
||||
@SerializedName("x")
|
||||
val x: Int,
|
||||
@SerializedName("y")
|
||||
val y: Int,
|
||||
)
|
||||
|
||||
data class CaptchaInfo(
|
||||
@SerializedName("id")
|
||||
val id: Int,
|
||||
@SerializedName("dot")
|
||||
val dot: List<DotPosition>
|
||||
)
|
||||
|
||||
|
||||
data class UpdateChatNotificationRequestBody(
|
||||
@SerializedName("targetUserId")
|
||||
val targetUserId: Int,
|
||||
@SerializedName("strategy")
|
||||
val strategy: 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("auth/refresh_token")
|
||||
suspend fun refreshToken(
|
||||
@Query("token") token: String
|
||||
): Response<AuthResult>
|
||||
|
||||
@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,
|
||||
@Query("contentSearch") contentSearch: String? = null,
|
||||
@Query("postUser") postUser: Int? = null,
|
||||
@Query("trend") trend: String? = null,
|
||||
@Query("favouriteUserId") favouriteUserId: Int? = null,
|
||||
@Query("explore") explore: String? = 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<DataContainer<Comment>>
|
||||
|
||||
@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>
|
||||
|
||||
@POST("comment/{id}/read")
|
||||
suspend fun updateReadStatus(
|
||||
@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,
|
||||
@Query("postUser") postUser: Int? = null,
|
||||
@Query("selfNotice") selfNotice: Int? = 0,
|
||||
@Query("order") order: String? = null,
|
||||
@Query("parentCommentId") parentCommentId: Int? = null,
|
||||
): Response<ListContainer<Comment>>
|
||||
|
||||
@GET("account/my")
|
||||
suspend fun getMyAccount(): Response<DataContainer<AccountProfile>>
|
||||
|
||||
@Multipart
|
||||
@PATCH("account/my/profile")
|
||||
suspend fun updateProfile(
|
||||
@Part avatar: MultipartBody.Part?,
|
||||
@Part banner: MultipartBody.Part?,
|
||||
@Part("nickname") nickname: RequestBody?,
|
||||
@Part("bio") bio: RequestBody?,
|
||||
): Response<Unit>
|
||||
|
||||
@POST("account/my/password")
|
||||
suspend fun changePassword(
|
||||
@Body body: ChangePasswordRequestBody
|
||||
): Response<Unit>
|
||||
|
||||
@GET("account/my/notice/like")
|
||||
suspend fun getMyLikeNotices(
|
||||
@Query("page") page: Int = 1,
|
||||
@Query("pageSize") pageSize: Int = 20,
|
||||
): Response<ListContainer<AccountLike>>
|
||||
|
||||
@GET("account/my/notice/follow")
|
||||
suspend fun getMyFollowNotices(
|
||||
@Query("page") page: Int = 1,
|
||||
@Query("pageSize") pageSize: Int = 20,
|
||||
): Response<ListContainer<AccountFollow>>
|
||||
|
||||
@GET("account/my/notice/favourite")
|
||||
suspend fun getMyFavouriteNotices(
|
||||
@Query("page") page: Int = 1,
|
||||
@Query("pageSize") pageSize: Int = 20,
|
||||
): Response<ListContainer<AccountFavourite>>
|
||||
|
||||
@GET("account/my/notice")
|
||||
suspend fun getMyNoticeInfo(): Response<DataContainer<AccountNotice>>
|
||||
|
||||
@POST("account/my/notice")
|
||||
suspend fun updateNoticeInfo(
|
||||
@Body body: UpdateNoticeRequestBody
|
||||
): Response<Unit>
|
||||
|
||||
@POST("account/my/messaging")
|
||||
suspend fun registerMessageChannel(
|
||||
@Body body: RegisterMessageChannelRequestBody
|
||||
): Response<Unit>
|
||||
|
||||
@POST("account/my/messaging/unregister")
|
||||
suspend fun unRegisterMessageChannel(
|
||||
@Body body: UnRegisterMessageChannelRequestBody
|
||||
): Response<Unit>
|
||||
|
||||
@GET("profile/{id}")
|
||||
suspend fun getAccountProfileById(
|
||||
@Path("id") id: Int
|
||||
): Response<DataContainer<AccountProfile>>
|
||||
|
||||
@GET("profile/trtc/{id}")
|
||||
suspend fun getAccountProfileByTrtcUserId(
|
||||
@Path("id") id: String
|
||||
): 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>
|
||||
|
||||
@GET("users")
|
||||
suspend fun getUsers(
|
||||
@Query("page") page: Int = 1,
|
||||
@Query("pageSize") pageSize: Int = 20,
|
||||
@Query("nickname") search: String? = null,
|
||||
@Query("followerId") followerId: Int? = null,
|
||||
@Query("followingId") followingId: Int? = null,
|
||||
): Response<ListContainer<AccountProfile>>
|
||||
|
||||
@POST("register/google")
|
||||
suspend fun registerWithGoogle(@Body body: GoogleRegisterRequestBody): Response<AuthResult>
|
||||
|
||||
@DELETE("post/{id}")
|
||||
suspend fun deletePost(
|
||||
@Path("id") id: Int
|
||||
): Response<Unit>
|
||||
|
||||
@DELETE("comment/{id}")
|
||||
suspend fun deleteComment(
|
||||
@Path("id") id: Int
|
||||
): Response<Unit>
|
||||
|
||||
@POST("account/my/password/reset")
|
||||
suspend fun resetPassword(
|
||||
@Body body: ResetPasswordRequestBody
|
||||
): Response<Unit>
|
||||
|
||||
@GET("comment/{id}")
|
||||
suspend fun getComment(
|
||||
@Path("id") id: Int
|
||||
): Response<DataContainer<Comment>>
|
||||
|
||||
@PATCH("account/my/extra")
|
||||
suspend fun updateUserExtra(
|
||||
@Body body: UpdateUserLangRequestBody
|
||||
): Response<Unit>
|
||||
|
||||
@GET("account/my/chat/sign")
|
||||
suspend fun getChatSign(): Response<DataContainer<TrtcSignResponseBody>>
|
||||
|
||||
@GET("app/info")
|
||||
suspend fun getAppConfig(): Response<DataContainer<AppConfig>>
|
||||
|
||||
@GET("dict")
|
||||
suspend fun getDict(
|
||||
@Query("key") key: String
|
||||
): Response<DataContainer<DictItem>>
|
||||
|
||||
@POST("captcha/generate")
|
||||
suspend fun generateCaptcha(
|
||||
@Body body: CaptchaRequestBody
|
||||
): Response<DataContainer<CaptchaResponseBody>>
|
||||
|
||||
@POST("login/needCaptcha")
|
||||
suspend fun checkLoginCaptcha(
|
||||
@Body body: CheckLoginCaptchaRequestBody
|
||||
): Response<DataContainer<Boolean>>
|
||||
|
||||
@POST("captcha/login/generate")
|
||||
suspend fun generateLoginCaptcha(
|
||||
@Body body: GenerateLoginCaptchaRequestBody
|
||||
): Response<DataContainer<CaptchaResponseBody>>
|
||||
|
||||
@GET("chat/notification")
|
||||
suspend fun getChatNotification(
|
||||
@Query("targetTrtcId") targetTrtcId: String
|
||||
): Response<DataContainer<ChatNotification>>
|
||||
|
||||
@POST("chat/notification")
|
||||
suspend fun updateChatNotification(
|
||||
@Body body: UpdateChatNotificationRequestBody
|
||||
): Response<DataContainer<ChatNotification>>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user