feat: Edit a matching filter directly from the timeline (#819)

Previously, if a status was filtered with "WARN" and was shown in the
timeline with the name of the filter, and the user then decided to
change
that filter, they had to:

1. Open the left navigation menu
2. Navigate to "Account preferences"
3. Open "Filters"
4. Find the filter they want to edit, tap it
5. Make the change, and save
6. "Back" to the list of filters
7. "Back" to "Account preferences"
8. "Back" to the timeline

That's a lot of clicks for a simple action.

Change this. Now the filtered status includes an "Edit filter" button
that takes the user directly to step 5, and when they press "Back" they
return directly to the timeline.

To do this create a new filter action, `onEditFilterById`. Update the
listeners to launch `EditFilterActivity` if appropriate.

Modify `item_status_filtered.xml` to show the new button.

Update the accessibility delegate to show just the "Show anyway" and
"Edit filter" actions. Modify `FilterableStatusViewHolder` to expose
the information it needs to do this.
This commit is contained in:
Nik Clayton 2024-07-19 13:45:24 +02:00 committed by GitHub
parent 7ef692c2c8
commit 6b55d107c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 185 additions and 93 deletions

View File

@ -18,6 +18,7 @@
package app.pachli.adapter package app.pachli.adapter
import android.view.View import android.view.View
import androidx.core.text.HtmlCompat
import app.pachli.R import app.pachli.R
import app.pachli.core.data.model.StatusDisplayOptions import app.pachli.core.data.model.StatusDisplayOptions
import app.pachli.core.network.model.Filter import app.pachli.core.network.model.Filter
@ -28,6 +29,8 @@ import app.pachli.viewdata.IStatusViewData
open class FilterableStatusViewHolder<T : IStatusViewData>( open class FilterableStatusViewHolder<T : IStatusViewData>(
private val binding: ItemStatusWrapperBinding, private val binding: ItemStatusWrapperBinding,
) : StatusViewHolder<T>(binding.statusContainer, binding.root) { ) : StatusViewHolder<T>(binding.statusContainer, binding.root) {
/** The filter that matched the status, null if the status is not being filtered. */
var matchedFilter: Filter? = null
override fun setupWithStatus( override fun setupWithStatus(
viewData: T, viewData: T,
@ -44,42 +47,38 @@ open class FilterableStatusViewHolder<T : IStatusViewData>(
listener: StatusActionListener<T>, listener: StatusActionListener<T>,
) { ) {
if (status.filterAction !== Filter.Action.WARN) { if (status.filterAction !== Filter.Action.WARN) {
showFilteredPlaceholder(false) matchedFilter = null
setPlaceholderVisibility(false)
return return
} }
// Shouldn't be necessary given the previous test against getFilterAction(), status.actionable.filtered?.find { it.filter.action === Filter.Action.WARN }?.let { result ->
// but guards against a possible NPE. See the TODO in StatusViewData.filterAction this.matchedFilter = result.filter
// for more details. setPlaceholderVisibility(true)
val filterResults = status.actionable.filtered
if (filterResults.isNullOrEmpty()) {
showFilteredPlaceholder(false)
return
}
var matchedFilter: Filter? = null
for ((filter) in filterResults) {
if (filter.action === Filter.Action.WARN) {
matchedFilter = filter
break
}
}
// Guard against a possible NPE val label = HtmlCompat.fromHtml(
if (matchedFilter == null) { context.getString(
showFilteredPlaceholder(false)
return
}
showFilteredPlaceholder(true)
binding.statusFilteredPlaceholder.statusFilterLabel.text = context.getString(
R.string.status_filter_placeholder_label_format, R.string.status_filter_placeholder_label_format,
matchedFilter.title, result.filter.title,
),
HtmlCompat.FROM_HTML_MODE_LEGACY,
) )
binding.root.contentDescription = label
binding.statusFilteredPlaceholder.statusFilterLabel.text = label
binding.statusFilteredPlaceholder.statusFilterShowAnyway.setOnClickListener { binding.statusFilteredPlaceholder.statusFilterShowAnyway.setOnClickListener {
listener.clearWarningAction(status) listener.clearWarningAction(status)
} }
binding.statusFilteredPlaceholder.statusFilterEditFilter.setOnClickListener {
listener.onEditFilterById(result.filter.id)
}
} ?: {
matchedFilter = null
setPlaceholderVisibility(false)
}
} }
private fun showFilteredPlaceholder(show: Boolean) { private fun setPlaceholderVisibility(show: Boolean) {
binding.statusContainer.root.visibility = if (show) View.GONE else View.VISIBLE binding.statusContainer.root.visibility = if (show) View.GONE else View.VISIBLE
binding.statusFilteredPlaceholder.root.visibility = if (show) View.VISIBLE else View.GONE binding.statusFilteredPlaceholder.root.visibility = if (show) View.VISIBLE else View.GONE
} }

View File

@ -11,7 +11,6 @@ import android.view.ViewGroup
import android.widget.Button import android.widget.Button
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
@ -100,7 +99,7 @@ abstract class StatusBaseViewHolder<T : IStatusViewData> protected constructor(i
private val contentWarningDescription: TextView = itemView.findViewById(R.id.status_content_warning_description) private val contentWarningDescription: TextView = itemView.findViewById(R.id.status_content_warning_description)
private val pollView: PollView = itemView.findViewById(R.id.status_poll) private val pollView: PollView = itemView.findViewById(R.id.status_poll)
private val cardView: PreviewCardView? = itemView.findViewById(R.id.status_card_view) private val cardView: PreviewCardView? = itemView.findViewById(R.id.status_card_view)
private val filteredPlaceholder: LinearLayout? = itemView.findViewById(R.id.status_filtered_placeholder) private val filteredPlaceholder: ConstraintLayout? = itemView.findViewById(R.id.status_filtered_placeholder)
private val filteredPlaceholderLabel: TextView? = itemView.findViewById(R.id.status_filter_label) private val filteredPlaceholderLabel: TextView? = itemView.findViewById(R.id.status_filter_label)
private val filteredPlaceholderShowButton: Button? = itemView.findViewById(R.id.status_filter_show_anyway) private val filteredPlaceholderShowButton: Button? = itemView.findViewById(R.id.status_filter_show_anyway)
private val statusContainer: ConstraintLayout? = itemView.findViewById(R.id.status_container) private val statusContainer: ConstraintLayout? = itemView.findViewById(R.id.status_container)

View File

@ -346,6 +346,9 @@ class ConversationsFragment :
override fun clearWarningAction(viewData: ConversationViewData) { override fun clearWarningAction(viewData: ConversationViewData) {
} }
// Filters don't apply in conversations
override fun onEditFilterById(filterId: String) {}
override fun onReselect() { override fun onReselect() {
if (isAdded) { if (isAdded) {
binding.recyclerView.layoutManager?.scrollToPosition(0) binding.recyclerView.layoutManager?.scrollToPosition(0)

View File

@ -49,11 +49,14 @@ import app.pachli.R
import app.pachli.adapter.StatusBaseViewHolder import app.pachli.adapter.StatusBaseViewHolder
import app.pachli.components.timeline.TimelineLoadStateAdapter import app.pachli.components.timeline.TimelineLoadStateAdapter
import app.pachli.core.activity.ReselectableFragment import app.pachli.core.activity.ReselectableFragment
import app.pachli.core.activity.extensions.TransitionKind
import app.pachli.core.activity.extensions.startActivityWithTransition
import app.pachli.core.activity.openLink import app.pachli.core.activity.openLink
import app.pachli.core.common.extensions.hide import app.pachli.core.common.extensions.hide
import app.pachli.core.common.extensions.show import app.pachli.core.common.extensions.show
import app.pachli.core.common.extensions.viewBinding import app.pachli.core.common.extensions.viewBinding
import app.pachli.core.navigation.AttachmentViewData.Companion.list import app.pachli.core.navigation.AttachmentViewData.Companion.list
import app.pachli.core.navigation.EditFilterActivityIntent
import app.pachli.core.network.model.Filter import app.pachli.core.network.model.Filter
import app.pachli.core.network.model.Notification import app.pachli.core.network.model.Notification
import app.pachli.core.network.model.Poll import app.pachli.core.network.model.Poll
@ -589,6 +592,13 @@ class NotificationsFragment :
} }
} }
override fun onEditFilterById(filterId: String) {
requireActivity().startActivityWithTransition(
EditFilterActivityIntent.edit(requireContext(), filterId),
TransitionKind.SLIDE_FROM_END,
)
}
override fun onNotificationContentCollapsedChange( override fun onNotificationContentCollapsedChange(
isCollapsed: Boolean, isCollapsed: Boolean,
viewData: NotificationViewData, viewData: NotificationViewData,

View File

@ -40,13 +40,16 @@ import app.pachli.R
import app.pachli.components.search.adapter.SearchStatusesAdapter import app.pachli.components.search.adapter.SearchStatusesAdapter
import app.pachli.core.activity.AccountSelectionListener import app.pachli.core.activity.AccountSelectionListener
import app.pachli.core.activity.BaseActivity import app.pachli.core.activity.BaseActivity
import app.pachli.core.activity.extensions.TransitionKind
import app.pachli.core.activity.extensions.startActivityWithDefaultTransition import app.pachli.core.activity.extensions.startActivityWithDefaultTransition
import app.pachli.core.activity.extensions.startActivityWithTransition
import app.pachli.core.activity.openLink import app.pachli.core.activity.openLink
import app.pachli.core.data.repository.StatusDisplayOptionsRepository import app.pachli.core.data.repository.StatusDisplayOptionsRepository
import app.pachli.core.database.model.AccountEntity import app.pachli.core.database.model.AccountEntity
import app.pachli.core.navigation.AttachmentViewData import app.pachli.core.navigation.AttachmentViewData
import app.pachli.core.navigation.ComposeActivityIntent import app.pachli.core.navigation.ComposeActivityIntent
import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions import app.pachli.core.navigation.ComposeActivityIntent.ComposeOptions
import app.pachli.core.navigation.EditFilterActivityIntent
import app.pachli.core.navigation.ReportActivityIntent import app.pachli.core.navigation.ReportActivityIntent
import app.pachli.core.navigation.ViewMediaActivityIntent import app.pachli.core.navigation.ViewMediaActivityIntent
import app.pachli.core.network.model.Attachment import app.pachli.core.network.model.Attachment
@ -162,6 +165,13 @@ class SearchStatusesFragment : SearchFragment<StatusViewData>(), StatusActionLis
viewModel.reblog(viewData, reblog) viewModel.reblog(viewData, reblog)
} }
override fun onEditFilterById(filterId: String) {
requireActivity().startActivityWithTransition(
EditFilterActivityIntent.edit(requireContext(), filterId),
TransitionKind.SLIDE_FROM_END,
)
}
companion object { companion object {
fun newInstance() = SearchStatusesFragment() fun newInstance() = SearchStatusesFragment()
} }

View File

@ -51,7 +51,9 @@ import app.pachli.components.timeline.viewmodel.TimelineViewModel
import app.pachli.components.timeline.viewmodel.UiSuccess import app.pachli.components.timeline.viewmodel.UiSuccess
import app.pachli.core.activity.RefreshableFragment import app.pachli.core.activity.RefreshableFragment
import app.pachli.core.activity.ReselectableFragment import app.pachli.core.activity.ReselectableFragment
import app.pachli.core.activity.extensions.TransitionKind
import app.pachli.core.activity.extensions.startActivityWithDefaultTransition import app.pachli.core.activity.extensions.startActivityWithDefaultTransition
import app.pachli.core.activity.extensions.startActivityWithTransition
import app.pachli.core.common.extensions.hide import app.pachli.core.common.extensions.hide
import app.pachli.core.common.extensions.show import app.pachli.core.common.extensions.show
import app.pachli.core.common.extensions.viewBinding import app.pachli.core.common.extensions.viewBinding
@ -59,6 +61,7 @@ import app.pachli.core.database.model.TranslationState
import app.pachli.core.model.Timeline import app.pachli.core.model.Timeline
import app.pachli.core.navigation.AccountListActivityIntent import app.pachli.core.navigation.AccountListActivityIntent
import app.pachli.core.navigation.AttachmentViewData import app.pachli.core.navigation.AttachmentViewData
import app.pachli.core.navigation.EditFilterActivityIntent
import app.pachli.core.network.model.Poll import app.pachli.core.network.model.Poll
import app.pachli.core.network.model.Status import app.pachli.core.network.model.Status
import app.pachli.core.ui.ActionButtonScrollListener import app.pachli.core.ui.ActionButtonScrollListener
@ -615,6 +618,13 @@ class TimelineFragment :
viewModel.clearWarning(viewData) viewModel.clearWarning(viewData)
} }
override fun onEditFilterById(filterId: String) {
requireActivity().startActivityWithTransition(
EditFilterActivityIntent.edit(requireContext(), filterId),
TransitionKind.SLIDE_FROM_END,
)
}
override fun onMore(view: View, viewData: StatusViewData) { override fun onMore(view: View, viewData: StatusViewData) {
super.more(view, viewData) super.more(view, viewData)
} }

View File

@ -33,7 +33,9 @@ import androidx.recyclerview.widget.SimpleItemAnimator
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import app.pachli.R import app.pachli.R
import app.pachli.components.viewthread.edits.ViewEditsFragment import app.pachli.components.viewthread.edits.ViewEditsFragment
import app.pachli.core.activity.extensions.TransitionKind
import app.pachli.core.activity.extensions.startActivityWithDefaultTransition import app.pachli.core.activity.extensions.startActivityWithDefaultTransition
import app.pachli.core.activity.extensions.startActivityWithTransition
import app.pachli.core.activity.openLink import app.pachli.core.activity.openLink
import app.pachli.core.common.extensions.hide import app.pachli.core.common.extensions.hide
import app.pachli.core.common.extensions.show import app.pachli.core.common.extensions.show
@ -41,6 +43,7 @@ import app.pachli.core.common.extensions.viewBinding
import app.pachli.core.designsystem.R as DR import app.pachli.core.designsystem.R as DR
import app.pachli.core.navigation.AccountListActivityIntent import app.pachli.core.navigation.AccountListActivityIntent
import app.pachli.core.navigation.AttachmentViewData.Companion.list import app.pachli.core.navigation.AttachmentViewData.Companion.list
import app.pachli.core.navigation.EditFilterActivityIntent
import app.pachli.core.network.model.Poll import app.pachli.core.network.model.Poll
import app.pachli.core.network.model.Status import app.pachli.core.network.model.Status
import app.pachli.core.ui.extensions.getErrorString import app.pachli.core.ui.extensions.getErrorString
@ -323,6 +326,13 @@ class ViewThreadFragment :
// there are no reblogs in threads // there are no reblogs in threads
} }
override fun onEditFilterById(filterId: String) {
requireActivity().startActivityWithTransition(
EditFilterActivityIntent.edit(requireContext(), filterId),
TransitionKind.SLIDE_FROM_END,
)
}
override fun onExpandedChange(viewData: StatusViewData, expanded: Boolean) { override fun onExpandedChange(viewData: StatusViewData, expanded: Boolean) {
viewModel.changeExpanded(expanded, viewData) viewModel.changeExpanded(expanded, viewData)
} }

