Commit c63e1da7 authored by Lucio Maciel's avatar Lucio Maciel

Initial message db models and DAO, save messages to database

parent cb57e76e
...@@ -5,9 +5,14 @@ import chat.rocket.android.chatroom.presentation.ChatRoomView ...@@ -5,9 +5,14 @@ import chat.rocket.android.chatroom.presentation.ChatRoomView
import chat.rocket.android.chatroom.ui.ChatRoomFragment import chat.rocket.android.chatroom.ui.ChatRoomFragment
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.db.ChatRoomDao
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.DatabaseManagerFactory
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
import javax.inject.Named
@Module @Module
class ChatRoomFragmentModule { class ChatRoomFragmentModule {
...@@ -33,4 +38,22 @@ class ChatRoomFragmentModule { ...@@ -33,4 +38,22 @@ class ChatRoomFragmentModule {
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy { fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs) return CancelStrategy(owner, jobs)
} }
@Provides
@PerFragment
@Named("currentServer")
fun provideCurrentServer(currentServerInteractor: GetCurrentServerInteractor): String {
return currentServerInteractor.get()!!
}
@Provides
@PerFragment
fun provideDatabaseManager(factory: DatabaseManagerFactory,
@Named("currentServer") currentServer: String): DatabaseManager {
return factory.create(currentServer)
}
@Provides
@PerFragment
fun provideChatRoomDao(manager: DatabaseManager): ChatRoomDao = manager.chatRoomDao()
} }
...@@ -227,6 +227,7 @@ class ChatRoomPresenter @Inject constructor( ...@@ -227,6 +227,7 @@ class ChatRoomPresenter @Inject constructor(
retryIO("loadAndShowMessages($chatRoomId, $chatRoomType, $offset") { retryIO("loadAndShowMessages($chatRoomId, $chatRoomType, $offset") {
client.messages(chatRoomId, roomTypeOf(chatRoomType), offset, 30).result client.messages(chatRoomId, roomTypeOf(chatRoomType), offset, 30).result
} }
dbManager.processMessagesBatch(messages)
messagesRepository.saveAll(messages) messagesRepository.saveAll(messages)
//we are saving last sync date of latest synced chat room message //we are saving last sync date of latest synced chat room message
...@@ -510,6 +511,8 @@ class ChatRoomPresenter @Inject constructor( ...@@ -510,6 +511,8 @@ class ChatRoomPresenter @Inject constructor(
} }
Timber.d("History: $messages") Timber.d("History: $messages")
dbManager.processMessagesBatch(messages.result)
if (messages.result.isNotEmpty()) { if (messages.result.isNotEmpty()) {
val models = mapper.map(messages.result, RoomUiModel( val models = mapper.map(messages.result, RoomUiModel(
roles = chatRoles, roles = chatRoles,
......
...@@ -30,7 +30,6 @@ import chat.rocket.android.chatrooms.viewmodel.ChatRoomsViewModel ...@@ -30,7 +30,6 @@ import chat.rocket.android.chatrooms.viewmodel.ChatRoomsViewModel
import chat.rocket.android.chatrooms.viewmodel.ChatRoomsViewModelFactory import chat.rocket.android.chatrooms.viewmodel.ChatRoomsViewModelFactory
import chat.rocket.android.chatrooms.viewmodel.LoadingState import chat.rocket.android.chatrooms.viewmodel.LoadingState
import chat.rocket.android.chatrooms.viewmodel.Query import chat.rocket.android.chatrooms.viewmodel.Query
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.helper.ChatRoomsSortOrder import chat.rocket.android.helper.ChatRoomsSortOrder
import chat.rocket.android.helper.Constants import chat.rocket.android.helper.Constants
import chat.rocket.android.helper.SharedPreferenceHelper import chat.rocket.android.helper.SharedPreferenceHelper
...@@ -57,10 +56,10 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -57,10 +56,10 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
@Inject @Inject
lateinit var factory: ChatRoomsViewModelFactory lateinit var factory: ChatRoomsViewModelFactory
@Inject @Inject
lateinit var dbManager: DatabaseManager // TODO - remove when moving ChatRoom screen to DB
@Inject
lateinit var analyticsManager: AnalyticsManager lateinit var analyticsManager: AnalyticsManager
lateinit var viewModel: ChatRoomsViewModel
private lateinit var viewModel: ChatRoomsViewModel
private var searchView: SearchView? = null private var searchView: SearchView? = null
private var sortView: MenuItem? = null private var sortView: MenuItem? = null
private val handler = Handler() private val handler = Handler()
......
...@@ -99,7 +99,7 @@ abstract class ChatRoomDao : BaseDao<ChatRoomEntity> { ...@@ -99,7 +99,7 @@ abstract class ChatRoomDao : BaseDao<ChatRoomEntity> {
abstract fun update(list: List<ChatRoomEntity>) abstract fun update(list: List<ChatRoomEntity>)
@Transaction @Transaction
open fun update(toRemove: List<String>, toInsert: List<ChatRoomEntity>, toUpdate: List<ChatRoomEntity>) { open fun update(toInsert: List<ChatRoomEntity>, toUpdate: List<ChatRoomEntity>, toRemove: List<String>) {
insertOrReplace(toInsert) insertOrReplace(toInsert)
update(toUpdate) update(toUpdate)
toRemove.forEach { id -> toRemove.forEach { id ->
......
package chat.rocket.android.db package chat.rocket.android.db
import android.app.Application import android.app.Application
import androidx.room.migration.Migration
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.db.model.BaseMessageEntity
import chat.rocket.android.db.model.BaseUserEntity import chat.rocket.android.db.model.BaseUserEntity
import chat.rocket.android.db.model.ChatRoomEntity import chat.rocket.android.db.model.ChatRoomEntity
import chat.rocket.android.db.model.MessageChannelsRelation
import chat.rocket.android.db.model.MessageEntity
import chat.rocket.android.db.model.MessageFavoritesRelation
import chat.rocket.android.db.model.MessageMentionsRelation
import chat.rocket.android.db.model.ReactionEntity
import chat.rocket.android.db.model.ReactionMessageRelation
import chat.rocket.android.db.model.UrlEntity
import chat.rocket.android.db.model.UserEntity import chat.rocket.android.db.model.UserEntity
import chat.rocket.android.db.model.UserStatus import chat.rocket.android.db.model.UserStatus
import chat.rocket.android.util.extensions.removeTrailingSlash import chat.rocket.android.util.extensions.removeTrailingSlash
import chat.rocket.android.util.extensions.toEntity
import chat.rocket.android.util.extensions.userId import chat.rocket.android.util.extensions.userId
import chat.rocket.common.model.BaseRoom import chat.rocket.common.model.BaseRoom
import chat.rocket.common.model.RoomType import chat.rocket.common.model.RoomType
import chat.rocket.common.model.SimpleUser
import chat.rocket.common.model.User import chat.rocket.common.model.User
import chat.rocket.core.internal.model.Subscription import chat.rocket.core.internal.model.Subscription
import chat.rocket.core.internal.realtime.socket.model.StreamMessage import chat.rocket.core.internal.realtime.socket.model.StreamMessage
...@@ -32,7 +41,6 @@ class DatabaseManager(val context: Application, ...@@ -32,7 +41,6 @@ class DatabaseManager(val context: Application,
private val database: RCDatabase = androidx.room.Room.databaseBuilder(context, private val database: RCDatabase = androidx.room.Room.databaseBuilder(context,
RCDatabase::class.java, serverUrl.databaseName()) RCDatabase::class.java, serverUrl.databaseName())
.addMigrations(RCDatabase.MIGRATION_4_5)
.fallbackToDestructiveMigration() .fallbackToDestructiveMigration()
.build() .build()
private val dbContext = newSingleThreadContext("$serverUrl-db-context") private val dbContext = newSingleThreadContext("$serverUrl-db-context")
...@@ -44,6 +52,7 @@ class DatabaseManager(val context: Application, ...@@ -44,6 +52,7 @@ class DatabaseManager(val context: Application,
fun chatRoomDao(): ChatRoomDao = database.chatRoomDao() fun chatRoomDao(): ChatRoomDao = database.chatRoomDao()
fun userDao(): UserDao = database.userDao() fun userDao(): UserDao = database.userDao()
fun messageDao(): MessageDao = database.messageDao()
fun clearUsersStatus() { fun clearUsersStatus() {
launch(dbContext) { launch(dbContext) {
...@@ -61,7 +70,7 @@ class DatabaseManager(val context: Application, ...@@ -61,7 +70,7 @@ class DatabaseManager(val context: Application,
fun processUsersBatch(users: List<User>) { fun processUsersBatch(users: List<User>) {
launch(dbContext) { launch(dbContext) {
val dao = database.userDao() val dao = userDao()
val list = ArrayList<BaseUserEntity>(users.size) val list = ArrayList<BaseUserEntity>(users.size)
users.forEach { user -> users.forEach { user ->
user.toEntity()?.let { entity -> user.toEntity()?.let { entity ->
...@@ -73,7 +82,10 @@ class DatabaseManager(val context: Application, ...@@ -73,7 +82,10 @@ class DatabaseManager(val context: Application,
} }
} }
fun processStreamBatch(batch: List<StreamMessage<BaseRoom>>) { /*
* Creates a list of data base operations
*/
fun processChatRoomsBatch(batch: List<StreamMessage<BaseRoom>>) {
launch(dbContext) { launch(dbContext) {
val toRemove = HashSet<String>() val toRemove = HashSet<String>()
val toInsert = ArrayList<ChatRoomEntity>(batch.size / 2) val toInsert = ArrayList<ChatRoomEntity>(batch.size / 2)
...@@ -100,7 +112,7 @@ class DatabaseManager(val context: Application, ...@@ -100,7 +112,7 @@ class DatabaseManager(val context: Application,
Timber.d("Running ChatRooms transaction: remove: $toRemove - insert: $toInsert - update: $filteredUpdate") Timber.d("Running ChatRooms transaction: remove: $toRemove - insert: $toInsert - update: $filteredUpdate")
chatRoomDao().update(toRemove.toList(), filteredInsert, filteredUpdate) chatRoomDao().update(filteredInsert, filteredUpdate, toRemove.toList())
} catch (ex: Exception) { } catch (ex: Exception) {
Timber.d(ex, "Error updating chatrooms") Timber.d(ex, "Error updating chatrooms")
} }
...@@ -129,6 +141,118 @@ class DatabaseManager(val context: Application, ...@@ -129,6 +141,118 @@ class DatabaseManager(val context: Application,
} }
} }
fun processMessagesBatch(messages: List<Message>) {
launch(dbContext) {
val dao = messageDao()
val list = mutableListOf<Pair<MessageEntity, List<BaseMessageEntity>>>()
messages.forEach { message ->
val pair = createMessageEntities(message)
list.add(pair)
}
dao.insert(list)
}
}
private suspend fun createMessageEntities(message: Message): Pair<MessageEntity, List<BaseMessageEntity>> {
val messageEntity = message.toEntity()
val list = mutableListOf<BaseMessageEntity>()
//createAttachments(message)?.let {}
createFavoriteRelations(message)?.let { list.addAll(it) }
createMentionRelations(message)?.let { list.addAll(it) }
createChannelRelations(message)?.let { list.addAll(it) }
createUrlEntities(message)?.let { list.addAll(it) }
createReactions(message)?.let { list.addAll(it) }
insertUserIfMissing(message.sender)
insertUserIfMissing(message.editedBy)
return Pair(messageEntity, list)
}
private fun createReactions(message: Message): List<BaseMessageEntity>? {
if (message.reactions == null || message.reactions!!.isEmpty()) {
return null
}
val list = mutableListOf<BaseMessageEntity>()
message.reactions!!.keys.forEach { reaction ->
list.add(ReactionEntity(reaction))
val users = message.reactions!![reaction]
users?.size?.let { size ->
list.add(ReactionMessageRelation(reaction, message.id, size))
}
}
return list
}
private fun createUrlEntities(message: Message): List<BaseMessageEntity>? {
if (message.urls == null || message.urls!!.isEmpty()) {
return null
}
val list = mutableListOf<UrlEntity>()
message.urls!!.forEach { url ->
list.add(UrlEntity(message.id, url.url, url.parsedUrl?.host, url.meta?.title,
url.meta?.description, url.meta?.imageUrl))
}
return list
}
private fun createChannelRelations(message: Message): List<BaseMessageEntity>? {
if (message.channels == null || message.channels!!.isEmpty()) {
return null
}
val list = mutableListOf<MessageChannelsRelation>()
message.channels!!.forEach { channel ->
list.add(MessageChannelsRelation(message.id, channel.id, channel.name))
}
return list
}
private suspend fun createMentionRelations(message: Message): List<BaseMessageEntity>? {
if (message.mentions == null || message.mentions!!.isEmpty()) {
return null
}
val list = mutableListOf<MessageMentionsRelation>()
message.mentions!!.filterNot { user -> user.id.isNullOrEmpty() }.forEach { mention ->
insertUserIfMissing(mention)
list.add(MessageMentionsRelation(message.id, mention.id!!))
}
return list
}
private suspend fun createFavoriteRelations(message: Message): List<BaseMessageEntity>? {
if (message.starred == null || message.starred!!.isEmpty()) {
return null
}
val list = mutableListOf<MessageFavoritesRelation>()
message.starred!!.filterNot { user -> user.id.isNullOrEmpty() }.forEach { userId ->
insertUserIfMissing(userId)
list.add(MessageFavoritesRelation(message.id, userId.id!!))
}
return list
}
private fun createAttachments(message: Message): List<BaseMessageEntity>? {
if (message.attachments == null || message.attachments!!.isEmpty()) {
return null
}
message.attachments!!.forEach {
}
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
private suspend fun createUpdates(): List<ChatRoomEntity> { private suspend fun createUpdates(): List<ChatRoomEntity> {
val list = ArrayList<ChatRoomEntity>() val list = ArrayList<ChatRoomEntity>()
...@@ -183,21 +307,8 @@ class DatabaseManager(val context: Application, ...@@ -183,21 +307,8 @@ class DatabaseManager(val context: Application,
with(data) { with(data) {
val chatRoom = current.chatRoom val chatRoom = current.chatRoom
lastMessage?.sender?.let { user -> insertUserIfMissing(lastMessage?.sender)
user.id?.let { id -> insertUserIfMissing(user)
if (findUser(id) == null) {
Timber.d("Missing last message user, inserting: $id")
insert(UserEntity(id, user.username, user.name))
}
}
}
user?.id?.let { id ->
if (findUser(id) == null) {
Timber.d("Missing owner user, inserting: $id")
insert(UserEntity(id, user!!.username, user!!.name))
}
}
chatRoom.copy( chatRoom.copy(
name = name ?: chatRoom.name, name = name ?: chatRoom.name,
...@@ -238,13 +349,9 @@ class DatabaseManager(val context: Application, ...@@ -238,13 +349,9 @@ class DatabaseManager(val context: Application,
null null
} }
if (userId != null && findUser(userId) == null) { insertUserIfMissing(userId)
Timber.d("Missing user, inserting: $userId")
insert(UserEntity(userId))
}
val chatRoom = current.chatRoom val chatRoom = current.chatRoom
chatRoom.copy( chatRoom.copy(
id = roomId, id = roomId,
subscriptionId = id, subscriptionId = id,
...@@ -303,28 +410,9 @@ class DatabaseManager(val context: Application, ...@@ -303,28 +410,9 @@ class DatabaseManager(val context: Application,
null null
} }
if (userId != null && findUser(userId) == null) { insertUserIfMissing(userId)
Timber.d("Missing user, inserting: $userId") insertUserIfMissing(room.lastMessage?.sender)
insert(UserEntity(userId)) insertUserIfMissing(room.user)
}
room.lastMessage?.sender?.let { user ->
user.id?.let { id ->
if (findUser(id) == null) {
Timber.d("Missing last message user, inserting: $id")
insert(UserEntity(id, user.username, user.name))
}
}
}
room.user?.let { user ->
user.id?.let { id ->
if (findUser(id) == null) {
Timber.d("Missing owner user, inserting: $id")
insert(UserEntity(id, user.username, user.name))
}
}
}
return ChatRoomEntity( return ChatRoomEntity(
id = room.id, id = room.id,
...@@ -360,21 +448,8 @@ class DatabaseManager(val context: Application, ...@@ -360,21 +448,8 @@ class DatabaseManager(val context: Application,
insert(UserEntity(userId)) insert(UserEntity(userId))
} }
lastMessage?.sender?.let { user -> insertUserIfMissing(lastMessage?.sender)
user.id?.let { id -> insertUserIfMissing(user)
if (findUser(id) == null) {
Timber.d("Missing last message user, inserting: $id")
insert(UserEntity(id, user.username, user.name))
}
}
}
user?.id?.let { id ->
if (findUser(id) == null) {
Timber.d("Missing owner user, inserting: $id")
insert(UserEntity(id, user?.username, user?.name))
}
}
return ChatRoomEntity( return ChatRoomEntity(
id = id, id = id,
...@@ -415,6 +490,20 @@ class DatabaseManager(val context: Application, ...@@ -415,6 +490,20 @@ class DatabaseManager(val context: Application,
} }
} }
private suspend fun insertUserIfMissing(id: String?) {
if (id != null && findUser(id) == null) {
Timber.d("Missing user, inserting: $id")
insert(UserEntity(id))
}
}
private suspend fun insertUserIfMissing(user: SimpleUser?) {
if (user?.id != null && findUser(user.id!!) == null) {
Timber.d("Missing user, inserting: ${user.id}")
insert(UserEntity(user.id!!, user.username, user.name))
}
}
fun findUser(userId: String): String? = userDao().findUser(userId) fun findUser(userId: String): String? = userDao().findUser(userId)
} }
......
package chat.rocket.android.db
import chat.rocket.android.infrastructure.MessagesRepository
import chat.rocket.core.model.Message
class DatabaseMessagesRepository(val dbManager: DatabaseManager) : MessagesRepository {
override fun saveMessage(message: Message) {
saveMessages(listOf(message))
}
override fun saveMessages(messages: List<Message>) {
messages.forEach { message ->
}
}
}
\ No newline at end of file
package chat.rocket.android.db
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import chat.rocket.android.db.model.AttachmentEntity
import chat.rocket.android.db.model.AttachmentFieldEntity
import chat.rocket.android.db.model.BaseMessageEntity
import chat.rocket.android.db.model.MessageChannelsRelation
import chat.rocket.android.db.model.MessageEntity
import chat.rocket.android.db.model.MessageFavoritesRelation
import chat.rocket.android.db.model.MessageMentionsRelation
import chat.rocket.android.db.model.ReactionEntity
import chat.rocket.android.db.model.ReactionMessageRelation
import chat.rocket.android.db.model.UrlEntity
import timber.log.Timber
@Dao
abstract class MessageDao {
@Insert
abstract fun insert(message: MessageEntity)
@Insert
abstract fun insert(relation: MessageFavoritesRelation)
@Insert
abstract fun insert(relation: MessageMentionsRelation)
@Insert
abstract fun insert(relation: MessageChannelsRelation)
@Insert
abstract fun insert(attachment: AttachmentEntity)
@Insert
abstract fun insert(field: AttachmentFieldEntity)
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(reaction: ReactionEntity)
@Insert
abstract fun insert(relation: ReactionMessageRelation)
@Insert
abstract fun insert(url: UrlEntity)
@Query("DELETE FROM messages WHERE id = :id")
abstract fun delete(id: String)
@Transaction
open fun insert(message: MessageEntity, entities: List<BaseMessageEntity>) {
insertInternal(message, entities)
}
private fun insertInternal(message: MessageEntity, entities: List<BaseMessageEntity>) {
Timber.d("Inserting message: ${message.id}, entities: ${entities.size}")
delete(message.id)
insert(message)
entities.forEach { entity ->
insert(entity)
}
}
private fun insert(entity: BaseMessageEntity) {
when(entity) {
is MessageEntity -> insert(entity)
is MessageFavoritesRelation -> insert(entity)
is MessageMentionsRelation -> insert(entity)
is MessageChannelsRelation -> insert(entity)
is AttachmentEntity -> insert(entity)
is AttachmentFieldEntity -> insert(entity)
is ReactionEntity -> insert(entity)
is ReactionMessageRelation -> insert(entity)
is UrlEntity -> insert(entity)
}
}
@Transaction
open fun insert(list: List<Pair<MessageEntity, List<BaseMessageEntity>>>) {
list.forEach { (message, entities) ->
insertInternal(message, entities)
}
}
}
\ No newline at end of file
...@@ -2,29 +2,31 @@ package chat.rocket.android.db ...@@ -2,29 +2,31 @@ package chat.rocket.android.db
import androidx.room.Database import androidx.room.Database
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import androidx.room.migration.Migration import chat.rocket.android.db.model.AttachmentEntity
import androidx.sqlite.db.SupportSQLiteDatabase import chat.rocket.android.db.model.AttachmentFieldEntity
import chat.rocket.android.db.model.ChatRoomEntity import chat.rocket.android.db.model.ChatRoomEntity
import chat.rocket.android.db.model.MessageChannelsRelation
import chat.rocket.android.db.model.MessageEntity
import chat.rocket.android.db.model.MessageFavoritesRelation
import chat.rocket.android.db.model.MessageMentionsRelation
import chat.rocket.android.db.model.ReactionEntity
import chat.rocket.android.db.model.ReactionMessageRelation
import chat.rocket.android.db.model.UrlEntity
import chat.rocket.android.db.model.UserEntity import chat.rocket.android.db.model.UserEntity
@Database( @Database(
entities = [UserEntity::class, ChatRoomEntity::class], entities = [
version = 5, UserEntity::class, ChatRoomEntity::class, MessageEntity::class,
MessageFavoritesRelation::class, MessageMentionsRelation::class,
MessageChannelsRelation::class, AttachmentEntity::class,
AttachmentFieldEntity::class, UrlEntity::class, ReactionEntity::class,
ReactionMessageRelation::class
],
version = 6,
exportSchema = true exportSchema = true
) )
abstract class RCDatabase : RoomDatabase() { abstract class RCDatabase : RoomDatabase() {
abstract fun userDao(): UserDao abstract fun userDao(): UserDao
abstract fun chatRoomDao(): ChatRoomDao abstract fun chatRoomDao(): ChatRoomDao
abstract fun messageDao(): MessageDao
companion object {
@JvmField
val MIGRATION_4_5 = Migration4to5()
}
}
class Migration4to5 : Migration(4, 5) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE INDEX `index_chatrooms_lastMessageUserId` ON `chatrooms` (`lastMessageUserId`)")
}
} }
\ No newline at end of file
package chat.rocket.android.db.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Entity(tableName = "attachments",
foreignKeys = [
ForeignKey(entity = MessageEntity::class, parentColumns = ["id"],
childColumns = ["message_id"], onDelete = ForeignKey.CASCADE)
])
data class AttachmentEntity(
@PrimaryKey
val id: String,
@ColumnInfo(name = "message_id")
val messageId: String,
val title: String?,
val type: String?,
val description: String?,
val text: String?,
@ColumnInfo(name = "author_name")
val authorName: String?,
@ColumnInfo(name = "author_icon")
val authorIcon: String?,
@ColumnInfo(name = "author_link")
val authorLink: String?,
@ColumnInfo(name = "thumb_url")
val thumbUrl: String?,
val color: String?,
@ColumnInfo(name = "title_link")
val titleLink: String?,
@ColumnInfo(name = "title_link_download")
val titleLinkDownload: String?,
@ColumnInfo(name = "image_url")
val imageUrl: String?,
@ColumnInfo(name = "image_type")
val imageType: String?,
@ColumnInfo(name = "image_size")
val imageSize: String?,
@ColumnInfo(name = "video_url")
val videoUrl: String?,
@ColumnInfo(name = "video_type")
val videoType: String?,
@ColumnInfo(name = "video_size")
val videoSize: String?,
@ColumnInfo(name = "audio_url")
val audioUrl: String?,
@ColumnInfo(name = "audio_type")
val audioType: String?,
@ColumnInfo(name = "audio_size")
val audioSize: String?,
@ColumnInfo(name = "message_link")
val messageLink: String?,
val timestamp: Long?
) : BaseMessageEntity
@Entity(tableName = "attachment_fields",
foreignKeys = [
ForeignKey(entity = AttachmentEntity::class, parentColumns = ["id"],
childColumns = ["attachmentId"], onDelete = ForeignKey.CASCADE)
])
data class AttachmentFieldEntity(
val attachmentId: String,
val title: String,
val value: String
) : BaseMessageEntity {
@PrimaryKey(autoGenerate = true)
var id: Long? = null
}
\ No newline at end of file
package chat.rocket.android.db.model
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
interface BaseMessageEntity
@Entity(tableName = "messages",
foreignKeys = [
ForeignKey(entity = UserEntity::class, parentColumns = ["id"], childColumns = ["senderId"]),
ForeignKey(entity = UserEntity::class, parentColumns = ["id"], childColumns = ["editedBy"])
])
data class MessageEntity(
@PrimaryKey val id: String,
val roomId: String,
val message: String,
val timestamp: Long,
val senderId: String?,
val updatedAt: Long?,
val editedAt: Long?,
val editedBy: String?,
val senderAlias: String?,
val avatar: String?,
val type: String?,
val groupable: Boolean = false,
val parseUrls: Boolean = false,
val pinned: Boolean = false,
val role: String?
) : BaseMessageEntity
@Entity(tableName = "message_favorites",
primaryKeys = ["messageId", "userId"],
foreignKeys = [
ForeignKey(entity = MessageEntity::class, parentColumns = ["id"],
childColumns = ["messageId"], onDelete = ForeignKey.CASCADE),
ForeignKey(entity = UserEntity::class, parentColumns = ["id"], childColumns = ["userId"])
])
data class MessageFavoritesRelation(
val messageId: String,
val userId: String
) : BaseMessageEntity
@Entity(tableName = "message_mentions",
primaryKeys = ["messageId", "userId"],
foreignKeys = [
ForeignKey(entity = MessageEntity::class, parentColumns = ["id"],
childColumns = ["messageId"], onDelete = ForeignKey.CASCADE),
ForeignKey(entity = UserEntity::class, parentColumns = ["id"], childColumns = ["userId"])
])
data class MessageMentionsRelation(
val messageId: String,
val userId: String
) : BaseMessageEntity
@Entity(tableName = "message_channels",
primaryKeys = ["messageId", "roomId"],
foreignKeys = [
ForeignKey(entity = MessageEntity::class, parentColumns = ["id"],
childColumns = ["messageId"], onDelete = ForeignKey.CASCADE)
]
)
data class MessageChannelsRelation(
val messageId: String,
val roomId: String,
val roomName: String?
) : BaseMessageEntity
package chat.rocket.android.db.model
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
@Entity(tableName = "reactions")
data class ReactionEntity(
@PrimaryKey val reaction: String
) : BaseMessageEntity
@Entity(tableName = "reactions_message_relations",
foreignKeys = [
ForeignKey(entity = ReactionEntity::class, parentColumns = ["reaction"],
childColumns = ["reactionId"]),
ForeignKey(entity = MessageEntity::class, parentColumns = ["id"],
childColumns = ["messageId"], onDelete = ForeignKey.CASCADE)
],
indices = [
Index(value = ["messageId"])
]
)
data class ReactionMessageRelation(
val reactionId: String,
val messageId: String,
val count: Int
) : BaseMessageEntity {
@PrimaryKey(autoGenerate = true)
var id: Long? = null
}
\ No newline at end of file
package chat.rocket.android.db.model
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
@Entity(tableName = "urls",
foreignKeys = [
ForeignKey(entity = MessageEntity::class, parentColumns = ["id"],
childColumns = ["messageId"], onDelete = ForeignKey.CASCADE)
],
indices = [
Index(value = ["messageId"])
])
data class UrlEntity(
val messageId: String,
val url: String,
val hostname: String?,
val title: String?,
val description: String?,
val imageUrl: String?
) : BaseMessageEntity {
@PrimaryKey(autoGenerate = true)
var id: Long? = null
}
\ No newline at end of file
package chat.rocket.android.infrastructure
import chat.rocket.core.model.Message
interface MessagesRepository {
fun saveMessage(message: Message)
fun saveMessages(messages: List<Message>)
}
\ No newline at end of file
...@@ -32,7 +32,6 @@ import kotlinx.coroutines.experimental.selects.select ...@@ -32,7 +32,6 @@ import kotlinx.coroutines.experimental.selects.select
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
import kotlin.coroutines.experimental.CoroutineContext import kotlin.coroutines.experimental.CoroutineContext
import kotlin.math.absoluteValue
class ConnectionManager( class ConnectionManager(
internal val client: RocketChatClient, internal val client: RocketChatClient,
...@@ -55,6 +54,7 @@ class ConnectionManager( ...@@ -55,6 +54,7 @@ class ConnectionManager(
private val activeUsersContext = newSingleThreadContext("activeUsersContext") private val activeUsersContext = newSingleThreadContext("activeUsersContext")
private val roomsContext = newSingleThreadContext("roomsContext") private val roomsContext = newSingleThreadContext("roomsContext")
private val messagesContext = newSingleThreadContext("messagesContext")
fun connect() { fun connect() {
if (connectJob?.isActive == true && (state !is State.Disconnected)) { if (connectJob?.isActive == true && (state !is State.Disconnected)) {
...@@ -125,7 +125,13 @@ class ConnectionManager( ...@@ -125,7 +125,13 @@ class ConnectionManager(
val roomsActor = createBatchActor<StreamMessage<BaseRoom>>(roomsContext, parent = connectJob, val roomsActor = createBatchActor<StreamMessage<BaseRoom>>(roomsContext, parent = connectJob,
maxSize = 10) { batch -> maxSize = 10) { batch ->
Timber.d("processing Stream batch: ${batch.size} - $batch") Timber.d("processing Stream batch: ${batch.size} - $batch")
dbManager.processStreamBatch(batch) dbManager.processChatRoomsBatch(batch)
}
val messagesActor = createBatchActor<Message>(messagesContext, parent = connectJob,
maxSize = 100, maxTime = 300) { messages ->
Timber.d("Processing Messages batch: ${messages.size}")
dbManager.processMessagesBatch(messages)
} }
// stream-notify-user - ${userId}/rooms-changed // stream-notify-user - ${userId}/rooms-changed
...@@ -148,6 +154,7 @@ class ConnectionManager( ...@@ -148,6 +154,7 @@ class ConnectionManager(
launch(parent = connectJob) { launch(parent = connectJob) {
for (message in client.messagesChannel) { for (message in client.messagesChannel) {
Timber.d("Received new Message for room ${message.roomId}") Timber.d("Received new Message for room ${message.roomId}")
messagesActor.send(message)
val channel = roomMessagesChannels[message.roomId] val channel = roomMessagesChannels[message.roomId]
channel?.send(message) channel?.send(message)
} }
...@@ -164,12 +171,9 @@ class ConnectionManager( ...@@ -164,12 +171,9 @@ class ConnectionManager(
} }
} }
var totalUsers = 0
// activeUsers // activeUsers
launch(parent = connectJob) { launch(parent = connectJob) {
for (user in client.activeUsersChannel) { for (user in client.activeUsersChannel) {
totalUsers++
//Timber.d("Got activeUsers: $totalUsers")
userActor.send(user) userActor.send(user)
} }
} }
......
package chat.rocket.android.util.extensions package chat.rocket.android.util.extensions
import chat.rocket.android.db.model.MessageEntity
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.retryIO import chat.rocket.android.util.retryIO
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.registerPushToken import chat.rocket.core.internal.rest.registerPushToken
import chat.rocket.core.model.Message
import chat.rocket.core.model.asString
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
...@@ -26,4 +29,24 @@ suspend fun RocketChatClientFactory.registerPushToken( ...@@ -26,4 +29,24 @@ suspend fun RocketChatClientFactory.registerPushToken(
} }
} }
} }
}
fun Message.toEntity(): MessageEntity {
return MessageEntity(
id = id,
roomId = roomId,
message = message,
timestamp = timestamp,
senderId = sender?.id,
updatedAt = updatedAt,
editedAt = editedAt,
editedBy = editedBy?.id,
senderAlias = senderAlias,
avatar = avatar,
type = type.asString(),
groupable = groupable,
parseUrls = parseUrls,
pinned = pinned,
role = role
)
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment