Route Android server links through channel selection and ordinary JOIN request
This commit is contained in:
1 parent
a9cc6785a9
commit
f2d6561509
3 files changed
+29
-11
No files matched your search
@@ -3,7 +3,9 @@ package com.utun.chat.data
|
||||
/* Kotlin-представление invite: wire/base64 и допустимость полей проверяет общий C-кодек через NativeLib.
|
||||
* Декодирование не запускает подключение. Для join передавать исходный link: из preview ссылка не собирается.
|
||||
* Массивы моделей принадлежат получателю, copy() их не клонирует; после создания использовать только для чтения. */
|
||||
const val INVITE_LINK_VERSION: Int = 0x03
|
||||
const val INVITE_LINK_VERSION: Int = 0x04
|
||||
const val INVITE_KIND_CHANNEL: Int = 0
|
||||
const val INVITE_KIND_SERVER: Int = 1
|
||||
const val INVITE_PUBKEY_SIZE: Int = 32
|
||||
const val INVITE_SNAME_MAX: Int = 64
|
||||
|
||||
@@ -64,16 +66,18 @@ data class InviteData(
|
||||
val addrs: List<InviteAddr>,
|
||||
val password: String = "",
|
||||
val reality: InviteReality? = null,
|
||||
val link: String = ""
|
||||
val link: String = "",
|
||||
val kind: Int = INVITE_KIND_CHANNEL
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is InviteData) return false
|
||||
return channelId == other.channelId && joinKey == other.joinKey && pubkey.contentEquals(other.pubkey) &&
|
||||
return kind == other.kind && channelId == other.channelId && joinKey == other.joinKey && pubkey.contentEquals(other.pubkey) &&
|
||||
nodeId == other.nodeId && addrs == other.addrs && password == other.password && reality == other.reality && link == other.link
|
||||
}
|
||||
override fun hashCode(): Int {
|
||||
var result = channelId.hashCode()
|
||||
result = 31 * result + kind
|
||||
result = result * 31 + joinKey.hashCode()
|
||||
result = result * 31 + pubkey.contentHashCode()
|
||||
result = result * 31 + nodeId.hashCode()
|
||||
@@ -112,7 +116,7 @@ object InviteLink {
|
||||
(0 until addresses.length()).map { i ->
|
||||
val a = addresses.getJSONObject(i)
|
||||
InviteAddr(a.getInt("family"), bytes(a.getString("ip")), a.getInt("port"), a.getInt("socket"), a.getInt("proto"))
|
||||
}, String(bytes(value.getString("password")), Charsets.UTF_8), reality, link)
|
||||
}, String(bytes(value.getString("password")), Charsets.UTF_8), reality, link, value.getInt("kind"))
|
||||
}
|
||||
|
||||
/** Диагностическая подпись адреса/сокета; IPv6 выводится hex без сжатия, строка не является endpoint для подключения. */
|
||||
|
||||
@@ -111,6 +111,7 @@ object NativeLib {
|
||||
private val joinSequence = java.util.concurrent.atomic.AtomicLong(1)
|
||||
fun nextJoinRequest(): Long = joinSequence.getAndIncrement()
|
||||
fun joinChannel(link: String, request: Long): Int = nativeJoinChannel(link, request)
|
||||
fun addServer(channel: Long, link: String, request: Long): Int = nativeAddServer(channel, link, request)
|
||||
fun decodeInvite(link: String): String? = nativeDecodeInvite(link)
|
||||
fun deriveNodeId(pubkey: ByteArray): Long = nativeDeriveNodeId(pubkey)
|
||||
fun connectChannel(channelId: String) { nativeConnectChannel(channelId) }
|
||||
@@ -367,6 +368,7 @@ object NativeLib {
|
||||
private external fun nativeConnectNode(address: String, port: Int, pubkeyHex: String)
|
||||
private external fun nativeJoinChannel(link: String, request: Long): Int
|
||||
private external fun nativeDecodeInvite(link: String): String?
|
||||
private external fun nativeAddServer(channel: Long, link: String, request: Long): Int
|
||||
private external fun nativeDeriveNodeId(pubkey: ByteArray): Long
|
||||
private external fun nativeConnectChannel(channelId: String)
|
||||
private external fun nativeConnectMemberNode(channelId: String, nodeId: Long)
|
||||
|
||||
@@ -154,9 +154,13 @@ class ChatViewModel : ViewModel() {
|
||||
private var nextPmInviteId = 0L
|
||||
|
||||
data class LinkJoinState(val link: String, val channelId: Long, val nodeId: Long,
|
||||
val pending: Boolean = true, val result: Int? = null, val request: Long = 0)
|
||||
val pending: Boolean = true, val result: Int? = null, val request: Long = 0,
|
||||
val serverAddition: Boolean = false)
|
||||
private val _linkJoin = MutableStateFlow<LinkJoinState?>(null)
|
||||
val linkJoin: StateFlow<LinkJoinState?> = _linkJoin
|
||||
private val _serverLink = MutableStateFlow<String?>(null)
|
||||
val serverLink: StateFlow<String?> = _serverLink
|
||||
fun dismissServerLink() { _serverLink.value = null }
|
||||
private val _joinedChannel = MutableStateFlow<Channel?>(null)
|
||||
val joinedChannel: StateFlow<Channel?> = _joinedChannel
|
||||
|
||||
@@ -1369,24 +1373,32 @@ class ChatViewModel : ViewModel() {
|
||||
LogManager.addLog("INFO", "INVITE", "PM group picker closed request=${state.requestId} pending=${state.pending}")
|
||||
}
|
||||
|
||||
fun openInviteLink(link: String) {
|
||||
fun openInviteLink(link: String, serverChannel: Long = 0) {
|
||||
if (_linkJoin.value?.pending == true) return
|
||||
val invite = InviteLink.decode(link)
|
||||
if (invite == null) {
|
||||
LogManager.addLog("ERROR", "JOIN", "invalid message invite link")
|
||||
_linkJoin.value = LinkJoinState(link, 0, 0, pending = false, result = -1); return
|
||||
}
|
||||
val existing = _channels.value.find { it.id.toULongOrNull()?.toLong() == invite.channelId }
|
||||
if (existing != null && NativeLib.isChannelMember(existing.id, NativeLib.getMyNodeId())) {
|
||||
val serverAddition = invite.kind == com.utun.chat.data.INVITE_KIND_SERVER
|
||||
if (serverAddition && serverChannel == 0L) { _serverLink.value = link; return }
|
||||
val channelId = if (serverAddition) serverChannel else invite.channelId
|
||||
if (serverAddition && !NativeLib.isChannelMember(channelId.toString(), NativeLib.getMyNodeId())) {
|
||||
LogManager.addLog("WARN", "JOIN", "server addition requires own channel ch=$channelId")
|
||||
_linkJoin.value = LinkJoinState(link, channelId, invite.nodeId, pending = false, result = -7, serverAddition = true); return
|
||||
}
|
||||
val existing = _channels.value.find { it.id.toULongOrNull()?.toLong() == channelId }
|
||||
if (!serverAddition && existing != null && NativeLib.isChannelMember(existing.id, NativeLib.getMyNodeId())) {
|
||||
_linkJoin.value = null; _joinedChannel.value = existing; return
|
||||
}
|
||||
if (repo == null || !NativeLib.isReady()) {
|
||||
LogManager.addLog("ERROR", "JOIN", "core unavailable for message invite")
|
||||
_linkJoin.value = LinkJoinState(link, invite.channelId, invite.nodeId, pending = false, result = -7); return
|
||||
_linkJoin.value = LinkJoinState(link, channelId, invite.nodeId, pending = false, result = -7, serverAddition = serverAddition); return
|
||||
}
|
||||
val request = NativeLib.nextJoinRequest()
|
||||
_linkJoin.value = LinkJoinState(link, invite.channelId, invite.nodeId, request = request)
|
||||
val result = repo?.joinChannel(link, request) ?: -7
|
||||
_serverLink.value = null
|
||||
_linkJoin.value = LinkJoinState(link, channelId, invite.nodeId, request = request, serverAddition = serverAddition)
|
||||
val result = if (serverAddition) NativeLib.addServer(channelId, link, request) else repo?.joinChannel(link, request) ?: -7
|
||||
if (result != 0) _linkJoin.value = _linkJoin.value?.copy(pending = false, result = result)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user