refactor: 重构data层
1.重构data层 2.重构ChatAdapter减少大量重复代码
This commit is contained in:
parent
e1816f525a
commit
57b6fd166e
@ -1,8 +1,8 @@
|
||||
package com.kaixed.kchat
|
||||
|
||||
import android.app.Application
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.init
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.init
|
||||
import com.kaixed.kchat.utils.DensityUtil
|
||||
import com.tencent.mmkv.MMKV
|
||||
import io.objectbox.android.Admin
|
||||
@ -18,6 +18,7 @@ class App : Application() {
|
||||
MMKV.initialize(this)
|
||||
|
||||
init(this)
|
||||
|
||||
Admin(getBoxStore()).start(this)
|
||||
|
||||
DensityUtil.init(this)
|
||||
|
@ -1,8 +1,8 @@
|
||||
package com.kaixed.kchat.data.objectbox.data
|
||||
package com.kaixed.kchat.data
|
||||
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact_
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.local.entity.Contact
|
||||
import com.kaixed.kchat.data.local.entity.Contact_
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
@ -14,5 +14,4 @@ object LocalContact {
|
||||
fun getContactByUsername(contactId: String): Contact? {
|
||||
return contactBox.query(Contact_.username.equal(contactId)).build().findFirst()
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package com.kaixed.kchat.data.local.box
|
||||
|
||||
import android.content.Context
|
||||
import com.kaixed.kchat.data.local.entity.MyObjectBox
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.BoxStore
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/10/24 16:57
|
||||
*/
|
||||
object ObjectBox {
|
||||
private lateinit var boxStore: BoxStore
|
||||
|
||||
fun init(context: Context) {
|
||||
boxStore = MyObjectBox.builder()
|
||||
.androidContext(context)
|
||||
.build()
|
||||
}
|
||||
|
||||
fun getBoxStore(): BoxStore = boxStore
|
||||
|
||||
fun <T> getBox(entityClass: Class<T>): Box<T> {
|
||||
return boxStore.boxFor(entityClass)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.data.objectbox.entity
|
||||
package com.kaixed.kchat.data.local.entity
|
||||
|
||||
import io.objectbox.annotation.Entity
|
||||
import io.objectbox.annotation.Id
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.data.objectbox.entity
|
||||
package com.kaixed.kchat.data.local.entity
|
||||
|
||||
import io.objectbox.annotation.Entity
|
||||
import io.objectbox.annotation.Id
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.data.objectbox.entity
|
||||
package com.kaixed.kchat.data.local.entity
|
||||
|
||||
import io.objectbox.annotation.Entity
|
||||
import io.objectbox.annotation.Id
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.data.objectbox.entity
|
||||
package com.kaixed.kchat.data.local.entity
|
||||
|
||||
import io.objectbox.annotation.Entity
|
||||
import io.objectbox.annotation.Id
|
@ -1,21 +0,0 @@
|
||||
package com.kaixed.kchat.data.objectbox
|
||||
|
||||
import android.content.Context
|
||||
import com.kaixed.kchat.data.objectbox.entity.MyObjectBox
|
||||
import io.objectbox.BoxStore
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/10/24 16:57
|
||||
*/
|
||||
object ObjectBox {
|
||||
private var store: BoxStore? = null
|
||||
|
||||
fun init(context: Context) {
|
||||
store = MyObjectBox.builder()
|
||||
.androidContext(context)
|
||||
.build()
|
||||
}
|
||||
|
||||
fun getBoxStore(): BoxStore = checkNotNull(store) { "ObjectBox is not initialized." }
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package com.kaixed.kchat.repository
|
||||
package com.kaixed.kchat.data.repository
|
||||
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact_
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.local.entity.Contact
|
||||
import com.kaixed.kchat.data.local.entity.Contact_
|
||||
import com.kaixed.kchat.model.friend.FriendRequestItem
|
||||
import com.kaixed.kchat.model.search.User
|
||||
import com.kaixed.kchat.network.ApiCall.apiCall
|
@ -1,8 +1,8 @@
|
||||
package com.kaixed.kchat.repository
|
||||
package com.kaixed.kchat.data.repository
|
||||
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo_
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.local.entity.UserInfo
|
||||
import com.kaixed.kchat.data.local.entity.UserInfo_
|
||||
import com.kaixed.kchat.model.request.RegisterRequest
|
||||
import com.kaixed.kchat.model.response.register.Register
|
||||
import com.kaixed.kchat.network.ApiCall.apiCall
|
@ -1,8 +1,8 @@
|
||||
package com.kaixed.kchat.repository
|
||||
package com.kaixed.kchat.data.repository
|
||||
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo_
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.local.entity.UserInfo
|
||||
import com.kaixed.kchat.data.local.entity.UserInfo_
|
||||
import com.kaixed.kchat.model.request.UserRequest
|
||||
import com.kaixed.kchat.model.search.SearchUser
|
||||
import com.kaixed.kchat.network.RetrofitClient
|
@ -1,4 +1,4 @@
|
||||
package com.kaixed.kchat.repository
|
||||
package com.kaixed.kchat.data.repository
|
||||
|
||||
import com.kaixed.kchat.model.response.search.User
|
||||
import com.kaixed.kchat.network.RetrofitClient
|
@ -1,6 +1,6 @@
|
||||
package com.kaixed.kchat.model.friend
|
||||
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.data.local.entity.Contact
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.kaixed.kchat.model.response.friend
|
||||
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.data.local.entity.Contact
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.kaixed.kchat.model.response.login
|
||||
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo
|
||||
import com.kaixed.kchat.data.local.entity.UserInfo
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.kaixed.kchat.network.service
|
||||
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.data.local.entity.Contact
|
||||
import com.kaixed.kchat.model.friend.FriendRequestItem
|
||||
import com.kaixed.kchat.model.search.User
|
||||
import com.kaixed.kchat.network.ApiResponse
|
||||
|
@ -1,17 +1,13 @@
|
||||
package com.kaixed.kchat.network.service
|
||||
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo
|
||||
import com.kaixed.kchat.data.local.entity.UserInfo
|
||||
import com.kaixed.kchat.model.request.RegisterRequest
|
||||
import com.kaixed.kchat.model.request.UserRequest
|
||||
import com.kaixed.kchat.model.response.friend.SearchFriends
|
||||
import com.kaixed.kchat.model.response.register.Register
|
||||
import com.kaixed.kchat.model.response.search.User
|
||||
import com.kaixed.kchat.model.response.user.ChangeNickname
|
||||
import com.kaixed.kchat.model.response.user.UploadAvatar
|
||||
import com.kaixed.kchat.model.search.SearchUser
|
||||
import com.kaixed.kchat.network.ApiResponse
|
||||
import okhttp3.MultipartBody
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.Field
|
||||
import retrofit2.http.FormUrlEncoded
|
||||
|
@ -8,10 +8,10 @@ import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.google.gson.Gson
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Conversation
|
||||
import com.kaixed.kchat.data.objectbox.entity.Conversation_
|
||||
import com.kaixed.kchat.data.objectbox.entity.Messages
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.local.entity.Conversation
|
||||
import com.kaixed.kchat.data.local.entity.Conversation_
|
||||
import com.kaixed.kchat.data.local.entity.Messages
|
||||
import com.kaixed.kchat.network.NetworkInterface.WEBSOCKET
|
||||
import com.kaixed.kchat.network.NetworkInterface.WEBSOCKET_SERVER_URL
|
||||
import com.kaixed.kchat.network.OkhttpHelper
|
||||
|
@ -24,9 +24,9 @@ import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Messages
|
||||
import com.kaixed.kchat.data.objectbox.entity.Messages_
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.local.entity.Messages
|
||||
import com.kaixed.kchat.data.local.entity.Messages_
|
||||
import com.kaixed.kchat.databinding.ActivityChatBinding
|
||||
import com.kaixed.kchat.model.FunctionItem
|
||||
import com.kaixed.kchat.service.WebSocketService
|
||||
|
@ -4,7 +4,7 @@ import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import com.bumptech.glide.Glide
|
||||
import com.kaixed.kchat.data.objectbox.data.LocalContact.getContactByUsername
|
||||
import com.kaixed.kchat.data.LocalContact.getContactByUsername
|
||||
import com.kaixed.kchat.databinding.ActivityChatDetailBinding
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
|
||||
|
@ -5,9 +5,9 @@ import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact_
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.local.entity.Contact
|
||||
import com.kaixed.kchat.data.local.entity.Contact_
|
||||
import com.kaixed.kchat.databinding.ActivityContactsDetailBinding
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
import io.objectbox.Box
|
||||
|
@ -11,8 +11,8 @@ import androidx.lifecycle.lifecycleScope
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.local.entity.Contact
|
||||
import com.kaixed.kchat.databinding.ActivityMainBinding
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
import com.kaixed.kchat.ui.fragment.ContactFragment
|
||||
|
@ -13,9 +13,9 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo_
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.local.entity.UserInfo
|
||||
import com.kaixed.kchat.data.local.entity.UserInfo_
|
||||
import com.kaixed.kchat.databinding.ActivityProfileDetailBinding
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
import com.kaixed.kchat.ui.widget.LoadingDialogFragment
|
||||
|
@ -14,8 +14,8 @@ import androidx.activity.viewModels
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.local.entity.UserInfo
|
||||
import com.kaixed.kchat.databinding.ActivityRegisterBinding
|
||||
import com.kaixed.kchat.model.request.RegisterRequest
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
|
@ -6,8 +6,8 @@ import android.os.Looper
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.local.entity.UserInfo
|
||||
import com.kaixed.kchat.databinding.ActivityRenameBinding
|
||||
import com.kaixed.kchat.model.request.UserRequest
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
|
@ -12,10 +12,9 @@ import androidx.core.widget.addTextChangedListener
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.data.LocalContact
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact_
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.LocalContact
|
||||
import com.kaixed.kchat.data.local.entity.Contact
|
||||
import com.kaixed.kchat.databinding.ActivitySetRemarkAndLabelBinding
|
||||
import com.kaixed.kchat.ui.base.BaseActivity
|
||||
import com.kaixed.kchat.ui.widget.LoadingDialogFragment
|
||||
|
@ -2,23 +2,20 @@ package com.kaixed.kchat.ui.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.drake.spannable.replaceSpan
|
||||
import com.drake.spannable.span.CenterImageSpan
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox
|
||||
import com.kaixed.kchat.data.objectbox.entity.Messages
|
||||
import com.kaixed.kchat.databinding.ChatRecycleItemCustomMineBinding
|
||||
import com.kaixed.kchat.databinding.ChatRecycleItemCustomOtherBinding
|
||||
import com.kaixed.kchat.databinding.ChatRecycleItemImageMineBinding
|
||||
import com.kaixed.kchat.databinding.ChatRecycleItemImageOtherBinding
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox
|
||||
import com.kaixed.kchat.data.local.entity.Messages
|
||||
import com.kaixed.kchat.databinding.ChatRecycleItemCustomNormalBinding
|
||||
import com.kaixed.kchat.databinding.ChatRecycleItemImageNormalBinding
|
||||
import com.kaixed.kchat.databinding.ChatRecycleItemTipBinding
|
||||
import com.kaixed.kchat.utils.ConstantsUtil.getUsername
|
||||
import com.kaixed.kchat.utils.PopWindowUtil.showPopupWindow
|
||||
import com.kaixed.kchat.utils.TextUtil
|
||||
import com.kaixed.kchat.utils.ViewUtil.changeTimerVisibility
|
||||
import com.kaixed.kchat.utils.ViewUtil.changeView
|
||||
import io.objectbox.Box
|
||||
import java.util.LinkedList
|
||||
|
||||
@ -28,72 +25,47 @@ class ChatAdapter(
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
companion object {
|
||||
// 普通文本消息
|
||||
private const val ITEM_TYPE_CUSTOM_MINE = 0
|
||||
private const val ITEM_TYPE_CUSTOM_OTHER = 1
|
||||
const val CUSTOM = 0
|
||||
|
||||
// 提示消息,如撤回消息
|
||||
private const val ITEM_TYPE_TIP_MINE = 2
|
||||
private const val ITEM_TYPE_TIP_OTHER = 3
|
||||
// 提示消息
|
||||
const val TIP = 2
|
||||
|
||||
// 图片消息
|
||||
private const val ITEM_TYPE_IMAGE_MINE = 4
|
||||
private const val ITEM_TYPE_IMAGE_OTHER = 5
|
||||
const val IMAGE = 4
|
||||
|
||||
// 语音消息
|
||||
private const val ITEM_TYPE_VOICE_MINE = 6
|
||||
private const val ITEM_TYPE_VOICE_OTHER = 7
|
||||
const val VOICE = 6
|
||||
|
||||
// 位置消息
|
||||
private const val ITEM_TYPE_LOCATION_MINE = 8
|
||||
private const val ITEM_TYPE_LOCATION_OTHER = 9
|
||||
const val LOCATION = 8
|
||||
|
||||
// 表情消息
|
||||
private const val ITEM_TYPE_EMOJI_MINE = 10
|
||||
private const val ITEM_TYPE_EMOJI_OTHER = 11
|
||||
const val EMOJI = 10
|
||||
|
||||
// 红包消息
|
||||
private const val ITEM_TYPE_RED_PACKET_MINE = 12
|
||||
private const val ITEM_TYPE_RED_PACKET_OTHER = 13
|
||||
const val RED_PACKET = 12
|
||||
|
||||
private const val TAG = "ChatAdapter"
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
ITEM_TYPE_CUSTOM_MINE -> {
|
||||
CustomViewMineHolder(
|
||||
ChatRecycleItemCustomMineBinding.inflate(
|
||||
CUSTOM -> {
|
||||
CustomViewHolder(
|
||||
ChatRecycleItemCustomNormalBinding.inflate(
|
||||
LayoutInflater.from(parent.context), parent, false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
ITEM_TYPE_CUSTOM_OTHER -> {
|
||||
CustomViewOtherHolder(
|
||||
ChatRecycleItemCustomOtherBinding.inflate(
|
||||
IMAGE -> {
|
||||
ImageViewHolder(
|
||||
ChatRecycleItemImageNormalBinding.inflate(
|
||||
LayoutInflater.from(parent.context), parent, false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
ITEM_TYPE_IMAGE_OTHER -> {
|
||||
ImageViewOtherHolder(
|
||||
ChatRecycleItemImageOtherBinding.inflate(
|
||||
LayoutInflater.from(parent.context), parent, false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
ITEM_TYPE_IMAGE_MINE -> {
|
||||
ImageViewMineHolder(
|
||||
ChatRecycleItemImageMineBinding.inflate(
|
||||
LayoutInflater.from(parent.context), parent, false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
else -> {
|
||||
TipViewHolder(
|
||||
ChatRecycleItemTipBinding.inflate(
|
||||
@ -108,83 +80,20 @@ class ChatAdapter(
|
||||
val singleMessage = messages[position]
|
||||
|
||||
when (holder) {
|
||||
is ImageViewOtherHolder -> {
|
||||
Glide.with(context).load(singleMessage.content).into(holder.binding.image)
|
||||
if (position == messages.size - 1) {
|
||||
holder.binding.tvTimer.visibility = View.VISIBLE
|
||||
holder.binding.tvTimer.text =
|
||||
TextUtil.getTimestampString(singleMessage.timestamp)
|
||||
} else {
|
||||
if (singleMessage.timestamp - messages[position + 1].timestamp < 1L * 60 * 1000) {
|
||||
holder.binding.tvTimer.visibility = View.GONE
|
||||
} else {
|
||||
holder.binding.tvTimer.visibility = View.VISIBLE
|
||||
holder.binding.tvTimer.text =
|
||||
TextUtil.getTimestampString(singleMessage.timestamp)
|
||||
}
|
||||
}
|
||||
is CustomViewHolder -> {
|
||||
holder.bindData(singleMessage)
|
||||
changeTimerVisibility(position, messages, holder.binding.tvTimer, singleMessage)
|
||||
}
|
||||
|
||||
is ImageViewHolder -> {
|
||||
holder.bindData(singleMessage)
|
||||
changeTimerVisibility(position, messages, holder.binding.tvTimer, singleMessage)
|
||||
}
|
||||
|
||||
is TipViewHolder -> {
|
||||
holder.bindData(singleMessage)
|
||||
}
|
||||
|
||||
is CustomViewMineHolder -> {
|
||||
if (position == messages.size - 1) {
|
||||
holder.binding.tvTimer.visibility = View.VISIBLE
|
||||
holder.binding.tvTimer.text =
|
||||
TextUtil.getTimestampString(singleMessage.timestamp)
|
||||
} else {
|
||||
if (singleMessage.timestamp - messages[position + 1].timestamp < 1L * 60 * 1000) {
|
||||
holder.binding.tvTimer.visibility = View.GONE
|
||||
} else {
|
||||
holder.binding.tvTimer.visibility = View.VISIBLE
|
||||
holder.binding.tvTimer.text =
|
||||
TextUtil.getTimestampString(singleMessage.timestamp)
|
||||
}
|
||||
}
|
||||
holder.binding.tvMessageContent.setOnLongClickListener {
|
||||
showPopupWindow(
|
||||
context,
|
||||
holder.binding.tvMessageContent,
|
||||
singleMessage.senderId == getUsername()
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
holder.binding.tvMessageContent.text = singleMessage.content.replaceSpan("[委屈]") {
|
||||
CenterImageSpan(context, R.drawable.emoji).setDrawableSize(55)
|
||||
}
|
||||
}
|
||||
|
||||
is CustomViewOtherHolder -> {
|
||||
if (position == messages.size - 1) {
|
||||
holder.binding.tvTimer.visibility = View.VISIBLE
|
||||
holder.binding.tvTimer.text =
|
||||
TextUtil.getTimestampString(singleMessage.timestamp)
|
||||
} else {
|
||||
if (singleMessage.timestamp - messages[position + 1].timestamp < 1L * 60 * 1000) {
|
||||
holder.binding.tvTimer.visibility = View.GONE
|
||||
} else {
|
||||
holder.binding.tvTimer.visibility = View.VISIBLE
|
||||
holder.binding.tvTimer.text =
|
||||
TextUtil.getTimestampString(singleMessage.timestamp)
|
||||
}
|
||||
}
|
||||
holder.binding.tvMessageContent.setOnLongClickListener {
|
||||
showPopupWindow(
|
||||
context,
|
||||
holder.binding.tvMessageContent,
|
||||
singleMessage.senderId == getUsername()
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
holder.binding.tvMessageContent.text = singleMessage.content.replaceSpan("[委屈]") {
|
||||
CenterImageSpan(context, R.drawable.emoji).setDrawableSize(55)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,25 +108,42 @@ class ChatAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
class CustomViewMineHolder(val binding: ChatRecycleItemCustomMineBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
class CustomViewOtherHolder(val binding: ChatRecycleItemCustomOtherBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
class CustomViewHolder(val binding: ChatRecycleItemCustomNormalBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
fun bindData(message: Messages) {
|
||||
val sender = message.senderId == getUsername()
|
||||
binding.tvMessageContent.text = message.content.replaceSpan("[委屈]") {
|
||||
CenterImageSpan(binding.root.context, R.drawable.emoji).setDrawableSize(55)
|
||||
}
|
||||
|
||||
class ImageViewMineHolder(val binding: ChatRecycleItemImageMineBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
changeView(
|
||||
parentView = binding.root,
|
||||
sender = sender,
|
||||
avatarId = binding.ifvAvatar.id,
|
||||
contentId = binding.tvMessageContent.id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ImageViewOtherHolder(val binding: ChatRecycleItemImageOtherBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
class ImageViewHolder(val binding: ChatRecycleItemImageNormalBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
fun bindData(message: Messages) {
|
||||
Glide.with(binding.root.context).load(message.content).into(binding.image)
|
||||
val sender = message.senderId == getUsername()
|
||||
changeView(
|
||||
parentView = binding.root,
|
||||
sender = sender,
|
||||
avatarId = binding.ifvAvatar.id,
|
||||
contentId = binding.image.id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
val message = messages[position]
|
||||
return if (message.senderId == getUsername()) {
|
||||
message.type.toInt()
|
||||
} else {
|
||||
message.type.toInt() + 1
|
||||
}
|
||||
return messages[position].type.toInt()
|
||||
|
||||
}
|
||||
|
||||
private fun updateDb(message: Messages) {
|
||||
@ -226,5 +152,4 @@ class ChatAdapter(
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = messages.size
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import com.bumptech.glide.request.RequestOptions
|
||||
import com.drake.spannable.replaceSpan
|
||||
import com.drake.spannable.span.CenterImageSpan
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.entity.Conversation
|
||||
import com.kaixed.kchat.data.local.entity.Conversation
|
||||
import com.kaixed.kchat.databinding.ChatMainItemBinding
|
||||
import com.kaixed.kchat.ui.activity.ChatActivity
|
||||
import com.kaixed.kchat.ui.i.OnChatListItemClickListener
|
||||
|
@ -11,7 +11,7 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.data.local.entity.Contact
|
||||
import com.kaixed.kchat.databinding.FriendRecycleFooterItemBinding
|
||||
import com.kaixed.kchat.databinding.FriendRecycleItemBinding
|
||||
import com.kaixed.kchat.ui.activity.ContactRequestListActivity
|
||||
|
@ -11,7 +11,7 @@ import android.view.ViewGroup
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.data.local.entity.Contact
|
||||
import com.kaixed.kchat.databinding.FragmentContactBinding
|
||||
import com.kaixed.kchat.ui.adapter.FriendListAdapter
|
||||
import com.kaixed.kchat.ui.base.BaseFragment
|
||||
|
@ -20,10 +20,10 @@ import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AppCompatActivity.BIND_AUTO_CREATE
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Conversation
|
||||
import com.kaixed.kchat.data.objectbox.entity.Conversation_
|
||||
import com.kaixed.kchat.data.objectbox.entity.Messages
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.local.entity.Conversation
|
||||
import com.kaixed.kchat.data.local.entity.Conversation_
|
||||
import com.kaixed.kchat.data.local.entity.Messages
|
||||
import com.kaixed.kchat.databinding.FragmentHomeBinding
|
||||
import com.kaixed.kchat.model.HomeItem
|
||||
import com.kaixed.kchat.service.WebSocketService
|
||||
|
@ -8,9 +8,9 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.kaixed.kchat.R
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Conversation
|
||||
import com.kaixed.kchat.data.objectbox.entity.Messages
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.local.entity.Conversation
|
||||
import com.kaixed.kchat.data.local.entity.Messages
|
||||
import com.kaixed.kchat.databinding.BottomSheetLayoutBinding
|
||||
import com.kaixed.kchat.ui.activity.LoginActivity
|
||||
import com.kaixed.kchat.utils.Constants.MMKV_USER_SESSION
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.kaixed.kchat.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.util.TypedValue
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
@ -15,7 +16,18 @@ object DensityUtil {
|
||||
}
|
||||
|
||||
fun dpToPx(dp: Int): Int =
|
||||
(appContext.resources.displayMetrics.density * dp).toInt()
|
||||
TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
dp.toFloat(),
|
||||
appContext.resources.displayMetrics
|
||||
).toInt()
|
||||
|
||||
fun dp2Px(dp: Int): Int =
|
||||
TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
dp.toFloat(),
|
||||
appContext.resources.displayMetrics
|
||||
).toInt()
|
||||
|
||||
fun pxToDp(px: Int): Int =
|
||||
(px / appContext.resources.displayMetrics.density).toInt()
|
||||
|
@ -22,7 +22,7 @@ object TextUtil {
|
||||
|
||||
// 判断是否是昨天
|
||||
if (isYesterday(timestamp)) {
|
||||
return "昨天${formatTimestamp(timestamp, "HH:mm")}"
|
||||
return "昨天 ${formatTimestamp(timestamp, "HH:mm")}"
|
||||
}
|
||||
|
||||
// 判断是否超过两天
|
||||
|
68
app/src/main/java/com/kaixed/kchat/utils/ViewUtil.kt
Normal file
68
app/src/main/java/com/kaixed/kchat/utils/ViewUtil.kt
Normal file
@ -0,0 +1,68 @@
|
||||
package com.kaixed.kchat.utils
|
||||
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
import com.kaixed.kchat.data.local.entity.Messages
|
||||
import com.kaixed.kchat.utils.DensityUtil.dp2Px
|
||||
|
||||
/**
|
||||
* @Author: kaixed
|
||||
* @Date: 2024/11/26 12:12
|
||||
*/
|
||||
object ViewUtil {
|
||||
fun changeView(
|
||||
parentView: ConstraintLayout,
|
||||
sender: Boolean,
|
||||
avatarId: Int,
|
||||
contentId: Int
|
||||
) {
|
||||
ConstraintSet().apply {
|
||||
clone(parentView)
|
||||
if (sender) {
|
||||
connect(
|
||||
avatarId, ConstraintSet.END,
|
||||
parentView.id, ConstraintSet.END,
|
||||
dp2Px(10)
|
||||
)
|
||||
connect(
|
||||
contentId, ConstraintSet.END,
|
||||
avatarId, ConstraintSet.START,
|
||||
dp2Px(10)
|
||||
)
|
||||
} else {
|
||||
connect(
|
||||
avatarId, ConstraintSet.START,
|
||||
parentView.id, ConstraintSet.START,
|
||||
dp2Px(10)
|
||||
)
|
||||
connect(
|
||||
contentId, ConstraintSet.START,
|
||||
avatarId, ConstraintSet.END,
|
||||
dp2Px(10)
|
||||
)
|
||||
}
|
||||
applyTo(parentView)
|
||||
}
|
||||
}
|
||||
|
||||
fun changeTimerVisibility(
|
||||
position: Int,
|
||||
messages: List<Messages>,
|
||||
tvTimer: TextView,
|
||||
singleMessage: Messages
|
||||
) {
|
||||
val showTimer =
|
||||
if (position == messages.size - 1) true else
|
||||
singleMessage.timestamp - messages[position + 1].timestamp >= 1L * 60 * 1000
|
||||
if (showTimer) {
|
||||
tvTimer.visibility = View.VISIBLE
|
||||
tvTimer.text = TextUtil.getTimestampString(singleMessage.timestamp)
|
||||
} else {
|
||||
tvTimer.visibility = View.GONE
|
||||
}
|
||||
tvTimer.visibility = if (showTimer) View.VISIBLE else View.GONE
|
||||
tvTimer.text = TextUtil.getTimestampString(singleMessage.timestamp)
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package com.kaixed.kchat.utils.handle
|
||||
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.local.entity.Contact
|
||||
import com.kaixed.kchat.utils.Pinyin4jUtil
|
||||
import io.objectbox.Box
|
||||
|
||||
|
@ -4,11 +4,11 @@ import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.kaixed.kchat.data.objectbox.ObjectBox.getBoxStore
|
||||
import com.kaixed.kchat.data.objectbox.entity.Contact
|
||||
import com.kaixed.kchat.data.local.box.ObjectBox.getBox
|
||||
import com.kaixed.kchat.data.local.entity.Contact
|
||||
import com.kaixed.kchat.model.friend.FriendRequestItem
|
||||
import com.kaixed.kchat.model.search.User
|
||||
import com.kaixed.kchat.repository.ContactRepository
|
||||
import com.kaixed.kchat.data.repository.ContactRepository
|
||||
import io.objectbox.Box
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@ -95,7 +95,7 @@ class ContactViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
fun loadFriendListInDb(): List<Contact> {
|
||||
val contactBox: Box<Contact> = getBoxStore().boxFor(Contact::class.java)
|
||||
val contactBox: Box<Contact> = getBox(Contact::class.java)
|
||||
|
||||
val sortedContacts = contactBox.query()
|
||||
.sort { contact1, contact2 ->
|
||||
|
@ -4,15 +4,15 @@ import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.kaixed.kchat.data.objectbox.entity.UserInfo
|
||||
import com.kaixed.kchat.data.local.entity.UserInfo
|
||||
import com.kaixed.kchat.model.request.RegisterRequest
|
||||
import com.kaixed.kchat.model.request.UserRequest
|
||||
import com.kaixed.kchat.model.response.register.Register
|
||||
import com.kaixed.kchat.model.response.search.User
|
||||
import com.kaixed.kchat.model.search.SearchUser
|
||||
import com.kaixed.kchat.repository.UserAuthRepository
|
||||
import com.kaixed.kchat.repository.UserProfileRepository
|
||||
import com.kaixed.kchat.repository.UserSearchRepository
|
||||
import com.kaixed.kchat.data.repository.UserAuthRepository
|
||||
import com.kaixed.kchat.data.repository.UserProfileRepository
|
||||
import com.kaixed.kchat.data.repository.UserSearchRepository
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.MultipartBody
|
||||
|
||||
|
45
app/src/main/res/layout/chat_recycle_item_custom_normal.xml
Normal file
45
app/src/main/res/layout/chat_recycle_item_custom_normal.xml
Normal file
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_timer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:paddingTop="20dp"
|
||||
android:text="昨天"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/ifv_avatar"
|
||||
android:layout_width="35dp"
|
||||
android:layout_height="35dp"
|
||||
android:layout_marginVertical="20dp"
|
||||
android:src="@drawable/ic_avatar"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_timer"
|
||||
app:roundPercent="0.3" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_message_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/chat_send_btn_bac_mine"
|
||||
android:gravity="center_vertical"
|
||||
android:maxWidth="250dp"
|
||||
android:minHeight="35dp"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingVertical="6dp"
|
||||
android:text="haha"
|
||||
android:textColor="@color/white"
|
||||
android:textColorHighlight="#CCCCCC"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="17sp"
|
||||
app:layout_constraintTop_toTopOf="@id/ifv_avatar" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -6,14 +6,33 @@
|
||||
android:paddingVertical="10dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:id="@+id/tv_timer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="这是一个小tip"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:gravity="center_horizontal"
|
||||
android:paddingTop="20dp"
|
||||
android:text="昨天"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/ifv_avatar"
|
||||
android:layout_width="35dp"
|
||||
android:layout_height="35dp"
|
||||
android:layout_marginVertical="20dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:src="@drawable/ic_avatar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_timer"
|
||||
app:roundPercent="0.3" />
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintEnd_toStartOf="@id/ifv_avatar"
|
||||
app:layout_constraintTop_toTopOf="@id/ifv_avatar"
|
||||
app:roundPercent="0.2" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
35
app/src/main/res/layout/chat_recycle_item_image_normal.xml
Normal file
35
app/src/main/res/layout/chat_recycle_item_image_normal.xml
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_timer"
|
||||
android:layout_width="match_parent"
|
||||
android:textSize="12sp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:paddingTop="20dp"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/ifv_avatar"
|
||||
android:layout_width="35dp"
|
||||
android:layout_height="35dp"
|
||||
android:layout_marginVertical="20dp"
|
||||
android:src="@drawable/ic_avatar"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_timer"
|
||||
app:roundPercent="0.3" />
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="100dp"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintTop_toTopOf="@id/ifv_avatar"
|
||||
app:roundPercent="0.15" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -25,13 +25,14 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_timer"
|
||||
app:roundPercent="0.3" />
|
||||
|
||||
<ImageView
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:scaleType="centerCrop"
|
||||
app:layout_constraintStart_toEndOf="@id/ifv_avatar"
|
||||
app:layout_constraintTop_toTopOf="@id/ifv_avatar" />
|
||||
app:layout_constraintTop_toTopOf="@id/ifv_avatar"
|
||||
app:roundPercent="0.2" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in New Issue
Block a user