View File

@ -59,4 +59,7 @@ interface StatusActionListener<T : IStatusViewData> : LinkListener {
fun onVoteInPoll(viewData: T, poll: Poll, choices: List<Int>) fun onVoteInPoll(viewData: T, poll: Poll, choices: List<Int>)
fun onShowEdits(statusId: String) {} fun onShowEdits(statusId: String) {}
fun clearWarningAction(viewData: T) fun clearWarningAction(viewData: T)
/** Edit the filter that matched this status. */
fun onEditFilterById(filterId: String)
} }

View File

@ -15,6 +15,7 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.Accessibilit
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate
import app.pachli.R import app.pachli.R
import app.pachli.adapter.FilterableStatusViewHolder
import app.pachli.adapter.StatusBaseViewHolder import app.pachli.adapter.StatusBaseViewHolder
import app.pachli.core.activity.openLink import app.pachli.core.activity.openLink
import app.pachli.core.network.model.Status.Companion.MAX_MEDIA_ATTACHMENTS import app.pachli.core.network.model.Status.Companion.MAX_MEDIA_ATTACHMENTS
@ -48,6 +49,13 @@ class ListStatusAccessibilityDelegate<T : IStatusViewData>(
) { ) {
super.onInitializeAccessibilityNodeInfo(host, info) super.onInitializeAccessibilityNodeInfo(host, info)
val viewHolder = recyclerView.findContainingViewHolder(host)
if (viewHolder is FilterableStatusViewHolder<*> && viewHolder.matchedFilter != null) {
info.addAction(showAnywayAction)
info.addAction(editFilterAction)
return
}
val pos = recyclerView.getChildAdapterPosition(host) val pos = recyclerView.getChildAdapterPosition(host)
val status = statusProvider.getStatus(pos) ?: return val status = statusProvider.getStatus(pos) ?: return
@ -183,6 +191,14 @@ class ListStatusAccessibilityDelegate<T : IStatusViewData>(
app.pachli.core.ui.R.id.action_more -> { app.pachli.core.ui.R.id.action_more -> {
statusActionListener.onMore(host, status) statusActionListener.onMore(host, status)
} }
app.pachli.core.ui.R.id.action_show_anyway -> statusActionListener.clearWarningAction(status)
app.pachli.core.ui.R.id.action_edit_filter -> {
(recyclerView.findContainingViewHolder(host) as? FilterableStatusViewHolder<*>)?.matchedFilter?.let {
statusActionListener.onEditFilterById(it.id)
return@let true
} ?: false
}
else -> return super.performAccessibilityAction(host, action, args) else -> return super.performAccessibilityAction(host, action, args)
} }
return true return true
@ -378,5 +394,15 @@ class ListStatusAccessibilityDelegate<T : IStatusViewData>(
context.getString(app.pachli.core.ui.R.string.action_more), context.getString(app.pachli.core.ui.R.string.action_more),
) )
private val showAnywayAction = AccessibilityActionCompat(
app.pachli.core.ui.R.id.action_show_anyway,
context.getString(R.string.status_filtered_show_anyway),
)
private val editFilterAction = AccessibilityActionCompat(
app.pachli.core.ui.R.id.action_edit_filter,
context.getString(R.string.filter_edit_title),
)
private data class LinkSpanInfo(val text: String, val link: String) private data class LinkSpanInfo(val text: String, val link: String)
} }

