From 63e48efdfef0756c52044f8b194d0bd28dbbc082 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 16 Mar 2022 14:22:11 -0300 Subject: [PATCH] Allow user to launch directly to a specific story, fix story chronology. --- .../securesms/database/MessageDatabase.java | 2 +- .../securesms/database/MmsDatabase.java | 6 +++--- .../securesms/database/SmsDatabase.java | 2 +- .../securesms/stories/my/MyStoriesFragment.kt | 9 ++++++-- .../securesms/stories/my/MyStoriesItem.kt | 1 + .../stories/my/MyStoriesRepository.kt | 2 +- .../stories/viewer/StoryViewerActivity.kt | 11 +++++++--- .../stories/viewer/StoryViewerFragment.kt | 9 ++++++-- .../stories/viewer/StoryViewerPagerAdapter.kt | 4 ++-- .../stories/viewer/StoryViewerRepository.kt | 2 +- .../viewer/page/StoryViewerPageFragment.kt | 9 ++++++-- .../viewer/page/StoryViewerPageRepository.kt | 2 +- .../viewer/page/StoryViewerPageViewModel.kt | 21 +++++++++++++------ 13 files changed, 55 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java index 6050d55318..20f98ce88b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageDatabase.java @@ -183,7 +183,7 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns public abstract boolean isStory(long messageId); public abstract @NonNull Reader getOutgoingStoriesTo(@NonNull RecipientId recipientId); - public abstract @NonNull Reader getAllOutgoingStories(); + public abstract @NonNull Reader getAllOutgoingStories(boolean reverse); public abstract @NonNull Reader getAllStories(); public abstract @NonNull List getAllStoriesRecipientsList(); public abstract @NonNull Reader getAllStoriesFor(@NonNull RecipientId recipientId); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java index 447cd978ae..4eba8ed5fb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -571,10 +571,10 @@ public class MmsDatabase extends MessageDatabase { } @Override - public @NonNull MessageDatabase.Reader getAllOutgoingStories() { + public @NonNull MessageDatabase.Reader getAllOutgoingStories(boolean reverse) { String where = IS_STORY_CLAUSE + " AND (" + getOutgoingTypeClause() + ")"; - return new Reader(rawQuery(where, null, true, -1L)); + return new Reader(rawQuery(where, null, reverse, -1L)); } @Override @@ -587,7 +587,7 @@ public class MmsDatabase extends MessageDatabase { long threadId = SignalDatabase.threads().getThreadIdIfExistsFor(recipientId); String where = IS_STORY_CLAUSE + " AND " + THREAD_ID_WHERE; String[] whereArgs = SqlUtil.buildArgs(threadId); - Cursor cursor = rawQuery(where, whereArgs, true, -1L); + Cursor cursor = rawQuery(where, whereArgs, false, -1L); return new Reader(cursor); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index dfc6eddc95..31d69b66a6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -1395,7 +1395,7 @@ public class SmsDatabase extends MessageDatabase { } @Override - public @NonNull MessageDatabase.Reader getAllOutgoingStories() { + public @NonNull MessageDatabase.Reader getAllOutgoingStories(boolean reverse) { throw new UnsupportedOperationException(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/my/MyStoriesFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/my/MyStoriesFragment.kt index 18e918e1cf..556c8dab51 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/my/MyStoriesFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/my/MyStoriesFragment.kt @@ -61,8 +61,13 @@ class MyStoriesFragment : DSLSettingsFragment( lifecycleDisposable += viewModel.resend(it.distributionStory.messageRecord).subscribe() Toast.makeText(requireContext(), R.string.message_recipients_list_item__resend, Toast.LENGTH_SHORT).show() } else { - // TODO [stories] pass in something more specific to start with the correct progress - startActivity(StoryViewerActivity.createIntent(requireContext(), Recipient.self().id)) + val recipientId = if (it.distributionStory.messageRecord.recipient.isGroup) { + it.distributionStory.messageRecord.recipient.id + } else { + Recipient.self().id + } + + startActivity(StoryViewerActivity.createIntent(requireContext(), recipientId, conversationMessage.messageRecord.id)) } }, onSaveClick = { diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/my/MyStoriesItem.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/my/MyStoriesItem.kt index bfdacf4854..bce52e526e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/my/MyStoriesItem.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/my/MyStoriesItem.kt @@ -82,6 +82,7 @@ object MyStoriesItem { private val errorIndicator: View = itemView.findViewById(R.id.error_indicator) override fun bind(model: Model) { + storyPreview.isClickable = false itemView.setOnClickListener { model.onClick(model) } downloadTarget.setOnClickListener { model.onSaveClick(model) } moreTarget.setOnClickListener { showContextMenu(model) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/my/MyStoriesRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/my/MyStoriesRepository.kt index c285079d5c..763a9bf9f9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/my/MyStoriesRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/my/MyStoriesRepository.kt @@ -26,7 +26,7 @@ class MyStoriesRepository(context: Context) { return Observable.create { emitter -> fun refresh() { val storiesMap = mutableMapOf>() - SignalDatabase.mms.allOutgoingStories.use { + SignalDatabase.mms.getAllOutgoingStories(true).use { while (it.next != null) { val messageRecord = it.current val currentList = storiesMap[messageRecord.recipient] ?: emptyList() diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerActivity.kt index 6bad3b2dd0..50465342f7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerActivity.kt @@ -21,17 +21,22 @@ class StoryViewerActivity : PassphraseRequiredActivity() { if (savedInstanceState == null) { supportFragmentManager.beginTransaction() - .replace(R.id.fragment_container, StoryViewerFragment.create(intent.getParcelableExtra(ARG_START_RECIPIENT_ID)!!)) + .replace(R.id.fragment_container, StoryViewerFragment.create( + intent.getParcelableExtra(ARG_START_RECIPIENT_ID)!!, + intent.getLongExtra(ARG_START_STORY_ID, -1L) + )) .commit() } } companion object { private const val ARG_START_RECIPIENT_ID = "start.recipient.id" + private const val ARG_START_STORY_ID = "start.story.id" - fun createIntent(context: Context, storyId: RecipientId): Intent { + fun createIntent(context: Context, recipientId: RecipientId, storyId: Long = -1L): Intent { return Intent(context, StoryViewerActivity::class.java) - .putExtra(ARG_START_RECIPIENT_ID, storyId) + .putExtra(ARG_START_RECIPIENT_ID, recipientId) + .putExtra(ARG_START_STORY_ID, storyId) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerFragment.kt index f3f1cdeb82..de0447c7b2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerFragment.kt @@ -27,10 +27,13 @@ class StoryViewerFragment : Fragment(R.layout.stories_viewer_fragment), StoryVie private val storyRecipientId: RecipientId get() = requireArguments().getParcelable(ARG_START_RECIPIENT_ID)!! + private val storyId: Long + get() = requireArguments().getLong(ARG_START_STORY_ID, -1L) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { storyPager = view.findViewById(R.id.story_item_pager) - val adapter = StoryViewerPagerAdapter(this) + val adapter = StoryViewerPagerAdapter(this, storyId) storyPager.adapter = adapter viewModel.state.observe(viewLifecycleOwner) { state -> @@ -77,11 +80,13 @@ class StoryViewerFragment : Fragment(R.layout.stories_viewer_fragment), StoryVie companion object { private const val ARG_START_RECIPIENT_ID = "start.recipient.id" + private const val ARG_START_STORY_ID = "start.story.id" - fun create(storyRecipientId: RecipientId): Fragment { + fun create(storyRecipientId: RecipientId, storyId: Long): Fragment { return StoryViewerFragment().apply { arguments = Bundle().apply { putParcelable(ARG_START_RECIPIENT_ID, storyRecipientId) + putLong(ARG_START_STORY_ID, storyId) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerPagerAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerPagerAdapter.kt index 6052048221..bd8883a6e6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerPagerAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerPagerAdapter.kt @@ -6,7 +6,7 @@ import androidx.viewpager2.adapter.FragmentStateAdapter import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.stories.viewer.page.StoryViewerPageFragment -class StoryViewerPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) { +class StoryViewerPagerAdapter(fragment: Fragment, private val initialStoryId: Long) : FragmentStateAdapter(fragment) { private var pages: List = emptyList() @@ -21,7 +21,7 @@ class StoryViewerPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragmen override fun getItemCount(): Int = pages.size override fun createFragment(position: Int): Fragment { - return StoryViewerPageFragment.create(pages[position]) + return StoryViewerPageFragment.create(pages[position], initialStoryId) } private class Callback( diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerRepository.kt index 1baf08776d..259caf6fb7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/StoryViewerRepository.kt @@ -19,7 +19,7 @@ class StoryViewerRepository { val myStory: RecipientId = SignalDatabase.recipients.getOrInsertFromDistributionListId(DistributionListId.MY_STORY) - val myStoriesCount = SignalDatabase.mms.allOutgoingStories.use { + val myStoriesCount = SignalDatabase.mms.getAllOutgoingStories(true).use { var count = 0 while (it.next != null) { if (!it.current.recipient.isGroup) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt index c37360b228..9b630fb24a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt @@ -77,7 +77,7 @@ class StoryViewerPageFragment : private val viewModel: StoryViewerPageViewModel by viewModels( factoryProducer = { - StoryViewerPageViewModel.Factory(storyRecipientId, StoryViewerPageRepository(requireContext())) + StoryViewerPageViewModel.Factory(storyRecipientId, initialStoryId, StoryViewerPageRepository(requireContext())) } ) @@ -93,6 +93,9 @@ class StoryViewerPageFragment : private val storyRecipientId: RecipientId get() = requireArguments().getParcelable(ARG_STORY_RECIPIENT_ID)!! + private val initialStoryId: Long + get() = requireArguments().getLong(ARG_STORY_ID, -1L) + @SuppressLint("ClickableViewAccessibility") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { callback = requireListener() @@ -624,11 +627,13 @@ class StoryViewerPageFragment : companion object { private const val ARG_STORY_RECIPIENT_ID = "arg.story.recipient.id" + private const val ARG_STORY_ID = "arg.story.id" - fun create(recipientId: RecipientId): Fragment { + fun create(recipientId: RecipientId, initialStoryId: Long): Fragment { return StoryViewerPageFragment().apply { arguments = Bundle().apply { putParcelable(ARG_STORY_RECIPIENT_ID, recipientId) + putLong(ARG_STORY_ID, initialStoryId) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageRepository.kt index b9d79ff25a..d58e23f516 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageRepository.kt @@ -31,7 +31,7 @@ class StoryViewerPageRepository(context: Context) { fun refresh() { val stories = if (recipient.isMyStory) { - SignalDatabase.mms.allOutgoingStories + SignalDatabase.mms.getAllOutgoingStories(false) } else { SignalDatabase.mms.getAllStoriesFor(recipientId) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageViewModel.kt index 6294b5feb2..5ee72b62fa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageViewModel.kt @@ -20,6 +20,7 @@ import kotlin.math.min */ class StoryViewerPageViewModel( private val recipientId: RecipientId, + private val initialStoryId: Long, private val repository: StoryViewerPageRepository ) : ViewModel() { @@ -44,10 +45,18 @@ class StoryViewerPageViewModel( fun refresh() { disposables.clear() disposables += repository.getStoryPostsFor(recipientId).subscribe { posts -> - store.update { - it.copy( + store.update { state -> + val startIndex = if (state.posts.isEmpty() && initialStoryId > 0) { + val initialIndex = posts.indexOfFirst { it.id == initialStoryId } + initialIndex.takeIf { it > -1 } ?: state.selectedPostIndex + } else { + state.selectedPostIndex + } + + state.copy( posts = posts, - replyState = resolveSwipeToReplyState(it) + replyState = resolveSwipeToReplyState(state, startIndex), + selectedPostIndex = startIndex ) } } @@ -162,7 +171,7 @@ class StoryViewerPageViewModel( storyViewerPlaybackStore.update { it.copy(isDisplayingLinkPreviewTooltip = isDisplayingLinkPreviewTooltip) } } - private fun resolveSwipeToReplyState(state: StoryViewerPageState, index: Int = state.selectedPostIndex): StoryViewerPageState.ReplyState { + private fun resolveSwipeToReplyState(state: StoryViewerPageState, index: Int): StoryViewerPageState.ReplyState { if (index !in state.posts.indices) { return StoryViewerPageState.ReplyState.NONE } @@ -182,9 +191,9 @@ class StoryViewerPageViewModel( return store.state.posts.getOrNull(index) } - class Factory(private val recipientId: RecipientId, private val repository: StoryViewerPageRepository) : ViewModelProvider.Factory { + class Factory(private val recipientId: RecipientId, private val initialStoryId: Long, private val repository: StoryViewerPageRepository) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { - return modelClass.cast(StoryViewerPageViewModel(recipientId, repository)) as T + return modelClass.cast(StoryViewerPageViewModel(recipientId, initialStoryId, repository)) as T } } }