ChatRoomsPresenter.kt 11.8 KB
Newer Older
1 2 3
package chat.rocket.android.chatrooms.presentation

import chat.rocket.android.core.lifecycle.CancelStrategy
Filipe de Lima Brito's avatar
Filipe de Lima Brito committed
4
import chat.rocket.android.main.presentation.MainNavigator
5
import chat.rocket.android.server.domain.*
6 7 8 9
import chat.rocket.android.server.infraestructure.ConnectionManager
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.chatRooms
import chat.rocket.android.server.infraestructure.state
10
import chat.rocket.android.util.extensions.launchUI
11
import chat.rocket.common.RocketChatException
12
import chat.rocket.common.model.*
13
import chat.rocket.core.internal.model.Subscription
14 15 16
import chat.rocket.core.internal.realtime.State
import chat.rocket.core.internal.realtime.StreamMessage
import chat.rocket.core.internal.realtime.Type
17
import chat.rocket.core.internal.rest.spotlight
18
import chat.rocket.core.model.ChatRoom
19 20
import chat.rocket.core.model.Room
import kotlinx.coroutines.experimental.*
21
import kotlinx.coroutines.experimental.android.UI
Lucio Maciel's avatar
Lucio Maciel committed
22
import kotlinx.coroutines.experimental.channels.Channel
23
import timber.log.Timber
24 25
import javax.inject.Inject

26 27
class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
                                             private val strategy: CancelStrategy,
Filipe de Lima Brito's avatar
Filipe de Lima Brito committed
28
                                             private val navigator: MainNavigator,
29
                                             private val serverInteractor: GetCurrentServerInteractor,
30 31
                                             private val getChatRoomsInteractor: GetChatRoomsInteractor,
                                             private val saveChatRoomsInteractor: SaveChatRoomsInteractor,
32
                                             private val refreshSettingsInteractor: RefreshSettingsInteractor,
33
                                             settingsRepository: SettingsRepository,
