Skip to content
Snippets Groups Projects
Select Git revision
  • 0582dbe472d9d4e26391fb7bb2d1d42e5eba4615
  • 3.0 default protected
  • 2.0
  • mamarley-autocomplete
  • master protected
5 results

Config.php

Blame
  • QuasselViewModel.kt 22.17 KiB
    /*
     * Quasseldroid - Quassel client for Android
     *
     * Copyright (c) 2018 Janne Koschinski
     * Copyright (c) 2018 The Quassel Project
     *
     * This program is free software: you can redistribute it and/or modify it
     * under the terms of the GNU General Public License version 3 as published
     * by the Free Software Foundation.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License along
     * with this program.  If not, see <http://www.gnu.org/licenses/>.
     */
    
    package de.kuschku.quasseldroid.viewmodel
    
    import android.arch.lifecycle.ViewModel
    import de.kuschku.libquassel.connection.ConnectionState
    import de.kuschku.libquassel.protocol.*
    import de.kuschku.libquassel.quassel.BufferInfo
    import de.kuschku.libquassel.quassel.syncables.BufferViewConfig
    import de.kuschku.libquassel.quassel.syncables.IrcChannel
    import de.kuschku.libquassel.quassel.syncables.IrcUser
    import de.kuschku.libquassel.quassel.syncables.Network
    import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
    import de.kuschku.libquassel.session.Backend
    import de.kuschku.libquassel.session.ISession
    import de.kuschku.libquassel.session.SessionManager
    import de.kuschku.libquassel.util.Optional
    import de.kuschku.libquassel.util.flag.and
    import de.kuschku.libquassel.util.flag.hasFlag
    import de.kuschku.libquassel.util.helpers.*
    import de.kuschku.libquassel.util.irc.IrcCaseMappers
    import de.kuschku.quasseldroid.util.helper.combineLatest
    import de.kuschku.quasseldroid.viewmodel.data.*
    import io.reactivex.BackpressureStrategy
    import io.reactivex.Flowable
    import io.reactivex.Observable
    import io.reactivex.subjects.BehaviorSubject
    import io.reactivex.subjects.PublishSubject
    import java.util.concurrent.TimeUnit
    
    class QuasselViewModel : ViewModel() {
      fun resetAccount() {
        buffer.onNext(Int.MAX_VALUE)
        bufferViewConfigId.onNext(Int.MAX_VALUE)
        selectedMessages.onNext(emptyMap())
        expandedMessages.onNext(emptySet())
        recentlySentMessages.onNext(emptyList())
      }
    
      val backendWrapper = BehaviorSubject.createDefault(Observable.empty<Optional<Backend>>())
    
      val selectedMessages = BehaviorSubject.createDefault(emptyMap<MsgId, FormattedMessage>())
      fun selectedMessagesToggle(key: MsgId, value: FormattedMessage): Boolean {
        val set = selectedMessages.value.orEmpty()
        val result = if (set.containsKey(key)) set - key else set + Pair(key, value)
        selectedMessages.onNext(result)
        return result.isNotEmpty()
      }
    
      val expandedMessages = BehaviorSubject.createDefault(emptySet<MsgId>())
    
      val buffer = BehaviorSubject.createDefault(Int.MAX_VALUE)
      val bufferOpened = PublishSubject.create<Unit>()
    
      val bufferViewConfigId = BehaviorSubject.createDefault(Int.MAX_VALUE)
    
      val MAX_RECENT_MESSAGES = 20
      val recentlySentMessages = BehaviorSubject.createDefault(emptyList<CharSequence>())
      fun addRecentlySentMessage(message: CharSequence) {
        recentlySentMessages.onNext(
          listOf(message) + recentlySentMessages.value
            .filter { it != message }
            .take(MAX_RECENT_MESSAGES - 1)
        )
      }
    
      val backend = backendWrapper.switchMap { it }
      val sessionManager = backend.mapMap(Backend::sessionManager)
      val session = sessionManager.mapSwitchMap(SessionManager::session)
      val rpcHandler = session.mapMapNullable(ISession::rpcHandler)
    
      val connectionProgress = sessionManager.mapSwitchMap(SessionManager::connectionProgress)
        .mapOrElse(Triple(ConnectionState.DISCONNECTED, 0, 0))
    
      val bufferViewManager = session.mapMapNullable(ISession::bufferViewManager)
    
      val bufferViewConfig = bufferViewManager.flatMapSwitchMap { manager ->
        bufferViewConfigId.map { id ->
          Optional.ofNullable(manager.bufferViewConfig(id))
        }
      }
    
      val errors = sessionManager.toFlowable(BackpressureStrategy.LATEST).switchMap {
        it.orNull()?.error ?: Flowable.empty()
      }
    
      val networkConfig = session.mapMapNullable(ISession::networkConfig)
    
      val ignoreListManager = session.mapMapNullable(ISession::ignoreListManager)
    
      val highlightRuleManager = session.mapMapNullable(ISession::highlightRuleManager)
    
      val aliasManager = session.mapMapNullable(ISession::aliasManager)
    
      val networks = session.switchMap {
        it.map(ISession::liveNetworks).orElse(Observable.just(emptyMap()))
      }
    
      val identities = session.switchMap {
        it.map(ISession::liveIdentities).orElse(Observable.just(emptyMap()))
      }
    
      val bufferSyncer = session.mapMapNullable(ISession::bufferSyncer)
      val allBuffers = bufferSyncer.mapSwitchMap {
        it.liveBufferInfos().map(Map<BufferId, BufferInfo>::values)
      }.mapOrElse(emptyList())
    
      val network = combineLatest(bufferSyncer, networks, buffer).map { (syncer, networks, buffer) ->
        Optional.ofNullable(syncer.orNull()?.bufferInfo(buffer)?.let { networks[it.networkId] })
      }
    
      /**
       * An observable of the changes of the markerline, as pairs of `(old, new)`
       */
      val markerLine = session.mapSwitchMap { currentSession ->
        buffer.switchMap { currentBuffer ->
          // Get a stream of the latest marker line
          currentSession.bufferSyncer?.liveMarkerLine(currentBuffer) ?: Observable.empty()
        }
      }
    
      // Remove orElse
      val lag: Observable<Long> = session.mapSwitchMap(ISession::lag).mapOrElse(0)
    
      val isSecure: Observable<Boolean> = session.mapSwitchMap { session ->
        session.state.map { _ ->
          session.sslSession != null
        }
      }.mapOrElse(false)
    
      val bufferData = combineLatest(session, buffer).switchMap { (sessionOptional, id) ->
        val session = sessionOptional.orNull()
        val bufferSyncer = session?.bufferSyncer
        if (bufferSyncer != null) {
          session.liveNetworks().switchMap { networks ->
            bufferSyncer.liveBufferInfos().switchMap {
              val info = bufferSyncer.bufferInfo(id)
              val network = networks[info?.networkId]
              if (info == null || network == null) {
                Observable.just(BufferData())
              } else {
                when (info.type.toInt()) {
                  BufferInfo.Type.QueryBuffer.toInt()   -> {
                    network.liveIrcUser(info.bufferName).switchMap {
                      it.updates().map { user ->
                        BufferData(
                          info = info,
                          network = network,
                          description = user.realName()
                        )
                      }
                    }
                  }
                  BufferInfo.Type.ChannelBuffer.toInt() -> {
                    network.liveIrcChannel(
                      info.bufferName
                    ).switchMap { channel ->
                      channel.updates().map {
                        BufferData(
                          info = info,
                          network = network,
                          description = it.topic()
                        )
                      }
                    }
                  }
                  BufferInfo.Type.StatusBuffer.toInt()  -> {
                    network.liveConnectionState().map {
                      BufferData(
                        info = info,
                        network = network
                      )
                    }
                  }
                  else                                  -> Observable.just(
                    BufferData(
                      description = "type is unknown: ${info.type.toInt()}"
                    )
                  )
                }
              }
            }
          }
        } else {
          Observable.just(BufferData())
        }
      }
    
      val nickData: Observable<List<IrcUserItem>> = combineLatest(session, buffer)
        .switchMap { (sessionOptional, buffer) ->
          val session = sessionOptional.orNull()
          val bufferSyncer = session?.bufferSyncer
          val bufferInfo = bufferSyncer?.bufferInfo(buffer)
          if (bufferInfo?.type?.hasFlag(Buffer_Type.ChannelBuffer) == true) {
            session.liveNetworks().switchMap { networks ->
              val network = networks[bufferInfo.networkId]
              network?.liveIrcChannel(bufferInfo.bufferName)?.switchMapNullable(IrcChannel.NULL) { ircChannel ->
                ircChannel?.liveIrcUsers()?.switchMap { users ->
                  combineLatest<IrcUserItem>(
                    users.map<IrcUser, Observable<IrcUserItem>?> {
                      it.updates().map { user ->
                        val userModes = ircChannel.userModes(user)
                        val prefixModes = network.prefixModes()
    
                        val lowestMode = userModes.asSequence().mapNotNull {
                          prefixModes.indexOf(it)
                        }.min() ?: prefixModes.size
    
                        IrcUserItem(
                          user.nick(),
                          network.modesToPrefixes(userModes),
                          lowestMode,
                          user.realName(),
                          user.hostMask(),
                          user.isAway(),
                          user.network().isMyNick(user.nick()),
                          network.support("CASEMAPPING")
                        )
                      }
                    }
                  )
                } ?: Observable.just(emptyList())
              }
            }
          } else {
            Observable.just(emptyList())
          }
        }
    
      val bufferViewConfigs = bufferViewManager.mapSwitchMap { manager ->
        manager.liveBufferViewConfigs().map { ids ->
          ids.mapNotNull { id ->
            manager.bufferViewConfig(id)
          }.sortedWith(BufferViewConfig.NameComparator)
        }
      }.mapOrElse(emptyList())
    
      val bufferViewConfigMap = bufferViewManager.switchMap {
        it.map { manager ->
          manager.liveBufferViewConfigs().map {
            it.mapNotNull(manager::bufferViewConfig).associateBy(BufferViewConfig::bufferViewId)
          }
        }.orElse(Observable.empty())
      }
    
      val showHidden = BehaviorSubject.createDefault(false)
      val collapsedNetworks = BehaviorSubject.createDefault(emptySet<NetworkId>())
      val selectedBufferId = BehaviorSubject.createDefault(Int.MAX_VALUE)
      val selectedBuffer = combineLatest(session, selectedBufferId, bufferViewConfig)
        .switchMap { (sessionOptional, buffer, bufferViewConfigOptional) ->
          val session = sessionOptional.orNull()
          val bufferSyncer = session?.bufferSyncer
          val bufferViewConfig = bufferViewConfigOptional.orNull()
          if (bufferSyncer != null && bufferViewConfig != null) {
            session.liveNetworks().switchMap { networks ->
              val hiddenState = when {
                bufferViewConfig.removedBuffers().contains(buffer)            ->
                  BufferHiddenState.HIDDEN_PERMANENT
                bufferViewConfig.temporarilyRemovedBuffers().contains(buffer) ->
                  BufferHiddenState.HIDDEN_TEMPORARY
                else                                                          ->
                  BufferHiddenState.VISIBLE
              }
    
              val info = if (buffer < 0) networks[-buffer]?.let {
                BufferInfo(
                  bufferId = buffer,
                  networkId = it.networkId(),
                  groupId = 0,
                  bufferName = it.networkName(),
                  type = Buffer_Type.of(Buffer_Type.StatusBuffer)
                )
              } else bufferSyncer.bufferInfo(buffer)
              if (info != null) {
                val network = networks[info.networkId]
                when (info.type.enabledValues().firstOrNull()) {
                  Buffer_Type.StatusBuffer  -> {
                    network?.liveConnectionState()?.map {
                      SelectedBufferItem(
                        info,
                        connectionState = it,
                        hiddenState = hiddenState
                      )
                    } ?: Observable.just(SelectedBufferItem(info, hiddenState = hiddenState))
                  }
                  Buffer_Type.ChannelBuffer -> {
                    network?.liveIrcChannel(info.bufferName)?.mapNullable(IrcChannel.NULL) {
                      SelectedBufferItem(
                        info,
                        joined = it != null,
                        hiddenState = hiddenState
                      )
                    } ?: Observable.just(SelectedBufferItem(info, hiddenState = hiddenState))
                  }
                  else                      ->
                    Observable.just(SelectedBufferItem(info, hiddenState = hiddenState))
                }
              } else {
                Observable.just(SelectedBufferItem())
              }
            }
          } else {
            Observable.just(SelectedBufferItem())
          }
        }
    
      val bufferList: Observable<Pair<BufferViewConfig?, List<BufferProps>>?> =
        combineLatest(session, bufferViewConfig, showHidden)
          .switchMap { (sessionOptional, configOptional, showHiddenRaw) ->
            val session = sessionOptional.orNull()
            val bufferSyncer = session?.bufferSyncer
            val showHidden = showHiddenRaw ?: false
            val config = configOptional.orNull()
            if (bufferSyncer != null && config != null) {
              session.liveNetworks().switchMap { networks ->
                config.liveUpdates()
                  .debounce(16, TimeUnit.MILLISECONDS)
                  .switchMap { currentConfig ->
                    combineLatest<Collection<BufferId>>(
                      listOf(
                        config.liveBuffers(),
                        config.liveTemporarilyRemovedBuffers(),
                        config.liveRemovedBuffers()
                      )
                    ).switchMap { (ids, temp, perm) ->
                      fun transformIds(ids: Collection<BufferId>, state: BufferHiddenState) =
                        ids.asSequence().mapNotNull { id ->
                          bufferSyncer.bufferInfo(id)
                        }.filter {
                          currentConfig.networkId() <= 0 || currentConfig.networkId() == it.networkId
                        }.filter {
                          (currentConfig.allowedBufferTypes() and it.type).isNotEmpty() ||
                          (it.type.hasFlag(Buffer_Type.StatusBuffer) && currentConfig.networkId() < 0)
                        }.mapNotNull {
                          val network = networks[it.networkId]
                          if (network == null) {
                            null
                          } else {
                            it to network
                          }
                        }.map<Pair<BufferInfo, Network>, Observable<BufferProps>?> { (info, network) ->
                          bufferSyncer.liveActivity(info.bufferId).switchMap { activity ->
                            bufferSyncer.liveHighlightCount(info.bufferId).map { highlights ->
                              activity to highlights
                            }
                          }.switchMap { (activity, highlights) ->
                            when (info.type.toInt()) {
                              BufferInfo.Type.QueryBuffer.toInt()   -> {
                                network.liveNetworkInfo().switchMap { networkInfo ->
                                  network.liveConnectionState().switchMap { connectionState ->
                                    network.liveIrcUser(info.bufferName).switchMap {
                                      it.updates().mapNullable(IrcUser.NULL) { user ->
                                        BufferProps(
                                          info = info,
                                          network = networkInfo,
                                          networkConnectionState = connectionState,
                                          bufferStatus = when {
                                            user == null  -> BufferStatus.OFFLINE
                                            user.isAway() -> BufferStatus.AWAY
                                            else          -> BufferStatus.ONLINE
                                          },
                                          description = user?.realName() ?: "",
                                          activity = activity,
                                          highlights = highlights,
                                          hiddenState = state
                                        )
                                      }
                                    }
                                  }
                                }
                              }
                              BufferInfo.Type.ChannelBuffer.toInt() -> {
                                network.liveNetworkInfo().switchMap { networkInfo ->
                                  network.liveConnectionState().switchMap { connectionState ->
                                    network.liveIrcChannel(info.bufferName).switchMap { channel ->
                                      channel.updates().mapNullable(IrcChannel.NULL) {
                                        BufferProps(
                                          info = info,
                                          network = networkInfo,
                                          networkConnectionState = connectionState,
                                          bufferStatus = when (it) {
                                            null -> BufferStatus.OFFLINE
                                            else -> BufferStatus.ONLINE
                                          },
                                          description = it?.topic() ?: "",
                                          activity = activity,
                                          highlights = highlights,
                                          hiddenState = state
                                        )
                                      }
                                    }
                                  }
                                }
                              }
                              BufferInfo.Type.StatusBuffer.toInt()  -> {
                                network.liveNetworkInfo().switchMap { networkInfo ->
                                  network.liveConnectionState().map { connectionState ->
                                    BufferProps(
                                      info = info,
                                      network = networkInfo,
                                      networkConnectionState = connectionState,
                                      bufferStatus = BufferStatus.OFFLINE,
                                      description = "",
                                      activity = activity,
                                      highlights = highlights,
                                      hiddenState = state
                                    )
                                  }
                                }
                              }
                              else                                  -> Observable.empty()
                            }
                          }
                        }
    
                      fun missingStatusBuffers(
                        list: Collection<BufferId>): Sequence<Observable<BufferProps>?> {
                        val totalNetworks = networks.keys
                        val wantedNetworks = if (currentConfig.networkId() <= 0) totalNetworks
                        else listOf(currentConfig.networkId())
    
                        val availableNetworks = list.asSequence().mapNotNull { id ->
                          bufferSyncer.bufferInfo(id)
                        }.filter {
                          it.type.hasFlag(Buffer_Type.StatusBuffer)
                        }.map {
                          it.networkId
                        }
    
                        val missingNetworks = wantedNetworks - availableNetworks
    
                        return missingNetworks.asSequence().filter {
                          currentConfig.networkId() <= 0 || currentConfig.networkId() == it
                        }.filter {
                          currentConfig.allowedBufferTypes().hasFlag(Buffer_Type.StatusBuffer)
                        }.mapNotNull {
                          networks[it]
                        }.filter {
                          !config.hideInactiveNetworks() || it.isConnected()
                        }.map<Network, Observable<BufferProps>?> { network ->
                          network.liveNetworkInfo().switchMap { networkInfo ->
                            network.liveConnectionState().map { connectionState ->
                              BufferProps(
                                info = BufferInfo(
                                  bufferId = -networkInfo.networkId,
                                  networkId = networkInfo.networkId,
                                  groupId = 0,
                                  bufferName = networkInfo.networkName,
                                  type = Buffer_Type.of(Buffer_Type.StatusBuffer)
                                ),
                                network = networkInfo,
                                networkConnectionState = connectionState,
                                bufferStatus = BufferStatus.OFFLINE,
                                description = "",
                                activity = Message_Type.of(),
                                highlights = 0,
                                hiddenState = BufferHiddenState.VISIBLE
                              )
                            }
                          }
                        }
                      }
    
                      bufferSyncer.liveBufferInfos().switchMap {
                        val buffers = if (showHidden) {
                          transformIds(ids, BufferHiddenState.VISIBLE) +
                          transformIds(temp - ids, BufferHiddenState.HIDDEN_TEMPORARY) +
                          transformIds(perm - temp - ids, BufferHiddenState.HIDDEN_PERMANENT) +
                          missingStatusBuffers(ids + temp + perm)
                        } else {
                          transformIds(ids, BufferHiddenState.VISIBLE) +
                          missingStatusBuffers(ids)
                        }
    
                        combineLatest<BufferProps>(buffers.toList()).map { list ->
                          Pair<BufferViewConfig?, List<BufferProps>>(
                            config,
                            list.asSequence().filter {
                              !config.hideInactiveNetworks() ||
                              it.networkConnectionState == INetwork.ConnectionState.Initialized
                            }.filter {
                              (!config.hideInactiveBuffers()) ||
                              it.bufferStatus != BufferStatus.OFFLINE ||
                              it.info.type.hasFlag(Buffer_Type.StatusBuffer)
                            }.let {
                              if (config.sortAlphabetically())
                                it.sortedBy { IrcCaseMappers.unicode.toLowerCaseNullable(it.info.bufferName) }
                                  .sortedByDescending { it.hiddenState == BufferHiddenState.VISIBLE }
                              else it
                            }.distinctBy {
                              it.info.bufferId
                            }.toList()
                          )
                        }
                      }
                    }
                  }
              }
            } else {
              Observable.just(Pair<BufferViewConfig?, List<BufferProps>>(null, emptyList()))
            }
          }
    }