Сборка перед кэшированием

This commit is contained in:
Халимов Рустам
2026-04-23 01:31:51 +03:00
parent 945134f029
commit d9a20e40b0
42 changed files with 16 additions and 185 deletions

10
.gitignore vendored
View File

@@ -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/

View File

@@ -1,2 +0,0 @@
#Tue Apr 14 00:27:15 MSK 2026
gradle.version=8.5

View File

@@ -1,2 +0,0 @@
#Tue Apr 14 00:13:58 MSK 2026
java.home=C\:\\Program Files\\Android\\Android Studio\\jbr

View File

@@ -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 {

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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()
}
}