View File

@ -1,33 +1,52 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/status_filtered_placeholder" android:id="@+id/status_filtered_placeholder"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="wrap_content"
android:paddingStart="14dp"
android:paddingEnd="14dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:minHeight="48dp">
<TextView <TextView
android:id="@+id/status_filter_label" android:id="@+id/status_filter_label"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:importantForAccessibility="no"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginBottom="0dp"
android:textColor="?android:textColorTertiary" android:textColor="?android:textColorTertiary"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"
android:textAlignment="center" android:textAlignment="center"
android:textIsSelectable="false" android:textIsSelectable="false"
tools:text="Filter: MyFilter" tools:text="Filter: MyFilter" />
/>
<Button
android:id="@+id/status_filter_edit_filter"
style="@style/AppButton.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:importantForAccessibility="no"
android:text="@string/filter_edit_title"
android:textSize="?attr/status_text_medium"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/status_filter_show_anyway"
app:layout_constraintTop_toTopOf="@+id/status_filter_show_anyway" />
<Button <Button
android:id="@+id/status_filter_show_anyway" android:id="@+id/status_filter_show_anyway"
android:layout_width="match_parent" style="@style/AppButton.Outlined"
android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="0dp" android:importantForAccessibility="no"
style="@style/AppButton.TextButton" android:layout_marginTop="8dp"
android:textStyle="bold"
android:textSize="?attr/status_text_medium"
android:text="@string/status_filtered_show_anyway" android:text="@string/status_filtered_show_anyway"
/> android:textSize="?attr/status_text_medium"
app:layout_constraintStart_toEndOf="@id/status_filter_edit_filter"
</LinearLayout> app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_filter_label" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -587,7 +587,7 @@
<string name="filter_action_warn">تحذير</string> <string name="filter_action_warn">تحذير</string>
<string name="title_public_trending_links">الروابط الشائعة</string> <string name="title_public_trending_links">الروابط الشائعة</string>
<string name="title_tab_public_trending_links">الروابط</string> <string name="title_tab_public_trending_links">الروابط</string>
<string name="status_filter_placeholder_label_format">مُصفّى: %s</string> <string name="status_filter_placeholder_label_format">مُصفّى: &lt;b>%1$s&lt;/b></string>
<string name="update_dialog_neutral">لا تذكرني بهذه النسخة</string> <string name="update_dialog_neutral">لا تذكرني بهذه النسخة</string>
<string name="ui_error_reject_follow_request">فشل رفض طلب المتابعة: %s</string> <string name="ui_error_reject_follow_request">فشل رفض طلب المتابعة: %s</string>
<string name="reaction_name_and_count">%1$s %2$d</string> <string name="reaction_name_and_count">%1$s %2$d</string>

