mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-15 07:28:30 +00:00
Fix issue where archival progress dialog never dismisses.
This commit is contained in:
@@ -14,7 +14,6 @@ import org.thoughtcrime.securesms.R
|
||||
*
|
||||
* @property message The text message to display in the snackbar.
|
||||
* @property actionState Optional action button configuration.
|
||||
* @property showProgress Whether to show a progress indicator in the snackbar.
|
||||
* @property duration How long the snackbar should be displayed.
|
||||
* @property hostKey The target host where this snackbar should be displayed. Defaults to [SnackbarHostKey.Global]
|
||||
* @property fallbackKey Optional host to fallback upon if the host key is not registered. Defaults to the Global key.
|
||||
@@ -22,7 +21,6 @@ import org.thoughtcrime.securesms.R
|
||||
data class SnackbarState(
|
||||
val message: String,
|
||||
val actionState: ActionState? = null,
|
||||
val showProgress: Boolean = false,
|
||||
val duration: Snackbars.Duration = Snackbars.Duration.SHORT,
|
||||
val hostKey: SnackbarHostKey = SnackbarHostKey.Global,
|
||||
val fallbackKey: SnackbarHostKey? = SnackbarHostKey.Global
|
||||
|
||||
@@ -8,7 +8,7 @@ package org.thoughtcrime.securesms.components.snackbars
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
@@ -27,11 +27,30 @@ val LocalSnackbarStateConsumerRegistry = staticCompositionLocalOf<SnackbarStateC
|
||||
error("No SnackbarStateConsumerRegistry provided")
|
||||
}
|
||||
|
||||
/**
|
||||
* Holder for snackbar state that allows clearing the state after consumption.
|
||||
*/
|
||||
@Stable
|
||||
class SnackbarStateHolder(
|
||||
private val state: MutableState<SnackbarState?>
|
||||
) {
|
||||
val value: SnackbarState?
|
||||
get() = state.value
|
||||
|
||||
/**
|
||||
* Clears the current snackbar state. Should be called after the snackbar has been consumed/dismissed.
|
||||
*/
|
||||
fun clear() {
|
||||
state.value = null
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberSnackbarState(
|
||||
key: SnackbarHostKey
|
||||
): State<SnackbarState?> {
|
||||
): SnackbarStateHolder {
|
||||
val state: MutableState<SnackbarState?> = remember(key) { mutableStateOf(null) }
|
||||
val holder = remember(key, state) { SnackbarStateHolder(state) }
|
||||
|
||||
val registry = LocalSnackbarStateConsumerRegistry.current
|
||||
DisposableEffect(registry, key) {
|
||||
@@ -44,7 +63,7 @@ fun rememberSnackbarState(
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
return holder
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -151,7 +151,6 @@ public class ConversationListArchiveFragment extends ConversationListFragment
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
),
|
||||
false,
|
||||
Snackbars.Duration.LONG,
|
||||
MainSnackbarHostKey.MainChrome.INSTANCE,
|
||||
null
|
||||
|
||||
@@ -68,6 +68,7 @@ import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.signal.core.util.DimensionUnit;
|
||||
import org.signal.core.util.Stopwatch;
|
||||
import org.signal.core.util.ThreadUtil;
|
||||
import org.signal.core.util.concurrent.LifecycleDisposable;
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.signal.core.util.concurrent.SimpleTask;
|
||||
@@ -158,7 +159,7 @@ import org.thoughtcrime.securesms.util.SignalProxyUtil;
|
||||
import org.thoughtcrime.securesms.util.SnapToTopDataObserver;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.PagingMappingAdapter;
|
||||
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
|
||||
import org.thoughtcrime.securesms.components.SignalProgressDialog;
|
||||
import org.thoughtcrime.securesms.util.views.Stub;
|
||||
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper;
|
||||
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState;
|
||||
@@ -230,6 +231,7 @@ public class ConversationListFragment extends MainFragment implements Conversati
|
||||
private ChatListBackHandler chatListBackHandler;
|
||||
|
||||
private BannerManager bannerManager;
|
||||
private SignalProgressDialog progressDialog;
|
||||
|
||||
protected MainNavigationViewModel mainNavigationViewModel;
|
||||
|
||||
@@ -464,6 +466,8 @@ public class ConversationListFragment extends MainFragment implements Conversati
|
||||
defaultAdapter = null;
|
||||
searchAdapter = null;
|
||||
|
||||
dismissProgressDialog();
|
||||
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@@ -968,16 +972,23 @@ public class ConversationListFragment extends MainFragment implements Conversati
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private void handleArchive(@NonNull Collection<Long> ids, boolean showProgress) {
|
||||
private void handleArchive(@NonNull Collection<Long> ids) {
|
||||
Set<Long> selectedConversations = new HashSet<>(ids);
|
||||
int count = selectedConversations.size();
|
||||
String snackBarTitle = getResources().getQuantityString(getArchivedSnackbarTitleRes(), count, count);
|
||||
boolean showProgress = count > 1;
|
||||
|
||||
dismissProgressDialog();
|
||||
if (showProgress) {
|
||||
progressDialog = SignalProgressDialog.show(requireContext(), null, null, true, false, null);
|
||||
}
|
||||
|
||||
lifecycleDisposable.add(Completable
|
||||
.fromAction(() -> archiveThreads(selectedConversations))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(() -> {
|
||||
dismissProgressDialog();
|
||||
endActionModeIfActive();
|
||||
|
||||
mainNavigationViewModel.getSnackbarRegistry().emit(new SnackbarState(
|
||||
@@ -986,11 +997,10 @@ public class ConversationListFragment extends MainFragment implements Conversati
|
||||
getString(R.string.ConversationListFragment_undo),
|
||||
R.color.amber_500,
|
||||
() -> {
|
||||
SignalExecutors.BOUNDED_IO.execute(() -> reverseArchiveThreads(selectedConversations));
|
||||
handleUnarchive(selectedConversations);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
),
|
||||
showProgress,
|
||||
Snackbars.Duration.LONG,
|
||||
MainSnackbarHostKey.MainChrome.INSTANCE,
|
||||
null
|
||||
@@ -998,6 +1008,27 @@ public class ConversationListFragment extends MainFragment implements Conversati
|
||||
}));
|
||||
}
|
||||
|
||||
private void handleUnarchive(@NonNull Set<Long> threadIds) {
|
||||
boolean showProgress = threadIds.size() > 1;
|
||||
|
||||
dismissProgressDialog();
|
||||
if (showProgress) {
|
||||
progressDialog = SignalProgressDialog.show(requireContext(), null, null, true, false, null);
|
||||
}
|
||||
|
||||
SignalExecutors.BOUNDED_IO.execute(() -> {
|
||||
reverseArchiveThreads(threadIds);
|
||||
ThreadUtil.runOnMain(this::dismissProgressDialog);
|
||||
});
|
||||
}
|
||||
|
||||
private void dismissProgressDialog() {
|
||||
if (progressDialog != null) {
|
||||
progressDialog.dismiss();
|
||||
progressDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private void handleDelete(@NonNull Collection<Long> ids) {
|
||||
if (DeleteSyncEducationDialog.shouldShow()) {
|
||||
@@ -1074,7 +1105,6 @@ public class ConversationListFragment extends MainFragment implements Conversati
|
||||
mainNavigationViewModel.getSnackbarRegistry().emit(new SnackbarState(
|
||||
getString(R.string.conversation_list__you_can_only_pin_up_to_d_chats, MAXIMUM_PINNED_CONVERSATIONS),
|
||||
null,
|
||||
false,
|
||||
Snackbars.Duration.LONG,
|
||||
MainSnackbarHostKey.MainChrome.INSTANCE,
|
||||
null
|
||||
@@ -1120,7 +1150,8 @@ public class ConversationListFragment extends MainFragment implements Conversati
|
||||
}
|
||||
|
||||
private void updateMute(@NonNull Collection<Conversation> conversations, long until) {
|
||||
SimpleProgressDialog.DismissibleDialog dialog = SimpleProgressDialog.showDelayed(requireContext(), 250, 250);
|
||||
dismissProgressDialog();
|
||||
progressDialog = SignalProgressDialog.show(requireContext(), null, null, true, false, null);
|
||||
|
||||
SimpleTask.run(SignalExecutors.BOUNDED, () -> {
|
||||
List<RecipientId> recipientIds = conversations.stream()
|
||||
@@ -1132,7 +1163,7 @@ public class ConversationListFragment extends MainFragment implements Conversati
|
||||
return null;
|
||||
}, unused -> {
|
||||
endActionModeIfActive();
|
||||
dialog.dismiss();
|
||||
dismissProgressDialog();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1245,7 +1276,7 @@ public class ConversationListFragment extends MainFragment implements Conversati
|
||||
}));
|
||||
|
||||
if (conversation.getThreadRecord().isArchived()) {
|
||||
items.add(new ActionItem(R.drawable.symbol_archive_up_24, getResources().getString(R.string.ConversationListFragment_unarchive), () -> handleArchive(id, false)));
|
||||
items.add(new ActionItem(R.drawable.symbol_archive_up_24, getResources().getString(R.string.ConversationListFragment_unarchive), () -> handleArchive(id)));
|
||||
} else {
|
||||
if (viewModel.getCurrentFolder().getFolderType() == ChatFolderRecord.FolderType.ALL &&
|
||||
(conversation.getThreadRecord().getRecipient().isIndividual() ||
|
||||
@@ -1257,7 +1288,7 @@ public class ConversationListFragment extends MainFragment implements Conversati
|
||||
} else if (viewModel.getCurrentFolder().getFolderType() != ChatFolderRecord.FolderType.ALL) {
|
||||
items.add(new ActionItem(R.drawable.symbol_folder_minus, getString(R.string.ConversationListFragment_remove_from_folder), () -> viewModel.removeChatFromFolder(conversation.getThreadRecord().getThreadId())));
|
||||
}
|
||||
items.add(new ActionItem(R.drawable.symbol_archive_24, getResources().getString(R.string.ConversationListFragment_archive), () -> handleArchive(id, false)));
|
||||
items.add(new ActionItem(R.drawable.symbol_archive_24, getResources().getString(R.string.ConversationListFragment_archive), () -> handleArchive(id)));
|
||||
}
|
||||
|
||||
items.add(new ActionItem(R.drawable.symbol_trash_24, getResources().getString(R.string.ConversationListFragment_delete), () -> handleDelete(id)));
|
||||
@@ -1360,9 +1391,9 @@ public class ConversationListFragment extends MainFragment implements Conversati
|
||||
}
|
||||
|
||||
if (isArchived()) {
|
||||
items.add(new ActionItem(R.drawable.symbol_archive_up_24, getResources().getString(R.string.ConversationListFragment_unarchive), () -> handleArchive(selectionIds, true)));
|
||||
items.add(new ActionItem(R.drawable.symbol_archive_up_24, getResources().getString(R.string.ConversationListFragment_unarchive), () -> handleArchive(selectionIds)));
|
||||
} else {
|
||||
items.add(new ActionItem(R.drawable.symbol_archive_24, getResources().getString(R.string.ConversationListFragment_archive), () -> handleArchive(selectionIds, true)));
|
||||
items.add(new ActionItem(R.drawable.symbol_archive_24, getResources().getString(R.string.ConversationListFragment_archive), () -> handleArchive(selectionIds)));
|
||||
}
|
||||
|
||||
items.add(new ActionItem(R.drawable.symbol_trash_24, getResources().getString(R.string.ConversationListFragment_delete), () -> handleDelete(selectionIds)));
|
||||
@@ -1440,7 +1471,6 @@ public class ConversationListFragment extends MainFragment implements Conversati
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
),
|
||||
false,
|
||||
Snackbars.Duration.LONG,
|
||||
MainSnackbarHostKey.MainChrome.INSTANCE,
|
||||
null
|
||||
|
||||
@@ -21,12 +21,10 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import org.signal.core.ui.compose.AllDevicePreviews
|
||||
import org.signal.core.ui.compose.Dialogs
|
||||
import org.signal.core.ui.compose.Previews
|
||||
import org.signal.core.ui.compose.Snackbars
|
||||
import org.signal.core.ui.compose.showSnackbar
|
||||
import org.thoughtcrime.securesms.components.snackbars.SnackbarHostKey
|
||||
import org.thoughtcrime.securesms.components.snackbars.SnackbarState
|
||||
import org.thoughtcrime.securesms.components.snackbars.rememberSnackbarState
|
||||
import org.thoughtcrime.securesms.megaphone.Megaphone
|
||||
import org.thoughtcrime.securesms.megaphone.MegaphoneActionController
|
||||
@@ -118,18 +116,14 @@ fun MainSnackbar(
|
||||
hostKey: SnackbarHostKey = MainSnackbarHostKey.MainChrome
|
||||
) {
|
||||
val hostState = remember { SnackbarHostState() }
|
||||
val state: SnackbarState? by rememberSnackbarState(hostKey)
|
||||
val snackbarState = state
|
||||
val stateHolder = rememberSnackbarState(hostKey)
|
||||
val snackbarState = stateHolder.value
|
||||
|
||||
Snackbars.Host(
|
||||
hostState,
|
||||
modifier = modifier
|
||||
)
|
||||
|
||||
if (snackbarState?.showProgress == true) {
|
||||
Dialogs.IndeterminateProgressDialog()
|
||||
}
|
||||
|
||||
LaunchedEffect(snackbarState) {
|
||||
if (snackbarState != null) {
|
||||
val result = hostState.showSnackbar(
|
||||
@@ -143,6 +137,7 @@ fun MainSnackbar(
|
||||
SnackbarResult.ActionPerformed -> snackbarState.actionState?.onActionClick?.invoke()
|
||||
}
|
||||
|
||||
stateHolder.clear()
|
||||
onDismissed()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user