chat: автоподключение к узлам группы (вкл/выкл) + ручной connect к узлу (desktop+android); фикс reinit при QR-join
This commit is contained in:
1 parent
9ef501a2fb
commit
d7bb58c6fb
20 files changed
+241
-5
No files matched your search
@@ -385,6 +385,7 @@ class ChatRepository {
|
||||
fun joinChannel(channelId: Long, nodeId: Long, pubkey: ByteArray, addrs: ByteArray, addrCount: Int) =
|
||||
NativeLib.joinChannel(channelId, nodeId, pubkey, addrs, addrCount)
|
||||
fun connectChannel(channelId: String) = NativeLib.connectChannel(channelId)
|
||||
fun connectMemberNode(channelId: String, nodeId: Long) = NativeLib.connectMemberNode(channelId, nodeId)
|
||||
fun isReady() = NativeLib.isReady()
|
||||
|
||||
fun getMembers(channelId: String): List<ChatMember> {
|
||||
|
||||
@@ -46,6 +46,7 @@ class ConfigProvider(private val context: Context) {
|
||||
private val chatStandbyActiveSec = intPreferencesKey("chat_standby_active_sec")
|
||||
private val chatStandbySleepSec = intPreferencesKey("chat_standby_sleep_sec")
|
||||
private val chatStandbyMinSleepSec = intPreferencesKey("chat_standby_min_sleep_sec")
|
||||
private val chatGroupAutoconnect = intPreferencesKey("chat_group_autoconnect")
|
||||
private val firstLaunchDone = booleanPreferencesKey("first_launch_done")
|
||||
private val connectionModeKey = stringPreferencesKey("connection_mode")
|
||||
private val serviceEnabledKey = booleanPreferencesKey("service_enabled")
|
||||
@@ -127,6 +128,7 @@ class ConfigProvider(private val context: Context) {
|
||||
key == "chat.standby_active_sec" -> context.dataStore.data.first()[chatStandbyActiveSec] ?: 2
|
||||
key == "chat.standby_sleep_sec" -> context.dataStore.data.first()[chatStandbySleepSec] ?: 60
|
||||
key == "chat.standby_min_sleep_sec" -> context.dataStore.data.first()[chatStandbyMinSleepSec] ?: 15
|
||||
key == "chat.group_autoconnect" -> context.dataStore.data.first()[chatGroupAutoconnect] ?: 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
@@ -157,6 +159,7 @@ class ConfigProvider(private val context: Context) {
|
||||
"chat.standby_active_sec" -> prefs[chatStandbyActiveSec] = value.toIntOrNull() ?: 2
|
||||
"chat.standby_sleep_sec" -> prefs[chatStandbySleepSec] = value.toIntOrNull() ?: 60
|
||||
"chat.standby_min_sleep_sec" -> prefs[chatStandbyMinSleepSec] = value.toIntOrNull() ?: 15
|
||||
"chat.group_autoconnect" -> prefs[chatGroupAutoconnect] = value.toIntOrNull() ?: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -387,6 +390,7 @@ class ConfigProvider(private val context: Context) {
|
||||
appendLine("standby_active_sec=${getInt("chat.standby_active_sec")}")
|
||||
appendLine("standby_sleep_sec=${getInt("chat.standby_sleep_sec")}")
|
||||
appendLine("standby_min_sleep_sec=${getInt("chat.standby_min_sleep_sec")}")
|
||||
appendLine("group_autoconnect=${getInt("chat.group_autoconnect")}")
|
||||
appendLine()
|
||||
appendLine("[debug]")
|
||||
appendLine("console_level=info")
|
||||
|
||||
@@ -75,6 +75,7 @@ object NativeLib {
|
||||
}
|
||||
fun deriveNodeId(pubkey: ByteArray): Long = nativeDeriveNodeId(pubkey)
|
||||
fun connectChannel(channelId: String) { nativeConnectChannel(channelId) }
|
||||
fun connectMemberNode(channelId: String, nodeId: Long) { nativeConnectMemberNode(channelId, nodeId) }
|
||||
fun getChannels(): String = nativeGetChannels() ?: "[]"
|
||||
fun getMessages(channelId: String, limit: Int = 50): String = nativeGetMessages(channelId, limit) ?: "[]"
|
||||
fun clearDatabase(): Boolean = nativeClearDatabase()
|
||||
@@ -148,6 +149,7 @@ object NativeLib {
|
||||
private external fun nativeJoinChannel(channelId: Long, nodeId: Long, pubkey: ByteArray, addrs: ByteArray, addrCount: Int)
|
||||
private external fun nativeDeriveNodeId(pubkey: ByteArray): Long
|
||||
private external fun nativeConnectChannel(channelId: String)
|
||||
private external fun nativeConnectMemberNode(channelId: String, nodeId: Long)
|
||||
private external fun nativeGetChannels(): String?
|
||||
private external fun nativeGetMessages(channelId: String, limit: Int): String?
|
||||
private external fun nativeSetDebugLevel(category: String, level: String)
|
||||
|
||||
@@ -83,6 +83,7 @@ fun MemberListScreen(
|
||||
member = m,
|
||||
isSelected = selectedMember?.nodeId == m.nodeId,
|
||||
onClick = { viewModel.showMemberDetail(channel.id, m.nodeId) },
|
||||
onConnect = { viewModel.connectMemberNode(channel.id, m.nodeId) },
|
||||
onEdit = {
|
||||
editMember = m
|
||||
showEditDialog = true
|
||||
@@ -133,6 +134,7 @@ private fun MemberListItem(
|
||||
member: ChatMember,
|
||||
isSelected: Boolean = false,
|
||||
onClick: () -> Unit,
|
||||
onConnect: () -> Unit,
|
||||
onEdit: () -> Unit
|
||||
) {
|
||||
val onlineColor = when {
|
||||
@@ -223,6 +225,9 @@ private fun MemberListItem(
|
||||
}
|
||||
}
|
||||
|
||||
if (!member.isSelf) {
|
||||
TextButton(onClick = onConnect) { Text("Connect") }
|
||||
}
|
||||
TextButton(onClick = onEdit) { Text("Edit") }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +118,7 @@ private fun GeneralSettingsTab(vm: ChatViewModel, dirty: Boolean, onDirty: (Bool
|
||||
var servers by remember { mutableStateOf<List<ServerEntry>>(emptyList()) }
|
||||
var availableIfaces by remember { mutableStateOf<List<Pair<String, String>>>(emptyList()) }
|
||||
var connectionMode by remember { mutableStateOf("manual") }
|
||||
var autoConnect by remember { mutableStateOf(true) }
|
||||
|
||||
val localSocketStates by vm.localSockets.collectAsState()
|
||||
val serviceEnabled by provider.serviceEnabledFlow.collectAsState(initial = true)
|
||||
@@ -143,6 +144,7 @@ private fun GeneralSettingsTab(vm: ChatViewModel, dirty: Boolean, onDirty: (Bool
|
||||
servers = provider.loadServersFromConfig()
|
||||
availableIfaces = provider.getAvailableInterfaces()
|
||||
connectionMode = provider.getConnectionMode()
|
||||
autoConnect = provider.getInt("chat.group_autoconnect") != 0
|
||||
}
|
||||
|
||||
val save = { s: List<ServerEntry> -> onDirty(true); servers = s; provider.saveServersToConfig(s) }
|
||||
@@ -336,6 +338,20 @@ private fun GeneralSettingsTab(vm: ChatViewModel, dirty: Boolean, onDirty: (Bool
|
||||
|
||||
HorizontalDivider(Modifier.padding(vertical = 8.dp))
|
||||
|
||||
/* ── Chat ── */
|
||||
Text("Chat", style = MaterialTheme.typography.titleMedium)
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text("Auto-connect to group nodes", modifier = Modifier.weight(1f))
|
||||
Switch(checked = autoConnect, onCheckedChange = {
|
||||
autoConnect = it
|
||||
val s = if (it) "1" else "0"
|
||||
scope.launch { provider.setValue("chat.group_autoconnect", s) }
|
||||
NativeLib.setChatSetting("group_autoconnect", s)
|
||||
})
|
||||
}
|
||||
|
||||
HorizontalDivider(Modifier.padding(vertical = 8.dp))
|
||||
|
||||
/* Debug */
|
||||
Text("Debug", style = MaterialTheme.typography.titleMedium)
|
||||
DebugLevelDropdown(label = "Global Level", current = debugLevel.uppercase(), onSelect = setDebug)
|
||||
|
||||
@@ -519,6 +519,12 @@ class ChatViewModel : ViewModel() {
|
||||
r.transferAdmin(channelId, nodeId)
|
||||
}
|
||||
|
||||
fun connectMemberNode(channelId: String, nodeId: Long) {
|
||||
val r = repo ?: return
|
||||
r.connectMemberNode(channelId, nodeId)
|
||||
refreshMembers(channelId)
|
||||
}
|
||||
|
||||
fun refreshMemberLinks(nodeId: Long) {
|
||||
if (nodeId == 0L) { _memberLinks.value = MemberLinks(); return }
|
||||
val r = repo ?: return
|
||||
|
||||
@@ -274,6 +274,22 @@ void utun_bridge_connect_channel(const char* channel_id) {
|
||||
uasync_post(ua, chat_core_connect_channel_trampoline, cid);
|
||||
}
|
||||
|
||||
void utun_bridge_connect_member_node(const char* channel_id, uint64_t node_id) {
|
||||
if (!channel_id || !channel_id[0] || node_id == 0) {
|
||||
bridge_log(BLEV_ERROR, "connect_member_node: invalid args ch=%s node=0x%016llx",
|
||||
channel_id ? channel_id : "(null)", (unsigned long long)node_id);
|
||||
return;
|
||||
}
|
||||
struct UASYNC* ua = instance_lite_get_uasync();
|
||||
if (!uasync_is_running(ua)) return;
|
||||
struct chat_connect_node_req* req = u_calloc(1, sizeof(*req));
|
||||
if (!req) { bridge_log(BLEV_ERROR, "connect_member_node: OOM"); return; }
|
||||
strncpy(req->ch_id, channel_id, sizeof(req->ch_id) - 1);
|
||||
req->node_id = node_id;
|
||||
bridge_log(BLEV_INFO, "connect_member_node ch=%s node=0x%016llx", channel_id, (unsigned long long)node_id);
|
||||
uasync_post(ua, chat_core_connect_node_trampoline, req);
|
||||
}
|
||||
|
||||
static char* json_escape_alloc(const char* str) {
|
||||
if (!str) return u_strdup("");
|
||||
size_t len = strlen(str), cap = len * 2 + 8;
|
||||
@@ -518,7 +534,7 @@ void utun_bridge_set_debug_level(const char* category, const char* level) {
|
||||
}
|
||||
|
||||
void utun_bridge_chat_setting_set(const char* name, const char* value) {
|
||||
chat_setting_set(name, value);
|
||||
chat_core_set_setting(name, value);
|
||||
}
|
||||
|
||||
int utun_bridge_chat_setting_get_int(const char* name, int def) {
|
||||
@@ -1561,6 +1577,14 @@ JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeConnectChannel(
|
||||
if (ch) (*env)->ReleaseStringUTFChars(env, channelId, ch);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeConnectMemberNode(
|
||||
JNIEnv* env, jobject thiz, jstring channelId, jlong nodeId) {
|
||||
(void)thiz;
|
||||
const char* ch = (*env)->GetStringUTFChars(env, channelId, NULL);
|
||||
utun_bridge_connect_member_node(ch, (uint64_t)nodeId);
|
||||
if (ch) (*env)->ReleaseStringUTFChars(env, channelId, ch);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeSetDebugLevel(
|
||||
JNIEnv* env, jobject thiz, jstring category, jstring level) {
|
||||
(void)thiz;
|
||||
@@ -1576,7 +1600,7 @@ JNIEXPORT void JNICALL Java_com_utun_chat_data_NativeLib_nativeSetChatSetting(
|
||||
(void)thiz;
|
||||
const char* n = (*env)->GetStringUTFChars(env, name, NULL);
|
||||
const char* v = (*env)->GetStringUTFChars(env, value, NULL);
|
||||
chat_setting_set(n, v);
|
||||
chat_core_set_setting(n, v);
|
||||
if (v) (*env)->ReleaseStringUTFChars(env, value, v);
|
||||
if (n) (*env)->ReleaseStringUTFChars(env, name, n);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,9 @@ void utun_bridge_join_channel(uint64_t channel_id, uint64_t node_id,
|
||||
/* Connect to all peers of a channel (auto-connect on select) */
|
||||
void utun_bridge_connect_channel(const char* channel_id);
|
||||
|
||||
/* Однократная попытка подключения к конкретному узлу канала (ручной "Connect"). */
|
||||
void utun_bridge_connect_member_node(const char* channel_id, uint64_t node_id);
|
||||
|
||||
/* ── Queries ── */
|
||||
|
||||
char* utun_bridge_get_channels_json(void);
|
||||
|
||||
Reference in new issue
Block a user