From 6c85115b678d2071573e045966b2cd9ddd6d3f53 Mon Sep 17 00:00:00 2001
From: Matthieu <24-artectrex@users.noreply.shinice.net>
Date: Wed, 13 Dec 2023 11:18:05 +0100
Subject: [PATCH] Stories progress
---
app/src/main/AndroidManifest.xml | 29 +--
.../org/pixeldroid/app/posts/AlbumActivity.kt | 4 +-
.../posts/feeds/CommonFeedFragmentUtils.kt | 25 ++-
.../feeds/cachedFeeds/CachedFeedFragment.kt | 7 +-
.../cachedFeeds/postFeeds/PostFeedFragment.kt | 30 +--
.../uncachedFeeds/UncachedFeedFragment.kt | 6 +-
.../app/settings/SettingsActivity.kt | 8 +-
.../app/stories/StoriesViewModel.kt | 7 +-
.../app/stories/StoryCarouselViewHolder.kt | 89 ++++++---
.../pixeldroid/app/utils/api/PixelfedAPI.kt | 2 +-
.../app/utils/di/ApplicationComponent.kt | 1 +
app/src/main/res/values-night/styles.xml | 134 -------------
app/src/main/res/values/styles.xml | 179 +-----------------
13 files changed, 134 insertions(+), 387 deletions(-)
delete mode 100644 app/src/main/res/values-night/styles.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f7cf28d4..0ca1cd23 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -12,6 +12,7 @@
+
+ android:theme="@style/BaseAppTheme" />
+ android:theme="@style/BaseAppTheme" />
+ android:theme="@style/BaseAppTheme"/>
+ android:theme="@style/BaseAppTheme">
@@ -77,32 +78,32 @@
+ android:theme="@style/BaseAppTheme" />
+ android:theme="@style/BaseAppTheme"/>
+ android:theme="@style/BaseAppTheme"/>
+ android:theme="@style/BaseAppTheme" />
@@ -143,7 +144,7 @@
@@ -156,7 +157,7 @@
android:resource="@xml/searchable" />
+ android:theme="@style/BaseAppTheme" />
initAdapter(
progressBar: ProgressBar, swipeRefreshLayout: SwipeRefreshLayout,
recyclerView: RecyclerView, motionLayout: MotionLayout, errorLayout: ErrorLayoutBinding,
- adapter: PagingDataAdapter) {
+ adapter: PagingDataAdapter,
+ header: StoriesAdapter? = null
+) {
- recyclerView.adapter = adapter.withLoadStateFooter(
- footer = ReposLoadStateAdapter { adapter.retry() }
+
+ val footer = ReposLoadStateAdapter { adapter.retry() }
+
+ adapter.addLoadStateListener { loadStates: CombinedLoadStates ->
+ footer.loadState = loadStates.append
+ }
+
+ recyclerView.adapter = ConcatAdapter(
+ *listOfNotNull(
+ header, // need to filter it if null
+ adapter,
+ footer
+ ).toTypedArray()
)
swipeRefreshLayout.setOnRefreshListener {
adapter.refresh()
+ header?.refreshStories()
}
adapter.addLoadStateListener { loadState ->
@@ -149,6 +166,8 @@ class ReposLoadStateAdapter(
}
}
+
+
/**
* [RecyclerView.ViewHolder] that is shown at the end of the feed to indicate loading or errors
* in the loading of appending values.
diff --git a/app/src/main/java/org/pixeldroid/app/posts/feeds/cachedFeeds/CachedFeedFragment.kt b/app/src/main/java/org/pixeldroid/app/posts/feeds/cachedFeeds/CachedFeedFragment.kt
index 592f5a53..81be3ba0 100644
--- a/app/src/main/java/org/pixeldroid/app/posts/feeds/cachedFeeds/CachedFeedFragment.kt
+++ b/app/src/main/java/org/pixeldroid/app/posts/feeds/cachedFeeds/CachedFeedFragment.kt
@@ -18,6 +18,7 @@ import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.filter
import org.pixeldroid.app.databinding.FragmentFeedBinding
import org.pixeldroid.app.posts.feeds.initAdapter
+import org.pixeldroid.app.stories.StoriesAdapter
import org.pixeldroid.app.utils.BaseFragment
import org.pixeldroid.app.utils.api.objects.FeedContentDatabase
import org.pixeldroid.app.utils.db.AppDatabase
@@ -31,6 +32,7 @@ open class CachedFeedFragment : BaseFragment() {
internal lateinit var viewModel: FeedViewModel
internal lateinit var adapter: PagingDataAdapter
+ internal var headerAdapter: StoriesAdapter? = null
private lateinit var binding: FragmentFeedBinding
@@ -49,6 +51,7 @@ open class CachedFeedFragment : BaseFragment() {
}
}
+ //TODO rename function to something that makes sense
internal fun initSearch() {
// Scroll to top when the list is refreshed from network.
lifecycleScope.launchWhenStarted {
@@ -73,7 +76,9 @@ open class CachedFeedFragment : BaseFragment() {
binding = FragmentFeedBinding.inflate(layoutInflater)
initAdapter(binding.progressBar, binding.swipeRefreshLayout,
- binding.list, binding.motionLayout, binding.errorLayout, adapter)
+ binding.list, binding.motionLayout, binding.errorLayout, adapter,
+ headerAdapter
+ )
return binding.root
}
diff --git a/app/src/main/java/org/pixeldroid/app/posts/feeds/cachedFeeds/postFeeds/PostFeedFragment.kt b/app/src/main/java/org/pixeldroid/app/posts/feeds/cachedFeeds/postFeeds/PostFeedFragment.kt
index 28b77a80..ba65353c 100644
--- a/app/src/main/java/org/pixeldroid/app/posts/feeds/cachedFeeds/postFeeds/PostFeedFragment.kt
+++ b/app/src/main/java/org/pixeldroid/app/posts/feeds/cachedFeeds/postFeeds/PostFeedFragment.kt
@@ -11,12 +11,11 @@ import androidx.paging.PagingDataAdapter
import androidx.paging.RemoteMediator
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
-import org.pixeldroid.app.R
import org.pixeldroid.app.posts.StatusViewHolder
import org.pixeldroid.app.posts.feeds.cachedFeeds.CachedFeedFragment
import org.pixeldroid.app.posts.feeds.cachedFeeds.FeedViewModel
import org.pixeldroid.app.posts.feeds.cachedFeeds.ViewModelFactory
-import org.pixeldroid.app.stories.StoryCarouselViewHolder
+import org.pixeldroid.app.stories.StoriesAdapter
import org.pixeldroid.app.utils.api.objects.FeedContentDatabase
import org.pixeldroid.app.utils.api.objects.Status
import org.pixeldroid.app.utils.db.dao.feedContent.FeedContentDao
@@ -39,14 +38,18 @@ class PostFeedFragment: CachedFeedFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- adapter = PostsAdapter(requireContext().displayDimensionsInPx())
-
home = requireArguments().getBoolean("home")
+ adapter = PostsAdapter(requireContext().displayDimensionsInPx())
+
@Suppress("UNCHECKED_CAST")
if (home){
mediator = HomeFeedRemoteMediator(apiHolder, db) as RemoteMediator
dao = db.homePostDao() as FeedContentDao
+ headerAdapter = StoriesAdapter(lifecycleScope, apiHolder)
+ headerAdapter?.showStories = false
+
+ headerAdapter?.refreshStories()
}
else {
mediator = PublicFeedRemoteMediator(apiHolder, db) as RemoteMediator
@@ -79,35 +82,20 @@ class PostFeedFragment: CachedFeedFragment() {
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
- return if(viewType == R.layout.post_fragment){
- StatusViewHolder.create(parent)
- } else {
- StoryCarouselViewHolder.create(parent)
- }
- }
-
- override fun getItemViewType(position: Int): Int {
- return if(home && position == 0) R.layout.story_carousel
- else R.layout.post_fragment
+ return StatusViewHolder.create(parent)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
- if(home && position == 0){
- holder.itemView.visibility = View.GONE
- holder.itemView.layoutParams = RecyclerView.LayoutParams(0, 0)
- (holder as StoryCarouselViewHolder).bind(apiHolder, lifecycleScope, holder.itemView)
- } else {
holder.itemView.visibility = View.VISIBLE
holder.itemView.layoutParams =
RecyclerView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
- val uiModel = getItem(if(home) position - 1 else position) as Status?
+ val uiModel = getItem(position) as Status?
uiModel?.let {
(holder as StatusViewHolder).bind(it, apiHolder, db, lifecycleScope, displayDimensionsInPx)
}
- }
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/org/pixeldroid/app/posts/feeds/uncachedFeeds/UncachedFeedFragment.kt b/app/src/main/java/org/pixeldroid/app/posts/feeds/uncachedFeeds/UncachedFeedFragment.kt
index 9037bfe2..db58c98e 100644
--- a/app/src/main/java/org/pixeldroid/app/posts/feeds/uncachedFeeds/UncachedFeedFragment.kt
+++ b/app/src/main/java/org/pixeldroid/app/posts/feeds/uncachedFeeds/UncachedFeedFragment.kt
@@ -61,8 +61,10 @@ open class UncachedFeedFragment : BaseFragment() {
binding = FragmentFeedBinding.inflate(layoutInflater)
- initAdapter(binding.progressBar, binding.swipeRefreshLayout, binding.list,
- binding.motionLayout, binding.errorLayout, adapter)
+ initAdapter(
+ binding.progressBar, binding.swipeRefreshLayout, binding.list,
+ binding.motionLayout, binding.errorLayout, adapter
+ )
return binding.root
}
diff --git a/app/src/main/java/org/pixeldroid/app/settings/SettingsActivity.kt b/app/src/main/java/org/pixeldroid/app/settings/SettingsActivity.kt
index 8dcb439d..99ecdf17 100644
--- a/app/src/main/java/org/pixeldroid/app/settings/SettingsActivity.kt
+++ b/app/src/main/java/org/pixeldroid/app/settings/SettingsActivity.kt
@@ -38,21 +38,19 @@ class SettingsActivity : ThemedActivity(), SharedPreferences.OnSharedPreferenceC
.commit()
supportActionBar?.setDisplayHomeAsUpEnabled(true)
- onBackPressedDispatcher.addCallback(this) {
+ onBackPressedDispatcher.addCallback(this /* lifecycle owner */) {
// Handle the back button event
// If a setting (for example language or theme) was changed, the main activity should be
// started without history so that the change is applied to the whole back stack
if (restartMainOnExit) {
val intent = Intent(this@SettingsActivity, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
- super.startActivity(intent)
+ super@SettingsActivity.startActivity(intent)
} else {
- super.onBackPressedDispatcher.onBackPressed()
+ finish()
}
-
}
-
restartMainOnExit = intent.getBooleanExtra("restartMain", false)
}
diff --git a/app/src/main/java/org/pixeldroid/app/stories/StoriesViewModel.kt b/app/src/main/java/org/pixeldroid/app/stories/StoriesViewModel.kt
index 602019cb..733ef3c1 100644
--- a/app/src/main/java/org/pixeldroid/app/stories/StoriesViewModel.kt
+++ b/app/src/main/java/org/pixeldroid/app/stories/StoriesViewModel.kt
@@ -126,7 +126,12 @@ class StoriesViewModel(
viewModelScope.launch {
try {
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
- currentStoryId()?.let { api.storySeen(it) }
+ val story = currentAccount?.nodes?.getOrNull(uiState.value.currentImage)
+
+ if (story?.seen == true){
+ //TODO update seen when marked successfully as seen?
+ story.id?.let { api.storySeen(it) }
+ }
} catch (exception: Exception){
_uiState.update { currentUiState ->
currentUiState.copy(errorMessage = R.string.story_could_not_see)
diff --git a/app/src/main/java/org/pixeldroid/app/stories/StoryCarouselViewHolder.kt b/app/src/main/java/org/pixeldroid/app/stories/StoryCarouselViewHolder.kt
index 89c6626d..5d884dfa 100644
--- a/app/src/main/java/org/pixeldroid/app/stories/StoryCarouselViewHolder.kt
+++ b/app/src/main/java/org/pixeldroid/app/stories/StoryCarouselViewHolder.kt
@@ -3,9 +3,9 @@ package org.pixeldroid.app.stories
import android.annotation.SuppressLint
import android.content.Intent
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.LifecycleCoroutineScope
+import androidx.paging.LoadState
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import kotlinx.coroutines.launch
@@ -15,51 +15,90 @@ import org.pixeldroid.app.databinding.StoryCarouselBinding
import org.pixeldroid.app.databinding.StoryCarouselItemBinding
import org.pixeldroid.app.postCreation.camera.CameraActivity
import org.pixeldroid.app.postCreation.camera.CameraFragment
-import org.pixeldroid.app.postCreation.carousel.dpToPx
import org.pixeldroid.app.utils.api.objects.CarouselUserContainer
import org.pixeldroid.app.utils.api.objects.StoryCarousel
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
-class StoryCarouselViewHolder(val binding: StoryCarouselBinding) : RecyclerView.ViewHolder(binding.root) {
- fun bind(
- pixelfedAPI: PixelfedAPIHolder,
- lifecycleScope: LifecycleCoroutineScope,
- itemView: View
- ) {
- val adapter = StoriesListAdapter()
- binding.storyCarousel.adapter = adapter
+/**
+ * Adapter to the show the a [RecyclerView] item for a [LoadState]
+ */
+class StoriesAdapter(val lifecycleScope: LifecycleCoroutineScope, val apiHolder: PixelfedAPIHolder) : RecyclerView.Adapter() {
+ var carousel: StoryCarousel? = null
- loadStories(adapter, lifecycleScope, pixelfedAPI, itemView)
+ /**
+ * Whether to show stories or not.
+ *
+ * Changing this property will immediately notify the Adapter to change the item it's
+ * presenting.
+ */
+ var showStories: Boolean = false
+ set(newValue) {
+ val oldValue = field
+
+ if (oldValue && !newValue) {
+ notifyItemRemoved(0)
+ } else if (newValue && !oldValue) {
+ notifyItemInserted(0)
+ } else if (oldValue && newValue) {
+ notifyItemChanged(0)
+ }
+ field = newValue
+
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StoryCarouselViewHolder {
+ return StoryCarouselViewHolder.create(parent, ::noStories)
}
- private fun loadStories(
- adapter: StoriesListAdapter,
- lifecycleScope: LifecycleCoroutineScope,
- apiHolder: PixelfedAPIHolder,
- itemView: View
- ) {
+ override fun onBindViewHolder(holder: StoryCarouselViewHolder, position: Int) {
+ holder.bind(carousel)
+ }
+
+ override fun getItemViewType(position: Int): Int = 0
+
+ override fun getItemCount(): Int = if (showStories) 1 else 0
+
+ private fun noStories(){
+ showStories = false
+ }
+
+ private fun gotStories(newCarousel: StoryCarousel) {
+ carousel = newCarousel
+ showStories = true
+ }
+
+ fun refreshStories(){
lifecycleScope.launch {
try{
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
val carousel = api.carousel()
if (carousel.nodes?.isEmpty() != true) {
- itemView.visibility = View.VISIBLE
- itemView.layoutParams.height = 200.dpToPx(binding.root.context)
- itemView.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
-
- adapter.initCarousel(carousel)
+ // Pass carousel to adapter
+ gotStories(carousel)
+ } else {
+ noStories()
}
-
} catch (exception: Exception){
- //TODO
+ noStories()
}
}
}
+}
+
+class StoryCarouselViewHolder(val binding: StoryCarouselBinding) : RecyclerView.ViewHolder(binding.root) {
+
+ fun bind(carousel: StoryCarousel?) {
+ val adapter = StoriesListAdapter()
+ binding.storyCarousel.adapter = adapter
+
+ carousel?.let { adapter.initCarousel(it) }
+ }
+
companion object {
- fun create(parent: ViewGroup): StoryCarouselViewHolder {
+ fun create(parent: ViewGroup, noStories: () -> Unit): StoryCarouselViewHolder {
val itemBinding = StoryCarouselBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
diff --git a/app/src/main/java/org/pixeldroid/app/utils/api/PixelfedAPI.kt b/app/src/main/java/org/pixeldroid/app/utils/api/PixelfedAPI.kt
index 49bcacf2..ed23d792 100644
--- a/app/src/main/java/org/pixeldroid/app/utils/api/PixelfedAPI.kt
+++ b/app/src/main/java/org/pixeldroid/app/utils/api/PixelfedAPI.kt
@@ -235,7 +235,7 @@ interface PixelfedAPI {
@Query("post_id") post_id: String,
)
- @GET("/api/v1.1/stories/carousel")
+ @GET("/api/pixelfed/v1/stories/self-carousel")
suspend fun carousel(): StoryCarousel
@POST("/api/v1.1/stories/seen")
diff --git a/app/src/main/java/org/pixeldroid/app/utils/di/ApplicationComponent.kt b/app/src/main/java/org/pixeldroid/app/utils/di/ApplicationComponent.kt
index 23aca907..c2490432 100644
--- a/app/src/main/java/org/pixeldroid/app/utils/di/ApplicationComponent.kt
+++ b/app/src/main/java/org/pixeldroid/app/utils/di/ApplicationComponent.kt
@@ -10,6 +10,7 @@ import dagger.Component
import org.pixeldroid.app.postCreation.PostCreationViewModel
import org.pixeldroid.app.profile.EditProfileViewModel
import org.pixeldroid.app.stories.StoriesViewModel
+import org.pixeldroid.app.stories.StoryCarouselViewHolder
import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker
import javax.inject.Singleton
diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml
deleted file mode 100644
index 78687208..00000000
--- a/app/src/main/res/values-night/styles.xml
+++ /dev/null
@@ -1,134 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index b6a964cc..a2d1c457 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -10,11 +10,9 @@
- @drawable/mascot_small
-
-
- - @style/BaseAppTheme.NoActionBar
+ - @style/BaseAppTheme
- false
- true
@@ -49,179 +47,4 @@
- @android:color/transparent
- @android:color/transparent
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-