mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 14:45:22 +00:00
- Created MeshDelegateHandler.kt to handle all BluetoothMeshDelegate callbacks - Created ChatViewModelUtils.kt for utility functions like haptic feedback - ChatViewModel now delegates all mesh events to MeshDelegateHandler - Final ChatViewModel size: 337 lines (down from 1000+ original) This completes the refactoring while maintaining 100% functionality.
144 lines
5.2 KiB
Kotlin
144 lines
5.2 KiB
Kotlin
package com.bitchat.android.ui
|
|
|
|
import androidx.lifecycle.LifecycleCoroutineScope
|
|
import com.bitchat.android.mesh.BluetoothMeshDelegate
|
|
import com.bitchat.android.model.BitchatMessage
|
|
import com.bitchat.android.model.DeliveryAck
|
|
import com.bitchat.android.model.DeliveryStatus
|
|
import com.bitchat.android.model.ReadReceipt
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.launch
|
|
import java.util.*
|
|
|
|
/**
|
|
* Handles all BluetoothMeshDelegate callbacks and routes them to appropriate managers
|
|
*/
|
|
class MeshDelegateHandler(
|
|
private val state: ChatState,
|
|
private val messageManager: MessageManager,
|
|
private val channelManager: ChannelManager,
|
|
private val privateChatManager: PrivateChatManager,
|
|
private val coroutineScope: CoroutineScope,
|
|
private val onHapticFeedback: () -> Unit,
|
|
private val getMyPeerID: () -> String
|
|
) : BluetoothMeshDelegate {
|
|
|
|
override fun didReceiveMessage(message: BitchatMessage) {
|
|
coroutineScope.launch {
|
|
// FIXED: Deduplicate messages from dual connection paths
|
|
val messageKey = messageManager.generateMessageKey(message)
|
|
if (messageManager.isMessageProcessed(messageKey)) {
|
|
return@launch // Duplicate message, ignore
|
|
}
|
|
messageManager.markMessageProcessed(messageKey)
|
|
|
|
// Check if sender is blocked
|
|
message.senderPeerID?.let { senderPeerID ->
|
|
if (privateChatManager.isPeerBlocked(senderPeerID)) {
|
|
return@launch
|
|
}
|
|
}
|
|
|
|
// Trigger haptic feedback
|
|
onHapticFeedback()
|
|
|
|
if (message.isPrivate) {
|
|
// Private message
|
|
privateChatManager.handleIncomingPrivateMessage(message)
|
|
} else if (message.channel != null) {
|
|
// Channel message
|
|
if (state.getJoinedChannelsValue().contains(message.channel)) {
|
|
channelManager.addChannelMessage(message.channel, message, message.senderPeerID)
|
|
}
|
|
} else {
|
|
// Public message
|
|
messageManager.addMessage(message)
|
|
}
|
|
|
|
// Periodic cleanup
|
|
if (messageManager.isMessageProcessed("cleanup_check_${System.currentTimeMillis()/30000}")) {
|
|
messageManager.cleanupDeduplicationCaches()
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun didConnectToPeer(peerID: String) {
|
|
coroutineScope.launch {
|
|
// FIXED: Deduplicate connection events from dual connection paths
|
|
if (messageManager.isDuplicateSystemEvent("connect", peerID)) {
|
|
return@launch
|
|
}
|
|
|
|
val systemMessage = BitchatMessage(
|
|
sender = "system",
|
|
content = "$peerID connected",
|
|
timestamp = Date(),
|
|
isRelay = false
|
|
)
|
|
messageManager.addMessage(systemMessage)
|
|
}
|
|
}
|
|
|
|
override fun didDisconnectFromPeer(peerID: String) {
|
|
coroutineScope.launch {
|
|
// FIXED: Deduplicate disconnection events from dual connection paths
|
|
if (messageManager.isDuplicateSystemEvent("disconnect", peerID)) {
|
|
return@launch
|
|
}
|
|
|
|
val systemMessage = BitchatMessage(
|
|
sender = "system",
|
|
content = "$peerID disconnected",
|
|
timestamp = Date(),
|
|
isRelay = false
|
|
)
|
|
messageManager.addMessage(systemMessage)
|
|
}
|
|
}
|
|
|
|
override fun didUpdatePeerList(peers: List<String>) {
|
|
coroutineScope.launch {
|
|
state.setConnectedPeers(peers)
|
|
state.setIsConnected(peers.isNotEmpty())
|
|
|
|
// Clean up channel members who disconnected
|
|
channelManager.cleanupDisconnectedMembers(peers, getMyPeerID())
|
|
|
|
// Exit private chat if peer disconnected
|
|
state.getSelectedPrivateChatPeerValue()?.let { currentPeer ->
|
|
if (!peers.contains(currentPeer)) {
|
|
privateChatManager.cleanupDisconnectedPeer(currentPeer)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun didReceiveChannelLeave(channel: String, fromPeer: String) {
|
|
coroutineScope.launch {
|
|
channelManager.removeChannelMember(channel, fromPeer)
|
|
}
|
|
}
|
|
|
|
override fun didReceiveDeliveryAck(ack: DeliveryAck) {
|
|
coroutineScope.launch {
|
|
messageManager.updateMessageDeliveryStatus(ack.originalMessageID, DeliveryStatus.Delivered(ack.recipientNickname, ack.timestamp))
|
|
}
|
|
}
|
|
|
|
override fun didReceiveReadReceipt(receipt: ReadReceipt) {
|
|
coroutineScope.launch {
|
|
messageManager.updateMessageDeliveryStatus(receipt.originalMessageID, DeliveryStatus.Read(receipt.readerNickname, receipt.timestamp))
|
|
}
|
|
}
|
|
|
|
override fun decryptChannelMessage(encryptedContent: ByteArray, channel: String): String? {
|
|
return channelManager.decryptChannelMessage(encryptedContent, channel)
|
|
}
|
|
|
|
override fun getNickname(): String? = state.getNicknameValue()
|
|
|
|
override fun isFavorite(peerID: String): Boolean {
|
|
return privateChatManager.isFavorite(peerID)
|
|
}
|
|
}
|