kopia lustrzana https://github.com/ryukoposting/Signal-Android
Add dialog protection and remote deletion to disabling stories and deleting lists.
rodzic
ad1801108d
commit
4b94509a7a
|
@ -0,0 +1,35 @@
|
||||||
|
package org.thoughtcrime.securesms.components
|
||||||
|
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages the lifecycle of displaying a dialog fragment. Will automatically close and nullify the reference
|
||||||
|
* if the bound lifecycle is destroyed, and handles repeat calls to show such that no more than one dialog is
|
||||||
|
* displayed.
|
||||||
|
*/
|
||||||
|
class DialogFragmentDisplayManager(private val builder: () -> DialogFragment) : DefaultLifecycleObserver {
|
||||||
|
|
||||||
|
private var dialogFragment: DialogFragment? = null
|
||||||
|
|
||||||
|
fun show(lifecycleOwner: LifecycleOwner, fragmentManager: FragmentManager, tag: String? = null) {
|
||||||
|
val fragment = dialogFragment ?: builder()
|
||||||
|
if (fragment.dialog?.isShowing != true) {
|
||||||
|
fragment.show(fragmentManager, tag)
|
||||||
|
dialogFragment = fragment
|
||||||
|
lifecycleOwner.lifecycle.addObserver(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hide() {
|
||||||
|
dialogFragment?.dismissNow()
|
||||||
|
dialogFragment = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy(owner: LifecycleOwner) {
|
||||||
|
owner.lifecycle.removeObserver(this)
|
||||||
|
hide()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.thoughtcrime.securesms.components
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import org.thoughtcrime.securesms.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a small progress spinner in a card view, as a non-cancellable dialog fragment.
|
||||||
|
*/
|
||||||
|
class ProgressCardDialogFragment : DialogFragment(R.layout.progress_card_dialog) {
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
isCancelable = false
|
||||||
|
return super.onCreateDialog(savedInstanceState).apply {
|
||||||
|
this.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,6 +44,33 @@ class StoryDialogLauncherFragment : DSLSettingsFragment(titleId = R.string.prefe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
clickPref(
|
||||||
|
title = DSLSettingsText.from(R.string.preferences__internal_turn_off_stories),
|
||||||
|
onClick = {
|
||||||
|
StoryDialogs.disableStories(requireContext(), false) {
|
||||||
|
Toast.makeText(requireContext(), R.string.preferences__internal_turn_off_stories, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
clickPref(
|
||||||
|
title = DSLSettingsText.from(R.string.preferences__internal_turn_off_stories_with_stories_on_disk),
|
||||||
|
onClick = {
|
||||||
|
StoryDialogs.disableStories(requireContext(), true) {
|
||||||
|
Toast.makeText(requireContext(), R.string.preferences__internal_turn_off_stories_with_stories_on_disk, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
clickPref(
|
||||||
|
title = DSLSettingsText.from(R.string.preferences__internal_delete_private_story),
|
||||||
|
onClick = {
|
||||||
|
StoryDialogs.deleteDistributionList(requireContext(), "Family") {
|
||||||
|
Toast.makeText(requireContext(), R.string.preferences__internal_delete_private_story, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,38 @@ import org.thoughtcrime.securesms.R
|
||||||
|
|
||||||
object StoryDialogs {
|
object StoryDialogs {
|
||||||
|
|
||||||
|
fun deleteDistributionList(
|
||||||
|
context: Context,
|
||||||
|
distributionListName: String,
|
||||||
|
onDelete: () -> Unit
|
||||||
|
) {
|
||||||
|
MaterialAlertDialogBuilder(context)
|
||||||
|
.setTitle(R.string.StoryDialogs__delete_private_story)
|
||||||
|
.setMessage(context.getString(R.string.StoryDialogs__s_and_updates_shared, distributionListName))
|
||||||
|
.setPositiveButton(R.string.StoryDialogs__delete) { _, _ -> onDelete() }
|
||||||
|
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun disableStories(
|
||||||
|
context: Context,
|
||||||
|
userHasStories: Boolean,
|
||||||
|
onDisable: () -> Unit
|
||||||
|
) {
|
||||||
|
val positiveButtonMessage = if (userHasStories) {
|
||||||
|
R.string.StoryDialogs__turn_off_and_delete
|
||||||
|
} else {
|
||||||
|
R.string.StoriesPrivacySettingsFragment__turn_off_stories
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialAlertDialogBuilder(context)
|
||||||
|
.setTitle(R.string.StoriesPrivacySettingsFragment__turn_off_stories_question)
|
||||||
|
.setMessage(R.string.StoriesPrivacySettingsFragment__you_will_no_longer_be_able_to_share)
|
||||||
|
.setPositiveButton(positiveButtonMessage) { _, _ -> onDisable() }
|
||||||
|
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
fun resendStory(context: Context, onDismiss: () -> Unit = {}, resend: () -> Unit) {
|
fun resendStory(context: Context, onDismiss: () -> Unit = {}, resend: () -> Unit) {
|
||||||
MaterialAlertDialogBuilder(context)
|
MaterialAlertDialogBuilder(context)
|
||||||
.setMessage(R.string.StoryDialogs__story_could_not_be_sent)
|
.setMessage(R.string.StoryDialogs__story_could_not_be_sent)
|
||||||
|
|
|
@ -10,6 +10,8 @@ import androidx.navigation.fragment.NavHostFragment
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
|
import org.thoughtcrime.securesms.components.DialogFragmentDisplayManager
|
||||||
|
import org.thoughtcrime.securesms.components.ProgressCardDialogFragment
|
||||||
import org.thoughtcrime.securesms.components.WrapperDialogFragment
|
import org.thoughtcrime.securesms.components.WrapperDialogFragment
|
||||||
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
||||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
|
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
|
||||||
|
@ -17,6 +19,7 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
||||||
import org.thoughtcrime.securesms.components.settings.configure
|
import org.thoughtcrime.securesms.components.settings.configure
|
||||||
import org.thoughtcrime.securesms.database.model.DistributionListId
|
import org.thoughtcrime.securesms.database.model.DistributionListId
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
|
import org.thoughtcrime.securesms.stories.dialogs.StoryDialogs
|
||||||
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
|
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
|
||||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||||
import org.thoughtcrime.securesms.util.fragments.findListener
|
import org.thoughtcrime.securesms.util.fragments.findListener
|
||||||
|
@ -28,6 +31,8 @@ class PrivateStorySettingsFragment : DSLSettingsFragment(
|
||||||
menuId = R.menu.story_private_menu
|
menuId = R.menu.story_private_menu
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
private val progressDisplayManager = DialogFragmentDisplayManager { ProgressCardDialogFragment() }
|
||||||
|
|
||||||
private val viewModel: PrivateStorySettingsViewModel by viewModels(
|
private val viewModel: PrivateStorySettingsViewModel by viewModels(
|
||||||
factoryProducer = {
|
factoryProducer = {
|
||||||
PrivateStorySettingsViewModel.Factory(PrivateStorySettingsFragmentArgs.fromBundle(requireArguments()).distributionListId, PrivateStorySettingsRepository())
|
PrivateStorySettingsViewModel.Factory(PrivateStorySettingsFragmentArgs.fromBundle(requireArguments()).distributionListId, PrivateStorySettingsRepository())
|
||||||
|
@ -49,6 +54,12 @@ class PrivateStorySettingsFragment : DSLSettingsFragment(
|
||||||
val toolbar: Toolbar = requireView().findViewById(R.id.toolbar)
|
val toolbar: Toolbar = requireView().findViewById(R.id.toolbar)
|
||||||
|
|
||||||
viewModel.state.observe(viewLifecycleOwner) { state ->
|
viewModel.state.observe(viewLifecycleOwner) { state ->
|
||||||
|
if (state.isActionInProgress) {
|
||||||
|
progressDisplayManager.show(viewLifecycleOwner, childFragmentManager)
|
||||||
|
} else {
|
||||||
|
progressDisplayManager.hide()
|
||||||
|
}
|
||||||
|
|
||||||
toolbar.title = state.privateStory?.name
|
toolbar.title = state.privateStory?.name
|
||||||
adapter.submitList(getConfiguration(state).toMappingModelList())
|
adapter.submitList(getConfiguration(state).toMappingModelList())
|
||||||
}
|
}
|
||||||
|
@ -88,7 +99,8 @@ class PrivateStorySettingsFragment : DSLSettingsFragment(
|
||||||
clickPref(
|
clickPref(
|
||||||
title = DSLSettingsText.from(R.string.PrivateStorySettingsFragment__delete_private_story, DSLSettingsText.ColorModifier(ContextCompat.getColor(requireContext(), R.color.signal_alert_primary))),
|
title = DSLSettingsText.from(R.string.PrivateStorySettingsFragment__delete_private_story, DSLSettingsText.ColorModifier(ContextCompat.getColor(requireContext(), R.color.signal_alert_primary))),
|
||||||
onClick = {
|
onClick = {
|
||||||
handleDeletePrivateStory()
|
val privateStoryName = viewModel.state.value?.privateStory?.name
|
||||||
|
handleDeletePrivateStory(privateStoryName)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -113,13 +125,12 @@ class PrivateStorySettingsFragment : DSLSettingsFragment(
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDeletePrivateStory() {
|
private fun handleDeletePrivateStory(privateStoryName: String?) {
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
val name = privateStoryName ?: return
|
||||||
.setTitle(R.string.PrivateStorySettingsFragment__are_you_sure)
|
|
||||||
.setMessage(R.string.PrivateStorySettingsFragment__this_action_cannot)
|
StoryDialogs.deleteDistributionList(requireContext(), name) {
|
||||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
viewModel.delete().subscribe { findNavController().popBackStack() }
|
||||||
.setPositiveButton(R.string.delete) { _, _ -> viewModel.delete().subscribe { findNavController().popBackStack() } }
|
}
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onToolbarNavigationClicked() {
|
override fun onToolbarNavigationClicked() {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import org.thoughtcrime.securesms.database.SignalDatabase
|
||||||
import org.thoughtcrime.securesms.database.model.DistributionListId
|
import org.thoughtcrime.securesms.database.model.DistributionListId
|
||||||
import org.thoughtcrime.securesms.database.model.DistributionListRecord
|
import org.thoughtcrime.securesms.database.model.DistributionListRecord
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||||
|
import org.thoughtcrime.securesms.sms.MessageSender
|
||||||
import org.thoughtcrime.securesms.stories.Stories
|
import org.thoughtcrime.securesms.stories.Stories
|
||||||
|
|
||||||
class PrivateStorySettingsRepository {
|
class PrivateStorySettingsRepository {
|
||||||
|
@ -27,6 +28,13 @@ class PrivateStorySettingsRepository {
|
||||||
return Completable.fromAction {
|
return Completable.fromAction {
|
||||||
SignalDatabase.distributionLists.deleteList(distributionListId)
|
SignalDatabase.distributionLists.deleteList(distributionListId)
|
||||||
Stories.onStorySettingsChanged(distributionListId)
|
Stories.onStorySettingsChanged(distributionListId)
|
||||||
|
|
||||||
|
val recipientId = SignalDatabase.recipients.getOrInsertFromDistributionListId(distributionListId)
|
||||||
|
SignalDatabase.mms.getAllStoriesFor(recipientId, -1).use { reader ->
|
||||||
|
for (record in reader) {
|
||||||
|
MessageSender.sendRemoteDelete(record.id, record.isMms)
|
||||||
|
}
|
||||||
|
}
|
||||||
}.subscribeOn(Schedulers.io())
|
}.subscribeOn(Schedulers.io())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,5 +4,6 @@ import org.thoughtcrime.securesms.database.model.DistributionListRecord
|
||||||
|
|
||||||
data class PrivateStorySettingsState(
|
data class PrivateStorySettingsState(
|
||||||
val privateStory: DistributionListRecord? = null,
|
val privateStory: DistributionListRecord? = null,
|
||||||
val areRepliesAndReactionsEnabled: Boolean = false
|
val areRepliesAndReactionsEnabled: Boolean = false,
|
||||||
|
val isActionInProgress: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
|
@ -52,7 +52,9 @@ class PrivateStorySettingsViewModel(private val distributionListId: Distribution
|
||||||
}
|
}
|
||||||
|
|
||||||
fun delete(): Completable {
|
fun delete(): Completable {
|
||||||
return repository.delete(distributionListId).observeOn(AndroidSchedulers.mainThread())
|
return repository.delete(distributionListId)
|
||||||
|
.doOnSubscribe { store.update { it.copy(isActionInProgress = true) } }
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory(private val privateStoryItemData: DistributionListId, private val repository: PrivateStorySettingsRepository) : ViewModelProvider.Factory {
|
class Factory(private val privateStoryItemData: DistributionListId, private val repository: PrivateStorySettingsRepository) : ViewModelProvider.Factory {
|
||||||
|
|
|
@ -4,9 +4,10 @@ import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.ConcatAdapter
|
import androidx.recyclerview.widget.ConcatAdapter
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import org.signal.core.util.dp
|
import org.signal.core.util.dp
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
|
import org.thoughtcrime.securesms.components.DialogFragmentDisplayManager
|
||||||
|
import org.thoughtcrime.securesms.components.ProgressCardDialogFragment
|
||||||
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
||||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
|
import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
|
||||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
|
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
|
||||||
|
@ -17,6 +18,7 @@ import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||||
import org.thoughtcrime.securesms.groups.ParcelableGroupId
|
import org.thoughtcrime.securesms.groups.ParcelableGroupId
|
||||||
import org.thoughtcrime.securesms.mediasend.v2.stories.ChooseGroupStoryBottomSheet
|
import org.thoughtcrime.securesms.mediasend.v2.stories.ChooseGroupStoryBottomSheet
|
||||||
import org.thoughtcrime.securesms.mediasend.v2.stories.ChooseStoryTypeBottomSheet
|
import org.thoughtcrime.securesms.mediasend.v2.stories.ChooseStoryTypeBottomSheet
|
||||||
|
import org.thoughtcrime.securesms.stories.dialogs.StoryDialogs
|
||||||
import org.thoughtcrime.securesms.stories.settings.create.CreateStoryFlowDialogFragment
|
import org.thoughtcrime.securesms.stories.settings.create.CreateStoryFlowDialogFragment
|
||||||
import org.thoughtcrime.securesms.stories.settings.create.CreateStoryWithViewersFragment
|
import org.thoughtcrime.securesms.stories.settings.create.CreateStoryWithViewersFragment
|
||||||
import org.thoughtcrime.securesms.util.BottomSheetUtil
|
import org.thoughtcrime.securesms.util.BottomSheetUtil
|
||||||
|
@ -36,6 +38,7 @@ class StoriesPrivacySettingsFragment :
|
||||||
|
|
||||||
private val viewModel: StoriesPrivacySettingsViewModel by viewModels()
|
private val viewModel: StoriesPrivacySettingsViewModel by viewModels()
|
||||||
private val lifecycleDisposable = LifecycleDisposable()
|
private val lifecycleDisposable = LifecycleDisposable()
|
||||||
|
private val progressDisplayManager = DialogFragmentDisplayManager { ProgressCardDialogFragment() }
|
||||||
|
|
||||||
override fun createAdapters(): Array<MappingAdapter> {
|
override fun createAdapters(): Array<MappingAdapter> {
|
||||||
return arrayOf(DSLSettingsAdapter(), PagingMappingAdapter<ContactSearchKey>(), DSLSettingsAdapter())
|
return arrayOf(DSLSettingsAdapter(), PagingMappingAdapter<ContactSearchKey>(), DSLSettingsAdapter())
|
||||||
|
@ -84,6 +87,12 @@ class StoriesPrivacySettingsFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
lifecycleDisposable += viewModel.state.subscribe { state ->
|
lifecycleDisposable += viewModel.state.subscribe { state ->
|
||||||
|
if (state.isUpdatingEnabledState) {
|
||||||
|
progressDisplayManager.show(viewLifecycleOwner, childFragmentManager)
|
||||||
|
} else {
|
||||||
|
progressDisplayManager.hide()
|
||||||
|
}
|
||||||
|
|
||||||
(top as MappingAdapter).submitList(getTopConfiguration(state).toMappingModelList())
|
(top as MappingAdapter).submitList(getTopConfiguration(state).toMappingModelList())
|
||||||
middle.submitList(getMiddleConfiguration(state).toMappingModelList())
|
middle.submitList(getMiddleConfiguration(state).toMappingModelList())
|
||||||
(bottom as MappingAdapter).submitList(getBottomConfiguration(state).toMappingModelList())
|
(bottom as MappingAdapter).submitList(getBottomConfiguration(state).toMappingModelList())
|
||||||
|
@ -144,12 +153,9 @@ class StoriesPrivacySettingsFragment :
|
||||||
DSLSettingsText.ColorModifier(ContextCompat.getColor(requireContext(), R.color.signal_colorOnSurfaceVariant))
|
DSLSettingsText.ColorModifier(ContextCompat.getColor(requireContext(), R.color.signal_colorOnSurfaceVariant))
|
||||||
),
|
),
|
||||||
onClick = {
|
onClick = {
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
StoryDialogs.disableStories(requireContext(), viewModel.userHasActiveStories) {
|
||||||
.setTitle(R.string.StoriesPrivacySettingsFragment__turn_off_stories_question)
|
viewModel.setStoriesEnabled(false)
|
||||||
.setMessage(R.string.StoriesPrivacySettingsFragment__you_will_no_longer_be_able_to)
|
}
|
||||||
.setPositiveButton(R.string.StoriesPrivacySettingsFragment__turn_off_stories) { _, _ -> viewModel.setStoriesEnabled(false) }
|
|
||||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package org.thoughtcrime.securesms.stories.settings.story
|
package org.thoughtcrime.securesms.stories.settings.story
|
||||||
|
|
||||||
import io.reactivex.rxjava3.core.Completable
|
import io.reactivex.rxjava3.core.Completable
|
||||||
|
import io.reactivex.rxjava3.core.Single
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase
|
import org.thoughtcrime.securesms.database.GroupDatabase
|
||||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||||
|
import org.thoughtcrime.securesms.sms.MessageSender
|
||||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
||||||
import org.thoughtcrime.securesms.stories.Stories
|
import org.thoughtcrime.securesms.stories.Stories
|
||||||
|
|
||||||
|
@ -23,6 +25,20 @@ class StoriesPrivacySettingsRepository {
|
||||||
return Completable.fromAction {
|
return Completable.fromAction {
|
||||||
SignalStore.storyValues().isFeatureDisabled = !isEnabled
|
SignalStore.storyValues().isFeatureDisabled = !isEnabled
|
||||||
Stories.onStorySettingsChanged(Recipient.self().id)
|
Stories.onStorySettingsChanged(Recipient.self().id)
|
||||||
|
|
||||||
|
SignalDatabase.mms.getAllOutgoingStories(false, -1).use { reader ->
|
||||||
|
reader.map { record -> record.id }
|
||||||
|
}.forEach { messageId ->
|
||||||
|
MessageSender.sendRemoteDelete(messageId, true)
|
||||||
|
}
|
||||||
|
}.subscribeOn(Schedulers.io())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun userHasOutgoingStories(): Single<Boolean> {
|
||||||
|
return Single.fromCallable {
|
||||||
|
SignalDatabase.mms.getAllOutgoingStories(false, -1).use {
|
||||||
|
it.iterator().hasNext()
|
||||||
|
}
|
||||||
}.subscribeOn(Schedulers.io())
|
}.subscribeOn(Schedulers.io())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,5 +5,6 @@ import org.thoughtcrime.securesms.contacts.paged.ContactSearchData
|
||||||
data class StoriesPrivacySettingsState(
|
data class StoriesPrivacySettingsState(
|
||||||
val areStoriesEnabled: Boolean,
|
val areStoriesEnabled: Boolean,
|
||||||
val isUpdatingEnabledState: Boolean = false,
|
val isUpdatingEnabledState: Boolean = false,
|
||||||
val storyContactItems: List<ContactSearchData> = emptyList()
|
val storyContactItems: List<ContactSearchData> = emptyList(),
|
||||||
|
val userHasStories: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
|
@ -39,6 +39,7 @@ class StoriesPrivacySettingsViewModel : ViewModel() {
|
||||||
private val headerActionRequestSubject = PublishSubject.create<Unit>()
|
private val headerActionRequestSubject = PublishSubject.create<Unit>()
|
||||||
|
|
||||||
val state: Flowable<StoriesPrivacySettingsState> = store.stateFlowable.observeOn(AndroidSchedulers.mainThread())
|
val state: Flowable<StoriesPrivacySettingsState> = store.stateFlowable.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
val userHasActiveStories: Boolean get() = store.state.userHasStories
|
||||||
val pagingController = ProxyPagingController<ContactSearchKey>()
|
val pagingController = ProxyPagingController<ContactSearchKey>()
|
||||||
val headerActionRequests: Observable<Unit> = headerActionRequestSubject.debounce(100, TimeUnit.MILLISECONDS)
|
val headerActionRequests: Observable<Unit> = headerActionRequestSubject.debounce(100, TimeUnit.MILLISECONDS)
|
||||||
|
|
||||||
|
@ -59,6 +60,8 @@ class StoriesPrivacySettingsViewModel : ViewModel() {
|
||||||
|
|
||||||
pagingController.set(observablePagedData.controller)
|
pagingController.set(observablePagedData.controller)
|
||||||
|
|
||||||
|
updateUserHasStories()
|
||||||
|
|
||||||
disposables += store.update(observablePagedData.data.toFlowable(BackpressureStrategy.LATEST)) { data, state ->
|
disposables += store.update(observablePagedData.data.toFlowable(BackpressureStrategy.LATEST)) { data, state ->
|
||||||
state.copy(storyContactItems = data)
|
state.copy(storyContactItems = data)
|
||||||
}
|
}
|
||||||
|
@ -78,6 +81,7 @@ class StoriesPrivacySettingsViewModel : ViewModel() {
|
||||||
areStoriesEnabled = Stories.isFeatureEnabled()
|
areStoriesEnabled = Stories.isFeatureEnabled()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
updateUserHasStories()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,4 +90,10 @@ class StoriesPrivacySettingsViewModel : ViewModel() {
|
||||||
pagingController.onDataInvalidated()
|
pagingController.onDataInvalidated()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateUserHasStories() {
|
||||||
|
disposables += repository.userHasOutgoingStories().subscribe { userHasActiveStories ->
|
||||||
|
store.update { it.copy(userHasStories = userHasActiveStories) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,5 +12,6 @@
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_margin="24dp"
|
android:layout_margin="24dp"
|
||||||
android:indeterminate="true"
|
android:indeterminate="true"
|
||||||
|
android:background="@color/transparent"
|
||||||
app:indicatorColor="@color/signal_colorPrimary" />
|
app:indicatorColor="@color/signal_colorPrimary" />
|
||||||
</merge>
|
</merge>
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.components.ProgressCard
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:cardCornerRadius="18dp" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
|
@ -2753,6 +2753,9 @@
|
||||||
<string name="configurable_single_select__customize_option">Customize option</string>
|
<string name="configurable_single_select__customize_option">Customize option</string>
|
||||||
|
|
||||||
<!-- Internal only preferences -->
|
<!-- Internal only preferences -->
|
||||||
|
<string name="preferences__internal_turn_off_stories_with_stories_on_disk" translatable="false">Turn off stories (with stories on disk)</string>
|
||||||
|
<string name="preferences__internal_turn_off_stories" translatable="false">Turn off stories</string>
|
||||||
|
<string name="preferences__internal_delete_private_story" translatable="false">Delete private story</string>
|
||||||
<string name="preferences__internal_hide_story" translatable="false">Hide story</string>
|
<string name="preferences__internal_hide_story" translatable="false">Hide story</string>
|
||||||
<string name="preferences__internal_story_or_profile_selector" translatable="false">Story or profile selector</string>
|
<string name="preferences__internal_story_or_profile_selector" translatable="false">Story or profile selector</string>
|
||||||
<string name="preferences__internal_stories_dialog_launcher" translatable="false">Stories dialog launcher</string>
|
<string name="preferences__internal_stories_dialog_launcher" translatable="false">Stories dialog launcher</string>
|
||||||
|
@ -4936,6 +4939,12 @@
|
||||||
<string name="ChangeMyStoryMembershipFragment__only_share_with">Only share with…</string>
|
<string name="ChangeMyStoryMembershipFragment__only_share_with">Only share with…</string>
|
||||||
<!-- Done button label for hide story from screen -->
|
<!-- Done button label for hide story from screen -->
|
||||||
<string name="HideStoryFromFragment__done">Done</string>
|
<string name="HideStoryFromFragment__done">Done</string>
|
||||||
|
<!-- Dialog title for deleting a private story -->
|
||||||
|
<string name="StoryDialogs__delete_private_story">Delete private story?</string>
|
||||||
|
<!-- Dialog message for deleting a private story -->
|
||||||
|
<string name="StoryDialogs__s_and_updates_shared">\"%1$s\" and updates shared to this story will be deleted.</string>
|
||||||
|
<!-- Dialog positive action for deleting a private story -->
|
||||||
|
<string name="StoryDialogs__delete">Delete</string>
|
||||||
<!-- Dialog title for first time adding something to a story -->
|
<!-- Dialog title for first time adding something to a story -->
|
||||||
<string name="StoryDialogs__add_to_story_q">Add to story?</string>
|
<string name="StoryDialogs__add_to_story_q">Add to story?</string>
|
||||||
<!-- Dialog message for first time adding something to a story -->
|
<!-- Dialog message for first time adding something to a story -->
|
||||||
|
@ -4948,6 +4957,8 @@
|
||||||
<string name="StoryDialogs__story_could_not_be_sent">Story could not be sent. Check your connection and try again.</string>
|
<string name="StoryDialogs__story_could_not_be_sent">Story could not be sent. Check your connection and try again.</string>
|
||||||
<!-- Error message dialog button to resend a previously failed story send -->
|
<!-- Error message dialog button to resend a previously failed story send -->
|
||||||
<string name="StoryDialogs__send">Send</string>
|
<string name="StoryDialogs__send">Send</string>
|
||||||
|
<!-- Action button for turning off stories when stories are present on the device -->
|
||||||
|
<string name="StoryDialogs__turn_off_and_delete">Turn off and delete</string>
|
||||||
<!-- Privacy Settings toggle title for stories -->
|
<!-- Privacy Settings toggle title for stories -->
|
||||||
<string name="PrivacySettingsFragment__share_and_view_stories">Share & View Stories</string>
|
<string name="PrivacySettingsFragment__share_and_view_stories">Share & View Stories</string>
|
||||||
<!-- Privacy Settings toggle summary for stories -->
|
<!-- Privacy Settings toggle summary for stories -->
|
||||||
|
@ -5231,7 +5242,7 @@
|
||||||
<!-- Dialog title to turn off stories -->
|
<!-- Dialog title to turn off stories -->
|
||||||
<string name="StoriesPrivacySettingsFragment__turn_off_stories_question">Turn off stories?</string>
|
<string name="StoriesPrivacySettingsFragment__turn_off_stories_question">Turn off stories?</string>
|
||||||
<!-- Dialog message to turn off stories -->
|
<!-- Dialog message to turn off stories -->
|
||||||
<string name="StoriesPrivacySettingsFragment__you_will_no_longer_be_able_to">You will no longer be able to share or view stories. Any stories you have recently sent will still be visible by others until they expire.</string>
|
<string name="StoriesPrivacySettingsFragment__you_will_no_longer_be_able_to_share">You will no longer be able to share or view stories. Story updates you have recently shared will also be deleted.</string>
|
||||||
<!-- Page title when launched from stories landing screen -->
|
<!-- Page title when launched from stories landing screen -->
|
||||||
<string name="StoriesPrivacySettingsFragment__story_privacy">Story privacy</string>
|
<string name="StoriesPrivacySettingsFragment__story_privacy">Story privacy</string>
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue