mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-28 00:44:27 +00:00
android, desktop, ios: extract progress indicator timeout
The effect that turns on the progress indicator after a delay was copied into 5 Kotlin and 4 Swift views. Two of the Swift copies had drifted: the deferred write captured the value that started the delay rather than reading the current one, so an operation that finished before the delay elapsed left the indicator on with nothing to turn it off. Timing is unchanged.
This commit is contained in:
@@ -75,15 +75,7 @@ struct CIGroupInvitationView: View {
|
||||
.background { chatItemFrameColor(chatItem, theme).modifier(ChatTailPadding()) }
|
||||
.textSelection(.disabled)
|
||||
.onPreferenceChange(DetermineWidth.Key.self) { frameWidth = $0 }
|
||||
.onChange(of: inProgress) { inProgress in
|
||||
if inProgress {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
progressByTimeout = inProgress
|
||||
}
|
||||
} else {
|
||||
progressByTimeout = false
|
||||
}
|
||||
}
|
||||
.progressByTimeout(inProgress, $progressByTimeout)
|
||||
|
||||
if action {
|
||||
v.simultaneousGesture(TapGesture().onEnded {
|
||||
|
||||
@@ -584,15 +584,7 @@ struct ComposeView: View {
|
||||
clearState()
|
||||
}
|
||||
}
|
||||
.onChange(of: composeState.inProgress) { inProgress in
|
||||
if inProgress {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
composeState.progressByTimeout = composeState.inProgress
|
||||
}
|
||||
} else {
|
||||
composeState.progressByTimeout = false
|
||||
}
|
||||
}
|
||||
.progressByTimeout(composeState.inProgress, $composeState.progressByTimeout)
|
||||
.confirmationDialog("Attach", isPresented: $showChooseSource, titleVisibility: .visible) {
|
||||
Button("Take picture") {
|
||||
showTakePhoto = true
|
||||
|
||||
@@ -44,15 +44,7 @@ struct ContextContactRequestActionsView: View {
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
}
|
||||
.onChange(of: inProgress) { inPrgrs in
|
||||
if inPrgrs {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
progressByTimeout = inProgress
|
||||
}
|
||||
} else {
|
||||
progressByTimeout = false
|
||||
}
|
||||
}
|
||||
.progressByTimeout(inProgress, $progressByTimeout)
|
||||
}
|
||||
|
||||
private func showRejectRequestAlert() {
|
||||
|
||||
@@ -79,15 +79,7 @@ struct ChatListNavLink: View {
|
||||
invalidJSONPreview(json)
|
||||
}
|
||||
}
|
||||
.onChange(of: inProgress) { inProgress in
|
||||
if inProgress {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
progressByTimeout = inProgress
|
||||
}
|
||||
} else {
|
||||
progressByTimeout = false
|
||||
}
|
||||
}
|
||||
.progressByTimeout(inProgress, $progressByTimeout)
|
||||
.actionSheet(item: $actionSheet) { $0.actionSheet }
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,23 @@ extension View {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
func progressByTimeout(_ inProgress: Bool, _ progressByTimeout: Binding<Bool>) -> some View {
|
||||
task(id: inProgress) {
|
||||
if inProgress {
|
||||
try? await Task.sleep(nanoseconds: progressTimeout)
|
||||
if !Task.isCancelled {
|
||||
progressByTimeout.wrappedValue = true
|
||||
}
|
||||
} else {
|
||||
progressByTimeout.wrappedValue = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let progressTimeout: UInt64 = 1_000_000000
|
||||
|
||||
extension Notification.Name {
|
||||
static let chatViewWillBeginScrolling = Notification.Name("chatWillBeginScrolling")
|
||||
}
|
||||
|
||||
+1
-12
@@ -21,7 +21,6 @@ import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
fun ComposeContextContactRequestActionsView(
|
||||
@@ -31,21 +30,11 @@ fun ComposeContextContactRequestActionsView(
|
||||
val inProgressLocal = rememberSaveable { mutableStateOf(false) }
|
||||
// the request can also be accepted from another view, e.g. via notification
|
||||
val inProgress = remember(contactRequestId) { derivedStateOf { inProgressLocal.value || contactRequestId in chatModel.acceptingContactRequests } }
|
||||
var progressByTimeout by rememberSaveable { mutableStateOf(false) }
|
||||
val progressByTimeout by rememberProgressByTimeout(inProgress)
|
||||
|
||||
KeyChangeEffect(chatModel.chatId.value) {
|
||||
if (inProgressLocal.value) {
|
||||
inProgressLocal.value = false
|
||||
progressByTimeout = false
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(inProgress.value) {
|
||||
progressByTimeout = if (inProgress.value) {
|
||||
delay(1000)
|
||||
inProgress.value
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-11
@@ -28,21 +28,11 @@ fun ComposeContextMemberContactActionsView(
|
||||
groupDirectInv: GroupDirectInvitation
|
||||
) {
|
||||
val inProgress = rememberSaveable { mutableStateOf(false) }
|
||||
var progressByTimeout by rememberSaveable { mutableStateOf(false) }
|
||||
val progressByTimeout by rememberProgressByTimeout(inProgress)
|
||||
|
||||
KeyChangeEffect(chatModel.chatId.value) {
|
||||
if (inProgress.value) {
|
||||
inProgress.value = false
|
||||
progressByTimeout = false
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(inProgress.value) {
|
||||
progressByTimeout = if (inProgress.value) {
|
||||
delay(1000)
|
||||
inProgress.value
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-8
@@ -1568,14 +1568,8 @@ fun ComposeView(
|
||||
chatModel.sharedContent.value = null
|
||||
}
|
||||
|
||||
LaunchedEffect(composeState.value.inProgress) {
|
||||
val newProgressByTimeout = if (composeState.value.inProgress) {
|
||||
delay(1000)
|
||||
composeState.value.inProgress
|
||||
} else {
|
||||
false
|
||||
}
|
||||
composeState.value = composeState.value.copy(progressByTimeout = newProgressByTimeout)
|
||||
ProgressByTimeoutEffect(composeState.value.inProgress) {
|
||||
composeState.value = composeState.value.copy(progressByTimeout = it)
|
||||
}
|
||||
|
||||
val relayListExpanded = remember { mutableStateOf(false) }
|
||||
|
||||
+1
-11
@@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
@@ -20,7 +19,6 @@ import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
fun CIGroupInvitationView(
|
||||
@@ -35,15 +33,7 @@ fun CIGroupInvitationView(
|
||||
val sent = ci.chatDir.sent
|
||||
val action = !sent && groupInvitation.status == CIGroupInvitationStatus.Pending
|
||||
val inProgress = remember { mutableStateOf(false) }
|
||||
var progressByTimeout by rememberSaveable { mutableStateOf(false) }
|
||||
LaunchedEffect(inProgress.value) {
|
||||
progressByTimeout = if (inProgress.value) {
|
||||
delay(1000)
|
||||
inProgress.value
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
val progressByTimeout by rememberProgressByTimeout(inProgress)
|
||||
|
||||
@Composable
|
||||
fun groupInfoView() {
|
||||
|
||||
+1
-10
@@ -48,16 +48,7 @@ fun ChatListNavLinkView(chat: Chat, nextChatSelected: State<Boolean>) {
|
||||
val selectedChat = remember(chat.id) { derivedStateOf { chat.id == chatModel.chatId.value } }
|
||||
val showChatPreviews = chatModel.showChatPreviews.value
|
||||
val inProgress = remember { mutableStateOf(false) }
|
||||
var progressByTimeout by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(inProgress.value) {
|
||||
progressByTimeout = if (inProgress.value) {
|
||||
delay(1000)
|
||||
inProgress.value
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
val progressByTimeout by rememberProgressByTimeout(inProgress)
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package chat.simplex.common.views.helpers
|
||||
|
||||
import androidx.compose.runtime.*
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
private const val PROGRESS_TIMEOUT_MS = 1000L
|
||||
|
||||
@Composable
|
||||
fun ProgressByTimeoutEffect(inProgress: Boolean, setProgressByTimeout: (Boolean) -> Unit) {
|
||||
LaunchedEffect(inProgress) {
|
||||
if (inProgress) {
|
||||
delay(PROGRESS_TIMEOUT_MS)
|
||||
setProgressByTimeout(true)
|
||||
} else {
|
||||
setProgressByTimeout(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberProgressByTimeout(inProgress: State<Boolean>): State<Boolean> {
|
||||
val progressByTimeout = remember { mutableStateOf(false) }
|
||||
ProgressByTimeoutEffect(inProgress.value) { progressByTimeout.value = it }
|
||||
return progressByTimeout
|
||||
}
|
||||
+1
-10
@@ -303,16 +303,7 @@ fun ActiveProfilePicker(
|
||||
filteredProfiles(chatModel.users.map { it.user }.sortedBy { !it.activeUser }, searchTextOrPassword.value)
|
||||
}
|
||||
|
||||
var progressByTimeout by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(switchingProfile.value) {
|
||||
progressByTimeout = if (switchingProfile.value) {
|
||||
delay(500)
|
||||
switchingProfile.value
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
val progressByTimeout by rememberProgressByTimeout(switchingProfile)
|
||||
|
||||
@Composable
|
||||
fun ProfilePickerUserOption(user: User) {
|
||||
|
||||
Reference in New Issue
Block a user