From 2fad52abdc51c0c21e02126a99c9f2bf08462e17 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Thu, 6 Nov 2025 13:28:29 -0400 Subject: [PATCH] Convert InternalStoryDialogLauncherFragment to Compose. --- .../InternalStoryDialogLauncherFragment.kt | 255 +++++++++++++----- 1 file changed, 182 insertions(+), 73 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalStoryDialogLauncherFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalStoryDialogLauncherFragment.kt index 591ae1bfb9..41d66a2356 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalStoryDialogLauncherFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalStoryDialogLauncherFragment.kt @@ -1,85 +1,194 @@ package org.thoughtcrime.securesms.components.settings.app.internal import android.widget.Toast +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import org.signal.core.ui.compose.DayNightPreviews +import org.signal.core.ui.compose.Previews +import org.signal.core.ui.compose.Rows +import org.signal.core.ui.compose.Scaffolds +import org.signal.core.ui.compose.theme.SignalTheme import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.components.settings.DSLConfiguration -import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment -import org.thoughtcrime.securesms.components.settings.DSLSettingsText -import org.thoughtcrime.securesms.components.settings.configure +import org.thoughtcrime.securesms.compose.ComposeFragment import org.thoughtcrime.securesms.stories.dialogs.StoryDialogs -import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter +import org.thoughtcrime.securesms.util.DynamicTheme -class InternalStoryDialogLauncherFragment : DSLSettingsFragment(titleId = R.string.preferences__internal_stories_dialog_launcher) { - override fun bindAdapter(adapter: MappingAdapter) { - adapter.submitList(getConfiguration().toMappingModelList()) - } +/** + * Internal tool for testing various story-related dialogs. + */ +class InternalStoryDialogLauncherFragment : ComposeFragment() { - private fun getConfiguration(): DSLConfiguration { - return configure { - clickPref( - title = DSLSettingsText.from("Remove group story"), - onClick = { - StoryDialogs.removeGroupStory(requireContext(), "Family") { - Toast.makeText(requireContext(), "Remove group story", Toast.LENGTH_SHORT).show() - } - } - ) + @Composable + override fun FragmentContent() { + val callback = remember { DefaultInternalStoryDialogLauncherCallback() } - clickPref( - title = DSLSettingsText.from("Retry send"), - onClick = { - StoryDialogs.resendStory(requireContext()) { - Toast.makeText(requireContext(), "Retry send", Toast.LENGTH_SHORT).show() - } - } - ) - - clickPref( - title = DSLSettingsText.from("Story or profile selector"), - onClick = { - StoryDialogs.displayStoryOrProfileImage( - context = requireContext(), - onViewStory = { Toast.makeText(requireContext(), R.string.StoryDialogs__view_story, Toast.LENGTH_SHORT).show() }, - onViewAvatar = { Toast.makeText(requireContext(), R.string.StoryDialogs__view_profile_photo, Toast.LENGTH_SHORT).show() } - ) - } - ) - - clickPref( - title = DSLSettingsText.from("Hide story"), - onClick = { - StoryDialogs.hideStory(requireContext(), "Spiderman") { - Toast.makeText(requireContext(), "Hide story", Toast.LENGTH_SHORT).show() - } - } - ) - - clickPref( - title = DSLSettingsText.from("Turn off stories"), - onClick = { - StoryDialogs.disableStories(requireContext(), false) { - Toast.makeText(requireContext(), "Turn off stories", Toast.LENGTH_SHORT).show() - } - } - ) - - clickPref( - title = DSLSettingsText.from("Turn off stories (with stories on disk)"), - onClick = { - StoryDialogs.disableStories(requireContext(), true) { - Toast.makeText(requireContext(), "Turn off stories (with stories on disk)", Toast.LENGTH_SHORT).show() - } - } - ) - - clickPref( - title = DSLSettingsText.from("Delete custom story"), - onClick = { - StoryDialogs.deleteDistributionList(requireContext(), "Family") { - Toast.makeText(requireContext(), "Delete custom story", Toast.LENGTH_SHORT).show() - } - } + SignalTheme(isDarkMode = DynamicTheme.isDarkTheme(LocalContext.current)) { + InternalStoryDialogLauncherScreen( + callback = callback ) } } + + /** + * Default callback implementation that launches story dialogs and displays toast notifications. + */ + inner class DefaultInternalStoryDialogLauncherCallback : InternalStoryDialogLauncherScreenCallback { + override fun onNavigationClick() { + requireActivity().onBackPressedDispatcher.onBackPressed() + } + + override fun onRemoveGroupStoryClick() { + StoryDialogs.removeGroupStory(requireContext(), "Family") { + Toast.makeText(requireContext(), "Remove group story", Toast.LENGTH_SHORT).show() + } + } + + override fun onRetrySendClick() { + StoryDialogs.resendStory(requireContext()) { + Toast.makeText(requireContext(), "Retry send", Toast.LENGTH_SHORT).show() + } + } + + override fun onStoryOrProfileSelectorClick() { + StoryDialogs.displayStoryOrProfileImage( + context = requireContext(), + onViewStory = { Toast.makeText(requireContext(), R.string.StoryDialogs__view_story, Toast.LENGTH_SHORT).show() }, + onViewAvatar = { Toast.makeText(requireContext(), R.string.StoryDialogs__view_profile_photo, Toast.LENGTH_SHORT).show() } + ) + } + + override fun onHideStoryClick() { + StoryDialogs.hideStory(requireContext(), "Spiderman") { + Toast.makeText(requireContext(), "Hide story", Toast.LENGTH_SHORT).show() + } + } + + override fun onTurnOffStoriesClick() { + StoryDialogs.disableStories(requireContext(), false) { + Toast.makeText(requireContext(), "Turn off stories", Toast.LENGTH_SHORT).show() + } + } + + override fun onTurnOffStoriesWithStoriesOnDiskClick() { + StoryDialogs.disableStories(requireContext(), true) { + Toast.makeText(requireContext(), "Turn off stories (with stories on disk)", Toast.LENGTH_SHORT).show() + } + } + + override fun onDeleteCustomStoryClick() { + StoryDialogs.deleteDistributionList(requireContext(), "Family") { + Toast.makeText(requireContext(), "Delete custom story", Toast.LENGTH_SHORT).show() + } + } + } +} + +/** + * Screen displaying a list of story dialogs available for testing. + */ +@Composable +fun InternalStoryDialogLauncherScreen( + callback: InternalStoryDialogLauncherScreenCallback +) { + Scaffolds.Settings( + title = stringResource(R.string.preferences__internal_stories_dialog_launcher), + onNavigationClick = callback::onNavigationClick, + navigationIcon = ImageVector.vectorResource(R.drawable.symbol_arrow_start_24) + ) { paddingValues -> + LazyColumn( + modifier = Modifier.padding(paddingValues) + ) { + item { + Rows.TextRow( + text = "Remove group story", + modifier = Modifier.clickable(onClick = callback::onRemoveGroupStoryClick) + ) + } + + item { + Rows.TextRow( + text = "Retry send", + modifier = Modifier.clickable(onClick = callback::onRetrySendClick) + ) + } + + item { + Rows.TextRow( + text = "Story or profile selector", + modifier = Modifier.clickable(onClick = callback::onStoryOrProfileSelectorClick) + ) + } + + item { + Rows.TextRow( + text = "Hide story", + modifier = Modifier.clickable(onClick = callback::onHideStoryClick) + ) + } + + item { + Rows.TextRow( + text = "Turn off stories", + modifier = Modifier.clickable(onClick = callback::onTurnOffStoriesClick) + ) + } + + item { + Rows.TextRow( + text = "Turn off stories (with stories on disk)", + modifier = Modifier.clickable(onClick = callback::onTurnOffStoriesWithStoriesOnDiskClick) + ) + } + + item { + Rows.TextRow( + text = "Delete custom story", + modifier = Modifier.clickable(onClick = callback::onDeleteCustomStoryClick) + ) + } + } + } +} + +/** + * Callback interface for [InternalStoryDialogLauncherScreen] interactions. + */ +interface InternalStoryDialogLauncherScreenCallback { + fun onNavigationClick() + fun onRemoveGroupStoryClick() + fun onRetrySendClick() + fun onStoryOrProfileSelectorClick() + fun onHideStoryClick() + fun onTurnOffStoriesClick() + fun onTurnOffStoriesWithStoriesOnDiskClick() + fun onDeleteCustomStoryClick() + + object Empty : InternalStoryDialogLauncherScreenCallback { + override fun onNavigationClick() = Unit + override fun onRemoveGroupStoryClick() = Unit + override fun onRetrySendClick() = Unit + override fun onStoryOrProfileSelectorClick() = Unit + override fun onHideStoryClick() = Unit + override fun onTurnOffStoriesClick() = Unit + override fun onTurnOffStoriesWithStoriesOnDiskClick() = Unit + override fun onDeleteCustomStoryClick() = Unit + } +} + +@DayNightPreviews +@Composable +private fun InternalStoryDialogLauncherScreenPreview() { + Previews.Preview { + InternalStoryDialogLauncherScreen( + callback = InternalStoryDialogLauncherScreenCallback.Empty + ) + } }