Display dialog to confirm hiding story in story viewer.

fork-5.53.8
Alex Hart 2022-09-16 13:29:39 -03:00 zatwierdzone przez Cody Henthorne
rodzic 115d1fcf63
commit 8141b53c15
10 zmienionych plików z 80 dodań i 32 usunięć

Wyświetl plik

@ -41,4 +41,21 @@ object StoryDialogs {
} }
.show() .show()
} }
fun hideStory(
context: Context,
recipientName: String,
onCancelled: () -> Unit = {},
onHideStoryConfirmed: () -> Unit,
) {
MaterialAlertDialogBuilder(context, R.style.ThemeOverlay_Signal_MaterialAlertDialog)
.setTitle(R.string.StoriesLandingFragment__hide_story)
.setMessage(context.getString(R.string.StoriesLandingFragment__new_story_updates, recipientName))
.setPositiveButton(R.string.StoriesLandingFragment__hide) { _, _ ->
onHideStoryConfirmed()
}
.setNegativeButton(android.R.string.cancel) { _, _ -> onCancelled() }
.setOnCancelListener { onCancelled() }
.show()
}
} }

Wyświetl plik

@ -16,7 +16,6 @@ import androidx.core.app.SharedElementCallback
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
@ -33,7 +32,6 @@ import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectFor
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.database.model.StoryViewState
import org.thoughtcrime.securesms.main.Material3OnScrollHelperBinder import org.thoughtcrime.securesms.main.Material3OnScrollHelperBinder
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity
import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.permissions.Permissions
@ -273,8 +271,8 @@ class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_l
storyThumbTextModel = text, storyThumbTextModel = text,
storyThumbUri = image, storyThumbUri = image,
storyThumbBlur = blur, storyThumbBlur = blur,
recipientIds = viewModel.getRecipientIds(model.data.isHidden, true), recipientIds = viewModel.getRecipientIds(model.data.isHidden, false),
isUnviewedOnly = model.data.storyViewState == StoryViewState.UNVIEWED, isUnviewedOnly = false,
isFromInfoContextMenuAction = isFromInfoContextMenuAction isFromInfoContextMenuAction = isFromInfoContextMenuAction
) )
), ),
@ -288,10 +286,7 @@ class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_l
} }
private fun handleHideStory(model: StoriesLandingItem.Model) { private fun handleHideStory(model: StoriesLandingItem.Model) {
MaterialAlertDialogBuilder(requireContext(), R.style.ThemeOverlay_Signal_MaterialAlertDialog) StoryDialogs.hideStory(requireContext(), model.data.storyRecipient.getShortDisplayName(requireContext())) {
.setTitle(R.string.StoriesLandingFragment__hide_story)
.setMessage(getString(R.string.StoriesLandingFragment__new_story_updates, model.data.storyRecipient.getShortDisplayName(requireContext())))
.setPositiveButton(R.string.StoriesLandingFragment__hide) { _, _ ->
viewModel.setHideStory(model.data.storyRecipient, true).subscribe { viewModel.setHideStory(model.data.storyRecipient, true).subscribe {
Snackbar.make(cameraFab, R.string.StoriesLandingFragment__story_hidden, Snackbar.LENGTH_SHORT) Snackbar.make(cameraFab, R.string.StoriesLandingFragment__story_hidden, Snackbar.LENGTH_SHORT)
.setAnchorView(cameraFab) .setAnchorView(cameraFab)
@ -299,8 +294,6 @@ class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_l
.show() .show()
} }
} }
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.show()
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {

Wyświetl plik

@ -66,6 +66,10 @@ class StoryViewerFragment :
lifecycleDisposable.bindTo(viewLifecycleOwner) lifecycleDisposable.bindTo(viewLifecycleOwner)
lifecycleDisposable += viewModel.state.observeOn(AndroidSchedulers.mainThread()).subscribe { state -> lifecycleDisposable += viewModel.state.observeOn(AndroidSchedulers.mainThread()).subscribe { state ->
if (state.noPosts) {
requireActivity().finish()
}
adapter.setPages(state.pages) adapter.setPages(state.pages)
if (state.pages.isNotEmpty() && storyPager.currentItem != state.page) { if (state.pages.isNotEmpty() && storyPager.currentItem != state.page) {
pagerOnPageSelectedLock = true pagerOnPageSelectedLock = true
@ -96,6 +100,17 @@ class StoryViewerFragment :
storyCrossfader.alpha = 0f storyCrossfader.alpha = 0f
} }
} }
if (savedInstanceState != null && savedInstanceState.containsKey(HIDDEN)) {
val ids: List<RecipientId> = savedInstanceState.getParcelableArrayList(HIDDEN)!!
viewModel.addHiddenAndRefresh(ids.toSet())
} else {
viewModel.refresh()
}
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putParcelableArrayList(HIDDEN, ArrayList(viewModel.getHidden()))
} }
override fun onResume() { override fun onResume() {
@ -119,7 +134,7 @@ class StoryViewerFragment :
} }
override fun onStoryHidden(recipientId: RecipientId) { override fun onStoryHidden(recipientId: RecipientId) {
viewModel.onRecipientHidden() viewModel.addHiddenAndRefresh(setOf(recipientId))
} }
override fun onReadyToAnimate() { override fun onReadyToAnimate() {
@ -150,6 +165,7 @@ class StoryViewerFragment :
companion object { companion object {
private const val ARGS = "args" private const val ARGS = "args"
private const val HIDDEN = "hidden"
fun create(storyViewerArgs: StoryViewerArgs): Fragment { fun create(storyViewerArgs: StoryViewerArgs): Fragment {
return StoryViewerFragment().apply { return StoryViewerFragment().apply {

Wyświetl plik

@ -17,11 +17,12 @@ class StoryViewerPagerAdapter(
private val isFromInfoContextMenuAction: Boolean private val isFromInfoContextMenuAction: Boolean
) : FragmentStateAdapter(fragment) { ) : FragmentStateAdapter(fragment) {
private var pages: List<RecipientId> = emptyList() private val pages: MutableList<RecipientId> = mutableListOf()
fun setPages(newPages: List<RecipientId>) { fun setPages(newPages: List<RecipientId>) {
val oldPages = pages val oldPages = ArrayList(pages)
pages = newPages pages.clear()
pages.addAll(newPages)
val callback = Callback(oldPages, pages) val callback = Callback(oldPages, pages)
DiffUtil.calculateDiff(callback).dispatchUpdatesTo(this) DiffUtil.calculateDiff(callback).dispatchUpdatesTo(this)
@ -34,6 +35,10 @@ class StoryViewerPagerAdapter(
override fun getItemCount(): Int = pages.size override fun getItemCount(): Int = pages.size
override fun getItemId(position: Int): Long {
return pages[position].toLong()
}
override fun createFragment(position: Int): Fragment { override fun createFragment(position: Int): Fragment {
return StoryViewerPageFragment.create(pages[position], initialStoryId, isFromNotification, groupReplyStartPosition, isUnviewedOnly, isOutgoingOnly, isFromInfoContextMenuAction) return StoryViewerPageFragment.create(pages[position], initialStoryId, isFromNotification, groupReplyStartPosition, isUnviewedOnly, isOutgoingOnly, isFromInfoContextMenuAction)
} }

Wyświetl plik

@ -13,7 +13,8 @@ data class StoryViewerState(
val crossfadeSource: CrossfadeSource, val crossfadeSource: CrossfadeSource,
val crossfadeTarget: CrossfadeTarget? = null, val crossfadeTarget: CrossfadeTarget? = null,
val loadState: LoadState = LoadState(), val loadState: LoadState = LoadState(),
val skipCrossfade: Boolean = false val skipCrossfade: Boolean = false,
val noPosts: Boolean = false
) { ) {
sealed class CrossfadeSource { sealed class CrossfadeSource {
object None : CrossfadeSource() object None : CrossfadeSource()

Wyświetl plik

@ -39,6 +39,8 @@ class StoryViewerViewModel(
val stateSnapshot: StoryViewerState get() = store.state val stateSnapshot: StoryViewerState get() = store.state
val state: Flowable<StoryViewerState> = store.stateFlowable val state: Flowable<StoryViewerState> = store.stateFlowable
private val hidden = mutableSetOf<RecipientId>()
private val scrollStatePublisher: MutableLiveData<Boolean> = MutableLiveData(false) private val scrollStatePublisher: MutableLiveData<Boolean> = MutableLiveData(false)
val isScrolling: LiveData<Boolean> = scrollStatePublisher val isScrolling: LiveData<Boolean> = scrollStatePublisher
@ -53,10 +55,13 @@ class StoryViewerViewModel(
val isChildScrolling: Observable<Boolean> = childScrollStatePublisher.distinctUntilChanged() val isChildScrolling: Observable<Boolean> = childScrollStatePublisher.distinctUntilChanged()
init { fun addHiddenAndRefresh(hidden: Set<RecipientId>) {
this.hidden.addAll(hidden)
refresh() refresh()
} }
fun getHidden(): Set<RecipientId> = hidden
fun setCrossfadeTarget(messageRecord: MmsMessageRecord) { fun setCrossfadeTarget(messageRecord: MmsMessageRecord) {
store.update { store.update {
it.copy(crossfadeTarget = StoryViewerState.CrossfadeTarget.Record(messageRecord)) it.copy(crossfadeTarget = StoryViewerState.CrossfadeTarget.Record(messageRecord))
@ -85,7 +90,7 @@ class StoryViewerViewModel(
private fun getStories(): Single<List<RecipientId>> { private fun getStories(): Single<List<RecipientId>> {
return if (storyViewerArgs.recipientIds.isNotEmpty()) { return if (storyViewerArgs.recipientIds.isNotEmpty()) {
Single.just(storyViewerArgs.recipientIds) Single.just(storyViewerArgs.recipientIds - hidden)
} else { } else {
repository.getStories( repository.getStories(
hiddenStories = storyViewerArgs.isInHiddenStoryMode, hiddenStories = storyViewerArgs.isInHiddenStoryMode,
@ -95,7 +100,7 @@ class StoryViewerViewModel(
} }
} }
private fun refresh() { fun refresh() {
disposables.clear() disposables.clear()
disposables += repository.getFirstStory(storyViewerArgs.recipientId, storyViewerArgs.isUnviewedOnly, storyViewerArgs.storyId).subscribe { record -> disposables += repository.getFirstStory(storyViewerArgs.recipientId, storyViewerArgs.isUnviewedOnly, storyViewerArgs.storyId).subscribe { record ->
store.update { store.update {
@ -119,7 +124,7 @@ class StoryViewerViewModel(
} else { } else {
it.page it.page
} }
updatePages(it.copy(pages = recipientIds), page) updatePages(it.copy(pages = recipientIds), page).copy(noPosts = recipientIds.isEmpty())
} }
} }
disposables += state disposables += state
@ -167,10 +172,6 @@ class StoryViewerViewModel(
} }
} }
fun onRecipientHidden() {
refresh()
}
private fun updatePages(state: StoryViewerState, page: Int): StoryViewerState { private fun updatePages(state: StoryViewerState, page: Int): StoryViewerState {
val newPage = resolvePage(page, state.pages) val newPage = resolvePage(page, state.pages)
val prevPage = if (newPage == state.page) { val prevPage = if (newPage == state.page) {

Wyświetl plik

@ -59,6 +59,7 @@ import org.thoughtcrime.securesms.stories.StoryFirstTimeNavigationView
import org.thoughtcrime.securesms.stories.StorySlateView import org.thoughtcrime.securesms.stories.StorySlateView
import org.thoughtcrime.securesms.stories.StoryVolumeOverlayView import org.thoughtcrime.securesms.stories.StoryVolumeOverlayView
import org.thoughtcrime.securesms.stories.dialogs.StoryContextMenu import org.thoughtcrime.securesms.stories.dialogs.StoryContextMenu
import org.thoughtcrime.securesms.stories.dialogs.StoryDialogs
import org.thoughtcrime.securesms.stories.viewer.StoryViewerViewModel import org.thoughtcrime.securesms.stories.viewer.StoryViewerViewModel
import org.thoughtcrime.securesms.stories.viewer.StoryVolumeViewModel import org.thoughtcrime.securesms.stories.viewer.StoryVolumeViewModel
import org.thoughtcrime.securesms.stories.viewer.info.StoryInfoBottomSheetDialogFragment import org.thoughtcrime.securesms.stories.viewer.info.StoryInfoBottomSheetDialogFragment
@ -979,9 +980,12 @@ class StoryViewerPageFragment :
startActivity(ConversationIntents.createBuilder(requireContext(), storyRecipientId, -1L).build()) startActivity(ConversationIntents.createBuilder(requireContext(), storyRecipientId, -1L).build())
}, },
onHide = { onHide = {
viewModel.setIsDisplayingHideDialog(true)
StoryDialogs.hideStory(requireContext(), Recipient.resolved(storyRecipientId).getDisplayName(requireContext()), { viewModel.setIsDisplayingHideDialog(true) }) {
lifecycleDisposable += viewModel.hideStory().subscribe { lifecycleDisposable += viewModel.hideStory().subscribe {
callback.onStoryHidden(storyRecipientId) callback.onStoryHidden(storyRecipientId)
} }
}
}, },
onShare = { onShare = {
StoryContextMenu.share(this, it.conversationMessage.messageRecord as MediaMmsMessageRecord) StoryContextMenu.share(this, it.conversationMessage.messageRecord as MediaMmsMessageRecord)

Wyświetl plik

@ -222,6 +222,10 @@ class StoryViewerPageViewModel(
storyViewerPlaybackStore.update { it.copy(isDisplayingDeleteDialog = isDisplayingDeleteDialog) } storyViewerPlaybackStore.update { it.copy(isDisplayingDeleteDialog = isDisplayingDeleteDialog) }
} }
fun setIsDisplayingHideDialog(isDisplayingHideDialog: Boolean) {
storyViewerPlaybackStore.update { it.copy(isDisplayingHideDialog = isDisplayingHideDialog) }
}
fun setIsDisplayingViewsAndRepliesDialog(isDisplayingViewsAndRepliesDialog: Boolean) { fun setIsDisplayingViewsAndRepliesDialog(isDisplayingViewsAndRepliesDialog: Boolean) {
storyViewerPlaybackStore.update { it.copy(isDisplayingViewsAndRepliesDialog = isDisplayingViewsAndRepliesDialog) } storyViewerPlaybackStore.update { it.copy(isDisplayingViewsAndRepliesDialog = isDisplayingViewsAndRepliesDialog) }
} }

Wyświetl plik

@ -5,6 +5,7 @@ data class StoryViewerPlaybackState(
val isUserTouching: Boolean = false, val isUserTouching: Boolean = false,
val isDisplayingForwardDialog: Boolean = false, val isDisplayingForwardDialog: Boolean = false,
val isDisplayingDeleteDialog: Boolean = false, val isDisplayingDeleteDialog: Boolean = false,
val isDisplayingHideDialog: Boolean = false,
val isDisplayingContextMenu: Boolean = false, val isDisplayingContextMenu: Boolean = false,
val isDisplayingViewsAndRepliesDialog: Boolean = false, val isDisplayingViewsAndRepliesDialog: Boolean = false,
val isDisplayingDirectReplyDialog: Boolean = false, val isDisplayingDirectReplyDialog: Boolean = false,
@ -44,5 +45,6 @@ data class StoryViewerPlaybackState(
isRunningSharedElementAnimation || isRunningSharedElementAnimation ||
isDisplayingFirstTimeNavigation || isDisplayingFirstTimeNavigation ||
isDisplayingInfoDialog || isDisplayingInfoDialog ||
isUserScaling isUserScaling ||
isDisplayingHideDialog
} }

Wyświetl plik

@ -63,6 +63,7 @@ class StoryViewerViewModelTest {
), ),
repository repository
) )
testSubject.refresh()
testScheduler.triggerActions() testScheduler.triggerActions()
// THEN // THEN
@ -107,6 +108,7 @@ class StoryViewerViewModelTest {
), ),
repository repository
) )
testSubject.refresh()
testScheduler.triggerActions() testScheduler.triggerActions()
// WHEN // WHEN
@ -133,6 +135,7 @@ class StoryViewerViewModelTest {
), ),
repository repository
) )
testSubject.refresh()
testScheduler.triggerActions() testScheduler.triggerActions()
// WHEN // WHEN
@ -159,6 +162,7 @@ class StoryViewerViewModelTest {
), ),
repository repository
) )
testSubject.refresh()
testScheduler.triggerActions() testScheduler.triggerActions()
// WHEN // WHEN
@ -185,6 +189,7 @@ class StoryViewerViewModelTest {
), ),
repository repository
) )
testSubject.refresh()
testScheduler.triggerActions() testScheduler.triggerActions()
// WHEN // WHEN