Unverified Commit 1fd253cc authored by Filipe Brito's avatar Filipe Brito Committed by GitHub

Merge pull request #2287 from RobotJINI/develop

[IMPROVEMENT] Immediate message loading
parents 475f195e ccec99f5
...@@ -117,7 +117,8 @@ class ChatRoomPresenter @Inject constructor( ...@@ -117,7 +117,8 @@ class ChatRoomPresenter @Inject constructor(
private var chatRoomId: String? = null private var chatRoomId: String? = null
private lateinit var chatRoomType: String private lateinit var chatRoomType: String
private var chatIsBroadcast: Boolean = false private lateinit var chatRoomName: String
private var isBroadcast: Boolean = false
private var chatRoles = emptyList<ChatRoomRole>() private var chatRoles = emptyList<ChatRoomRole>()
private val stateChannel = Channel<State>() private val stateChannel = Channel<State>()
private var typingStatusSubscriptionId: String? = null private var typingStatusSubscriptionId: String? = null
...@@ -136,34 +137,30 @@ class ChatRoomPresenter @Inject constructor( ...@@ -136,34 +137,30 @@ class ChatRoomPresenter @Inject constructor(
draftKey = "${currentServer}_${LocalRepository.DRAFT_KEY}$roomId" draftKey = "${currentServer}_${LocalRepository.DRAFT_KEY}$roomId"
chatRoomId = roomId chatRoomId = roomId
chatRoomType = roomType chatRoomType = roomType
GlobalScope.launch(Dispatchers.IO + strategy.jobs) { chatRoomName = roomName
try {
chatRoles = if (roomTypeOf(roomType) !is RoomType.DirectMessage) {
client.chatRoomRoles(roomType = roomTypeOf(roomType), roomName = roomName)
} else {
emptyList()
}
} catch (ex: Exception) {
Timber.e(ex)
chatRoles = emptyList() chatRoles = emptyList()
} finally { var canModerate = isOwnerOrMod()
// User has at least an 'owner' or 'moderator' role.
val canModerate = isOwnerOrMod() GlobalScope.launch(Dispatchers.IO + strategy.jobs) {
// Can post anyway if has the 'post-readonly' permission on server. // Can post anyway if has the 'post-readonly' permission on server.
val room = dbManager.getRoom(roomId) val room = dbManager.getRoom(roomId)
room?.let { room?.let {
chatIsBroadcast = it.chatRoom.broadcast ?: false isBroadcast = it.chatRoom.broadcast ?: false
val roomUiModel = roomMapper.map(it, true) val roomUiModel = roomMapper.map(it, true)
launchUI(strategy) { launchUI(strategy) {
view.onRoomUpdated(roomUiModel = roomUiModel.copy( view.onRoomUpdated(
broadcast = chatIsBroadcast, roomUiModel = roomUiModel.copy(
broadcast = isBroadcast,
canModerate = canModerate, canModerate = canModerate,
writable = roomUiModel.writable || canModerate writable = roomUiModel.writable || canModerate
)) )
)
} }
} }
loadMessages(roomId, roomType, clearDataSet = true) loadMessages(roomId, chatRoomType, clearDataSet = true)
loadActiveMembers(roomId, chatRoomType, filterSelfOut = true)
chatRoomMessage?.let { messageHelper.messageIdFromPermalink(it) } chatRoomMessage?.let { messageHelper.messageIdFromPermalink(it) }
?.let { messageId -> ?.let { messageId ->
val name = messageHelper.roomNameFromPermalink(chatRoomMessage) val name = messageHelper.roomNameFromPermalink(chatRoomMessage)
...@@ -174,9 +171,43 @@ class ChatRoomPresenter @Inject constructor( ...@@ -174,9 +171,43 @@ class ChatRoomPresenter @Inject constructor(
true true
) )
} }
/*FIXME: Get chat role can cause unresponsive problems especially on slower connections
We are updating the room again after the first step so that initial messages
get loaded in and the system appears more responsive. Something should be
done to either fix the load in speed of moderator roles or store the
information locally*/
if (getChatRole()) {
canModerate = isOwnerOrMod()
if (canModerate) {
//FIXME: add this in when moderator page is actually created
//view.updateModeration()
}
}
subscribeRoomChanges() subscribeRoomChanges()
} }
} }
private suspend fun getChatRole(): Boolean {
try {
if (roomTypeOf(chatRoomType) !is RoomType.DirectMessage) {
chatRoles = withContext(Dispatchers.IO + strategy.jobs) {
client.chatRoomRoles(
roomType = roomTypeOf(chatRoomType),
roomName = chatRoomName
)
}
return true
} else {
chatRoles = emptyList()
}
} catch (ex: Exception) {
Timber.e(ex)
chatRoles = emptyList()
}
return false
} }
private suspend fun subscribeRoomChanges() { private suspend fun subscribeRoomChanges() {
...@@ -185,7 +216,12 @@ class ChatRoomPresenter @Inject constructor( ...@@ -185,7 +216,12 @@ class ChatRoomPresenter @Inject constructor(
manager.addRoomChannel(it, roomChangesChannel) manager.addRoomChannel(it, roomChangesChannel)
for (room in roomChangesChannel) { for (room in roomChangesChannel) {
dbManager.getRoom(room.id)?.let { chatRoom -> dbManager.getRoom(room.id)?.let { chatRoom ->
view.onRoomUpdated(roomMapper.map(chatRoom = chatRoom, showLastMessage = true)) view.onRoomUpdated(
roomMapper.map(
chatRoom = chatRoom,
showLastMessage = true
)
)
} }
} }
} }
...@@ -210,8 +246,8 @@ class ChatRoomPresenter @Inject constructor( ...@@ -210,8 +246,8 @@ class ChatRoomPresenter @Inject constructor(
) { ) {
this.chatRoomId = chatRoomId this.chatRoomId = chatRoomId
this.chatRoomType = chatRoomType this.chatRoomType = chatRoomType
launchUI(strategy) {
view.showLoading() GlobalScope.launch(Dispatchers.IO + strategy.jobs) {
try { try {
if (offset == 0L) { if (offset == 0L) {
// FIXME - load just 50 messages from DB to speed up. We will reload from Network after that // FIXME - load just 50 messages from DB to speed up. We will reload from Network after that
...@@ -221,7 +257,7 @@ class ChatRoomPresenter @Inject constructor( ...@@ -221,7 +257,7 @@ class ChatRoomPresenter @Inject constructor(
localMessages, RoomUiModel( localMessages, RoomUiModel(
roles = chatRoles, roles = chatRoles,
// FIXME: Why are we fixing isRoom attribute to true here? // FIXME: Why are we fixing isRoom attribute to true here?
isBroadcast = chatIsBroadcast, isRoom = true isBroadcast = isBroadcast, isRoom = true
) )
) )
lastMessageId = localMessages.firstOrNull()?.id lastMessageId = localMessages.firstOrNull()?.id
...@@ -248,8 +284,6 @@ class ChatRoomPresenter @Inject constructor( ...@@ -248,8 +284,6 @@ class ChatRoomPresenter @Inject constructor(
}.ifNull { }.ifNull {
view.showGenericErrorMessage() view.showGenericErrorMessage()
} }
} finally {
view.hideLoading()
} }
subscribeTypingStatus() subscribeTypingStatus()
...@@ -284,7 +318,7 @@ class ChatRoomPresenter @Inject constructor( ...@@ -284,7 +318,7 @@ class ChatRoomPresenter @Inject constructor(
view.showMessages( view.showMessages(
mapper.map( mapper.map(
messages, messages,
RoomUiModel(roles = chatRoles, isBroadcast = chatIsBroadcast, isRoom = true) RoomUiModel(roles = chatRoles, isBroadcast = isBroadcast, isRoom = true)
), ),
clearDataSet clearDataSet
) )
...@@ -300,7 +334,7 @@ class ChatRoomPresenter @Inject constructor( ...@@ -300,7 +334,7 @@ class ChatRoomPresenter @Inject constructor(
view.showSearchedMessages( view.showSearchedMessages(
mapper.map( mapper.map(
messages, messages,
RoomUiModel(chatRoles, chatIsBroadcast, true) RoomUiModel(chatRoles, isBroadcast, true)
) )
) )
} catch (ex: Exception) { } catch (ex: Exception) {
...@@ -332,7 +366,11 @@ class ChatRoomPresenter @Inject constructor( ...@@ -332,7 +366,11 @@ class ChatRoomPresenter @Inject constructor(
timestamp = Instant.now().toEpochMilli(), timestamp = Instant.now().toEpochMilli(),
sender = SimpleUser(user?.id, user?.username ?: username, user?.name), sender = SimpleUser(user?.id, user?.username ?: username, user?.name),
attachments = null, attachments = null,
avatar = currentServer.avatarUrl(username!!, token?.userId, token?.authToken), avatar = currentServer.avatarUrl(
username!!,
token?.userId,
token?.authToken
),
channels = null, channels = null,
editedAt = null, editedAt = null,
editedBy = null, editedBy = null,
...@@ -354,7 +392,7 @@ class ChatRoomPresenter @Inject constructor( ...@@ -354,7 +392,7 @@ class ChatRoomPresenter @Inject constructor(
view.showNewMessage( view.showNewMessage(
mapper.map( mapper.map(
newMessage, newMessage,
RoomUiModel(roles = chatRoles, isBroadcast = chatIsBroadcast) RoomUiModel(roles = chatRoles, isBroadcast = isBroadcast)
), false ), false
) )
client.sendMessage(id, chatRoomId, text) client.sendMessage(id, chatRoomId, text)
...@@ -605,16 +643,21 @@ class ChatRoomPresenter @Inject constructor( ...@@ -605,16 +643,21 @@ class ChatRoomPresenter @Inject constructor(
Timber.d("History: $messages") Timber.d("History: $messages")
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,
isBroadcast = chatIsBroadcast, isBroadcast = isBroadcast,
// FIXME: Why are we fixing isRoom attribute to true here? // FIXME: Why are we fixing isRoom attribute to true here?
isRoom = true isRoom = true
)) )
)
messagesRepository.saveAll(messages.result) messagesRepository.saveAll(messages.result)
//if success - saving last synced time //if success - saving last synced time
//assume that BE returns ordered messages, the first message is the latest one //assume that BE returns ordered messages, the first message is the latest one
messagesRepository.saveLastSyncDate(chatRoomId, messages.result.first().timestamp) messagesRepository.saveLastSyncDate(
chatRoomId,
messages.result.first().timestamp
)
launchUI(strategy) { launchUI(strategy) {
view.showNewMessage(models, true) view.showNewMessage(models, true)
...@@ -692,7 +735,7 @@ class ChatRoomPresenter @Inject constructor( ...@@ -692,7 +735,7 @@ class ChatRoomPresenter @Inject constructor(
quotedMessage = mapper.map( quotedMessage = mapper.map(
message, RoomUiModel( message, RoomUiModel(
roles = chatRoles, roles = chatRoles,
isBroadcast = chatIsBroadcast isBroadcast = isBroadcast
) )
).last().preview?.message ?: "" ).last().preview?.message ?: ""
) )
...@@ -796,13 +839,15 @@ class ChatRoomPresenter @Inject constructor( ...@@ -796,13 +839,15 @@ class ChatRoomPresenter @Inject constructor(
} }
} }
fun loadActiveMembers( suspend fun loadActiveMembers(
chatRoomId: String, chatRoomId: String,
chatRoomType: String, chatRoomType: String,
offset: Long = 0, offset: Long = 0,
filterSelfOut: Boolean = false filterSelfOut: Boolean = false
) { ) {
launchUI(strategy) { val activeUsers = mutableListOf<PeopleSuggestionUiModel>()
withContext(Dispatchers.IO + strategy.jobs) {
try { try {
val members = retryIO("getMembers($chatRoomId, $chatRoomType, $offset)") { val members = retryIO("getMembers($chatRoomId, $chatRoomType, $offset)") {
client.getMembers(chatRoomId, roomTypeOf(chatRoomType), offset, 50).result client.getMembers(chatRoomId, roomTypeOf(chatRoomType), offset, 50).result
...@@ -813,12 +858,12 @@ class ChatRoomPresenter @Inject constructor( ...@@ -813,12 +858,12 @@ class ChatRoomPresenter @Inject constructor(
// Take at most the 100 most recent messages distinguished by user. Can return less. // Take at most the 100 most recent messages distinguished by user. Can return less.
val recentMessages = messagesRepository.getRecentMessages(chatRoomId, 100) val recentMessages = messagesRepository.getRecentMessages(chatRoomId, 100)
.filterNot { filterSelfOut && it.sender?.username == self } .filterNot { filterSelfOut && it.sender?.username == self }
val activeUsers = mutableListOf<PeopleSuggestionUiModel>()
recentMessages.forEach { recentMessages.forEach {
val sender = it.sender val sender = it.sender
val username = sender?.username ?: "" val username = sender?.username ?: ""
val name = sender?.name ?: "" val name = sender?.name ?: ""
val avatarUrl = currentServer.avatarUrl(username, token?.userId, token?.authToken) val avatarUrl =
currentServer.avatarUrl(username, token?.userId, token?.authToken)
val found = members.firstOrNull { member -> member.username == username } val found = members.firstOrNull { member -> member.username == username }
val status = if (found != null) found.status else UserStatus.Offline() val status = if (found != null) found.status else UserStatus.Offline()
val searchList = mutableListOf(username, name) val searchList = mutableListOf(username, name)
...@@ -839,7 +884,8 @@ class ChatRoomPresenter @Inject constructor( ...@@ -839,7 +884,8 @@ class ChatRoomPresenter @Inject constructor(
activeUsers.addAll(others.map { activeUsers.addAll(others.map {
val username = it.username ?: "" val username = it.username ?: ""
val name = it.name ?: "" val name = it.name ?: ""
val avatarUrl = currentServer.avatarUrl(username, token?.userId, token?.authToken) val avatarUrl =
currentServer.avatarUrl(username, token?.userId, token?.authToken)
val searchList = mutableListOf(username, name) val searchList = mutableListOf(username, name)
PeopleSuggestionUiModel( PeopleSuggestionUiModel(
avatarUrl, avatarUrl,
...@@ -852,11 +898,13 @@ class ChatRoomPresenter @Inject constructor( ...@@ -852,11 +898,13 @@ class ChatRoomPresenter @Inject constructor(
) )
}) })
view.populatePeopleSuggestions(activeUsers)
} catch (e: RocketChatException) { } catch (e: RocketChatException) {
Timber.e(e) Timber.e(e)
} }
} }
launchUI(strategy) {
view.populatePeopleSuggestions(activeUsers)
}
} }
fun spotlight(query: String, @AutoCompleteType type: Int, filterSelfOut: Boolean = false) { fun spotlight(query: String, @AutoCompleteType type: Int, filterSelfOut: Boolean = false) {
...@@ -969,7 +1017,8 @@ class ChatRoomPresenter @Inject constructor( ...@@ -969,7 +1017,8 @@ class ChatRoomPresenter @Inject constructor(
} }
// TODO: move this to new interactor or FetchChatRoomsInteractor? // TODO: move this to new interactor or FetchChatRoomsInteractor?
private suspend fun getChatRoomsAsync(name: String? = null): List<ChatRoom> = withContext(Dispatchers.IO) { private suspend fun getChatRoomsAsync(name: String? = null): List<ChatRoom> =
withContext(Dispatchers.IO) {
retryDB("getAllSync()") { retryDB("getAllSync()") {
dbManager.chatRoomDao().getAllSync().filter { dbManager.chatRoomDao().getAllSync().filter {
if (name == null) { if (name == null) {
...@@ -1019,7 +1068,8 @@ class ChatRoomPresenter @Inject constructor( ...@@ -1019,7 +1068,8 @@ class ChatRoomPresenter @Inject constructor(
val canPost = permissions.canPostToReadOnlyChannels() val canPost = permissions.canPostToReadOnlyChannels()
dbManager.getRoom(chatRoomId)?.let { dbManager.getRoom(chatRoomId)?.let {
val roomUiModel = roomMapper.map(it, true).copy( val roomUiModel = roomMapper.map(it, true).copy(
writable = canPost) writable = canPost
)
view.onJoined(roomUiModel = roomUiModel) view.onJoined(roomUiModel = roomUiModel)
view.onRoomUpdated(roomUiModel = roomUiModel) view.onRoomUpdated(roomUiModel = roomUiModel)
} }
...@@ -1280,7 +1330,7 @@ class ChatRoomPresenter @Inject constructor( ...@@ -1280,7 +1330,7 @@ class ChatRoomPresenter @Inject constructor(
launchUI(strategy) { launchUI(strategy) {
val viewModelStreamedMessage = mapper.map( val viewModelStreamedMessage = mapper.map(
streamedMessage, RoomUiModel( streamedMessage, RoomUiModel(
roles = chatRoles, isBroadcast = chatIsBroadcast, isRoom = true roles = chatRoles, isBroadcast = isBroadcast, isRoom = true
) )
) )
val roomMessages = messagesRepository.getByRoomId(streamedMessage.roomId) val roomMessages = messagesRepository.getByRoomId(streamedMessage.roomId)
......
...@@ -429,25 +429,12 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -429,25 +429,12 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
} }
if (recycler_view.adapter == null) {
recycler_view.adapter = adapter
if (dataSet.size >= 30) {
recycler_view.addOnScrollListener(endlessRecyclerViewScrollListener)
}
recycler_view.addOnLayoutChangeListener(layoutChangeListener)
recycler_view.addOnScrollListener(onScrollListener)
// Load just once, on the first page...
presenter.loadActiveMembers(chatRoomId, chatRoomType, filterSelfOut = true)
}
val oldMessagesCount = adapter.itemCount val oldMessagesCount = adapter.itemCount
adapter.appendData(dataSet) adapter.appendData(dataSet)
if (oldMessagesCount == 0 && dataSet.isNotEmpty()) { if (oldMessagesCount == 0 && dataSet.isNotEmpty()) {
recycler_view.scrollToPosition(0) recycler_view.scrollToPosition(0)
verticalScrollOffset.set(0) verticalScrollOffset.set(0)
} }
presenter.loadActiveMembers(chatRoomId, chatRoomType, filterSelfOut = true)
empty_chat_view.isVisible = adapter.itemCount == 0 empty_chat_view.isVisible = adapter.itemCount == 0
dismissEmojiKeyboard() dismissEmojiKeyboard()
} }
...@@ -469,12 +456,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -469,12 +456,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
setupMessageComposer(roomUiModel) setupMessageComposer(roomUiModel)
isBroadcastChannel = roomUiModel.broadcast isBroadcastChannel = roomUiModel.broadcast
isFavorite = roomUiModel.favorite.orFalse() isFavorite = roomUiModel.favorite.orFalse()
if (isBroadcastChannel && !roomUiModel.canModerate) { disableMenu = (roomUiModel.broadcast && !roomUiModel.canModerate)
disableMenu = true
activity?.invalidateOptionsMenu() activity?.invalidateOptionsMenu()
} }
} }
}
override fun sendMessage(text: String) { override fun sendMessage(text: String) {
ui { ui {
...@@ -807,7 +792,12 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -807,7 +792,12 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
presenter.loadMessages(chatRoomId, chatRoomType, page * 30L) presenter.loadMessages(chatRoomId, chatRoomType, page * 30L)
} }
} }
recycler_view.adapter = adapter
recycler_view.addOnScrollListener(fabScrollListener) recycler_view.addOnScrollListener(fabScrollListener)
recycler_view.addOnScrollListener(endlessRecyclerViewScrollListener)
recycler_view.addOnLayoutChangeListener(layoutChangeListener)
recycler_view.addOnScrollListener(onScrollListener)
} }
private fun setupFab() { private fun setupFab() {
......
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