From c82080acf42e6a33abc78579029c126faa5a6e95 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Thu, 8 Jul 2021 16:51:46 -0400 Subject: [PATCH] restore marking feed stories as seen --- .../fragments/StoryViewerFragment.kt | 168 +++++++++--------- .../fragments/main/FeedFragment.java | 6 +- .../repositories/responses/stories/Story.kt | 2 +- .../viewmodels/StoryFragmentViewModel.kt | 38 +++- 4 files changed, 116 insertions(+), 98 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt index 40362e0c..897f4746 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt +++ b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt @@ -17,10 +17,9 @@ import androidx.appcompat.view.ContextThemeWrapper import androidx.appcompat.widget.PopupMenu import androidx.core.view.GestureDetectorCompat import androidx.fragment.app.Fragment -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Observer -import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.navigation.NavController @@ -31,7 +30,6 @@ import awais.instagrabber.R import awais.instagrabber.adapters.StoriesAdapter import awais.instagrabber.customviews.helpers.SwipeGestureListener import awais.instagrabber.databinding.FragmentStoryViewerBinding -import awais.instagrabber.fragments.main.ProfileFragment import awais.instagrabber.fragments.settings.PreferenceKeys import awais.instagrabber.interfaces.SwipeEvent import awais.instagrabber.models.Resource @@ -41,9 +39,7 @@ import awais.instagrabber.models.enums.StoryPaginationType import awais.instagrabber.repositories.requests.StoryViewerOptions import awais.instagrabber.repositories.responses.directmessages.RankedRecipient import awais.instagrabber.repositories.responses.stories.* -import awais.instagrabber.utils.Constants import awais.instagrabber.utils.DownloadUtils.download -import awais.instagrabber.utils.TextUtils.epochSecondToString import awais.instagrabber.utils.ResponseBodyUtils import awais.instagrabber.utils.Utils import awais.instagrabber.utils.extensions.TAG @@ -79,7 +75,6 @@ class StoryViewerFragment : Fragment() { private var gestureDetector: GestureDetectorCompat? = null private val storiesRepository: StoriesRepository? = null private val mediaRepository: MediaRepository? = null - private var live: Broadcast? = null private var menuProfile: MenuItem? = null private var profileVisible: Boolean = false private var player: SimpleExoPlayer? = null @@ -218,8 +213,8 @@ class StoryViewerFragment : Fragment() { storiesViewModel.setMedia(position) } binding.storiesList.adapter = storiesAdapter - storiesViewModel.getCurrentStory().observe(fragmentActivity, { - if (it?.items != null) { + storiesViewModel.getCurrentStory().observe(viewLifecycleOwner, { + if (it?.items != null && it.items.size > 1) { val storyMedias = it.items.toMutableList() val newItem = storyMedias.get(0) newItem.isCurrentSlide = true @@ -231,23 +226,24 @@ class StoryViewerFragment : Fragment() { else View.GONE } else { + if (it?.items != null) storiesViewModel.setMedia(0) binding.listToggle.isEnabled = false binding.storiesList.visibility = View.GONE } }) - storiesViewModel.getDate().observe(fragmentActivity, { + storiesViewModel.getDate().observe(viewLifecycleOwner, { val actionBar = fragmentActivity.supportActionBar if (actionBar != null && it != null) actionBar.subtitle = it }) - storiesViewModel.getTitle().observe(fragmentActivity, { + storiesViewModel.getTitle().observe(viewLifecycleOwner, { val actionBar = fragmentActivity.supportActionBar if (actionBar != null && it != null) actionBar.title = it }) - storiesViewModel.getCurrentMedia().observe(fragmentActivity, { refreshStory(it) }) - storiesViewModel.getCurrentIndex().observe(fragmentActivity, { + storiesViewModel.getCurrentMedia().observe(viewLifecycleOwner, { refreshStory(it) }) + storiesViewModel.getCurrentIndex().observe(viewLifecycleOwner, { storiesAdapter!!.paginate(it) }) - storiesViewModel.getOptions().observe(fragmentActivity, { + storiesViewModel.getOptions().observe(viewLifecycleOwner, { binding.stickers.isEnabled = it.first.size > 0 }) } @@ -267,65 +263,28 @@ class StoryViewerFragment : Fragment() { @SuppressLint("ClickableViewAccessibility") private fun setupListeners() { - var liveModels: LiveData?>? = null if (currentFeedStoryIndex >= 0) { val type = options!!.type when (type) { StoryViewerOptions.Type.HIGHLIGHT -> { storiesViewModel.fetchHighlights(options!!.id) - liveModels = storiesViewModel.getHighlights() + storiesViewModel.highlights.observe(viewLifecycleOwner) { + setupMultipage(it) + } } StoryViewerOptions.Type.FEED_STORY_POSITION -> { val feedStoriesViewModel = listViewModel as FeedStoriesViewModel? - liveModels = feedStoriesViewModel!!.list + setupMultipage(feedStoriesViewModel!!.list.value) } StoryViewerOptions.Type.STORY_ARCHIVE -> { val archivesViewModel = listViewModel as ArchivesViewModel? - liveModels = archivesViewModel!!.list + setupMultipage(archivesViewModel!!.list.value) } StoryViewerOptions.Type.USER -> { resetView() } } } - if (liveModels != null) liveModels.observe(viewLifecycleOwner, { models -> - storiesViewModel.getPagination().observe(fragmentActivity, { - if (models != null) { - when (it) { - StoryPaginationType.FORWARD -> { - if (currentFeedStoryIndex == models.size - 1) - Toast.makeText( - context, - R.string.no_more_stories, - Toast.LENGTH_SHORT - ).show() - else paginateStories(false, currentFeedStoryIndex == models.size - 2) - } - StoryPaginationType.BACKWARD -> { - if (currentFeedStoryIndex == 0) - Toast.makeText( - context, - R.string.no_more_stories, - Toast.LENGTH_SHORT - ).show() - else paginateStories(true, false) - } - StoryPaginationType.ERROR -> { - Toast.makeText( - context, - R.string.downloader_unknown_error, - Toast.LENGTH_SHORT - ).show() - } - } - } - }) - if (models != null && !models.isEmpty()) { - binding.btnBackward.isEnabled = currentFeedStoryIndex != 0 - binding.btnForward.isEnabled = currentFeedStoryIndex != models.size - 1 - resetView() - } - }) val context = context ?: return swipeEvent = SwipeEvent { isRightSwipe: Boolean -> @@ -358,9 +317,46 @@ class StoryViewerFragment : Fragment() { binding.imageViewer.setTapListener(simpleOnGestureListener) } + private fun setupMultipage(models: List?) { + if (models == null) return + storiesViewModel.getPagination().observe(viewLifecycleOwner, { + when (it) { + StoryPaginationType.FORWARD -> { + if (currentFeedStoryIndex == models.size - 1) + Toast.makeText( + context, + R.string.no_more_stories, + Toast.LENGTH_SHORT + ).show() + else paginateStories(false, currentFeedStoryIndex == models.size - 2) + } + StoryPaginationType.BACKWARD -> { + if (currentFeedStoryIndex == 0) + Toast.makeText( + context, + R.string.no_more_stories, + Toast.LENGTH_SHORT + ).show() + else paginateStories(true, false) + } + StoryPaginationType.ERROR -> { + Toast.makeText( + context, + R.string.downloader_unknown_error, + Toast.LENGTH_SHORT + ).show() + } + } + }) + if (!models.isEmpty()) { + binding.btnBackward.isEnabled = currentFeedStoryIndex != 0 + binding.btnForward.isEnabled = currentFeedStoryIndex != models.size - 1 + resetView() + } + } + private fun resetView() { val context = context ?: return - live = null if (menuProfile != null) menuProfile!!.isVisible = false binding.imageViewer.controller = null releasePlayer() @@ -368,7 +364,7 @@ class StoryViewerFragment : Fragment() { var fetchOptions: StoryViewerOptions? = null when (type) { StoryViewerOptions.Type.HIGHLIGHT -> { - val models = storiesViewModel.getHighlights().value + val models = storiesViewModel.highlights.value if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size || currentFeedStoryIndex < 0) { Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show() return @@ -379,10 +375,15 @@ class StoryViewerFragment : Fragment() { val feedStoriesViewModel = listViewModel as FeedStoriesViewModel? val models = feedStoriesViewModel!!.list.value if (models == null || currentFeedStoryIndex >= models.size || currentFeedStoryIndex < 0) return - val (_, _, _, _, user, _, _, _, _, _, _, broadcast) = models[currentFeedStoryIndex] - currentStoryUsername = user!!.username - fetchOptions = StoryViewerOptions.forUser(user.pk, currentStoryUsername) - live = broadcast + val userStory = models[currentFeedStoryIndex] + currentStoryUsername = userStory.user!!.username + fetchOptions = StoryViewerOptions.forUser(userStory.user.pk, currentStoryUsername) + val live = userStory.broadcast + if (live != null) { + storiesViewModel.setStory(userStory) + refreshLive(live) + return + } } StoryViewerOptions.Type.STORY_ARCHIVE -> { val archivesViewModel = listViewModel as ArchivesViewModel? @@ -405,11 +406,7 @@ class StoryViewerFragment : Fragment() { storiesViewModel.fetchSingleMedia(options!!.id) return } - if (live != null) { - refreshLive() - return - } - storiesViewModel.fetchStory(fetchOptions).observe(fragmentActivity, { + storiesViewModel.fetchStory(fetchOptions).observe(viewLifecycleOwner, { if (it.status == Resource.Status.ERROR) { Toast.makeText(context, "Error: " + it.message, Toast.LENGTH_SHORT).show() } @@ -417,18 +414,14 @@ class StoryViewerFragment : Fragment() { } @Synchronized - private fun refreshLive() { + private fun refreshLive(live: Broadcast) { + binding.btnDownload.isEnabled = false + binding.stickers.isEnabled = false + binding.listToggle.isEnabled = false + binding.btnShare.isEnabled = false + binding.btnReply.isEnabled = false releasePlayer() - setupLive(live!!.dashPlaybackUrl ?: live!!.dashAbrPlaybackUrl ?: return) - val actionBar = fragmentActivity.supportActionBar - actionBarSubtitle = epochSecondToString(live!!.publishedTime!!) - if (actionBar != null) { - try { - actionBar.setSubtitle(actionBarSubtitle) - } catch (e: Exception) { - Log.e(TAG, "refreshLive: ", e) - } - } + setupLive(live.dashPlaybackUrl ?: live.dashAbrPlaybackUrl ?: return) } @Synchronized @@ -447,14 +440,19 @@ class StoryViewerFragment : Fragment() { binding.btnReply.isEnabled = currentStory.canReply if (itemType === MediaItemType.MEDIA_TYPE_VIDEO) setupVideo(url) else setupImage(url) -// if (Utils.settingsHelper.getBoolean(MARK_AS_SEEN)) storiesRepository!!.seen( -// csrfToken, -// userId, -// deviceId, -// currentStory!!.id!!, -// currentStory!!.takenAt, -// System.currentTimeMillis() / 1000 -// ) + if (options!!.type == StoryViewerOptions.Type.FEED_STORY_POSITION + && Utils.settingsHelper.getBoolean(PreferenceKeys.MARK_AS_SEEN)) { + val feedStoriesViewModel = listViewModel as FeedStoriesViewModel? + storiesViewModel.markAsSeen(currentStory).observe(viewLifecycleOwner) { m -> + if (m.status == Resource.Status.SUCCESS && m.data != null) { + val liveModels: MutableLiveData> = feedStoriesViewModel!!.list + val models = liveModels.value + val modelsCopy: MutableList = models!!.toMutableList() + modelsCopy.set(currentFeedStoryIndex, m.data) + liveModels.postValue(modelsCopy) + } + } + } } private fun downloadStory() { diff --git a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java index 593ea96f..c7f6d0b7 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java @@ -322,7 +322,9 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre @Override public void onResume() { super.onResume(); - binding.getRoot().postDelayed(feedStoriesAdapter::notifyDataSetChanged, 1000); + // temporary fix + feedStoriesViewModel.getList().removeObservers(getViewLifecycleOwner()); + feedStoriesViewModel.getList().observe(getViewLifecycleOwner(), feedStoriesAdapter::submitList); } @Override @@ -398,8 +400,6 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre storiesFetching = false; //noinspection unchecked feedStoriesViewModel.getList().postValue((List) feedStoryModels); - //noinspection unchecked - feedStoriesAdapter.submitList((List) feedStoryModels); if (storyListMenu != null) storyListMenu.setVisible(true); updateSwipeRefreshState(); }), Dispatchers.getIO()) diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/stories/Story.kt b/app/src/main/java/awais/instagrabber/repositories/responses/stories/Story.kt index b8288127..eb75944f 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/stories/Story.kt +++ b/app/src/main/java/awais/instagrabber/repositories/responses/stories/Story.kt @@ -11,7 +11,7 @@ data class Story( val latestReelMedia: Long? = null, // = timestamp val mediaCount: Int? = null, // for stories and highlights - var seen: Long? = null, + val seen: Long? = null, val user: User? = null, // for stories val muted: Boolean? = null, diff --git a/app/src/main/java/awais/instagrabber/viewmodels/StoryFragmentViewModel.kt b/app/src/main/java/awais/instagrabber/viewmodels/StoryFragmentViewModel.kt index d463c251..8979705e 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/StoryFragmentViewModel.kt +++ b/app/src/main/java/awais/instagrabber/viewmodels/StoryFragmentViewModel.kt @@ -60,21 +60,22 @@ class StoryFragmentViewModel : ViewModel() { private val mediaRepository: MediaRepository by lazy { MediaRepository.getInstance() } // for highlights ONLY - private val highlights = MutableLiveData?>() + val highlights = MutableLiveData?>() /* set functions */ fun setStory(story: Story) { - if (story.items == null || story.items.size == 0) { - pagination.postValue(StoryPaginationType.ERROR) - return - } currentStory.postValue(story) storyTitle.postValue(story.title ?: story.user?.username) if (story.broadcast != null) { date.postValue(story.dateTime) type.postValue(MediaItemType.MEDIA_TYPE_LIVE) pagination.postValue(StoryPaginationType.DO_NOTHING) + return + } + if (story.items == null || story.items.size == 0) { + pagination.postValue(StoryPaginationType.ERROR) + return } } @@ -184,10 +185,6 @@ class StoryFragmentViewModel : ViewModel() { /* get functions */ - fun getHighlights(): LiveData?> { - return highlights - } - fun getCurrentStory(): LiveData { return currentStory } @@ -472,4 +469,27 @@ class StoryFragmentViewModel : ViewModel() { } return data } + + fun markAsSeen(storyMedia: StoryMedia): LiveData> { + val data = MutableLiveData>() + data.postValue(loading(null)) + viewModelScope.launch(Dispatchers.IO) { + try { + storiesRepository.seen( + csrfToken!!, + userId, + deviceId, + storyMedia.id, + storyMedia.takenAt, + System.currentTimeMillis() / 1000 + ) + val oldStory = currentStory.value!! + val newStory = oldStory.copy(seen = storyMedia.takenAt) + data.postValue(success(newStory)) + } catch (e: Exception) { + data.postValue(error(e.message, null)) + } + } + return data + } } \ No newline at end of file