From c65606fbda9aa453d25fea9752e4243172c5357d Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski <janne@kuschku.de> Date: Sun, 28 Feb 2021 01:38:52 +0100 Subject: [PATCH] Implement all syncables --- .../client/exceptions/IrcListException.kt | 4 +- .../client/syncables/ClientBacklogManager.kt | 198 +++++++++++++ .../client/syncables/ClientIrcListHelper.kt | 79 ++++++ .../protocol/syncables/AliasManager.kt | 2 +- .../protocol/syncables/BacklogManager.kt | 12 + .../protocol/syncables/BufferSyncer.kt | 207 ++++++++++++++ .../protocol/syncables/BufferViewConfig.kt | 266 ++++++++++++++++++ .../protocol/syncables/BufferViewManager.kt | 66 +++++ .../protocol/syncables/CertManager.kt | 2 +- .../libquassel/protocol/syncables/CoreInfo.kt | 2 +- .../protocol/syncables/DccConfig.kt | 2 +- .../syncables/HighlightRuleManager.kt | 2 +- .../libquassel/protocol/syncables/Identity.kt | 232 +++++++++++++++ .../protocol/syncables/IrcChannel.kt | 2 +- .../protocol/syncables/IrcListHelper.kt | 11 + .../libquassel/protocol/syncables/IrcUser.kt | 2 +- .../libquassel/protocol/syncables/Network.kt | 2 +- .../protocol/syncables/NetworkConfig.kt | 119 ++++++++ .../libquassel/protocol/syncables/Session.kt | 19 +- .../syncables/state/BufferSyncerState.kt | 51 ++++ .../syncables/state/BufferViewConfigState.kt | 25 ++ .../syncables/state/BufferViewManagerState.kt | 10 + .../protocol/syncables/state/IdentityState.kt | 26 ++ .../syncables/state/NetworkConfigState.kt | 11 + .../syncables/stubs/BacklogManagerStub.kt | 17 +- .../libquassel/protocol/util/move.kt | 25 ++ .../libquassel/protocol/util/pairs.kt | 3 +- .../libquassel/protocol/util/plus.kt | 25 ++ .../libquassel/protocol/util/remove.kt | 14 + 29 files changed, 1418 insertions(+), 18 deletions(-) rename libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/IrcListHelperState.kt => libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/exceptions/IrcListException.kt (73%) create mode 100644 libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientBacklogManager.kt create mode 100644 libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientIrcListHelper.kt create mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/move.kt create mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/plus.kt create mode 100644 libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/remove.kt diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/IrcListHelperState.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/exceptions/IrcListException.kt similarity index 73% rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/IrcListHelperState.kt rename to libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/exceptions/IrcListException.kt index 182244f..2b1dd3d 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/IrcListHelperState.kt +++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/exceptions/IrcListException.kt @@ -8,4 +8,6 @@ * obtain one at https://mozilla.org/MPL/2.0/. */ -package de.justjanne.libquassel.protocol.syncables.state +package de.justjanne.libquassel.client.exceptions + +class IrcListException(message: String) : Exception(message) diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientBacklogManager.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientBacklogManager.kt new file mode 100644 index 0000000..7bbe4e0 --- /dev/null +++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientBacklogManager.kt @@ -0,0 +1,198 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.client.syncables + +import de.justjanne.bitflags.of +import de.justjanne.libquassel.protocol.models.flags.MessageFlag +import de.justjanne.libquassel.protocol.models.flags.MessageFlags +import de.justjanne.libquassel.protocol.models.flags.MessageType +import de.justjanne.libquassel.protocol.models.flags.MessageTypes +import de.justjanne.libquassel.protocol.models.ids.BufferId +import de.justjanne.libquassel.protocol.models.ids.MsgId +import de.justjanne.libquassel.protocol.syncables.BacklogManager +import de.justjanne.libquassel.protocol.syncables.Session +import de.justjanne.libquassel.protocol.variant.QVariantList +import kotlin.coroutines.Continuation +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine + +class ClientBacklogManager( + session: Session +) : BacklogManager(session) { + private val bufferListeners = + mutableMapOf<BacklogData.Buffer, Continuation<BacklogData.Buffer>>() + private val bufferFilteredListeners = + mutableMapOf<BacklogData.BufferFiltered, Continuation<BacklogData.BufferFiltered>>() + private val allListeners = + mutableMapOf<BacklogData.All, Continuation<BacklogData.All>>() + private val allFilteredListeners = + mutableMapOf<BacklogData.AllFiltered, Continuation<BacklogData.AllFiltered>>() + + suspend fun backlog( + bufferId: BufferId, + first: MsgId = MsgId(-1), + last: MsgId = MsgId(-1), + limit: Int = -1, + additional: Int = 0 + ) = suspendCoroutine<BacklogData.Buffer> { + val data = BacklogData.Buffer(bufferId, first, last, limit, additional) + bufferListeners[data] = it + } + + suspend fun backlogFiltered( + bufferId: BufferId, + first: MsgId = MsgId(-1), + last: MsgId = MsgId(-1), + limit: Int = -1, + additional: Int = 0, + type: MessageTypes = MessageType.all, + flags: MessageFlags = MessageFlag.all + ) = suspendCoroutine<BacklogData.BufferFiltered> { + val data = BacklogData.BufferFiltered(bufferId, first, last, limit, additional, type, flags) + bufferFilteredListeners[data] = it + } + + suspend fun backlogAll( + first: MsgId = MsgId(-1), + last: MsgId = MsgId(-1), + limit: Int = -1, + additional: Int = 0 + ) = suspendCoroutine<BacklogData.All> { + val data = BacklogData.All(first, last, limit, additional) + allListeners[data] = it + } + + suspend fun backlogAllFiltered( + first: MsgId = MsgId(-1), + last: MsgId = MsgId(-1), + limit: Int = -1, + additional: Int = 0, + type: MessageTypes = MessageType.all, + flags: MessageFlags = MessageFlag.all + ) = suspendCoroutine<BacklogData.AllFiltered> { + val data = BacklogData.AllFiltered(first, last, limit, additional, type, flags) + allFilteredListeners[data] = it + } + + override fun receiveBacklog( + bufferId: BufferId, + first: MsgId, + last: MsgId, + limit: Int, + additional: Int, + messages: QVariantList + ) { + val data = BacklogData.Buffer( + bufferId, + first, + last, + limit, + additional + ) + bufferListeners[data]?.resume(data.copy(messages = messages)) + super.receiveBacklog(bufferId, first, last, limit, additional, messages) + } + + override fun receiveBacklogFiltered( + bufferId: BufferId, + first: MsgId, + last: MsgId, + limit: Int, + additional: Int, + type: Int, + flags: Int, + messages: QVariantList + ) { + val data = BacklogData.BufferFiltered( + bufferId, + first, + last, + limit, + additional, + MessageType.of(type.toUInt()), + MessageFlag.of(flags.toUInt()) + ) + bufferFilteredListeners[data]?.resume(data.copy(messages = messages)) + super.receiveBacklogFiltered(bufferId, first, last, limit, additional, type, flags, messages) + } + + override fun receiveBacklogAll(first: MsgId, last: MsgId, limit: Int, additional: Int, messages: QVariantList) { + val data = BacklogData.All( + first, + last, + limit, + additional + ) + allListeners[data]?.resume(data.copy(messages = messages)) + super.receiveBacklogAll(first, last, limit, additional, messages) + } + + override fun receiveBacklogAllFiltered( + first: MsgId, + last: MsgId, + limit: Int, + additional: Int, + type: Int, + flags: Int, + messages: QVariantList + ) { + val data = BacklogData.AllFiltered( + first, + last, + limit, + additional, + MessageType.of(type.toUInt()), + MessageFlag.of(flags.toUInt()), + ) + allFilteredListeners[data]?.resume(data.copy(messages = messages)) + super.receiveBacklogAllFiltered(first, last, limit, additional, type, flags, messages) + } + + sealed class BacklogData { + data class Buffer( + val bufferId: BufferId, + val first: MsgId = MsgId(-1), + val last: MsgId = MsgId(-1), + val limit: Int = -1, + val additional: Int = 0, + val messages: QVariantList = emptyList() + ) : BacklogData() + + data class BufferFiltered( + val bufferId: BufferId, + val first: MsgId = MsgId(-1), + val last: MsgId = MsgId(-1), + val limit: Int = -1, + val additional: Int = 0, + val type: MessageTypes = MessageType.all, + val flags: MessageFlags = MessageFlag.all, + val messages: QVariantList = emptyList() + ) : BacklogData() + + data class All( + val first: MsgId = MsgId(-1), + val last: MsgId = MsgId(-1), + val limit: Int = -1, + val additional: Int = 0, + val messages: QVariantList = emptyList() + ) : BacklogData() + + data class AllFiltered( + val first: MsgId = MsgId(-1), + val last: MsgId = MsgId(-1), + val limit: Int = -1, + val additional: Int = 0, + val type: MessageTypes = MessageType.all, + val flags: MessageFlags = MessageFlag.all, + val messages: QVariantList = emptyList() + ) : BacklogData() + } +} diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientIrcListHelper.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientIrcListHelper.kt new file mode 100644 index 0000000..e75b723 --- /dev/null +++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientIrcListHelper.kt @@ -0,0 +1,79 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.client.syncables + +import de.justjanne.libquassel.client.exceptions.IrcListException +import de.justjanne.libquassel.protocol.models.QStringList +import de.justjanne.libquassel.protocol.models.ids.NetworkId +import de.justjanne.libquassel.protocol.syncables.IrcListHelper +import de.justjanne.libquassel.protocol.syncables.Session +import de.justjanne.libquassel.protocol.variant.QVariantList +import de.justjanne.libquassel.protocol.variant.into +import kotlin.coroutines.Continuation +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine + +class ClientIrcListHelper( + session: Session +) : IrcListHelper(session) { + private val waitingContinuations = mutableMapOf<NetworkId, Continuation<List<ChannelDescription>>>() + private val readyContinuations = mutableMapOf<NetworkId, Continuation<List<ChannelDescription>>>() + + suspend fun channelList( + networkId: NetworkId, + channelFilters: List<String> + ) = suspendCoroutine<List<ChannelDescription>> { + waitingContinuations[networkId] = it + requestChannelList(networkId, channelFilters) + } + + override fun reportFinishedList(netId: NetworkId) { + val continuation = waitingContinuations.remove(netId) + if (continuation != null) { + readyContinuations[netId] = continuation + requestChannelList(netId, emptyList()) + } + super.reportFinishedList(netId) + } + + override fun reportError(error: String?) { + for (continuation in waitingContinuations.values + readyContinuations.values) { + continuation.resumeWith(Result.failure(IrcListException(error ?: "Unknown Error"))) + } + super.reportError(error) + } + + override fun receiveChannelList(netId: NetworkId, channelFilters: QStringList, channels: QVariantList) { + readyContinuations[netId]?.resume( + channels.mapNotNull { + val list = it.into<QVariantList>().orEmpty() + if (list.size == 3) { + ChannelDescription( + netId, + list[0].into(""), + list[1].into(0u), + list[2].into(""), + ) + } else { + null + } + } + ) + super.receiveChannelList(netId, channelFilters, channels) + } + + data class ChannelDescription( + val netId: NetworkId, + val channelName: String, + val userCount: UInt, + val topic: String + ) +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/AliasManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/AliasManager.kt index 95fd8c0..8b1e1fd 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/AliasManager.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/AliasManager.kt @@ -23,7 +23,7 @@ import de.justjanne.libquassel.protocol.variant.into import de.justjanne.libquassel.protocol.variant.qVariant import kotlinx.coroutines.flow.MutableStateFlow -class AliasManager constructor( +open class AliasManager( session: Session ) : SyncableObject(session, "AliasManager"), AliasManagerStub { override fun toVariantMap(): QVariantMap = mapOf( diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/BacklogManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/BacklogManager.kt index 30254bc..d6a15b6 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/BacklogManager.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/BacklogManager.kt @@ -9,3 +9,15 @@ */ package de.justjanne.libquassel.protocol.syncables + +import de.justjanne.libquassel.protocol.syncables.stubs.BacklogManagerStub +import de.justjanne.libquassel.protocol.variant.QVariantMap +import de.justjanne.libquassel.protocol.variant.QVariant_ + +open class BacklogManager( + session: Session +) : SyncableObject(session, "BacklogManager"), BacklogManagerStub { + + override fun fromVariantMap(properties: QVariantMap) = Unit + override fun toVariantMap() = mapOf<String, QVariant_>() +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/BufferSyncer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/BufferSyncer.kt index 30254bc..ae81bcb 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/BufferSyncer.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/BufferSyncer.kt @@ -9,3 +9,210 @@ */ package de.justjanne.libquassel.protocol.syncables + +import de.justjanne.bitflags.none +import de.justjanne.bitflags.of +import de.justjanne.bitflags.toBits +import de.justjanne.libquassel.protocol.models.BufferInfo +import de.justjanne.libquassel.protocol.models.flags.MessageType +import de.justjanne.libquassel.protocol.models.flags.MessageTypes +import de.justjanne.libquassel.protocol.models.ids.BufferId +import de.justjanne.libquassel.protocol.models.ids.MsgId +import de.justjanne.libquassel.protocol.models.ids.isValid +import de.justjanne.libquassel.protocol.models.types.QtType +import de.justjanne.libquassel.protocol.models.types.QuasselType +import de.justjanne.libquassel.protocol.syncables.state.BufferSyncerState +import de.justjanne.libquassel.protocol.syncables.stubs.BufferSyncerStub +import de.justjanne.libquassel.protocol.util.pairs +import de.justjanne.libquassel.protocol.util.update +import de.justjanne.libquassel.protocol.variant.QVariantList +import de.justjanne.libquassel.protocol.variant.QVariantMap +import de.justjanne.libquassel.protocol.variant.into +import de.justjanne.libquassel.protocol.variant.qVariant +import kotlinx.coroutines.flow.MutableStateFlow + +open class BufferSyncer( + session: Session +) : SyncableObject(session, "BufferSyncer"), BufferSyncerStub { + override fun toVariantMap() = mapOf( + "Activities" to qVariant( + state().activities.flatMap { (key, value) -> + listOf( + qVariant(key, QuasselType.BufferId), + qVariant(value.toBits(), QtType.UInt) + ) + }, + QtType.QVariantList + ), + "HighlightCounts" to qVariant( + state().highlightCounts.flatMap { (key, value) -> + listOf( + qVariant(key, QuasselType.BufferId), + qVariant(value, QtType.Int) + ) + }, + QtType.QVariantList + ), + "LastSeenMsg" to qVariant( + state().lastSeenMsg.flatMap { (key, value) -> + listOf( + qVariant(key, QuasselType.BufferId), + qVariant(value, QuasselType.MsgId) + ) + }, + QtType.QVariantList + ), + "MarkerLines" to qVariant( + state().markerLines.flatMap { (key, value) -> + listOf( + qVariant(key, QuasselType.BufferId), + qVariant(value, QuasselType.MsgId) + ) + }, + QtType.QVariantList + ), + ) + + override fun fromVariantMap(properties: QVariantMap) { + state.update { + copy( + activities = properties["Activities"].into<QVariantList>()?.pairs { a, b -> + Pair( + a.into<BufferId>() ?: return@pairs null, + MessageType.of(b.into<UInt>() ?: return@pairs null) + ) + }?.filterNotNull()?.toMap().orEmpty(), + highlightCounts = properties["HighlightCounts"].into<QVariantList>()?.pairs { a, b -> + Pair( + a.into<BufferId>() ?: return@pairs null, + b.into<Int>() ?: return@pairs null + ) + }?.filterNotNull()?.toMap().orEmpty(), + lastSeenMsg = properties["LastSeenMsg"].into<QVariantList>()?.pairs { a, b -> + Pair( + a.into<BufferId>() ?: return@pairs null, + b.into<MsgId>() ?: return@pairs null + ) + }?.filterNotNull()?.toMap().orEmpty(), + markerLines = properties["MarkerLines"].into<QVariantList>()?.pairs { a, b -> + Pair( + a.into<BufferId>() ?: return@pairs null, + b.into<MsgId>() ?: return@pairs null + ) + }?.filterNotNull()?.toMap().orEmpty() + ) + } + } + + fun lastSeenMsg(buffer: BufferId): MsgId = state().lastSeenMsg[buffer] ?: MsgId(0) + + fun markerLine(buffer: BufferId): MsgId = state().markerLines[buffer] ?: MsgId(0) + + fun activity(buffer: BufferId): MessageTypes = + state().activities[buffer] ?: MessageType.none() + + fun highlightCount(buffer: BufferId): Int = state().highlightCounts[buffer] ?: 0 + + fun bufferInfo(bufferId: BufferId) = state().bufferInfos[bufferId] + + fun bufferInfos(): Collection<BufferInfo> = state().bufferInfos.values.toList() + + override fun mergeBuffersPermanently(buffer: BufferId, buffer2: BufferId) { + removeBuffer(buffer2) + super.mergeBuffersPermanently(buffer, buffer2) + } + + override fun removeBuffer(buffer: BufferId) { + state.update { + copy( + activities = activities - buffer, + lastSeenMsg = lastSeenMsg - buffer, + markerLines = markerLines - buffer, + highlightCounts = highlightCounts - buffer, + bufferInfos = bufferInfos - buffer + ) + } + super.removeBuffer(buffer) + } + + override fun setLastSeenMsg(buffer: BufferId, msgId: MsgId) { + if (!msgId.isValid() || lastSeenMsg(buffer) >= msgId) { + return + } + + state.update { + copy(lastSeenMsg = lastSeenMsg + Pair(buffer, msgId)) + } + + super.setLastSeenMsg(buffer, msgId) + } + + override fun setMarkerLine(buffer: BufferId, msgId: MsgId) { + if (!msgId.isValid() || markerLine(buffer) >= msgId) { + return + } + + state.update { + copy(markerLines = markerLines + Pair(buffer, msgId)) + } + + super.setMarkerLine(buffer, msgId) + } + + override fun setBufferActivity(buffer: BufferId, types: Int) { + state.update { + copy(activities = activities + Pair(buffer, MessageType.of(types.toUInt()))) + } + + super.setBufferActivity(buffer, types) + } + + fun setBufferActivity(buffer: BufferId, types: MessageTypes) { + val oldTypes = activity(buffer) + + state.update { + copy(activities = activities + Pair(buffer, types)) + } + + if ((types - oldTypes).isNotEmpty()) { + val bufferInfo = bufferInfo(buffer) + + if (bufferInfo != null) { + session.bufferViewManager().handleBuffer(bufferInfo, true) + } + } + + super.setBufferActivity(buffer, types.toBits().toInt()) + } + + override fun setHighlightCount(buffer: BufferId, count: Int) { + state.update { + copy(highlightCounts = highlightCounts + Pair(buffer, count)) + } + super.setHighlightCount(buffer, count) + } + + fun bufferInfoUpdated(info: BufferInfo) { + val oldInfo = bufferInfo(info.bufferId) + if (info != oldInfo) { + state.update { + copy(bufferInfos = bufferInfos + Pair(info.bufferId, info)) + } + + if (oldInfo != null) { + session.bufferViewManager().handleBuffer(info) + } + } + } + + @Suppress("NOTHING_TO_INLINE") + inline fun state() = flow().value + + @Suppress("NOTHING_TO_INLINE") + inline fun flow() = state + + @PublishedApi + internal val state = MutableStateFlow( + BufferSyncerState() + ) +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/BufferViewConfig.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/BufferViewConfig.kt index 30254bc..583f119 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/BufferViewConfig.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/BufferViewConfig.kt @@ -9,3 +9,269 @@ */ package de.justjanne.libquassel.protocol.syncables + +import de.justjanne.bitflags.of +import de.justjanne.bitflags.toBits +import de.justjanne.libquassel.protocol.models.BufferInfo +import de.justjanne.libquassel.protocol.models.flags.BufferActivity +import de.justjanne.libquassel.protocol.models.flags.BufferType +import de.justjanne.libquassel.protocol.models.ids.BufferId +import de.justjanne.libquassel.protocol.models.ids.NetworkId +import de.justjanne.libquassel.protocol.models.types.QtType +import de.justjanne.libquassel.protocol.models.types.QuasselType +import de.justjanne.libquassel.protocol.syncables.state.BufferViewConfigState +import de.justjanne.libquassel.protocol.syncables.stubs.BufferViewConfigStub +import de.justjanne.libquassel.protocol.util.insert +import de.justjanne.libquassel.protocol.util.move +import de.justjanne.libquassel.protocol.util.update +import de.justjanne.libquassel.protocol.variant.QVariantList +import de.justjanne.libquassel.protocol.variant.QVariantMap +import de.justjanne.libquassel.protocol.variant.into +import de.justjanne.libquassel.protocol.variant.qVariant +import kotlinx.coroutines.flow.MutableStateFlow + +open class BufferViewConfig( + bufferViewId: Int, + session: Session +) : SyncableObject(session, "BufferViewConfig"), BufferViewConfigStub { + override fun fromVariantMap(properties: QVariantMap) { + state.update { + copy( + buffers = properties["BufferList"].into<QVariantList>() + ?.mapNotNull { it.into<BufferId>() } + .orEmpty(), + removedBuffers = properties["RemovedBuffers"].into<QVariantList>() + ?.mapNotNull { it.into<BufferId>() } + ?.toSet() + .orEmpty(), + temporarilyRemovedBuffers = properties["TemporarilyRemovedBuffers"].into<QVariantList>() + ?.mapNotNull { it.into<BufferId>() } + ?.toSet() + .orEmpty(), + bufferViewName = properties["bufferViewName"].into(bufferViewName()), + networkId = properties["networkId"].into(networkId()), + addNewBuffersAutomatically = properties["addNewBuffersAutomatically"].into(addNewBuffersAutomatically()), + sortAlphabetically = properties["sortAlphabetically"].into(sortAlphabetically()), + hideInactiveBuffers = properties["hideInactiveBuffers"].into(hideInactiveBuffers()), + hideInactiveNetworks = properties["hideInactiveNetworks"].into(hideInactiveNetworks()), + disableDecoration = properties["disableDecoration"].into(disableDecoration()), + allowedBufferTypes = properties["allowedBufferTypes"].into(allowedBufferTypes()), + minimumActivity = properties["minimumActivity"].into(minimumActivity()), + showSearch = properties["showSearch"].into(showSearch()), + ) + } + } + + override fun toVariantMap() = mapOf( + "BufferList" to qVariant( + buffers().map { + qVariant(it, QuasselType.BufferId) + }, + QtType.QVariantList + ), + "RemovedBuffers" to qVariant( + removedBuffers().map { + qVariant(it, QuasselType.BufferId) + }, + QtType.QVariantList + ), + "TemporarilyRemovedBuffers" to qVariant( + temporarilyRemovedBuffers().map { + qVariant(it, QuasselType.BufferId) + }, + QtType.QVariantList + ), + "bufferViewName" to qVariant(bufferViewName(), QtType.QString), + "networkId" to qVariant(networkId(), QuasselType.NetworkId), + "addNewBuffersAutomatically" to qVariant(addNewBuffersAutomatically(), QtType.Bool), + "sortAlphabetically" to qVariant(sortAlphabetically(), QtType.Bool), + "hideInactiveBuffers" to qVariant(hideInactiveBuffers(), QtType.Bool), + "hideInactiveNetworks" to qVariant(hideInactiveNetworks(), QtType.Bool), + "disableDecoration" to qVariant(disableDecoration(), QtType.Bool), + "allowedBufferTypes" to qVariant(allowedBufferTypes().toBits().toInt(), QtType.Int), + "minimumActivity" to qVariant(minimumActivity().toBits().toInt(), QtType.Int), + "showSearch" to qVariant(showSearch(), QtType.Bool) + ) + + fun bufferViewId() = state().bufferViewId + fun bufferViewName() = state().bufferViewName + fun networkId() = state().networkId + fun addNewBuffersAutomatically() = state().addNewBuffersAutomatically + fun sortAlphabetically() = state().sortAlphabetically + fun hideInactiveBuffers() = state().hideInactiveBuffers + fun hideInactiveNetworks() = state().hideInactiveNetworks + fun disableDecoration() = state().disableDecoration + fun allowedBufferTypes() = state().allowedBufferTypes + fun minimumActivity() = state().minimumActivity + fun showSearch() = state().showSearch + + fun buffers() = state().buffers + fun removedBuffers() = state().removedBuffers + fun temporarilyRemovedBuffers() = state().temporarilyRemovedBuffers + + override fun addBuffer(buffer: BufferId, pos: Int) { + state.update { + copy( + buffers = buffers.insert(buffer, pos), + removedBuffers = removedBuffers - buffer, + temporarilyRemovedBuffers = temporarilyRemovedBuffers - buffer + ) + } + + super.addBuffer(buffer, pos) + } + + override fun removeBuffer(buffer: BufferId) { + state.update { + copy( + buffers = buffers - buffer, + removedBuffers = removedBuffers - buffer, + temporarilyRemovedBuffers = temporarilyRemovedBuffers + buffer + ) + } + + super.removeBuffer(buffer) + } + + override fun removeBufferPermanently(buffer: BufferId) { + state.update { + copy( + buffers = buffers - buffer, + removedBuffers = removedBuffers + buffer, + temporarilyRemovedBuffers = temporarilyRemovedBuffers - buffer + ) + } + + super.removeBufferPermanently(buffer) + } + + override fun moveBuffer(buffer: BufferId, pos: Int) { + if (!buffers().contains(buffer)) { + return + } + + state.update { + copy( + buffers = buffers.move(buffer, pos), + removedBuffers = removedBuffers - buffer, + temporarilyRemovedBuffers = temporarilyRemovedBuffers - buffer + ) + } + + super.moveBuffer(buffer, pos) + } + + override fun setBufferViewName(value: String) { + state.update { + copy(bufferViewName = value) + } + super.setBufferViewName(value) + } + + override fun setAddNewBuffersAutomatically(value: Boolean) { + state.update { + copy(addNewBuffersAutomatically = value) + } + super.setAddNewBuffersAutomatically(value) + } + + override fun setAllowedBufferTypes(value: Int) { + state.update { + copy(allowedBufferTypes = BufferType.of(value.toUShort())) + } + super.setAllowedBufferTypes(value) + } + + override fun setDisableDecoration(value: Boolean) { + state.update { + copy(disableDecoration = value) + } + super.setDisableDecoration(value) + } + + override fun setHideInactiveBuffers(value: Boolean) { + state.update { + copy(hideInactiveBuffers = value) + } + super.setHideInactiveBuffers(value) + } + + override fun setHideInactiveNetworks(value: Boolean) { + state.update { + copy(hideInactiveNetworks = value) + } + super.setHideInactiveNetworks(value) + } + + override fun setMinimumActivity(value: Int) { + state.update { + copy(minimumActivity = BufferActivity.of(value.toUInt())) + } + super.setMinimumActivity(value) + } + + override fun setNetworkId(value: NetworkId) { + state.update { + copy(networkId = value) + } + super.setNetworkId(value) + } + + override fun setShowSearch(value: Boolean) { + state.update { + copy(showSearch = value) + } + super.setShowSearch(value) + } + + override fun setSortAlphabetically(value: Boolean) { + state.update { + copy(sortAlphabetically = value) + } + super.setSortAlphabetically(value) + } + + fun insertBufferSorted(info: BufferInfo) { + requestAddBuffer( + info.bufferId, + buffers() + .asSequence() + .map(session.bufferSyncer()::bufferInfo) + .withIndex() + .mapNotNull { (index, value) -> IndexedValue(index, value ?: return@mapNotNull null) } + .filter { (_, value) -> value.networkId == info.networkId } + .find { (_, value) -> + String.CASE_INSENSITIVE_ORDER.compare(value.bufferName, info.bufferName) > 0 + }?.index ?: buffers().size + ) + } + + fun handleBuffer(info: BufferInfo, unhide: Boolean = false) { + if (addNewBuffersAutomatically() && + !buffers().contains(info.bufferId) && + !temporarilyRemovedBuffers().contains(info.bufferId) && + !removedBuffers().contains(info.bufferId) && + !info.type.contains(BufferType.Status) + ) { + insertBufferSorted(info) + } else if (unhide && + !buffers().contains(info.bufferId) && + temporarilyRemovedBuffers().contains(info.bufferId) + ) { + insertBufferSorted(info) + } + } + + @Suppress("NOTHING_TO_INLINE") + inline fun state() = flow().value + + @Suppress("NOTHING_TO_INLINE") + inline fun flow() = state + + @PublishedApi + internal val state = MutableStateFlow( + BufferViewConfigState( + bufferViewId = bufferViewId + ) + ) +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/BufferViewManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/BufferViewManager.kt index 30254bc..dde3434 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/BufferViewManager.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/BufferViewManager.kt @@ -9,3 +9,69 @@ */ package de.justjanne.libquassel.protocol.syncables + +import de.justjanne.libquassel.protocol.models.BufferInfo +import de.justjanne.libquassel.protocol.models.types.QtType +import de.justjanne.libquassel.protocol.syncables.state.BufferViewManagerState +import de.justjanne.libquassel.protocol.syncables.stubs.BufferViewManagerStub +import de.justjanne.libquassel.protocol.util.update +import de.justjanne.libquassel.protocol.variant.QVariantList +import de.justjanne.libquassel.protocol.variant.QVariantMap +import de.justjanne.libquassel.protocol.variant.QVariant_ +import de.justjanne.libquassel.protocol.variant.into +import de.justjanne.libquassel.protocol.variant.qVariant +import kotlinx.coroutines.flow.MutableStateFlow + +open class BufferViewManager( + session: Session +) : SyncableObject(session, "BufferViewManager"), BufferViewManagerStub { + override fun fromVariantMap(properties: QVariantMap) { + properties["BufferViewIds"].into<QVariantList>() + ?.mapNotNull<QVariant_, Int>(QVariant_::into) + ?.forEach(this::addBufferViewConfig) + } + + override fun toVariantMap() = mapOf( + "BufferViewIds" to qVariant( + state().bufferViewConfigs.map { + qVariant(it.key, QtType.Int) + }, + QtType.QVariantList + ) + ) + + fun contains(bufferViewId: Int) = state().contains(bufferViewId) + fun bufferViewConfig(bufferViewId: Int) = state().bufferViewConfig(bufferViewId) + fun bufferViewConfigs() = state().bufferViewConfigs() + + override fun addBufferViewConfig(bufferViewConfigId: Int) { + if (contains(bufferViewConfigId)) { + return + } + + val config = BufferViewConfig(bufferViewConfigId, session) + session.synchronize(config) + state.update { + copy(bufferViewConfigs = bufferViewConfigs + Pair(bufferViewConfigId, config)) + } + + super.addBufferViewConfig(bufferViewConfigId) + } + + fun handleBuffer(info: BufferInfo, unhide: Boolean = false) { + for (bufferViewConfig in bufferViewConfigs()) { + bufferViewConfig.handleBuffer(info, unhide) + } + } + + @Suppress("NOTHING_TO_INLINE") + inline fun state() = flow().value + + @Suppress("NOTHING_TO_INLINE") + inline fun flow() = state + + @PublishedApi + internal val state = MutableStateFlow( + BufferViewManagerState() + ) +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/CertManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/CertManager.kt index f2d5172..a420f96 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/CertManager.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/CertManager.kt @@ -29,7 +29,7 @@ import java.security.PrivateKey import java.security.cert.Certificate import java.security.cert.CertificateFactory -class CertManager( +open class CertManager( identityId: IdentityId, session: Session ) : SyncableObject(session, "CertManager"), CertManagerStub { diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/CoreInfo.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/CoreInfo.kt index 95933ef..d78c7e3 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/CoreInfo.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/CoreInfo.kt @@ -23,7 +23,7 @@ import de.justjanne.libquassel.protocol.variant.qVariant import kotlinx.coroutines.flow.MutableStateFlow import org.threeten.bp.Instant -class CoreInfo constructor( +open class CoreInfo( session: Session ) : SyncableObject(session, "CoreInfo"), CoreInfoStub { override fun fromVariantMap(properties: QVariantMap) { diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/DccConfig.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/DccConfig.kt index 991995b..b199aa0 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/DccConfig.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/DccConfig.kt @@ -23,7 +23,7 @@ import de.justjanne.libquassel.protocol.variant.qVariant import kotlinx.coroutines.flow.MutableStateFlow import java.net.InetAddress -class DccConfig constructor( +open class DccConfig( session: Session ) : SyncableObject(session, "DccConfig"), DccConfigStub { override fun init() { diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/HighlightRuleManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/HighlightRuleManager.kt index 20dd19c..cc24585 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/HighlightRuleManager.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/HighlightRuleManager.kt @@ -23,7 +23,7 @@ import de.justjanne.libquassel.protocol.variant.into import de.justjanne.libquassel.protocol.variant.qVariant import kotlinx.coroutines.flow.MutableStateFlow -class HighlightRuleManager( +open class HighlightRuleManager( session: Session ) : SyncableObject(session, "HighlightRuleManager"), HighlightRuleManagerStub { override fun fromVariantMap(properties: QVariantMap) { diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Identity.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Identity.kt index 30254bc..6690de8 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Identity.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Identity.kt @@ -9,3 +9,235 @@ */ package de.justjanne.libquassel.protocol.syncables + +import de.justjanne.libquassel.protocol.models.QStringList +import de.justjanne.libquassel.protocol.models.ids.IdentityId +import de.justjanne.libquassel.protocol.models.types.QtType +import de.justjanne.libquassel.protocol.models.types.QuasselType +import de.justjanne.libquassel.protocol.syncables.state.IdentityState +import de.justjanne.libquassel.protocol.syncables.stubs.IdentityStub +import de.justjanne.libquassel.protocol.util.update +import de.justjanne.libquassel.protocol.variant.QVariantMap +import de.justjanne.libquassel.protocol.variant.into +import de.justjanne.libquassel.protocol.variant.qVariant +import kotlinx.coroutines.flow.MutableStateFlow + +open class Identity( + session: Session +) : SyncableObject(session, "Identity"), IdentityStub { + override fun init() { + renameObject(state().identifier()) + } + + override fun fromVariantMap(properties: QVariantMap) { + state.update { + copy( + identityId = properties["identityId"].into(identityId), + identityName = properties["identityName"].into(identityName), + realName = properties["realName"].into(realName), + nicks = properties["nicks"].into(nicks), + awayNick = properties["awayNick"].into(awayNick), + awayNickEnabled = properties["awayNickEnabled"].into(awayNickEnabled), + awayReason = properties["awayReason"].into(awayReason), + awayReasonEnabled = properties["awayReasonEnabled"].into(awayReasonEnabled), + autoAwayEnabled = properties["autoAwayEnabled"].into(autoAwayEnabled), + autoAwayTime = properties["autoAwayTime"].into(autoAwayTime), + autoAwayReason = properties["autoAwayReason"].into(autoAwayReason), + autoAwayReasonEnabled = properties["autoAwayReasonEnabled"].into(autoAwayReasonEnabled), + detachAwayEnabled = properties["detachAwayEnabled"].into(detachAwayEnabled), + detachAwayReason = properties["detachAwayReason"].into(detachAwayReason), + detachAwayReasonEnabled = properties["detachAwayReasonEnabled"].into(detachAwayReasonEnabled), + ident = properties["ident"].into(ident), + kickReason = properties["kickReason"].into(kickReason), + partReason = properties["partReason"].into(partReason), + quitReason = properties["quitReason"].into(quitReason), + ) + } + } + + override fun toVariantMap() = mapOf( + "identityId" to qVariant(id(), QuasselType.IdentityId), + "identityName" to qVariant(identityName(), QtType.QString), + "realName" to qVariant(realName(), QtType.QString), + "nicks" to qVariant(nicks(), QtType.QStringList), + "awayNick" to qVariant(awayNick(), QtType.QString), + "awayNickEnabled" to qVariant(awayNickEnabled(), QtType.Bool), + "awayReason" to qVariant(awayReason(), QtType.QString), + "awayReasonEnabled" to qVariant(awayReasonEnabled(), QtType.Bool), + "autoAwayEnabled" to qVariant(autoAwayEnabled(), QtType.Bool), + "autoAwayTime" to qVariant(autoAwayTime(), QtType.Int), + "autoAwayReason" to qVariant(autoAwayReason(), QtType.QString), + "autoAwayReasonEnabled" to qVariant(autoAwayReasonEnabled(), QtType.Bool), + "detachAwayEnabled" to qVariant(detachAwayEnabled(), QtType.Bool), + "detachAwayReason" to qVariant(detachAwayReason(), QtType.QString), + "detachAwayReasonEnabled" to qVariant(detachAwayReasonEnabled(), QtType.Bool), + "ident" to qVariant(ident(), QtType.QString), + "kickReason" to qVariant(kickReason(), QtType.QString), + "partReason" to qVariant(partReason(), QtType.QString), + "quitReason" to qVariant(quitReason(), QtType.QString) + ) + + fun id() = state().identityId + fun identityName() = state().identityName + fun realName() = state().realName + fun nicks() = state().nicks + fun awayNick() = state().awayNick + fun awayNickEnabled() = state().awayNickEnabled + fun awayReason() = state().awayReason + fun awayReasonEnabled() = state().awayReasonEnabled + fun autoAwayEnabled() = state().autoAwayEnabled + fun autoAwayTime() = state().autoAwayTime + fun autoAwayReason() = state().autoAwayReason + fun autoAwayReasonEnabled() = state().autoAwayReasonEnabled + fun detachAwayEnabled() = state().detachAwayEnabled + fun detachAwayReason() = state().detachAwayReason + fun detachAwayReasonEnabled() = state().detachAwayReasonEnabled + fun ident() = state().ident + fun kickReason() = state().kickReason + fun partReason() = state().partReason + fun quitReason() = state().quitReason + + override fun setAutoAwayEnabled(enabled: Boolean) { + state.update { + copy(autoAwayEnabled = enabled) + } + super.setAutoAwayEnabled(enabled) + } + + override fun setAutoAwayReason(reason: String?) { + state.update { + copy(autoAwayReason = reason ?: "") + } + super.setAutoAwayReason(reason) + } + + override fun setAutoAwayReasonEnabled(enabled: Boolean) { + state.update { + copy(autoAwayReasonEnabled = enabled) + } + super.setAutoAwayReasonEnabled(enabled) + } + + override fun setAutoAwayTime(time: Int) { + state.update { + copy(autoAwayTime = time) + } + super.setAutoAwayTime(time) + } + + override fun setAwayNick(awayNick: String?) { + state.update { + copy(awayNick = awayNick ?: "") + } + super.setAwayNick(awayNick) + } + + override fun setAwayNickEnabled(enabled: Boolean) { + state.update { + copy(awayNickEnabled = enabled) + } + super.setAwayNickEnabled(enabled) + } + + override fun setAwayReason(awayReason: String?) { + state.update { + copy(awayReason = awayReason ?: "") + } + super.setAwayReason(awayReason) + } + + override fun setAwayReasonEnabled(enabled: Boolean) { + state.update { + copy(awayReasonEnabled = enabled) + } + super.setAwayReasonEnabled(enabled) + } + + override fun setDetachAwayEnabled(enabled: Boolean) { + state.update { + copy(detachAwayEnabled = enabled) + } + super.setDetachAwayEnabled(enabled) + } + + override fun setDetachAwayReason(reason: String?) { + state.update { + copy(detachAwayReason = reason ?: "") + } + super.setDetachAwayReason(reason) + } + + override fun setDetachAwayReasonEnabled(enabled: Boolean) { + state.update { + copy(detachAwayReasonEnabled = enabled) + } + super.setDetachAwayReasonEnabled(enabled) + } + + override fun setId(id: IdentityId) { + state.update { + copy(identityId = id) + } + super.setId(id) + } + + override fun setIdent(ident: String?) { + state.update { + copy(ident = ident ?: "") + } + super.setIdent(ident) + } + + override fun setIdentityName(name: String?) { + state.update { + copy(identityName = name ?: "") + } + super.setIdentityName(name) + } + + override fun setKickReason(reason: String?) { + state.update { + copy(kickReason = reason ?: "") + } + super.setKickReason(reason) + } + + override fun setNicks(nicks: QStringList) { + state.update { + copy(nicks = nicks.map { it ?: "" }) + } + super.setNicks(nicks) + } + + override fun setPartReason(reason: String?) { + state.update { + copy(partReason = reason ?: "") + } + super.setPartReason(reason) + } + + override fun setQuitReason(reason: String?) { + state.update { + copy(quitReason = reason ?: "") + } + super.setQuitReason(reason) + } + + override fun setRealName(realName: String?) { + state.update { + copy(realName = realName ?: "") + } + super.setRealName(realName) + } + + @Suppress("NOTHING_TO_INLINE") + inline fun state() = flow().value + + @Suppress("NOTHING_TO_INLINE") + inline fun flow() = state + + @PublishedApi + internal val state = MutableStateFlow( + IdentityState() + ) +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/IrcChannel.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/IrcChannel.kt index 885b4f0..210546c 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/IrcChannel.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/IrcChannel.kt @@ -24,7 +24,7 @@ import de.justjanne.libquassel.protocol.variant.into import de.justjanne.libquassel.protocol.variant.qVariant import kotlinx.coroutines.flow.MutableStateFlow -class IrcChannel( +open class IrcChannel( name: String, network: NetworkId, session: Session diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/IrcListHelper.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/IrcListHelper.kt index 30254bc..c997cb7 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/IrcListHelper.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/IrcListHelper.kt @@ -9,3 +9,14 @@ */ package de.justjanne.libquassel.protocol.syncables + +import de.justjanne.libquassel.protocol.syncables.stubs.IrcListHelperStub +import de.justjanne.libquassel.protocol.variant.QVariantMap +import de.justjanne.libquassel.protocol.variant.QVariant_ + +open class IrcListHelper( + session: Session +) : SyncableObject(session, "IrcListHelper"), IrcListHelperStub { + override fun fromVariantMap(properties: QVariantMap) = Unit + override fun toVariantMap() = emptyMap<String, QVariant_>() +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/IrcUser.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/IrcUser.kt index 115f120..36b4fb2 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/IrcUser.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/IrcUser.kt @@ -24,7 +24,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import org.threeten.bp.Instant import org.threeten.bp.temporal.Temporal -class IrcUser( +open class IrcUser( hostmask: String, network: NetworkId, session: Session diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Network.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Network.kt index 80bbef5..67a9bcc 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Network.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Network.kt @@ -34,7 +34,7 @@ import de.justjanne.libquassel.protocol.variant.qVariant import kotlinx.coroutines.flow.MutableStateFlow import java.nio.ByteBuffer -class Network constructor( +open class Network( networkId: NetworkId, session: Session ) : SyncableObject(session, "Network"), NetworkStub { diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/NetworkConfig.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/NetworkConfig.kt index 30254bc..36b4edc 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/NetworkConfig.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/NetworkConfig.kt @@ -9,3 +9,122 @@ */ package de.justjanne.libquassel.protocol.syncables + +import de.justjanne.libquassel.protocol.models.types.QtType +import de.justjanne.libquassel.protocol.syncables.state.NetworkConfigState +import de.justjanne.libquassel.protocol.syncables.stubs.NetworkConfigStub +import de.justjanne.libquassel.protocol.util.update +import de.justjanne.libquassel.protocol.variant.QVariantMap +import de.justjanne.libquassel.protocol.variant.into +import de.justjanne.libquassel.protocol.variant.qVariant +import kotlinx.coroutines.flow.MutableStateFlow + +open class NetworkConfig( + session: Session +) : SyncableObject(session, "NetworkConfig"), NetworkConfigStub { + override fun init() { + renameObject("GlobalNetworkConfig") + } + + override fun fromVariantMap(properties: QVariantMap) { + state.update { + copy( + pingTimeoutEnabled = properties["pingTimeoutEnabled"].into(pingTimeoutEnabled), + pingInterval = properties["pingInterval"].into(pingInterval), + maxPingCount = properties["maxPingCount"].into(maxPingCount), + autoWhoEnabled = properties["autoWhoEnabled"].into(autoWhoEnabled), + autoWhoInterval = properties["autoWhoInterval"].into(autoWhoInterval), + autoWhoNickLimit = properties["autoWhoNickLimit"].into(autoWhoNickLimit), + autoWhoDelay = properties["autoWhoDelay"].into(autoWhoDelay), + standardCtcp = properties["standardCtcp"].into(standardCtcp), + ) + } + } + + override fun toVariantMap() = mapOf( + "pingTimeoutEnabled" to qVariant(pingTimeoutEnabled(), QtType.Bool), + "pingInterval" to qVariant(pingInterval(), QtType.Int), + "maxPingCount" to qVariant(maxPingCount(), QtType.Int), + "autoWhoEnabled" to qVariant(autoWhoEnabled(), QtType.Bool), + "autoWhoInterval" to qVariant(autoWhoInterval(), QtType.Int), + "autoWhoNickLimit" to qVariant(autoWhoNickLimit(), QtType.Int), + "autoWhoDelay" to qVariant(autoWhoDelay(), QtType.Int), + "standardCtcp" to qVariant(standardCtcp(), QtType.Bool) + ) + + fun pingTimeoutEnabled() = state().pingTimeoutEnabled + fun pingInterval() = state().pingInterval + fun maxPingCount() = state().maxPingCount + fun autoWhoEnabled() = state().autoWhoEnabled + fun autoWhoInterval() = state().autoWhoInterval + fun autoWhoNickLimit() = state().autoWhoNickLimit + fun autoWhoDelay() = state().autoWhoDelay + fun standardCtcp() = state().standardCtcp + + override fun setAutoWhoDelay(delay: Int) { + state.update { + copy(autoWhoDelay = delay) + } + super.setAutoWhoDelay(delay) + } + + override fun setAutoWhoEnabled(enabled: Boolean) { + state.update { + copy(autoWhoEnabled = enabled) + } + super.setAutoWhoEnabled(enabled) + } + + override fun setAutoWhoInterval(interval: Int) { + state.update { + copy(autoWhoInterval = interval) + } + super.setAutoWhoInterval(interval) + } + + override fun setAutoWhoNickLimit(limit: Int) { + state.update { + copy(autoWhoNickLimit = limit) + } + super.setAutoWhoNickLimit(limit) + } + + override fun setMaxPingCount(count: Int) { + state.update { + copy(maxPingCount = count) + } + super.setMaxPingCount(count) + } + + override fun setPingInterval(interval: Int) { + state.update { + copy(pingInterval = interval) + } + super.setPingInterval(interval) + } + + override fun setPingTimeoutEnabled(enabled: Boolean) { + state.update { + copy(pingTimeoutEnabled = enabled) + } + super.setPingTimeoutEnabled(enabled) + } + + override fun setStandardCtcp(enabled: Boolean) { + state.update { + copy(standardCtcp = enabled) + } + super.setStandardCtcp(enabled) + } + + @Suppress("NOTHING_TO_INLINE") + inline fun state() = flow().value + + @Suppress("NOTHING_TO_INLINE") + inline fun flow() = state + + @PublishedApi + internal val state = MutableStateFlow( + NetworkConfigState() + ) +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Session.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Session.kt index 75fd70a..42c2184 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Session.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Session.kt @@ -14,17 +14,30 @@ import de.justjanne.libquassel.annotations.ProtocolSide import de.justjanne.libquassel.protocol.models.SignalProxyMessage import de.justjanne.libquassel.protocol.models.ids.IdentityId import de.justjanne.libquassel.protocol.models.ids.NetworkId -import de.justjanne.libquassel.protocol.syncables.stubs.IdentityStub +import de.justjanne.libquassel.protocol.syncables.stubs.BacklogManagerStub +import de.justjanne.libquassel.protocol.syncables.stubs.IgnoreListManagerStub +import de.justjanne.libquassel.protocol.syncables.stubs.IrcListHelperStub import de.justjanne.libquassel.protocol.syncables.stubs.RpcHandlerStub import de.justjanne.libquassel.protocol.variant.QVariantList interface Session : RpcHandlerStub { val protocolSide: ProtocolSide - val objectRepository: ObjectRepository fun network(id: NetworkId): Network? - fun identity(id: IdentityId): IdentityStub + fun identity(id: IdentityId): Identity + + fun aliasManager(): AliasManager + fun bufferSyncer(): BufferSyncer + fun backlogManager(): BacklogManagerStub + fun bufferViewManager(): BufferViewManager + fun ignoreListManager(): IgnoreListManagerStub + fun highlightRuleManager(): HighlightRuleManager + fun ircListHelper(): IrcListHelperStub + + fun coreInfo(): CoreInfo + fun dccConfig(): DccConfig + fun networkConfig(): NetworkConfig fun synchronize(it: SyncableObject) fun stopSynchronize(it: SyncableObject) diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/BufferSyncerState.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/BufferSyncerState.kt index 182244f..b479fbc 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/BufferSyncerState.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/BufferSyncerState.kt @@ -9,3 +9,54 @@ */ package de.justjanne.libquassel.protocol.syncables.state + +import de.justjanne.libquassel.protocol.models.BufferInfo +import de.justjanne.libquassel.protocol.models.flags.BufferTypes +import de.justjanne.libquassel.protocol.models.flags.MessageTypes +import de.justjanne.libquassel.protocol.models.ids.BufferId +import de.justjanne.libquassel.protocol.models.ids.MsgId +import de.justjanne.libquassel.protocol.models.ids.NetworkId + +data class BufferSyncerState( + val activities: Map<BufferId, MessageTypes> = emptyMap(), + val highlightCounts: Map<BufferId, Int> = emptyMap(), + val lastSeenMsg: Map<BufferId, MsgId> = emptyMap(), + val markerLines: Map<BufferId, MsgId> = emptyMap(), + val bufferInfos: Map<BufferId, BufferInfo> = emptyMap() +) { + fun where( + bufferName: String? = null, + bufferId: BufferId? = null, + networkId: NetworkId? = null, + type: BufferTypes? = null, + groupId: Int? = null, + networkState: NetworkState? = null + ) = bufferInfos.values.asSequence() + .filter { + bufferName == null || + networkState == null || + networkState.caseMapper().equalsIgnoreCase(it.bufferName, bufferName) + } + .filter { bufferId == null || it.bufferId == bufferId } + .filter { networkId == null || it.networkId == networkId } + .filter { type == null || it.type == type } + .filter { groupId == null || it.groupId == groupId } + + fun find( + bufferName: String? = null, + bufferId: BufferId? = null, + networkId: NetworkId? = null, + type: BufferTypes? = null, + groupId: Int? = null, + networkState: NetworkState? = null + ) = where(bufferName, bufferId, networkId, type, groupId, networkState).firstOrNull() + + fun all( + bufferName: String? = null, + bufferId: BufferId? = null, + networkId: NetworkId? = null, + type: BufferTypes? = null, + groupId: Int? = null, + networkState: NetworkState? = null + ) = where(bufferName, bufferId, networkId, type, groupId, networkState).toList() +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/BufferViewConfigState.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/BufferViewConfigState.kt index 182244f..663ba1e 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/BufferViewConfigState.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/BufferViewConfigState.kt @@ -9,3 +9,28 @@ */ package de.justjanne.libquassel.protocol.syncables.state + +import de.justjanne.bitflags.none +import de.justjanne.libquassel.protocol.models.flags.BufferActivities +import de.justjanne.libquassel.protocol.models.flags.BufferActivity +import de.justjanne.libquassel.protocol.models.flags.BufferType +import de.justjanne.libquassel.protocol.models.flags.BufferTypes +import de.justjanne.libquassel.protocol.models.ids.BufferId +import de.justjanne.libquassel.protocol.models.ids.NetworkId + +data class BufferViewConfigState( + val bufferViewId: Int, + val bufferViewName: String = "", + val networkId: NetworkId = NetworkId(0), + val addNewBuffersAutomatically: Boolean = true, + val sortAlphabetically: Boolean = true, + val hideInactiveBuffers: Boolean = false, + val hideInactiveNetworks: Boolean = false, + val disableDecoration: Boolean = false, + val allowedBufferTypes: BufferTypes = BufferType.all, + val minimumActivity: BufferActivities = BufferActivity.none(), + val showSearch: Boolean = false, + val buffers: List<BufferId> = emptyList(), + val removedBuffers: Set<BufferId> = emptySet(), + val temporarilyRemovedBuffers: Set<BufferId> = emptySet(), +) diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/BufferViewManagerState.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/BufferViewManagerState.kt index 182244f..785a6f4 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/BufferViewManagerState.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/BufferViewManagerState.kt @@ -9,3 +9,13 @@ */ package de.justjanne.libquassel.protocol.syncables.state + +import de.justjanne.libquassel.protocol.syncables.BufferViewConfig + +data class BufferViewManagerState( + val bufferViewConfigs: Map<Int, BufferViewConfig> = emptyMap() +) { + fun contains(bufferViewId: Int) = bufferViewConfigs.containsKey(bufferViewId) + fun bufferViewConfig(bufferViewId: Int) = bufferViewConfigs[bufferViewId] + fun bufferViewConfigs() = bufferViewConfigs.values +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/IdentityState.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/IdentityState.kt index 182244f..d814eb7 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/IdentityState.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/IdentityState.kt @@ -9,3 +9,29 @@ */ package de.justjanne.libquassel.protocol.syncables.state + +import de.justjanne.libquassel.protocol.models.ids.IdentityId + +data class IdentityState( + val identityId: IdentityId = IdentityId(-1), + val identityName: String = "<empty>", + val realName: String = "", + val nicks: List<String> = listOf("quassel"), + val awayNick: String = "", + val awayNickEnabled: Boolean = false, + val awayReason: String = "Gone fishing.", + val awayReasonEnabled: Boolean = true, + val autoAwayEnabled: Boolean = false, + val autoAwayTime: Int = 10, + val autoAwayReason: String = "Not here. No really. not here!", + val autoAwayReasonEnabled: Boolean = false, + val detachAwayEnabled: Boolean = false, + val detachAwayReason: String = "All Quassel clients vanished from the face of the earth...", + val detachAwayReasonEnabled: Boolean = false, + val ident: String = "quassel", + val kickReason: String = "Kindergarten is elsewhere!", + val partReason: String = "http://quassel-irc.org - Chat comfortably. Anywhere.", + val quitReason: String = "http://quassel-irc.org - Chat comfortably. Anywhere." +) { + fun identifier() = "${identityId.id}" +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/NetworkConfigState.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/NetworkConfigState.kt index 182244f..5733a20 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/NetworkConfigState.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/NetworkConfigState.kt @@ -9,3 +9,14 @@ */ package de.justjanne.libquassel.protocol.syncables.state + +data class NetworkConfigState( + val pingTimeoutEnabled: Boolean = true, + val pingInterval: Int = 30, + val maxPingCount: Int = 6, + val autoWhoEnabled: Boolean = true, + val autoWhoInterval: Int = 90, + val autoWhoNickLimit: Int = 200, + val autoWhoDelay: Int = 5, + val standardCtcp: Boolean = false +) diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/stubs/BacklogManagerStub.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/stubs/BacklogManagerStub.kt index 77656fb..f9dc5b8 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/stubs/BacklogManagerStub.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/stubs/BacklogManagerStub.kt @@ -18,6 +18,7 @@ import de.justjanne.libquassel.protocol.models.ids.MsgId import de.justjanne.libquassel.protocol.models.types.QtType import de.justjanne.libquassel.protocol.models.types.QuasselType import de.justjanne.libquassel.protocol.syncables.SyncableStub +import de.justjanne.libquassel.protocol.variant.QVariantList import de.justjanne.libquassel.protocol.variant.qVariant @SyncedObject("BacklogManager") @@ -108,7 +109,8 @@ interface BacklogManagerStub : SyncableStub { first: MsgId = MsgId(-1), last: MsgId = MsgId(-1), limit: Int = -1, - additional: Int = 0 + additional: Int = 0, + messages: QVariantList ) { sync( target = ProtocolSide.CLIENT, @@ -118,6 +120,7 @@ interface BacklogManagerStub : SyncableStub { qVariant(last, QuasselType.MsgId), qVariant(limit, QtType.Int), qVariant(additional, QtType.Int), + qVariant(messages, QtType.QVariantList), ) } @@ -129,7 +132,8 @@ interface BacklogManagerStub : SyncableStub { limit: Int = -1, additional: Int = 0, type: Int = -1, - flags: Int = -1 + flags: Int = -1, + messages: QVariantList ) { sync( target = ProtocolSide.CLIENT, @@ -141,6 +145,7 @@ interface BacklogManagerStub : SyncableStub { qVariant(additional, QtType.Int), qVariant(type, QtType.Int), qVariant(flags, QtType.Int), + qVariant(messages, QtType.QVariantList), ) } @@ -149,7 +154,8 @@ interface BacklogManagerStub : SyncableStub { first: MsgId = MsgId(-1), last: MsgId = MsgId(-1), limit: Int = -1, - additional: Int = 0 + additional: Int = 0, + messages: QVariantList ) { sync( target = ProtocolSide.CLIENT, @@ -158,6 +164,7 @@ interface BacklogManagerStub : SyncableStub { qVariant(last, QuasselType.MsgId), qVariant(limit, QtType.Int), qVariant(additional, QtType.Int), + qVariant(messages, QtType.QVariantList), ) } @@ -168,7 +175,8 @@ interface BacklogManagerStub : SyncableStub { limit: Int = -1, additional: Int = 0, type: Int = -1, - flags: Int = -1 + flags: Int = -1, + messages: QVariantList ) { sync( target = ProtocolSide.CLIENT, @@ -179,6 +187,7 @@ interface BacklogManagerStub : SyncableStub { qVariant(additional, QtType.Int), qVariant(type, QtType.Int), qVariant(flags, QtType.Int), + qVariant(messages, QtType.QVariantList), ) } } diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/move.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/move.kt new file mode 100644 index 0000000..63d7016 --- /dev/null +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/move.kt @@ -0,0 +1,25 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.protocol.util + +@Suppress("NOTHING_TO_INLINE") +inline fun <T> List<T>.move(value: T, pos: Int = size): List<T> { + val newPos = pos.coerceIn(0, size) + val oldPos = indexOf(value) + + return if (oldPos > newPos) { + remove(value).insert(value, newPos) + } else if (newPos > oldPos) { + remove(value).insert(value, newPos - 1) + } else { + this + } +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/pairs.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/pairs.kt index 794cb1a..49305be 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/pairs.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/pairs.kt @@ -14,14 +14,13 @@ package de.justjanne.libquassel.protocol.util * Returns a partitioned list of pairs */ fun <T> Iterable<T>.pairs(): List<Pair<T, T>> { - zipWithNext() return pairs { a, b -> Pair(a, b) } } /** * Returns a partitioned list of pairs transformed with the given transformer */ -inline fun <T, R> Iterable<T>.pairs(transform: (a: T, b: T) -> R): List<R> { +inline fun <T, R> Iterable<T>.pairs(crossinline transform: (a: T, b: T) -> R): List<R> { val iterator = iterator() val result = mutableListOf<R>() while (true) { diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/plus.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/plus.kt new file mode 100644 index 0000000..ff91f83 --- /dev/null +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/plus.kt @@ -0,0 +1,25 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.protocol.util + +@Suppress("NOTHING_TO_INLINE") +inline fun <T> List<T>.insert(value: T, pos: Int = size): List<T> { + return if (pos <= 0) { + listOf(value) + this + } else if (pos >= size) { + this + value + } else { + val before = subList(0, pos) + val after = subList(pos, size) + + before + value + after + } +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/remove.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/remove.kt new file mode 100644 index 0000000..fde9dcf --- /dev/null +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/remove.kt @@ -0,0 +1,14 @@ +/* + * libquassel + * Copyright (c) 2021 Janne Mareike Koschinski + * Copyright (c) 2021 The Quassel Project + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +package de.justjanne.libquassel.protocol.util + +@Suppress("NOTHING_TO_INLINE") +inline fun <T> List<T>.remove(value: T): List<T> = this.filter { it != value } -- GitLab