View File

@ -551,7 +551,7 @@
<string name="description_login">Працуе амаль заўсёды. Даныя не ўцякаюць у іншыя праграмы.</string> <string name="description_login">Працуе амаль заўсёды. Даныя не ўцякаюць у іншыя праграмы.</string>
<string name="notification_unknown_name">Невядома</string> <string name="notification_unknown_name">Невядома</string>
<string name="status_filtered_show_anyway">Усё роўна паказаць</string> <string name="status_filtered_show_anyway">Усё роўна паказаць</string>
<string name="status_filter_placeholder_label_format">Адфільтрована: %s</string> <string name="status_filter_placeholder_label_format">Адфільтрована: &lt;b>%1$s&lt;/b></string>
<string name="pref_title_account_filter_keywords">Профілі</string> <string name="pref_title_account_filter_keywords">Профілі</string>
<string name="title_public_trending_hashtags">Папулярныя хэштэгі</string> <string name="title_public_trending_hashtags">Папулярныя хэштэгі</string>
<string name="description_browser_login">Можа падтрымліваць дадатковыя метады праверкі сапраўднасці, але для гэтага патрэбны адпаведны браузер.</string> <string name="description_browser_login">Можа падтрымліваць дадатковыя метады праверкі сапраўднасці, але для гэтага патрэбны адпаведны браузер.</string>

View File

@ -537,7 +537,7 @@
<string name="dialog_follow_hashtag_title">Segueix hashtag</string> <string name="dialog_follow_hashtag_title">Segueix hashtag</string>
<string name="dialog_follow_hashtag_hint">#hashtag</string> <string name="dialog_follow_hashtag_hint">#hashtag</string>
<string name="notification_unknown_name">Desconegut</string> <string name="notification_unknown_name">Desconegut</string>
<string name="status_filter_placeholder_label_format">Filtrat: %s</string> <string name="status_filter_placeholder_label_format">Filtrat: &lt;b>%1$s&lt;/b></string>
<string name="pref_title_account_filter_keywords">Perfils</string> <string name="pref_title_account_filter_keywords">Perfils</string>
<string name="status_filtered_show_anyway">Mostra de totes maneres</string> <string name="status_filtered_show_anyway">Mostra de totes maneres</string>
<string name="socket_timeout_exception">El contacte amb el teu servidor ha trigat massa</string> <string name="socket_timeout_exception">El contacte amb el teu servidor ha trigat massa</string>

