core: faster tracking of active subscriptions; ui: only track in foreground (#4446)

* core: faster tracking of active subscriptions

* combine db transaction

* optimizations of queries from UI

* ios: track when active

* ios: disable log

---------

Co-authored-by: Avently <7953703+avently@users.noreply.github.com>
Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
This commit is contained in:
Evgeny Poberezkin
2024-07-13 08:44:51 +01:00
committed by GitHub
parent 23f24b1677
commit 3e873fcb32
8 changed files with 47 additions and 95 deletions
@@ -266,23 +266,19 @@ struct ChatListView: View {
}
struct SubsStatusIndicator: View {
@State private var subs: SMPServerSubs = SMPServerSubs.newSMPServerSubs
@State private var sess: ServerSessions = ServerSessions.newServerSessions
@State private var serversSummary: PresentedServersSummary?
@State private var timer: Timer? = nil
@State private var timerCounter = 0
@State private var showServersSummary = false
@AppStorage(DEFAULT_SHOW_SUBSCRIPTION_PERCENTAGE) private var showSubscriptionPercentage = false
// Constants for the intervals
let initialInterval: TimeInterval = 1.0
let regularInterval: TimeInterval = 3.0
let initialPhaseDuration: TimeInterval = 10.0 // Duration for initial phase in seconds
var body: some View {
Button {
showServersSummary = true
} label: {
let subs = serversSummary?.allUsersSMP.smpTotals.subs ?? SMPServerSubs.newSMPServerSubs
let sess = serversSummary?.allUsersSMP.smpTotals.sessions ?? ServerSessions.newServerSessions
HStack(spacing: 4) {
SubscriptionStatusIndicatorView(subs: subs, sess: sess)
if showSubscriptionPercentage {
@@ -291,34 +287,24 @@ struct SubsStatusIndicator: View {
}
}
.onAppear {
startInitialTimer()
startTimer()
}
.onDisappear {
stopTimer()
}
.sheet(isPresented: $showServersSummary) {
ServersSummaryView()
ServersSummaryView(serversSummary: $serversSummary)
}
}
private func startInitialTimer() {
timer = Timer.scheduledTimer(withTimeInterval: initialInterval, repeats: true) { _ in
getServersSummary()
timerCounter += 1
// Switch to the regular timer after the initial phase
if timerCounter * Int(initialInterval) >= Int(initialPhaseDuration) {
switchToRegularTimer()
private func startTimer() {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
if AppChatState.shared.value == .active {
getServersSummary()
}
}
}
func switchToRegularTimer() {
timer?.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: regularInterval, repeats: true) { _ in
getServersSummary()
}
}
func stopTimer() {
timer?.invalidate()
timer = nil
@@ -326,8 +312,7 @@ struct SubsStatusIndicator: View {
private func getServersSummary() {
do {
let summ = try getAgentServersSummary()
(subs, sess) = (summ.allUsersSMP.smpTotals.subs, summ.allUsersSMP.smpTotals.sessions)
serversSummary = try getAgentServersSummary()
} catch let error {
logger.error("getAgentServersSummary error: \(responseError(error))")
}
@@ -11,12 +11,12 @@ import SimpleXChat
struct ServersSummaryView: View {
@EnvironmentObject var m: ChatModel
@State private var serversSummary: PresentedServersSummary? = nil
@EnvironmentObject var theme: AppTheme
@Binding var serversSummary: PresentedServersSummary?
@State private var selectedUserCategory: PresentedUserCategory = .allUsers
@State private var selectedServerType: PresentedServerType = .smp
@State private var selectedSMPServer: String? = nil
@State private var selectedXFTPServer: String? = nil
@State private var timer: Timer? = nil
@State private var alert: SomeAlert?
@AppStorage(DEFAULT_SHOW_SUBSCRIPTION_PERCENTAGE) private var showSubscriptionPercentage = false
@@ -47,26 +47,10 @@ struct ServersSummaryView: View {
if m.users.filter({ u in u.user.activeUser || !u.user.hidden }).count == 1 {
selectedUserCategory = .currentUser
}
getServersSummary()
startTimer()
}
.onDisappear {
stopTimer()
}
.alert(item: $alert) { $0.alert }
}
private func startTimer() {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
getServersSummary()
}
}
func stopTimer() {
timer?.invalidate()
timer = nil
}
private func shareButton() -> some View {
Button {
if let serversSummary = serversSummary {
@@ -183,6 +167,8 @@ struct ServersSummaryView: View {
}
} else {
Text("No info, try to reload")
.foregroundColor(theme.colors.secondary)
.background(theme.colors.background)
}
}
@@ -369,7 +355,6 @@ struct ServersSummaryView: View {
Task {
do {
try await resetAgentServersStats()
getServersSummary()
} catch let error {
alert = SomeAlert(
alert: mkAlert(
@@ -389,14 +374,6 @@ struct ServersSummaryView: View {
Text("Reset all statistics")
}
}
private func getServersSummary() {
do {
serversSummary = try getAgentServersSummary()
} catch let error {
logger.error("getAgentServersSummary error: \(responseError(error))")
}
}
}
struct SubscriptionStatusIndicatorView: View {
@@ -734,5 +711,7 @@ struct DetailedXFTPStatsView: View {
}
#Preview {
ServersSummaryView()
ServersSummaryView(
serversSummary: Binding.constant(nil)
)
}
@@ -6,8 +6,6 @@ import android.net.LocalServerSocket
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.fragment.app.FragmentActivity
import chat.simplex.common.*
import chat.simplex.common.platform.*
import java.io.*
import java.lang.ref.WeakReference
import java.util.*
@@ -24,6 +22,8 @@ var isAppOnForeground: Boolean = false
@Suppress("ConstantLocale")
val defaultLocale: Locale = Locale.getDefault()
actual fun isAppVisibleAndFocused(): Boolean = isAppOnForeground
@SuppressLint("StaticFieldLeak")
lateinit var androidAppContext: Context
var mainActivity: WeakReference<FragmentActivity> = WeakReference(null)
@@ -3,7 +3,6 @@ package chat.simplex.common.platform
import chat.simplex.common.BuildConfigCommon
import chat.simplex.common.model.*
import chat.simplex.common.ui.theme.DefaultTheme
import java.io.File
import java.util.*
enum class AppPlatform {
@@ -20,6 +19,8 @@ expect val appPlatform: AppPlatform
expect val deviceName: String
expect fun isAppVisibleAndFocused(): Boolean
val appVersionInfo: Pair<String, Int?> = if (appPlatform == AppPlatform.ANDROID)
BuildConfigCommon.ANDROID_VERSION_NAME to BuildConfigCommon.ANDROID_VERSION_CODE
else
@@ -266,44 +266,29 @@ private fun ChatListToolbar(drawerState: DrawerState, userPickerState: MutableSt
fun SubscriptionStatusIndicator(serversSummary: MutableState<PresentedServersSummary?>, click: (() -> Unit)) {
var subs by remember { mutableStateOf(SMPServerSubs.newSMPServerSubs) }
var sess by remember { mutableStateOf(ServerSessions.newServerSessions) }
var timer: Job? by remember { mutableStateOf(null) }
val fetchInterval: Duration = 1.seconds
val scope = rememberCoroutineScope()
fun setServersSummary() {
withBGApi {
serversSummary.value = chatModel.controller.getAgentServersSummary(chatModel.remoteHostId())
suspend fun setServersSummary() {
serversSummary.value = chatModel.controller.getAgentServersSummary(chatModel.remoteHostId())
serversSummary.value?.let {
subs = it.allUsersSMP.smpTotals.subs
sess = it.allUsersSMP.smpTotals.sessions
}
serversSummary.value?.let {
subs = it.allUsersSMP.smpTotals.subs
sess = it.allUsersSMP.smpTotals.sessions
}
}
LaunchedEffect(Unit) {
setServersSummary()
timer = timer ?: scope.launch {
while (true) {
delay(fetchInterval.inWholeMilliseconds)
setServersSummary()
scope.launch {
while (isActive) {
delay(1.seconds)
if ((appPlatform.isDesktop || chatModel.chatId.value == null) && !ModalManager.start.hasModalsOpen() && !ModalManager.fullscreen.hasModalsOpen() && isAppVisibleAndFocused()) {
setServersSummary()
}
}
}
}
fun stopTimer() {
timer?.cancel()
timer = null
}
DisposableEffect(Unit) {
onDispose {
stopTimer()
}
}
SimpleButtonFrame(click = click) {
SubscriptionStatusIndicatorView(subs = subs, sess = sess)
}
@@ -1,6 +1,7 @@
package chat.simplex.common.platform
import chat.simplex.common.model.*
import chat.simplex.common.simplexWindowState
import chat.simplex.common.views.call.RcvCallInvitation
import chat.simplex.common.views.helpers.*
import java.util.*
@@ -10,6 +11,8 @@ actual val appPlatform = AppPlatform.DESKTOP
actual val deviceName = generalGetString(MR.strings.desktop_device)
actual fun isAppVisibleAndFocused() = simplexWindowState.windowFocused.value
@Suppress("ConstantLocale")
val defaultLocale: Locale = Locale.getDefault()