Track Android PM invitations and joining from message links in ViewModel
This commit is contained in:
1 parent
f841c8eb8a
commit
e5a87bd043
1 file changed
+99
@@ -25,6 +25,19 @@ import java.io.FileOutputStream
|
||||
private val IMAGE_EXTENSIONS = setOf("jpg", "jpeg", "png", "gif", "bmp", "webp")
|
||||
|
||||
class ChatViewModel : ViewModel() {
|
||||
data class PmInviteState(val peer: Long, val name: String, val source: String, val groups: List<Channel>,
|
||||
val groupId: String = "", val requestId: Long = 0, val pending: Boolean = false, val errorRes: Int = 0)
|
||||
private val _pmInvite = MutableStateFlow<PmInviteState?>(null)
|
||||
val pmInvite: StateFlow<PmInviteState?> = _pmInvite
|
||||
private var nextPmInviteId = 0L
|
||||
|
||||
data class LinkJoinState(val link: String, val channelId: Long, val nodeId: Long,
|
||||
val pending: Boolean = true, val result: Int? = null)
|
||||
private val _linkJoin = MutableStateFlow<LinkJoinState?>(null)
|
||||
val linkJoin: StateFlow<LinkJoinState?> = _linkJoin
|
||||
private val _joinedChannel = MutableStateFlow<Channel?>(null)
|
||||
val joinedChannel: StateFlow<Channel?> = _joinedChannel
|
||||
|
||||
data class AttachmentPreparation(val jobs: Set<String> = emptySet(), val failed: Boolean = false)
|
||||
private val _attachmentPreparations = MutableStateFlow<Map<String, AttachmentPreparation>>(emptyMap())
|
||||
val attachmentPreparations: StateFlow<Map<String, AttachmentPreparation>> = _attachmentPreparations
|
||||
@@ -317,6 +330,18 @@ class ChatViewModel : ViewModel() {
|
||||
val nodeId = buf.getLong()
|
||||
val result = buf.getInt()
|
||||
val chId = buf.getLong()
|
||||
val joining = _linkJoin.value
|
||||
if (joining != null && joining.pending && joining.channelId == chId && joining.nodeId == nodeId) {
|
||||
_linkJoin.value = joining.copy(pending = false, result = result)
|
||||
if (result == 0) viewModelScope.launch {
|
||||
val channels = withContext(Dispatchers.IO) { repo?.getChannels().orEmpty() }
|
||||
_channels.value = channels
|
||||
val channel = channels.find { it.id.toULongOrNull()?.toLong() == chId }
|
||||
if (_linkJoin.value?.link == joining.link && channel != null) {
|
||||
_linkJoin.value = null; _joinedChannel.value = channel
|
||||
} else if (channel == null) LogManager.addLog("ERROR", "JOIN", "joined channel missing ch=$chId")
|
||||
}
|
||||
}
|
||||
LogManager.addLog(
|
||||
if (result == 0) "INFO" else "ERROR",
|
||||
"JOIN", "connect to 0x%016x ch=$chId result=$result".format(nodeId))
|
||||
@@ -326,6 +351,26 @@ class ChatViewModel : ViewModel() {
|
||||
4 -> { if (repo != null) { LogManager.addLog("INFO", "VM", "CHANNEL_UPDATED — refreshChannels"); refreshChannels() } else { LogManager.addLog("WARN", "VM", "CHANNEL_UPDATED skipped — repo is NULL") } }
|
||||
12 -> LogManager.addLog("INFO", "VM", "service started")
|
||||
13 -> { _attachmentPreparations.value = emptyMap(); LogManager.addLog("INFO", "VM", "service stopped") }
|
||||
48 -> { /* [request_id:8][result:4][peer:8][conv:8] */
|
||||
if (data == null || data.size != 28) { LogManager.addLog("ERROR", "INVITE", "invalid PM invite result"); return }
|
||||
val buf = java.nio.ByteBuffer.wrap(data).order(java.nio.ByteOrder.LITTLE_ENDIAN)
|
||||
val request = buf.long; val result = buf.int; val peer = buf.long; val conv = buf.long.toULong().toString()
|
||||
val state = _pmInvite.value ?: return
|
||||
if (!state.pending || state.requestId != request || state.peer != peer) return
|
||||
LogManager.addLog(if (result == 0) "INFO" else "ERROR", "INVITE", "PM invite result request=$request peer=$peer result=$result")
|
||||
if (result == 0) {
|
||||
_pmInvite.value = null
|
||||
openDm(conv, peer, state.name); _dmOpen.value = conv; refreshDmConversations()
|
||||
} else {
|
||||
val error = when (result) {
|
||||
-1 -> R.string.invite_pm_start_failed
|
||||
-3 -> R.string.invite_pm_send_failed
|
||||
-5 -> R.string.invite_pm_busy
|
||||
else -> R.string.invite_build_failed
|
||||
}
|
||||
_pmInvite.value = state.copy(pending = false, errorRes = error)
|
||||
}
|
||||
}
|
||||
47 -> {
|
||||
if (data == null || data.size < 17 || data.size != 17 + (data[0].toInt() and 0xff)) {
|
||||
LogManager.addLog("WARN", "Call", "invalid PM route snapshot size=${data?.size}"); return
|
||||
@@ -919,6 +964,59 @@ class ChatViewModel : ViewModel() {
|
||||
r.dmSend(dm.convId, text)
|
||||
}
|
||||
|
||||
fun showPmInvite(peer: Long, name: String, source: String = "") {
|
||||
if (peer == 0L || peer == NativeLib.getMyNodeId()) { LogManager.addLog("ERROR", "INVITE", "invalid PM recipient"); return }
|
||||
if (_pmInvite.value != null) return
|
||||
val groups = _channels.value.filter { NativeLib.isChannelMember(it.id, NativeLib.getMyNodeId()) }
|
||||
_pmInvite.value = PmInviteState(peer, name, source, groups)
|
||||
LogManager.addLog("INFO", "INVITE", "PM group picker peer=$peer source=$source groups=${groups.size}")
|
||||
}
|
||||
|
||||
fun selectPmInviteGroup(groupId: String) {
|
||||
_pmInvite.value?.let { if (!it.pending) _pmInvite.value = it.copy(groupId = groupId, errorRes = 0) }
|
||||
}
|
||||
|
||||
fun sendPmInvite() {
|
||||
val state = _pmInvite.value ?: return
|
||||
if (state.pending) return
|
||||
val channel = state.groupId.toULongOrNull()?.toLong() ?: return
|
||||
val request = ++nextPmInviteId
|
||||
_pmInvite.value = state.copy(requestId = request, pending = true, errorRes = 0)
|
||||
if (!NativeLib.dmInvite(request, state.peer, state.source, channel, state.name)) {
|
||||
_pmInvite.value = state.copy(errorRes = R.string.invite_core_unavailable)
|
||||
}
|
||||
}
|
||||
|
||||
fun dismissPmInvite() {
|
||||
val state = _pmInvite.value ?: return
|
||||
_pmInvite.value = null
|
||||
if (state.pending) NativeLib.dmInvite(state.requestId, state.peer, state.source, 0, state.name, cancel = true)
|
||||
LogManager.addLog("INFO", "INVITE", "PM group picker closed request=${state.requestId} pending=${state.pending}")
|
||||
}
|
||||
|
||||
fun openInviteLink(link: String) {
|
||||
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())) {
|
||||
_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, invite.channelId, invite.nodeId)
|
||||
val addresses = invite.serializeAddrs()
|
||||
joinChannel(invite.channelId, invite.nodeId, invite.joinKey, invite.pubkey, addresses, invite.addrs.size)
|
||||
}
|
||||
|
||||
fun dismissLinkJoin() { _linkJoin.value = null }
|
||||
fun consumeJoinedChannel() { _joinedChannel.value = null }
|
||||
|
||||
fun sendDmFile(convId: String, path: String, name: String) {
|
||||
if (repo != null) NativeLib.dmSendFile(convId, path, name)
|
||||
}
|
||||
@@ -1288,6 +1386,7 @@ class ChatViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
fun clearState() {
|
||||
dismissPmInvite(); _linkJoin.value = null; _joinedChannel.value = null
|
||||
_attachmentPreparations.value = emptyMap()
|
||||
repo?.close()
|
||||
repo = null
|
||||
|
||||
Reference in new issue
Block a user