View File

@ -585,7 +585,7 @@
<string name="dialog_follow_hashtag_hint">#hashnod</string> <string name="dialog_follow_hashtag_hint">#hashnod</string>
<string name="notification_unknown_name">Anhysbys</string> <string name="notification_unknown_name">Anhysbys</string>
<string name="status_filtered_show_anyway">Dangos beth bynnag</string> <string name="status_filtered_show_anyway">Dangos beth bynnag</string>
<string name="status_filter_placeholder_label_format">Hidlwyd: %s</string> <string name="status_filter_placeholder_label_format">Hidlwyd: &lt;b>%1$s&lt;/b></string>
<string name="pref_title_account_filter_keywords">Proffiliau</string> <string name="pref_title_account_filter_keywords">Proffiliau</string>
<string name="socket_timeout_exception">Cymrodd hi\'n rhy hir i gysylltu â\'ch gweinydd</string> <string name="socket_timeout_exception">Cymrodd hi\'n rhy hir i gysylltu â\'ch gweinydd</string>
<string name="ui_error_bookmark_fmt">Methodd tudalnodi\'r neges: %1$s</string> <string name="ui_error_bookmark_fmt">Methodd tudalnodi\'r neges: %1$s</string>

View File

@ -554,7 +554,7 @@
<string name="notification_unknown_name">Unbekannt</string> <string name="notification_unknown_name">Unbekannt</string>
<string name="ui_error_clear_notifications">Löschen der Benachrichtigungen schlug fehl: %s</string> <string name="ui_error_clear_notifications">Löschen der Benachrichtigungen schlug fehl: %s</string>
<string name="ui_error_reject_follow_request">Ablehnen der Folgeanfrage schlug fehl: %s</string> <string name="ui_error_reject_follow_request">Ablehnen der Folgeanfrage schlug fehl: %s</string>
<string name="status_filter_placeholder_label_format">Gefiltert: %s</string> <string name="status_filter_placeholder_label_format">Gefiltert: &lt;b>%1$s&lt;/b></string>
<string name="pref_title_account_filter_keywords">Profile</string> <string name="pref_title_account_filter_keywords">Profile</string>
<string name="hint_filter_title">Mein Filter</string> <string name="hint_filter_title">Mein Filter</string>
<string name="label_filter_title">Titel</string> <string name="label_filter_title">Titel</string>

View File

@ -554,7 +554,7 @@
<string name="pref_title_account_filter_keywords">Perfiles</string> <string name="pref_title_account_filter_keywords">Perfiles</string>
<string name="notification_unknown_name">Desconocido</string> <string name="notification_unknown_name">Desconocido</string>
<string name="status_filtered_show_anyway">Mostrar de todas formas</string> <string name="status_filtered_show_anyway">Mostrar de todas formas</string>
<string name="status_filter_placeholder_label_format">Filtrado: %s</string> <string name="status_filter_placeholder_label_format">Filtrado: &lt;b>%1$s&lt;/b></string>
<string name="pref_title_show_stat_inline">Mostrar estadísticas de la entrada en la línea de tiempo</string> <string name="pref_title_show_stat_inline">Mostrar estadísticas de la entrada en la línea de tiempo</string>
<string name="socket_timeout_exception">Contactar con tu servidor ha tardado demasiado tiempo</string> <string name="socket_timeout_exception">Contactar con tu servidor ha tardado demasiado tiempo</string>
<string name="select_list_empty">Todavía no tienes listas</string> <string name="select_list_empty">Todavía no tienes listas</string>

View File

@ -551,7 +551,7 @@
<string name="ui_error_reblog_fmt">%1$s: تقویت فرسته شکست خورد</string> <string name="ui_error_reblog_fmt">%1$s: تقویت فرسته شکست خورد</string>
<string name="ui_error_reject_follow_request">رد کردن درخواست پی‌گیری شکست خورد: %s</string> <string name="ui_error_reject_follow_request">رد کردن درخواست پی‌گیری شکست خورد: %s</string>
<string name="status_filtered_show_anyway">نمایش به هر روی</string> <string name="status_filtered_show_anyway">نمایش به هر روی</string>
<string name="status_filter_placeholder_label_format">پالوده: %s</string> <string name="status_filter_placeholder_label_format">پالوده: &lt;b>%1$s&lt;/b></string>
<string name="pref_title_account_filter_keywords">نمایه‌ها</string> <string name="pref_title_account_filter_keywords">نمایه‌ها</string>
<string name="hint_filter_title">پالایه‌ام</string> <string name="hint_filter_title">پالایه‌ام</string>
<string name="label_filter_title">عنوان</string> <string name="label_filter_title">عنوان</string>

View File

@ -364,7 +364,7 @@
<string name="send_post_notification_error_title">Julkaisun lähettäminen epäonnistui</string> <string name="send_post_notification_error_title">Julkaisun lähettäminen epäonnistui</string>
<string name="pref_title_notification_filter_follow_requests">seuraamista pyydetty</string> <string name="pref_title_notification_filter_follow_requests">seuraamista pyydetty</string>
<string name="pref_title_notification_filters">Ilmoita kun</string> <string name="pref_title_notification_filters">Ilmoita kun</string>
<string name="status_filter_placeholder_label_format">Suodatettu: %s</string> <string name="status_filter_placeholder_label_format">Suodatettu: &lt;b>%1$s&lt;/b></string>
<string name="pref_main_nav_position_option_bottom">Alareuna</string> <string name="pref_main_nav_position_option_bottom">Alareuna</string>
<string name="pref_title_notification_filter_poll">päättyneet äänestykset</string> <string name="pref_title_notification_filter_poll">päättyneet äänestykset</string>
<string name="abbreviated_in_seconds">%ds</string> <string name="abbreviated_in_seconds">%ds</string>

View File

@ -528,7 +528,7 @@
<string name="pref_title_notification_filter_reports">il y a un nouveau signalement</string> <string name="pref_title_notification_filter_reports">il y a un nouveau signalement</string>
<string name="pref_title_account_filter_keywords">Profils</string> <string name="pref_title_account_filter_keywords">Profils</string>
<string name="status_filtered_show_anyway">Montrer quand même</string> <string name="status_filtered_show_anyway">Montrer quand même</string>
<string name="status_filter_placeholder_label_format">Caché : %s</string> <string name="status_filter_placeholder_label_format">Caché : &lt;b>%1$s&lt;/b></string>
<string name="title_public_trending_hashtags">Hashtags tendance</string> <string name="title_public_trending_hashtags">Hashtags tendance</string>
<string name="send_account_username_to">Partager le nom du compte avec…</string> <string name="send_account_username_to">Partager le nom du compte avec…</string>
<string name="status_created_at_now">maintenant</string> <string name="status_created_at_now">maintenant</string>

View File

@ -569,7 +569,7 @@
<string name="select_list_empty">Chan eil liosta agad fhathast</string> <string name="select_list_empty">Chan eil liosta agad fhathast</string>
<string name="error_list_load">Mearachd a luchdadh nan liostaichean</string> <string name="error_list_load">Mearachd a luchdadh nan liostaichean</string>
<string name="status_filtered_show_anyway">Seall e co-dhiù</string> <string name="status_filtered_show_anyway">Seall e co-dhiù</string>
<string name="status_filter_placeholder_label_format">Criathraichte: %s</string> <string name="status_filter_placeholder_label_format">Criathraichte: &lt;b>%1$s&lt;/b></string>
<string name="pref_title_account_filter_keywords">Pròifilean</string> <string name="pref_title_account_filter_keywords">Pròifilean</string>
<string name="hint_filter_title">A chriathrag agam</string> <string name="hint_filter_title">A chriathrag agam</string>
<string name="filter_description_warn">Falaich le rabhadh</string> <string name="filter_description_warn">Falaich le rabhadh</string>

View File

@ -550,7 +550,7 @@
<string name="ui_success_accepted_follow_request">Aceptado o seguimento</string> <string name="ui_success_accepted_follow_request">Aceptado o seguimento</string>
<string name="ui_success_rejected_follow_request">Bloqueada a solicitude de seguimento</string> <string name="ui_success_rejected_follow_request">Bloqueada a solicitude de seguimento</string>
<string name="status_filtered_show_anyway">Mostrar igualmente</string> <string name="status_filtered_show_anyway">Mostrar igualmente</string>
<string name="status_filter_placeholder_label_format">Filtrado: %s</string> <string name="status_filter_placeholder_label_format">Filtrado: &lt;b>%1$s&lt;/b></string>
<string name="pref_title_account_filter_keywords">Perfís</string> <string name="pref_title_account_filter_keywords">Perfís</string>
<string name="label_filter_title">Título</string> <string name="label_filter_title">Título</string>
<string name="filter_action_warn">Aviso</string> <string name="filter_action_warn">Aviso</string>

View File

@ -545,7 +545,7 @@
<string name="ui_error_vote_fmt">Szavazat leadása a szavazásba sikertelen: %1$s</string> <string name="ui_error_vote_fmt">Szavazat leadása a szavazásba sikertelen: %1$s</string>
<string name="ui_error_accept_follow_request">Követési kérelem elfogadása sikertelen: %s</string> <string name="ui_error_accept_follow_request">Követési kérelem elfogadása sikertelen: %s</string>
<string name="status_filtered_show_anyway">Mutatás mindenképpen</string> <string name="status_filtered_show_anyway">Mutatás mindenképpen</string>
<string name="status_filter_placeholder_label_format">Szűrve: %s</string> <string name="status_filter_placeholder_label_format">Szűrve: &lt;b>%1$s&lt;/b></string>
<string name="pref_title_account_filter_keywords">Profil</string> <string name="pref_title_account_filter_keywords">Profil</string>
<string name="socket_timeout_exception">A kapcsolatfelvétel a kiszolgálóddal túl sokáig tartott</string> <string name="socket_timeout_exception">A kapcsolatfelvétel a kiszolgálóddal túl sokáig tartott</string>
<string name="ui_error_bookmark_fmt">Bejegyzés könyvjelzőzése sikertelen: %1$s</string> <string name="ui_error_bookmark_fmt">Bejegyzés könyvjelzőzése sikertelen: %1$s</string>

View File

@ -534,7 +534,7 @@
<string name="dialog_follow_hashtag_hint">#myllumerki</string> <string name="dialog_follow_hashtag_hint">#myllumerki</string>
<string name="notification_unknown_name">Óþekkt</string> <string name="notification_unknown_name">Óþekkt</string>
<string name="status_filtered_show_anyway">Birta samt</string> <string name="status_filtered_show_anyway">Birta samt</string>
<string name="status_filter_placeholder_label_format">Síað: %s</string> <string name="status_filter_placeholder_label_format">Síað: &lt;b>%1$s&lt;/b></string>
<string name="pref_title_account_filter_keywords">Notendasnið</string> <string name="pref_title_account_filter_keywords">Notendasnið</string>
<string name="pref_title_show_stat_inline">Sýna tölfræði færslu í tímalínu</string> <string name="pref_title_show_stat_inline">Sýna tölfræði færslu í tímalínu</string>
<string name="ui_error_favourite_fmt">Mistókst að setja færslu í eftirlæti: %1$s</string> <string name="ui_error_favourite_fmt">Mistókst að setja færslu í eftirlæti: %1$s</string>

View File

@ -593,7 +593,7 @@
<string name="select_list_manage">Gestisci liste</string> <string name="select_list_manage">Gestisci liste</string>
<string name="error_list_load">Errore nel caricamento delle liste</string> <string name="error_list_load">Errore nel caricamento delle liste</string>
<string name="status_filtered_show_anyway">Mostra comunque</string> <string name="status_filtered_show_anyway">Mostra comunque</string>
<string name="status_filter_placeholder_label_format">Filtrato: %s</string> <string name="status_filter_placeholder_label_format">Filtrato: &lt;b>%1$s&lt;/b></string>
<string name="pref_title_account_filter_keywords">Profili</string> <string name="pref_title_account_filter_keywords">Profili</string>
<string name="hint_filter_title">I miei filtri</string> <string name="hint_filter_title">I miei filtri</string>
<string name="action_add">Aggiungi</string> <string name="action_add">Aggiungi</string>

