Merge branch 'develop' into notification_policy
This commit is contained in:
commit
954046e647
|
@ -28,6 +28,7 @@ import com.google.android.material.snackbar.Snackbar
|
|||
import com.keylesspalace.tusky.appstore.EventHub
|
||||
import com.keylesspalace.tusky.appstore.FilterUpdatedEvent
|
||||
import com.keylesspalace.tusky.components.filters.EditFilterActivity
|
||||
import com.keylesspalace.tusky.components.filters.FilterExpiration
|
||||
import com.keylesspalace.tusky.components.filters.FiltersActivity
|
||||
import com.keylesspalace.tusky.components.timeline.TimelineFragment
|
||||
import com.keylesspalace.tusky.components.timeline.viewmodel.TimelineViewModel.Kind
|
||||
|
@ -251,7 +252,7 @@ class StatusListActivity : BottomSheetActivity() {
|
|||
title = "#$tag",
|
||||
context = listOf(Filter.Kind.HOME.kind),
|
||||
filterAction = Filter.Action.WARN.action,
|
||||
expiresInSeconds = null
|
||||
expiresIn = FilterExpiration.never
|
||||
).fold(
|
||||
{ filter ->
|
||||
if (mastodonApi.addFilterKeyword(
|
||||
|
@ -281,7 +282,7 @@ class StatusListActivity : BottomSheetActivity() {
|
|||
listOf(Filter.Kind.HOME.kind),
|
||||
irreversible = false,
|
||||
wholeWord = true,
|
||||
expiresInSeconds = null
|
||||
expiresIn = FilterExpiration.never
|
||||
).fold(
|
||||
{ filter ->
|
||||
mutedFilterV1 = filter
|
||||
|
@ -358,7 +359,7 @@ class StatusListActivity : BottomSheetActivity() {
|
|||
context = filter.context.filter { it != Filter.Kind.HOME.kind },
|
||||
irreversible = null,
|
||||
wholeWord = null,
|
||||
expiresInSeconds = null
|
||||
expiresIn = FilterExpiration.never
|
||||
)
|
||||
} else {
|
||||
mastodonApi.deleteFilterV1(filter.id)
|
||||
|
|
|
@ -1186,12 +1186,12 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
cardImage.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
|
||||
RequestBuilder<Drawable> builder = Glide.with(cardImage.getContext())
|
||||
.load(card.getImage())
|
||||
.dontTransform();
|
||||
.load(card.getImage());
|
||||
if (statusDisplayOptions.useBlurhash() && !TextUtils.isEmpty(card.getBlurhash())) {
|
||||
builder = builder.placeholder(decodeBlurHash(card.getBlurhash()));
|
||||
}
|
||||
builder.into(cardImage);
|
||||
builder.centerInside()
|
||||
.into(cardImage);
|
||||
} else if (statusDisplayOptions.useBlurhash() && !TextUtils.isEmpty(card.getBlurhash())) {
|
||||
int radius = cardImage.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.card_radius);
|
||||
|
@ -1213,7 +1213,6 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
|
||||
Glide.with(cardImage.getContext())
|
||||
.load(decodeBlurHash(card.getBlurhash()))
|
||||
.dontTransform()
|
||||
.into(cardImage);
|
||||
} else {
|
||||
cardView.setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
|
|
@ -1,6 +1,20 @@
|
|||
/* Copyright 2024 Tusky contributors
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.components.filters
|
||||
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface.BUTTON_POSITIVE
|
||||
import android.os.Bundle
|
||||
import android.view.WindowManager
|
||||
|
@ -28,7 +42,6 @@ import com.keylesspalace.tusky.util.isHttpNotFound
|
|||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.Date
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
@ -123,6 +136,7 @@ class EditFilterActivity : BaseActivity() {
|
|||
|
||||
if (originalFilter == null) {
|
||||
binding.filterActionWarn.isChecked = true
|
||||
initializeDurationDropDown(false)
|
||||
} else {
|
||||
loadFilter()
|
||||
}
|
||||
|
@ -164,7 +178,11 @@ class EditFilterActivity : BaseActivity() {
|
|||
// Populate the UI from the filter's members
|
||||
private fun loadFilter() {
|
||||
viewModel.load(filter)
|
||||
val durationNames = if (filter.expiresAt != null) {
|
||||
initializeDurationDropDown(withNoChange = filter.expiresAt != null)
|
||||
}
|
||||
|
||||
private fun initializeDurationDropDown(withNoChange: Boolean) {
|
||||
val durationNames = if (withNoChange) {
|
||||
arrayOf(getString(R.string.duration_no_change)) + resources.getStringArray(R.array.filter_duration_names)
|
||||
} else {
|
||||
resources.getStringArray(R.array.filter_duration_names)
|
||||
|
@ -321,19 +339,5 @@ class EditFilterActivity : BaseActivity() {
|
|||
|
||||
companion object {
|
||||
const val FILTER_TO_EDIT = "FilterToEdit"
|
||||
|
||||
// Mastodon *stores* the absolute date in the filter,
|
||||
// but create/edit take a number of seconds (relative to the time the operation is posted)
|
||||
fun getSecondsForDurationIndex(index: Int, context: Context?, default: Date? = null): Int? {
|
||||
return when (index) {
|
||||
-1 -> if (default == null) {
|
||||
default
|
||||
} else {
|
||||
((default.time - System.currentTimeMillis()) / 1000).toInt()
|
||||
}
|
||||
0 -> null
|
||||
else -> context?.resources?.getIntArray(R.array.filter_duration_values)?.get(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,25 @@
|
|||
/* Copyright 2024 Tusky contributors
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.components.filters
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import at.connyduck.calladapter.networkresult.fold
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.entity.Filter
|
||||
import com.keylesspalace.tusky.entity.FilterKeyword
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
|
@ -112,12 +128,12 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi) : ViewModel(
|
|||
durationIndex: Int,
|
||||
context: Context
|
||||
): Boolean {
|
||||
val expiresInSeconds = EditFilterActivity.getSecondsForDurationIndex(durationIndex, context)
|
||||
val expiration = getExpirationForDurationIndex(durationIndex, context)
|
||||
api.createFilter(
|
||||
title = title,
|
||||
context = contexts,
|
||||
filterAction = action,
|
||||
expiresInSeconds = expiresInSeconds
|
||||
expiresIn = expiration
|
||||
).fold(
|
||||
{ newFilter ->
|
||||
// This is _terrible_, but the all-in-one update filter api Just Doesn't Work
|
||||
|
@ -133,7 +149,7 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi) : ViewModel(
|
|||
return (
|
||||
throwable.isHttpNotFound() &&
|
||||
// Endpoint not found, fall back to v1 api
|
||||
createFilterV1(contexts, expiresInSeconds)
|
||||
createFilterV1(contexts, expiration)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
@ -147,13 +163,13 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi) : ViewModel(
|
|||
durationIndex: Int,
|
||||
context: Context
|
||||
): Boolean {
|
||||
val expiresInSeconds = EditFilterActivity.getSecondsForDurationIndex(durationIndex, context)
|
||||
val expiration = getExpirationForDurationIndex(durationIndex, context)
|
||||
api.updateFilter(
|
||||
id = originalFilter.id,
|
||||
title = title,
|
||||
context = contexts,
|
||||
filterAction = action,
|
||||
expiresInSeconds = expiresInSeconds
|
||||
expires = expiration
|
||||
).fold(
|
||||
{
|
||||
// This is _terrible_, but the all-in-one update filter api Just Doesn't Work
|
||||
|
@ -173,7 +189,7 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi) : ViewModel(
|
|||
{ throwable ->
|
||||
if (throwable.isHttpNotFound()) {
|
||||
// Endpoint not found, fall back to v1 api
|
||||
if (updateFilterV1(contexts, expiresInSeconds)) {
|
||||
if (updateFilterV1(contexts, expiration)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -182,13 +198,13 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi) : ViewModel(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun createFilterV1(context: List<String>, expiresInSeconds: Int?): Boolean {
|
||||
private suspend fun createFilterV1(context: List<String>, expiration: FilterExpiration?): Boolean {
|
||||
return _keywords.value.map { keyword ->
|
||||
api.createFilterV1(keyword.keyword, context, false, keyword.wholeWord, expiresInSeconds)
|
||||
api.createFilterV1(keyword.keyword, context, false, keyword.wholeWord, expiration)
|
||||
}.none { it.isFailure }
|
||||
}
|
||||
|
||||
private suspend fun updateFilterV1(context: List<String>, expiresInSeconds: Int?): Boolean {
|
||||
private suspend fun updateFilterV1(context: List<String>, expiration: FilterExpiration?): Boolean {
|
||||
val results = _keywords.value.map { keyword ->
|
||||
if (originalFilter == null) {
|
||||
api.createFilterV1(
|
||||
|
@ -196,7 +212,7 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi) : ViewModel(
|
|||
context = context,
|
||||
irreversible = false,
|
||||
wholeWord = keyword.wholeWord,
|
||||
expiresInSeconds = expiresInSeconds
|
||||
expiresIn = expiration
|
||||
)
|
||||
} else {
|
||||
api.updateFilterV1(
|
||||
|
@ -205,7 +221,7 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi) : ViewModel(
|
|||
context = context,
|
||||
irreversible = false,
|
||||
wholeWord = keyword.wholeWord,
|
||||
expiresInSeconds = expiresInSeconds
|
||||
expiresIn = expiration
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -213,4 +229,18 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi) : ViewModel(
|
|||
|
||||
return results.none { it.isFailure }
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Mastodon *stores* the absolute date in the filter,
|
||||
// but create/edit take a number of seconds (relative to the time the operation is posted)
|
||||
private fun getExpirationForDurationIndex(index: Int, context: Context): FilterExpiration? {
|
||||
return when (index) {
|
||||
-1 -> FilterExpiration.unchanged
|
||||
0 -> FilterExpiration.never
|
||||
else -> FilterExpiration.seconds(
|
||||
context.resources.getIntArray(R.array.filter_duration_values)[index]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/* Copyright 2024 Tusky contributors
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.components.filters
|
||||
|
||||
import kotlin.jvm.JvmInline
|
||||
|
||||
/**
|
||||
* Custom class to have typesafety for filter expirations.
|
||||
* Retrofit will call toString when sending this class as part of a form-urlencoded body.
|
||||
*/
|
||||
@JvmInline
|
||||
value class FilterExpiration private constructor(val seconds: Int) {
|
||||
|
||||
override fun toString(): String {
|
||||
return if (seconds < 0) "" else seconds.toString()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val unchanged: FilterExpiration? = null
|
||||
val never: FilterExpiration = FilterExpiration(-1)
|
||||
|
||||
fun seconds(seconds: Int): FilterExpiration = FilterExpiration(seconds)
|
||||
}
|
||||
}
|
|
@ -153,6 +153,9 @@ class NotificationsViewModel @Inject constructor(
|
|||
return when ((notificationViewData as? NotificationViewData.Concrete)?.type) {
|
||||
Notification.Type.MENTION, Notification.Type.POLL -> {
|
||||
notificationViewData.statusViewData?.let { statusViewData ->
|
||||
if (statusViewData.status.account.id == account.accountId) {
|
||||
return Filter.Action.NONE
|
||||
}
|
||||
statusViewData.filterAction = filterModel.shouldFilterStatus(statusViewData.actionable)
|
||||
return statusViewData.filterAction
|
||||
}
|
||||
|
|
|
@ -76,8 +76,6 @@ class CachedTimelineViewModel @Inject constructor(
|
|||
filterModel
|
||||
) {
|
||||
|
||||
private val account = accountManager.activeAccount!!
|
||||
|
||||
private var currentPagingSource: PagingSource<Int, HomeTimelineData>? = null
|
||||
|
||||
/** Map from status id to translation. */
|
||||
|
|
|
@ -304,7 +304,7 @@ class NetworkTimelineViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun clearWarning(status: StatusViewData.Concrete) {
|
||||
updateStatusByActionableId(status.id) {
|
||||
updateStatusByActionableId(status.actionableId) {
|
||||
it.copy(filtered = emptyList())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,8 @@ abstract class TimelineViewModel(
|
|||
private val filterModel: FilterModel
|
||||
) : ViewModel() {
|
||||
|
||||
protected val account = accountManager.activeAccount!!
|
||||
|
||||
abstract val statuses: Flow<PagingData<StatusViewData>>
|
||||
|
||||
var kind: Kind = Kind.HOME
|
||||
|
@ -179,6 +181,10 @@ abstract class TimelineViewModel(
|
|||
|
||||
protected fun shouldFilterStatus(statusViewData: StatusViewData): Filter.Action {
|
||||
val status = statusViewData.asStatusOrNull()?.status ?: return Filter.Action.NONE
|
||||
if (status.actionableStatus.account.id == account.accountId) {
|
||||
// never filter own posts
|
||||
return Filter.Action.NONE
|
||||
}
|
||||
return if (
|
||||
(status.inReplyToId != null && filterRemoveReplies) ||
|
||||
(status.reblog != null && filterRemoveReblogs) ||
|
||||
|
|
|
@ -68,6 +68,8 @@ class ViewThreadViewModel @Inject constructor(
|
|||
private val moshi: Moshi
|
||||
) : ViewModel() {
|
||||
|
||||
private val activeAccount = accountManager.activeAccount!!
|
||||
|
||||
private val _uiState = MutableStateFlow(ThreadUiState.Loading as ThreadUiState)
|
||||
val uiState: Flow<ThreadUiState> = _uiState.asStateFlow()
|
||||
|
||||
|
@ -80,14 +82,10 @@ class ViewThreadViewModel @Inject constructor(
|
|||
|
||||
var isInitialLoad: Boolean = true
|
||||
|
||||
private val alwaysShowSensitiveMedia: Boolean
|
||||
private val alwaysOpenSpoiler: Boolean
|
||||
private val alwaysShowSensitiveMedia: Boolean = activeAccount.alwaysShowSensitiveMedia
|
||||
private val alwaysOpenSpoiler: Boolean = activeAccount.alwaysOpenSpoiler
|
||||
|
||||
init {
|
||||
val activeAccount = accountManager.activeAccount
|
||||
alwaysShowSensitiveMedia = activeAccount?.alwaysShowSensitiveMedia ?: false
|
||||
alwaysOpenSpoiler = activeAccount?.alwaysOpenSpoiler ?: false
|
||||
|
||||
viewModelScope.launch {
|
||||
eventHub.events
|
||||
.collect { event ->
|
||||
|
@ -109,7 +107,7 @@ class ViewThreadViewModel @Inject constructor(
|
|||
val filterCall = async { filterModel.init(Filter.Kind.THREAD) }
|
||||
|
||||
val contextCall = async { api.statusContext(id) }
|
||||
val statusAndAccount = db.timelineStatusDao().getStatusWithAccount(accountManager.activeAccount!!.id, id)
|
||||
val statusAndAccount = db.timelineStatusDao().getStatusWithAccount(activeAccount.id, id)
|
||||
|
||||
var detailedStatus = if (statusAndAccount != null) {
|
||||
Log.d(TAG, "Loaded status from local timeline")
|
||||
|
@ -142,7 +140,7 @@ class ViewThreadViewModel @Inject constructor(
|
|||
if (statusAndAccount != null) {
|
||||
api.status(id).onSuccess { result ->
|
||||
db.timelineStatusDao().update(
|
||||
tuskyAccountId = accountManager.activeAccount!!.id,
|
||||
tuskyAccountId = activeAccount.id,
|
||||
status = result,
|
||||
moshi = moshi
|
||||
)
|
||||
|
@ -421,7 +419,7 @@ class ViewThreadViewModel @Inject constructor(
|
|||
|
||||
private fun List<StatusViewData.Concrete>.filter(): List<StatusViewData.Concrete> {
|
||||
return filter { status ->
|
||||
if (status.isDetailed) {
|
||||
if (status.isDetailed || status.status.account.id == activeAccount.accountId) {
|
||||
true
|
||||
} else {
|
||||
status.filterAction = filterModel.shouldFilterStatus(status.status)
|
||||
|
|
|
@ -180,9 +180,10 @@ abstract class SFragment(@LayoutRes contentLayoutId: Int) : Fragment(contentLayo
|
|||
|
||||
protected fun more(status: Status, view: View, position: Int, translation: Translation?) {
|
||||
val id = status.actionableId
|
||||
val accountId = status.actionableStatus.account.id
|
||||
val accountUsername = status.actionableStatus.account.username
|
||||
val statusUrl = status.actionableStatus.url
|
||||
val actionableStatus = status.actionableStatus
|
||||
val accountId = actionableStatus.account.id
|
||||
val accountUsername = actionableStatus.account.username
|
||||
val statusUrl = actionableStatus.url
|
||||
var loggedInAccountId: String? = null
|
||||
val activeAccount = accountManager.activeAccount
|
||||
if (activeAccount != null) {
|
||||
|
@ -194,22 +195,21 @@ abstract class SFragment(@LayoutRes contentLayoutId: Int) : Fragment(contentLayo
|
|||
if (statusIsByCurrentUser) {
|
||||
popup.inflate(R.menu.status_more_for_user)
|
||||
val menu = popup.menu
|
||||
when (status.visibility) {
|
||||
when (actionableStatus.visibility) {
|
||||
Status.Visibility.PUBLIC, Status.Visibility.UNLISTED -> {
|
||||
menu.add(
|
||||
0,
|
||||
R.id.pin,
|
||||
1,
|
||||
getString(
|
||||
if (status.pinned) R.string.unpin_action else R.string.pin_action
|
||||
if (actionableStatus.pinned) R.string.unpin_action else R.string.pin_action
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Status.Visibility.PRIVATE -> {
|
||||
val reblogged = status.reblog?.reblogged ?: status.reblogged
|
||||
menu.findItem(R.id.status_reblog_private).isVisible = !reblogged
|
||||
menu.findItem(R.id.status_unreblog_private).isVisible = reblogged
|
||||
menu.findItem(R.id.status_reblog_private).isVisible = !actionableStatus.reblogged
|
||||
menu.findItem(R.id.status_unreblog_private).isVisible = actionableStatus.reblogged
|
||||
}
|
||||
|
||||
else -> {}
|
||||
|
@ -298,7 +298,7 @@ abstract class SFragment(@LayoutRes contentLayoutId: Int) : Fragment(contentLayo
|
|||
}
|
||||
|
||||
R.id.status_download_media -> {
|
||||
requestDownloadAllMedia(status)
|
||||
requestDownloadAllMedia(actionableStatus)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
|
@ -344,7 +344,7 @@ abstract class SFragment(@LayoutRes contentLayoutId: Int) : Fragment(contentLayo
|
|||
|
||||
R.id.pin -> {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
timelineCases.pin(status.id, !status.pinned)
|
||||
timelineCases.pin(status.actionableId, !actionableStatus.pinned)
|
||||
.onFailure { e: Throwable ->
|
||||
val message = e.message
|
||||
?: getString(if (status.pinned) R.string.failed_to_unpin else R.string.failed_to_pin)
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package com.keylesspalace.tusky.network
|
||||
|
||||
import at.connyduck.calladapter.networkresult.NetworkResult
|
||||
import com.keylesspalace.tusky.components.filters.FilterExpiration
|
||||
import com.keylesspalace.tusky.entity.AccessToken
|
||||
import com.keylesspalace.tusky.entity.Account
|
||||
import com.keylesspalace.tusky.entity.Announcement
|
||||
|
@ -543,7 +544,7 @@ interface MastodonApi {
|
|||
@Field("context[]") context: List<String>,
|
||||
@Field("irreversible") irreversible: Boolean?,
|
||||
@Field("whole_word") wholeWord: Boolean?,
|
||||
@Field("expires_in") expiresInSeconds: Int?
|
||||
@Field("expires_in") expiresIn: FilterExpiration?
|
||||
): NetworkResult<FilterV1>
|
||||
|
||||
@FormUrlEncoded
|
||||
|
@ -554,7 +555,7 @@ interface MastodonApi {
|
|||
@Field("context[]") context: List<String>,
|
||||
@Field("irreversible") irreversible: Boolean?,
|
||||
@Field("whole_word") wholeWord: Boolean?,
|
||||
@Field("expires_in") expiresInSeconds: Int?
|
||||
@Field("expires_in") expiresIn: FilterExpiration?
|
||||
): NetworkResult<FilterV1>
|
||||
|
||||
@DELETE("api/v1/filters/{id}")
|
||||
|
@ -566,7 +567,7 @@ interface MastodonApi {
|
|||
@Field("title") title: String,
|
||||
@Field("context[]") context: List<String>,
|
||||
@Field("filter_action") filterAction: String,
|
||||
@Field("expires_in") expiresInSeconds: Int?
|
||||
@Field("expires_in") expiresIn: FilterExpiration?
|
||||
): NetworkResult<Filter>
|
||||
|
||||
@FormUrlEncoded
|
||||
|
@ -576,7 +577,7 @@ interface MastodonApi {
|
|||
@Field("title") title: String? = null,
|
||||
@Field("context[]") context: List<String>? = null,
|
||||
@Field("filter_action") filterAction: String? = null,
|
||||
@Field("expires_in") expiresInSeconds: Int? = null
|
||||
@Field("expires_in") expires: FilterExpiration? = null
|
||||
): NetworkResult<Filter>
|
||||
|
||||
@DELETE("api/v2/filters/{id}")
|
||||
|
|
|
@ -52,11 +52,11 @@
|
|||
<string name="button_done">Xong</string>
|
||||
<string name="action_send_public">ĐĂNG</string>
|
||||
<string name="button_back">Quay lại</string>
|
||||
<string name="button_continue">Tiếp tục</string>
|
||||
<string name="button_continue">TIẾP TỤC</string>
|
||||
<string name="filter_dialog_update_button">Cập nhật</string>
|
||||
<string name="filter_dialog_remove_button">Xóa</string>
|
||||
<string name="action_send">ĐĂNG</string>
|
||||
<string name="action_login">Đăng nhập Mastodon</string>
|
||||
<string name="action_login">ĐĂNG NHẬP MASTODON</string>
|
||||
<string name="dialog_redraft_post_warning">Xóa và viết lại tút này\?</string>
|
||||
<string name="dialog_delete_post_warning">Xóa tút này\?</string>
|
||||
<string name="dialog_unfollow_warning">Bỏ theo dõi người này\?</string>
|
||||
|
@ -90,7 +90,7 @@
|
|||
<string name="send_post_link_to">Đăng lại URL tút với…</string>
|
||||
<string name="send_post_content_to">Đăng lại tút với…</string>
|
||||
<string name="downloading_media">Đang lưu vào thiết bị</string>
|
||||
<string name="download_media">Tải xuống</string>
|
||||
<string name="download_media">Lưu media</string>
|
||||
<string name="action_share_as">Đăng lại với tư cách …</string>
|
||||
<string name="action_open_as">Mở với tư cách %1$s</string>
|
||||
<string name="action_copy_link">Chép URL</string>
|
||||
|
@ -98,7 +98,7 @@
|
|||
<string name="action_open_media_n">Mở tập tin #%1$d</string>
|
||||
<string name="title_links_dialog">Links</string>
|
||||
<string name="title_mentions_dialog">Lượt nhắc tới</string>
|
||||
<string name="title_hashtags_dialog">Hashtag</string>
|
||||
<string name="title_hashtags_dialog">HASHTAG</string>
|
||||
<string name="action_open_faved_by">Xem lượt thích</string>
|
||||
<string name="action_open_reblogged_by">Xem lượt đăng lại</string>
|
||||
<string name="action_open_reblogger">Xem lượt đăng lại</string>
|
||||
|
@ -139,7 +139,7 @@
|
|||
<string name="action_view_favourites">Thích</string>
|
||||
<string name="action_view_profile">Trang hồ sơ</string>
|
||||
<string name="action_close">Đóng</string>
|
||||
<string name="action_retry">Thử lại</string>
|
||||
<string name="action_retry">THỬ LẠI</string>
|
||||
<string name="action_delete_and_redraft">Xóa & viết lại</string>
|
||||
<string name="action_delete">Xóa</string>
|
||||
<string name="action_edit">Sửa</string>
|
||||
|
@ -148,8 +148,8 @@
|
|||
<string name="action_hide_reblogs">Ẩn lượt đăng lại</string>
|
||||
<string name="action_unblock">Bỏ chặn</string>
|
||||
<string name="action_block">Chặn</string>
|
||||
<string name="action_unfollow">Bỏ theo dõi</string>
|
||||
<string name="action_follow">Theo dõi</string>
|
||||
<string name="action_unfollow">BỎ THEO DÕI</string>
|
||||
<string name="action_follow">THEO DÕI</string>
|
||||
<string name="action_logout_confirm">Bạn có muốn đăng xuất tài khoản %1$s\? Dữ liệu của tài khoản sẽ bị xóa, bao gồm những bản nháp và thiết lập.</string>
|
||||
<string name="action_compose">Soạn tút</string>
|
||||
<string name="action_more">Thêm</string>
|
||||
|
@ -183,36 +183,36 @@
|
|||
<string name="title_bookmarks">Những tút đã lưu</string>
|
||||
<string name="title_followers">Người theo dõi</string>
|
||||
<string name="title_follows">Theo dõi</string>
|
||||
<string name="title_posts_pinned">Ghim</string>
|
||||
<string name="title_posts_with_replies">Lượt trả lời</string>
|
||||
<string name="title_posts">Tút</string>
|
||||
<string name="title_view_thread">Nội dung tút</string>
|
||||
<string name="title_posts_pinned">GHIM</string>
|
||||
<string name="title_posts_with_replies">LƯỢT TRẢ LỜI</string>
|
||||
<string name="title_posts">TÚT</string>
|
||||
<string name="title_view_thread">Tút</string>
|
||||
<string name="title_tab_preferences">Xếp tab</string>
|
||||
<string name="title_direct_messages">Nhắn riêng</string>
|
||||
<string name="title_public_federated">Liên hợp</string>
|
||||
<string name="title_public_local">Máy chủ</string>
|
||||
<string name="title_notifications">Thông báo</string>
|
||||
<string name="title_home">Trang chính</string>
|
||||
<string name="title_home">Trang chủ</string>
|
||||
<string name="title_drafts">Những tút nháp</string>
|
||||
<string name="title_favourites">Những tút đã thích</string>
|
||||
<string name="link_whats_an_instance">Máy chủ là gì\?</string>
|
||||
<string name="pref_title_show_media_preview">Hiện xem trước hình ảnh</string>
|
||||
<string name="pref_title_show_replies">Hiện những trả lời</string>
|
||||
<string name="pref_title_show_boosts">Hiện lượt đăng lại</string>
|
||||
<string name="pref_title_show_replies">Hiện những tút dạng trả lời</string>
|
||||
<string name="pref_title_show_boosts">Hiện những lượt đăng lại</string>
|
||||
<string name="pref_title_post_tabs">Trang chủ</string>
|
||||
<string name="pref_title_post_filter">Lọc bảng tin</string>
|
||||
<string name="pref_title_gradient_for_media">Phủ màu media nhạy cảm</string>
|
||||
<string name="pref_title_gradient_for_media">Làm mờ media nhạy cảm</string>
|
||||
<string name="pref_title_animate_gif_avatars">Ảnh đại diện GIF</string>
|
||||
<string name="pref_title_bot_overlay">Icon cho tài khoản Bot</string>
|
||||
<string name="pref_title_language">Ngôn ngữ</string>
|
||||
<string name="pref_title_custom_tabs">Mở luôn trong app</string>
|
||||
<string name="pref_title_browser_settings">Trình duyệt</string>
|
||||
<string name="app_theme_system">Mặc định của thiết bị</string>
|
||||
<string name="app_theme_system">Giống thiết bị</string>
|
||||
<string name="app_theme_auto">Tự động khi trời tối</string>
|
||||
<string name="app_theme_black">Đen</string>
|
||||
<string name="app_theme_light">Sáng</string>
|
||||
<string name="app_them_dark">Tối</string>
|
||||
<string name="pref_title_timeline_filters">Bộ lọc</string>
|
||||
<string name="pref_title_timeline_filters">Những từ khóa đã lọc</string>
|
||||
<string name="pref_title_timelines">Bảng tin</string>
|
||||
<string name="pref_title_app_theme">Chủ đề</string>
|
||||
<string name="pref_title_appearance_settings">Giao diện</string>
|
||||
|
@ -254,9 +254,9 @@
|
|||
<string name="pref_main_nav_position_option_top">Trên màn hình</string>
|
||||
<string name="pref_main_nav_position">Vị trí menu</string>
|
||||
<string name="pref_failed_to_sync">Không thể lưu thiết lập</string>
|
||||
<string name="pref_publishing">Đăng mặc định</string>
|
||||
<string name="pref_default_media_sensitivity">Tài khoản nhạy cảm (đồng bộ máy chủ)</string>
|
||||
<string name="pref_default_post_privacy">Kiểu tút mặc định (đồng bộ máy chủ)</string>
|
||||
<string name="pref_publishing">Mặc định khi đăng</string>
|
||||
<string name="pref_default_media_sensitivity">Media nhạy cảm (đ.bộ máy chủ)</string>
|
||||
<string name="pref_default_post_privacy">Kiểu tút (đ.bộ máy chủ)</string>
|
||||
<string name="pref_title_http_proxy_server">Máy chủ proxy</string>
|
||||
<string name="pref_title_http_proxy_port">Cổng</string>
|
||||
<string name="pref_title_http_proxy_enable">Bật proxy</string>
|
||||
|
@ -268,7 +268,7 @@
|
|||
<string name="about_bug_feature_request_site">Báo lỗi và đề xuất tính năng
|
||||
\nhttps://github.com/tuskyapp/Tusky/issues</string>
|
||||
<string name="about_project_site">Website dự án: https://tusky.app</string>
|
||||
<string name="about_tusky_license">Tusky là phần mềm mã nguồn mở, được phân phối với giấy phép GNU General Public License Version 3. Bạn có thể tham khảo thêm tại: https://www.gnu.org/licenses/gpl-3.0.en.html</string>
|
||||
<string name="about_tusky_license">Tusky là phần mềm mã nguồn mở, được phân phối với giấy phép GNU General Public License Version 3. Tham khảo thêm tại: https://www.gnu.org/licenses/gpl-3.0.en.html</string>
|
||||
<string name="about_powered_by_tusky">Powered by Tusky</string>
|
||||
<string name="about_tusky_version">Tusky %1$s</string>
|
||||
<string name="description_account_locked">Tài khoản bị khóa</string>
|
||||
|
@ -288,13 +288,13 @@
|
|||
<string name="pref_title_alway_show_sensitive_media">Hiện nội dung nhạy cảm</string>
|
||||
<string name="follows_you">Đang theo dõi bạn</string>
|
||||
<string name="abbreviated_seconds_ago">%1$ds</string>
|
||||
<string name="abbreviated_minutes_ago">%1$d phút</string>
|
||||
<string name="abbreviated_hours_ago">%1$d giờ</string>
|
||||
<string name="abbreviated_minutes_ago">%1$dp</string>
|
||||
<string name="abbreviated_hours_ago">%1$dh</string>
|
||||
<string name="abbreviated_days_ago">%1$d ngày</string>
|
||||
<string name="abbreviated_years_ago">%1$d năm</string>
|
||||
<string name="abbreviated_in_seconds">%1$ds</string>
|
||||
<string name="abbreviated_in_minutes">%1$d phút</string>
|
||||
<string name="abbreviated_in_hours">%1$d giờ</string>
|
||||
<string name="abbreviated_in_hours">%1$dh%1$ giờ</string>
|
||||
<string name="abbreviated_in_days">%1$d ngày</string>
|
||||
<string name="abbreviated_in_years">%1$d năm</string>
|
||||
<string name="state_follow_requested">Yêu cầu theo dõi</string>
|
||||
|
@ -322,7 +322,7 @@
|
|||
<string name="create_poll_title">Bình chọn</string>
|
||||
<string name="pref_title_enable_swipe_for_tabs">Vuốt qua lại giữa các tab</string>
|
||||
<string name="failed_search">Không thể tìm thấy</string>
|
||||
<string name="title_accounts">Người</string>
|
||||
<string name="title_accounts">NGƯỜI</string>
|
||||
<string name="report_description_remote_instance">Người này thuộc máy chủ khác. Gửi luôn cho máy chủ đó\?</string>
|
||||
<string name="report_description_1">Báo cáo này sẽ được gửi tới kiểm duyệt viên. Hãy cho biết lý do vì sao bạn báo cáo người này bên dưới:</string>
|
||||
<string name="failed_fetch_posts">Chưa tải được tút</string>
|
||||
|
@ -358,8 +358,8 @@
|
|||
<string name="compose_shortcut_short_label">Soạn</string>
|
||||
<string name="compose_shortcut_long_label">Soạn tút</string>
|
||||
<string name="filter_apply">Áp dụng</string>
|
||||
<string name="notifications_apply_filter">Lọc</string>
|
||||
<string name="notifications_clear">Xóa hết</string>
|
||||
<string name="notifications_apply_filter">LỌC</string>
|
||||
<string name="notifications_clear">XÓA HẾT</string>
|
||||
<string name="list">Danh sách</string>
|
||||
<string name="select_list_title">Chọn danh sách</string>
|
||||
<string name="hashtags">Hashtag</string>
|
||||
|
@ -379,13 +379,13 @@
|
|||
<string name="conversation_more_recipients">%1$s, %2$s và %3$d người nữa</string>
|
||||
<string name="conversation_2_recipients">%1$s và %2$s</string>
|
||||
<string name="conversation_1_recipients">%1$s</string>
|
||||
<string name="title_favourited_by">Lượt thích tút này</string>
|
||||
<string name="title_reblogged_by">Lượt đăng lại tút này</string>
|
||||
<string name="title_favourited_by">Thích bởi</string>
|
||||
<string name="title_reblogged_by">Đăng lại bởi</string>
|
||||
<plurals name="reblogs">
|
||||
<item quantity="other"><b>%1$s</b> Đăng lại</item>
|
||||
<item quantity="other"><b>%1$s</b> đăng lại</item>
|
||||
</plurals>
|
||||
<plurals name="favs">
|
||||
<item quantity="other"><b>%1$s</b> Thích</item>
|
||||
<item quantity="other"><b>%1$s</b> thích</item>
|
||||
</plurals>
|
||||
<string name="pin_action">Ghim</string>
|
||||
<string name="unpin_action">Gỡ ghim</string>
|
||||
|
@ -393,12 +393,12 @@
|
|||
<string name="pref_title_absolute_time">Sử dụng thời gian thiết bị</string>
|
||||
<string name="profile_metadata_content_label">Nội dung</string>
|
||||
<string name="profile_metadata_label_label">Nhãn</string>
|
||||
<string name="profile_metadata_add">thêm nội dung</string>
|
||||
<string name="profile_metadata_add">THÊM NỘI DUNG</string>
|
||||
<string name="profile_metadata_label">Metadata</string>
|
||||
<string name="license_cc_by_sa_4">CC-BY-SA 4.0</string>
|
||||
<string name="license_cc_by_4">CC-BY 4.0</string>
|
||||
<string name="license_apache_2">Giấy phép Apache (xem bên dưới)</string>
|
||||
<string name="license_description">Tusky có sử dụng mã nguồn từ những dự án mã nguồn mở sau:</string>
|
||||
<string name="license_description">Tusky có sử dụng những dự án mã nguồn mở sau:</string>
|
||||
<string name="unreblog_private">Hủy đăng lại</string>
|
||||
<string name="reblog_private">Đăng lại công khai</string>
|
||||
<string name="account_moved_description">%1$s đã chuyển sang:</string>
|
||||
|
@ -412,11 +412,11 @@
|
|||
<string name="later">Để sau</string>
|
||||
<string name="restart_emoji">Bạn cần khởi động lại Tusky để áp dụng các thiết lập</string>
|
||||
<string name="restart_required">Yêu cầu khởi động lại ứng dụng</string>
|
||||
<string name="action_open_post">Đọc tút</string>
|
||||
<string name="action_open_post">Xem tút</string>
|
||||
<string name="expand_collapse_all_posts">Mở rộng/Thu gọn toàn bộ tút</string>
|
||||
<string name="performing_lookup_title">Đang tra cứu…</string>
|
||||
<string name="download_fonts">Bạn cần tải về bộ emoji này trước</string>
|
||||
<string name="system_default">Mặc định của thiết bị</string>
|
||||
<string name="system_default">Giống thiết bị</string>
|
||||
<string name="emoji_style">Emoji</string>
|
||||
<string name="action_compose_shortcut">Soạn</string>
|
||||
<string name="compose_save_draft">Lưu nháp\?</string>
|
||||
|
@ -443,7 +443,7 @@
|
|||
<string name="dialog_mute_hide_notifications">Ẩn thông báo</string>
|
||||
<string name="action_unmute_desc">Bỏ ẩn %1$s</string>
|
||||
<string name="action_unmute_domain">Bỏ ẩn %1$s</string>
|
||||
<string name="pref_title_hide_top_toolbar">Ẩn tiêu đề tab</string>
|
||||
<string name="pref_title_hide_top_toolbar">Ẩn tên tab</string>
|
||||
<string name="account_note_saved">Đã lưu!</string>
|
||||
<string name="account_note_hint">Ghi chú về người này</string>
|
||||
<string name="no_announcements">Chưa có thông báo.</string>
|
||||
|
@ -552,7 +552,7 @@
|
|||
<string name="title_followed_hashtags">Những hashtag theo dõi</string>
|
||||
<string name="post_edited">Sửa %1$s</string>
|
||||
<string name="description_post_edited">Đã sửa</string>
|
||||
<string name="pref_default_post_language">Ngôn ngữ đăng (đồng bộ máy chủ)</string>
|
||||
<string name="pref_default_post_language">Ngôn ngữ đăng (đ.bộ máy chủ)</string>
|
||||
<string name="language_display_name_format">%1$s (%2$s)</string>
|
||||
<string name="error_muting_hashtag_format">Lỗi khi ẩn #%1$s</string>
|
||||
<string name="error_unmuting_hashtag_format">Lỗi khi bỏ ẩn #%1$s</string>
|
||||
|
@ -568,9 +568,9 @@
|
|||
<string name="status_created_info">Đăng %1$s</string>
|
||||
<string name="title_edits">Những lượt sửa tút</string>
|
||||
<string name="a11y_label_loading_thread">Đang tải thảo luận</string>
|
||||
<string name="action_share_account_link">Chia sẻ URL người dùng</string>
|
||||
<string name="action_share_account_link">Liên kết trang hồ sơ</string>
|
||||
<string name="send_account_link_to">Chia sẻ URL người dùng…</string>
|
||||
<string name="action_share_account_username">Chia sẻ tên người này</string>
|
||||
<string name="action_share_account_username">Địa chỉ Mastodon</string>
|
||||
<string name="send_account_username_to">Chia sẻ tên người này…</string>
|
||||
<string name="account_username_copied">Đã sao chép tên người này</string>
|
||||
<string name="pref_title_reading_order">Thứ tự đọc</string>
|
||||
|
@ -612,12 +612,12 @@
|
|||
<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_filter_placeholder_label_format">Đã lọc: %1$s</string>
|
||||
<string name="pref_title_account_filter_keywords">Người</string>
|
||||
<string name="pref_title_account_filter_keywords">Trang hồ sơ</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="filter_action_warn">Cảnh báo</string>
|
||||
<string name="filter_action_hide">Đã ẩn</string>
|
||||
<string name="filter_description_warn">Ẩn với cảnh báo</string>
|
||||
<string name="filter_action_warn">Cảnh báo ở</string>
|
||||
<string name="filter_action_hide">Lọc ở</string>
|
||||
<string name="filter_description_warn">Ẩn kèm theo cảnh báo</string>
|
||||
<string name="filter_description_hide">Ẩn hoàn toàn</string>
|
||||
<string name="label_filter_action">Hành động</string>
|
||||
<string name="label_filter_context">Nơi áp dụng</string>
|
||||
|
@ -627,7 +627,7 @@
|
|||
<string name="filter_keyword_addition_title">Thêm từ</string>
|
||||
<string name="filter_edit_keyword_title">Sửa từ</string>
|
||||
<string name="filter_description_format">%1$s: %2$s</string>
|
||||
<string name="pref_title_show_stat_inline">Hiện số tương tác trên tút</string>
|
||||
<string name="pref_title_show_stat_inline">Hiện số tương tác tút</string>
|
||||
<string name="help_empty_home">Đây là <b>bảng tin của bạn</b>. Nó sẽ hiện tút gần đây từ những người bạn theo dõi.
|
||||
\n
|
||||
\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>
|
||||
|
@ -649,7 +649,7 @@
|
|||
\nSDK %4$d</string>
|
||||
<string name="about_account_info_title">Tài khoản của bạn</string>
|
||||
<string name="about_account_info">\@%1$s@%2$s
|
||||
\nPhiên bản: %3$s</string>
|
||||
\nPhiên bản máy chủ: %3$s</string>
|
||||
<string name="about_copy">Sao chép phiên bản và thông tin thiết bị</string>
|
||||
<string name="about_copied">Đã sao chép phiên bản và thông tin thiết bị</string>
|
||||
<string name="about_device_info_title">Thiết bị của bạn</string>
|
||||
|
@ -663,11 +663,11 @@
|
|||
\nTin nhắn riêng được tạo bằng cách chọn tùy chọn kiểu tút [iconics gmd_public] thành [iconics gmd_mail] <i>Nhắn riêng</i> và có nhắc đến một người nào đó.
|
||||
\n
|
||||
\nVí dụ: bạn có thể xem hồ sơ của một người và nhấn vào nút [iconics gmd_edit] và đổi kiểu tút. </string>
|
||||
<string name="help_empty_lists">Đây là <b>danh sách</b>. Bạn có thể tạo nhiều danh sách riêng và thêm người dùng vào đó.
|
||||
\n
|
||||
\nBạn chỉ có thể thêm những người MÀ BẠN THEO DÕI vào danh sách.
|
||||
\n
|
||||
\nDanh sách có thể được sử dụng như một tab trong thiết lập Cá nhân [iconics gmd_account_circle] [iconics gmd_navigate_next] Xếp tab. </string>
|
||||
<string name="help_empty_lists">Đây là <b>danh sách</b>. Bạn có thể tạo nhiều danh sách riêng và thêm người dùng vào đó.
|
||||
\n
|
||||
\nBạn chỉ có thể thêm những người BẠN ĐANG THEO DÕI vào danh sách.
|
||||
\n
|
||||
\nDanh sách có thể được sử dụng như một tab trong thiết lập Cá nhân [iconics gmd_account_circle] [iconics gmd_navigate_next] Xếp tab. \u0020</string>
|
||||
<string name="muting_hashtag_success_format">Đang ẩn hashtag #%1$s như một cảnh báo</string>
|
||||
<string name="unmuting_hashtag_success_format">Đang bỏ ẩn hashtag #%1$s</string>
|
||||
<string name="action_view_filter">Xem bộ lọc</string>
|
||||
|
@ -676,15 +676,15 @@
|
|||
<string name="error_blocking_domain">Không thể ẩn %1$s: %2$s</string>
|
||||
<string name="error_unblocking_domain">Không thể bỏ ẩn %1$s: %2$s</string>
|
||||
<string name="label_image">Hình ảnh</string>
|
||||
<string name="app_theme_system_black">Mặc định của thiết bị (Đen)</string>
|
||||
<string name="app_theme_system_black">Giống thiết bị (Đen)</string>
|
||||
<string name="title_public_trending_statuses">Tút xu hướng</string>
|
||||
<string name="list_reply_policy_none">Không ai</string>
|
||||
<string name="list_reply_policy_list">Người trong danh sách</string>
|
||||
<string name="list_reply_policy_followed">Người đã theo dõi</string>
|
||||
<string name="list_reply_policy_label">Hiện lượt trả lời</string>
|
||||
<string name="pref_title_show_self_boosts">Hiện lượt tự đăng lại</string>
|
||||
<string name="pref_title_show_self_boosts">Hiện những lượt tự đăng lại</string>
|
||||
<string name="pref_title_show_self_boosts_description">Ai đó đăng lại tút của chính họ</string>
|
||||
<string name="pref_title_per_timeline_preferences">Thiết lập từng bảng tin</string>
|
||||
<string name="pref_title_per_timeline_preferences">Lọc bảng tin</string>
|
||||
<string name="pref_title_show_notifications_filter">Hiện bộ lọc thông báo</string>
|
||||
<string name="reply_sending">Đang gửi…</string>
|
||||
<string name="reply_sending_long">Câu trả lời của bạn đã được gửi đi.</string>
|
||||
|
@ -699,12 +699,12 @@
|
|||
<string name="pref_title_confirm_follows">Hỏi trước khi theo dõi</string>
|
||||
<string name="url_copied">Đã sao chép liên kết</string>
|
||||
<string name="confirmation_hashtag_copied">Đã sao chép \'#%1$s\'</string>
|
||||
<string name="pref_default_reply_privacy">Kiểu trả lời (không đồng bộ máy chủ)</string>
|
||||
<string name="pref_default_reply_privacy">Kiểu trả lời (không đ.bộ máy chủ)</string>
|
||||
<string name="error_deleting_filter">Xóa bộ lọc \'%1$s\' không được</string>
|
||||
<string name="error_saving_filter">Chưa thể lưu bộ lọc \'%1$s\'</string>
|
||||
<string name="action_follow_hashtag">Theo dõi một hashtag mới</string>
|
||||
<string name="pref_default_reply_privacy_explanation">Chọn dựa vào tút bạn đang trả lời.</string>
|
||||
<string name="pref_match_default_post_privacy">Khớp mặc định tút</string>
|
||||
<string name="pref_default_reply_privacy_explanation">Tự động giống tút bạn đang trả lời</string>
|
||||
<string name="pref_match_default_post_privacy">Giống mặc định tút</string>
|
||||
<string name="post_privacy_direct">Nhắn riêng</string>
|
||||
<string name="label_expires_after">Hết hạn sau</string>
|
||||
</resources>
|
|
@ -19,7 +19,6 @@ package com.keylesspalace.tusky
|
|||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import at.connyduck.calladapter.networkresult.NetworkResult
|
||||
import com.keylesspalace.tusky.components.filters.EditFilterActivity
|
||||
import com.keylesspalace.tusky.components.instanceinfo.InstanceInfoRepository
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.entity.Filter
|
||||
|
@ -277,22 +276,6 @@ class FilterV1Test {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unchangedExpiration_shouldBeNegative_whenFilterIsExpired() {
|
||||
val expiredBySeconds = 3600
|
||||
val expiredDate = Date.from(Instant.now().minusSeconds(expiredBySeconds.toLong()))
|
||||
val updatedDuration = EditFilterActivity.getSecondsForDurationIndex(-1, null, expiredDate)
|
||||
assert(updatedDuration != null && updatedDuration <= -expiredBySeconds)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unchangedExpiration_shouldBePositive_whenFilterIsUnexpired() {
|
||||
val expiresInSeconds = 3600
|
||||
val expiredDate = Date.from(Instant.now().plusSeconds(expiresInSeconds.toLong()))
|
||||
val updatedDuration = EditFilterActivity.getSecondsForDurationIndex(-1, null, expiredDate)
|
||||
assert(updatedDuration != null && updatedDuration > (expiresInSeconds - 60))
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun mockStatus(
|
||||
content: String = "",
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
[versions]
|
||||
agp = "8.7.0"
|
||||
androidx-activity = "1.9.2"
|
||||
agp = "8.7.2"
|
||||
androidx-activity = "1.9.3"
|
||||
androidx-appcompat = "1.7.0"
|
||||
androidx-browser = "1.8.0"
|
||||
androidx-cardview = "1.0.0"
|
||||
androidx-constraintlayout = "2.1.4"
|
||||
androidx-constraintlayout = "2.2.0"
|
||||
androidx-core = "1.13.1"
|
||||
androidx-drawerlayout = "1.2.0"
|
||||
androidx-exifinterface = "1.3.7"
|
||||
androidx-fragment = "1.8.4"
|
||||
androidx-fragment = "1.8.5"
|
||||
androidx-hilt = "1.2.0"
|
||||
androidx-junit = "1.2.1"
|
||||
androidx-lifecycle = "2.8.6"
|
||||
androidx-lifecycle = "2.8.7"
|
||||
androidx-media3 = "1.4.1"
|
||||
androidx-paging = "3.3.2"
|
||||
androidx-preference = "1.2.1"
|
||||
|
@ -50,13 +50,13 @@ robolectric = "4.13"
|
|||
sparkbutton = "4.2.0"
|
||||
touchimageview = "3.6"
|
||||
truth = "1.4.4"
|
||||
turbine = "1.1.0"
|
||||
turbine = "1.2.0"
|
||||
unified-push = "2.4.0"
|
||||
xmlwriter = "1.0.4"
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
google-ksp = "com.google.devtools.ksp:2.0.21-1.0.25"
|
||||
google-ksp = "com.google.devtools.ksp:2.0.21-1.0.26"
|
||||
hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue