Upstream changes (#23)
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,354 @@
|
||||
package com.meloda.app.fast.data
|
||||
|
||||
import android.util.Log
|
||||
import com.meloda.app.fast.common.UserConfig
|
||||
import com.meloda.app.fast.common.VkConstants
|
||||
import com.meloda.app.fast.common.extensions.asInt
|
||||
import com.meloda.app.fast.common.extensions.listenValue
|
||||
import com.meloda.app.fast.common.extensions.toList
|
||||
import com.meloda.app.fast.data.api.messages.MessagesUseCase
|
||||
import com.meloda.app.fast.model.ApiEvent
|
||||
import com.meloda.app.fast.model.InteractionType
|
||||
import com.meloda.app.fast.model.LongPollEvent
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class LongPollUpdatesParser(
|
||||
private val messagesUseCase: MessagesUseCase
|
||||
) {
|
||||
private val job = SupervisorJob()
|
||||
|
||||
private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
|
||||
Log.d("LongPollUpdatesParser", "error: $throwable")
|
||||
throwable.printStackTrace()
|
||||
}
|
||||
|
||||
private val coroutineContext: CoroutineContext
|
||||
get() = Dispatchers.Default + job + exceptionHandler
|
||||
|
||||
private val coroutineScope = CoroutineScope(coroutineContext)
|
||||
|
||||
private val listenersMap: MutableMap<ApiEvent, MutableCollection<VkEventCallback<*>>> =
|
||||
mutableMapOf()
|
||||
|
||||
fun parseNextUpdate(event: List<Any>) {
|
||||
val eventId = event.first().asInt()
|
||||
|
||||
val eventType: ApiEvent = try {
|
||||
ApiEvent.parse(eventId)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Log.d("LongPollUpdatesParser", "parseNextUpdate: unknownEvent: $event")
|
||||
return
|
||||
}
|
||||
|
||||
when (eventType) {
|
||||
ApiEvent.MESSAGE_SET_FLAGS -> parseMessageSetFlags(eventType, event)
|
||||
ApiEvent.MESSAGE_CLEAR_FLAGS -> parseMessageClearFlags(eventType, event)
|
||||
ApiEvent.MESSAGE_NEW -> parseMessageNew(eventType, event)
|
||||
ApiEvent.MESSAGE_EDIT -> parseMessageEdit(eventType, event)
|
||||
ApiEvent.MESSAGE_READ_INCOMING -> parseMessageReadIncoming(eventType, event)
|
||||
ApiEvent.MESSAGE_READ_OUTGOING -> parseMessageReadOutgoing(eventType, event)
|
||||
ApiEvent.MESSAGES_DELETED -> parseMessagesDeleted(eventType, event)
|
||||
ApiEvent.PIN_UNPIN_CONVERSATION -> parseConversationPinStateChanged(eventType, event)
|
||||
|
||||
ApiEvent.TYPING,
|
||||
ApiEvent.AUDIO_MESSAGE_RECORDING,
|
||||
ApiEvent.PHOTO_UPLOADING,
|
||||
ApiEvent.VIDEO_UPLOADING,
|
||||
ApiEvent.FILE_UPLOADING -> parseInteraction(eventType, event)
|
||||
|
||||
ApiEvent.UNREAD_COUNT_UPDATE -> onNewEvent(eventType, event)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onNewEvent(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "newEvent: $eventType: $event")
|
||||
}
|
||||
|
||||
private fun parseInteraction(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
|
||||
val interactionType = when (eventType) {
|
||||
ApiEvent.TYPING -> InteractionType.Typing
|
||||
ApiEvent.AUDIO_MESSAGE_RECORDING -> InteractionType.VoiceMessage
|
||||
ApiEvent.PHOTO_UPLOADING -> InteractionType.Photo
|
||||
ApiEvent.VIDEO_UPLOADING -> InteractionType.Video
|
||||
ApiEvent.FILE_UPLOADING -> InteractionType.File
|
||||
else -> return
|
||||
}
|
||||
|
||||
val peerId = event[1].asInt()
|
||||
val userIds = event[2].toList(Any::asInt).filter { it != UserConfig.userId }
|
||||
val totalCount = event[3].asInt()
|
||||
val timestamp = event[4].asInt()
|
||||
|
||||
// if userIds contains only account's id, then we don't need to show our status
|
||||
if (userIds.isEmpty()) return
|
||||
|
||||
coroutineScope.launch {
|
||||
listenersMap[eventType]?.let { listeners ->
|
||||
listeners.forEach { vkEventCallback ->
|
||||
(vkEventCallback as VkEventCallback<LongPollEvent.Interaction>)
|
||||
.onEvent(
|
||||
LongPollEvent.Interaction(
|
||||
interactionType = interactionType,
|
||||
peerId = peerId,
|
||||
userIds = userIds,
|
||||
totalCount = totalCount,
|
||||
timestamp = timestamp
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseConversationPinStateChanged(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
|
||||
val peerId = event[1].asInt()
|
||||
val majorId = event[2].asInt()
|
||||
|
||||
coroutineScope.launch {
|
||||
listenersMap[ApiEvent.PIN_UNPIN_CONVERSATION]?.let { listeners ->
|
||||
listeners.forEach { vkEventCallback ->
|
||||
(vkEventCallback as VkEventCallback<LongPollEvent.VkConversationPinStateChangedEvent>)
|
||||
.onEvent(
|
||||
LongPollEvent.VkConversationPinStateChangedEvent(
|
||||
peerId = peerId,
|
||||
majorId = majorId
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseMessageSetFlags(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
}
|
||||
|
||||
private fun parseMessageClearFlags(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
}
|
||||
|
||||
private fun parseMessageNew(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
val messageId = event[1].asInt()
|
||||
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
val newMessageEvent: LongPollEvent.VkMessageNewEvent? =
|
||||
loadNormalMessage(
|
||||
eventType,
|
||||
messageId
|
||||
)
|
||||
|
||||
newMessageEvent?.let { event ->
|
||||
listenersMap[ApiEvent.MESSAGE_NEW]?.let {
|
||||
it.map { vkEventCallback ->
|
||||
(vkEventCallback as VkEventCallback<LongPollEvent.VkMessageNewEvent>)
|
||||
.onEvent(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseMessageEdit(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
val messageId = event[1].asInt()
|
||||
|
||||
coroutineScope.launch {
|
||||
val editedMessageEvent: LongPollEvent.VkMessageEditEvent? =
|
||||
loadNormalMessage(
|
||||
eventType,
|
||||
messageId
|
||||
)
|
||||
|
||||
editedMessageEvent?.let { event ->
|
||||
listenersMap[ApiEvent.MESSAGE_EDIT]?.let {
|
||||
it.map { vkEventCallback ->
|
||||
(vkEventCallback as VkEventCallback<LongPollEvent.VkMessageEditEvent>)
|
||||
.onEvent(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseMessageReadIncoming(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
val peerId = event[1].asInt()
|
||||
val messageId = event[2].asInt()
|
||||
val unreadCount = event[3].asInt()
|
||||
|
||||
coroutineScope.launch {
|
||||
listenersMap[ApiEvent.MESSAGE_READ_INCOMING]?.let { listeners ->
|
||||
listeners.map { vkEventCallback ->
|
||||
(vkEventCallback as VkEventCallback<LongPollEvent.VkMessageReadIncomingEvent>)
|
||||
.onEvent(
|
||||
LongPollEvent.VkMessageReadIncomingEvent(
|
||||
peerId = peerId,
|
||||
messageId = messageId,
|
||||
unreadCount = unreadCount
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseMessageReadOutgoing(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
val peerId = event[1].asInt()
|
||||
val messageId = event[2].asInt()
|
||||
val unreadCount = event[3].asInt()
|
||||
|
||||
coroutineScope.launch {
|
||||
listenersMap[ApiEvent.MESSAGE_READ_OUTGOING]?.let { listeners ->
|
||||
listeners.map { vkEventCallback ->
|
||||
(vkEventCallback as VkEventCallback<LongPollEvent.VkMessageReadOutgoingEvent>)
|
||||
.onEvent(
|
||||
LongPollEvent.VkMessageReadOutgoingEvent(
|
||||
peerId = peerId,
|
||||
messageId = messageId,
|
||||
unreadCount = unreadCount
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseMessagesDeleted(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
}
|
||||
|
||||
private suspend fun <T : LongPollEvent> loadNormalMessage(
|
||||
eventType: ApiEvent,
|
||||
messageId: Int
|
||||
): T? = suspendCoroutine {
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
messagesUseCase.getById(
|
||||
messageId = messageId,
|
||||
extended = true,
|
||||
fields = VkConstants.ALL_FIELDS
|
||||
).listenValue(this) { state ->
|
||||
state.processState(
|
||||
error = { error ->
|
||||
Log.e("LongPollUpdatesParser", "loadNormalMessage: error: $error")
|
||||
},
|
||||
success = { response ->
|
||||
response?.let { message ->
|
||||
VkMemoryCache[message.id] = message
|
||||
messagesUseCase.storeMessage(message)
|
||||
|
||||
val resumeValue: LongPollEvent? = when (eventType) {
|
||||
ApiEvent.MESSAGE_NEW -> LongPollEvent.VkMessageNewEvent(message)
|
||||
ApiEvent.MESSAGE_EDIT -> LongPollEvent.VkMessageEditEvent(message)
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
resumeValue?.let { value -> it.resume(value as T) }
|
||||
} ?: it.resume(null)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : Any> registerListener(
|
||||
eventType: ApiEvent,
|
||||
listener: VkEventCallback<T>
|
||||
) {
|
||||
listenersMap.let { map ->
|
||||
map[eventType] = (map[eventType] ?: mutableListOf()).also { it.add(listener) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : Any> registerListeners(
|
||||
eventTypes: List<ApiEvent>,
|
||||
listener: VkEventCallback<T>
|
||||
) {
|
||||
eventTypes.forEach { eventType -> registerListener(eventType, listener) }
|
||||
}
|
||||
|
||||
fun onConversationPinStateChanged(listener: VkEventCallback<LongPollEvent.VkConversationPinStateChangedEvent>) {
|
||||
registerListener(ApiEvent.PIN_UNPIN_CONVERSATION, listener)
|
||||
}
|
||||
|
||||
fun onConversationPinStateChanged(block: (LongPollEvent.VkConversationPinStateChangedEvent) -> Unit) {
|
||||
onConversationPinStateChanged(assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onMessageIncomingRead(listener: VkEventCallback<LongPollEvent.VkMessageReadIncomingEvent>) {
|
||||
registerListener(ApiEvent.MESSAGE_READ_INCOMING, listener)
|
||||
}
|
||||
|
||||
fun onMessageIncomingRead(block: (LongPollEvent.VkMessageReadIncomingEvent) -> Unit) {
|
||||
onMessageIncomingRead(assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onMessageOutgoingRead(listener: VkEventCallback<LongPollEvent.VkMessageReadOutgoingEvent>) {
|
||||
registerListener(ApiEvent.MESSAGE_READ_OUTGOING, listener)
|
||||
}
|
||||
|
||||
fun onMessageOutgoingRead(block: (LongPollEvent.VkMessageReadOutgoingEvent) -> Unit) {
|
||||
onMessageOutgoingRead(assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onNewMessage(listener: VkEventCallback<LongPollEvent.VkMessageNewEvent>) {
|
||||
registerListener(ApiEvent.MESSAGE_NEW, listener)
|
||||
}
|
||||
|
||||
fun onNewMessage(block: (LongPollEvent.VkMessageNewEvent) -> Unit) {
|
||||
onNewMessage(assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onMessageEdited(listener: VkEventCallback<LongPollEvent.VkMessageEditEvent>) {
|
||||
registerListener(ApiEvent.MESSAGE_EDIT, listener)
|
||||
}
|
||||
|
||||
fun onMessageEdited(block: (LongPollEvent.VkMessageEditEvent) -> Unit) {
|
||||
onMessageEdited(assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onInteractions(listener: VkEventCallback<LongPollEvent.Interaction>) {
|
||||
registerListeners(
|
||||
eventTypes = listOf(
|
||||
ApiEvent.TYPING,
|
||||
ApiEvent.AUDIO_MESSAGE_RECORDING,
|
||||
ApiEvent.PHOTO_UPLOADING,
|
||||
ApiEvent.VIDEO_UPLOADING,
|
||||
ApiEvent.FILE_UPLOADING
|
||||
),
|
||||
listener = listener
|
||||
)
|
||||
}
|
||||
|
||||
fun onInteractions(block: (LongPollEvent.Interaction) -> Unit) {
|
||||
onInteractions(assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun clearListeners() {
|
||||
listenersMap.clear()
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <R : Any> assembleEventCallback(
|
||||
crossinline block: (R) -> Unit,
|
||||
): VkEventCallback<R> {
|
||||
return VkEventCallback { event -> block.invoke(event) }
|
||||
}
|
||||
|
||||
fun interface VkEventCallback<in T : Any> {
|
||||
fun onEvent(event: T)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.meloda.app.fast.data
|
||||
|
||||
import com.meloda.app.fast.model.api.data.LongPollUpdates
|
||||
import com.meloda.app.fast.model.api.data.VkLongPollData
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface LongPollUseCase {
|
||||
|
||||
fun getLongPollServer(
|
||||
needPts: Boolean,
|
||||
version: Int
|
||||
): Flow<State<VkLongPollData>>
|
||||
|
||||
fun getLongPollUpdates(
|
||||
serverUrl: String,
|
||||
act: String = "a_check",
|
||||
key: String,
|
||||
ts: Int,
|
||||
wait: Int,
|
||||
mode: Int,
|
||||
version: Int
|
||||
): Flow<State<LongPollUpdates>>
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.meloda.app.fast.data
|
||||
|
||||
import com.meloda.app.fast.data.api.longpoll.LongPollRepository
|
||||
import com.meloda.app.fast.model.api.data.LongPollUpdates
|
||||
import com.meloda.app.fast.model.api.data.VkLongPollData
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
class LongPollUseCaseImpl(
|
||||
private val repository: LongPollRepository
|
||||
) : LongPollUseCase {
|
||||
|
||||
override fun getLongPollServer(
|
||||
needPts: Boolean,
|
||||
version: Int
|
||||
): Flow<State<VkLongPollData>> = flow {
|
||||
emit(State.Loading)
|
||||
|
||||
val newState = repository.getLongPollServer(
|
||||
needPts = needPts,
|
||||
version = version
|
||||
).mapToState()
|
||||
|
||||
emit(newState)
|
||||
}
|
||||
|
||||
override fun getLongPollUpdates(
|
||||
serverUrl: String,
|
||||
act: String,
|
||||
key: String,
|
||||
ts: Int,
|
||||
wait: Int,
|
||||
mode: Int,
|
||||
version: Int
|
||||
): Flow<State<LongPollUpdates>> = flow {
|
||||
emit(State.Loading)
|
||||
|
||||
val newState = repository.getLongPollUpdates(
|
||||
serverUrl,
|
||||
act = act,
|
||||
key = key,
|
||||
ts = ts,
|
||||
wait = wait,
|
||||
mode = mode,
|
||||
version = version
|
||||
).mapToState()
|
||||
emit(newState)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.meloda.app.fast.data
|
||||
|
||||
import com.meloda.app.fast.network.OAuthErrorDomain
|
||||
import com.meloda.app.fast.network.RestApiErrorDomain
|
||||
import com.slack.eithernet.ApiResult
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
sealed class State<out T> {
|
||||
|
||||
data object Idle : State<Nothing>()
|
||||
data class Success<T>(val data: T) : State<T>()
|
||||
data object Loading : State<Nothing>()
|
||||
|
||||
sealed class Error : State<Nothing>() {
|
||||
|
||||
data class ApiError(
|
||||
val errorCode: Int,
|
||||
val errorMessage: String,
|
||||
) : Error()
|
||||
|
||||
data object ConnectionError : Error()
|
||||
|
||||
data object Unknown : Error()
|
||||
|
||||
data object InternalError : Error()
|
||||
|
||||
data class OAuthError(val error: OAuthErrorDomain) : Error()
|
||||
}
|
||||
|
||||
fun isLoading(): Boolean = this is Loading
|
||||
|
||||
companion object {
|
||||
|
||||
val UNKNOWN_ERROR = Error.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> State<T>.processState(
|
||||
error: (error: State.Error) -> (Unit),
|
||||
success: (data: T) -> (Unit),
|
||||
idle: (() -> (Unit)) = {},
|
||||
loading: (() -> (Unit)) = {},
|
||||
) {
|
||||
when (this) {
|
||||
is State.Error -> error(this)
|
||||
State.Idle -> idle()
|
||||
State.Loading -> loading()
|
||||
is State.Success -> success(data)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T, R> Flow<State<T>>.mapSuccess(
|
||||
crossinline transform: suspend (value: T) -> R
|
||||
): Flow<R> = filterIsInstance<State.Success<T>>()
|
||||
.map { state -> transform.invoke(state.data) }
|
||||
|
||||
fun RestApiErrorDomain?.toStateApiError(): State.Error = when (this) {
|
||||
null -> State.Error.ConnectionError
|
||||
else -> State.Error.ApiError(code, message)
|
||||
}
|
||||
|
||||
fun OAuthErrorDomain?.toStateApiError(): State.Error = when (this) {
|
||||
null -> State.Error.ConnectionError
|
||||
else -> State.Error.OAuthError(this)
|
||||
}
|
||||
|
||||
fun <T : Any> ApiResult<T, RestApiErrorDomain>.mapToState() = when (this) {
|
||||
is ApiResult.Success -> State.Success(this.value)
|
||||
|
||||
is ApiResult.Failure.NetworkFailure -> State.Error.ConnectionError
|
||||
is ApiResult.Failure.UnknownFailure -> State.UNKNOWN_ERROR
|
||||
is ApiResult.Failure.HttpFailure -> this.error.toStateApiError()
|
||||
is ApiResult.Failure.ApiFailure -> this.error.toStateApiError()
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.meloda.app.fast.data
|
||||
|
||||
import com.meloda.app.fast.model.api.data.VkMessageData
|
||||
import com.meloda.app.fast.model.api.domain.VkConversation
|
||||
import com.meloda.app.fast.model.api.domain.VkGroupDomain
|
||||
import com.meloda.app.fast.model.api.domain.VkMessage
|
||||
import kotlin.math.abs
|
||||
|
||||
class VkGroupsMap(
|
||||
private val groups: List<VkGroupDomain>
|
||||
) {
|
||||
|
||||
private val map: HashMap<Int, VkGroupDomain> by lazy {
|
||||
HashMap(groups.associateBy(VkGroupDomain::id))
|
||||
}
|
||||
|
||||
fun groups(): List<VkGroupDomain> = map.values.toList()
|
||||
|
||||
fun conversationGroup(conversation: VkConversation): VkGroupDomain? =
|
||||
if (!conversation.peerType.isGroup()) null
|
||||
else map[abs(conversation.id)]
|
||||
|
||||
fun messageActionGroup(message: VkMessage): VkGroupDomain? =
|
||||
if (message.actionMemberId == null || message.actionMemberId!! >= 0) null
|
||||
else map[abs(message.actionMemberId!!)]
|
||||
|
||||
fun messageActionGroup(message: VkMessageData): VkGroupDomain? =
|
||||
if (message.action?.memberId == null || message.action!!.memberId!! >= 0) null
|
||||
else map[abs(message.action!!.memberId!!)]
|
||||
|
||||
fun messageGroup(message: VkMessage): VkGroupDomain? =
|
||||
if (!message.isGroup()) null
|
||||
else map[abs(message.fromId)]
|
||||
|
||||
fun messageGroup(message: VkMessageData): VkGroupDomain? =
|
||||
if (message.fromId >= 0) null
|
||||
else map[abs(message.fromId)]
|
||||
|
||||
fun group(groupId: Int): VkGroupDomain? = map[abs(groupId)]
|
||||
|
||||
companion object {
|
||||
|
||||
fun forGroups(groups: List<VkGroupDomain>): VkGroupsMap = VkGroupsMap(groups = groups)
|
||||
|
||||
fun List<VkGroupDomain>.toGroupsMap(): VkGroupsMap = forGroups(this)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.meloda.app.fast.data
|
||||
|
||||
import com.meloda.app.fast.model.api.domain.VkContactDomain
|
||||
import com.meloda.app.fast.model.api.domain.VkConversation
|
||||
import com.meloda.app.fast.model.api.domain.VkGroupDomain
|
||||
import com.meloda.app.fast.model.api.domain.VkMessage
|
||||
import com.meloda.app.fast.model.api.domain.VkUser
|
||||
import kotlin.math.abs
|
||||
|
||||
object VkMemoryCache {
|
||||
|
||||
private val users: HashMap<Int, VkUser> = hashMapOf()
|
||||
private val groups: HashMap<Int, VkGroupDomain> = hashMapOf()
|
||||
private val messages: HashMap<Int, VkMessage> = hashMapOf()
|
||||
private val conversations: HashMap<Int, VkConversation> = hashMapOf()
|
||||
private val contacts: HashMap<Int, VkContactDomain> = hashMapOf()
|
||||
|
||||
fun appendUsers(users: List<VkUser>) {
|
||||
users.forEach { user -> VkMemoryCache.users[user.id] = user }
|
||||
}
|
||||
|
||||
fun appendGroups(groups: List<VkGroupDomain>) {
|
||||
groups.forEach { group -> VkMemoryCache.groups[abs(group.id)] = group }
|
||||
}
|
||||
|
||||
fun appendMessages(messages: List<VkMessage>) {
|
||||
messages.forEach { message -> VkMemoryCache.messages[message.id] = message }
|
||||
}
|
||||
|
||||
fun appendConversations(conversations: List<VkConversation>) {
|
||||
conversations.forEach { conversation ->
|
||||
VkMemoryCache.conversations[conversation.id] = conversation
|
||||
}
|
||||
}
|
||||
|
||||
fun appendContacts(contacts: List<VkContactDomain>) {
|
||||
contacts.forEach { contact -> VkMemoryCache.contacts[contact.userId] = contact }
|
||||
}
|
||||
|
||||
operator fun set(userId: Int, user: VkUser) {
|
||||
users[userId] = user
|
||||
}
|
||||
|
||||
operator fun set(groupId: Int, group: VkGroupDomain) {
|
||||
groups[groupId] = group
|
||||
}
|
||||
|
||||
operator fun set(messageId: Int, message: VkMessage) {
|
||||
messages[messageId] = message
|
||||
}
|
||||
|
||||
operator fun set(conversationId: Int, conversation: VkConversation) {
|
||||
conversations[conversationId] = conversation
|
||||
}
|
||||
|
||||
operator fun set(contactId: Int, contact: VkContactDomain) {
|
||||
contacts[contactId] = contact
|
||||
}
|
||||
|
||||
fun getUser(id: Int): VkUser? {
|
||||
return getUsers(id).firstOrNull()
|
||||
}
|
||||
|
||||
fun getUsers(vararg ids: Int): List<VkUser> {
|
||||
return getUsers(ids.toList())
|
||||
}
|
||||
|
||||
fun getUsers(ids: List<Int>): List<VkUser> {
|
||||
return ids.mapNotNull { id -> users[id] }
|
||||
}
|
||||
|
||||
fun getGroup(id: Int): VkGroupDomain? {
|
||||
return getGroups(id).firstOrNull()
|
||||
}
|
||||
|
||||
fun getGroups(vararg ids: Int): List<VkGroupDomain> {
|
||||
return getGroups(ids.toList())
|
||||
}
|
||||
|
||||
fun getGroups(ids: List<Int>): List<VkGroupDomain> {
|
||||
return ids.mapNotNull { id -> groups[id] }
|
||||
}
|
||||
|
||||
fun getMessage(id: Int): VkMessage? {
|
||||
return getMessages(id).firstOrNull()
|
||||
}
|
||||
|
||||
fun getMessages(vararg ids: Int): List<VkMessage> {
|
||||
return getMessages(ids.toList())
|
||||
}
|
||||
|
||||
fun getMessages(ids: List<Int>): List<VkMessage> {
|
||||
return ids.mapNotNull { id -> messages[id] }
|
||||
}
|
||||
|
||||
fun getConversation(id: Int): VkConversation? {
|
||||
return getConversations(id).firstOrNull()
|
||||
}
|
||||
|
||||
fun getConversations(vararg ids: Int): List<VkConversation> {
|
||||
return getConversations(ids.toList())
|
||||
}
|
||||
|
||||
fun getConversations(ids: List<Int>): List<VkConversation> {
|
||||
return ids.mapNotNull { id -> conversations[id] }
|
||||
}
|
||||
|
||||
fun getContact(id: Int): VkContactDomain? {
|
||||
return getContacts(id).firstOrNull()
|
||||
}
|
||||
|
||||
fun getContacts(vararg ids: Int): List<VkContactDomain> {
|
||||
return getContacts(ids.toList())
|
||||
}
|
||||
|
||||
fun getContacts(ids: List<Int>): List<VkContactDomain> {
|
||||
return ids.mapNotNull { id -> contacts[id] }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.meloda.app.fast.data
|
||||
|
||||
import com.meloda.app.fast.model.api.data.VkMessageData
|
||||
import com.meloda.app.fast.model.api.domain.VkConversation
|
||||
import com.meloda.app.fast.model.api.domain.VkMessage
|
||||
import com.meloda.app.fast.model.api.domain.VkUser
|
||||
|
||||
class VkUsersMap(
|
||||
private val users: List<VkUser>
|
||||
) {
|
||||
|
||||
private val map: HashMap<Int, VkUser> by lazy {
|
||||
HashMap(users.associateBy(VkUser::id))
|
||||
}
|
||||
|
||||
fun users(): List<VkUser> = map.values.toList()
|
||||
|
||||
fun conversationUser(conversation: VkConversation): VkUser? =
|
||||
if (!conversation.peerType.isUser()) null
|
||||
else map[conversation.id]
|
||||
|
||||
fun messageActionUser(message: VkMessage): VkUser? =
|
||||
if (message.actionMemberId == null || message.actionMemberId!! <= 0) null
|
||||
else map[message.actionMemberId]
|
||||
|
||||
fun messageActionUser(message: VkMessageData): VkUser? =
|
||||
if (message.action?.memberId == null || message.action!!.memberId!! <= 0) null
|
||||
else map[message.action!!.memberId]
|
||||
|
||||
fun messageUser(message: VkMessage): VkUser? =
|
||||
if (!message.isUser()) null
|
||||
else map[message.fromId]
|
||||
|
||||
fun messageUser(message: VkMessageData): VkUser? =
|
||||
if (message.fromId > 0) map[message.fromId]
|
||||
else null
|
||||
|
||||
fun user(userId: Int): VkUser? = map[userId]
|
||||
|
||||
companion object {
|
||||
|
||||
fun forUsers(users: List<VkUser>): VkUsersMap = VkUsersMap(users = users)
|
||||
|
||||
fun List<VkUser>.toUsersMap(): VkUsersMap = forUsers(this)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.meloda.app.fast.data.api.account
|
||||
|
||||
import com.meloda.app.fast.model.api.requests.AccountSetOfflineRequest
|
||||
import com.meloda.app.fast.model.api.requests.AccountSetOnlineRequest
|
||||
|
||||
interface AccountRepository {
|
||||
|
||||
suspend fun setOnline(
|
||||
params: AccountSetOnlineRequest
|
||||
): Boolean
|
||||
|
||||
suspend fun setOffline(
|
||||
params: AccountSetOfflineRequest
|
||||
): Boolean
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.meloda.app.fast.data.api.account
|
||||
|
||||
import com.meloda.app.fast.model.api.requests.AccountSetOfflineRequest
|
||||
import com.meloda.app.fast.model.api.requests.AccountSetOnlineRequest
|
||||
import com.meloda.app.fast.network.service.account.AccountService
|
||||
|
||||
// TODO: 05/05/2024, Danil Nikolaev: implement
|
||||
class AccountRepositoryImpl(
|
||||
private val accountService: AccountService
|
||||
) : com.meloda.app.fast.data.api.account.AccountRepository {
|
||||
|
||||
override suspend fun setOnline(params: AccountSetOnlineRequest): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override suspend fun setOffline(params: AccountSetOfflineRequest): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.meloda.app.fast.data.api.account
|
||||
|
||||
import com.meloda.app.fast.data.State
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AccountUseCase {
|
||||
|
||||
suspend fun setOnline(
|
||||
voip: Boolean,
|
||||
accessToken: String
|
||||
): Flow<State<Unit>>
|
||||
|
||||
suspend fun setOffline(
|
||||
accessToken: String
|
||||
): Flow<State<Unit>>
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.meloda.app.fast.data.api.account
|
||||
|
||||
import com.meloda.app.fast.data.State
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
// TODO: 05/05/2024, Danil Nikolaev: implement
|
||||
class AccountUseCaseImpl(
|
||||
private val accountRepository: com.meloda.app.fast.data.api.account.AccountRepository
|
||||
) : com.meloda.app.fast.data.api.account.AccountUseCase {
|
||||
|
||||
override suspend fun setOnline(
|
||||
voip: Boolean,
|
||||
accessToken: String
|
||||
): Flow<State<Unit>> = flow {
|
||||
// emit(com.meloda.app.fast.data.State.Loading)
|
||||
//
|
||||
// val newState = accountRepository.setOnline(
|
||||
// params = AccountSetOnlineRequest(
|
||||
// voip = voip,
|
||||
// accessToken = accessToken
|
||||
// )
|
||||
// ).fold(
|
||||
// onSuccess = { response -> com.meloda.app.fast.data.State.Success(response) },
|
||||
// onNetworkFailure = { com.meloda.app.fast.data.State.Error.ConnectionError },
|
||||
// onUnknownFailure = { com.meloda.app.fast.data.State.UNKNOWN_ERROR },
|
||||
// onHttpFailure = { result -> result.error.toStateApiError() },
|
||||
// onApiFailure = { result -> result.error.toStateApiError() }
|
||||
// )
|
||||
// emit(newState)
|
||||
}
|
||||
|
||||
override suspend fun setOffline(
|
||||
accessToken: String
|
||||
): Flow<com.meloda.app.fast.data.State<Unit>> = flow {
|
||||
emit(com.meloda.app.fast.data.State.Loading)
|
||||
|
||||
// val newState = accountRepository.setOffline(
|
||||
// params = AccountSetOfflineRequest(accessToken = accessToken)
|
||||
// ).fold(
|
||||
// onSuccess = { response -> com.meloda.app.fast.data.State.Success(response) },
|
||||
// onNetworkFailure = { com.meloda.app.fast.data.State.Error.ConnectionError },
|
||||
// onUnknownFailure = { com.meloda.app.fast.data.State.UNKNOWN_ERROR },
|
||||
// onHttpFailure = { result -> result.error.toStateApiError() },
|
||||
// onApiFailure = { result -> result.error.toStateApiError() }
|
||||
// )
|
||||
// emit(newState)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.meloda.app.fast.data.api.audios
|
||||
|
||||
import com.meloda.app.fast.model.api.responses.AudiosGetUploadServerResponse
|
||||
import com.meloda.app.fast.network.ApiResponse
|
||||
import com.meloda.app.fast.network.RestApiError
|
||||
import com.meloda.app.fast.network.service.audios.AudiosService
|
||||
import com.slack.eithernet.ApiResult
|
||||
import okhttp3.MultipartBody
|
||||
|
||||
class AudiosRepository(
|
||||
private val audiosService: AudiosService
|
||||
) {
|
||||
|
||||
suspend fun getUploadServer(): ApiResult<ApiResponse<AudiosGetUploadServerResponse>, RestApiError> =
|
||||
audiosService.getUploadServer()
|
||||
|
||||
suspend fun upload(url: String, file: MultipartBody.Part) = audiosService.upload(url, file)
|
||||
|
||||
suspend fun save(server: Int, audio: String, hash: String) = audiosService.save(
|
||||
mapOf(
|
||||
"server" to server.toString(),
|
||||
"audio" to audio,
|
||||
"hash" to hash
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.meloda.app.fast.data.api.auth
|
||||
|
||||
import com.meloda.app.fast.model.api.requests.AuthDirectRequest
|
||||
import com.meloda.app.fast.model.api.responses.AuthDirectResponse
|
||||
import com.meloda.app.fast.model.api.responses.SendSmsResponse
|
||||
import com.meloda.app.fast.network.OAuthErrorDomain
|
||||
import com.slack.eithernet.ApiResult
|
||||
|
||||
interface AuthRepository {
|
||||
|
||||
// suspend fun auth(
|
||||
// params: AuthDirectRequest
|
||||
// ): ApiResult<AuthDirectResponse, OAuthErrorDomain>
|
||||
|
||||
suspend fun sendSms(
|
||||
validationSid: String
|
||||
): SendSmsResponse
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.meloda.app.fast.data.api.auth
|
||||
|
||||
import com.meloda.app.fast.model.api.requests.AuthDirectRequest
|
||||
import com.meloda.app.fast.model.api.responses.AuthDirectResponse
|
||||
import com.meloda.app.fast.model.api.responses.SendSmsResponse
|
||||
import com.meloda.app.fast.network.OAuthErrorDomain
|
||||
import com.meloda.app.fast.network.service.auth.AuthService
|
||||
import com.slack.eithernet.ApiResult
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class AuthRepositoryImpl(
|
||||
private val authService: AuthService
|
||||
) : AuthRepository {
|
||||
|
||||
// override suspend fun auth(
|
||||
// params: AuthDirectRequest
|
||||
// ): ApiResult<AuthDirectResponse, OAuthErrorDomain> {
|
||||
//
|
||||
// }
|
||||
|
||||
// TODO: 05/05/2024, Danil Nikolaev: implement
|
||||
override suspend fun sendSms(
|
||||
validationSid: String
|
||||
): SendSmsResponse = withContext(Dispatchers.IO) {
|
||||
SendSmsResponse(
|
||||
validationSid = null, delay = null, validationType = null, validationResend = null
|
||||
|
||||
)
|
||||
// authService.sendSms(validationSid).mapResult(
|
||||
// successMapper = { response -> response.requireResponse() },
|
||||
// errorMapper = { error -> error?.toDomain() }
|
||||
// )
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.meloda.app.fast.data.api.conversations
|
||||
|
||||
import com.meloda.app.fast.model.api.domain.VkConversation
|
||||
import com.meloda.app.fast.network.RestApiErrorDomain
|
||||
import com.slack.eithernet.ApiResult
|
||||
|
||||
interface ConversationsRepository {
|
||||
|
||||
suspend fun getConversations(
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): ApiResult<List<VkConversation>, RestApiErrorDomain>
|
||||
|
||||
suspend fun storeConversations(conversations: List<VkConversation>)
|
||||
suspend fun delete(peerId: Int): ApiResult<Int, RestApiErrorDomain>
|
||||
suspend fun pin(peerId: Int): ApiResult<Int, RestApiErrorDomain>
|
||||
suspend fun unpin(peerId: Int): ApiResult<Int, RestApiErrorDomain>
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
package com.meloda.app.fast.data.api.conversations
|
||||
|
||||
import com.meloda.app.fast.common.VkConstants
|
||||
import com.meloda.app.fast.data.VkGroupsMap
|
||||
import com.meloda.app.fast.data.VkMemoryCache
|
||||
import com.meloda.app.fast.data.VkUsersMap
|
||||
import com.meloda.app.fast.database.dao.ConversationDao
|
||||
import com.meloda.app.fast.model.api.data.VkContactData
|
||||
import com.meloda.app.fast.model.api.data.VkGroupData
|
||||
import com.meloda.app.fast.model.api.data.VkUserData
|
||||
import com.meloda.app.fast.model.api.data.asDomain
|
||||
import com.meloda.app.fast.model.api.domain.VkConversation
|
||||
import com.meloda.app.fast.model.api.domain.asEntity
|
||||
import com.meloda.app.fast.model.api.requests.ConversationsDeleteRequest
|
||||
import com.meloda.app.fast.model.api.requests.ConversationsGetRequest
|
||||
import com.meloda.app.fast.model.api.requests.ConversationsPinRequest
|
||||
import com.meloda.app.fast.model.api.requests.ConversationsUnpinRequest
|
||||
import com.meloda.app.fast.network.RestApiErrorDomain
|
||||
import com.meloda.app.fast.network.mapApiDefault
|
||||
import com.meloda.app.fast.network.mapApiResult
|
||||
import com.meloda.app.fast.network.service.conversations.ConversationsService
|
||||
import com.slack.eithernet.ApiResult
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class ConversationsRepositoryImpl(
|
||||
private val conversationsService: ConversationsService,
|
||||
private val conversationDao: ConversationDao
|
||||
) : ConversationsRepository {
|
||||
|
||||
override suspend fun getConversations(
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): ApiResult<List<VkConversation>, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
val requestModel = ConversationsGetRequest(
|
||||
count = count,
|
||||
offset = offset,
|
||||
fields = VkConstants.ALL_FIELDS,
|
||||
filter = "all",
|
||||
extended = true,
|
||||
startMessageId = null
|
||||
)
|
||||
|
||||
conversationsService.getConversations(requestModel.map).mapApiResult(
|
||||
successMapper = { apiResponse ->
|
||||
val response = apiResponse.requireResponse()
|
||||
|
||||
val profilesList = response.profiles.orEmpty().map(VkUserData::mapToDomain)
|
||||
val groupsList = response.groups.orEmpty().map(VkGroupData::mapToDomain)
|
||||
val contactsList = response.contacts.orEmpty().map(VkContactData::mapToDomain)
|
||||
|
||||
val usersMap = VkUsersMap.forUsers(profilesList)
|
||||
val groupsMap = VkGroupsMap.forGroups(groupsList)
|
||||
|
||||
VkMemoryCache.appendUsers(profilesList)
|
||||
VkMemoryCache.appendGroups(groupsList)
|
||||
VkMemoryCache.appendContacts(contactsList)
|
||||
|
||||
response.items.map { item ->
|
||||
val lastMessage = item.lastMessage?.asDomain()?.let { message ->
|
||||
message.copy(
|
||||
user = usersMap.messageUser(message),
|
||||
group = groupsMap.messageGroup(message),
|
||||
actionUser = usersMap.messageActionUser(message),
|
||||
actionGroup = groupsMap.messageActionGroup(message)
|
||||
).also { VkMemoryCache[message.id] = it }
|
||||
}
|
||||
item.conversation.asDomain(lastMessage).let { conversation ->
|
||||
conversation.copy(
|
||||
user = usersMap.conversationUser(conversation),
|
||||
group = groupsMap.conversationGroup(conversation)
|
||||
).also { VkMemoryCache[conversation.id] = it }
|
||||
}
|
||||
}
|
||||
},
|
||||
errorMapper = { error ->
|
||||
error?.toDomain()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun storeConversations(conversations: List<VkConversation>) {
|
||||
conversationDao.insertAll(conversations.map(VkConversation::asEntity))
|
||||
}
|
||||
|
||||
override suspend fun delete(peerId: Int): ApiResult<Int, RestApiErrorDomain> =
|
||||
withContext(Dispatchers.IO) {
|
||||
val requestModel = ConversationsDeleteRequest(peerId = peerId)
|
||||
|
||||
conversationsService.delete(requestModel.map).mapApiResult(
|
||||
successMapper = { response -> response.requireResponse().lastDeletedId },
|
||||
errorMapper = { error -> error?.toDomain() }
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun pin(
|
||||
peerId: Int
|
||||
): ApiResult<Int, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
val requestModel = ConversationsPinRequest(peerId = peerId)
|
||||
conversationsService.pin(requestModel.map).mapApiDefault()
|
||||
}
|
||||
|
||||
override suspend fun unpin(
|
||||
peerId: Int
|
||||
): ApiResult<Int, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
val requestModel = ConversationsUnpinRequest(peerId = peerId)
|
||||
conversationsService.unpin(requestModel.map).mapApiDefault()
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.meloda.app.fast.data.api.conversations
|
||||
|
||||
import com.meloda.app.fast.data.State
|
||||
import com.meloda.app.fast.model.api.domain.VkConversation
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface ConversationsUseCase {
|
||||
|
||||
fun getConversations(
|
||||
count: Int?,
|
||||
offset: Int?,
|
||||
): Flow<State<List<VkConversation>>>
|
||||
|
||||
fun delete(peerId: Int): Flow<State<Int>>
|
||||
|
||||
fun changePinState(peerId: Int, pin: Boolean): Flow<State<Int>>
|
||||
|
||||
suspend fun storeConversations(conversations: List<VkConversation>)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.meloda.app.fast.data.api.files
|
||||
|
||||
import com.meloda.app.fast.network.service.files.FilesService
|
||||
import okhttp3.MultipartBody
|
||||
|
||||
class FilesRepository(
|
||||
private val filesService: FilesService
|
||||
) {
|
||||
|
||||
// TODO: 05/05/2024, Danil Nikolaev: reimplement
|
||||
// enum class FileType(val value: String) {
|
||||
// @Json(name = "doc")
|
||||
// FILE("doc"),
|
||||
//
|
||||
// @Json(name = "audio_message")
|
||||
// AUDIO_MESSAGE("audio_message")
|
||||
// }
|
||||
//
|
||||
// suspend fun getMessagesUploadServer(peerId: Int, type: FileType) =
|
||||
// filesService.getUploadServer(
|
||||
// mapOf(
|
||||
// "peer_id" to peerId.toString(),
|
||||
// "type" to type.value
|
||||
// )
|
||||
// )
|
||||
|
||||
suspend fun uploadFile(url: String, file: MultipartBody.Part) = filesService.upload(url, file)
|
||||
|
||||
suspend fun saveMessageFile(file: String) = filesService.save(mapOf("file" to file))
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.meloda.app.fast.data.api.friends
|
||||
|
||||
import com.meloda.app.fast.model.FriendsInfo
|
||||
import com.meloda.app.fast.model.api.domain.VkUser
|
||||
import com.meloda.app.fast.network.RestApiErrorDomain
|
||||
import com.slack.eithernet.ApiResult
|
||||
|
||||
interface FriendsRepository {
|
||||
|
||||
suspend fun getAllFriends(
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): ApiResult<FriendsInfo, RestApiErrorDomain>
|
||||
|
||||
suspend fun getFriends(
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): ApiResult<List<VkUser>, RestApiErrorDomain>
|
||||
|
||||
suspend fun getOnlineFriends(
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): ApiResult<List<Int>, RestApiErrorDomain>
|
||||
|
||||
suspend fun storeUsers(users: List<VkUser>)
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package com.meloda.app.fast.data.api.friends
|
||||
|
||||
import com.meloda.app.fast.common.VkConstants
|
||||
import com.meloda.app.fast.data.VkMemoryCache
|
||||
import com.meloda.app.fast.database.dao.UsersDao
|
||||
import com.meloda.app.fast.model.FriendsInfo
|
||||
import com.meloda.app.fast.model.api.data.VkUserData
|
||||
import com.meloda.app.fast.model.api.domain.VkUser
|
||||
import com.meloda.app.fast.model.api.domain.asEntity
|
||||
import com.meloda.app.fast.model.api.requests.GetFriendsRequest
|
||||
import com.meloda.app.fast.model.api.requests.GetOnlineFriendsRequest
|
||||
import com.meloda.app.fast.network.RestApiErrorDomain
|
||||
import com.meloda.app.fast.network.mapApiDefault
|
||||
import com.meloda.app.fast.network.mapApiResult
|
||||
import com.meloda.app.fast.network.service.friends.FriendsService
|
||||
import com.slack.eithernet.ApiResult
|
||||
import com.slack.eithernet.successOrElse
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class FriendsRepositoryImpl(
|
||||
private val service: FriendsService,
|
||||
private val dao: UsersDao
|
||||
) : FriendsRepository {
|
||||
|
||||
override suspend fun getAllFriends(
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): ApiResult<FriendsInfo, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
val friends = async { getFriends(count, offset) }.await()
|
||||
.successOrElse { failure ->
|
||||
return@withContext failure
|
||||
}
|
||||
|
||||
val onlineFriends = async { getOnlineFriends(count, offset) }.await()
|
||||
.successOrElse { failure ->
|
||||
return@withContext failure
|
||||
}.mapNotNull { userId -> friends.find { it.id == userId } }
|
||||
|
||||
ApiResult.success(FriendsInfo(friends, onlineFriends))
|
||||
}
|
||||
|
||||
override suspend fun getFriends(
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): ApiResult<List<VkUser>, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
val requestModel = GetFriendsRequest(
|
||||
order = "hints",
|
||||
count = count,
|
||||
offset = offset,
|
||||
fields = VkConstants.USER_FIELDS
|
||||
)
|
||||
service.getFriends(requestModel.map).mapApiResult(
|
||||
successMapper = { apiResponse ->
|
||||
val response = apiResponse.requireResponse()
|
||||
val users = response.items.map(VkUserData::mapToDomain)
|
||||
|
||||
VkMemoryCache.appendUsers(users)
|
||||
|
||||
users
|
||||
},
|
||||
errorMapper = { error -> error?.toDomain() }
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getOnlineFriends(
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): ApiResult<List<Int>, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
val requestModel = GetOnlineFriendsRequest(
|
||||
order = "hints",
|
||||
count = count,
|
||||
offset = offset,
|
||||
)
|
||||
|
||||
service.getOnlineFriends(requestModel.map).mapApiDefault()
|
||||
}
|
||||
|
||||
override suspend fun storeUsers(users: List<VkUser>) = withContext(Dispatchers.IO) {
|
||||
dao.insertAll(users.map(VkUser::asEntity))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.meloda.app.fast.data.api.friends
|
||||
|
||||
import com.meloda.app.fast.data.State
|
||||
import com.meloda.app.fast.model.FriendsInfo
|
||||
import com.meloda.app.fast.model.api.domain.VkUser
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface FriendsUseCase {
|
||||
|
||||
fun getAllFriends(
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): Flow<State<FriendsInfo>>
|
||||
|
||||
fun getFriends(
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): Flow<State<List<VkUser>>>
|
||||
|
||||
fun getOnlineFriends(
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): Flow<State<List<Int>>>
|
||||
|
||||
suspend fun storeUsers(users: List<VkUser>)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.meloda.app.fast.data.api.longpoll
|
||||
|
||||
import com.meloda.app.fast.model.api.data.LongPollUpdates
|
||||
import com.meloda.app.fast.model.api.data.VkLongPollData
|
||||
import com.meloda.app.fast.network.RestApiErrorDomain
|
||||
import com.slack.eithernet.ApiResult
|
||||
|
||||
interface LongPollRepository {
|
||||
|
||||
suspend fun getLongPollServer(
|
||||
needPts: Boolean,
|
||||
version: Int
|
||||
): ApiResult<VkLongPollData, RestApiErrorDomain>
|
||||
|
||||
suspend fun getLongPollUpdates(
|
||||
serverUrl: String,
|
||||
act: String,
|
||||
key: String,
|
||||
ts: Int,
|
||||
wait: Int,
|
||||
mode: Int,
|
||||
version: Int
|
||||
): ApiResult<LongPollUpdates, RestApiErrorDomain>
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package com.meloda.app.fast.data.api.longpoll
|
||||
|
||||
import com.meloda.app.fast.model.api.data.LongPollUpdates
|
||||
import com.meloda.app.fast.model.api.data.VkLongPollData
|
||||
import com.meloda.app.fast.model.api.requests.LongPollGetUpdatesRequest
|
||||
import com.meloda.app.fast.model.api.requests.MessagesGetLongPollServerRequest
|
||||
import com.meloda.app.fast.network.RestApiErrorDomain
|
||||
import com.meloda.app.fast.network.mapApiResult
|
||||
import com.meloda.app.fast.network.mapResult
|
||||
import com.meloda.app.fast.network.service.longpoll.LongPollService
|
||||
import com.meloda.app.fast.network.service.messages.MessagesService
|
||||
import com.slack.eithernet.ApiResult
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class LongPollRepositoryImpl(
|
||||
private val longPollService: LongPollService,
|
||||
private val messagesService: MessagesService
|
||||
) : LongPollRepository {
|
||||
|
||||
override suspend fun getLongPollServer(
|
||||
needPts: Boolean,
|
||||
version: Int
|
||||
): ApiResult<VkLongPollData, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
val requestModel = MessagesGetLongPollServerRequest(
|
||||
needPts = needPts,
|
||||
version = version
|
||||
)
|
||||
messagesService.getLongPollServer(requestModel.map).mapApiResult(
|
||||
successMapper = { response -> response.requireResponse() },
|
||||
errorMapper = { error -> error?.toDomain() }
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getLongPollUpdates(
|
||||
serverUrl: String,
|
||||
act: String,
|
||||
key: String,
|
||||
ts: Int,
|
||||
wait: Int,
|
||||
mode: Int,
|
||||
version: Int
|
||||
): ApiResult<LongPollUpdates, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
val requestModel = LongPollGetUpdatesRequest(
|
||||
act = act,
|
||||
key = key,
|
||||
ts = ts,
|
||||
wait = wait,
|
||||
mode = mode,
|
||||
version = version
|
||||
)
|
||||
longPollService.getResponse(serverUrl, requestModel.map).mapResult(
|
||||
successMapper = { response -> response },
|
||||
errorMapper = { error -> error?.toDomain() }
|
||||
)
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.meloda.app.fast.data.api.messages
|
||||
|
||||
import com.meloda.app.fast.model.database.VkMessageEntity
|
||||
|
||||
interface MessagesLocalDataSource {
|
||||
|
||||
suspend fun getMessages(
|
||||
conversationId: Int,
|
||||
offset: Int?,
|
||||
count: Int?
|
||||
): List<VkMessageEntity>
|
||||
|
||||
suspend fun getMessage(messageId: Int): VkMessageEntity?
|
||||
|
||||
suspend fun storeMessages(messages: List<VkMessageEntity>)
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.meloda.app.fast.data.api.messages
|
||||
|
||||
import com.meloda.app.fast.database.dao.MessageDao
|
||||
import com.meloda.app.fast.model.database.VkMessageEntity
|
||||
|
||||
// TODO: 05/05/2024, Danil Nikolaev: use paging for room
|
||||
class MessagesLocalDataSourceImpl(
|
||||
private val messageDao: MessageDao
|
||||
) : MessagesLocalDataSource {
|
||||
|
||||
override suspend fun getMessages(
|
||||
conversationId: Int,
|
||||
offset: Int?,
|
||||
count: Int?
|
||||
): List<VkMessageEntity> = messageDao.getAll(conversationId)
|
||||
|
||||
override suspend fun getMessage(
|
||||
messageId: Int
|
||||
): VkMessageEntity? = messageDao.getById(messageId)
|
||||
|
||||
override suspend fun storeMessages(messages: List<VkMessageEntity>) {
|
||||
messageDao.insertAll(messages)
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.meloda.app.fast.data.api.messages
|
||||
|
||||
import com.meloda.app.fast.model.api.domain.VkAttachment
|
||||
import com.meloda.app.fast.model.api.domain.VkMessage
|
||||
import com.meloda.app.fast.network.RestApiErrorDomain
|
||||
import com.slack.eithernet.ApiResult
|
||||
|
||||
interface MessagesNetworkDataSource {
|
||||
|
||||
suspend fun getMessagesHistory(
|
||||
conversationId: Int,
|
||||
offset: Int?,
|
||||
count: Int?,
|
||||
): ApiResult<MessagesHistoryDomain, RestApiErrorDomain>
|
||||
|
||||
suspend fun getMessageById(
|
||||
messagesIds: List<Int>,
|
||||
extended: Boolean?,
|
||||
fields: String?
|
||||
): ApiResult<VkMessage, RestApiErrorDomain>
|
||||
|
||||
suspend fun send(
|
||||
peerId: Int,
|
||||
randomId: Int,
|
||||
message: String?,
|
||||
replyTo: Int?,
|
||||
attachments: List<VkAttachment>?
|
||||
): ApiResult<Int, RestApiErrorDomain>
|
||||
|
||||
suspend fun markAsRead(
|
||||
peerId: Int,
|
||||
startMessageId: Int?
|
||||
): ApiResult<Int, RestApiErrorDomain>
|
||||
|
||||
suspend fun getMessage(messageId: Int): VkMessage?
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
package com.meloda.app.fast.data.api.messages
|
||||
|
||||
import com.meloda.app.fast.common.VkConstants
|
||||
import com.meloda.app.fast.data.VkGroupsMap
|
||||
import com.meloda.app.fast.data.VkMemoryCache
|
||||
import com.meloda.app.fast.data.VkUsersMap
|
||||
import com.meloda.app.fast.model.api.data.VkContactData
|
||||
import com.meloda.app.fast.model.api.data.VkGroupData
|
||||
import com.meloda.app.fast.model.api.data.VkUserData
|
||||
import com.meloda.app.fast.model.api.data.asDomain
|
||||
import com.meloda.app.fast.model.api.domain.VkAttachment
|
||||
import com.meloda.app.fast.model.api.domain.VkConversation
|
||||
import com.meloda.app.fast.model.api.domain.VkMessage
|
||||
import com.meloda.app.fast.model.api.requests.MessagesGetByIdRequest
|
||||
import com.meloda.app.fast.model.api.requests.MessagesGetHistoryRequest
|
||||
import com.meloda.app.fast.model.api.requests.MessagesMarkAsReadRequest
|
||||
import com.meloda.app.fast.model.api.requests.MessagesSendRequest
|
||||
import com.meloda.app.fast.network.RestApiErrorDomain
|
||||
import com.meloda.app.fast.network.mapApiDefault
|
||||
import com.meloda.app.fast.network.mapApiResult
|
||||
import com.meloda.app.fast.network.service.messages.MessagesService
|
||||
import com.slack.eithernet.ApiResult
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class MessagesNetworkDataSourceImpl(
|
||||
private val messagesService: MessagesService
|
||||
) : MessagesNetworkDataSource {
|
||||
|
||||
override suspend fun getMessagesHistory(
|
||||
conversationId: Int,
|
||||
offset: Int?,
|
||||
count: Int?
|
||||
): ApiResult<MessagesHistoryDomain, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
val requestModel = MessagesGetHistoryRequest(
|
||||
count = count,
|
||||
offset = offset,
|
||||
peerId = conversationId,
|
||||
extended = true,
|
||||
startMessageId = null,
|
||||
rev = null,
|
||||
fields = VkConstants.ALL_FIELDS
|
||||
)
|
||||
|
||||
messagesService.getHistory(requestModel.map).mapApiResult(
|
||||
successMapper = { apiResponse ->
|
||||
val response = apiResponse.requireResponse()
|
||||
|
||||
val profilesList = response.profiles.orEmpty().map(VkUserData::mapToDomain)
|
||||
val groupsList = response.groups.orEmpty().map(VkGroupData::mapToDomain)
|
||||
val contactsList = response.contacts.orEmpty().map(VkContactData::mapToDomain)
|
||||
|
||||
val usersMap = VkUsersMap.forUsers(profilesList)
|
||||
val groupsMap = VkGroupsMap.forGroups(groupsList)
|
||||
|
||||
VkMemoryCache.appendUsers(profilesList)
|
||||
VkMemoryCache.appendGroups(groupsList)
|
||||
VkMemoryCache.appendContacts(contactsList)
|
||||
|
||||
val messages = response.items.map { item ->
|
||||
item.asDomain().let { message ->
|
||||
message.copy(
|
||||
user = usersMap.messageUser(message),
|
||||
group = groupsMap.messageGroup(message),
|
||||
actionUser = usersMap.messageActionUser(message),
|
||||
actionGroup = groupsMap.messageActionGroup(message)
|
||||
).also { VkMemoryCache[message.id] = it }
|
||||
}
|
||||
}
|
||||
|
||||
val conversations = response.conversations.orEmpty().map { item ->
|
||||
val message = messages.firstOrNull { it.id == item.lastMessageId }
|
||||
item.asDomain(message)
|
||||
.let { conversation ->
|
||||
conversation.copy(
|
||||
user = usersMap.conversationUser(conversation),
|
||||
group = groupsMap.conversationGroup(conversation)
|
||||
).also { VkMemoryCache[conversation.id] = it }
|
||||
}
|
||||
}
|
||||
|
||||
MessagesHistoryDomain(
|
||||
messages = messages,
|
||||
conversations = conversations
|
||||
)
|
||||
},
|
||||
errorMapper = { error ->
|
||||
error?.toDomain()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getMessageById(
|
||||
messagesIds: List<Int>,
|
||||
extended: Boolean?,
|
||||
fields: String?
|
||||
): ApiResult<VkMessage, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
val requestModel = MessagesGetByIdRequest(
|
||||
messagesIds = messagesIds,
|
||||
extended = extended,
|
||||
fields = fields
|
||||
)
|
||||
|
||||
messagesService.getById(requestModel.map).mapApiResult(
|
||||
successMapper = { apiResponse ->
|
||||
val response = apiResponse.requireResponse()
|
||||
|
||||
val message = response.items.single()
|
||||
val usersMap =
|
||||
VkUsersMap.forUsers(response.profiles.orEmpty().map(VkUserData::mapToDomain))
|
||||
val groupsMap =
|
||||
VkGroupsMap.forGroups(response.groups.orEmpty().map(VkGroupData::mapToDomain))
|
||||
|
||||
message.asDomain().copy(
|
||||
user = usersMap.messageUser(message),
|
||||
group = groupsMap.messageGroup(message),
|
||||
actionUser = usersMap.messageActionUser(message),
|
||||
actionGroup = groupsMap.messageActionGroup(message)
|
||||
)
|
||||
},
|
||||
errorMapper = { error -> error?.toDomain() }
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun send(
|
||||
peerId: Int,
|
||||
randomId: Int,
|
||||
message: String?,
|
||||
replyTo: Int?,
|
||||
attachments: List<VkAttachment>?
|
||||
): ApiResult<Int, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
val requestModel = MessagesSendRequest(
|
||||
peerId = peerId,
|
||||
randomId = randomId,
|
||||
message = message,
|
||||
replyTo = replyTo,
|
||||
attachments = attachments
|
||||
)
|
||||
|
||||
messagesService.send(requestModel.map).mapApiDefault()
|
||||
}
|
||||
|
||||
override suspend fun markAsRead(
|
||||
peerId: Int,
|
||||
startMessageId: Int?
|
||||
): ApiResult<Int, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
val requestModel = MessagesMarkAsReadRequest(
|
||||
peerId = peerId,
|
||||
startMessageId = startMessageId
|
||||
)
|
||||
|
||||
messagesService.markAsRead(requestModel.map).mapApiDefault()
|
||||
}
|
||||
|
||||
override suspend fun getMessage(messageId: Int): VkMessage? = withContext(Dispatchers.IO) {
|
||||
// TODO: 05/05/2024, Danil Nikolaev: get message
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
data class MessagesHistoryDomain(
|
||||
val messages: List<VkMessage>,
|
||||
val conversations: List<VkConversation>
|
||||
)
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.meloda.app.fast.data.api.messages
|
||||
|
||||
import com.meloda.app.fast.model.api.domain.VkAttachment
|
||||
import com.meloda.app.fast.model.api.domain.VkMessage
|
||||
import com.meloda.app.fast.network.RestApiErrorDomain
|
||||
import com.slack.eithernet.ApiResult
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface MessagesRepository {
|
||||
|
||||
suspend fun getMessagesHistory(
|
||||
conversationId: Int,
|
||||
offset: Int?,
|
||||
count: Int?
|
||||
): ApiResult<MessagesHistoryDomain, RestApiErrorDomain>
|
||||
|
||||
suspend fun getMessageById(
|
||||
messagesIds: List<Int>,
|
||||
extended: Boolean?,
|
||||
fields: String?
|
||||
): ApiResult<VkMessage, RestApiErrorDomain>
|
||||
|
||||
suspend fun send(
|
||||
peerId: Int,
|
||||
randomId: Int,
|
||||
message: String?,
|
||||
replyTo: Int?,
|
||||
attachments: List<VkAttachment>?
|
||||
): ApiResult<Int, RestApiErrorDomain>
|
||||
|
||||
suspend fun markAsRead(
|
||||
peerId: Int,
|
||||
startMessageId: Int?
|
||||
): ApiResult<Int, RestApiErrorDomain>
|
||||
|
||||
suspend fun getMessage(messageId: Int): Flow<VkMessage?>
|
||||
|
||||
suspend fun storeMessages(messages: List<VkMessage>)
|
||||
|
||||
// suspend fun getHistory(
|
||||
// params: MessagesGetHistoryRequest
|
||||
// ): ApiResult<MessagesGetHistoryResponse, RestApiErrorDomain>
|
||||
|
||||
// suspend fun markAsImportant(
|
||||
// params: MessagesMarkAsImportantRequest
|
||||
// ): ApiResult<List<Int>, RestApiErrorDomain>
|
||||
//
|
||||
// suspend fun pin(
|
||||
// params: MessagesPinMessageRequest
|
||||
// ): ApiResult<VkMessageData, RestApiErrorDomain>
|
||||
//
|
||||
// suspend fun unpin(
|
||||
// params: MessagesUnPinMessageRequest
|
||||
// ): ApiResult<Unit, RestApiErrorDomain>
|
||||
//
|
||||
// suspend fun delete(
|
||||
// params: MessagesDeleteRequest
|
||||
// ): ApiResult<Unit, RestApiErrorDomain>
|
||||
//
|
||||
// suspend fun edit(
|
||||
// params: MessagesEditRequest
|
||||
// ): ApiResult<Int, RestApiErrorDomain>
|
||||
//
|
||||
// suspend fun getChat(
|
||||
// params: MessagesGetChatRequest
|
||||
// ): ApiResult<VkChatData, RestApiErrorDomain>
|
||||
//
|
||||
// suspend fun getConversationMembers(
|
||||
// params: MessagesGetConversationMembersRequest
|
||||
// ): ApiResult<MessagesGetConversationMembersResponse, RestApiErrorDomain>
|
||||
//
|
||||
// suspend fun removeChatUser(
|
||||
// params: MessagesRemoveChatUserRequest
|
||||
// ): ApiResult<Int, RestApiErrorDomain>
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
package com.meloda.app.fast.data.api.messages
|
||||
|
||||
import com.meloda.app.fast.model.api.domain.VkAttachment
|
||||
import com.meloda.app.fast.model.api.domain.VkMessage
|
||||
import com.meloda.app.fast.model.api.domain.asEntity
|
||||
import com.meloda.app.fast.model.database.asExternalModel
|
||||
import com.meloda.app.fast.network.RestApiErrorDomain
|
||||
import com.slack.eithernet.ApiResult
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
// TODO: 05/05/2024, Danil Nikolaev: implement syncing
|
||||
class MessagesRepositoryImpl(
|
||||
private val networkDataSource: MessagesNetworkDataSource,
|
||||
private val localDataSource: MessagesLocalDataSource
|
||||
) : MessagesRepository {
|
||||
|
||||
override suspend fun getMessagesHistory(
|
||||
conversationId: Int,
|
||||
offset: Int?,
|
||||
count: Int?
|
||||
): ApiResult<MessagesHistoryDomain, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
// val localMessages = localDataSource.getMessages(
|
||||
// conversationId = conversationId,
|
||||
// offset = offset,
|
||||
// count = count
|
||||
// ).map(VkMessageEntity::asExternalModel)
|
||||
//
|
||||
// emit(localMessages)
|
||||
//
|
||||
// val networkMessages = networkDataSource.getMessagesHistory(
|
||||
// conversationId = conversationId,
|
||||
// offset = offset,
|
||||
// count = count
|
||||
// )
|
||||
//
|
||||
// emit(networkMessages)
|
||||
|
||||
networkDataSource.getMessagesHistory(conversationId, offset, count)
|
||||
}
|
||||
|
||||
override suspend fun getMessageById(
|
||||
messagesIds: List<Int>,
|
||||
extended: Boolean?,
|
||||
fields: String?
|
||||
): ApiResult<VkMessage, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
networkDataSource.getMessageById(
|
||||
messagesIds = messagesIds,
|
||||
extended = extended,
|
||||
fields = fields
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun send(
|
||||
peerId: Int,
|
||||
randomId: Int,
|
||||
message: String?,
|
||||
replyTo: Int?,
|
||||
attachments: List<VkAttachment>?
|
||||
): ApiResult<Int, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
networkDataSource.send(
|
||||
peerId,
|
||||
randomId,
|
||||
message,
|
||||
replyTo,
|
||||
attachments
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun markAsRead(
|
||||
peerId: Int,
|
||||
startMessageId: Int?
|
||||
): ApiResult<Int, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
networkDataSource.markAsRead(peerId, startMessageId)
|
||||
}
|
||||
|
||||
override suspend fun getMessage(messageId: Int): Flow<VkMessage?> = flow {
|
||||
val localMessage = localDataSource.getMessage(messageId)?.asExternalModel()
|
||||
|
||||
emit(localMessage)
|
||||
|
||||
val networkMessage = networkDataSource.getMessage(messageId)
|
||||
|
||||
emit(networkMessage)
|
||||
}
|
||||
|
||||
override suspend fun storeMessages(messages: List<VkMessage>) {
|
||||
localDataSource.storeMessages(messages.map(VkMessage::asEntity))
|
||||
}
|
||||
|
||||
// override suspend fun getHistory(
|
||||
// params: MessagesGetHistoryRequest
|
||||
// ): ApiResult<MessagesGetHistoryResponse, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
// messagesService.getHistory(params.map).mapResult(
|
||||
// successMapper = { response -> response.requireResponse() },
|
||||
// errorMapper = { error -> error?.toDomain() }
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// override suspend fun send(
|
||||
// params: MessagesSendRequest
|
||||
// ): ApiResult<Int, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
// messagesService.send(params.map).mapResult(
|
||||
// successMapper = { response -> response.requireResponse() },
|
||||
// errorMapper = { error -> error?.toDomain() }
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// override suspend fun markAsImportant(
|
||||
// params: MessagesMarkAsImportantRequest
|
||||
// ): ApiResult<List<Int>, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
// messagesService.markAsImportant(params.map).mapResult(
|
||||
// successMapper = { response -> response.requireResponse() },
|
||||
// errorMapper = { error -> error?.toDomain() }
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// override suspend fun pin(
|
||||
// params: MessagesPinMessageRequest
|
||||
// ): ApiResult<VkMessageData, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
// messagesService.pin(params.map).mapResult(
|
||||
// successMapper = { response -> response.requireResponse() },
|
||||
// errorMapper = { error -> error?.toDomain() }
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// override suspend fun unpin(
|
||||
// params: MessagesUnPinMessageRequest
|
||||
// ): ApiResult<Unit, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
// messagesService.unpin(params.map).mapResult(
|
||||
// successMapper = {},
|
||||
// errorMapper = { error -> error?.toDomain() }
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// override suspend fun delete(
|
||||
// params: MessagesDeleteRequest
|
||||
// ): ApiResult<Unit, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
// messagesService.delete(params.map).mapResult(
|
||||
// successMapper = {},
|
||||
// errorMapper = { error -> error?.toDomain() }
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// override suspend fun edit(
|
||||
// params: MessagesEditRequest
|
||||
// ): ApiResult<Int, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
// messagesService.edit(params.map).mapResult(
|
||||
// successMapper = { response -> response.requireResponse() },
|
||||
// errorMapper = { error -> error?.toDomain() }
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// override suspend fun getById(
|
||||
// params: MessagesGetByIdRequest
|
||||
// ): ApiResult<MessagesGetByIdResponse, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
// messagesService.getById(params.map).mapResult(
|
||||
// successMapper = { response -> response.requireResponse() },
|
||||
// errorMapper = { error -> error?.toDomain() }
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// override suspend fun markAsRead(
|
||||
// params: MessagesMarkAsReadRequest
|
||||
// ): ApiResult<Int, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
// messagesService.markAsRead(params.map).mapResult(
|
||||
// successMapper = { response -> response.requireResponse() },
|
||||
// errorMapper = { error -> error?.toDomain() }
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// override suspend fun getChat(
|
||||
// params: MessagesGetChatRequest
|
||||
// ): ApiResult<VkChatData, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
// messagesService.getChat(params.map).mapResult(
|
||||
// successMapper = { response -> response.requireResponse() },
|
||||
// errorMapper = { error -> error?.toDomain() }
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// override suspend fun getConversationMembers(
|
||||
// params: MessagesGetConversationMembersRequest
|
||||
// ): ApiResult<MessagesGetConversationMembersResponse, RestApiErrorDomain> =
|
||||
// withContext(Dispatchers.IO) {
|
||||
// messagesService.getConversationMembers(params.map).mapResult(
|
||||
// successMapper = { response -> response.requireResponse() },
|
||||
// errorMapper = { error -> error?.toDomain() }
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// override suspend fun removeChatUser(
|
||||
// params: MessagesRemoveChatUserRequest
|
||||
// ): ApiResult<Int, RestApiErrorDomain> = withContext(Dispatchers.IO) {
|
||||
// messagesService.removeChatUser(params.map).mapResult(
|
||||
// successMapper = { response -> response.requireResponse() },
|
||||
// errorMapper = { error -> error?.toDomain() }
|
||||
// )
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.meloda.app.fast.data.api.messages
|
||||
|
||||
import com.meloda.app.fast.data.State
|
||||
import com.meloda.app.fast.model.api.domain.VkAttachment
|
||||
import com.meloda.app.fast.model.api.domain.VkMessage
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface MessagesUseCase {
|
||||
|
||||
fun getMessagesHistory(
|
||||
conversationId: Int,
|
||||
count: Int?,
|
||||
offset: Int?
|
||||
): Flow<State<MessagesHistoryDomain>>
|
||||
|
||||
fun getById(
|
||||
messageId: Int,
|
||||
extended: Boolean?,
|
||||
fields: String?
|
||||
): Flow<State<VkMessage?>>
|
||||
|
||||
fun getByIds(
|
||||
messageIds: List<Int>,
|
||||
extended: Boolean?,
|
||||
fields: String?
|
||||
): Flow<State<List<VkMessage>>>
|
||||
|
||||
fun sendMessage(
|
||||
peerId: Int,
|
||||
randomId: Int,
|
||||
message: String?,
|
||||
replyTo: Int?,
|
||||
attachments: List<VkAttachment>?
|
||||
): Flow<State<Int>>
|
||||
|
||||
fun markAsRead(
|
||||
peerId: Int,
|
||||
startMessageId: Int
|
||||
): Flow<State<Int>>
|
||||
|
||||
suspend fun storeMessage(message: VkMessage)
|
||||
suspend fun storeMessages(messages: List<VkMessage>)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.meloda.app.fast.data.api.oauth
|
||||
|
||||
import com.meloda.app.fast.model.api.responses.AuthDirectResponse
|
||||
|
||||
interface OAuthRepository {
|
||||
|
||||
suspend fun auth(
|
||||
login: String,
|
||||
password: String,
|
||||
forceSms: Boolean,
|
||||
twoFaCode: String?,
|
||||
captchaSid: String?,
|
||||
captchaKey: String?
|
||||
): AuthDirectResponse
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.meloda.app.fast.data.api.oauth
|
||||
|
||||
import com.meloda.app.fast.common.VkConstants
|
||||
import com.meloda.app.fast.model.api.requests.AuthDirectRequest
|
||||
import com.meloda.app.fast.model.api.responses.AuthDirectResponse
|
||||
import com.meloda.app.fast.network.service.oauth.OAuthService
|
||||
import com.slack.eithernet.ApiResult
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class OAuthRepositoryImpl(
|
||||
private val oAuthService: OAuthService,
|
||||
) : OAuthRepository {
|
||||
|
||||
override suspend fun auth(
|
||||
login: String,
|
||||
password: String,
|
||||
forceSms: Boolean,
|
||||
twoFaCode: String?,
|
||||
captchaSid: String?,
|
||||
captchaKey: String?
|
||||
): AuthDirectResponse = withContext(Dispatchers.IO) {
|
||||
val requestModel = AuthDirectRequest(
|
||||
grantType = VkConstants.Auth.GrantType.PASSWORD,
|
||||
clientId = VkConstants.VK_APP_ID,
|
||||
clientSecret = VkConstants.VK_SECRET,
|
||||
username = login,
|
||||
password = password,
|
||||
scope = VkConstants.Auth.SCOPE,
|
||||
twoFaForceSms = forceSms,
|
||||
twoFaCode = twoFaCode,
|
||||
captchaSid = captchaSid,
|
||||
captchaKey = captchaKey,
|
||||
)
|
||||
|
||||
when (val result = oAuthService.auth(requestModel.map)) {
|
||||
is ApiResult.Success -> result.value
|
||||
|
||||
is ApiResult.Failure.HttpFailure -> {
|
||||
requireNotNull(result.error)
|
||||
}
|
||||
|
||||
else -> throw IllegalStateException("Unknown result")
|
||||
|
||||
// is ApiResult.Failure.ApiFailure -> TODO()
|
||||
// is ApiResult.Failure.HttpFailure -> TODO()
|
||||
// is ApiResult.Failure.NetworkFailure -> TODO()
|
||||
// is ApiResult.Failure.UnknownFailure -> TODO()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.meloda.app.fast.data.api.photos
|
||||
|
||||
import com.meloda.app.fast.model.api.requests.PhotosSaveMessagePhotoRequest
|
||||
import com.meloda.app.fast.network.service.photos.PhotosService
|
||||
import okhttp3.MultipartBody
|
||||
|
||||
class PhotosRepository(
|
||||
private val photosService: PhotosService
|
||||
) {
|
||||
|
||||
suspend fun getMessagesUploadServer(peerId: Int) =
|
||||
photosService.getUploadServer(mapOf("peer_id" to peerId.toString()))
|
||||
|
||||
suspend fun uploadPhoto(url: String, photo: MultipartBody.Part) =
|
||||
photosService.upload(url, photo)
|
||||
|
||||
suspend fun saveMessagePhoto(body: PhotosSaveMessagePhotoRequest) =
|
||||
photosService.save(body.map)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.meloda.app.fast.data.api.users
|
||||
|
||||
import com.meloda.app.fast.model.api.data.VkUserData
|
||||
import com.meloda.app.fast.model.api.requests.UsersGetRequest
|
||||
|
||||
interface UsersRepository {
|
||||
suspend fun getById(params: UsersGetRequest): List<VkUserData>
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.meloda.app.fast.data.api.users
|
||||
|
||||
import com.meloda.app.fast.model.api.data.VkUserData
|
||||
import com.meloda.app.fast.model.api.requests.UsersGetRequest
|
||||
import com.meloda.app.fast.network.service.users.UsersService
|
||||
|
||||
class UsersRepositoryImpl(
|
||||
private val usersService: UsersService
|
||||
) : UsersRepository {
|
||||
|
||||
override suspend fun getById(params: UsersGetRequest): List<VkUserData> {
|
||||
// TODO: 05/05/2024, Danil Nikolaev: implement
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.meloda.app.fast.data.api.users
|
||||
|
||||
import com.meloda.app.fast.data.State
|
||||
import com.meloda.app.fast.model.api.domain.VkUser
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface UsersUseCase {
|
||||
|
||||
fun getUserById(
|
||||
userId: Int,
|
||||
fields: String?,
|
||||
nomCase: String?
|
||||
): Flow<State<VkUser?>>
|
||||
|
||||
fun getUsersByIds(
|
||||
userIds: List<Int>,
|
||||
fields: String?,
|
||||
nomCase: String?
|
||||
): Flow<State<List<VkUser>>>
|
||||
|
||||
suspend fun storeUser(user: VkUser)
|
||||
suspend fun storeUsers(users: List<VkUser>)
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.meloda.app.fast.data.api.users
|
||||
|
||||
import com.meloda.app.fast.data.State
|
||||
import com.meloda.app.fast.model.api.domain.VkUser
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
|
||||
// TODO: 05/05/2024, Danil Nikolaev: implement
|
||||
class UsersUseCaseImpl(
|
||||
private val usersRepository: UsersRepository,
|
||||
) : UsersUseCase {
|
||||
|
||||
override fun getUserById(
|
||||
userId: Int,
|
||||
fields: String?,
|
||||
nomCase: String?
|
||||
): Flow<State<VkUser?>> = flow {
|
||||
// emit(State.Loading)
|
||||
//
|
||||
// val newState = usersRepository.getById(
|
||||
// UsersGetRequest(
|
||||
// userIds = listOf(userId),
|
||||
// fields = fields,
|
||||
// nomCase = nomCase
|
||||
// )
|
||||
// ).fold(
|
||||
// onSuccess = { response -> State.Success(response.singleOrNull()?.mapToDomain()) },
|
||||
// onNetworkFailure = { State.Error.ConnectionError },
|
||||
// onUnknownFailure = { State.UNKNOWN_ERROR },
|
||||
// onHttpFailure = { result -> result.error.toStateApiError() },
|
||||
// onApiFailure = { result -> result.error.toStateApiError() }
|
||||
// )
|
||||
// emit(newState)
|
||||
}
|
||||
|
||||
override fun getUsersByIds(
|
||||
userIds: List<Int>,
|
||||
fields: String?,
|
||||
nomCase: String?
|
||||
): Flow<State<List<VkUser>>> = flow {
|
||||
// emit(State.Loading)
|
||||
//
|
||||
// val newState = usersRepository.getById(
|
||||
// UsersGetRequest(
|
||||
// userIds = userIds,
|
||||
// fields = fields,
|
||||
// nomCase = nomCase
|
||||
// )
|
||||
// ).fold(
|
||||
// onSuccess = { response -> State.Success(response.map(VkUserData::mapToDomain)) },
|
||||
// onNetworkFailure = { State.Error.ConnectionError },
|
||||
// onUnknownFailure = { State.UNKNOWN_ERROR },
|
||||
// onHttpFailure = { result -> result.error.toStateApiError() },
|
||||
// onApiFailure = { result -> result.error.toStateApiError() }
|
||||
// )
|
||||
// emit(newState)
|
||||
}
|
||||
|
||||
override suspend fun storeUser(user: VkUser) {
|
||||
|
||||
}
|
||||
|
||||
override suspend fun storeUsers(users: List<VkUser>) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.meloda.app.fast.data.api.videos
|
||||
|
||||
import com.meloda.app.fast.network.service.videos.VideosService
|
||||
import okhttp3.MultipartBody
|
||||
|
||||
class VideosRepository(
|
||||
private val videosService: VideosService
|
||||
) {
|
||||
|
||||
suspend fun save() = videosService.save()
|
||||
|
||||
// TODO: 05/05/2024, Danil Nikolaev: research, maybe remove multipart.body
|
||||
suspend fun upload(url: String, file: MultipartBody.Part) = videosService.upload(url, file)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.meloda.app.fast.data.db
|
||||
|
||||
import com.meloda.app.fast.model.database.AccountEntity
|
||||
|
||||
interface AccountsRepository {
|
||||
|
||||
suspend fun getAccounts(): List<AccountEntity>
|
||||
|
||||
suspend fun storeAccounts(accounts: List<AccountEntity>)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.meloda.app.fast.data.db
|
||||
|
||||
import com.meloda.app.fast.database.dao.AccountDao
|
||||
import com.meloda.app.fast.model.database.AccountEntity
|
||||
|
||||
class AccountsRepositoryImpl(
|
||||
private val accountDao: AccountDao
|
||||
) : AccountsRepository {
|
||||
|
||||
override suspend fun getAccounts(): List<AccountEntity> = accountDao.getAll()
|
||||
|
||||
override suspend fun storeAccounts(
|
||||
accounts: List<AccountEntity>
|
||||
) = accountDao.insertAll(accounts)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.meloda.app.fast.data.di
|
||||
|
||||
import com.meloda.app.fast.common.di.commonModule
|
||||
import com.meloda.app.fast.data.api.account.AccountRepository
|
||||
import com.meloda.app.fast.data.api.account.AccountRepositoryImpl
|
||||
import com.meloda.app.fast.data.api.account.AccountUseCase
|
||||
import com.meloda.app.fast.data.api.account.AccountUseCaseImpl
|
||||
import com.meloda.app.fast.data.api.audios.AudiosRepository
|
||||
import com.meloda.app.fast.data.api.auth.AuthRepository
|
||||
import com.meloda.app.fast.data.api.auth.AuthRepositoryImpl
|
||||
import com.meloda.app.fast.data.api.conversations.ConversationsRepository
|
||||
import com.meloda.app.fast.data.api.conversations.ConversationsRepositoryImpl
|
||||
import com.meloda.app.fast.data.api.files.FilesRepository
|
||||
import com.meloda.app.fast.data.api.friends.FriendsRepository
|
||||
import com.meloda.app.fast.data.api.friends.FriendsRepositoryImpl
|
||||
import com.meloda.app.fast.data.api.longpoll.LongPollRepository
|
||||
import com.meloda.app.fast.data.api.longpoll.LongPollRepositoryImpl
|
||||
import com.meloda.app.fast.data.api.messages.MessagesLocalDataSource
|
||||
import com.meloda.app.fast.data.api.messages.MessagesLocalDataSourceImpl
|
||||
import com.meloda.app.fast.data.api.messages.MessagesNetworkDataSource
|
||||
import com.meloda.app.fast.data.api.messages.MessagesNetworkDataSourceImpl
|
||||
import com.meloda.app.fast.data.api.messages.MessagesRepository
|
||||
import com.meloda.app.fast.data.api.messages.MessagesRepositoryImpl
|
||||
import com.meloda.app.fast.data.api.oauth.OAuthRepository
|
||||
import com.meloda.app.fast.data.api.oauth.OAuthRepositoryImpl
|
||||
import com.meloda.app.fast.data.api.photos.PhotosRepository
|
||||
import com.meloda.app.fast.data.api.users.UsersRepository
|
||||
import com.meloda.app.fast.data.api.users.UsersRepositoryImpl
|
||||
import com.meloda.app.fast.data.api.users.UsersUseCase
|
||||
import com.meloda.app.fast.data.api.users.UsersUseCaseImpl
|
||||
import com.meloda.app.fast.data.api.videos.VideosRepository
|
||||
import com.meloda.app.fast.data.db.AccountsRepository
|
||||
import com.meloda.app.fast.data.db.AccountsRepositoryImpl
|
||||
import com.meloda.app.fast.database.di.databaseModule
|
||||
import com.meloda.app.fast.datastore.di.dataStoreModule
|
||||
import com.meloda.app.fast.network.di.networkModule
|
||||
import org.koin.core.module.dsl.singleOf
|
||||
import org.koin.dsl.bind
|
||||
import org.koin.dsl.module
|
||||
|
||||
val dataModule = module {
|
||||
includes(
|
||||
commonModule,
|
||||
databaseModule,
|
||||
dataStoreModule,
|
||||
networkModule,
|
||||
)
|
||||
|
||||
singleOf(::AccountRepositoryImpl) bind AccountRepository::class
|
||||
singleOf(::AccountUseCaseImpl) bind AccountUseCase::class
|
||||
|
||||
singleOf(::AudiosRepository)
|
||||
|
||||
singleOf(::AuthRepositoryImpl) bind AuthRepository::class
|
||||
|
||||
singleOf(::ConversationsRepositoryImpl) bind ConversationsRepository::class
|
||||
|
||||
singleOf(::FilesRepository)
|
||||
|
||||
singleOf(::LongPollRepositoryImpl) bind LongPollRepository::class
|
||||
|
||||
singleOf(::MessagesLocalDataSourceImpl) bind MessagesLocalDataSource::class
|
||||
singleOf(::MessagesNetworkDataSourceImpl) bind MessagesNetworkDataSource::class
|
||||
singleOf(::MessagesRepositoryImpl) bind MessagesRepository::class
|
||||
|
||||
singleOf(::OAuthRepositoryImpl) bind OAuthRepository::class
|
||||
|
||||
singleOf(::PhotosRepository)
|
||||
|
||||
singleOf(::UsersRepositoryImpl) bind UsersRepository::class
|
||||
singleOf(::UsersUseCaseImpl) bind UsersUseCase::class
|
||||
|
||||
singleOf(::VideosRepository)
|
||||
|
||||
singleOf(::AccountsRepositoryImpl) bind AccountsRepository::class
|
||||
|
||||
singleOf(::FriendsRepositoryImpl) bind FriendsRepository::class
|
||||
}
|
||||
Reference in New Issue
Block a user