View File

@ -595,7 +595,7 @@
<string name="pref_title_font_family">フォント</string> <string name="pref_title_font_family">フォント</string>
<string name="description_poll">選択肢付きの投票: %1$s, %2$s, %3$s, %4$s; %5$s</string> <string name="description_poll">選択肢付きの投票: %1$s, %2$s, %3$s, %4$s; %5$s</string>
<string name="label_image">画像</string> <string name="label_image">画像</string>
<string name="status_filter_placeholder_label_format">フィルタ済み: %s</string> <string name="status_filter_placeholder_label_format">フィルタ済み: &lt;b>%1$s&lt;/b></string>
<string name="pref_title_show_self_boosts_description">自分の投稿をブーストすること</string> <string name="pref_title_show_self_boosts_description">自分の投稿をブーストすること</string>
<string name="confirmation_hashtag_unmuted">%s を非表示にしました</string> <string name="confirmation_hashtag_unmuted">%s を非表示にしました</string>
<string name="pref_title_show_self_boosts">セルフブーストを表示</string> <string name="pref_title_show_self_boosts">セルフブーストを表示</string>

View File

@ -564,7 +564,7 @@
<string name="account_username_copied">Brukernavn kopiert</string> <string name="account_username_copied">Brukernavn kopiert</string>
<string name="error_status_source_load">Kunne ikke laste status fra tjeneren.</string> <string name="error_status_source_load">Kunne ikke laste status fra tjeneren.</string>
<string name="status_filtered_show_anyway">Vis allikevel</string> <string name="status_filtered_show_anyway">Vis allikevel</string>
<string name="status_filter_placeholder_label_format">Filtrert: %s</string> <string name="status_filter_placeholder_label_format">Filtrert: &lt;b>%1$s&lt;/b></string>
<string name="pref_title_account_filter_keywords">Profiler</string> <string name="pref_title_account_filter_keywords">Profiler</string>
<string name="action_add">Legg til</string> <string name="action_add">Legg til</string>
<string name="filter_keyword_display_format">%s (helt ord)</string> <string name="filter_keyword_display_format">%s (helt ord)</string>

View File

@ -543,7 +543,7 @@
<string name="select_list_manage">Lijsten beheren</string> <string name="select_list_manage">Lijsten beheren</string>
<string name="pref_title_account_filter_keywords">Profielen</string> <string name="pref_title_account_filter_keywords">Profielen</string>
<string name="status_filtered_show_anyway">Toch tonen</string> <string name="status_filtered_show_anyway">Toch tonen</string>
<string name="status_filter_placeholder_label_format">Gefilterd: %s</string> <string name="status_filter_placeholder_label_format">Gefilterd: &lt;b>%1$s&lt;/b></string>
<string name="hint_filter_title">Mijn filter</string> <string name="hint_filter_title">Mijn filter</string>
<string name="label_filter_action">Filteractie</string> <string name="label_filter_action">Filteractie</string>
<string name="filter_action_warn">Waarschuwen</string> <string name="filter_action_warn">Waarschuwen</string>

View File

@ -546,7 +546,7 @@
<string name="ui_success_accepted_follow_request">Demanda dabonament acceptada</string> <string name="ui_success_accepted_follow_request">Demanda dabonament acceptada</string>
<string name="ui_success_rejected_follow_request">Demanda dabonament blocada</string> <string name="ui_success_rejected_follow_request">Demanda dabonament blocada</string>
<string name="status_filtered_show_anyway">Afichar ça que la</string> <string name="status_filtered_show_anyway">Afichar ça que la</string>
<string name="status_filter_placeholder_label_format">Filtrat : %s</string> <string name="status_filter_placeholder_label_format">Filtrat : &lt;b>%1$s&lt;/b></string>
<string name="pref_title_account_filter_keywords">Perfils</string> <string name="pref_title_account_filter_keywords">Perfils</string>
<string name="hint_filter_title">Mon filtre</string> <string name="hint_filter_title">Mon filtre</string>
<string name="label_filter_title">Títol</string> <string name="label_filter_title">Títol</string>

View File

@ -531,7 +531,7 @@
<string name="title_public_trending_links">URL em alta</string> <string name="title_public_trending_links">URL em alta</string>
<string name="title_tab_public_trending_links">URL</string> <string name="title_tab_public_trending_links">URL</string>
<string name="title_public_trending_hashtags">Hashtags em alta</string> <string name="title_public_trending_hashtags">Hashtags em alta</string>
<string name="status_filter_placeholder_label_format">Filtrado(s): %s</string> <string name="status_filter_placeholder_label_format">Filtrado(s): &lt;b>%1$s&lt;/b></string>
<string name="confirmation_hashtag_unfollowed">#%s deixado de seguir</string> <string name="confirmation_hashtag_unfollowed">#%s deixado de seguir</string>
<string name="ui_error_vote_fmt">Votar na enquete falhou: %1$s</string> <string name="ui_error_vote_fmt">Votar na enquete falhou: %1$s</string>
<string name="update_dialog_neutral">Não me lembre desta versão</string> <string name="update_dialog_neutral">Não me lembre desta versão</string>

View File

