Add first time My Story privacy configuration.

This commit is contained in:
Cody Henthorne
2022-07-05 11:58:02 -04:00
committed by Alex Hart
parent 3eac397263
commit 78d4d9a3dd
15 changed files with 485 additions and 140 deletions

View File

@@ -10,6 +10,7 @@ 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.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
@@ -55,7 +56,7 @@ object ContactSearchItems {
return MappingModelList(
contactSearchData.filterNotNull().map {
when (it) {
is ContactSearchData.Story -> StoryModel(it, selection.contains(it.contactSearchKey))
is ContactSearchData.Story -> StoryModel(it, selection.contains(it.contactSearchKey), SignalStore.storyValues().userHasBeenNotifiedAboutStories)
is ContactSearchData.KnownRecipient -> RecipientModel(it, selection.contains(it.contactSearchKey))
is ContactSearchData.Expand -> ExpandModel(it)
is ContactSearchData.Header -> HeaderModel(it)
@@ -67,18 +68,23 @@ object ContactSearchItems {
/**
* Story Model
*/
private class StoryModel(val story: ContactSearchData.Story, val isSelected: Boolean) : MappingModel<StoryModel> {
private class StoryModel(val story: ContactSearchData.Story, val isSelected: Boolean, val hasBeenNotified: Boolean) : MappingModel<StoryModel> {
override fun areItemsTheSame(newItem: StoryModel): Boolean {
return newItem.story == story
}
override fun areContentsTheSame(newItem: StoryModel): Boolean {
return story.recipient.hasSameContent(newItem.story.recipient) && isSelected == newItem.isSelected
return story.recipient.hasSameContent(newItem.story.recipient) &&
isSelected == newItem.isSelected &&
hasBeenNotified == newItem.hasBeenNotified
}
override fun getChangePayload(newItem: StoryModel): Any? {
return if (story.recipient.hasSameContent(newItem.story.recipient) && newItem.isSelected != isSelected) {
return if (story.recipient.hasSameContent(newItem.story.recipient) &&
hasBeenNotified == newItem.hasBeenNotified &&
newItem.isSelected != isSelected
) {
0
} else {
null
@@ -100,13 +106,17 @@ object ContactSearchItems {
model.story.viewerCount
}
val pluralId = when {
model.story.recipient.isGroup -> R.plurals.ContactSearchItems__group_story_d_viewers
model.story.recipient.isMyStory -> R.plurals.SelectViewersFragment__d_viewers
else -> R.plurals.ContactSearchItems__private_story_d_viewers
}
if (model.story.recipient.isMyStory && !model.hasBeenNotified) {
number.setText(R.string.ContactSearchItems__tap_to_choose_your_viewers)
} else {
val pluralId = when {
model.story.recipient.isGroup -> R.plurals.ContactSearchItems__group_story_d_viewers
model.story.recipient.isMyStory -> R.plurals.SelectViewersFragment__d_viewers
else -> R.plurals.ContactSearchItems__private_story_d_viewers
}
number.text = context.resources.getQuantityString(pluralId, count, count)
number.text = context.resources.getQuantityString(pluralId, count, count)
}
}
override fun bindLongPress(model: StoryModel) {

View File

@@ -9,8 +9,10 @@ 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.keyvalue.SignalStore
import org.thoughtcrime.securesms.stories.settings.custom.PrivateStorySettingsFragment
import org.thoughtcrime.securesms.stories.settings.my.MyStorySettingsFragment
import org.thoughtcrime.securesms.stories.settings.privacy.ChooseInitialMyStoryMembershipBottomSheetDialogFragment
import org.thoughtcrime.securesms.util.SpanUtil
import org.thoughtcrime.securesms.util.adapter.mapping.PagingMappingAdapter
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil
@@ -35,7 +37,7 @@ class ContactSearchMediator(
mappingAdapter = adapter,
displayCheckBox = displayCheckBox,
recipientListener = this::toggleSelection,
storyListener = this::toggleSelection,
storyListener = this::toggleStorySelection,
storyContextMenuCallbacks = StoryContextMenuCallbacks(),
expandListener = { viewModel.expandSection(it.sectionKey) }
)
@@ -87,6 +89,14 @@ class ContactSearchMediator(
viewModel.refresh()
}
private fun toggleStorySelection(view: View, contactSearchData: ContactSearchData.Story, isSelected: Boolean) {
if (contactSearchData.recipient.isMyStory && !SignalStore.storyValues().userHasBeenNotifiedAboutStories) {
ChooseInitialMyStoryMembershipBottomSheetDialogFragment.show(fragment.childFragmentManager)
} else {
toggleSelection(view, contactSearchData, isSelected)
}
}
private fun toggleSelection(view: View, contactSearchData: ContactSearchData, isSelected: Boolean) {
return if (isSelected) {
viewModel.setKeysNotSelected(setOf(contactSearchData.contactSearchKey))

View File

@@ -45,10 +45,9 @@ import org.thoughtcrime.securesms.sharing.ShareSelectionAdapter
import org.thoughtcrime.securesms.sharing.ShareSelectionMappingModel
import org.thoughtcrime.securesms.stories.Stories
import org.thoughtcrime.securesms.stories.Stories.getHeaderAction
import org.thoughtcrime.securesms.stories.dialogs.StoryDialogs
import org.thoughtcrime.securesms.stories.settings.create.CreateStoryFlowDialogFragment
import org.thoughtcrime.securesms.stories.settings.create.CreateStoryWithViewersFragment
import org.thoughtcrime.securesms.stories.settings.privacy.HideStoryFromDialogFragment
import org.thoughtcrime.securesms.stories.settings.privacy.ChooseInitialMyStoryMembershipBottomSheetDialogFragment
import org.thoughtcrime.securesms.util.BottomSheetUtil
import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.FullscreenHelper
@@ -77,7 +76,8 @@ class MultiselectForwardFragment :
Fragment(R.layout.multiselect_forward_fragment),
SafetyNumberChangeDialog.Callback,
ChooseStoryTypeBottomSheet.Callback,
WrapperDialogFragment.WrapperDialogFragmentCallback {
WrapperDialogFragment.WrapperDialogFragmentCallback,
ChooseInitialMyStoryMembershipBottomSheetDialogFragment.Callback {
private val viewModel: MultiselectForwardViewModel by viewModels(factoryProducer = this::createViewModelFactory)
private val disposables = LifecycleDisposable()
@@ -285,24 +285,6 @@ class MultiselectForwardFragment :
private fun onSend(sendButton: View) {
sendButton.isEnabled = false
StoryDialogs.guardWithAddToYourStoryDialog(
requireContext(),
contactSearchMediator.getSelectedContacts(),
onAddToStory = {
performSend()
},
onEditViewers = {
sendButton.isEnabled = true
HideStoryFromDialogFragment().show(childFragmentManager, null)
},
onCancel = {
sendButton.isEnabled = true
}
)
}
private fun performSend() {
viewModel.send(addMessage.text.toString(), contactSearchMediator.getSelectedContacts())
}
@@ -483,6 +465,15 @@ class MultiselectForwardFragment :
CreateStoryFlowDialogFragment().show(parentFragmentManager, CreateStoryWithViewersFragment.REQUEST_KEY)
}
override fun onWrapperDialogFragmentDismissed() {
contactSearchMediator.refresh()
}
override fun onMyStoryConfigured(recipientId: RecipientId) {
contactSearchMediator.setKeysSelected(setOf(ContactSearchKey.RecipientSearchKey.Story(recipientId)))
contactSearchMediator.refresh()
}
interface Callback {
fun onFinishForwardAction()
fun exitFlow()
@@ -544,8 +535,4 @@ class MultiselectForwardFragment :
}
}
}
override fun onWrapperDialogFragmentDismissed() {
contactSearchMediator.refresh()
}
}

View File

@@ -22,8 +22,6 @@ import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionViewModel
import org.thoughtcrime.securesms.mediasend.v2.text.send.TextStoryPostSendRepository
import org.thoughtcrime.securesms.mediasend.v2.text.send.TextStoryPostSendResult
import org.thoughtcrime.securesms.stories.StoryTextPostView
import org.thoughtcrime.securesms.stories.dialogs.StoryDialogs
import org.thoughtcrime.securesms.stories.settings.privacy.HideStoryFromDialogFragment
import org.thoughtcrime.securesms.util.LifecycleDisposable
import org.thoughtcrime.securesms.util.navigation.safeNavigate
@@ -134,22 +132,7 @@ class TextStoryPostCreationFragment : Fragment(R.layout.stories_text_post_creati
findNavController().safeNavigate(R.id.action_textStoryPostCreationFragment_to_textStoryPostSendFragment)
} else {
send.isClickable = false
StoryDialogs.guardWithAddToYourStoryDialog(
contacts = contacts,
context = requireContext(),
onAddToStory = {
performSend(contacts)
},
onEditViewers = {
send.isClickable = true
storyTextPostView.hideCloseButton()
HideStoryFromDialogFragment().show(childFragmentManager, null)
},
onCancel = {
send.isClickable = true
storyTextPostView.hideCloseButton()
}
)
performSend(contacts)
}
}
}

View File

@@ -27,16 +27,19 @@ import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.sharing.ShareSelectionAdapter
import org.thoughtcrime.securesms.sharing.ShareSelectionMappingModel
import org.thoughtcrime.securesms.stories.Stories
import org.thoughtcrime.securesms.stories.dialogs.StoryDialogs
import org.thoughtcrime.securesms.stories.settings.create.CreateStoryFlowDialogFragment
import org.thoughtcrime.securesms.stories.settings.create.CreateStoryWithViewersFragment
import org.thoughtcrime.securesms.stories.settings.privacy.HideStoryFromDialogFragment
import org.thoughtcrime.securesms.stories.settings.privacy.ChooseInitialMyStoryMembershipBottomSheetDialogFragment
import org.thoughtcrime.securesms.util.BottomSheetUtil
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, WrapperDialogFragment.WrapperDialogFragmentCallback {
class TextStoryPostSendFragment :
Fragment(R.layout.stories_send_text_post_fragment),
ChooseStoryTypeBottomSheet.Callback,
WrapperDialogFragment.WrapperDialogFragmentCallback,
ChooseInitialMyStoryMembershipBottomSheetDialogFragment.Callback {
private lateinit var shareListWrapper: View
private lateinit var shareSelectionRecyclerView: RecyclerView
@@ -87,18 +90,7 @@ class TextStoryPostSendFragment : Fragment(R.layout.stories_send_text_post_fragm
shareConfirmButton.setOnClickListener {
viewModel.onSending()
StoryDialogs.guardWithAddToYourStoryDialog(
contacts = contactSearchMediator.getSelectedContacts(),
context = requireContext(),
onAddToStory = { send() },
onEditViewers = {
viewModel.onSendCancelled()
HideStoryFromDialogFragment().show(childFragmentManager, null)
},
onCancel = {
viewModel.onSendCancelled()
}
)
send()
}
disposables += viewModel.untrustedIdentities.subscribe {
@@ -200,4 +192,9 @@ class TextStoryPostSendFragment : Fragment(R.layout.stories_send_text_post_fragm
override fun onWrapperDialogFragmentDismissed() {
contactSearchMediator.refresh()
}
override fun onMyStoryConfigured(recipientId: RecipientId) {
contactSearchMediator.setKeysSelected(setOf(ContactSearchKey.RecipientSearchKey.Story(recipientId)))
contactSearchMediator.refresh()
}
}

View File

@@ -2,53 +2,14 @@ package org.thoughtcrime.securesms.stories.dialogs
import android.content.Context
import android.view.View
import android.widget.Toast
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.shape.ShapeAppearanceModel
import org.signal.core.util.DimensionUnit
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.Recipient
object StoryDialogs {
/**
* Guards onAddToStory with a dialog
*/
fun guardWithAddToYourStoryDialog(
context: Context,
contacts: Collection<ContactSearchKey>,
onAddToStory: () -> Unit,
onEditViewers: () -> Unit,
onCancel: () -> Unit = {}
) {
if (!isFirstSendToMyStory(contacts)) {
onAddToStory()
} else {
SignalStore.storyValues().userHasBeenNotifiedAboutStories = true
MaterialAlertDialogBuilder(context, R.style.ThemeOverlay_Signal_MaterialAlertDialog)
.setTitle(R.string.StoryDialogs__add_to_story_q)
.setMessage(R.string.StoryDialogs__adding_content)
.setPositiveButton(R.string.StoryDialogs__add_to_story) { _, _ ->
onAddToStory.invoke()
}
.setNeutralButton(R.string.StoryDialogs__edit_viewers) { _, _ -> Toast.makeText(context, "New flow coming soon", Toast.LENGTH_SHORT).show() }
.setNegativeButton(android.R.string.cancel) { _, _ -> onCancel.invoke() }
.setCancelable(false)
.show()
}
}
private fun isFirstSendToMyStory(shareContacts: Collection<ContactSearchKey>): Boolean {
if (SignalStore.storyValues().userHasBeenNotifiedAboutStories) {
return false
}
return shareContacts.any { it is ContactSearchKey.RecipientSearchKey.Story && Recipient.resolved(it.recipientId).isMyStory }
}
fun resendStory(context: Context, resend: () -> Unit) {
MaterialAlertDialogBuilder(context)
.setMessage(R.string.StoryDialogs__story_could_not_be_sent)

View File

@@ -1,27 +1,35 @@
package org.thoughtcrime.securesms.stories.settings.my
import androidx.annotation.WorkerThread
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.Observable
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.database.model.DistributionListPrivacyData
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.stories.Stories
import org.thoughtcrime.securesms.stories.settings.privacy.ChooseInitialMyStoryMembershipState
class MyStorySettingsRepository {
fun getPrivacyState(): Single<MyStoryPrivacyState> {
return Single.fromCallable {
val privacyData: DistributionListPrivacyData = SignalDatabase.distributionLists.getPrivacyData(DistributionListId.MY_STORY)
MyStoryPrivacyState(
privacyMode = privacyData.privacyMode,
connectionCount = if (privacyData.privacyMode == DistributionListPrivacyMode.ALL_EXCEPT) privacyData.rawMemberCount else privacyData.memberCount
)
getStoryPrivacyState()
}.subscribeOn(Schedulers.io())
}
fun observeChooseInitialPrivacy(): Observable<ChooseInitialMyStoryMembershipState> {
return Single.fromCallable { SignalDatabase.distributionLists.getRecipientId(DistributionListId.MY_STORY)!! }
.subscribeOn(Schedulers.io())
.flatMapObservable { recipientId ->
Recipient.observable(recipientId)
.flatMap { Observable.just(ChooseInitialMyStoryMembershipState(recipientId = recipientId, privacyState = getStoryPrivacyState())) }
}
}
fun setPrivacyMode(privacyMode: DistributionListPrivacyMode): Completable {
return Completable.fromAction {
SignalDatabase.distributionLists.setPrivacyMode(DistributionListId.MY_STORY, privacyMode)
@@ -41,4 +49,14 @@ class MyStorySettingsRepository {
Stories.onStorySettingsChanged(DistributionListId.MY_STORY)
}.subscribeOn(Schedulers.io())
}
@WorkerThread
private fun getStoryPrivacyState(): MyStoryPrivacyState {
val privacyData: DistributionListPrivacyData = SignalDatabase.distributionLists.getPrivacyData(DistributionListId.MY_STORY)
return MyStoryPrivacyState(
privacyMode = privacyData.privacyMode,
connectionCount = if (privacyData.privacyMode == DistributionListPrivacyMode.ALL_EXCEPT) privacyData.rawMemberCount else privacyData.memberCount
)
}
}

View File

@@ -1,7 +1,10 @@
package org.thoughtcrime.securesms.stories.settings.privacy
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.WrapperDialogFragment
import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.stories.settings.select.BaseStoryRecipientSelectionFragment
@@ -20,6 +23,18 @@ abstract class ChangeMyStoryMembershipFragment : BaseStoryRecipientSelectionFrag
class AllExceptFragment : ChangeMyStoryMembershipFragment() {
override val toolbarTitleId: Int = R.string.ChangeMyStoryMembershipFragment__all_except
override val checkboxResource: Int = R.drawable.contact_selection_exclude_checkbox
class Dialog : WrapperDialogFragment() {
override fun getWrappedFragment(): Fragment {
return AllExceptFragment()
}
}
companion object {
fun createAsDialog(): DialogFragment {
return Dialog()
}
}
}
/**
@@ -27,4 +42,16 @@ class AllExceptFragment : ChangeMyStoryMembershipFragment() {
*/
class OnlyShareWithFragment : ChangeMyStoryMembershipFragment() {
override val toolbarTitleId: Int = R.string.ChangeMyStoryMembershipFragment__only_share_with
class Dialog : WrapperDialogFragment() {
override fun getWrappedFragment(): Fragment {
return OnlyShareWithFragment()
}
}
companion object {
fun createAsDialog(): DialogFragment {
return Dialog()
}
}
}

View File

@@ -0,0 +1,135 @@
package org.thoughtcrime.securesms.stories.settings.privacy
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.viewModels
import com.google.android.material.radiobutton.MaterialRadioButton
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.FixedRoundedCornerBottomSheetDialogFragment
import org.thoughtcrime.securesms.components.WrapperDialogFragment
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.stories.settings.select.BaseStoryRecipientSelectionFragment
import org.thoughtcrime.securesms.util.BottomSheetUtil
import org.thoughtcrime.securesms.util.LifecycleDisposable
import org.thoughtcrime.securesms.util.fragments.findListener
import org.thoughtcrime.securesms.util.visible
/**
* Choose the initial settings for My Story when first sending to My Story.
*/
class ChooseInitialMyStoryMembershipBottomSheetDialogFragment :
FixedRoundedCornerBottomSheetDialogFragment(),
WrapperDialogFragment.WrapperDialogFragmentCallback,
BaseStoryRecipientSelectionFragment.Callback {
private val viewModel: ChooseInitialMyStoryMembershipViewModel by viewModels()
private lateinit var lifecycleDisposable: LifecycleDisposable
private lateinit var allRow: View
private lateinit var allExceptRow: View
private lateinit var onlyWitRow: View
private lateinit var allRadio: MaterialRadioButton
private lateinit var allExceptRadio: MaterialRadioButton
private lateinit var onlyWitRadio: MaterialRadioButton
private lateinit var allExceptCount: TextView
private lateinit var onlyWithCount: TextView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.choose_initial_my_story_membership_fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
allRow = view.findViewById(R.id.choose_initial_my_story_all_signal_connnections_row)
allExceptRow = view.findViewById(R.id.choose_initial_my_story_all_signal_connnections_except_row)
onlyWitRow = view.findViewById(R.id.choose_initial_my_story_only_share_with_row)
allRadio = view.findViewById(R.id.choose_initial_my_story_all_signal_connnections_radio)
allExceptRadio = view.findViewById(R.id.choose_initial_my_story_all_signal_connnections_except_radio)
onlyWitRadio = view.findViewById(R.id.choose_initial_my_story_only_share_with_radio)
allExceptCount = view.findViewById(R.id.choose_initial_my_story_all_signal_connnections_except_count)
onlyWithCount = view.findViewById(R.id.choose_initial_my_story_only_share_with_count)
val save = view.findViewById<View>(R.id.choose_initial_my_story_save).apply {
isEnabled = false
}
lifecycleDisposable = LifecycleDisposable().apply { bindTo(viewLifecycleOwner) }
lifecycleDisposable += viewModel.state
.subscribe { state ->
allRadio.isChecked = state.privacyState.privacyMode == DistributionListPrivacyMode.ALL
allExceptRadio.isChecked = state.privacyState.privacyMode == DistributionListPrivacyMode.ALL_EXCEPT
onlyWitRadio.isChecked = state.privacyState.privacyMode == DistributionListPrivacyMode.ONLY_WITH
allExceptCount.visible = allExceptRadio.isChecked
onlyWithCount.visible = onlyWitRadio.isChecked
when (state.privacyState.privacyMode) {
DistributionListPrivacyMode.ALL_EXCEPT -> allExceptCount.text = resources.getQuantityString(R.plurals.MyStorySettingsFragment__d_people_excluded, state.privacyState.connectionCount, state.privacyState.connectionCount)
DistributionListPrivacyMode.ONLY_WITH -> onlyWithCount.text = resources.getQuantityString(R.plurals.MyStorySettingsFragment__d_people, state.privacyState.connectionCount, state.privacyState.connectionCount)
else -> Unit
}
save.isEnabled = state.recipientId != null
}
val clickListener = { v: View ->
val selection = when (v) {
allRow -> DistributionListPrivacyMode.ALL
allExceptRow -> DistributionListPrivacyMode.ALL_EXCEPT
onlyWitRow -> DistributionListPrivacyMode.ONLY_WITH
else -> throw AssertionError()
}
viewModel
.select(selection)
.subscribe { confirmedSelection ->
when (confirmedSelection) {
DistributionListPrivacyMode.ALL_EXCEPT -> AllExceptFragment.createAsDialog().show(childFragmentManager, SELECTION_FRAGMENT)
DistributionListPrivacyMode.ONLY_WITH -> OnlyShareWithFragment.createAsDialog().show(childFragmentManager, SELECTION_FRAGMENT)
else -> Unit
}
}
}
listOf(allRow, allExceptRow, onlyWitRow).forEach { it.setOnClickListener { v -> clickListener(v) } }
save.setOnClickListener {
lifecycleDisposable += viewModel
.save()
.subscribe { recipientId ->
dismissAllowingStateLoss()
findListener<Callback>()?.onMyStoryConfigured(recipientId)
}
}
}
override fun exitFlow() {
(childFragmentManager.findFragmentByTag(SELECTION_FRAGMENT) as? DialogFragment)?.dismissAllowingStateLoss()
}
override fun onWrapperDialogFragmentDismissed() = Unit
companion object {
private const val SELECTION_FRAGMENT = "selection_fragment"
fun show(fragmentManager: FragmentManager) {
val fragment = ChooseInitialMyStoryMembershipBottomSheetDialogFragment()
fragment.show(fragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
}
}
interface Callback {
fun onMyStoryConfigured(recipientId: RecipientId)
}
}

View File

@@ -0,0 +1,6 @@
package org.thoughtcrime.securesms.stories.settings.privacy
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.stories.settings.my.MyStoryPrivacyState
data class ChooseInitialMyStoryMembershipState(val recipientId: RecipientId? = null, val privacyState: MyStoryPrivacyState = MyStoryPrivacyState())

View File

@@ -0,0 +1,46 @@
package org.thoughtcrime.securesms.stories.settings.privacy
import androidx.lifecycle.ViewModel
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.stories.settings.my.MyStorySettingsRepository
import org.thoughtcrime.securesms.util.rx.RxStore
class ChooseInitialMyStoryMembershipViewModel @JvmOverloads constructor(
private val repository: MyStorySettingsRepository = MyStorySettingsRepository()
) : ViewModel() {
private val store = RxStore(ChooseInitialMyStoryMembershipState())
private val disposables = CompositeDisposable()
val state: Flowable<ChooseInitialMyStoryMembershipState> = store.stateFlowable.observeOn(AndroidSchedulers.mainThread())
init {
disposables += repository.observeChooseInitialPrivacy()
.distinctUntilChanged()
.subscribe { state -> store.update { state } }
}
override fun onCleared() {
disposables.clear()
}
fun select(selection: DistributionListPrivacyMode): Single<DistributionListPrivacyMode> {
return repository.setPrivacyMode(selection)
.toSingleDefault(selection)
.observeOn(AndroidSchedulers.mainThread())
}
fun save(): Single<RecipientId> {
return Single.fromCallable<RecipientId> {
SignalStore.storyValues().userHasBeenNotifiedAboutStories = true
store.state.recipientId
}.observeOn(AndroidSchedulers.mainThread())
}
}

View File

@@ -1,25 +0,0 @@
package org.thoughtcrime.securesms.stories.settings.privacy
import android.os.Bundle
import android.view.View
import androidx.fragment.app.DialogFragment
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.stories.settings.select.BaseStoryRecipientSelectionFragment
/**
* Embeds HideStoryFromFragment in a full-screen dialog.
*/
class HideStoryFromDialogFragment : DialogFragment(R.layout.fragment_container), BaseStoryRecipientSelectionFragment.Callback {
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?) {
// TODO [stories] replace with new bottom sheet
}
override fun exitFlow() {
dismissAllowingStateLoss()
}
}