feat: Display compose button when viewing hashtags (#254)

Display the "compose" FAB when viewing a hashtag list. Tapping the
button will open `ComposeActivity` prepopulated with the hashtag at the
end of the post with the cursor at the start.

Fixes #228
This commit is contained in:
sanao 2023-11-21 03:19:51 +09:00 committed by GitHub
parent 1f21bd45d4
commit adb9fcf468
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 63 additions and 6 deletions

View File

@ -26,15 +26,19 @@ import androidx.fragment.app.commit
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import app.pachli.appstore.EventHub import app.pachli.appstore.EventHub
import app.pachli.appstore.FilterChangedEvent import app.pachli.appstore.FilterChangedEvent
import app.pachli.components.compose.ComposeActivity
import app.pachli.components.timeline.TimelineFragment import app.pachli.components.timeline.TimelineFragment
import app.pachli.components.timeline.TimelineKind import app.pachli.components.timeline.TimelineKind
import app.pachli.databinding.ActivityStatuslistBinding import app.pachli.databinding.ActivityStatuslistBinding
import app.pachli.entity.Filter import app.pachli.entity.Filter
import app.pachli.entity.FilterV1 import app.pachli.entity.FilterV1
import app.pachli.interfaces.ActionButtonActivity
import app.pachli.interfaces.AppBarLayoutHost import app.pachli.interfaces.AppBarLayoutHost
import app.pachli.util.unsafeLazy
import app.pachli.util.viewBinding import app.pachli.util.viewBinding
import at.connyduck.calladapter.networkresult.fold import at.connyduck.calladapter.networkresult.fold
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -47,7 +51,7 @@ import javax.inject.Inject
* the user's favourites, bookmarks, etc. * the user's favourites, bookmarks, etc.
*/ */
@AndroidEntryPoint @AndroidEntryPoint
class StatusListActivity : BottomSheetActivity(), AppBarLayoutHost { class StatusListActivity : BottomSheetActivity(), AppBarLayoutHost, ActionButtonActivity {
@Inject @Inject
lateinit var eventHub: EventHub lateinit var eventHub: EventHub
@ -57,6 +61,8 @@ class StatusListActivity : BottomSheetActivity(), AppBarLayoutHost {
override val appBarLayout: AppBarLayout override val appBarLayout: AppBarLayout
get() = binding.includedToolbar.appbar get() = binding.includedToolbar.appbar
override val actionButton: FloatingActionButton? by unsafeLazy { binding.composeButton }
/** /**
* If showing statuses with a hashtag, the hashtag being used, without the * If showing statuses with a hashtag, the hashtag being used, without the
* leading `#`. * leading `#`.
@ -103,6 +109,27 @@ class StatusListActivity : BottomSheetActivity(), AppBarLayoutHost {
replace(R.id.fragmentContainer, fragment) replace(R.id.fragmentContainer, fragment)
} }
} }
val composeIntent = when (timelineKind) {
is TimelineKind.Tag -> {
val tag = (timelineKind as TimelineKind.Tag).tags.first()
ComposeActivity.startIntent(
this,
ComposeActivity.ComposeOptions(
content = getString(R.string.title_tag_with_initial_position).format(tag),
initialCursorPosition = ComposeActivity.InitialCursorPosition.START,
),
)
}
else -> null
}
if (composeIntent == null) {
binding.composeButton.hide()
} else {
binding.composeButton.setOnClickListener { startActivity(composeIntent) }
binding.composeButton.show()
}
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {

View File

@ -260,7 +260,7 @@ class ComposeActivity :
} }
setupLanguageSpinner(getInitialLanguages(composeOptions?.language, activeAccount)) setupLanguageSpinner(getInitialLanguages(composeOptions?.language, activeAccount))
setupComposeField(sharedPreferencesRepository, viewModel.startingText) setupComposeField(sharedPreferencesRepository, viewModel.startingText, composeOptions)
setupContentWarningField(composeOptions?.contentWarning) setupContentWarningField(composeOptions?.contentWarning)
setupPollView() setupPollView()
applyShareIntent(intent, savedInstanceState) applyShareIntent(intent, savedInstanceState)
@ -364,7 +364,11 @@ class ComposeActivity :
binding.composeContentWarningField.doOnTextChanged { _, _, _, _ -> updateVisibleCharactersLeft() } binding.composeContentWarningField.doOnTextChanged { _, _, _, _ -> updateVisibleCharactersLeft() }
} }
private fun setupComposeField(preferences: SharedPreferencesRepository, startingText: String?) { private fun setupComposeField(
preferences: SharedPreferencesRepository,
startingText: String?,
composeOptions: ComposeOptions?,
) {
binding.composeEditField.setOnReceiveContentListener(this) binding.composeEditField.setOnReceiveContentListener(this)
binding.composeEditField.setOnKeyListener { _, keyCode, event -> this.onKeyDown(keyCode, event) } binding.composeEditField.setOnKeyListener { _, keyCode, event -> this.onKeyDown(keyCode, event) }
@ -380,7 +384,13 @@ class ComposeActivity :
binding.composeEditField.setTokenizer(ComposeTokenizer()) binding.composeEditField.setTokenizer(ComposeTokenizer())
binding.composeEditField.setText(startingText) binding.composeEditField.setText(startingText)
binding.composeEditField.setSelection(binding.composeEditField.length())
when (composeOptions?.initialCursorPosition ?: InitialCursorPosition.END) {
InitialCursorPosition.START -> binding.composeEditField.setSelection(0)
InitialCursorPosition.END -> binding.composeEditField.setSelection(
binding.composeEditField.length(),
)
}
val mentionColour = binding.composeEditField.linkTextColors.defaultColor val mentionColour = binding.composeEditField.linkTextColors.defaultColor
highlightSpans(binding.composeEditField.text, mentionColour) highlightSpans(binding.composeEditField.text, mentionColour)
@ -1305,6 +1315,15 @@ class ComposeActivity :
EDIT_SCHEDULED, EDIT_SCHEDULED,
} }
/**
* Initial position of the cursor in EditText when the compose button is clicked
* in a hashtag timeline
*/
enum class InitialCursorPosition {
START,
END,
}
@Parcelize @Parcelize
data class ComposeOptions( data class ComposeOptions(
// Let's keep fields var until all consumers are Kotlin // Let's keep fields var until all consumers are Kotlin
@ -1329,6 +1348,7 @@ class ComposeActivity :
var language: String? = null, var language: String? = null,
var statusId: String? = null, var statusId: String? = null,
var kind: ComposeKind? = null, var kind: ComposeKind? = null,
var initialCursorPosition: InitialCursorPosition = InitialCursorPosition.END,
) : Parcelable ) : Parcelable
companion object { companion object {

View File

@ -16,6 +16,15 @@
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" /> app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/composeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fabMargin"
android:contentDescription="@string/action_compose"
app:layout_anchor="@id/fragmentContainer"
app:layout_anchorGravity="bottom|end"
app:srcCompat="@drawable/ic_create_24dp" />
<include layout="@layout/item_status_bottom_sheet" /> <include layout="@layout/item_status_bottom_sheet" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -9,6 +9,7 @@
<string name="at_symbol" translatable="false">\@</string> <string name="at_symbol" translatable="false">\@</string>
<string name="hash_symbol" translatable="false">#</string> <string name="hash_symbol" translatable="false">#</string>
<string name="title_tag" translatable="false">#%s</string> <string name="title_tag" translatable="false">#%s</string>
<string name="title_tag_with_initial_position" translatable="false">" #%s"</string>
<string name="emoji_shortcode_format" translatable="false">:%s:</string> <string name="emoji_shortcode_format" translatable="false">:%s:</string>
<string name="post_timestamp_with_edited_indicator" translatable="false">%s *</string> <string name="post_timestamp_with_edited_indicator" translatable="false">%s *</string>