android: add bulk forward core foundation for multi-select
Some checks failed
CI / test (push) Failing after 2m10s
Some checks failed
CI / test (push) Failing after 2m10s
This commit is contained in:
@@ -162,3 +162,10 @@
|
||||
- Added safe area insets handling (`WindowInsets.safeDrawing`) for login, chat list, session-check and chat screens.
|
||||
- Added bottom composer protection in chat screen with `navigationBarsPadding()` and `imePadding()`.
|
||||
- Fixed UI overlap with status bar and navigation bar on modern Android devices.
|
||||
|
||||
### Step 26 - Core base / bulk forward foundation
|
||||
- Added message API/data contracts for bulk forward (`POST /api/v1/messages/{message_id}/forward-bulk`).
|
||||
- Extended `MessageRepository` with `forwardMessageBulk(...)`.
|
||||
- Implemented bulk-forward flow in `NetworkMessageRepository` with Room/chat last-message updates.
|
||||
- Added `ForwardMessageBulkUseCase` for future multi-select message actions.
|
||||
- Updated message repository unit test fakes to cover new API surface.
|
||||
|
||||
@@ -9,6 +9,7 @@ import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageCreateRequestDto
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageForwardRequestDto
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageForwardBulkRequestDto
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageReadDto
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageReactionDto
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageReactionToggleRequestDto
|
||||
@@ -51,6 +52,12 @@ interface MessageApiService {
|
||||
@Body request: MessageForwardRequestDto,
|
||||
): MessageReadDto
|
||||
|
||||
@POST("/api/v1/messages/{message_id}/forward-bulk")
|
||||
suspend fun forwardMessageBulk(
|
||||
@Path("message_id") messageId: Long,
|
||||
@Body request: MessageForwardBulkRequestDto,
|
||||
): List<MessageReadDto>
|
||||
|
||||
@GET("/api/v1/messages/{message_id}/reactions")
|
||||
suspend fun listReactions(
|
||||
@Path("message_id") messageId: Long,
|
||||
|
||||
@@ -60,6 +60,14 @@ data class MessageForwardRequestDto(
|
||||
val includeAuthor: Boolean = true,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MessageForwardBulkRequestDto(
|
||||
@SerialName("target_chat_ids")
|
||||
val targetChatIds: List<Long>,
|
||||
@SerialName("include_author")
|
||||
val includeAuthor: Boolean = true,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MessageReactionDto(
|
||||
val emoji: String,
|
||||
|
||||
@@ -26,6 +26,7 @@ import ru.daemonlord.messenger.domain.message.model.MessageReaction
|
||||
import ru.daemonlord.messenger.domain.message.repository.MessageRepository
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageCreateRequestDto
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageForwardRequestDto
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageForwardBulkRequestDto
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageReactionToggleRequestDto
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageStatusUpdateRequestDto
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageUpdateRequestDto
|
||||
@@ -313,6 +314,38 @@ class NetworkMessageRepository @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun forwardMessageBulk(
|
||||
messageId: Long,
|
||||
targetChatIds: List<Long>,
|
||||
includeAuthor: Boolean,
|
||||
): AppResult<Unit> = withContext(ioDispatcher) {
|
||||
if (targetChatIds.isEmpty()) return@withContext AppResult.Success(Unit)
|
||||
try {
|
||||
val forwarded = messageApiService.forwardMessageBulk(
|
||||
messageId = messageId,
|
||||
request = MessageForwardBulkRequestDto(
|
||||
targetChatIds = targetChatIds,
|
||||
includeAuthor = includeAuthor,
|
||||
),
|
||||
)
|
||||
if (forwarded.isNotEmpty()) {
|
||||
messageDao.upsertMessages(forwarded.map { it.toEntity() })
|
||||
forwarded.forEach { message ->
|
||||
chatDao.updateLastMessage(
|
||||
chatId = message.chatId,
|
||||
lastMessageText = message.text,
|
||||
lastMessageType = message.type,
|
||||
lastMessageCreatedAt = message.createdAt,
|
||||
updatedSortAt = message.createdAt,
|
||||
)
|
||||
}
|
||||
}
|
||||
AppResult.Success(Unit)
|
||||
} catch (error: Throwable) {
|
||||
AppResult.Error(error.toAppError())
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun listReactions(messageId: Long): AppResult<List<MessageReaction>> = withContext(ioDispatcher) {
|
||||
try {
|
||||
val reactions = messageApiService.listReactions(messageId = messageId).map { it.toDomain() }
|
||||
|
||||
@@ -23,6 +23,7 @@ interface MessageRepository {
|
||||
suspend fun markMessageDelivered(chatId: Long, messageId: Long): AppResult<Unit>
|
||||
suspend fun markMessageRead(chatId: Long, messageId: Long): AppResult<Unit>
|
||||
suspend fun forwardMessage(messageId: Long, targetChatId: Long, includeAuthor: Boolean = true): AppResult<Unit>
|
||||
suspend fun forwardMessageBulk(messageId: Long, targetChatIds: List<Long>, includeAuthor: Boolean = true): AppResult<Unit>
|
||||
suspend fun listReactions(messageId: Long): AppResult<List<MessageReaction>>
|
||||
suspend fun toggleReaction(messageId: Long, emoji: String): AppResult<List<MessageReaction>>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package ru.daemonlord.messenger.domain.message.usecase
|
||||
|
||||
import ru.daemonlord.messenger.domain.common.AppResult
|
||||
import ru.daemonlord.messenger.domain.message.repository.MessageRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class ForwardMessageBulkUseCase @Inject constructor(
|
||||
private val repository: MessageRepository,
|
||||
) {
|
||||
suspend operator fun invoke(
|
||||
messageId: Long,
|
||||
targetChatIds: List<Long>,
|
||||
includeAuthor: Boolean = true,
|
||||
): AppResult<Unit> {
|
||||
return repository.forwardMessageBulk(
|
||||
messageId = messageId,
|
||||
targetChatIds = targetChatIds,
|
||||
includeAuthor = includeAuthor,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import ru.daemonlord.messenger.data.media.dto.UploadUrlResponseDto
|
||||
import ru.daemonlord.messenger.data.message.api.MessageApiService
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageCreateRequestDto
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageForwardRequestDto
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageForwardBulkRequestDto
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageReadDto
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageReactionDto
|
||||
import ru.daemonlord.messenger.data.message.dto.MessageReactionToggleRequestDto
|
||||
@@ -145,6 +146,18 @@ class NetworkMessageRepositoryTest {
|
||||
return sendResponse.copy(id = messageId + 1000, chatId = request.targetChatId)
|
||||
}
|
||||
|
||||
override suspend fun forwardMessageBulk(
|
||||
messageId: Long,
|
||||
request: MessageForwardBulkRequestDto,
|
||||
): List<MessageReadDto> {
|
||||
return request.targetChatIds.mapIndexed { index, chatId ->
|
||||
sendResponse.copy(
|
||||
id = messageId + 2000 + index,
|
||||
chatId = chatId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun listReactions(messageId: Long): List<MessageReactionDto> = emptyList()
|
||||
|
||||
override suspend fun toggleReaction(
|
||||
|
||||
Reference in New Issue
Block a user