multiplatform: optimize subscription indicator (#4503)

This commit is contained in:
spaced4ndy
2024-07-22 20:40:22 +04:00
committed by GitHub
parent 2689d1e27b
commit 70a94d772a
3 changed files with 76 additions and 43 deletions
@@ -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" +
@@ -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)
}
}
@@ -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
)
}