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 - - - - - - - - - - - - - - - - - - - - - - - -