diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt index 5f6ac0b5b0c09f060758094cee13138d64e5ed06..adbf785f6df75cd1745ee8348c61228ef35ea146 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt @@ -44,7 +44,7 @@ class Network constructor( fun isMe(ircUser: IrcUser) = myNick().equals(ircUser.nick(), true) fun isChannelName(channelName: String) = when { channelName.isBlank() -> false - supports("CHANTYPES") -> support("CHANTYPES").contains(channelName[0]) + supports("CHANTYPES") -> support("CHANTYPES")?.contains(channelName[0]) ?: false else -> "#&!+".contains(channelName[0]) } @@ -63,7 +63,7 @@ class Network constructor( */ fun isStatusMsg(target: String) = when { target.isBlank() -> false - supports("STATUSMSG") -> support("STATUSMSG").contains(target[0]) + supports("STATUSMSG") -> support("STATUSMSG")?.contains(target[0]) ?: false else -> "@+".contains(target[0]) } @@ -98,9 +98,10 @@ class Network constructor( _channelModes = ChannelModeType.validValues .zip( support("CHANMODES") - .split(',', limit = ChannelModeType.validValues.size) - .map(String::toCharArray) - .map(CharArray::toSet) + ?.split(',', limit = ChannelModeType.validValues.size) + ?.map(String::toCharArray) + ?.map(CharArray::toSet) + .orEmpty() ).toMap() } @@ -160,6 +161,8 @@ class Network constructor( unlimitedMessageRate = unlimitedMessageRate() ) + fun liveNetworkInfo() = live_networkInfo.map { networkInfo() } + fun setNetworkInfo(info: NetworkInfo) { // we don't set our ID! if (!info.networkName.isEmpty() && info.networkName != networkName()) @@ -174,7 +177,7 @@ class Network constructor( setCodecForDecoding(Charset.forName(info.codecForDecoding)) // FIXME compare components if (info.serverList.isNotEmpty()) - setServerList(info.serverList.map { QVariant.of(it, QType.Network_Server) }) + setServerList(info.serverList.map { QVariant.of(it.toVariantMap(), QType.Network_Server) }) if (info.useRandomServer != useRandomServer()) setUseRandomServer(info.useRandomServer) if (info.perform != perform()) @@ -226,7 +229,7 @@ class Network constructor( private fun determinePrefixes() { // seems like we have to construct them first - val prefix = support("PREFIX") + val prefix = support("PREFIX") ?: "" if (prefix.startsWith("(") && prefix.contains(")")) { val (prefixModes, prefixes) = prefix.substring(1) .split(')', limit = 2) @@ -264,7 +267,7 @@ class Network constructor( fun channelModes(): Map<ChannelModeType, Set<Char>>? = _channelModes - fun supports(): Map<String, String> = _supports + fun supports(): Map<String, String?> = _supports fun supports(param: String) = _supports.contains(param.toUpperCase(Locale.US)) fun support(param: String) = _supports.getOr(param, "") /** @@ -319,7 +322,8 @@ class Network constructor( // reduce the risk of breaking existing setups. // See: http://ircv3.net/specs/extensions/sasl-3.1.html // And: http://ircv3.net/specs/extensions/sasl-3.2.html - return (capValue.isBlank() || capValue.contains(saslMechanism, ignoreCase = true)) + return (capValue.isNullOrBlank() || + capValue?.contains(saslMechanism, ignoreCase = true) ?: false) } fun newIrcUser(hostMask: String, initData: QVariantMap = emptyMap()): IrcUser { @@ -410,7 +414,7 @@ class Network constructor( super.setNetworkName(networkName) } - override fun setCurrentServer(currentServer: String) { + override fun setCurrentServer(currentServer: String?) { if (_currentServer == currentServer) return _currentServer = currentServer @@ -438,12 +442,12 @@ class Network constructor( super.setConnectionState(state) } - override fun setMyNick(mynick: String) { + override fun setMyNick(mynick: String?) { if (_myNick == mynick) return _myNick = mynick - if (_myNick.isNotEmpty() && ircUser(myNick()) == null) { - newIrcUser(myNick()) + if (_myNick != null && _myNick.isNullOrEmpty() && ircUser(myNick()) == null) { + newIrcUser(myNick() ?: "") } super.setMyNick(mynick) } @@ -464,8 +468,9 @@ class Network constructor( override fun setServerList(serverList: QVariantList) { val actualServerList = serverList.map { - it.valueOrThrow<Server>() - } + it.valueOrThrow<QVariantMap>() + }.map(Server.Companion::fromVariantMap) + if (_serverList == actualServerList) return _serverList = actualServerList @@ -631,7 +636,7 @@ class Network constructor( setCodecForDecoding(Charset.forName(codecName)) } - override fun addSupport(param: String, value: String) { + override fun addSupport(param: String, value: String?) { _supports[param] = value super.addSupport(param, value) } @@ -643,7 +648,7 @@ class Network constructor( super.removeSupport(param) } - override fun addCap(capability: String, value: String) { + override fun addCap(capability: String, value: String?) { _caps[capability.toLowerCase(Locale.US)] = value super.addCap(capability, value) } @@ -699,13 +704,17 @@ class Network constructor( override fun initIrcUsersAndChannels(): QVariantMap { return mapOf( - "Users" to QVariant.of(_ircUsers.values.map { it.toVariantMap() }.transpose().map { - QVariant.of(it, Type.QVariantList) - }, Type.QVariantMap + "Users" to QVariant.of( + _ircUsers.values.map { it.toVariantMap() }.transpose().map { + QVariant.of(it, Type.QVariantList) + }, + Type.QVariantMap ), - "Channels" to QVariant.of(_ircChannels.values.map { it.toVariantMap() }.transpose().map { - QVariant.of(it, Type.QVariantList) - }, Type.QVariantMap + "Channels" to QVariant.of( + _ircChannels.values.map { it.toVariantMap() }.transpose().map { + QVariant.of(it, Type.QVariantList) + }, + Type.QVariantMap ) ) } @@ -715,14 +724,14 @@ class Network constructor( "currentServer" to QVariant.of(currentServer(), Type.QString), "myNick" to QVariant.of(myNick(), Type.QString), "latency" to QVariant.of(latency(), Type.Int), - "codecForServer" to QVariant.of(codecForServer().serializeString(StringSerializer.UTF8), - Type.QByteArray + "codecForServer" to QVariant.of( + codecForServer().serializeString(StringSerializer.UTF8), Type.QByteArray ), - "codecForEncoding" to QVariant.of(codecForEncoding().serializeString(StringSerializer.UTF8), - Type.QByteArray + "codecForEncoding" to QVariant.of( + codecForEncoding().serializeString(StringSerializer.UTF8), Type.QByteArray ), - "codecForDecoding" to QVariant.of(codecForDecoding().serializeString(StringSerializer.UTF8), - Type.QByteArray + "codecForDecoding" to QVariant.of( + codecForDecoding().serializeString(StringSerializer.UTF8), Type.QByteArray ), "identityId" to QVariant.of(identity(), QType.IdentityId), "isConnected" to QVariant.of(isConnected(), Type.Bool), @@ -875,11 +884,23 @@ class Network constructor( } private var _networkId: NetworkId = networkId + set(value) { + field = value + live_networkInfo.onNext(Unit) + } private var _identity: IdentityId = -1 - private var _myNick: String = "" + set(value) { + field = value + live_networkInfo.onNext(Unit) + } + private var _myNick: String? = null private var _latency: Int = 0 private var _networkName: String = "<not initialized>" - private var _currentServer: String = "" + set(value) { + field = value + live_networkInfo.onNext(Unit) + } + private var _currentServer: String? = null private var _connected: Boolean = false private var _connectionState: ConnectionState = ConnectionState.Disconnected val live_connectionState = BehaviorSubject.createDefault(ConnectionState.Disconnected) @@ -893,48 +914,134 @@ class Network constructor( private var _ircChannels: MutableMap<String, IrcChannel> = mutableMapOf() private val live_ircChannels = BehaviorSubject.createDefault(emptyMap<String, IrcChannel>()) // stores results from RPL_ISUPPORT - private var _supports: MutableMap<String, String> = mutableMapOf() + private var _supports: MutableMap<String, String?> = mutableMapOf() /** * Capabilities supported by the IRC server * By synchronizing the supported capabilities, the client could suggest certain behaviors, e.g. * in the Network settings dialog, recommending SASL instead of using NickServ, or warning if * SASL EXTERNAL isn't available. */ - private var _caps: MutableMap<String, String> = mutableMapOf() + private var _caps: MutableMap<String, String?> = mutableMapOf() /** * Enabled capabilities that received 'CAP ACK' * _capsEnabled uses the same values from the <name>=<value> pairs stored in _caps */ private var _capsEnabled: MutableSet<String> = mutableSetOf() - private var _serverList: List<Server> = mutableListOf() + private var _serverList: List<Server> = listOf() + set(value) { + field = value + live_networkInfo.onNext(Unit) + } private var _useRandomServer: Boolean = false - private var _perform: List<String> = mutableListOf() + set(value) { + field = value + live_networkInfo.onNext(Unit) + } + private var _perform: List<String> = listOf() + set(value) { + field = value + live_networkInfo.onNext(Unit) + } private var _useAutoIdentify: Boolean = false + set(value) { + field = value + live_networkInfo.onNext(Unit) + } private var _autoIdentifyService: String = "" + set(value) { + field = value + live_networkInfo.onNext(Unit) + } private var _autoIdentifyPassword: String = "" + set(value) { + field = value + live_networkInfo.onNext(Unit) + } private var _useSasl: Boolean = false + set(value) { + field = value + live_networkInfo.onNext(Unit) + } private var _saslAccount: String = "" + set(value) { + field = value + live_networkInfo.onNext(Unit) + } private var _saslPassword: String = "" + set(value) { + field = value + live_networkInfo.onNext(Unit) + } private var _useAutoReconnect: Boolean = false + set(value) { + field = value + live_networkInfo.onNext(Unit) + } private var _autoReconnectInterval: UInt = 60 + set(value) { + field = value + live_networkInfo.onNext(Unit) + } private var _autoReconnectRetries: UShort = 10 + set(value) { + field = value + live_networkInfo.onNext(Unit) + } private var _unlimitedReconnectRetries = false + set(value) { + field = value + live_networkInfo.onNext(Unit) + } private var _rejoinChannels = false + set(value) { + field = value + live_networkInfo.onNext(Unit) + } // Custom rate limiting /** If true, use custom rate limits, otherwise use defaults */ private var _useCustomMessageRate: Boolean = false + set(value) { + field = value + live_networkInfo.onNext(Unit) + } /** Maximum number of messages to send without any delays */ private var _messageRateBurstSize: UInt = 5 + set(value) { + field = value + live_networkInfo.onNext(Unit) + } /** Delay in ms. for messages when max. burst messages sent */ private var _messageRateDelay: UInt = 2200 + set(value) { + field = value + live_networkInfo.onNext(Unit) + } /** If true, disable rate limiting, otherwise apply limits */ private var _unlimitedMessageRate: Boolean = false + set(value) { + field = value + live_networkInfo.onNext(Unit) + } private var _codecForServer: Charset = Charsets.UTF_8 + set(value) { + field = value + live_networkInfo.onNext(Unit) + } private var _codecForEncoding: Charset = Charsets.UTF_8 + set(value) { + field = value + live_networkInfo.onNext(Unit) + } private var _codecForDecoding: Charset = Charsets.UTF_8 + set(value) { + field = value + live_networkInfo.onNext(Unit) + } /** when this is active handle305 and handle306 don't trigger any output */ private var _autoAwayActive: Boolean = false + private val live_networkInfo = BehaviorSubject.createDefault(Unit) + companion object { val NULL = Network(-1, SignalProxy.NULL) } diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt index 649dd2da950c376dd0208e1e06045f4efffccac7..a88c48fea2f8cb5462233f140ea654de97f3a0c8 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt @@ -21,18 +21,11 @@ class RpcHandler( session.bufferSyncer.bufferInfoUpdated(bufferInfo) } - override fun identityCreated(identity: QVariantMap) { - } - - override fun identityRemoved(identityId: IdentityId) { - } - - override fun networkCreated(networkId: NetworkId) { + override fun identityCreated(identity: QVariantMap) = session.addIdentity(identity) + override fun identityRemoved(identityId: IdentityId) = session.removeIdentity(identityId) - } - - override fun networkRemoved(networkId: NetworkId) { - } + override fun networkCreated(networkId: NetworkId) = session.addNetwork(networkId) + override fun networkRemoved(networkId: NetworkId) = session.removeNetwork(networkId) override fun passwordChanged(ignored: Long, success: Boolean) { } diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt index df3410e34457cc6db1ca899975e958de498136fb..03d07e2ddfb2dd5e7979af7fa99bb988f54c06c2 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt @@ -29,7 +29,7 @@ interface INetwork : ISyncableObject { } @Slot - fun addCap(capability: String, value: String = "") { + fun addCap(capability: String, value: String?) { SYNC("addCap", ARG(capability, Type.QString), ARG(value, Type.QString)) } @@ -44,7 +44,7 @@ interface INetwork : ISyncableObject { } @Slot - fun addSupport(param: String, value: String = String()) { + fun addSupport(param: String, value: String? = null) { SYNC( "addSupport(param: String, value: String = String", ARG(param, Type.QString), ARG(value, Type.QString) @@ -135,7 +135,7 @@ interface INetwork : ISyncableObject { } @Slot - fun setCurrentServer(currentServer: String) { + fun setCurrentServer(currentServer: String?) { SYNC("setCurrentServer", ARG(currentServer, Type.QString)) } @@ -160,7 +160,7 @@ interface INetwork : ISyncableObject { } @Slot - fun setMyNick(mynick: String) { + fun setMyNick(mynick: String?) { SYNC("setMyNick", ARG(mynick, Type.QString)) } diff --git a/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt b/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt index 42338ad78746c9ecdc11a4f4b5d0155e9e0934ca..02cdf68e350df788ec248bcc5995e1befb85ce46 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt @@ -25,9 +25,11 @@ interface ISession : Closeable { val coreInfo: CoreInfo? val dccConfig: DccConfig? val identities: Map<IdentityId, Identity> + fun live_identities(): Observable<Map<IdentityId, Identity>> val ignoreListManager: IgnoreListManager? val ircListHelper: IrcListHelper? val networks: Map<NetworkId, Network> + fun live_networks(): Observable<Map<NetworkId, Network>> val networkConfig: NetworkConfig? val rpcHandler: RpcHandler? val initStatus: Observable<Pair<Int, Int>> @@ -55,9 +57,11 @@ interface ISession : Closeable { override val coreInfo: CoreInfo? = null override val dccConfig: DccConfig? = null override val identities: Map<IdentityId, Identity> = emptyMap() + override fun live_identities() = Observable.empty<Map<IdentityId, Identity>>() override val ignoreListManager: IgnoreListManager? = null override val ircListHelper: IrcListHelper? = null override val networks: Map<NetworkId, Network> = emptyMap() + override fun live_networks() = Observable.empty<Map<NetworkId, Network>>() override val networkConfig: NetworkConfig? = null override val initStatus: Observable<Pair<Int, Int>> = Observable.just(0 to 0) override val lag: Observable<Long> = Observable.just(0L) diff --git a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt index 991ff35a591f3a64e80e7f3453519c96b186485a..8b96dfd16610adda00b20b28b7cbf0742dddb91e 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt @@ -8,6 +8,7 @@ import de.kuschku.libquassel.quassel.QuasselFeatures import de.kuschku.libquassel.quassel.syncables.* import de.kuschku.libquassel.util.compatibility.HandlerService import io.reactivex.BackpressureStrategy +import io.reactivex.Observable import io.reactivex.subjects.BehaviorSubject import io.reactivex.subjects.PublishSubject import org.threeten.bp.Instant @@ -43,10 +44,18 @@ class Session( override val certManagers = mutableMapOf<IdentityId, CertManager>() override val coreInfo = CoreInfo(this) override val dccConfig = DccConfig(this) + override val identities = mutableMapOf<IdentityId, Identity>() + private val live_identities = BehaviorSubject.createDefault(Unit) + override fun live_identities(): Observable<Map<IdentityId, Identity>> = live_identities.map { identities } + override val ignoreListManager = IgnoreListManager(this) override val ircListHelper = IrcListHelper(this) + override val networks = mutableMapOf<NetworkId, Network>() + private val live_networks = BehaviorSubject.createDefault(Unit) + override fun live_networks(): Observable<Map<NetworkId, Network>> = live_networks.map { networks } + override val networkConfig = NetworkConfig(this) override var rpcHandler: RpcHandler? = RpcHandler(this, backlogStorage) @@ -104,6 +113,33 @@ class Session( return true } + fun addNetwork(networkId: NetworkId) { + val network = Network(networkId, this) + networks[networkId] = network + synchronize(network) + live_networks.onNext(Unit) + } + + fun removeNetwork(networkId: NetworkId) { + val network = networks.remove(networkId) + stopSynchronize(network) + live_networks.onNext(Unit) + } + + fun addIdentity(initData: QVariantMap) { + val identity = Identity(this) + identity.fromVariantMap(initData) + identities[identity.id()] = identity + synchronize(identity) + live_identities.onNext(Unit) + } + + fun removeIdentity(identityId: IdentityId) { + val identity = identities.remove(identityId) + stopSynchronize(identity) + live_identities.onNext(Unit) + } + override fun handle(f: HandshakeMessage.SessionInit): Boolean { coreConnection.setState(ConnectionState.INIT) @@ -114,6 +150,7 @@ class Session( val network = Network(it.value(-1), this) networks[network.networkId()] = network } + live_networks.onNext(Unit) f.identities?.forEach { val identity = Identity(this) diff --git a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt index 84cf52dadaea34bdf0379c9ef4ec0199f50227d9..7e0ded8ca24909d4b8cf0bf595fc21f4207cca44 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt @@ -1,10 +1,7 @@ package de.kuschku.libquassel.session import de.kuschku.libquassel.protocol.ClientData -import de.kuschku.libquassel.protocol.IdentityId -import de.kuschku.libquassel.protocol.NetworkId import de.kuschku.libquassel.protocol.message.HandshakeMessage -import de.kuschku.libquassel.quassel.syncables.* import de.kuschku.libquassel.quassel.syncables.interfaces.invokers.Invokers import de.kuschku.libquassel.util.compatibility.HandlerService import de.kuschku.libquassel.util.compatibility.LoggingHandler @@ -15,7 +12,6 @@ import io.reactivex.Flowable import io.reactivex.Observable import io.reactivex.functions.BiFunction import io.reactivex.subjects.BehaviorSubject -import javax.net.ssl.SSLSession import javax.net.ssl.X509TrustManager class SessionManager( @@ -24,41 +20,8 @@ class SessionManager( val handlerService: HandlerService, private val disconnectFromCore: () -> Unit, private val exceptionHandler: (Throwable) -> Unit -) : ISession { - override val features: Features - get() = session.or(lastSession).features - override val sslSession: SSLSession? - get() = session.or(lastSession).sslSession - override val aliasManager: AliasManager? - get() = session.or(lastSession).aliasManager - override val backlogManager: BacklogManager? - get() = session.or(lastSession).backlogManager - override val bufferSyncer: BufferSyncer? - get() = session.or(lastSession).bufferSyncer - override val bufferViewManager: BufferViewManager? - get() = session.or(lastSession).bufferViewManager - override val certManagers: Map<IdentityId, CertManager> - get() = session.or(lastSession).certManagers - override val coreInfo: CoreInfo? - get() = session.or(lastSession).coreInfo - override val dccConfig: DccConfig? - get() = session.or(lastSession).dccConfig - override val identities: Map<IdentityId, Identity> - get() = session.or(lastSession).identities - override val ignoreListManager: IgnoreListManager? - get() = session.or(lastSession).ignoreListManager - override val ircListHelper: IrcListHelper? - get() = session.or(lastSession).ircListHelper - override val networks: Map<NetworkId, Network> - get() = session.or(lastSession).networks - override val networkConfig: NetworkConfig? - get() = session.or(lastSession).networkConfig - override val rpcHandler: RpcHandler? - get() = session.or(lastSession).rpcHandler - override val lag: Observable<Long> - get() = session.or(lastSession).lag - - override fun close() = session.or(lastSession).close() +) { + fun close() = session.or(lastSession).close() private var lastClientData: ClientData? = null private var lastTrustManager: X509TrustManager? = null @@ -68,16 +31,16 @@ class SessionManager( private var inProgressSession = BehaviorSubject.createDefault(ISession.NULL) private var lastSession: ISession = offlineSession - override val state: Observable<ConnectionState> = inProgressSession.switchMap(ISession::state) + val state: Observable<ConnectionState> = inProgressSession.switchMap(ISession::state) - override val initStatus: Observable<Pair<Int, Int>> = inProgressSession.switchMap(ISession::initStatus) + val initStatus: Observable<Pair<Int, Int>> = inProgressSession.switchMap(ISession::initStatus) val session: Observable<ISession> = state.map { connectionState -> if (connectionState == ConnectionState.CONNECTED) inProgressSession.value else lastSession } - override val error: Flowable<HandshakeMessage> + val error: Flowable<HandshakeMessage> get() = inProgressSession.toFlowable(BackpressureStrategy.LATEST).switchMap(ISession::error) val connectionProgress: Observable<Triple<ConnectionState, Int, Int>> = Observable.combineLatest( @@ -154,7 +117,7 @@ class SessionManager( inProgressSession.onNext(ISession.NULL) } - override fun login(user: String, pass: String) { + fun login(user: String, pass: String) { inProgressSession.value.login(user, pass) } } diff --git a/lib/src/main/java/de/kuschku/libquassel/util/irc/IrcCaseMappers.kt b/lib/src/main/java/de/kuschku/libquassel/util/irc/IrcCaseMappers.kt index d0c05235473ca32d9d96759c88f1828ba606c5a1..d184f0122bb7828f709012df81ee1c5277ef5955 100644 --- a/lib/src/main/java/de/kuschku/libquassel/util/irc/IrcCaseMappers.kt +++ b/lib/src/main/java/de/kuschku/libquassel/util/irc/IrcCaseMappers.kt @@ -44,7 +44,7 @@ object IrcCaseMappers { } } - operator fun get(caseMapping: String) = if (caseMapping.equals("rfc1459", ignoreCase = true)) { + operator fun get(caseMapping: String?) = if (caseMapping.equals("rfc1459", ignoreCase = true)) { irc } else { unicode diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt index 22c94253c4ec2e57e4c209dff3160f0d04e224f5..4ba1fec689567256a681d11a2a54327c26d11180 100644 --- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt +++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/QuasselViewModel.kt @@ -100,50 +100,52 @@ class QuasselViewModel : ViewModel() { val session = sessionOptional.orNull() val bufferSyncer = session?.bufferSyncer if (bufferSyncer != null) { - bufferSyncer.liveBufferInfos().switchMap { - val info = bufferSyncer.bufferInfo(id) - val network = session.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() - ) + session.live_networks().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.liveTopic().map { topic -> + BufferInfo.Type.ChannelBuffer.toInt() -> { + network.liveIrcChannel( + info.bufferName + ).switchMap { channel -> + channel.liveTopic().map { topic -> + BufferData( + info = info, + network = network, + description = topic + ) + } + } + } + BufferInfo.Type.StatusBuffer.toInt() -> { + network.live_connectionState.map { BufferData( info = info, - network = network, - description = topic + network = network ) } } - } - BufferInfo.Type.StatusBuffer.toInt() -> { - network.live_connectionState.map { + else -> Observable.just( BufferData( - info = info, - network = network + description = "type is unknown: ${info.type.toInt()}" ) - } - } - else -> Observable.just( - BufferData( - description = "type is unknown: ${info.type.toInt()}" ) - ) + } } } } @@ -158,34 +160,36 @@ class QuasselViewModel : ViewModel() { val bufferSyncer = session?.bufferSyncer val bufferInfo = bufferSyncer?.bufferInfo(buffer) if (bufferInfo?.type?.hasFlag(Buffer_Type.ChannelBuffer) == true) { - val network = session.networks[bufferInfo.networkId] - val ircChannel = network?.ircChannel(bufferInfo.bufferName) - if (ircChannel != null) { - ircChannel.liveIrcUsers().switchMap { users -> - combineLatest<IrcUserItem>( - users.map<IrcUser, Observable<IrcUserItem>?> { - it.updates().map { user -> - val userModes = ircChannel.userModes(user) - val prefixModes = network.prefixModes() + session.live_networks().switchMap { networks -> + val network = networks[bufferInfo.networkId] + val ircChannel = network?.ircChannel(bufferInfo.bufferName) + if (ircChannel != null) { + 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.mapNotNull { - prefixModes.indexOf(it) - }.min() ?: prefixModes.size + val lowestMode = userModes.mapNotNull { + prefixModes.indexOf(it) + }.min() ?: prefixModes.size - IrcUserItem( - user.nick(), - network.modesToPrefixes(userModes), - lowestMode, - user.realName(), - user.isAway(), - network.support("CASEMAPPING") - ) + IrcUserItem( + user.nick(), + network.modesToPrefixes(userModes), + lowestMode, + user.realName(), + user.isAway(), + network.support("CASEMAPPING") + ) + } } - } - ) + ) + } + } else { + Observable.just(emptyList()) } - } else { - Observable.just(emptyList()) } } else { Observable.just(emptyList()) @@ -204,80 +208,82 @@ class QuasselViewModel : ViewModel() { val bufferSyncer = session?.bufferSyncer val bufferInfo = bufferSyncer?.bufferInfo(id) if (bufferSyncer != null) { - bufferSyncer.liveBufferInfos().switchMap { infos -> - if (bufferInfo?.type?.hasFlag( - Buffer_Type.ChannelBuffer - ) == true) { - val network = session.networks[bufferInfo.networkId] - val ircChannel = network?.ircChannel( - bufferInfo.bufferName - ) - if (ircChannel != null) { - ircChannel.liveIrcUsers().switchMap { users -> - val buffers: List<Observable<AutoCompleteItem.ChannelItem>?> = infos.values - .filter { - it.type.toInt() == Buffer_Type.ChannelBuffer.toInt() - }.mapNotNull { info -> - session.networks[info.networkId]?.let { info to it } - }.map<Pair<BufferInfo, Network>, Observable<AutoCompleteItem.ChannelItem>?> { (info, network) -> - network.liveIrcChannel( - info.bufferName - ).switchMap { channel -> - channel.liveTopic().map { topic -> - AutoCompleteItem.ChannelItem( - info = info, - network = network.networkInfo(), - bufferStatus = when (channel) { - IrcChannel.NULL -> BufferStatus.OFFLINE - else -> BufferStatus.ONLINE - }, - description = topic - ) + session.live_networks().switchMap { networks -> + bufferSyncer.liveBufferInfos().switchMap { infos -> + if (bufferInfo?.type?.hasFlag( + Buffer_Type.ChannelBuffer + ) == true) { + val network = networks[bufferInfo.networkId] + val ircChannel = network?.ircChannel( + bufferInfo.bufferName + ) + if (ircChannel != null) { + ircChannel.liveIrcUsers().switchMap { users -> + val buffers: List<Observable<AutoCompleteItem.ChannelItem>?> = infos.values + .filter { + it.type.toInt() == Buffer_Type.ChannelBuffer.toInt() + }.mapNotNull { info -> + networks[info.networkId]?.let { info to it } + }.map<Pair<BufferInfo, Network>, Observable<AutoCompleteItem.ChannelItem>?> { (info, network) -> + network.liveIrcChannel( + info.bufferName + ).switchMap { channel -> + channel.liveTopic().map { topic -> + AutoCompleteItem.ChannelItem( + info = info, + network = network.networkInfo(), + bufferStatus = when (channel) { + IrcChannel.NULL -> BufferStatus.OFFLINE + else -> BufferStatus.ONLINE + }, + description = topic + ) + } } } - } - val nicks = users.map<IrcUser, Observable<AutoCompleteItem.UserItem>?> { - it.updates().map { user -> - val userModes = ircChannel.userModes(user) - val prefixModes = network.prefixModes() + val nicks = users.map<IrcUser, Observable<AutoCompleteItem.UserItem>?> { + it.updates().map { user -> + val userModes = ircChannel.userModes(user) + val prefixModes = network.prefixModes() - val lowestMode = userModes.mapNotNull(prefixModes::indexOf).min() - ?: prefixModes.size + val lowestMode = userModes.mapNotNull(prefixModes::indexOf).min() + ?: prefixModes.size - AutoCompleteItem.UserItem( - user.nick(), - network.modesToPrefixes(userModes), - lowestMode, - user.realName(), - user.isAway(), - network.support("CASEMAPPING") - ) + AutoCompleteItem.UserItem( + user.nick(), + network.modesToPrefixes(userModes), + lowestMode, + user.realName(), + user.isAway(), + network.support("CASEMAPPING") + ) + } } - } - combineLatest<AutoCompleteItem>(nicks + buffers) - .map { list -> - val ignoredStartingCharacters = charArrayOf( - '-', '_', '[', ']', '{', '}', '|', '`', '^', '.', '\\', '@' - ) + combineLatest<AutoCompleteItem>(nicks + buffers) + .map { list -> + val ignoredStartingCharacters = charArrayOf( + '-', '_', '[', ']', '{', '}', '|', '`', '^', '.', '\\', '@' + ) - Pair( - lastWord.first, - list.filter { - it.name.trimStart(*ignoredStartingCharacters) - .startsWith( - lastWord.first.trimStart(*ignoredStartingCharacters), - ignoreCase = true - ) - }.sorted() - ) - } + Pair( + lastWord.first, + list.filter { + it.name.trimStart(*ignoredStartingCharacters) + .startsWith( + lastWord.first.trimStart(*ignoredStartingCharacters), + ignoreCase = true + ) + }.sorted() + ) + } + } + } else { + Observable.just(Pair(lastWord.first, emptyList())) } } else { Observable.just(Pair(lastWord.first, emptyList())) } - } else { - Observable.just(Pair(lastWord.first, emptyList())) } } } else { @@ -303,42 +309,44 @@ class QuasselViewModel : ViewModel() { val bufferSyncer = session?.bufferSyncer val bufferViewConfig = bufferViewConfigOptional.orNull() if (bufferSyncer != null && bufferViewConfig != null) { - val hiddenState = when { - bufferViewConfig.removedBuffers().contains(buffer) -> - BufferHiddenState.HIDDEN_PERMANENT - bufferViewConfig.temporarilyRemovedBuffers().contains(buffer) -> - BufferHiddenState.HIDDEN_TEMPORARY - else -> - BufferHiddenState.VISIBLE - } + session.live_networks().switchMap { networks -> + val hiddenState = when { + bufferViewConfig.removedBuffers().contains(buffer) -> + BufferHiddenState.HIDDEN_PERMANENT + bufferViewConfig.temporarilyRemovedBuffers().contains(buffer) -> + BufferHiddenState.HIDDEN_TEMPORARY + else -> + BufferHiddenState.VISIBLE + } - val info = bufferSyncer.bufferInfo(buffer) - if (info != null) { - val network = session.networks[info.networkId] - when (info.type.enabledValues().firstOrNull()) { - Buffer_Type.StatusBuffer -> { - network?.live_connectionState?.map { - SelectedBufferItem( - info, - connectionState = it, - hiddenState = hiddenState - ) - } ?: Observable.just(SelectedBufferItem(info, hiddenState = hiddenState)) - } - Buffer_Type.ChannelBuffer -> { - network?.liveIrcChannel(info.bufferName)?.map { - SelectedBufferItem( - info, - joined = it != IrcChannel.NULL, - hiddenState = hiddenState - ) - } ?: Observable.just(SelectedBufferItem(info, hiddenState = hiddenState)) + val info = bufferSyncer.bufferInfo(buffer) + if (info != null) { + val network = networks[info.networkId] + when (info.type.enabledValues().firstOrNull()) { + Buffer_Type.StatusBuffer -> { + network?.live_connectionState?.map { + SelectedBufferItem( + info, + connectionState = it, + hiddenState = hiddenState + ) + } ?: Observable.just(SelectedBufferItem(info, hiddenState = hiddenState)) + } + Buffer_Type.ChannelBuffer -> { + network?.liveIrcChannel(info.bufferName)?.map { + SelectedBufferItem( + info, + joined = it != IrcChannel.NULL, + hiddenState = hiddenState + ) + } ?: Observable.just(SelectedBufferItem(info, hiddenState = hiddenState)) + } + else -> + Observable.just(SelectedBufferItem(info, hiddenState = hiddenState)) } - else -> - Observable.just(SelectedBufferItem(info, hiddenState = hiddenState)) + } else { + Observable.just(SelectedBufferItem()) } - } else { - Observable.just(SelectedBufferItem()) } } else { Observable.just(SelectedBufferItem()) @@ -354,77 +362,96 @@ class QuasselViewModel : ViewModel() { val showHidden = showHiddenRaw ?: false val config = configOptional.orNull() if (bufferSyncer != null && config != null) { - config.live_config - .debounce(16, TimeUnit.MILLISECONDS) - .switchMap { currentConfig -> - combineLatest<Collection<BufferId>>( - listOf( - config.live_buffers, - config.live_temporarilyRemovedBuffers, - config.live_removedBuffers - ) - ).switchMap { (ids, temp, perm) -> - fun transformIds(ids: Collection<BufferId>, state: BufferHiddenState) = - ids.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) - }.mapNotNull { - val network = session.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 + session.live_networks().switchMap { networks -> + config.live_config + .debounce(16, TimeUnit.MILLISECONDS) + .switchMap { currentConfig -> + combineLatest<Collection<BufferId>>( + listOf( + config.live_buffers, + config.live_temporarilyRemovedBuffers, + config.live_removedBuffers + ) + ).switchMap { (ids, temp, perm) -> + fun transformIds(ids: Collection<BufferId>, state: BufferHiddenState) = + ids.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) + }.mapNotNull { + val network = networks[it.networkId] + if (network == null) { + null + } else { + it to network } - }.switchMap { (activity, highlights) -> - when (info.type.toInt()) { - BufferInfo.Type.QueryBuffer.toInt() -> { - network.liveIrcUser(info.bufferName).switchMap { - it.updates().map { user -> - BufferProps( - info = info, - network = network.networkInfo(), - bufferStatus = when { - user == IrcUser.NULL -> BufferStatus.OFFLINE - user.isAway() -> BufferStatus.AWAY - else -> BufferStatus.ONLINE - }, - description = user.realName(), - activity = activity, - highlights = highlights, - hiddenState = state - ) + }.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.liveIrcUser(info.bufferName).switchMap { + it.updates().map { user -> + BufferProps( + info = info, + network = networkInfo, + bufferStatus = when { + user == IrcUser.NULL -> BufferStatus.OFFLINE + user.isAway() -> BufferStatus.AWAY + else -> BufferStatus.ONLINE + }, + description = user.realName(), + activity = activity, + highlights = highlights, + hiddenState = state + ) + } + } } } - } - BufferInfo.Type.ChannelBuffer.toInt() -> { - network.liveIrcChannel(info.bufferName).switchMap { channel -> - channel.liveTopic().map { topic -> - BufferProps( - info = info, - network = network.networkInfo(), - bufferStatus = when (channel) { - IrcChannel.NULL -> BufferStatus.OFFLINE - else -> BufferStatus.ONLINE - }, - description = topic, - activity = activity, - highlights = highlights, - hiddenState = state - ) + BufferInfo.Type.ChannelBuffer.toInt() -> { + network.liveNetworkInfo().switchMap { networkInfo -> + network.liveIrcChannel(info.bufferName).switchMap { channel -> + channel.liveTopic().map { topic -> + BufferProps( + info = info, + network = networkInfo, + bufferStatus = when (channel) { + IrcChannel.NULL -> BufferStatus.OFFLINE + else -> BufferStatus.ONLINE + }, + description = topic, + activity = activity, + highlights = highlights, + hiddenState = state + ) + } + } } } - } - BufferInfo.Type.StatusBuffer.toInt() -> { - network.live_connectionState.map { + BufferInfo.Type.StatusBuffer.toInt() -> { + network.liveNetworkInfo().switchMap { networkInfo -> + network.live_connectionState.map { + BufferProps( + info = info, + network = networkInfo, + bufferStatus = BufferStatus.OFFLINE, + description = "", + activity = activity, + highlights = highlights, + hiddenState = state + ) + } + } + } + else -> Observable.just( BufferProps( info = info, network = network.networkInfo(), @@ -434,45 +461,34 @@ class QuasselViewModel : ViewModel() { highlights = highlights, hiddenState = state ) - } - } - else -> Observable.just( - BufferProps( - info = info, - network = network.networkInfo(), - bufferStatus = BufferStatus.OFFLINE, - description = "", - activity = activity, - highlights = highlights, - hiddenState = state ) - ) + } } } - } - bufferSyncer.liveBufferInfos().switchMap { - val buffers = if (showHidden) { - transformIds(ids, BufferHiddenState.VISIBLE) + - transformIds(temp, BufferHiddenState.HIDDEN_TEMPORARY) + - transformIds(perm, BufferHiddenState.HIDDEN_PERMANENT) - } else { - transformIds(ids, BufferHiddenState.VISIBLE) - } + bufferSyncer.liveBufferInfos().switchMap { + val buffers = if (showHidden) { + transformIds(ids, BufferHiddenState.VISIBLE) + + transformIds(temp, BufferHiddenState.HIDDEN_TEMPORARY) + + transformIds(perm, BufferHiddenState.HIDDEN_PERMANENT) + } else { + transformIds(ids, BufferHiddenState.VISIBLE) + } - combineLatest<BufferProps>(buffers).map { list -> - Pair<BufferViewConfig?, List<BufferProps>>( - config, - list.filter { - (!config.hideInactiveBuffers()) || - it.bufferStatus != BufferStatus.OFFLINE || - it.info.type.hasFlag(Buffer_Type.StatusBuffer) - } - ) + combineLatest<BufferProps>(buffers).map { list -> + Pair<BufferViewConfig?, List<BufferProps>>( + config, + list.filter { + (!config.hideInactiveBuffers()) || + it.bufferStatus != BufferStatus.OFFLINE || + it.info.type.hasFlag(Buffer_Type.StatusBuffer) + } + ) + } } } } - } + } } else { Observable.just(Pair<BufferViewConfig?, List<BufferProps>>(null, emptyList())) } diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/AutoCompleteItem.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/AutoCompleteItem.kt index f207cea27108971922229faa8c48d4895088bcff..0aea3b45e49fdaa5e26e0644d92d92276b69d256 100644 --- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/AutoCompleteItem.kt +++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/AutoCompleteItem.kt @@ -20,7 +20,7 @@ sealed class AutoCompleteItem(open val name: String) : Comparable<AutoCompleteIt val lowestMode: Int, val realname: CharSequence, val away: Boolean, - val networkCasemapping: String + val networkCasemapping: String? ) : AutoCompleteItem(nick) data class ChannelItem( diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/IrcUserItem.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/IrcUserItem.kt index 3b0a7e062607e8f1afac022c0c01577beaa0a239..651699812d34bf2bc96b00584ff494759c601eae 100644 --- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/IrcUserItem.kt +++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/IrcUserItem.kt @@ -6,5 +6,5 @@ data class IrcUserItem( val lowestMode: Int, val realname: CharSequence, val away: Boolean, - val networkCasemapping: String + val networkCasemapping: String? ) \ No newline at end of file