mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-09-28 15:53:25 +01:00
Add remove pack protection dialog.
This commit is contained in:
+5
-13
@@ -734,19 +734,11 @@ class ConversationFragment :
|
||||
)
|
||||
}
|
||||
|
||||
is MediaKeyboardAction.RemoveStickerPackClicked -> {
|
||||
container.onHostWindowShown()
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(resources.getQuantityString(R.plurals.StickerManagement_delete_n_packs_confirmation, 1, 1))
|
||||
.setMessage(resources.getQuantityString(R.plurals.StickerManagement_delete_n_packs_confirmation_body, 1, 1))
|
||||
.setPositiveButton(R.string.StickerManagement_menu_remove_pack) { _, _ ->
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
StickerManagementRepository.uninstallStickerPacks(mapOf(StickerPackId(action.packId) to StickerPackKey(action.packKey)))
|
||||
}
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setOnDismissListener { container.onHostWindowHidden() }
|
||||
.show()
|
||||
// The keyboard has already confirmed this with the user.
|
||||
is MediaKeyboardAction.RemoveStickerPackConfirmed -> {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
StickerManagementRepository.uninstallStickerPacks(mapOf(StickerPackId(action.packId) to StickerPackKey(action.packKey)))
|
||||
}
|
||||
}
|
||||
|
||||
// A screen of its own rather than a window over this one, and picking a gif carries on into
|
||||
|
||||
@@ -74,7 +74,7 @@ fun MainScreen(
|
||||
MediaKeyboardAction.GifSearchClicked,
|
||||
is MediaKeyboardAction.ViewStickerPackClicked,
|
||||
is MediaKeyboardAction.SendStickerPackClicked,
|
||||
is MediaKeyboardAction.RemoveStickerPackClicked -> Unit
|
||||
is MediaKeyboardAction.RemoveStickerPackConfirmed -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -67,10 +67,10 @@ sealed interface MediaKeyboardAction {
|
||||
data class SendStickerPackClicked(val packId: String, val packKey: String) : MediaKeyboardAction
|
||||
|
||||
/**
|
||||
* Uninstall a sticker pack, confirming with the user first.
|
||||
* Uninstall a sticker pack. The keyboard has already confirmed it with the user.
|
||||
*
|
||||
* @param packId The pack to remove.
|
||||
* @param packKey Its key, which identifies the pack alongside [packId].
|
||||
*/
|
||||
data class RemoveStickerPackClicked(val packId: String, val packKey: String) : MediaKeyboardAction
|
||||
data class RemoveStickerPackConfirmed(val packId: String, val packKey: String) : MediaKeyboardAction
|
||||
}
|
||||
|
||||
+28
@@ -30,6 +30,7 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -42,9 +43,11 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.signal.core.ui.compose.DayNightPreviews
|
||||
import org.signal.core.ui.compose.Dialogs
|
||||
import org.signal.core.ui.compose.DropdownMenus
|
||||
import org.signal.core.ui.compose.SignalIcons
|
||||
import org.signal.core.ui.compose.SignalPreviewWrapper
|
||||
import org.signal.core.ui.compose.keyboard.LocalKeyboardSheetController
|
||||
import org.signal.core.ui.compose.theme.SignalTheme
|
||||
import org.signal.glide.compose.GlideImage
|
||||
import org.signal.mediakeyboard.R
|
||||
@@ -77,6 +80,31 @@ fun StickerPageScreen(
|
||||
onSearchFieldRevealedChange = onSearchFieldRevealedChange
|
||||
)
|
||||
}
|
||||
|
||||
if (state.confirmRemovePack != null) {
|
||||
ConfirmRemovePackDialog(onEvent = onEvent)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ConfirmRemovePackDialog(onEvent: (StickerPageScreenEvents) -> Unit) {
|
||||
val host = LocalKeyboardSheetController.current
|
||||
|
||||
// A window of our own in front of the sheet, so the sheet stays put rather than giving way to it.
|
||||
DisposableEffect(Unit) {
|
||||
host.onHostWindowShown()
|
||||
onDispose { host.onHostWindowHidden() }
|
||||
}
|
||||
|
||||
Dialogs.SimpleAlertDialog(
|
||||
title = stringResource(R.string.MediaKeyboard__remove_sticker_pack_question),
|
||||
body = stringResource(R.string.MediaKeyboard__this_will_remove_the_sticker_pack),
|
||||
confirm = stringResource(R.string.MediaKeyboard__remove_pack),
|
||||
dismiss = stringResource(android.R.string.cancel),
|
||||
onConfirm = { onEvent(StickerPageScreenEvents.RemoveStickerPackConfirmed) },
|
||||
onDeny = { onEvent(StickerPageScreenEvents.RemoveStickerPackCanceled) },
|
||||
onDismissRequest = { onEvent(StickerPageScreenEvents.RemoveStickerPackCanceled) }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
+2
@@ -19,5 +19,7 @@ sealed interface StickerPageScreenEvents {
|
||||
data class ViewStickerPackClicked(val packId: String, val packKey: String) : StickerPageScreenEvents
|
||||
data class SendStickerPackClicked(val packId: String, val packKey: String) : StickerPageScreenEvents
|
||||
data class RemoveStickerPackClicked(val packId: String, val packKey: String) : StickerPageScreenEvents
|
||||
data object RemoveStickerPackConfirmed : StickerPageScreenEvents
|
||||
data object RemoveStickerPackCanceled : StickerPageScreenEvents
|
||||
data object ClearRecentStickersClicked : StickerPageScreenEvents
|
||||
}
|
||||
|
||||
+12
-1
@@ -11,5 +11,16 @@ data class StickerPageState(
|
||||
val packs: List<KeyboardStickerPack> = emptyList(),
|
||||
val selectedPackId: String? = null,
|
||||
val scrollTargetPackId: String? = null,
|
||||
val allowAnimation: Boolean = true
|
||||
val allowAnimation: Boolean = true,
|
||||
val confirmRemovePack: ConfirmRemovePack? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* The pack the user has asked to remove, held until they confirm or dismiss the prompt.
|
||||
*
|
||||
* @param packKey The pack's key, which identifies it alongside [packId] to whoever uninstalls it.
|
||||
*/
|
||||
data class ConfirmRemovePack(
|
||||
val packId: String,
|
||||
val packKey: String
|
||||
)
|
||||
|
||||
+14
-1
@@ -89,7 +89,20 @@ class StickerPageViewModel(
|
||||
}
|
||||
|
||||
is StickerPageScreenEvents.RemoveStickerPackClicked -> {
|
||||
onAction(MediaKeyboardAction.RemoveStickerPackClicked(event.packId, event.packKey))
|
||||
stateEmitter(state.copy(confirmRemovePack = ConfirmRemovePack(event.packId, event.packKey)))
|
||||
}
|
||||
|
||||
is StickerPageScreenEvents.RemoveStickerPackConfirmed -> {
|
||||
val pack = state.confirmRemovePack
|
||||
stateEmitter(state.copy(confirmRemovePack = null))
|
||||
|
||||
if (pack != null) {
|
||||
onAction(MediaKeyboardAction.RemoveStickerPackConfirmed(pack.packId, pack.packKey))
|
||||
}
|
||||
}
|
||||
|
||||
is StickerPageScreenEvents.RemoveStickerPackCanceled -> {
|
||||
stateEmitter(state.copy(confirmRemovePack = null))
|
||||
}
|
||||
|
||||
is StickerPageScreenEvents.ClearRecentStickersClicked -> {
|
||||
|
||||
@@ -63,6 +63,10 @@
|
||||
<string name="MediaKeyboard__more_options">More options</string>
|
||||
<!-- Menu option in a sticker pack header's menu that uninstalls the pack. Verb, imperative. -->
|
||||
<string name="MediaKeyboard__remove_pack">Remove</string>
|
||||
<!-- Title of the confirmation dialog shown before uninstalling a sticker pack. -->
|
||||
<string name="MediaKeyboard__remove_sticker_pack_question">Remove sticker pack?</string>
|
||||
<!-- Body of the confirmation dialog shown before uninstalling a sticker pack. -->
|
||||
<string name="MediaKeyboard__this_will_remove_the_sticker_pack">This will remove the sticker pack. You can\'t add it again without a link or sticker message.</string>
|
||||
<!-- Menu option in the recently used header's menu that forgets every recently used sticker. Verb phrase, imperative. -->
|
||||
<string name="MediaKeyboard__clear_recents">Clear recents</string>
|
||||
|
||||
|
||||
+34
@@ -6,7 +6,9 @@
|
||||
package org.signal.mediakeyboard.screens.sticker
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEmpty
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isNull
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
@@ -109,6 +111,38 @@ class StickerPageViewModelTest {
|
||||
assertThat(actions).isEqualTo(listOf(MediaKeyboardAction.ViewStickerPackClicked("pack-1", "pack-1-key")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `remove pack clicked - prompts instead of removing`() {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.onEvent(StickerPageScreenEvents.RemoveStickerPackClicked("pack-1", "pack-1-key"))
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
assertThat(viewModel.state.value.confirmRemovePack).isEqualTo(ConfirmRemovePack("pack-1", "pack-1-key"))
|
||||
assertThat(actions).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `remove pack confirmed - dismisses the prompt and hands the pack's ids to the host`() {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.onEvent(StickerPageScreenEvents.RemoveStickerPackClicked("pack-1", "pack-1-key"))
|
||||
viewModel.onEvent(StickerPageScreenEvents.RemoveStickerPackConfirmed)
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
assertThat(viewModel.state.value.confirmRemovePack).isNull()
|
||||
assertThat(actions).isEqualTo(listOf(MediaKeyboardAction.RemoveStickerPackConfirmed("pack-1", "pack-1-key")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `remove pack canceled - dismisses the prompt without removing`() {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.onEvent(StickerPageScreenEvents.RemoveStickerPackClicked("pack-1", "pack-1-key"))
|
||||
viewModel.onEvent(StickerPageScreenEvents.RemoveStickerPackCanceled)
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
assertThat(viewModel.state.value.confirmRemovePack).isNull()
|
||||
assertThat(actions).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `search clicked - hands off to the host`() {
|
||||
val viewModel = createViewModel()
|
||||
|
||||
Reference in New Issue
Block a user