3488 improve profile list (#3507)
Fixes #3488 Working with lists from a profile page and in the normal "lists view" from the drawer now use the same fragment view code. (also) RFC regarding joining different list lists ![grafik](https://user-images.githubusercontent.com/1618905/229463168-397bd943-82d8-4e05-a8bf-9fcf22f6c1f9.png)
This commit is contained in:
parent
6494247301
commit
0698333665
|
@ -1,4 +1,4 @@
|
|||
/* Copyright 2017 Andrew Dawson
|
||||
/* Copyright Tusky contributors
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
|
@ -23,9 +23,7 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.TextView
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
|
@ -35,14 +33,14 @@ import androidx.recyclerview.widget.DiffUtil
|
|||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.keylesspalace.tusky.databinding.ActivityListsBinding
|
||||
import com.keylesspalace.tusky.databinding.DialogListBinding
|
||||
import com.keylesspalace.tusky.databinding.ItemListBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.entity.MastoList
|
||||
import com.keylesspalace.tusky.util.BindingHolder
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
|
@ -54,18 +52,12 @@ import com.keylesspalace.tusky.viewmodel.ListsViewModel.LoadingState.ERROR_OTHER
|
|||
import com.keylesspalace.tusky.viewmodel.ListsViewModel.LoadingState.INITIAL
|
||||
import com.keylesspalace.tusky.viewmodel.ListsViewModel.LoadingState.LOADED
|
||||
import com.keylesspalace.tusky.viewmodel.ListsViewModel.LoadingState.LOADING
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import com.mikepenz.iconics.utils.sizeDp
|
||||
import dagger.android.DispatchingAndroidInjector
|
||||
import dagger.android.HasAndroidInjector
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Created by charlag on 1/4/18.
|
||||
*/
|
||||
// TODO use the ListSelectionFragment (and/or its adapter or binding) here; but keep the LoadingState from here (?)
|
||||
|
||||
class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
|
||||
|
||||
|
@ -214,9 +206,9 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
|
|||
).show()
|
||||
}
|
||||
|
||||
private fun onListSelected(listId: String, listTitle: String) {
|
||||
private fun onListSelected(list: MastoList) {
|
||||
startActivityWithSlideInAnimation(
|
||||
StatusListActivity.newListIntent(this, listId, listTitle)
|
||||
StatusListActivity.newListIntent(this, list.id, list.title)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -255,42 +247,28 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
|
|||
}
|
||||
|
||||
private inner class ListsAdapter :
|
||||
ListAdapter<MastoList, ListsAdapter.ListViewHolder>(ListsDiffer) {
|
||||
ListAdapter<MastoList, BindingHolder<ItemListBinding>>(ListsDiffer) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
|
||||
return LayoutInflater.from(parent.context).inflate(R.layout.item_list, parent, false)
|
||||
.let(this::ListViewHolder)
|
||||
.apply {
|
||||
val iconColor = MaterialColors.getColor(nameTextView, android.R.attr.textColorTertiary)
|
||||
val context = nameTextView.context
|
||||
val icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_list).apply { sizeDp = 20; colorInt = iconColor }
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
): BindingHolder<ItemListBinding> {
|
||||
return BindingHolder(ItemListBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||
}
|
||||
|
||||
nameTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null)
|
||||
override fun onBindViewHolder(holder: BindingHolder<ItemListBinding>, position: Int) {
|
||||
val item = getItem(position)
|
||||
holder.binding.listName.text = item.title
|
||||
|
||||
holder.binding.moreButton.apply {
|
||||
visible(true)
|
||||
setOnClickListener {
|
||||
onMore(item, holder.binding.moreButton)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
|
||||
holder.nameTextView.text = getItem(position).title
|
||||
}
|
||||
|
||||
private inner class ListViewHolder(view: View) :
|
||||
RecyclerView.ViewHolder(view),
|
||||
View.OnClickListener {
|
||||
val nameTextView: TextView = view.findViewById(R.id.list_name_textview)
|
||||
val moreButton: ImageButton = view.findViewById(R.id.editListButton)
|
||||
|
||||
init {
|
||||
view.setOnClickListener(this)
|
||||
moreButton.setOnClickListener(this)
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
if (v == itemView) {
|
||||
val list = getItem(bindingAdapterPosition)
|
||||
onListSelected(list.id, list.title)
|
||||
} else {
|
||||
onMore(getItem(bindingAdapterPosition), v)
|
||||
}
|
||||
holder.itemView.setOnClickListener {
|
||||
onListSelected(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,18 +15,10 @@
|
|||
|
||||
package com.keylesspalace.tusky
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.AppCompatEditText
|
||||
|
@ -38,34 +30,29 @@ import androidx.recyclerview.widget.ItemTouchHelper
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.transition.TransitionManager
|
||||
import at.connyduck.calladapter.networkresult.fold
|
||||
import at.connyduck.sparkbutton.helpers.Utils
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.transition.MaterialArcMotion
|
||||
import com.google.android.material.transition.MaterialContainerTransform
|
||||
import com.keylesspalace.tusky.adapter.ItemInteractionListener
|
||||
import com.keylesspalace.tusky.adapter.TabAdapter
|
||||
import com.keylesspalace.tusky.appstore.EventHub
|
||||
import com.keylesspalace.tusky.appstore.MainTabsChangedEvent
|
||||
import com.keylesspalace.tusky.components.account.list.ListSelectionFragment
|
||||
import com.keylesspalace.tusky.databinding.ActivityTabPreferenceBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.entity.MastoList
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.getDimension
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.unsafeLazy
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import dagger.android.DispatchingAndroidInjector
|
||||
import dagger.android.HasAndroidInjector
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.awaitCancellation
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.regex.Pattern
|
||||
import javax.inject.Inject
|
||||
|
||||
class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListener {
|
||||
class TabPreferenceActivity : BaseActivity(), Injectable, HasAndroidInjector, ItemInteractionListener, ListSelectionFragment.ListSelectionListener {
|
||||
|
||||
@Inject
|
||||
lateinit var mastodonApi: MastodonApi
|
||||
|
@ -73,6 +60,9 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
|
|||
@Inject
|
||||
lateinit var eventHub: EventHub
|
||||
|
||||
@Inject
|
||||
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
|
||||
|
||||
private val binding by viewBinding(ActivityTabPreferenceBinding::inflate)
|
||||
|
||||
private lateinit var currentTabs: MutableList<TabData>
|
||||
|
@ -267,81 +257,24 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
|
|||
editText.requestFocus()
|
||||
}
|
||||
|
||||
private var listSelectDialog: ListSelectionFragment? = null
|
||||
|
||||
private fun showSelectListDialog() {
|
||||
val adapter = object : ArrayAdapter<MastoList>(this, android.R.layout.simple_list_item_1) {
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val view = super.getView(position, convertView, parent)
|
||||
getItem(position)?.let { item -> (view as TextView).text = item.title }
|
||||
return view
|
||||
}
|
||||
}
|
||||
listSelectDialog = ListSelectionFragment.newInstance(null)
|
||||
listSelectDialog?.show(supportFragmentManager, null)
|
||||
|
||||
val statusLayout = LinearLayout(this)
|
||||
statusLayout.gravity = Gravity.CENTER
|
||||
val progress = ProgressBar(this)
|
||||
val preferredPadding = getDimension(this, androidx.appcompat.R.attr.dialogPreferredPadding)
|
||||
progress.setPadding(preferredPadding, 0, preferredPadding, 0)
|
||||
progress.visible(false)
|
||||
|
||||
val noListsText = TextView(this)
|
||||
noListsText.setPadding(preferredPadding, 0, preferredPadding, 0)
|
||||
noListsText.text = getText(R.string.select_list_empty)
|
||||
noListsText.visible(false)
|
||||
|
||||
statusLayout.addView(progress)
|
||||
statusLayout.addView(noListsText)
|
||||
|
||||
val dialogBuilder = AlertDialog.Builder(this)
|
||||
.setTitle(R.string.select_list_title)
|
||||
.setNeutralButton(R.string.select_list_manage) { _, _ ->
|
||||
val listIntent = Intent(applicationContext, ListsActivity::class.java)
|
||||
startActivity(listIntent)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setView(statusLayout)
|
||||
.setAdapter(adapter) { _, position ->
|
||||
adapter.getItem(position)?.let { item ->
|
||||
val newTab = createTabDataFromId(LIST, listOf(item.id, item.title))
|
||||
currentTabs.add(newTab)
|
||||
currentTabsAdapter.notifyItemInserted(currentTabs.size - 1)
|
||||
updateAvailableTabs()
|
||||
saveTabs()
|
||||
}
|
||||
}
|
||||
|
||||
val showProgressBarJob = getProgressBarJob(progress, 500)
|
||||
showProgressBarJob.start()
|
||||
|
||||
val dialog = dialogBuilder.show()
|
||||
|
||||
lifecycleScope.launch {
|
||||
mastodonApi.getLists().fold(
|
||||
{ lists ->
|
||||
showProgressBarJob.cancel()
|
||||
adapter.addAll(lists)
|
||||
if (lists.isEmpty()) {
|
||||
noListsText.show()
|
||||
}
|
||||
},
|
||||
{ throwable ->
|
||||
dialog.hide()
|
||||
Log.e("TabPreferenceActivity", "failed to load lists", throwable)
|
||||
Snackbar.make(binding.root, R.string.error_list_load, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
private fun getProgressBarJob(progressView: View, delayMs: Long) = this.lifecycleScope.launch(
|
||||
start = CoroutineStart.LAZY
|
||||
) {
|
||||
try {
|
||||
delay(delayMs)
|
||||
progressView.show()
|
||||
awaitCancellation()
|
||||
} finally {
|
||||
progressView.hide()
|
||||
}
|
||||
override fun onListSelected(list: MastoList) {
|
||||
listSelectDialog?.dismiss()
|
||||
listSelectDialog = null
|
||||
|
||||
val newTab = createTabDataFromId(LIST, listOf(list.id, list.title))
|
||||
currentTabs.add(newTab)
|
||||
currentTabsAdapter.notifyItemInserted(currentTabs.size - 1)
|
||||
updateAvailableTabs()
|
||||
saveTabs()
|
||||
}
|
||||
|
||||
private fun validateHashtag(input: CharSequence?): Boolean {
|
||||
|
@ -419,6 +352,8 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
|
|||
}
|
||||
}
|
||||
|
||||
override fun androidInjector() = dispatchingAndroidInjector
|
||||
|
||||
companion object {
|
||||
private const val MIN_TAB_COUNT = 2
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ import com.keylesspalace.tusky.EditProfileActivity
|
|||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.StatusListActivity
|
||||
import com.keylesspalace.tusky.ViewMediaActivity
|
||||
import com.keylesspalace.tusky.components.account.list.ListsForAccountFragment
|
||||
import com.keylesspalace.tusky.components.account.list.ListSelectionFragment
|
||||
import com.keylesspalace.tusky.components.accountlist.AccountListActivity
|
||||
import com.keylesspalace.tusky.components.compose.ComposeActivity
|
||||
import com.keylesspalace.tusky.components.report.ReportActivity
|
||||
|
@ -991,7 +991,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
|||
return true
|
||||
}
|
||||
R.id.action_add_or_remove_from_list -> {
|
||||
ListsForAccountFragment.newInstance(viewModel.accountId).show(supportFragmentManager, null)
|
||||
ListSelectionFragment.newInstance(viewModel.accountId).show(supportFragmentManager, null)
|
||||
return true
|
||||
}
|
||||
R.id.action_mute_domain -> {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright 2022 kyori19
|
||||
/* Copyright Tusky Contributors
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
|
@ -16,79 +16,99 @@
|
|||
|
||||
package com.keylesspalace.tusky.components.account.list
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.keylesspalace.tusky.ListsActivity
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.databinding.FragmentListsForAccountBinding
|
||||
import com.keylesspalace.tusky.databinding.ItemAddOrRemoveFromListBinding
|
||||
import com.keylesspalace.tusky.databinding.FragmentListsListBinding
|
||||
import com.keylesspalace.tusky.databinding.ItemListBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.entity.MastoList
|
||||
import com.keylesspalace.tusky.util.BindingHolder
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.awaitCancellation
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class ListsForAccountFragment : DialogFragment(), Injectable {
|
||||
class ListSelectionFragment : DialogFragment(), Injectable {
|
||||
|
||||
interface ListSelectionListener {
|
||||
fun onListSelected(list: MastoList)
|
||||
}
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
||||
private val viewModel: ListsForAccountViewModel by viewModels { viewModelFactory }
|
||||
private val binding by viewBinding(FragmentListsForAccountBinding::bind)
|
||||
|
||||
private var _binding: FragmentListsListBinding? = null
|
||||
|
||||
// This property is only valid between onCreateDialog and onDestroyView
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val adapter = Adapter()
|
||||
|
||||
private var selectListener: ListSelectionListener? = null
|
||||
private var accountId: String? = null
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
selectListener = context as? ListSelectionListener
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NORMAL, R.style.TuskyDialogFragmentStyle)
|
||||
|
||||
viewModel.setup(requireArguments().getString(ARG_ACCOUNT_ID)!!)
|
||||
accountId = requireArguments().getString(ARG_ACCOUNT_ID)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
dialog?.apply {
|
||||
window?.setLayout(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
}
|
||||
}
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val context = requireContext()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_lists_for_account, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
binding.listsView.layoutManager = LinearLayoutManager(view.context)
|
||||
_binding = FragmentListsListBinding.inflate(layoutInflater)
|
||||
binding.listsView.adapter = adapter
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
val dialogBuilder = AlertDialog.Builder(context)
|
||||
.setView(binding.root)
|
||||
.setTitle(R.string.select_list_title)
|
||||
.setNeutralButton(R.string.select_list_manage) { _, _ ->
|
||||
val listIntent = Intent(context, ListsActivity::class.java)
|
||||
startActivity(listIntent)
|
||||
}
|
||||
.setNegativeButton(if (accountId != null) R.string.button_done else android.R.string.cancel, null)
|
||||
|
||||
val dialog = dialogBuilder.create()
|
||||
|
||||
val showProgressBarJob = getProgressBarJob(binding.progressBar, 500)
|
||||
showProgressBarJob.start()
|
||||
|
||||
// TODO change this to a (single) LoadState like elsewhere?
|
||||
lifecycleScope.launch {
|
||||
viewModel.states.collectLatest { states ->
|
||||
binding.progressBar.hide()
|
||||
showProgressBarJob.cancel()
|
||||
if (states.isEmpty()) {
|
||||
binding.messageView.show()
|
||||
binding.messageView.setup(R.drawable.elephant_friend_empty, R.string.no_lists) {
|
||||
load()
|
||||
}
|
||||
binding.messageView.setup(R.drawable.elephant_friend_empty, R.string.no_lists)
|
||||
} else {
|
||||
binding.listsView.show()
|
||||
adapter.submitList(states)
|
||||
|
@ -96,9 +116,11 @@ class ListsForAccountFragment : DialogFragment(), Injectable {
|
|||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
lifecycleScope.launch {
|
||||
viewModel.loadError.collectLatest { error ->
|
||||
Log.e(TAG, "failed to load lists", error)
|
||||
binding.progressBar.hide()
|
||||
showProgressBarJob.cancel()
|
||||
binding.listsView.hide()
|
||||
binding.messageView.apply {
|
||||
show()
|
||||
|
@ -107,20 +129,20 @@ class ListsForAccountFragment : DialogFragment(), Injectable {
|
|||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
lifecycleScope.launch {
|
||||
viewModel.actionError.collectLatest { error ->
|
||||
when (error.type) {
|
||||
ActionError.Type.ADD -> {
|
||||
Snackbar.make(binding.root, R.string.failed_to_add_to_list, Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.action_retry) {
|
||||
viewModel.addAccountToList(error.listId)
|
||||
viewModel.addAccountToList(accountId!!, error.listId)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
ActionError.Type.REMOVE -> {
|
||||
Snackbar.make(binding.root, R.string.failed_to_remove_from_list, Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.action_retry) {
|
||||
viewModel.removeAccountFromList(error.listId)
|
||||
viewModel.removeAccountFromList(accountId!!, error.listId)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
@ -128,18 +150,35 @@ class ListsForAccountFragment : DialogFragment(), Injectable {
|
|||
}
|
||||
}
|
||||
|
||||
binding.doneButton.setOnClickListener {
|
||||
dismiss()
|
||||
lifecycleScope.launch {
|
||||
load()
|
||||
}
|
||||
|
||||
load()
|
||||
return dialog
|
||||
}
|
||||
|
||||
private fun getProgressBarJob(progressView: View, delayMs: Long) = this.lifecycleScope.launch(
|
||||
start = CoroutineStart.LAZY
|
||||
) {
|
||||
try {
|
||||
delay(delayMs)
|
||||
progressView.show()
|
||||
awaitCancellation()
|
||||
} finally {
|
||||
progressView.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
private fun load() {
|
||||
binding.progressBar.show()
|
||||
binding.listsView.hide()
|
||||
binding.messageView.hide()
|
||||
viewModel.load()
|
||||
viewModel.load(accountId)
|
||||
}
|
||||
|
||||
private object Differ : DiffUtil.ItemCallback<AccountListState>() {
|
||||
|
@ -159,42 +198,55 @@ class ListsForAccountFragment : DialogFragment(), Injectable {
|
|||
}
|
||||
|
||||
inner class Adapter :
|
||||
ListAdapter<AccountListState, BindingHolder<ItemAddOrRemoveFromListBinding>>(Differ) {
|
||||
ListAdapter<AccountListState, BindingHolder<ItemListBinding>>(Differ) {
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
): BindingHolder<ItemAddOrRemoveFromListBinding> {
|
||||
val binding =
|
||||
ItemAddOrRemoveFromListBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
return BindingHolder(binding)
|
||||
): BindingHolder<ItemListBinding> {
|
||||
return BindingHolder(ItemListBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: BindingHolder<ItemAddOrRemoveFromListBinding>, position: Int) {
|
||||
override fun onBindViewHolder(holder: BindingHolder<ItemListBinding>, position: Int) {
|
||||
val item = getItem(position)
|
||||
holder.binding.listNameView.text = item.list.title
|
||||
holder.binding.addButton.apply {
|
||||
visible(!item.includesAccount)
|
||||
setOnClickListener {
|
||||
viewModel.addAccountToList(item.list.id)
|
||||
holder.binding.listName.text = item.list.title
|
||||
accountId?.let { accountId ->
|
||||
holder.binding.addButton.apply {
|
||||
visible(!item.includesAccount)
|
||||
setOnClickListener {
|
||||
viewModel.addAccountToList(accountId, item.list.id)
|
||||
}
|
||||
}
|
||||
holder.binding.removeButton.apply {
|
||||
visible(item.includesAccount)
|
||||
setOnClickListener {
|
||||
viewModel.removeAccountFromList(accountId, item.list.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
holder.binding.removeButton.apply {
|
||||
visible(item.includesAccount)
|
||||
setOnClickListener {
|
||||
viewModel.removeAccountFromList(item.list.id)
|
||||
|
||||
holder.itemView.setOnClickListener {
|
||||
selectListener?.onListSelected(item.list)
|
||||
|
||||
accountId?.let { accountId ->
|
||||
if (item.includesAccount) {
|
||||
viewModel.removeAccountFromList(accountId, item.list.id)
|
||||
} else {
|
||||
viewModel.addAccountToList(accountId, item.list.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ListsListFragment"
|
||||
private const val ARG_ACCOUNT_ID = "accountId"
|
||||
|
||||
fun newInstance(accountId: String): ListsForAccountFragment {
|
||||
fun newInstance(accountId: String?): ListSelectionFragment {
|
||||
val args = Bundle().apply {
|
||||
putString(ARG_ACCOUNT_ID, accountId)
|
||||
}
|
||||
return ListsForAccountFragment().apply { arguments = args }
|
||||
return ListSelectionFragment().apply { arguments = args }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,8 +25,6 @@ import at.connyduck.calladapter.networkresult.runCatching
|
|||
import com.keylesspalace.tusky.entity.MastoList
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
@ -54,8 +52,6 @@ class ListsForAccountViewModel @Inject constructor(
|
|||
private val mastodonApi: MastodonApi
|
||||
) : ViewModel() {
|
||||
|
||||
private lateinit var accountId: String
|
||||
|
||||
private val _states = MutableSharedFlow<List<AccountListState>>(1)
|
||||
val states: SharedFlow<List<AccountListState>> = _states
|
||||
|
||||
|
@ -65,24 +61,21 @@ class ListsForAccountViewModel @Inject constructor(
|
|||
private val _actionError = MutableSharedFlow<ActionError>(1)
|
||||
val actionError: SharedFlow<ActionError> = _actionError
|
||||
|
||||
fun setup(accountId: String) {
|
||||
this.accountId = accountId
|
||||
}
|
||||
|
||||
fun load() {
|
||||
fun load(accountId: String?) {
|
||||
_loadError.resetReplayCache()
|
||||
viewModelScope.launch {
|
||||
runCatching {
|
||||
val (all, includes) = listOf(
|
||||
async { mastodonApi.getLists() },
|
||||
async { mastodonApi.getListsIncludesAccount(accountId) }
|
||||
).awaitAll()
|
||||
val all = mastodonApi.getLists().getOrThrow()
|
||||
var includes: List<MastoList> = emptyList()
|
||||
if (accountId != null) {
|
||||
includes = mastodonApi.getListsIncludesAccount(accountId).getOrThrow()
|
||||
}
|
||||
|
||||
_states.emit(
|
||||
all.getOrThrow().map { list ->
|
||||
all.map { listState ->
|
||||
AccountListState(
|
||||
list = list,
|
||||
includesAccount = includes.getOrThrow().any { it.id == list.id }
|
||||
list = listState,
|
||||
includesAccount = includes.any { it.id == listState.id }
|
||||
)
|
||||
}
|
||||
)
|
||||
|
@ -93,7 +86,9 @@ class ListsForAccountViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun addAccountToList(listId: String) {
|
||||
// TODO there is no "progress" visible for these
|
||||
|
||||
fun addAccountToList(accountId: String, listId: String) {
|
||||
_actionError.resetReplayCache()
|
||||
viewModelScope.launch {
|
||||
mastodonApi.addAccountToList(listId, listOf(accountId))
|
||||
|
@ -114,7 +109,7 @@ class ListsForAccountViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun removeAccountFromList(listId: String) {
|
||||
fun removeAccountFromList(accountId: String, listId: String) {
|
||||
_actionError.resetReplayCache()
|
||||
viewModelScope.launch {
|
||||
mastodonApi.deleteAccountFromList(listId, listOf(accountId))
|
||||
|
|
|
@ -100,7 +100,7 @@ abstract class ActivitiesModule {
|
|||
@ContributesAndroidInjector
|
||||
abstract fun contributesLicenseActivity(): LicenseActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
|
||||
abstract fun contributesTabPreferenceActivity(): TabPreferenceActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
package com.keylesspalace.tusky.di
|
||||
|
||||
import com.keylesspalace.tusky.AccountsInListFragment
|
||||
import com.keylesspalace.tusky.components.account.list.ListsForAccountFragment
|
||||
import com.keylesspalace.tusky.components.account.list.ListSelectionFragment
|
||||
import com.keylesspalace.tusky.components.account.media.AccountMediaFragment
|
||||
import com.keylesspalace.tusky.components.accountlist.AccountListFragment
|
||||
import com.keylesspalace.tusky.components.conversation.ConversationsFragment
|
||||
|
@ -97,7 +97,7 @@ abstract class FragmentBuildersModule {
|
|||
abstract fun preferencesFragment(): PreferencesFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun listsForAccountFragment(): ListsForAccountFragment
|
||||
abstract fun listsForAccountFragment(): ListSelectionFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun trendingTagsFragment(): TrendingTagsFragment
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/listsView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.keylesspalace.tusky.view.BackgroundMessageView
|
||||
android:id="@+id/messageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@android:color/transparent"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/errorphant_error"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/doneButton"
|
||||
style="@style/TuskyButton.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:text="@string/button_done" />
|
||||
|
||||
</LinearLayout>
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/listsView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.keylesspalace.tusky.view.BackgroundMessageView
|
||||
android:id="@+id/messageView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:src="@android:color/transparent"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/errorphant_error"
|
||||
tools:visibility="visible" />
|
||||
</FrameLayout>
|
|
@ -1,50 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/listNameView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:drawablePadding="8dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium"
|
||||
app:drawableStartCompat="@drawable/ic_list"
|
||||
app:drawableTint="?android:attr/textColorSecondary"
|
||||
tools:text="Example list" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/addButton"
|
||||
style="@style/TuskyImageButton"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_add_to_list"
|
||||
android:padding="4dp"
|
||||
android:src="@drawable/ic_plus_24dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/removeButton"
|
||||
style="@style/TuskyImageButton"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_remove_from_list"
|
||||
android:padding="4dp"
|
||||
android:src="@drawable/ic_clear_24dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
|
@ -1,8 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="?dialogPreferredPadding"
|
||||
android:paddingRight="?dialogPreferredPadding"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:minHeight="?android:attr/listPreferredItemHeightSmall"
|
||||
|
@ -12,21 +15,47 @@
|
|||
tools:ignore="Overdraw">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/list_name_textview"
|
||||
android:id="@+id/list_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:drawablePadding="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSmall"
|
||||
app:drawableStartCompat="@drawable/ic_list"
|
||||
app:drawableTint="?android:attr/textColorSecondary"
|
||||
tools:text="Example list" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/editListButton"
|
||||
android:id="@+id/more_button"
|
||||
android:visibility="gone"
|
||||
style="@style/TuskyImageButton"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_more"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:src="@drawable/ic_more_horiz_24dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/add_button"
|
||||
android:visibility="gone"
|
||||
style="@style/TuskyImageButton"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_more"
|
||||
android:src="@drawable/ic_more_horiz_24dp" />
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_add_to_list"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:src="@drawable/ic_plus_24dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/remove_button"
|
||||
android:visibility="gone"
|
||||
style="@style/TuskyImageButton"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_remove_from_list"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:src="@drawable/ic_clear_24dp" />
|
||||
</LinearLayout>
|
||||
|
|
|
@ -675,9 +675,7 @@
|
|||
<string name="dialog_save_profile_changes_message">هل تريد حفط التغييرات التي طرأت على ملفك التعريفي؟</string>
|
||||
<string name="dialog_delete_filter_text">أتريد حذف عامل التصفية ”%1$s“؟</string>
|
||||
<string name="dialog_delete_filter_positive_action">حذف</string>
|
||||
<string name="select_list_empty">ليس لديك أية قائمة بعدُ</string>
|
||||
<string name="select_list_manage">إدارة القوائم</string>
|
||||
<string name="error_list_load">خطأ أثناء تحميل القوائم</string>
|
||||
<string name="status_filtered_show_anyway">أظهره على أي حال</string>
|
||||
<string name="filter_description_hide">إخفاء بالكامل</string>
|
||||
<string name="ui_success_accepted_follow_request">تم قبول طلب المتابَعة</string>
|
||||
|
@ -695,4 +693,4 @@
|
|||
<string name="error_media_upload_sending_fmt">فَشِلَ التحميل: %s</string>
|
||||
<string name="load_newest_notifications">تحميل الإشعارات الجديدة</string>
|
||||
<string name="compose_delete_draft">أتريد حذف المسودّة؟</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -636,9 +636,7 @@
|
|||
<string name="description_browser_login">Можа падтрымліваць дадатковыя метады праверкі сапраўднасці, але для гэтага патрэбны адпаведны браузер.</string>
|
||||
<string name="post_media_image">Відарыс</string>
|
||||
<string name="action_refresh">Абнавіць</string>
|
||||
<string name="select_list_empty">Яшчэ няма спісаў</string>
|
||||
<string name="select_list_manage">Кіраванне спісамі</string>
|
||||
<string name="error_list_load">Памылка загрузкі спісаў</string>
|
||||
<string name="total_usage">Усяго выкарыстана</string>
|
||||
<string name="total_accounts">Усяго ўліковых запісаў</string>
|
||||
<string name="accessibility_talking_about_tag">%1$d людзей кажуць пра хэштэг %2$s</string>
|
||||
|
|
|
@ -557,7 +557,6 @@
|
|||
<string name="error_following_hashtag_format">Грешка при последване на #%s</string>
|
||||
<string name="error_unfollowing_hashtag_format">Грешка при отследване на #%s</string>
|
||||
<string name="title_public_federated">Федерирани</string>
|
||||
<string name="error_list_load">Грешка при зареждане на списъци</string>
|
||||
<string name="select_list_manage">Управление на списъци</string>
|
||||
<string name="notification_update_format">%s редактира публикацията си</string>
|
||||
<string name="pref_title_account_filter_keywords">Профили</string>
|
||||
|
@ -595,4 +594,4 @@
|
|||
<string name="compose_delete_draft">Изтриване на чернова\?</string>
|
||||
<string name="action_unfollow_hashtag_format">Отследване на #%s\?</string>
|
||||
<string name="mute_notifications_switch">Заглушаване на известията</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -692,9 +692,7 @@
|
|||
<string name="filter_edit_keyword_title">Golygu allweddair</string>
|
||||
<string name="filter_description_format">%s: %s</string>
|
||||
<string name="post_media_image">Delwedd</string>
|
||||
<string name="select_list_empty">Does gennyt ti ddim rhestrau, eto</string>
|
||||
<string name="select_list_manage">Rheoli rhestrau</string>
|
||||
<string name="error_list_load">Gwall wrth lwytho rhestrau</string>
|
||||
<string name="help_empty_home">Dyma dy <b>ffrwd cartref</b>. Mae\'n dangos negeseuon diweddar y cyfrifon rwyt ti\'n eu dilyn.
|
||||
\n
|
||||
\nEr mwyn archwilio cyfrifon galli di ddod o hyd iddyn o fewn un o\'r ffrydiau eraill. Er enghraifft, ffrwd dy weinydd [iconics gmd_group]. Neu galli di eu chwilio yn ôl eu henw [iconics gmd_search]; er enghraifft, chwiliwch am Tusky i ddod o hyd ein cyfrif Mastodon.</string>
|
||||
|
@ -750,4 +748,5 @@
|
|||
<string name="list_reply_policy_none">Neb</string>
|
||||
<string name="pref_title_show_self_boosts">Dangos hunan-hybiau</string>
|
||||
<string name="pref_title_show_self_boosts_description">Rhywun yn hybu ei neges ei hunan</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
|
|
|
@ -421,9 +421,7 @@
|
|||
<string name="about_powered_by_tusky">Unterstützt von Tusky</string>
|
||||
<string name="description_post_bookmarked">Als Lesezeichen gespeichert</string>
|
||||
<string name="select_list_title">Liste auswählen</string>
|
||||
<string name="select_list_empty">Du hast noch keine Listen</string>
|
||||
<string name="select_list_manage">Listen verwalten</string>
|
||||
<string name="error_list_load">Fehler beim Laden der Listen</string>
|
||||
<string name="list">Liste</string>
|
||||
<string name="post_lookup_error_format">Fehler beim Nachschlagen von %s</string>
|
||||
<string name="no_drafts">Du hast keine Entwürfe.</string>
|
||||
|
|
|
@ -647,13 +647,11 @@
|
|||
<string name="pref_title_show_stat_inline">Mostrar estadísticas de la entrada en la línea de tiempo</string>
|
||||
<string name="ui_error_unknown">razón desconocida</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="help_empty_home">Esta es tu <b> cronología de inicio</b>. Muestra las publicaciones recientes de las cuentas que sigues.
|
||||
\n
|
||||
\nPara encontrar cuentas, puedes mirar en alguna de las otras cronologías. Por ejemplo, la cronología local de tu instancia [iconics gmd_group]. O puedes buscarlas por nombre [iconics gmd_search]; por ejemplo, busca Tusky para encontrar nuestra cuenta de Mastodon.</string>
|
||||
<string name="ui_error_bookmark">Fallo al añadir a marcadores: %s</string>
|
||||
<string name="select_list_manage">Gestionar listas</string>
|
||||
<string name="error_list_load">Error al cargar listas</string>
|
||||
<string name="ui_error_favourite">Fallo al favoritear publicación: %s</string>
|
||||
<string name="ui_error_clear_notifications">Fallo al limpiar notificaciones: %s</string>
|
||||
<string name="ui_error_accept_follow_request">Fallo al aceptar solicitud de seguimiento: %s</string>
|
||||
|
|
|
@ -652,9 +652,7 @@
|
|||
\n
|
||||
\nبرای کاوش حسابها میتوانید آنها را در دیگر خطهای زمانی (برای نمونه خط زمانی محلّی نمونهتان [iconics gmd_group]) یافته یا با نام (برای نمونه Tusky برای حساب ماستودونمان) بجوییدشان [iconics gmd_search].</string>
|
||||
<string name="post_media_image">تصویر</string>
|
||||
<string name="select_list_empty">هنوز هیچ سیاههای ندارید</string>
|
||||
<string name="select_list_manage">مدیریت سیاههها</string>
|
||||
<string name="error_list_load">خطا در بار کردن سیاههها</string>
|
||||
<string name="load_newest_notifications">بار کردن جدیدترین آگاهیها</string>
|
||||
<string name="compose_delete_draft">حذف پیشنویس؟</string>
|
||||
<string name="error_missing_edits">کارسازتان میداند که این فرسته ویرایش شده؛ ولی رونوشتی از ویرایشها ندارد. پس نمیتوانند نشانتان داده شوند.
|
||||
|
|
|
@ -647,9 +647,7 @@
|
|||
<string name="mute_notifications_switch">Masquer les notifications</string>
|
||||
<string name="compose_unsaved_changes">Il y a des modifications non enregistrées.</string>
|
||||
<string name="description_post_edited">Modifié</string>
|
||||
<string name="select_list_empty">Vous n\'avez pas encore de liste</string>
|
||||
<string name="select_list_manage">Gérer les listes</string>
|
||||
<string name="error_list_load">Erreur de chargement des listes</string>
|
||||
<string name="report_category_violation">Règle enfreinte</string>
|
||||
<string name="error_status_source_load">Le texte d\'origine du statut n\'a pas pu être chargé.</string>
|
||||
<string name="ui_error_unknown">raison inconnue</string>
|
||||
|
|
|
@ -645,8 +645,6 @@
|
|||
<string name="ui_success_accepted_follow_request">Chaidh gabhail ris an iarrtas leantainn</string>
|
||||
<string name="ui_success_rejected_follow_request">Chaidh iarrtas leantainn a bhacadh</string>
|
||||
<string name="select_list_manage">Stiùirich na liostaichean</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="status_filtered_show_anyway">Seall e co-dhiù</string>
|
||||
<string name="status_filter_placeholder_label_format">Criathraichte: %s</string>
|
||||
<string name="pref_title_account_filter_keywords">Pròifilean</string>
|
||||
|
|
|
@ -645,9 +645,7 @@
|
|||
<string name="pref_title_show_stat_inline">Mostrar estatísticas da publicación na cronoloxía</string>
|
||||
<string name="filter_keyword_addition_title">Engadir palabra</string>
|
||||
<string name="filter_edit_keyword_title">Editar palabra</string>
|
||||
<string name="select_list_empty">Aínda non tes listas</string>
|
||||
<string name="select_list_manage">Xestionar listas</string>
|
||||
<string name="error_list_load">Erro ao cargar as listas</string>
|
||||
<string name="error_missing_edits">O teu servidor sabe que a publicación foi editada, pero non ten unha copia das edición, polo que non pode mostrarchas.
|
||||
\n
|
||||
\nÉ un <a href="https://github.com/mastodon/mastodon/issues/25398">problema coñecido</a> en Mastodon.</string>
|
||||
|
|
|
@ -654,9 +654,7 @@
|
|||
\n
|
||||
\nMás fiókokat másik idővonalakon fedezhetsz fel. Például a példányod helyi idővonalán [iconics gmd_group]. Vagy megkeresheted őket név szerint [iconics gmd_search]; például keress rá a Tuskyra, hogy megtaláld a Mastodon-fiókunkat.</string>
|
||||
<string name="post_media_image">Kép</string>
|
||||
<string name="select_list_empty">Még nincsenek listáid</string>
|
||||
<string name="select_list_manage">Listák kezelése</string>
|
||||
<string name="error_list_load">Hiba a listák betöltése során</string>
|
||||
<string name="pref_ui_text_size">UI betűméret</string>
|
||||
<string name="notification_listenable_worker_name">Háttértevékenység</string>
|
||||
<string name="notification_listenable_worker_description">Értesítések, amikor a Tusky a háttérben működik</string>
|
||||
|
|
|
@ -642,9 +642,7 @@
|
|||
<string name="filter_edit_keyword_title">Breyta stikkorði</string>
|
||||
<string name="filter_description_format">%s: %s</string>
|
||||
<string name="post_media_image">Mynd</string>
|
||||
<string name="select_list_empty">Þú ert ekki með neina lista</string>
|
||||
<string name="select_list_manage">Sýsla með lista</string>
|
||||
<string name="error_list_load">Villa við að hlaða inn listum</string>
|
||||
<string name="help_empty_home">Þetta er <b>tímalínan þín</b>. Hún sýnir nýlegar færslur þeirra sem þú fylgist með.
|
||||
\n
|
||||
\nTil að skoða hvað aðrir eru að gera getur þú til dæmis uppgötvað viðkomandi í einni af hinum tímalínunum. Til dæmis á staðværu tímalínu netþjónsins þíns [iconics gmd_group]. Eða að þú leitar að þeim eftir nafni [iconics gmd_search]; til dæmis geturðu leitað að Tusky til að finna Mastodon-aðganginn okkar.</string>
|
||||
|
|
|
@ -652,9 +652,7 @@
|
|||
<string name="ui_error_accept_follow_request">Accettazione richiesta di follow fallita: %s</string>
|
||||
<string name="ui_success_accepted_follow_request">Richiesta di follow accettata</string>
|
||||
<string name="ui_success_rejected_follow_request">Richiesta di follow bloccata</string>
|
||||
<string name="select_list_empty">Non hai ancora alcuna lista</string>
|
||||
<string name="select_list_manage">Gestisci 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_filter_placeholder_label_format">Filtrato: %s</string>
|
||||
<string name="pref_title_account_filter_keywords">Profili</string>
|
||||
|
|
|
@ -607,7 +607,6 @@
|
|||
<string name="post_media_image">画像</string>
|
||||
<string name="notification_unknown_name">不明</string>
|
||||
<string name="select_list_manage">リストを管理する</string>
|
||||
<string name="select_list_empty">リストはまだありません</string>
|
||||
<string name="status_filtered_show_anyway">とにかく表示する</string>
|
||||
<string name="pref_title_account_filter_keywords">プロフィール</string>
|
||||
<string name="title_public_trending_hashtags">トレンドのハッシュタグ</string>
|
||||
|
@ -646,7 +645,6 @@
|
|||
<string name="filter_description_format">%s: %s</string>
|
||||
<string name="load_newest_notifications">最新の通知を読み込む</string>
|
||||
<string name="compose_delete_draft">下書きを削除しますか?</string>
|
||||
<string name="error_list_load">リストを読み込む際のエラー</string>
|
||||
<string name="error_media_upload_sending_fmt">アップロードに失敗しました: %s</string>
|
||||
<string name="error_missing_edits">あなたのサーバーは、この投稿が変更されたことを把握していますが、編集履歴のコピーを備えていないので、表示できません。
|
||||
\n
|
||||
|
|
|
@ -592,7 +592,6 @@
|
|||
<string name="post_privacy_followers_only">Tikai sekotājiem</string>
|
||||
<string name="post_media_image">Attēls</string>
|
||||
<string name="filter_description_hide">Paslēpt pilnībā</string>
|
||||
<string name="select_list_empty">Tev vēl nav sarakstu</string>
|
||||
<string name="select_list_manage">Pārvaldīt sarakstus</string>
|
||||
<string name="filter_keyword_display_format">%s (viss vārds)</string>
|
||||
<string name="pref_title_account_filter_keywords">Profili</string>
|
||||
|
|
|
@ -626,8 +626,6 @@
|
|||
<string name="action_discard">Forkast endringer</string>
|
||||
<string name="action_continue_edit">Fortsett endring</string>
|
||||
<string name="compose_unsaved_changes">Du har ulagrede endringer.</string>
|
||||
<string name="select_list_empty">Du har ingen lister, enda</string>
|
||||
<string name="error_list_load">Feil under lading av lister</string>
|
||||
<string name="select_list_manage">Forvalte lister</string>
|
||||
<string name="ui_error_reblog">Deling av innlegget feilet: %s</string>
|
||||
<string name="ui_error_unknown">ukjent grunn</string>
|
||||
|
|
|
@ -617,8 +617,6 @@
|
|||
<string name="notification_unknown_name">Onbekend</string>
|
||||
<string name="ui_error_clear_notifications">Wissen meldingen mislukt: %s</string>
|
||||
<string name="ui_success_accepted_follow_request">Volgverzoek geaccepteerd</string>
|
||||
<string name="error_list_load">Fout bij laden lijsten</string>
|
||||
<string name="select_list_empty">Je hebt nog geen lijsten</string>
|
||||
<string name="select_list_manage">Lijsten beheren</string>
|
||||
<string name="pref_title_account_filter_keywords">Profielen</string>
|
||||
<string name="status_filtered_show_anyway">Toch tonen</string>
|
||||
|
|
|
@ -644,9 +644,7 @@
|
|||
<string name="filter_description_format">%s : %s</string>
|
||||
<string name="label_filter_keywords">Mots clau o frasas de filtrar</string>
|
||||
<string name="post_media_image">Imatge</string>
|
||||
<string name="select_list_empty">Avètz pas encara de lista</string>
|
||||
<string name="select_list_manage">Gerir las listas</string>
|
||||
<string name="error_list_load">Error en cargant las litas</string>
|
||||
<string name="ui_error_favourite">Fracàs de la mes en favorit : %s</string>
|
||||
<string name="ui_error_reblog">Fracàs en partejant : %s</string>
|
||||
<string name="ui_error_vote">Fracàs del vòt : %s</string>
|
||||
|
|
|
@ -617,7 +617,6 @@
|
|||
<string name="failed_to_add_to_list">Falha em adicionar a conta à lista</string>
|
||||
<string name="failed_to_pin">Falha em afixar</string>
|
||||
<string name="compose_save_draft_loses_media">Guardar rascunho\? (anexos serão carregados novamente quando recuperares o rascunho.)</string>
|
||||
<string name="select_list_empty">Ainda não tens nenhuma lista</string>
|
||||
<string name="select_list_manage">Gerir listas</string>
|
||||
<string name="status_filter_placeholder_label_format">Filtrados: %s</string>
|
||||
<string name="status_filtered_show_anyway">Mostrar à mesma</string>
|
||||
|
@ -641,7 +640,6 @@
|
|||
<string name="status_edit_info">Editado: %1$s</string>
|
||||
<string name="status_created_info">Criado: %1$s</string>
|
||||
<string name="pref_title_reading_order">Ordem de leitura</string>
|
||||
<string name="error_list_load">Erro ao carregar listas</string>
|
||||
<string name="report_category_violation">Violação de regra</string>
|
||||
<string name="no_lists">Não tens nenhuma lista.</string>
|
||||
<string name="socket_timeout_exception">A tentativa de contato com o teu servidor demorou demasiado temp0</string>
|
||||
|
@ -690,4 +688,4 @@
|
|||
<string name="list_reply_policy_followed">Qualquer utilizador que segues</string>
|
||||
<string name="list_reply_policy_label">Mostrar respostas a</string>
|
||||
<string name="total_usage">Uso total</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -639,8 +639,6 @@
|
|||
<string name="about_copy">Копирование версии и информации об устройстве</string>
|
||||
<string name="about_copied">Версия и информация об устройстве скопировано</string>
|
||||
<string name="action_add_reaction">добавить реакцию</string>
|
||||
<string name="error_list_load">Ошибка при загрузке списков</string>
|
||||
<string name="select_list_empty">У вас нет списков, пока что</string>
|
||||
<string name="select_list_manage">Управлять списками</string>
|
||||
<string name="ui_error_reject_follow_request">Отклонение запроса на подписку не удалось: %s</string>
|
||||
<string name="ui_error_accept_follow_request">Принятие запроса на подписку не удалось: %s</string>
|
||||
|
@ -724,4 +722,4 @@
|
|||
<string name="filter_description_format">%s: %s</string>
|
||||
<string name="dialog_save_profile_changes_message">Хотите сохранить изменения профиля\?</string>
|
||||
<string name="action_edit_image">Редактировать изображение</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -632,7 +632,6 @@
|
|||
<string name="ui_success_accepted_follow_request">Följ begäran accepterad</string>
|
||||
<string name="ui_success_rejected_follow_request">Följ begäran blockerad</string>
|
||||
<string name="select_list_manage">Hantera listor</string>
|
||||
<string name="select_list_empty">Du har inga listor, än</string>
|
||||
<string name="status_filtered_show_anyway">Visa allafall</string>
|
||||
<string name="status_filter_placeholder_label_format">Filtrerad: %s</string>
|
||||
<string name="pref_title_account_filter_keywords">Profiler</string>
|
||||
|
@ -663,7 +662,6 @@
|
|||
<string name="ui_error_bookmark">Att bokmärka inlägg misslyckades: %s</string>
|
||||
<string name="ui_error_clear_notifications">Rensing av aviseringar misslyckades: %s</string>
|
||||
<string name="ui_error_favourite">Att favoritmarkera inlägg misslyckades: %s</string>
|
||||
<string name="error_list_load">Fel vid laddning av listor</string>
|
||||
<string name="ui_error_reject_follow_request">Avacceptera följarförgrågan misslyckades: %s</string>
|
||||
<string name="label_filter_context">Filtrera sammanhang</string>
|
||||
<string name="title_public_trending_hashtags">Populära hashtaggar</string>
|
||||
|
|
|
@ -628,9 +628,7 @@
|
|||
<string name="action_browser_login">Tarayıcı ile Giriş Yap</string>
|
||||
<string name="description_login">Çoğu durumda çalışır. Diğer uygulamalara veri sızmaz.</string>
|
||||
<string name="description_browser_login">Ek kimlik doğrulama yöntemlerini destekleyebilir ancak desteklenen bir tarayıcı gerektirir.</string>
|
||||
<string name="select_list_empty">Henüz hiç listen yok</string>
|
||||
<string name="select_list_manage">Listeleri yönet</string>
|
||||
<string name="error_list_load">Listeler yüklenirken hata oluştu</string>
|
||||
<string name="action_share_account_link">Hesaba bağlantı paylaş</string>
|
||||
<string name="action_share_account_username">Hesap adını paylaş</string>
|
||||
<string name="account_username_copied">Kullanıcı adı kopyalandı</string>
|
||||
|
|
|
@ -667,8 +667,6 @@
|
|||
\n
|
||||
\nЩоб переглянути облікові записи, ви можете знайти їх в одній з інших стрічок. Наприклад, на локальній стрічці вашого сервера [iconics gmd_group]. Або ви можете шукати їх за іменами [iconics gmd_search]; наприклад, шукайте Tusky, щоб знайти наш обліковий запис Mastodon.</string>
|
||||
<string name="post_media_image">Зображення</string>
|
||||
<string name="error_list_load">Помилка завантаження списків</string>
|
||||
<string name="select_list_empty">У вас ще немає списків</string>
|
||||
<string name="select_list_manage">Керувати списками</string>
|
||||
<string name="pref_ui_text_size">Розмір шрифту інтерфейсу</string>
|
||||
<string name="notification_listenable_worker_name">Фонова діяльність</string>
|
||||
|
|
|
@ -636,8 +636,6 @@
|
|||
\nĐể khám phá mọi người, bạn có thể xem qua ở các bảng tin khác. Ví dụ: [iconics gmd_group] Bảng tin máy chủ của bạn. Hoặc bạn cũng có thể [iconics gmd_search] tìm theo tên người dùng; ví dụ như Tusky.</string>
|
||||
<string name="post_media_image">Hình ảnh</string>
|
||||
<string name="select_list_manage">Quản lý danh sách</string>
|
||||
<string name="error_list_load">Xảy ra lỗi khi tải danh sách</string>
|
||||
<string name="select_list_empty">Bạn chưa có danh sách</string>
|
||||
<string name="load_newest_notifications">Tải những thông báo mới nhất</string>
|
||||
<string name="compose_delete_draft">Xóa bản nháp\?</string>
|
||||
<string name="error_missing_edits">Máy chủ của bạn biết tút đã được sửa, nhưng không có bản sao của lần sửa, nên nó không thể hiện.
|
||||
|
|
|
@ -650,8 +650,6 @@
|
|||
\n要探索账户, 你可以浏览其他时间线。比如,你的账户所在实例服务器的本地时间线 [iconics gmd_group]。你也可以按名称 [iconics gmd_search]进行搜索;比如,搜索Tusky 来寻找我们的 Mastodon 账户。</string>
|
||||
<string name="post_media_image">图片</string>
|
||||
<string name="select_list_manage">管理列表</string>
|
||||
<string name="error_list_load">加载列表出错</string>
|
||||
<string name="select_list_empty">你还没有列表</string>
|
||||
<string name="load_newest_notifications">加载最新通知</string>
|
||||
<string name="compose_delete_draft">删除草稿?</string>
|
||||
<string name="error_missing_edits">你的服务器知晓这篇帖子被编辑,但没有编辑的副本,所以无法呈现给你。
|
||||
|
|
|
@ -608,9 +608,7 @@
|
|||
<string name="edit_hashtag_hint">Hashtag without #</string>
|
||||
<string name="hashtags">Hashtags</string>
|
||||
<string name="select_list_title">Select list</string>
|
||||
<string name="select_list_empty">You have no lists, yet</string>
|
||||
<string name="select_list_manage">Manage lists</string>
|
||||
<string name="error_list_load">Error loading lists</string>
|
||||
<string name="list_exclusive_label">Hide from the home timeline</string>
|
||||
<string name="list">List</string>
|
||||
<string name="notifications_clear">Delete</string>
|
||||
|
|
Loading…
Reference in New Issue