mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-29 20:06:00 +00:00
multiplatform: optimize subscription indicator (#4503)
This commit is contained in:
+19
@@ -423,6 +423,16 @@ object ChatController {
|
||||
|
||||
fun hasChatCtrl() = ctrl != -1L && ctrl != null
|
||||
|
||||
suspend fun getAgentSubsTotal(rh: Long?): Pair<SMPServerSubs, Boolean>? {
|
||||
val userId = currentUserId("getAgentSubsTotal")
|
||||
|
||||
val r = sendCmd(rh, CC.GetAgentSubsTotal(userId), log = false)
|
||||
|
||||
if (r is CR.AgentSubsTotal) return r.subsTotal to r.hasSession
|
||||
Log.e(TAG, "getAgentSubsTotal bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun getAgentServersSummary(rh: Long?): PresentedServersSummary? {
|
||||
val userId = currentUserId("getAgentServersSummary")
|
||||
|
||||
@@ -2815,6 +2825,7 @@ sealed class CC {
|
||||
// misc
|
||||
class ShowVersion(): CC()
|
||||
class ResetAgentServersStats(): CC()
|
||||
class GetAgentSubsTotal(val userId: Long): CC()
|
||||
class GetAgentServersSummary(val userId: Long): CC()
|
||||
|
||||
val cmdString: String get() = when (this) {
|
||||
@@ -2975,6 +2986,7 @@ sealed class CC {
|
||||
is ApiStandaloneFileInfo -> "/_download info $url"
|
||||
is ShowVersion -> "/version"
|
||||
is ResetAgentServersStats -> "/reset servers stats"
|
||||
is GetAgentSubsTotal -> "/get subs total $userId"
|
||||
is GetAgentServersSummary -> "/get servers summary $userId"
|
||||
}
|
||||
|
||||
@@ -3108,6 +3120,7 @@ sealed class CC {
|
||||
is ApiStandaloneFileInfo -> "apiStandaloneFileInfo"
|
||||
is ShowVersion -> "showVersion"
|
||||
is ResetAgentServersStats -> "resetAgentServersStats"
|
||||
is GetAgentSubsTotal -> "getAgentSubsTotal"
|
||||
is GetAgentServersSummary -> "getAgentServersSummary"
|
||||
}
|
||||
|
||||
@@ -3638,6 +3651,9 @@ data class ServerSessions(
|
||||
ssConnecting = 0
|
||||
)
|
||||
}
|
||||
|
||||
val hasSess: Boolean
|
||||
get() = ssConnected > 0
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@@ -4743,6 +4759,7 @@ sealed class CR {
|
||||
@Serializable @SerialName("chatError") class ChatRespError(val user_: UserRef?, val chatError: ChatError): CR()
|
||||
@Serializable @SerialName("archiveImported") class ArchiveImported(val archiveErrors: List<ArchiveError>): CR()
|
||||
@Serializable @SerialName("appSettings") class AppSettingsR(val appSettings: AppSettings): CR()
|
||||
@Serializable @SerialName("agentSubsTotal") class AgentSubsTotal(val user: UserRef, val subsTotal: SMPServerSubs, val hasSession: Boolean): CR()
|
||||
@Serializable @SerialName("agentServersSummary") class AgentServersSummary(val user: UserRef, val serversSummary: PresentedServersSummary): CR()
|
||||
// general
|
||||
@Serializable class Response(val type: String, val json: String): CR()
|
||||
@@ -4904,6 +4921,7 @@ sealed class CR {
|
||||
is ContactPQAllowed -> "contactPQAllowed"
|
||||
is ContactPQEnabled -> "contactPQEnabled"
|
||||
is VersionInfo -> "versionInfo"
|
||||
is AgentSubsTotal -> "agentSubsTotal"
|
||||
is AgentServersSummary -> "agentServersSummary"
|
||||
is CmdOk -> "cmdOk"
|
||||
is ChatCmdError -> "chatCmdError"
|
||||
@@ -5084,6 +5102,7 @@ sealed class CR {
|
||||
is RemoteCtrlStopped -> "rcsState: $rcsState\nrcsStopReason: $rcStopReason"
|
||||
is ContactPQAllowed -> withUser(user, "contact: ${contact.id}\npqEncryption: $pqEncryption")
|
||||
is ContactPQEnabled -> withUser(user, "contact: ${contact.id}\npqEnabled: $pqEnabled")
|
||||
is AgentSubsTotal -> withUser(user, "subsTotal: ${subsTotal}\nhasSession: $hasSession")
|
||||
is AgentServersSummary -> withUser(user, json.encodeToString(serversSummary))
|
||||
is VersionInfo -> "version ${json.encodeToString(versionInfo)}\n\n" +
|
||||
"chat migrations: ${json.encodeToString(chatMigrations.map { it.upName })}\n\n" +
|
||||
|
||||
+10
-12
@@ -255,7 +255,6 @@ private fun ChatListToolbar(drawerState: DrawerState, userPickerState: MutableSt
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
SubscriptionStatusIndicator(
|
||||
serversSummary = serversSummary,
|
||||
click = {
|
||||
ModalManager.start.closeModals()
|
||||
ModalManager.start.showModalCloseable(
|
||||
@@ -286,34 +285,33 @@ private fun ChatListToolbar(drawerState: DrawerState, userPickerState: MutableSt
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SubscriptionStatusIndicator(serversSummary: MutableState<PresentedServersSummary?>, click: (() -> Unit)) {
|
||||
fun SubscriptionStatusIndicator(click: (() -> Unit)) {
|
||||
var subs by remember { mutableStateOf(SMPServerSubs.newSMPServerSubs) }
|
||||
var sess by remember { mutableStateOf(ServerSessions.newServerSessions) }
|
||||
var hasSess by remember { mutableStateOf(false) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
suspend fun setServersSummary() {
|
||||
serversSummary.value = chatModel.controller.getAgentServersSummary(chatModel.remoteHostId())
|
||||
|
||||
serversSummary.value?.let {
|
||||
subs = it.allUsersSMP.smpTotals.subs
|
||||
sess = it.allUsersSMP.smpTotals.sessions
|
||||
suspend fun setSubsTotal() {
|
||||
val r = chatModel.controller.getAgentSubsTotal(chatModel.remoteHostId())
|
||||
if (r != null) {
|
||||
subs = r.first
|
||||
hasSess = r.second
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
setServersSummary()
|
||||
setSubsTotal()
|
||||
scope.launch {
|
||||
while (isActive) {
|
||||
delay(1.seconds)
|
||||
if ((appPlatform.isDesktop || chatModel.chatId.value == null) && !ModalManager.start.hasModalsOpen() && !ModalManager.fullscreen.hasModalsOpen() && isAppVisibleAndFocused()) {
|
||||
setServersSummary()
|
||||
setSubsTotal()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SimpleButtonFrame(click = click) {
|
||||
SubscriptionStatusIndicatorView(subs = subs, sess = sess)
|
||||
SubscriptionStatusIndicatorView(subs = subs, hasSess = hasSess)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+47
-31
@@ -45,8 +45,7 @@ import chat.simplex.common.model.ServerProtocol
|
||||
import chat.simplex.common.model.ServerSessions
|
||||
import chat.simplex.common.model.XFTPServerSummary
|
||||
import chat.simplex.common.model.localTimestamp
|
||||
import chat.simplex.common.platform.ColumnWithScrollBar
|
||||
import chat.simplex.common.platform.appPlatform
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.usersettings.ProtocolServersView
|
||||
@@ -54,12 +53,13 @@ import chat.simplex.common.views.usersettings.SettingsPreferenceItem
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.datetime.Instant
|
||||
import numOrDash
|
||||
import java.text.DecimalFormat
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
enum class SubscriptionColorType {
|
||||
ACTIVE, ACTIVE_SOCKS_PROXY, DISCONNECTED, ACTIVE_DISCONNECTED
|
||||
@@ -76,7 +76,7 @@ fun subscriptionStatusColorAndPercentage(
|
||||
online: Boolean,
|
||||
socksProxy: String?,
|
||||
subs: SMPServerSubs,
|
||||
sess: ServerSessions
|
||||
hasSess: Boolean
|
||||
): SubscriptionStatus {
|
||||
|
||||
fun roundedToQuarter(n: Float): Float = when {
|
||||
@@ -91,16 +91,16 @@ fun subscriptionStatusColorAndPercentage(
|
||||
|
||||
return if (online && subs.total > 0) {
|
||||
if (subs.ssActive == 0) {
|
||||
if (sess.ssConnected == 0)
|
||||
noConnColorAndPercent
|
||||
else
|
||||
if (hasSess)
|
||||
SubscriptionStatus(activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive)
|
||||
else
|
||||
noConnColorAndPercent
|
||||
} else { // ssActive > 0
|
||||
if (sess.ssConnected == 0)
|
||||
if (hasSess)
|
||||
SubscriptionStatus(activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive)
|
||||
else
|
||||
// This would mean implementation error
|
||||
SubscriptionStatus(SubscriptionColorType.ACTIVE_DISCONNECTED, activeSubsRounded, subs.shareOfActive, subs.shareOfActive)
|
||||
else
|
||||
SubscriptionStatus(activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive)
|
||||
}
|
||||
} else noConnColorAndPercent
|
||||
}
|
||||
@@ -116,9 +116,9 @@ private fun SubscriptionStatusIndicatorPercentage(percentageText: String) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SubscriptionStatusIndicatorView(subs: SMPServerSubs, sess: ServerSessions, leadingPercentage: Boolean = false) {
|
||||
fun SubscriptionStatusIndicatorView(subs: SMPServerSubs, hasSess: Boolean, leadingPercentage: Boolean = false) {
|
||||
val netCfg = rememberUpdatedState(chatModel.controller.getNetCfg())
|
||||
val statusColorAndPercentage = subscriptionStatusColorAndPercentage(chatModel.networkInfo.value.online, netCfg.value.socksProxy, subs, sess)
|
||||
val statusColorAndPercentage = subscriptionStatusColorAndPercentage(chatModel.networkInfo.value.online, netCfg.value.socksProxy, subs, hasSess)
|
||||
val pref = remember { chatModel.controller.appPrefs.networkShowSubscriptionPercentage }
|
||||
val percentageText = "${(floor(statusColorAndPercentage.statusPercent * 100)).toInt()}%"
|
||||
|
||||
@@ -193,7 +193,7 @@ private fun SMPServerView(srvSumm: SMPServerSummary, statsStartedAt: Instant, rh
|
||||
)
|
||||
if (srvSumm.subs != null) {
|
||||
Spacer(Modifier.fillMaxWidth().weight(1f))
|
||||
SubscriptionStatusIndicatorView(subs = srvSumm.subs, sess = srvSumm.sessionsOrNew, leadingPercentage = true)
|
||||
SubscriptionStatusIndicatorView(subs = srvSumm.subs, hasSess = srvSumm.sessionsOrNew.hasSess, leadingPercentage = true)
|
||||
} else if (srvSumm.sessions != null) {
|
||||
Spacer(Modifier.fillMaxWidth().weight(1f))
|
||||
Icon(painterResource(MR.images.ic_arrow_upward), contentDescription = null, tint = SessIconColor(srvSumm.sessions))
|
||||
@@ -334,7 +334,7 @@ private fun SMPSubscriptionsSection(totals: SMPTotals) {
|
||||
style = MaterialTheme.typography.body2,
|
||||
fontSize = 12.sp
|
||||
)
|
||||
SubscriptionStatusIndicatorView(totals.subs, totals.sessions)
|
||||
SubscriptionStatusIndicatorView(totals.subs, totals.sessions.hasSess)
|
||||
}
|
||||
Column(Modifier.padding(PaddingValues()).fillMaxWidth()) {
|
||||
InfoRow(
|
||||
@@ -364,7 +364,7 @@ private fun SMPSubscriptionsSection(subs: SMPServerSubs, summary: SMPServerSumma
|
||||
style = MaterialTheme.typography.body2,
|
||||
fontSize = 12.sp
|
||||
)
|
||||
SubscriptionStatusIndicatorView(subs, summary.sessionsOrNew)
|
||||
SubscriptionStatusIndicatorView(subs, summary.sessionsOrNew.hasSess)
|
||||
}
|
||||
Column(Modifier.padding(PaddingValues()).fillMaxWidth()) {
|
||||
InfoRow(
|
||||
@@ -718,6 +718,10 @@ fun ModalData.ServersSummaryView(rh: RemoteHostInfo?, serversSummary: MutableSta
|
||||
remember { stateGetOrPut("serverTypeSelection") { PresentedServerType.SMP } }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
suspend fun setServersSummary() {
|
||||
serversSummary.value = chatModel.controller.getAgentServersSummary(chatModel.remoteHostId())
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (chatModel.users.count { u -> u.user.activeUser || !u.user.hidden } == 1
|
||||
) {
|
||||
@@ -725,6 +729,29 @@ fun ModalData.ServersSummaryView(rh: RemoteHostInfo?, serversSummary: MutableSta
|
||||
} else {
|
||||
showUserSelection = true
|
||||
}
|
||||
setServersSummary()
|
||||
scope.launch {
|
||||
while (isActive) {
|
||||
delay(1.seconds)
|
||||
if ((appPlatform.isDesktop || chat.simplex.common.platform.chatModel.chatId.value == null) && isAppVisibleAndFocused()) {
|
||||
setServersSummary()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun resetStats() {
|
||||
withBGApi {
|
||||
val success = controller.resetAgentServersStats(rh?.remoteHostId)
|
||||
if (success) {
|
||||
setServersSummary()
|
||||
} else {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.servers_info_modal_error_title),
|
||||
text = generalGetString(MR.strings.servers_info_reset_stats_alert_error_title)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
@@ -908,7 +935,7 @@ fun ModalData.ServersSummaryView(rh: RemoteHostInfo?, serversSummary: MutableSta
|
||||
|
||||
SectionView {
|
||||
ReconnectAllServersButton(rh)
|
||||
ResetStatisticsButton(rh)
|
||||
ResetStatisticsButton(rh, resetStats = { resetStats() })
|
||||
}
|
||||
|
||||
SectionBottomSpacer()
|
||||
@@ -949,8 +976,8 @@ private fun reconnectAllServersAlert(rh: RemoteHostInfo?) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ResetStatisticsButton(rh: RemoteHostInfo?) {
|
||||
SectionItemView(click = { resetStatisticsAlert(rh) }) {
|
||||
private fun ResetStatisticsButton(rh: RemoteHostInfo?, resetStats: () -> Unit) {
|
||||
SectionItemView(click = { resetStatisticsAlert(rh, resetStats) }) {
|
||||
Text(
|
||||
stringResource(MR.strings.servers_info_reset_stats),
|
||||
color = MaterialTheme.colors.primary
|
||||
@@ -958,23 +985,12 @@ private fun ResetStatisticsButton(rh: RemoteHostInfo?) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetStatisticsAlert(rh: RemoteHostInfo?) {
|
||||
private fun resetStatisticsAlert(rh: RemoteHostInfo?, resetStats: () -> Unit) {
|
||||
AlertManager.shared.showAlertDialog(
|
||||
title = generalGetString(MR.strings.servers_info_reset_stats_alert_title),
|
||||
text = generalGetString(MR.strings.servers_info_reset_stats_alert_message),
|
||||
confirmText = generalGetString(MR.strings.servers_info_reset_stats_alert_confirm),
|
||||
destructive = true,
|
||||
onConfirm = {
|
||||
withBGApi {
|
||||
val success = controller.resetAgentServersStats(rh?.remoteHostId)
|
||||
|
||||
if (!success) {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.servers_info_modal_error_title),
|
||||
text = generalGetString(MR.strings.servers_info_reset_stats_alert_error_title)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
onConfirm = resetStats
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user