package com.vgmlr.wedge
import android.content.Context
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.edit
import java.net.Inet4Address
import java.net.NetworkInterface
import androidx.compose.ui.graphics.RectangleShape
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SyncView(vm: MainViewModel, prefs: PreferenceManager, onBack: () -> Unit) {
Scaffold(
topBar = {
TopAppBar(
title = { Text("Sync", color = colorResource(id = R.color.title_color), fontSize = 18.sp) },
colors = TopAppBarDefaults.topAppBarColors(containerColor = Color(0xFF486860)),
navigationIcon = {
IconButton(onClick = onBack) { Icon(Icons.AutoMirrored.Filled.ArrowBack, null, tint = colorResource(id = R.color.title_color)) }
}
)
}
) { p ->
Column(
Modifier.padding(p).fillMaxSize().imePadding().background(CBg)
.padding(horizontal = 16.dp).verticalScroll(rememberScrollState())
) {
Spacer(Modifier.height(24.dp))
SyncViewContent(vm, prefs)
Spacer(Modifier.height(24.dp))
}
}
}
@Composable
fun SyncViewContent(vm: MainViewModel, prefs: PreferenceManager) {
val context = LocalContext.current
val sharedPrefs = remember { context.getSharedPreferences("sync_prefs", Context.MODE_PRIVATE) }
val noteEntity by vm.dao.getNote().collectAsState(initial = null)
val lastModifiedStr = WedgeDate.getOtcMetadata(
noteEntity?.lastModified ?: System.currentTimeMillis()
)
var selectedRoute by remember { mutableStateOf(sharedPrefs.getString("route", "USB") ?: "USB") }
var peerIp by remember { mutableStateOf(sharedPrefs.getString("peer_ip", "") ?: "") }
var autoMerge by remember { mutableStateOf(sharedPrefs.getBoolean("auto_merge", false)) }
val passKeyFlow by prefs.passKey.collectAsState(initial = "wedge03569")
var localPassKey by remember { mutableStateOf<String?>(null) }
val displayPassKey = localPassKey ?: passKeyFlow
val syncStatusText by vm.syncStatus.collectAsState()
var showOlderDataDialog by remember { mutableStateOf(false) }
val syncStamp = remember(syncStatusText) { sharedPrefs.getLong("sync_last_stamp", 0L) }
val syncedStr = if (syncStamp == 0L) "[none]" else WedgeDate.getOtcMetadata(syncStamp)
val localIpAddress = remember {
try {
NetworkInterface.getNetworkInterfaces().asSequence()
.flatMap { it.inetAddresses.asSequence() }
.firstOrNull { !it.isLoopbackAddress && it is Inet4Address }
?.hostAddress ?: "Unknown"
} catch (_: Exception) {
"Unknown"
}
}
LaunchedEffect(selectedRoute) { sharedPrefs.edit { putString("route", selectedRoute) } }
LaunchedEffect(peerIp) { sharedPrefs.edit { putString("peer_ip", peerIp) } }
Column(modifier = Modifier.fillMaxWidth()) {
WgCard {
WgRow("Route") {
WgRadio(
value = selectedRoute,
options = listOf("USB", "WIFI", "BT"),
disabledOptions = listOf("BT")
) { selectedRoute = it }
}
WgDivider()
WgRow("Pass Key", edit = true) {
WgField(
value = displayPassKey,
onValueChange = {
localPassKey = it
vm.updatePassKey(it)
},
placeholder = "",
modifier = Modifier.weight(1f)
)
}
WgDivider()
WgRow("Auto-Merge") {
WgRadio(
value = if (autoMerge) "True" else "False",
options = listOf("True", "False")
) {
autoMerge = it == "True"
vm.setAutoMerge(autoMerge)
}
}
WgDivider()
WgRow("Local IP") {
WgValue(localIpAddress, mono = true, color = CText)
}
WgDivider()
WgRow("Peer IP", edit = true) {
WgField(
value = peerIp,
onValueChange = { peerIp = it },
placeholder = "0.0.0.0",
modifier = Modifier.weight(1f)
)
}
WgDivider()
WgRow("Modified") {
WgValue(lastModifiedStr, mono = true)
}
WgDivider()
WgRow("Synced") {
WgValue(syncedStr, mono = true)
}
WgDivider()
WgRow("Status") {
WgValue(syncStatusText, mono = true, color = CText)
}
WgDivider()
Button(
onClick = {
vm.trySendNoteText(selectedRoute, peerIp, onOlderThanDestination = {
showOlderDataDialog = true
})
},
enabled = selectedRoute == "USB" || selectedRoute == "WIFI",
modifier = Modifier.fillMaxWidth().height(48.dp),
shape = RectangleShape,
elevation = null,
colors = ButtonDefaults.buttonColors(
containerColor = CChip,
contentColor = CText,
disabledContainerColor = CCard,
disabledContentColor = CChip
)
) {
Text("Send", fontSize = FS)
}
}
}
if (showOlderDataDialog) {
val btnShape = RoundedCornerShape(8.dp)
val btnColors = ButtonDefaults.buttonColors(
containerColor = CChip,
contentColor = CText
)
AlertDialog(
onDismissRequest = { showOlderDataDialog = false },
shape = RoundedCornerShape(14.dp),
containerColor = CCard,
text = {
Text(
text = "Local data is older than destination",
fontSize = 17.sp
)
},
dismissButton = {
Button(
onClick = { showOlderDataDialog = false },
modifier = Modifier.height(44.dp),
colors = btnColors,
shape = btnShape
) { Text("Oops", color = CText, fontSize = 15.sp) }
},
confirmButton = {
Button(
onClick = {
showOlderDataDialog = false
vm.confirmSendNoteText()
},
modifier = Modifier.height(44.dp),
colors = btnColors,
shape = btnShape
) { Text("Force", color = CText, fontSize = 15.sp) }
}
)
}
}