Merge branch 'hashtags' into 'master'
Implement viewing hashtags See merge request pixeldroid/PixelDroid!358
This commit is contained in:
commit
2ab6651be0
|
@ -111,7 +111,7 @@ dependencies {
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
|
||||||
implementation "androidx.browser:browser:1.3.0"
|
implementation "androidx.browser:browser:1.3.0"
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.2.0'
|
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
|
||||||
|
@ -133,7 +133,7 @@ dependencies {
|
||||||
implementation "androidx.camera:camera-lifecycle:$cameraX_version"
|
implementation "androidx.camera:camera-lifecycle:$cameraX_version"
|
||||||
|
|
||||||
// CameraX View class
|
// CameraX View class
|
||||||
implementation 'androidx.camera:camera-view:1.0.0-alpha24'
|
implementation 'androidx.camera:camera-view:1.0.0-alpha25'
|
||||||
|
|
||||||
def room_version = "2.3.0"
|
def room_version = "2.3.0"
|
||||||
implementation "androidx.room:room-runtime:$room_version"
|
implementation "androidx.room:room-runtime:$room_version"
|
||||||
|
|
|
@ -6,6 +6,7 @@ import androidx.test.core.app.ActivityScenario
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import androidx.test.espresso.Espresso.onView
|
import androidx.test.espresso.Espresso.onView
|
||||||
import androidx.test.espresso.action.ViewActions
|
import androidx.test.espresso.action.ViewActions
|
||||||
|
import androidx.test.espresso.action.ViewActions.openLinkWithText
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition
|
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition
|
||||||
import androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition
|
import androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition
|
||||||
|
@ -13,11 +14,10 @@ import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.viewpager2.widget.ViewPager2
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
|
import org.hamcrest.CoreMatchers.*
|
||||||
import org.pixeldroid.app.utils.db.AppDatabase
|
import org.pixeldroid.app.utils.db.AppDatabase
|
||||||
import org.pixeldroid.app.posts.StatusViewHolder
|
import org.pixeldroid.app.posts.StatusViewHolder
|
||||||
import org.pixeldroid.app.testUtility.*
|
import org.pixeldroid.app.testUtility.*
|
||||||
import org.hamcrest.CoreMatchers.not
|
|
||||||
import org.hamcrest.CoreMatchers.sameInstance
|
|
||||||
import org.hamcrest.core.IsInstanceOf
|
import org.hamcrest.core.IsInstanceOf
|
||||||
import org.hamcrest.core.StringContains.containsString
|
import org.hamcrest.core.StringContains.containsString
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
|
@ -40,8 +40,6 @@ class HomeFeedTest {
|
||||||
@Rule @JvmField
|
@Rule @JvmField
|
||||||
var repeatRule: RepeatRule = RepeatRule()
|
var repeatRule: RepeatRule = RepeatRule()
|
||||||
|
|
||||||
@get:Rule
|
|
||||||
var globalTimeout: Timeout = Timeout.seconds(100)
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun before(){
|
fun before(){
|
||||||
|
@ -94,6 +92,26 @@ class HomeFeedTest {
|
||||||
|
|
||||||
onView(first(withId(R.id.description))).check(matches(withText(containsString("@user2"))));
|
onView(first(withId(R.id.description))).check(matches(withText(containsString("@user2"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@RepeatTest
|
||||||
|
fun hashtag() {
|
||||||
|
//Wait for the feed to load
|
||||||
|
waitForView(R.id.postPager)
|
||||||
|
|
||||||
|
onView(allOf(withClassName(endsWith("RecyclerView")), not(withId(R.id.material_drawer_recycler_view))))
|
||||||
|
.perform(
|
||||||
|
scrollToPosition<StatusViewHolder>(3)
|
||||||
|
)
|
||||||
|
|
||||||
|
onView(allOf(withText(containsString("randomNoise"))))
|
||||||
|
.perform(clickClickableSpan("#randomNoise"))
|
||||||
|
|
||||||
|
waitForView(R.id.action_bar, allOf(withText("#randomNoise"), not(withId(R.id.description))))
|
||||||
|
|
||||||
|
onView(withId(R.id.action_bar)).check(matches(isDisplayed()));
|
||||||
|
onView(allOf(withText("#randomNoise"), not(withId(R.id.description)))).check(matches(withParent(withId(R.id.action_bar))));
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
@Test
|
@Test
|
||||||
fun clickingReblogButtonWorks() {
|
fun clickingReblogButtonWorks() {
|
||||||
|
@ -187,13 +205,6 @@ class HomeFeedTest {
|
||||||
onView(first(withId(R.id.username))).check(matches(isDisplayed()))
|
onView(first(withId(R.id.username))).check(matches(isDisplayed()))
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
@Test
|
|
||||||
fun clickingHashTagsWorks() {
|
|
||||||
onView(withId(R.id.list)).perform(
|
|
||||||
actionOnItemAtPosition<StatusViewHolder>(1, clickChildViewWithId(R.id.description))
|
|
||||||
)
|
|
||||||
onView(withId(R.id.list)).check(matches(isDisplayed()))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.widget.TextView
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.test.espresso.*
|
import androidx.test.espresso.*
|
||||||
|
import androidx.test.espresso.NoMatchingViewException
|
||||||
import androidx.test.espresso.action.*
|
import androidx.test.espresso.action.*
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import androidx.test.espresso.matcher.BoundedMatcher
|
import androidx.test.espresso.matcher.BoundedMatcher
|
||||||
|
@ -15,7 +16,6 @@ import androidx.test.espresso.matcher.ViewMatchers
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.*
|
import androidx.test.espresso.matcher.ViewMatchers.*
|
||||||
import androidx.test.espresso.util.HumanReadables
|
import androidx.test.espresso.util.HumanReadables
|
||||||
import androidx.test.espresso.util.TreeIterables
|
import androidx.test.espresso.util.TreeIterables
|
||||||
import org.pixeldroid.app.R
|
|
||||||
import org.hamcrest.BaseMatcher
|
import org.hamcrest.BaseMatcher
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
import org.hamcrest.CoreMatchers.allOf
|
||||||
import org.hamcrest.Description
|
import org.hamcrest.Description
|
||||||
|
@ -23,6 +23,7 @@ import org.hamcrest.Matcher
|
||||||
import org.hamcrest.Matchers
|
import org.hamcrest.Matchers
|
||||||
import org.junit.rules.TestRule
|
import org.junit.rules.TestRule
|
||||||
import org.junit.runners.model.Statement
|
import org.junit.runners.model.Statement
|
||||||
|
import org.pixeldroid.app.R
|
||||||
import java.util.concurrent.TimeoutException
|
import java.util.concurrent.TimeoutException
|
||||||
|
|
||||||
|
|
||||||
|
@ -298,6 +299,54 @@ fun clickChildViewWithId(id: Int) = object : ViewAction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clickClickableSpan(textToClick: CharSequence): ViewAction? {
|
||||||
|
return object : ViewAction {
|
||||||
|
override fun getConstraints(): Matcher<View> {
|
||||||
|
return Matchers.instanceOf(TextView::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDescription(): String {
|
||||||
|
return "clicking on a ClickableSpan"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun perform(uiController: UiController?, view: View) {
|
||||||
|
val textView = view as TextView
|
||||||
|
val spannableString = textView.text as SpannableString
|
||||||
|
if (spannableString.isEmpty()) {
|
||||||
|
// TextView is empty, nothing to do
|
||||||
|
throw NoMatchingViewException.Builder()
|
||||||
|
.includeViewHierarchy(true)
|
||||||
|
.withRootView(textView)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the links inside the TextView and check if we find textToClick
|
||||||
|
val spans = spannableString.getSpans(
|
||||||
|
0, spannableString.length,
|
||||||
|
ClickableSpan::class.java
|
||||||
|
)
|
||||||
|
if (spans.isNotEmpty()) {
|
||||||
|
var spanCandidate: ClickableSpan?
|
||||||
|
for (span in spans) {
|
||||||
|
spanCandidate = span
|
||||||
|
val start = spannableString.getSpanStart(spanCandidate)
|
||||||
|
val end = spannableString.getSpanEnd(spanCandidate)
|
||||||
|
val sequence = spannableString.subSequence(start, end)
|
||||||
|
if (textToClick.toString() == sequence.toString()) {
|
||||||
|
span.onClick(textView)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw NoMatchingViewException.Builder()
|
||||||
|
.includeViewHierarchy(true)
|
||||||
|
.withRootView(textView)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun typeTextInViewWithId(id: Int, text: String) = object : ViewAction {
|
fun typeTextInViewWithId(id: Int, text: String) = object : ViewAction {
|
||||||
|
|
||||||
override fun getConstraints() = null
|
override fun getConstraints() = null
|
||||||
|
|
|
@ -49,6 +49,10 @@
|
||||||
android:name=".profile.FollowsActivity"
|
android:name=".profile.FollowsActivity"
|
||||||
android:screenOrientation="sensorPortrait"
|
android:screenOrientation="sensorPortrait"
|
||||||
tools:ignore="LockedOrientationActivity" />
|
tools:ignore="LockedOrientationActivity" />
|
||||||
|
<activity
|
||||||
|
android:name=".posts.feeds.uncachedFeeds.hashtags.HashTagActivity"
|
||||||
|
android:screenOrientation="sensorPortrait"
|
||||||
|
tools:ignore="LockedOrientationActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".posts.PostActivity"
|
android:name=".posts.PostActivity"
|
||||||
android:screenOrientation="sensorPortrait"
|
android:screenOrientation="sensorPortrait"
|
||||||
|
|
|
@ -20,9 +20,4 @@ class CameraActivity : BaseActivity() {
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
.add(R.id.camera_activity_fragment, cameraFragment).commit()
|
.add(R.id.camera_activity_fragment, cameraFragment).commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
|
||||||
onBackPressed()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -17,6 +17,7 @@ import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.utils.api.PixelfedAPI
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
import org.pixeldroid.app.utils.api.objects.Account.Companion.openAccountFromId
|
import org.pixeldroid.app.utils.api.objects.Account.Companion.openAccountFromId
|
||||||
import org.pixeldroid.app.utils.api.objects.Mention
|
import org.pixeldroid.app.utils.api.objects.Mention
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Tag.Companion.openTag
|
||||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URISyntaxException
|
import java.net.URISyntaxException
|
||||||
|
@ -74,7 +75,7 @@ fun parseHTMLText(
|
||||||
val tag = text.subSequence(1, text.length).toString()
|
val tag = text.subSequence(1, text.length).toString()
|
||||||
customSpan = object : ClickableSpanNoUnderline() {
|
customSpan = object : ClickableSpanNoUnderline() {
|
||||||
override fun onClick(widget: View) {
|
override fun onClick(widget: View) {
|
||||||
Toast.makeText(context, tag, Toast.LENGTH_SHORT).show()
|
openTag(context, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,11 +72,6 @@ class PostActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
|
||||||
onBackPressed()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun activateCommenter() {
|
private fun activateCommenter() {
|
||||||
//Activate commenter
|
//Activate commenter
|
||||||
binding.submitComment.setOnClickListener {
|
binding.submitComment.setOnClickListener {
|
||||||
|
|
|
@ -66,9 +66,4 @@ class ReportActivity : BaseActivity() {
|
||||||
binding.reportProgressBar.visibility = View.GONE
|
binding.reportProgressBar.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
|
||||||
onBackPressed()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package org.pixeldroid.app.posts.feeds.uncachedFeeds.search
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
@ -12,24 +12,31 @@ import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
import org.pixeldroid.app.posts.StatusViewHolder
|
import org.pixeldroid.app.posts.StatusViewHolder
|
||||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.*
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.hashtags.HashTagContentRepository
|
||||||
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.search.SearchContentRepository
|
||||||
import org.pixeldroid.app.utils.api.objects.Results
|
import org.pixeldroid.app.utils.api.objects.Results
|
||||||
import org.pixeldroid.app.utils.api.objects.Status
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Tag.Companion.HASHTAG_TAG
|
||||||
import org.pixeldroid.app.utils.displayDimensionsInPx
|
import org.pixeldroid.app.utils.displayDimensionsInPx
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fragment to show a list of [Status]es, as a result of a search.
|
* Fragment to show a list of [Status]es, as a result of a search or a hashtag.
|
||||||
*/
|
*/
|
||||||
class SearchPostsFragment : UncachedFeedFragment<Status>() {
|
class UncachedPostsFragment : UncachedFeedFragment<Status>() {
|
||||||
|
|
||||||
private lateinit var query: String
|
private var hashtagOrQuery: String? = null
|
||||||
|
private var search: Boolean = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
adapter = PostsAdapter(requireContext().displayDimensionsInPx())
|
adapter = PostsAdapter(requireContext().displayDimensionsInPx())
|
||||||
|
|
||||||
query = arguments?.getSerializable("searchFeed") as String
|
hashtagOrQuery = arguments?.getString(HASHTAG_TAG)
|
||||||
|
|
||||||
|
if(hashtagOrQuery == null){
|
||||||
|
search = true
|
||||||
|
hashtagOrQuery = arguments?.getString("searchFeed")!!
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExperimentalPagingApi
|
@ExperimentalPagingApi
|
||||||
|
@ -41,15 +48,27 @@ class SearchPostsFragment : UncachedFeedFragment<Status>() {
|
||||||
|
|
||||||
// get the view model
|
// get the view model
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
viewModel = ViewModelProvider(requireActivity(), ViewModelFactory(
|
viewModel = if(search) {
|
||||||
|
ViewModelProvider(
|
||||||
|
requireActivity(), ViewModelFactory(
|
||||||
SearchContentRepository<Status>(
|
SearchContentRepository<Status>(
|
||||||
apiHolder.setToCurrentUser(),
|
apiHolder.setToCurrentUser(),
|
||||||
Results.SearchType.statuses,
|
Results.SearchType.statuses,
|
||||||
query
|
hashtagOrQuery!!
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.get("searchPosts", FeedViewModel::class.java) as FeedViewModel<Status>
|
.get("searchPosts", FeedViewModel::class.java) as FeedViewModel<Status>
|
||||||
|
} else {
|
||||||
|
ViewModelProvider(requireActivity(), ViewModelFactory(
|
||||||
|
HashTagContentRepository(
|
||||||
|
apiHolder.setToCurrentUser(),
|
||||||
|
hashtagOrQuery!!
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.get(HASHTAG_TAG, FeedViewModel::class.java) as FeedViewModel<Status>
|
||||||
|
}
|
||||||
|
|
||||||
launch()
|
launch()
|
||||||
initSearch()
|
initSearch()
|
|
@ -0,0 +1,35 @@
|
||||||
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds.hashtags
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import org.pixeldroid.app.R
|
||||||
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedPostsFragment
|
||||||
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Tag.Companion.HASHTAG_TAG
|
||||||
|
|
||||||
|
|
||||||
|
class HashTagActivity : BaseActivity() {
|
||||||
|
private var tagFragment = UncachedPostsFragment()
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_followers)
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
|
// Get hashtag tag
|
||||||
|
val tag = intent.getSerializableExtra(HASHTAG_TAG) as String?
|
||||||
|
|
||||||
|
startFragment(tag!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startFragment(tag : String) {
|
||||||
|
supportActionBar?.title = getString(R.string.hashtag_title).format(tag)
|
||||||
|
|
||||||
|
val arguments = Bundle()
|
||||||
|
arguments.putSerializable(HASHTAG_TAG, tag)
|
||||||
|
tagFragment.arguments = arguments
|
||||||
|
|
||||||
|
supportFragmentManager.beginTransaction()
|
||||||
|
.add(R.id.followsFragment, tagFragment).commit()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds.hashtags
|
||||||
|
|
||||||
|
import androidx.paging.ExperimentalPagingApi
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedContentRepository
|
||||||
|
import org.pixeldroid.app.utils.api.objects.FeedContent
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Results
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repository class for viewing hashtags
|
||||||
|
*/
|
||||||
|
class HashTagContentRepository @ExperimentalPagingApi
|
||||||
|
@Inject constructor(
|
||||||
|
private val api: PixelfedAPI,
|
||||||
|
private val hashtag: String,
|
||||||
|
): UncachedContentRepository<Status> {
|
||||||
|
override fun getStream(): Flow<PagingData<Status>> {
|
||||||
|
return Pager(
|
||||||
|
config = PagingConfig(
|
||||||
|
initialLoadSize = NETWORK_PAGE_SIZE,
|
||||||
|
pageSize = NETWORK_PAGE_SIZE,
|
||||||
|
enablePlaceholders = false),
|
||||||
|
pagingSourceFactory = {
|
||||||
|
HashTagPagingSource(api, hashtag)
|
||||||
|
}
|
||||||
|
).flow
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val NETWORK_PAGE_SIZE = 20
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package org.pixeldroid.app.posts.feeds.uncachedFeeds.hashtags
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import androidx.paging.PagingState
|
||||||
|
import org.pixeldroid.app.utils.api.PixelfedAPI
|
||||||
|
import org.pixeldroid.app.utils.api.objects.FeedContent
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Results
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Status
|
||||||
|
import retrofit2.HttpException
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the PagingSource for hashtag feeds. Is used in [HashTagContentRepository]
|
||||||
|
*/
|
||||||
|
class HashTagPagingSource(
|
||||||
|
private val api: PixelfedAPI,
|
||||||
|
private val query: String,
|
||||||
|
) : PagingSource<String, Status>() {
|
||||||
|
override suspend fun load(params: LoadParams<String>): LoadResult<String, Status> {
|
||||||
|
val position = params.key
|
||||||
|
return try {
|
||||||
|
val response = api.hashtag(
|
||||||
|
hashtag = query,
|
||||||
|
limit = params.loadSize,
|
||||||
|
max_id = position,
|
||||||
|
)
|
||||||
|
|
||||||
|
LoadResult.Page(
|
||||||
|
data = response,
|
||||||
|
prevKey = null,
|
||||||
|
nextKey = response.lastOrNull()?.id
|
||||||
|
)
|
||||||
|
} catch (exception: HttpException) {
|
||||||
|
LoadResult.Error(exception)
|
||||||
|
} catch (exception: IOException) {
|
||||||
|
LoadResult.Error(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIXME if implemented with [PagingState.anchorPosition], this breaks refreshes? How is this
|
||||||
|
* supposed to work?
|
||||||
|
*/
|
||||||
|
override fun getRefreshKey(state: PagingState<String, Status>): String? = null
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedFeedFragment
|
||||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.ViewModelFactory
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.ViewModelFactory
|
||||||
import org.pixeldroid.app.utils.api.objects.Results
|
import org.pixeldroid.app.utils.api.objects.Results
|
||||||
import org.pixeldroid.app.utils.api.objects.Tag
|
import org.pixeldroid.app.utils.api.objects.Tag
|
||||||
|
import org.pixeldroid.app.utils.api.objects.Tag.Companion.openTag
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fragment to show a list of [hashtag][Tag]s, as a result of a search.
|
* Fragment to show a list of [hashtag][Tag]s, as a result of a search.
|
||||||
|
@ -86,7 +87,7 @@ class HashTagAdapter : PagingDataAdapter<Tag, RecyclerView.ViewHolder>(
|
||||||
companion object {
|
companion object {
|
||||||
private val UIMODEL_COMPARATOR = object : DiffUtil.ItemCallback<Tag>() {
|
private val UIMODEL_COMPARATOR = object : DiffUtil.ItemCallback<Tag>() {
|
||||||
override fun areItemsTheSame(oldItem: Tag, newItem: Tag): Boolean {
|
override fun areItemsTheSame(oldItem: Tag, newItem: Tag): Boolean {
|
||||||
return oldItem.id == newItem.id
|
return oldItem.name == newItem.name
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: Tag, newItem: Tag): Boolean =
|
override fun areContentsTheSame(oldItem: Tag, newItem: Tag): Boolean =
|
||||||
|
@ -107,7 +108,9 @@ class HashTagViewHolder(binding: FragmentTagsBinding) : RecyclerView.ViewHolder(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
itemView.setOnClickListener {
|
itemView.setOnClickListener {
|
||||||
//TODO
|
tag?.apply {
|
||||||
|
openTag(itemView.context, this.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,11 +31,6 @@ class FollowsActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
|
||||||
onBackPressed()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startFragment(id : String, displayName: String, followers : Boolean) {
|
private fun startFragment(id : String, displayName: String, followers : Boolean) {
|
||||||
supportActionBar?.title =
|
supportActionBar?.title =
|
||||||
if (followers) {
|
if (followers) {
|
||||||
|
|
|
@ -107,11 +107,6 @@ class ProfileActivity : BaseActivity() {
|
||||||
binding.profileRefreshLayout.isRefreshing = false
|
binding.profileRefreshLayout.isRefreshing = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
|
||||||
onBackPressed()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setContent(account: Account?) {
|
private fun setContent(account: Account?) {
|
||||||
if(account != null) {
|
if(account != null) {
|
||||||
setViews(account)
|
setViews(account)
|
||||||
|
|
|
@ -9,9 +9,9 @@ import androidx.viewpager2.widget.ViewPager2
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import org.pixeldroid.app.R
|
import org.pixeldroid.app.R
|
||||||
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.UncachedPostsFragment
|
||||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.search.SearchAccountFragment
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.search.SearchAccountFragment
|
||||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.search.SearchHashtagFragment
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.search.SearchHashtagFragment
|
||||||
import org.pixeldroid.app.posts.feeds.uncachedFeeds.search.SearchPostsFragment
|
|
||||||
import org.pixeldroid.app.utils.api.objects.Results
|
import org.pixeldroid.app.utils.api.objects.Results
|
||||||
import org.pixeldroid.app.utils.BaseActivity
|
import org.pixeldroid.app.utils.BaseActivity
|
||||||
|
|
||||||
|
@ -47,14 +47,9 @@ class SearchActivity : BaseActivity() {
|
||||||
setupTabs(tabs, searchType)
|
setupTabs(tabs, searchType)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
|
||||||
onBackPressed()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createSearchTabs(query: String): Array<Fragment>{
|
private fun createSearchTabs(query: String): Array<Fragment>{
|
||||||
|
|
||||||
val searchFeedFragment = SearchPostsFragment()
|
val searchFeedFragment = UncachedPostsFragment()
|
||||||
val searchAccountListFragment =
|
val searchAccountListFragment =
|
||||||
SearchAccountFragment()
|
SearchAccountFragment()
|
||||||
val searchHashtagFragment: Fragment = SearchHashtagFragment()
|
val searchHashtagFragment: Fragment = SearchHashtagFragment()
|
||||||
|
|
|
@ -28,6 +28,11 @@ open class BaseActivity : AppCompatActivity() {
|
||||||
super.attachBaseContext(updateBaseContextLocale(base))
|
super.attachBaseContext(updateBaseContextLocale(base))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSupportNavigateUp(): Boolean {
|
||||||
|
onBackPressed()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateBaseContextLocale(context: Context): Context {
|
private fun updateBaseContextLocale(context: Context): Context {
|
||||||
val language = PreferenceManager.getDefaultSharedPreferences(context).getString("language", "default") ?: "default"
|
val language = PreferenceManager.getDefaultSharedPreferences(context).getString("language", "default") ?: "default"
|
||||||
if(language == "default"){
|
if(language == "default"){
|
||||||
|
|
|
@ -153,6 +153,17 @@ interface PixelfedAPI {
|
||||||
@Query("local") local: Boolean? = null
|
@Query("local") local: Boolean? = null
|
||||||
): List<Status>
|
): List<Status>
|
||||||
|
|
||||||
|
@GET("/api/v1/timelines/tag/{hashtag}")
|
||||||
|
suspend fun hashtag(
|
||||||
|
@Path("hashtag") hashtag: String? = null,
|
||||||
|
@Query("local") local: Boolean? = null,
|
||||||
|
@Query("only_media") only_media: Boolean? = null,
|
||||||
|
@Query("max_id") max_id: String? = null,
|
||||||
|
@Query("since_id") since_id: String? = null,
|
||||||
|
@Query("min_id") min_id: String? = null,
|
||||||
|
@Query("limit") limit: Int? = null,
|
||||||
|
): List<Status>
|
||||||
|
|
||||||
@GET("/api/v2/search")
|
@GET("/api/v2/search")
|
||||||
suspend fun search(
|
suspend fun search(
|
||||||
@Query("account_id") account_id: String? = null,
|
@Query("account_id") account_id: String? = null,
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package org.pixeldroid.app.utils.api.objects
|
package org.pixeldroid.app.utils.api.objects
|
||||||
|
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
data class Error(
|
data class Error(
|
||||||
val error: String?
|
val error: String?
|
||||||
)
|
): Serializable
|
|
@ -1,8 +1,10 @@
|
||||||
package org.pixeldroid.app.utils.api.objects
|
package org.pixeldroid.app.utils.api.objects
|
||||||
|
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
data class History(
|
data class History(
|
||||||
//Required attributes
|
//Required attributes
|
||||||
val day: String,
|
val day: String,
|
||||||
val uses: String,
|
val uses: String,
|
||||||
val accounts: String
|
val accounts: String
|
||||||
)
|
): Serializable
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.pixeldroid.app.utils.api.objects
|
package org.pixeldroid.app.utils.api.objects
|
||||||
|
|
||||||
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity.Companion.DEFAULT_MAX_TOOT_CHARS
|
import org.pixeldroid.app.utils.db.entities.InstanceDatabaseEntity.Companion.DEFAULT_MAX_TOOT_CHARS
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
data class Instance (
|
data class Instance (
|
||||||
val description: String?,
|
val description: String?,
|
||||||
|
@ -11,4 +12,4 @@ data class Instance (
|
||||||
val title: String?,
|
val title: String?,
|
||||||
val uri: String?,
|
val uri: String?,
|
||||||
val version: String?
|
val version: String?
|
||||||
)
|
): Serializable
|
|
@ -1,5 +1,9 @@
|
||||||
package org.pixeldroid.app.utils.api.objects
|
package org.pixeldroid.app.utils.api.objects
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import org.pixeldroid.app.posts.feeds.uncachedFeeds.hashtags.HashTagActivity
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
data class Tag(
|
data class Tag(
|
||||||
|
@ -11,5 +15,15 @@ data class Tag(
|
||||||
//needed to be a FeedContent, this inheritance is a bit fickle. Do not use.
|
//needed to be a FeedContent, this inheritance is a bit fickle. Do not use.
|
||||||
override val id: String
|
override val id: String
|
||||||
get() = "tag"
|
get() = "tag"
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val HASHTAG_TAG = "HashtagTag"
|
||||||
|
|
||||||
|
fun openTag(context: Context, tag: String) {
|
||||||
|
val intent = Intent(context, HashTagActivity::class.java)
|
||||||
|
intent.putExtra(HASHTAG_TAG, tag)
|
||||||
|
ContextCompat.startActivity(context, intent, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/followsFragment"
|
android:id="@+id/followsFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"/>
|
||||||
tools:context=".profile.FollowsActivity">
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
|
@ -200,6 +200,7 @@ For more info about Pixelfed, you can check here: https://pixelfed.org"</string>
|
||||||
<string name="about">About</string>
|
<string name="about">About</string>
|
||||||
<string name="post_title">%1$s\'s post</string>
|
<string name="post_title">%1$s\'s post</string>
|
||||||
<string name="followers_title">%1$s\'s followers</string>
|
<string name="followers_title">%1$s\'s followers</string>
|
||||||
|
<string name="hashtag_title">#%1$s</string>
|
||||||
<string name="follows_title">%1$s\'s follows</string>
|
<string name="follows_title">%1$s\'s follows</string>
|
||||||
<string name="search_empty_error">Search query can\'t be empty</string>
|
<string name="search_empty_error">Search query can\'t be empty</string>
|
||||||
<string name="status_more_options">More options</string>
|
<string name="status_more_options">More options</string>
|
||||||
|
|
Loading…
Reference in New Issue