From c64be82710c3be0785e36febd95d38aa2cdcbdd5 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 29 Jun 2022 09:57:06 -0300 Subject: [PATCH] Add context menus to story contacts in contact selection. --- .../ContactSelectionListFragment.java | 1 - .../components/WrapperDialogFragment.kt | 59 ++++++++++++++++++ .../securesms/components/menu/ActionItem.kt | 7 ++- .../components/menu/ContextMenuList.kt | 5 ++ .../contacts/paged/ContactSearchItems.kt | 61 ++++++++++++++++++- .../contacts/paged/ContactSearchMediator.kt | 43 ++++++++++++- .../contacts/paged/ContactSearchRepository.kt | 18 ++++++ .../contacts/paged/ContactSearchViewModel.kt | 26 ++++++++ .../forward/MultiselectForwardFragment.kt | 8 ++- .../securesms/database/GroupDatabase.java | 6 +- .../v2/text/send/TextStoryPostSendFragment.kt | 7 ++- .../custom/PrivateStorySettingsFragment.kt | 23 +++++++ .../settings/my/MyStorySettingsFragment.kt | 21 +++++++ .../res/layout/signal_context_menu_item.xml | 2 - app/src/main/res/values/strings.xml | 18 ++++++ 15 files changed, 294 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/WrapperDialogFragment.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java index 6dd968cc26..787d8d5afd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ContactSelectionListFragment.java @@ -96,7 +96,6 @@ import java.util.Optional; import java.util.Set; import java.util.function.Consumer; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.disposables.Disposable; import kotlin.Unit; diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/WrapperDialogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/WrapperDialogFragment.kt new file mode 100644 index 0000000000..92ab7b0600 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/WrapperDialogFragment.kt @@ -0,0 +1,59 @@ +package org.thoughtcrime.securesms.components + +import android.content.DialogInterface +import android.os.Bundle +import android.view.View +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.Fragment +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.util.fragments.findListener + +/** + * Convenience class for wrapping Fragments in full-screen dialogs. Due to how fragments work, they + * must be public static classes. Therefore, this class should be subclassed as its own entity, rather + * than via `object : WrapperDialogFragment`. + * + * Example usage: + * + * ``` + * class Dialog : WrapperDialogFragment() { + * override fun getWrappedFragment(): Fragment { + * return NavHostFragment.create(R.navigation.private_story_settings, requireArguments()) + * } + * } + * + * companion object { + * fun createAsDialog(distributionListId: DistributionListId): DialogFragment { + * return Dialog().apply { + * arguments = PrivateStorySettingsFragmentArgs.Builder(distributionListId).build().toBundle() + * } + * } + * } + * ``` + */ +abstract class WrapperDialogFragment : DialogFragment(R.layout.fragment_container) { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setStyle(STYLE_NO_FRAME, R.style.Signal_DayNight_Dialog_FullScreen) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + if (savedInstanceState == null) { + childFragmentManager.beginTransaction() + .replace(R.id.fragment_container, getWrappedFragment()) + .commitAllowingStateLoss() + } + } + + override fun onDismiss(dialog: DialogInterface) { + findListener()?.onWrapperDialogFragmentDismissed() + } + + abstract fun getWrappedFragment(): Fragment + + interface WrapperDialogFragmentCallback { + fun onWrapperDialogFragmentDismissed() + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/menu/ActionItem.kt b/app/src/main/java/org/thoughtcrime/securesms/components/menu/ActionItem.kt index c13d141590..2b7bdfe951 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/menu/ActionItem.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/menu/ActionItem.kt @@ -1,12 +1,15 @@ package org.thoughtcrime.securesms.components.menu +import androidx.annotation.ColorRes import androidx.annotation.DrawableRes +import org.thoughtcrime.securesms.R /** * Represents an action to be rendered via [SignalContextMenu] or [SignalBottomActionBar] */ -data class ActionItem( +data class ActionItem @JvmOverloads constructor( @DrawableRes val iconRes: Int, val title: CharSequence, - val action: Runnable + @ColorRes val tintRes: Int = R.color.signal_colorOnSurface, + val action: Runnable, ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/menu/ContextMenuList.kt b/app/src/main/java/org/thoughtcrime/securesms/components/menu/ContextMenuList.kt index 44d94571cc..49a5ae3035 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/menu/ContextMenuList.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/menu/ContextMenuList.kt @@ -4,6 +4,7 @@ import android.os.Build import android.view.View import android.widget.ImageView import android.widget.TextView +import androidx.core.content.ContextCompat import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import org.thoughtcrime.securesms.R @@ -78,6 +79,10 @@ class ContextMenuList(recyclerView: RecyclerView, onItemClick: () -> Unit) { onItemClick() } + val tintColor = ContextCompat.getColor(context, model.item.tintRes) + icon.setColorFilter(tintColor) + title.setTextColor(tintColor) + if (Build.VERSION.SDK_INT >= 21) { when (model.displayType) { DisplayType.TOP -> itemView.setBackgroundResource(R.drawable.signal_context_menu_item_background_top) diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchItems.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchItems.kt index e19cab9a43..68d8918aab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchItems.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchItems.kt @@ -1,12 +1,15 @@ package org.thoughtcrime.securesms.contacts.paged import android.view.View +import android.view.ViewGroup import android.widget.CheckBox import android.widget.TextView import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.badges.BadgeImageView import org.thoughtcrime.securesms.components.AvatarImageView import org.thoughtcrime.securesms.components.FromTextView +import org.thoughtcrime.securesms.components.menu.ActionItem +import org.thoughtcrime.securesms.components.menu.SignalContextMenu import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter @@ -27,11 +30,12 @@ object ContactSearchItems { displayCheckBox: Boolean, recipientListener: RecipientClickListener, storyListener: StoryClickListener, + storyContextMenuCallbacks: StoryContextMenuCallbacks, expandListener: (ContactSearchData.Expand) -> Unit ) { mappingAdapter.registerFactory( StoryModel::class.java, - LayoutFactory({ StoryViewHolder(it, displayCheckBox, storyListener) }, R.layout.contact_search_item) + LayoutFactory({ StoryViewHolder(it, displayCheckBox, storyListener, storyContextMenuCallbacks) }, R.layout.contact_search_item) ) mappingAdapter.registerFactory( RecipientModel::class.java, @@ -82,7 +86,7 @@ object ContactSearchItems { } } - private class StoryViewHolder(itemView: View, displayCheckBox: Boolean, onClick: StoryClickListener) : BaseRecipientViewHolder(itemView, displayCheckBox, onClick) { + private class StoryViewHolder(itemView: View, displayCheckBox: Boolean, onClick: StoryClickListener, private val storyContextMenuCallbacks: StoryContextMenuCallbacks) : BaseRecipientViewHolder(itemView, displayCheckBox, onClick) { override fun isSelected(model: StoryModel): Boolean = model.isSelected override fun getData(model: StoryModel): ContactSearchData.Story = model.story override fun getRecipient(model: StoryModel): Recipient = model.story.recipient @@ -104,6 +108,50 @@ object ContactSearchItems { number.text = context.resources.getQuantityString(pluralId, count, count) } + + override fun bindLongPress(model: StoryModel) { + itemView.setOnLongClickListener { + val actions: List = when { + model.story.recipient.isMyStory -> getMyStoryContextMenuActions(model) + model.story.recipient.isGroup -> getGroupStoryContextMenuActions(model) + model.story.recipient.isDistributionList -> getPrivateStoryContextMenuActions(model) + else -> error("Unsupported story target. Not a group or distribution list.") + } + + SignalContextMenu.Builder(itemView, itemView.rootView as ViewGroup) + .offsetX(context.resources.getDimensionPixelSize(R.dimen.dsl_settings_gutter)) + .show(actions) + + true + } + } + + private fun getMyStoryContextMenuActions(model: StoryModel): List { + return listOf( + ActionItem(R.drawable.ic_settings_24, context.getString(R.string.ContactSearchItems__story_settings)) { + storyContextMenuCallbacks.onOpenStorySettings(model.story) + } + ) + } + + private fun getGroupStoryContextMenuActions(model: StoryModel): List { + return listOf( + ActionItem(R.drawable.ic_minus_circle_20, context.getString(R.string.ContactSearchItems__remove_story)) { + storyContextMenuCallbacks.onRemoveGroupStory(model.story, model.isSelected) + } + ) + } + + private fun getPrivateStoryContextMenuActions(model: StoryModel): List { + return listOf( + ActionItem(R.drawable.ic_settings_24, context.getString(R.string.ContactSearchItems__story_settings)) { + storyContextMenuCallbacks.onOpenStorySettings(model.story) + }, + ActionItem(R.drawable.ic_delete_24, context.getString(R.string.ContactSearchItems__delete_story), R.color.signal_colorError) { + storyContextMenuCallbacks.onDeletePrivateStory(model.story, model.isSelected) + } + ) + } } /** @@ -151,6 +199,7 @@ object ContactSearchItems { checkbox.visible = displayCheckBox checkbox.isChecked = isSelected(model) itemView.setOnClickListener { onClick(itemView, getData(model), isSelected(model)) } + bindLongPress(model) if (payload.isNotEmpty()) { return @@ -188,6 +237,8 @@ object ContactSearchItems { smsTag.visible = isSmsContact(model) } + protected open fun bindLongPress(model: T) = Unit + private fun isSmsContact(model: T): Boolean { return (getRecipient(model).isForceSmsSelection || getRecipient(model).isUnregistered) && !getRecipient(model).isDistributionList } @@ -271,4 +322,10 @@ object ContactSearchItems { return if (isLeftSelf == isRightSelf) 0 else if (isLeftSelf) 1 else -1 } } + + interface StoryContextMenuCallbacks { + fun onOpenStorySettings(story: ContactSearchData.Story) + fun onRemoveGroupStory(story: ContactSearchData.Story, isSelected: Boolean) + fun onDeletePrivateStory(story: ContactSearchData.Story, isSelected: Boolean) + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchMediator.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchMediator.kt index a3e5d09345..cfa1790722 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchMediator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchMediator.kt @@ -1,16 +1,22 @@ package org.thoughtcrime.securesms.contacts.paged import android.view.View +import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.groups.SelectionLimits +import org.thoughtcrime.securesms.stories.settings.custom.PrivateStorySettingsFragment +import org.thoughtcrime.securesms.stories.settings.my.MyStorySettingsFragment +import org.thoughtcrime.securesms.util.SpanUtil import org.thoughtcrime.securesms.util.adapter.mapping.PagingMappingAdapter import org.thoughtcrime.securesms.util.livedata.LiveDataUtil class ContactSearchMediator( - fragment: Fragment, + private val fragment: Fragment, recyclerView: RecyclerView, selectionLimits: SelectionLimits, displayCheckBox: Boolean, @@ -30,6 +36,7 @@ class ContactSearchMediator( displayCheckBox = displayCheckBox, recipientListener = this::toggleSelection, storyListener = this::toggleSelection, + storyContextMenuCallbacks = StoryContextMenuCallbacks(), expandListener = { viewModel.expandSection(it.sectionKey) } ) @@ -76,6 +83,10 @@ class ContactSearchMediator( viewModel.addToVisibleGroupStories(groupStories) } + fun refresh() { + viewModel.refresh() + } + private fun toggleSelection(view: View, contactSearchData: ContactSearchData, isSelected: Boolean) { return if (isSelected) { viewModel.setKeysNotSelected(setOf(contactSearchData.contactSearchKey)) @@ -83,4 +94,34 @@ class ContactSearchMediator( viewModel.setKeysSelected(contactSelectionPreFilter(view, setOf(contactSearchData.contactSearchKey))) } } + + private inner class StoryContextMenuCallbacks : ContactSearchItems.StoryContextMenuCallbacks { + override fun onOpenStorySettings(story: ContactSearchData.Story) { + if (story.recipient.isMyStory) { + MyStorySettingsFragment.createAsDialog() + .show(fragment.childFragmentManager, null) + } else { + PrivateStorySettingsFragment.createAsDialog(story.recipient.requireDistributionListId()) + .show(fragment.childFragmentManager, null) + } + } + + override fun onRemoveGroupStory(story: ContactSearchData.Story, isSelected: Boolean) { + MaterialAlertDialogBuilder(fragment.requireContext()) + .setTitle(R.string.ContactSearchMediator__remove_group_story) + .setMessage(R.string.ContactSearchMediator__this_will_remove) + .setPositiveButton(R.string.ContactSearchMediator__remove) { _, _ -> viewModel.removeGroupStory(story) } + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .show() + } + + override fun onDeletePrivateStory(story: ContactSearchData.Story, isSelected: Boolean) { + MaterialAlertDialogBuilder(fragment.requireContext()) + .setTitle(R.string.ContactSearchMediator__delete_story) + .setMessage(fragment.getString(R.string.ContactSearchMediator__delete_the_private, story.recipient.getDisplayName(fragment.requireContext()))) + .setPositiveButton(SpanUtil.color(ContextCompat.getColor(fragment.requireContext(), R.color.signal_colorError), fragment.getString(R.string.ContactSearchMediator__delete))) { _, _ -> viewModel.deletePrivateStory(story) } + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .show() + } + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchRepository.kt index c40acd96a2..6a1c3a0cd3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchRepository.kt @@ -1,9 +1,14 @@ package org.thoughtcrime.securesms.contacts.paged +import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Single +import io.reactivex.rxjava3.schedulers.Schedulers import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.model.DistributionListId +import org.thoughtcrime.securesms.groups.GroupId import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId +import org.thoughtcrime.securesms.stories.Stories class ContactSearchRepository { fun filterOutUnselectableContactSearchKeys(contactSearchKeys: Set): Single> { @@ -29,4 +34,17 @@ class ContactSearchRepository { true } } + + fun unmarkDisplayAsStory(groupId: GroupId): Completable { + return Completable.fromAction { + SignalDatabase.groups.markDisplayAsStory(groupId, false) + }.subscribeOn(Schedulers.io()) + } + + fun deletePrivateStory(distributionListId: DistributionListId): Completable { + return Completable.fromAction { + SignalDatabase.distributionLists.deleteList(distributionListId) + Stories.onStorySettingsChanged(distributionListId) + }.subscribeOn(Schedulers.io()) + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel.kt index 164a01958f..6ee6d5b62c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/paged/ContactSearchViewModel.kt @@ -14,6 +14,7 @@ import org.signal.paging.PagingController import org.thoughtcrime.securesms.groups.SelectionLimits import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.util.livedata.Store +import org.whispersystems.signalservice.api.util.Preconditions /** * Simple, reusable view model that manages a ContactSearchPagedDataSource as well as filter and expansion state. @@ -97,6 +98,31 @@ class ContactSearchViewModel( } } + fun removeGroupStory(story: ContactSearchData.Story) { + Preconditions.checkArgument(story.recipient.isGroup) + setKeysNotSelected(setOf(story.contactSearchKey)) + disposables += contactSearchRepository.unmarkDisplayAsStory(story.recipient.requireGroupId()).subscribe { + configurationStore.update { state -> + state.copy( + groupStories = state.groupStories.filter { it.recipient.id == story.recipient.id }.toSet() + ) + } + refresh() + } + } + + fun deletePrivateStory(story: ContactSearchData.Story) { + Preconditions.checkArgument(story.recipient.isDistributionList && !story.recipient.isMyStory) + setKeysNotSelected(setOf(story.contactSearchKey)) + disposables += contactSearchRepository.deletePrivateStory(story.recipient.requireDistributionListId()).subscribe { + refresh() + } + } + + fun refresh() { + controller.value?.onDataInvalidated() + } + class Factory(private val selectionLimits: SelectionLimits, private val repository: ContactSearchRepository) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { return modelClass.cast(ContactSearchViewModel(selectionLimits, repository)) as T diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragment.kt index efd5deb521..db15f6fa77 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/mutiselect/forward/MultiselectForwardFragment.kt @@ -29,6 +29,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.ContactFilterView import org.thoughtcrime.securesms.components.TooltipPopup +import org.thoughtcrime.securesms.components.WrapperDialogFragment import org.thoughtcrime.securesms.contacts.paged.ContactSearchConfiguration import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey import org.thoughtcrime.securesms.contacts.paged.ContactSearchMediator @@ -75,7 +76,8 @@ import org.thoughtcrime.securesms.util.visible class MultiselectForwardFragment : Fragment(R.layout.multiselect_forward_fragment), SafetyNumberChangeDialog.Callback, - ChooseStoryTypeBottomSheet.Callback { + ChooseStoryTypeBottomSheet.Callback, + WrapperDialogFragment.WrapperDialogFragmentCallback { private val viewModel: MultiselectForwardViewModel by viewModels(factoryProducer = this::createViewModelFactory) private val disposables = LifecycleDisposable() @@ -542,4 +544,8 @@ class MultiselectForwardFragment : } } } + + override fun onWrapperDialogFragmentDismissed() { + contactSearchMediator.refresh() + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java index 7ea4d7e859..4ace704874 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java @@ -1418,8 +1418,12 @@ public class GroupDatabase extends Database { } public void markDisplayAsStory(@NonNull GroupId groupId) { + markDisplayAsStory(groupId, true); + } + + public void markDisplayAsStory(@NonNull GroupId groupId, boolean displayAsStory) { ContentValues contentValues = new ContentValues(1); - contentValues.put(DISPLAY_AS_STORY, true); + contentValues.put(DISPLAY_AS_STORY, displayAsStory); getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?", SqlUtil.buildArgs(groupId.toString())); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/text/send/TextStoryPostSendFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/text/send/TextStoryPostSendFragment.kt index 04c8b8ad12..a29e9af187 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/text/send/TextStoryPostSendFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediasend/v2/text/send/TextStoryPostSendFragment.kt @@ -14,6 +14,7 @@ import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.RecyclerView import org.signal.core.util.DimensionUnit import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.WrapperDialogFragment import org.thoughtcrime.securesms.contacts.paged.ContactSearchConfiguration import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey import org.thoughtcrime.securesms.contacts.paged.ContactSearchMediator @@ -35,7 +36,7 @@ import org.thoughtcrime.securesms.util.FeatureFlags import org.thoughtcrime.securesms.util.LifecycleDisposable import org.thoughtcrime.securesms.util.livedata.LiveDataUtil -class TextStoryPostSendFragment : Fragment(R.layout.stories_send_text_post_fragment), ChooseStoryTypeBottomSheet.Callback { +class TextStoryPostSendFragment : Fragment(R.layout.stories_send_text_post_fragment), ChooseStoryTypeBottomSheet.Callback, WrapperDialogFragment.WrapperDialogFragmentCallback { private lateinit var shareListWrapper: View private lateinit var shareSelectionRecyclerView: RecyclerView @@ -195,4 +196,8 @@ class TextStoryPostSendFragment : Fragment(R.layout.stories_send_text_post_fragm override fun onGroupStoryClicked() { ChooseGroupStoryBottomSheet().show(parentFragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) } + + override fun onWrapperDialogFragmentDismissed() { + contactSearchMediator.refresh() + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/custom/PrivateStorySettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/custom/PrivateStorySettingsFragment.kt index 22e0605bfe..bc2668eaad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/custom/PrivateStorySettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/custom/PrivateStorySettingsFragment.kt @@ -3,10 +3,14 @@ package org.thoughtcrime.securesms.stories.settings.custom import android.view.MenuItem import androidx.appcompat.widget.Toolbar import androidx.core.content.ContextCompat +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels +import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.WrapperDialogFragment import org.thoughtcrime.securesms.components.settings.DSLConfiguration import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment @@ -16,6 +20,7 @@ import org.thoughtcrime.securesms.database.model.DistributionListId import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.stories.settings.story.PrivateStoryItem import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory +import org.thoughtcrime.securesms.util.fragments.findListener import org.thoughtcrime.securesms.util.navigation.safeNavigate import org.thoughtcrime.securesms.util.viewholders.RecipientMappingModel import org.thoughtcrime.securesms.util.viewholders.RecipientViewHolder @@ -118,9 +123,27 @@ class PrivateStorySettingsFragment : DSLSettingsFragment( .show() } + override fun onToolbarNavigationClicked() { + findListener()?.dismiss() ?: super.onToolbarNavigationClicked() + } + inner class RecipientEventListener : RecipientViewHolder.EventListener { override fun onClick(recipient: Recipient) { handleRemoveRecipient(recipient) } } + + class Dialog : WrapperDialogFragment() { + override fun getWrappedFragment(): Fragment { + return NavHostFragment.create(R.navigation.private_story_settings, requireArguments()) + } + } + + companion object { + fun createAsDialog(distributionListId: DistributionListId): DialogFragment { + return Dialog().apply { + arguments = PrivateStorySettingsFragmentArgs.Builder(distributionListId).build().toBundle() + } + } + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsFragment.kt index f88387e76f..de3e7c4f71 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/my/MyStorySettingsFragment.kt @@ -2,9 +2,13 @@ package org.thoughtcrime.securesms.stories.settings.my import android.os.Bundle import android.view.View +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels +import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.WrapperDialogFragment import org.thoughtcrime.securesms.components.settings.DSLConfiguration import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment @@ -12,6 +16,7 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsText import org.thoughtcrime.securesms.components.settings.configure import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode import org.thoughtcrime.securesms.util.LifecycleDisposable +import org.thoughtcrime.securesms.util.fragments.findListener import org.thoughtcrime.securesms.util.navigation.safeNavigate class MyStorySettingsFragment : DSLSettingsFragment( @@ -105,4 +110,20 @@ class MyStorySettingsFragment : DSLSettingsFragment( ) } } + + override fun onToolbarNavigationClicked() { + findListener()?.dismiss() ?: super.onToolbarNavigationClicked() + } + + class Dialog : WrapperDialogFragment() { + override fun getWrappedFragment(): Fragment { + return NavHostFragment.create(R.navigation.my_story_settings) + } + } + + companion object { + fun createAsDialog(): DialogFragment { + return Dialog() + } + } } diff --git a/app/src/main/res/layout/signal_context_menu_item.xml b/app/src/main/res/layout/signal_context_menu_item.xml index 8869566608..19187f67cd 100644 --- a/app/src/main/res/layout/signal_context_menu_item.xml +++ b/app/src/main/res/layout/signal_context_menu_item.xml @@ -17,7 +17,6 @@ android:layout_width="24dp" android:layout_height="24dp" android:importantForAccessibility="no" - app:tint="@color/signal_colorOnSurface" tools:src="@drawable/ic_archive_24dp" /> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 71040bedc7..ec5ab78739 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4886,6 +4886,24 @@ Group Story · %1$d viewer Group Story · %1$d viewers + + Story settings + + Remove story + + Delete story + + Remove group story? + + This will remove the story from this list. You will still be able to view stories from this group. + + Remove + + Delete story? + + Delete the private story \"%1$s\"? + + Delete %1$d days remaining