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 182244f9d68a95c102cf5122edc7dec5c5d06394..2b1dd3da40b56b7ffac2d38bb5fa803e04284cf5 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 0000000000000000000000000000000000000000..7bbe4e08035029876ceeec4b69ce83b8298e7367 --- /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 0000000000000000000000000000000000000000..e75b72360a68a99ab0f367f5a3efe5a09e0166de --- /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 95fd8c0edc8d4590ae6e38285fc4a1d17302c110..8b1e1fde9eb09ad634e06329e3c76500d4416f09 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 30254bc6ea1c52a76d06cff7e300ecf560db246b..d6a15b60cb1df05d211e667daf4585c0d7b6d99b 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 30254bc6ea1c52a76d06cff7e300ecf560db246b..ae81bcbd039a838637f0c3ccd35f2226766f0ad5 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 30254bc6ea1c52a76d06cff7e300ecf560db246b..583f11995e05a86c897fe580cbd745db079ddf4c 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 30254bc6ea1c52a76d06cff7e300ecf560db246b..dde3434bb89f38ee9382b0f67d3021667893bfbd 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 f2d51725d618a804b628d6eebb445fc1056779c6..a420f96e0a53171848b5e673d5e39a4e8fb3e759 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 95933ef3384e57b07a8958d014efa7475569de63..d78c7e38317a2f6c9a8b527a41965c935d9fda8d 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 991995bc6dad37bf036b3e47bc139e4982e22a28..b199aa04309cbcfe9f8ebf04fa1707e10f91db93 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 20dd19ca5330faf6bbd25732dcf56d8f4a438e72..cc245854459796228fac9a660e81b09ac17318d8 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 30254bc6ea1c52a76d06cff7e300ecf560db246b..6690de8a6bb91f32384b4e13041d3244e3625ec7 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 885b4f01a8490bb7bc4988d79d6177830317358c..210546c54fa8c7051b3a36d7a4c999e6f43bd5c9 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 30254bc6ea1c52a76d06cff7e300ecf560db246b..c997cb7336671e9433522085e70d99a7fb144e2b 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 115f120ae5d5ad0a11d9d62d0627f6ca35234ae1..36b4fb20395464d126566c547dfd7393a1924ca3 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 80bbef5781fd70c4586be3a4b69df7bdd4e2e846..67a9bcc826ce65326ca09f1dc75d4a71a1e6f8a2 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 30254bc6ea1c52a76d06cff7e300ecf560db246b..36b4edcb5bcc444ad0c37639cdc4681995365923 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 75fd70a06f9a62ed99a562b940b763248c8ae459..42c218455514b15b725cb92ea893f380231bc83b 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 182244f9d68a95c102cf5122edc7dec5c5d06394..b479fbc445244c0b84d5a9f80a5af4775de24494 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 182244f9d68a95c102cf5122edc7dec5c5d06394..663ba1e45914200b7d2baede7e7419af884b6ad1 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 182244f9d68a95c102cf5122edc7dec5c5d06394..785a6f47841d028c34d77da500eb7ab1b0c3e08c 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 182244f9d68a95c102cf5122edc7dec5c5d06394..d814eb73e1dcd0b66d38b7c7915585a2726cc80f 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 182244f9d68a95c102cf5122edc7dec5c5d06394..5733a20112c7075bcc6ec2bc43a0fc02147e78c6 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 77656fb2fd8c2d17a966a0d0da57aa5cf0eae3b6..f9dc5b883eb83c2430633f75bffbb7121fb58444 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 0000000000000000000000000000000000000000..63d7016493a6d695fad973ebacb02ad70e274eef --- /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 794cb1a61e538d60f3f6245688917c29e268b324..49305be34aa19e187bb9fb54e66253fc7d7cddd5 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 0000000000000000000000000000000000000000..ff91f83412beced45fd9a64cd5da7f6b4a8e176f --- /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 0000000000000000000000000000000000000000..fde9dcf114ee2ecc3e9e84e22c55949ec5a72845 --- /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 }