@ -556,7 +556,7 @@
<string name="select_list_manage">Hantera listor</string> <string name="select_list_manage">Hantera listor</string>
<string name="select_list_empty">Du har inga listor, än</string> <string name="select_list_empty">Du har inga listor, än</string>
<string name="status_filtered_show_anyway">Visa allafall</string> <string name="status_filtered_show_anyway">Visa allafall</string>
<string name="status_filter_placeholder_label_format">Filtrerad: %s</string> <string name="status_filter_placeholder_label_format">Filtrerad: &lt;b>%1$s&lt;/b></string>
<string name="pref_title_account_filter_keywords">Profiler</string> <string name="pref_title_account_filter_keywords">Profiler</string>
<string name="label_filter_action">Filteråtgärd</string> <string name="label_filter_action">Filteråtgärd</string>
<string name="label_filter_keywords">Nyckelord eller fraser att filtrera</string> <string name="label_filter_keywords">Nyckelord eller fraser att filtrera</string>

View File

@ -568,7 +568,7 @@
<string name="filter_action_hide">Gizle</string> <string name="filter_action_hide">Gizle</string>
<string name="filter_description_warn">Bir uyarı ile gizle</string> <string name="filter_description_warn">Bir uyarı ile gizle</string>
<string name="status_filtered_show_anyway">Yine de göster</string> <string name="status_filtered_show_anyway">Yine de göster</string>
<string name="status_filter_placeholder_label_format">Süzgeçlendi: %s</string> <string name="status_filter_placeholder_label_format">Süzgeçlendi: &lt;b>%1$s&lt;/b></string>
<string name="filter_edit_keyword_title">Anahtar kelimeyi düzenle</string> <string name="filter_edit_keyword_title">Anahtar kelimeyi düzenle</string>
<string name="filter_description_format">%s: %s</string> <string name="filter_description_format">%s: %s</string>
<string name="pref_title_show_stat_inline">Gönderi istatistiklerini sğ akışında göster</string> <string name="pref_title_show_stat_inline">Gönderi istatistiklerini sğ akışında göster</string>

View File

@ -566,7 +566,7 @@
<string name="ui_success_accepted_follow_request">Запит на стеження погоджено</string> <string name="ui_success_accepted_follow_request">Запит на стеження погоджено</string>
<string name="ui_success_rejected_follow_request">Запит на стеження заблоковано</string> <string name="ui_success_rejected_follow_request">Запит на стеження заблоковано</string>
<string name="status_filtered_show_anyway">Усе одно показати</string> <string name="status_filtered_show_anyway">Усе одно показати</string>
<string name="status_filter_placeholder_label_format">Відфільтровано: %s</string> <string name="status_filter_placeholder_label_format">Відфільтровано: &lt;b>%1$s&lt;/b></string>
<string name="pref_title_account_filter_keywords">Профілі</string> <string name="pref_title_account_filter_keywords">Профілі</string>
<string name="label_filter_title">Заголовок</string> <string name="label_filter_title">Заголовок</string>
<string name="filter_action_warn">Попередження</string> <string name="filter_action_warn">Попередження</string>

View File

@ -534,7 +534,7 @@
<string name="ui_success_accepted_follow_request">Đã chấp nhận yêu cầu theo dõi</string> <string name="ui_success_accepted_follow_request">Đã chấp nhận yêu cầu theo dõi</string>
<string name="ui_success_rejected_follow_request">Đã từ chối yêu cầu theo dõi</string> <string name="ui_success_rejected_follow_request">Đã từ chối yêu cầu theo dõi</string>
<string name="status_filtered_show_anyway">Vẫn hiện</string> <string name="status_filtered_show_anyway">Vẫn hiện</string>
<string name="status_filter_placeholder_label_format">Đã lọc: %s</string> <string name="status_filter_placeholder_label_format">Đã lọc: &lt;b>%1$s&lt;/b></string>
<string name="pref_title_account_filter_keywords">Người</string> <string name="pref_title_account_filter_keywords">Người</string>
<string name="hint_filter_title">Bộ lọc của tôi</string> <string name="hint_filter_title">Bộ lọc của tôi</string>
<string name="label_filter_title">Tên bộ lọc</string> <string name="label_filter_title">Tên bộ lọc</string>

View File

@ -548,7 +548,7 @@
<string name="ui_success_accepted_follow_request">关注请求被接受</string> <string name="ui_success_accepted_follow_request">关注请求被接受</string>
<string name="ui_success_rejected_follow_request">关注请求被拦截</string> <string name="ui_success_rejected_follow_request">关注请求被拦截</string>
<string name="status_filtered_show_anyway">仍要显示</string> <string name="status_filtered_show_anyway">仍要显示</string>
<string name="status_filter_placeholder_label_format">已过滤:%s</string> <string name="status_filter_placeholder_label_format">已过滤:&lt;b>%1$s&lt;/b></string>
<string name="pref_title_account_filter_keywords">个人资料</string> <string name="pref_title_account_filter_keywords">个人资料</string>
<string name="hint_filter_title">我的筛选器</string> <string name="hint_filter_title">我的筛选器</string>
<string name="label_filter_title">标题</string> <string name="label_filter_title">标题</string>

View File

@ -357,7 +357,7 @@
<string name="status_count_one_plus">1+</string> <string name="status_count_one_plus">1+</string>
<string name="status_created_at_now">now</string> <string name="status_created_at_now">now</string>
<string name="status_filtered_show_anyway">Show anyway</string> <string name="status_filtered_show_anyway">Show anyway</string>
<string name="status_filter_placeholder_label_format">Filtered: %s</string> <string name="status_filter_placeholder_label_format">Filtered: &lt;b>%1$s&lt;/b></string>
<string name="state_follow_requested">Follow requested</string> <string name="state_follow_requested">Follow requested</string>
<!--These are for timestamps on posts. For example: "16s" or "2d"--> <!--These are for timestamps on posts. For example: "16s" or "2d"-->
<string name="abbreviated_in_years">in %dy</string> <string name="abbreviated_in_years">in %dy</string>

View File

@ -49,4 +49,7 @@
<item name="action_dismiss_follow_suggestion" type="id" /> <item name="action_dismiss_follow_suggestion" type="id" />
<item name="action_follow_account" type="id" /> <item name="action_follow_account" type="id" />
<item name="action_show_anyway" type="id" />
<item name="action_edit_filter" type="id" />
</resources> </resources>