diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIGroupInvitationView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIGroupInvitationView.swift index ddb58fdfd1..b34076eafd 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIGroupInvitationView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIGroupInvitationView.swift @@ -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 { diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift index 734ccc083c..ce809dedf6 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift @@ -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 diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ContextContactRequestActionsView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ContextContactRequestActionsView.swift index 82c89cd43d..41794f2dad 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ContextContactRequestActionsView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ContextContactRequestActionsView.swift @@ -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() { diff --git a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift index 0ed78401b0..e7e871a20e 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift @@ -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 } } diff --git a/apps/ios/Shared/Views/Helpers/ViewModifiers.swift b/apps/ios/Shared/Views/Helpers/ViewModifiers.swift index 902a3f95d7..cedd75d561 100644 --- a/apps/ios/Shared/Views/Helpers/ViewModifiers.swift +++ b/apps/ios/Shared/Views/Helpers/ViewModifiers.swift @@ -26,8 +26,23 @@ extension View { self } } + + func progressByTimeout(_ inProgress: Bool, _ progressByTimeout: Binding) -> 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") } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeContextContactRequestActionsView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeContextContactRequestActionsView.kt index de558a894e..86b3f7e14d 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeContextContactRequestActionsView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeContextContactRequestActionsView.kt @@ -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 } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeContextGroupDirectInvitationActionsView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeContextGroupDirectInvitationActionsView.kt index 21799ff820..d6092bfa69 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeContextGroupDirectInvitationActionsView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeContextGroupDirectInvitationActionsView.kt @@ -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 } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt index b0dad99452..20776957af 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt @@ -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) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIGroupInvitationView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIGroupInvitationView.kt index 9b8393f66a..86f03f15ac 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIGroupInvitationView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIGroupInvitationView.kt @@ -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() { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt index 8401f138fe..d594881014 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt @@ -48,16 +48,7 @@ fun ChatListNavLinkView(chat: Chat, nextChatSelected: State) { 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() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ProgressByTimeout.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ProgressByTimeout.kt new file mode 100644 index 0000000000..a5e0b79ab4 --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ProgressByTimeout.kt @@ -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): State { + val progressByTimeout = remember { mutableStateOf(false) } + ProgressByTimeoutEffect(inProgress.value) { progressByTimeout.value = it } + return progressByTimeout +} diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt index d3bca178aa..3c67cd1aea 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt @@ -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) {