Сборка перед кэшированием
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -87,3 +87,13 @@ postgres_data/
|
||||
tmp/
|
||||
|
||||
*.txt
|
||||
|
||||
# Android / Kotlin Mobile
|
||||
client-mobile/.gradle/
|
||||
client-mobile/.idea/
|
||||
client-mobile/.run/
|
||||
client-mobile/build/
|
||||
client-mobile/.cxx/
|
||||
client-mobile/local.properties
|
||||
client-mobile/*.iml
|
||||
client-mobile/.kotlin/
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,2 +0,0 @@
|
||||
#Tue Apr 14 00:27:15 MSK 2026
|
||||
gradle.version=8.5
|
||||
Binary file not shown.
@@ -1,2 +0,0 @@
|
||||
#Tue Apr 14 00:13:58 MSK 2026
|
||||
java.home=C\:\\Program Files\\Android\\Android Studio\\jbr
|
||||
@@ -23,7 +23,6 @@ class ChatRepositoryImpl @Inject constructor(
|
||||
private val api: ChatApi,
|
||||
private val tokenManager: TokenManager,
|
||||
private val serverConfig: ServerConfig,
|
||||
private val messageDao: core.database.data.MessageDao,
|
||||
private val hubClient: chats.data.remote.signalr.ChatHubClient
|
||||
) : ChatRepository {
|
||||
private val gson = com.google.gson.Gson()
|
||||
@@ -35,10 +34,8 @@ class ChatRepositoryImpl @Inject constructor(
|
||||
}
|
||||
|
||||
override fun getMessagesFlow(chatId: String): kotlinx.coroutines.flow.Flow<List<Message>> {
|
||||
val baseUrl = serverConfig.getBaseUrl().removeSuffix("/api/")
|
||||
return messageDao.getMessages(chatId).map { entities: List<core.database.data.MessageEntity> ->
|
||||
entities.map { it.toDomain(baseUrl, gson) }
|
||||
}
|
||||
// Кэш отключён - всегда возвращаем пустой поток
|
||||
return kotlinx.coroutines.flow.flowOf(emptyList())
|
||||
}
|
||||
|
||||
override suspend fun getMessages(chatId: String, cursor: String?, pivot: Long?, limit: Int?): List<Message> {
|
||||
@@ -93,21 +90,19 @@ class ChatRepositoryImpl @Inject constructor(
|
||||
|
||||
override suspend fun markMessagesAsRead(chatId: String, lastMessageId: String, lastReadSequenceId: Int) {
|
||||
try {
|
||||
android.util.Log.d("ChatRepoImpl", "markMessagesAsRead CALLED FOR $chatId. Caller stack: ${android.util.Log.getStackTraceString(Throwable())}")
|
||||
android.util.Log.d("ChatRepoImpl", "markMessagesAsRead CALLED FOR $chatId")
|
||||
hubClient.readMessages(ReadMessagesRequest(chatId, lastMessageId, lastReadSequenceId))
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("ChatRepo", "Error marking messages as read", e)
|
||||
}
|
||||
// Обновляем локальную БД
|
||||
messageDao.markMessagesAsRead(chatId, lastReadSequenceId)
|
||||
}
|
||||
|
||||
override suspend fun saveMessage(message: Message) {
|
||||
android.util.Log.d("ChatRepo", "DB cache disabled, skipping save: ${message.id}")
|
||||
// Кэш отключён
|
||||
}
|
||||
|
||||
override suspend fun deleteLocalMessage(messageId: String) {
|
||||
messageDao.deleteMessage(messageId)
|
||||
// Локальное удаление не поддерживается без кэша
|
||||
}
|
||||
|
||||
override suspend fun uploadMedia(file: java.io.File): String {
|
||||
@@ -145,7 +140,6 @@ class ChatRepositoryImpl @Inject constructor(
|
||||
|
||||
override suspend fun deleteMessage(messageId: String, forEveryone: Boolean) {
|
||||
api.deleteMessage(messageId, forEveryone)
|
||||
messageDao.deleteMessage(messageId)
|
||||
}
|
||||
|
||||
override suspend fun editMessage(messageId: String, content: String): Message {
|
||||
|
||||
@@ -23,24 +23,6 @@ fun ChatDto.toDomain(currentUserId: String, baseUrl: String): Chat {
|
||||
)
|
||||
}
|
||||
|
||||
fun Message.toEntity(gson: com.google.gson.Gson): core.database.data.MessageEntity {
|
||||
return core.database.data.MessageEntity(
|
||||
id = id,
|
||||
chatId = chatId,
|
||||
senderId = senderId,
|
||||
senderName = senderName,
|
||||
senderAvatar = senderAvatar,
|
||||
content = content,
|
||||
sequenceId = sequenceId,
|
||||
createdAt = createdAt,
|
||||
mediaType = mediaType.name.lowercase(),
|
||||
mediaJson = gson.toJson(media),
|
||||
reactionsJson = gson.toJson(reactions),
|
||||
isRead = isRead,
|
||||
replyToId = replyTo?.id
|
||||
)
|
||||
}
|
||||
|
||||
fun MessageDto.toDomain(currentUserId: String, baseUrl: String): Message {
|
||||
val domainMediaType = when (type) {
|
||||
"gif" -> MediaType.GIF
|
||||
@@ -86,79 +68,6 @@ fun MessageDto.toDomain(currentUserId: String, baseUrl: String): Message {
|
||||
)
|
||||
}
|
||||
|
||||
fun MessageDto.toEntity(baseUrl: String, currentUserId: String, gson: com.google.gson.Gson): core.database.data.MessageEntity {
|
||||
return core.database.data.MessageEntity(
|
||||
id = id,
|
||||
chatId = chatId ?: "",
|
||||
senderId = senderId ?: "",
|
||||
senderName = sender?.displayName ?: "Unknown",
|
||||
senderAvatar = sender?.avatarUrl?.ensureAbsoluteUrl(baseUrl),
|
||||
content = content,
|
||||
sequenceId = sequenceId ?: 0,
|
||||
createdAt = createdAt ?: "",
|
||||
mediaType = type ?: "text",
|
||||
mediaJson = gson.toJson(media),
|
||||
reactionsJson = gson.toJson(reactions),
|
||||
isRead = senderId == currentUserId,
|
||||
replyToId = replyTo?.id
|
||||
)
|
||||
}
|
||||
|
||||
fun core.database.data.MessageEntity.toDomain(baseUrl: String, gson: com.google.gson.Gson): Message {
|
||||
val mediaTypeEnum = when (mediaType) {
|
||||
"gif" -> MediaType.GIF
|
||||
"image", "photo" -> MediaType.IMAGE
|
||||
"video" -> MediaType.VIDEO
|
||||
"audio", "voice" -> MediaType.AUDIO
|
||||
"file" -> MediaType.FILE
|
||||
else -> MediaType.TEXT
|
||||
}
|
||||
|
||||
val mediaTypeToken = object : com.google.gson.reflect.TypeToken<List<chats.data.remote.dto.MediaItemDto>>() {}.type
|
||||
val mediaDtos: List<chats.data.remote.dto.MediaItemDto> = try {
|
||||
gson.fromJson(mediaJson, mediaTypeToken)
|
||||
} catch (e: Exception) {
|
||||
emptyList()
|
||||
} ?: emptyList()
|
||||
|
||||
val reactionsTypeToken = object : com.google.gson.reflect.TypeToken<List<chats.data.remote.dto.ReactionDto>>() {}.type
|
||||
val reactionDtos: List<chats.data.remote.dto.ReactionDto> = try {
|
||||
gson.fromJson(reactionsJson, reactionsTypeToken)
|
||||
} catch (e: Exception) {
|
||||
try {
|
||||
val mapType = object : com.google.gson.reflect.TypeToken<Map<String, Int>>() {}.type
|
||||
val map: Map<String, Int> = gson.fromJson(reactionsJson, mapType) ?: emptyMap()
|
||||
map.map { chats.data.remote.dto.ReactionDto(it.key, it.value, false) }
|
||||
} catch (innerE: Exception) {
|
||||
emptyList()
|
||||
}
|
||||
} ?: emptyList()
|
||||
|
||||
return Message(
|
||||
id = id,
|
||||
chatId = chatId,
|
||||
senderId = senderId,
|
||||
senderName = senderName,
|
||||
senderAvatar = senderAvatar,
|
||||
content = content,
|
||||
sequenceId = sequenceId,
|
||||
createdAt = createdAt,
|
||||
media = mediaDtos.map {
|
||||
chats.domain.model.Media(
|
||||
id = it.id ?: java.util.UUID.randomUUID().toString(),
|
||||
type = it.type ?: "unknown",
|
||||
url = (it.url ?: "").ensureAbsoluteUrl(baseUrl),
|
||||
filename = it.filename,
|
||||
size = it.size,
|
||||
duration = it.duration
|
||||
)
|
||||
},
|
||||
mediaType = mediaTypeEnum,
|
||||
reactions = reactionDtos.associate { it.emoji to it.count },
|
||||
isRead = isRead
|
||||
)
|
||||
}
|
||||
|
||||
fun String.ensureAbsoluteUrl(baseUrl: String): String {
|
||||
return if (this.startsWith("http")) {
|
||||
this
|
||||
|
||||
@@ -4,7 +4,6 @@ import chats.data.remote.api.ChatApi
|
||||
import chats.data.remote.signalr.ChatHubClient
|
||||
import chats.data.repository.ChatRepositoryImpl
|
||||
import chats.domain.repository.ChatRepository
|
||||
import core.database.data.MessageDao
|
||||
import core.network.ServerConfig
|
||||
import core.security.TokenManager
|
||||
import dagger.Module
|
||||
@@ -30,10 +29,9 @@ object ChatModule {
|
||||
api: ChatApi,
|
||||
tokenManager: TokenManager,
|
||||
serverConfig: ServerConfig,
|
||||
messageDao: MessageDao,
|
||||
hubClient: chats.data.remote.signalr.ChatHubClient
|
||||
): ChatRepository {
|
||||
return ChatRepositoryImpl(api, tokenManager, serverConfig, messageDao, hubClient)
|
||||
return ChatRepositoryImpl(api, tokenManager, serverConfig, hubClient)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package core.database.data
|
||||
|
||||
import androidx.room.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Entity(tableName = "messages")
|
||||
data class MessageEntity(
|
||||
@PrimaryKey val id: String,
|
||||
val chatId: String,
|
||||
val senderId: String,
|
||||
val senderName: String,
|
||||
val senderAvatar: String?,
|
||||
val content: String?,
|
||||
val sequenceId: Int,
|
||||
val createdAt: String,
|
||||
val mediaType: String,
|
||||
val mediaJson: String, // Simplified for now
|
||||
val reactionsJson: String,
|
||||
val isRead: Boolean,
|
||||
val replyToId: String? = null
|
||||
)
|
||||
|
||||
@Dao
|
||||
interface MessageDao {
|
||||
@Query("SELECT * FROM messages WHERE chatId = :chatId ORDER BY sequenceId ASC")
|
||||
fun getMessages(chatId: String): Flow<List<MessageEntity>>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertMessages(messages: List<MessageEntity>)
|
||||
|
||||
@Query("DELETE FROM messages WHERE chatId = :chatId")
|
||||
suspend fun clearChat(chatId: String)
|
||||
|
||||
@Query("DELETE FROM messages WHERE id = :messageId")
|
||||
suspend fun deleteMessage(messageId: String)
|
||||
|
||||
@Query("UPDATE messages SET isRead = 1 WHERE chatId = :chatId AND sequenceId <= :lastReadSequenceId AND isRead = 0")
|
||||
suspend fun markMessagesAsRead(chatId: String, lastReadSequenceId: Int)
|
||||
}
|
||||
|
||||
@Database(entities = [MessageEntity::class], version = 1)
|
||||
abstract class ChatDatabase : RoomDatabase() {
|
||||
abstract fun messageDao(): MessageDao
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package core.di
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
import core.database.data.ChatDatabase
|
||||
import core.database.data.MessageDao
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object DatabaseModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDatabase(@ApplicationContext context: Context): ChatDatabase {
|
||||
return Room.databaseBuilder(
|
||||
context,
|
||||
ChatDatabase::class.java,
|
||||
"chat_database"
|
||||
).fallbackToDestructiveMigration().build()
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideMessageDao(database: ChatDatabase): MessageDao {
|
||||
return database.messageDao()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user