34 35
                                             factory: ConnectionManagerFactory) {
    private val manager: ConnectionManager = factory.create(serverInteractor.get()!!)
36
    private val currentServer = serverInteractor.get()!!
37
    private val client = manager.client
38
    private var reloadJob: Deferred<List<ChatRoom>>? = null
39
    private val settings = settingsRepository.get(currentServer)!!
40

41
    private val subscriptionsChannel = Channel<StreamMessage<BaseRoom>>()
Lucio Maciel's avatar
Lucio Maciel committed
42 43
    private val stateChannel = Channel<State>()

44 45
    private var lastState = manager.state

46
    fun loadChatRooms() {
47
        refreshSettingsInteractor.refreshAsync(currentServer)
48 49
        launchUI(strategy) {
            view.showLoading()
50
            subscribeStatusChange()
51 52 53 54 55 56 57 58
            try {
                view.updateChatRooms(loadRooms())
            } catch (e: RocketChatException) {
                Timber.e(e)
                view.showMessage(e.message!!)
            } finally {
                view.hideLoading()
            }
59
            subscribeRoomUpdates()
60 61
        }
    }
62

63 64 65 66 67 68 69 70 71 72
    fun loadChatRoom(chatRoom: ChatRoom) {
        val roomName = if (chatRoom.type is RoomType.DirectMessage
                && chatRoom.fullName != null
                && settings.useRealName()) {
            chatRoom.fullName!!
        } else {
            chatRoom.name
        }

        navigator.toChatRoom(chatRoom.id, roomName,
73 74 75
                chatRoom.type.toString(), chatRoom.readonly ?: false,
                chatRoom.lastSeen ?: -1,
                chatRoom.open)
76
    }
77 78 79 80 81 82 83 84 85

    /**
     * Gets a [ChatRoom] list from local repository.
     * ChatRooms returned are filtered by name.
     */
    fun chatRoomsByName(name: String) {
        val currentServer = serverInteractor.get()!!
        launchUI(strategy) {
            val roomList = getChatRoomsInteractor.getByName(currentServer, name)
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
            if (roomList.isEmpty()) {
                val (users, rooms) = client.spotlight(name)
                val chatRoomsCombined = mutableListOf<ChatRoom>()
                chatRoomsCombined.addAll(usersToChatRooms(users))
                chatRoomsCombined.addAll(roomsToChatRooms(rooms))
                view.updateChatRooms(chatRoomsCombined)
            } else {
                view.updateChatRooms(roomList)
            }
        }
    }

    private suspend fun usersToChatRooms(users: List<User>): List<ChatRoom> {
        return users.map {
            ChatRoom(it.id, RoomType.DIRECT_MESSAGE, SimpleUser(
                    username = it.username, name = it.name, id = null), it.name ?: "",
                    it.name, false, null, null, null,
                    null, null, false, false, false,
                    0L, null, 0L, null, client
            )
        }
    }

    private suspend fun roomsToChatRooms(rooms: List<Room>): List<ChatRoom> {
        return rooms.map {
            ChatRoom(it.id, it.type, it.user, it.name ?: "",
                    it.fullName, it.readonly, it.updatedAt, null, null,
113
                    it.topic, it.announcement, false, false, false,
114 115
                    0L, null, 0L, it.lastMessage, client
            )
116
        }
117 118
    }

119
    private suspend fun loadRooms(): List<ChatRoom> {
120
        val chatRooms = manager.chatRooms().update
121
        val sortedRooms = sortRooms(chatRooms)
122
        Timber.d("Loaded rooms: ${sortedRooms.size}")
123 124 125 126 127 128 129 130 131 132
        saveChatRoomsInteractor.save(currentServer, sortedRooms)
        return sortedRooms
    }

    private fun sortRooms(chatRooms: List<ChatRoom>): List<ChatRoom> {
        val openChatRooms = getOpenChatRooms(chatRooms)
        return sortChatRooms(openChatRooms)
    }

    private fun updateRooms() {
133
        Timber.d("Updating Rooms")
134 135 136
        launch {
            view.updateChatRooms(getChatRoomsInteractor.get(currentServer))
        }
137 138
    }

139 140 141 142 143
    private fun getOpenChatRooms(chatRooms: List<ChatRoom>): List<ChatRoom> {
        return chatRooms.filter(ChatRoom::open)
    }

    private fun sortChatRooms(chatRooms: List<ChatRoom>): List<ChatRoom> {
144 145
        return chatRooms.sortedByDescending { chatRoom ->
            chatRoom.lastMessage?.timestamp
146 147
        }
    }
148

149 150
    private suspend fun subscribeStatusChange() {
        lastState = manager.state
151
        launch(CommonPool + strategy.jobs) {
152 153 154 155 156
            for (state in stateChannel) {
                Timber.d("Got new state: $state - last: $lastState")
                if (state != lastState) {
                    launch(UI) {
                        view.showConnectionState(state)
157 158
                    }

159 160 161 162 163 164
                    if (state is State.Connected) {
                        reloadRooms()
                        updateRooms()
                    }
                }
                lastState = state
165 166
            }
        }
167
    }
168

169 170
    // TODO - Temporary stuff, remove when adding DB support
    private suspend fun subscribeRoomUpdates() {
171 172 173 174 175 176 177 178 179 180 181
        manager.addStatusChannel(stateChannel)
        manager.addRoomsAndSubscriptionsChannel(subscriptionsChannel)
        launch(CommonPool + strategy.jobs) {
            for (message in subscriptionsChannel) {
                Timber.d("Got message: $message")
                when (message.data) {
                    is Room -> updateRoom(message as StreamMessage<Room>)
                    is Subscription -> updateSubscription(message as StreamMessage<Subscription>)
                }
            }
        }
182 183
    }

184 185
    private suspend fun updateRoom(message: StreamMessage<Room>) {
        Timber.d("Update Room: ${message.type} - ${message.data.id} - ${message.data.name}")
186 187 188 189 190 191 192 193 194 195 196
        when (message.type) {
            Type.Removed -> {
                removeRoom(message.data.id)
            }
            Type.Updated -> {
                updateRoom(message.data)
            }
            Type.Inserted -> {
                // On insertion, just get all chatrooms again, since we can't create one just
                // from a Room
                reloadRooms()
197
            }
198
        }
199

200
        updateRooms()
201 202
    }

203 204
    private suspend fun updateSubscription(message: StreamMessage<Subscription>) {
        Timber.d("Update Subscription: ${message.type} - ${message.data.id} - ${message.data.name}")
205 206 207 208 209 210 211 212 213 214 215
        when (message.type) {
            Type.Removed -> {
                removeRoom(message.data.roomId)
            }
            Type.Updated -> {
                updateSubscription(message.data)
            }
            Type.Inserted -> {
                // On insertion, just get all chatrooms again, since we can't create one just
                // from a Subscription
                reloadRooms()
216
            }
217
        }
218

219
        updateRooms()
220 221 222 223 224 225
    }

    private suspend fun reloadRooms() {
        Timber.d("realoadRooms()")
        reloadJob?.cancel()

226 227 228 229 230 231 232 233 234
        try {
            reloadJob = async(CommonPool + strategy.jobs) {
                delay(1000)
                Timber.d("reloading rooms after wait")
                loadRooms()
            }
            reloadJob?.await()
        } catch (ex: Exception) {
            ex.printStackTrace()
235 236 237 238 239
        }
    }

    // Update a ChatRoom with a Room information
    private fun updateRoom(room: Room) {
240
        Timber.d("Updating Room: ${room.id} - ${room.name}")
241 242 243 244 245 246 247 248 249 250 251
        val chatRooms = getChatRoomsInteractor.get(currentServer).toMutableList()
        val chatRoom = chatRooms.find { chatRoom -> chatRoom.id == room.id }
        chatRoom?.apply {
            val newRoom = ChatRoom(room.id,
                    room.type,
                    room.user ?: user,
                    room.name ?: name,
                    room.fullName ?: fullName,
                    room.readonly,
                    room.updatedAt ?: updatedAt,
                    timestamp,
252
                    lastSeen,
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
                    room.topic,
                    room.announcement,
                    default,
                    open,
                    alert,
                    unread,
                    userMenstions,
                    groupMentions,
                    room.lastMessage,
                    client)
            removeRoom(room.id, chatRooms)
            chatRooms.add(newRoom)
            saveChatRoomsInteractor.save(currentServer, sortRooms(chatRooms))
        }
    }

    // Update a ChatRoom with a Subscription information
    private fun updateSubscription(subscription: Subscription) {
271
        Timber.d("Updating subscrition: ${subscription.id} - ${subscription.name}")
272 273 274 275 276 277 278 279 280 281 282
        val chatRooms = getChatRoomsInteractor.get(currentServer).toMutableList()
        val chatRoom = chatRooms.find { chatRoom -> chatRoom.id == subscription.roomId }
        chatRoom?.apply {
            val newRoom = ChatRoom(subscription.roomId,
                    subscription.type,
                    subscription.user ?: user,
                    subscription.name,
                    subscription.fullName ?: fullName,
                    subscription.readonly ?: readonly,
                    subscription.updatedAt ?: updatedAt,
                    subscription.timestamp ?: timestamp,
283
                    subscription.lastSeen ?: lastSeen,
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
                    topic,
                    announcement,
                    subscription.isDefault,
                    subscription.open,
                    subscription.alert,
                    subscription.unread,
                    subscription.userMentions,
                    subscription.groupMentions,
                    lastMessage,
                    client)
            removeRoom(subscription.roomId, chatRooms)
            chatRooms.add(newRoom)
            saveChatRoomsInteractor.save(currentServer, sortRooms(chatRooms))
        }
    }


    private fun removeRoom(id: String,
                           chatRooms: MutableList<ChatRoom> = getChatRoomsInteractor.get(currentServer).toMutableList()) {
303
        Timber.d("Removing ROOM: $id")
304 305 306 307 308
        synchronized(this) {
            chatRooms.removeAll { chatRoom -> chatRoom.id == id }
        }
        saveChatRoomsInteractor.save(currentServer, sortRooms(chatRooms))
    }
309 310

    fun disconnect() {
311 312
        manager.removeStatusChannel(stateChannel)
        manager.removeRoomsAndSubscriptionsChannel(subscriptionsChannel)
313
    }
314
}