Compare commits

...

11 Commits

Author SHA1 Message Date
kyori19 4f705ae074
Merge remote-tracking branch 'tuskyapp/main'
# Conflicts:
#	app/build.gradle
#	app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt
2023-06-10 01:02:01 +09:00
Nik Clayton 039f18ce25
Prepare 22.0 (versionCode 110) (#3718) 2023-06-08 12:35:09 +02:00
Danial Behzadi 74433e0d39 Translated using Weblate (Persian)
Currently translated at 100.0% (27 of 27 strings)

Translation: Tusky/Tusky description
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky-app/fa/
2023-06-07 11:10:12 +02:00
Nik Clayton 3be1e1150a
Prepare 22.0 beta 7 (versionCode 109) (#3710) 2023-06-05 13:02:32 +02:00
Nik Clayton 01b3cb3a53
Fetch all outstanding Mastodon notifications when creating Android notifications (#3700)
* Fetch all outstanding Mastodon notifications when creating Android notifications

Previous code fetched the oldest page of unfetched Mastodon notifications.

If you had more than a page of Mastodon notifications you'd get Android notifications for that page, then ~ 15 minutes later Android notifications for the next page, and so on.

This code fetches all the outstanding notifications at once.

If this results in more than 40 total notifications the list is still trimmed so that a maximum of 40 Android notifications is displayed.

Fixes https://github.com/tuskyapp/Tusky/issues/3648

* Build the list using buildList
2023-06-01 19:31:30 +02:00
Nik Clayton 346dabffc5
Write notification account information to the correct account (#3697)
Don't use `accountManager.activeAccount` throughout the code.

Instead, get the active account once, and use that over the life of the viewmodel.

As shown in https://github.com/tuskyapp/Tusky/issues/3689#issuecomment-1567219338 the active account can change before the view model is destroyed, and if that happens account information for the account will be written to the wrong account.
2023-06-01 19:19:18 +02:00
Manuel 61374c5180 Translated using Weblate (Italian)
Currently translated at 93.5% (562 of 601 strings)

Co-authored-by: Manuel <mannivuwiki@gmail.com>
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/it/
Translation: Tusky/Tusky
2023-05-29 23:35:34 +02:00
Nik Clayton 34de33220c
Don't overwrite the active account when composing from a notification (#3688)
Fix a bug where the active account can be overwritten.

1. Have two accounts logged in to Tusky, A and B
2. Open Tusky as account A
3. Send a DM to account B (doesn't have to be a DM, just anything that creates a notification for account B)
4. Restart Tusky so the Android notification for the DM is displayed immediately. You are still acting as account A.
5. Drag down the Android notification, you should see two options, "Quick reply" and "Compose"
6. Tap "Compose"
7. ComposeActivity will start. You are now acting as account B. Compose and send a reply
8. You'll be returned to the "Home" tab.

The UI will show you are still account A (i.e., it's account A's avatar at the top of the screen, if you have the "Show username in toolbars" option turned on it will be account A's username in the toolbar).

But you are now seeing the home timeline for account B.

Fix this.

ComposeViewModel
- Do not rely on the active account in sendStatus(), receive the account ID as a parameter

ComposeActivity
- Use either the account ID from the intent, or the current active account. **Do not** change the active account
- Pass the account ID to use in the sendStatus() call
2023-05-29 13:32:56 +02:00
Danial Behzadi 4a4dbe9012 Translated using Weblate (Persian)
Currently translated at 100.0% (26 of 26 strings)

Translation: Tusky/Tusky description
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky-app/fa/
2023-05-27 17:44:29 +02:00
Deleted User 1ef31db862 Translated using Weblate (German)
Currently translated at 100.0% (26 of 26 strings)

Translation: Tusky/Tusky description
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky-app/de/
2023-05-27 17:44:29 +02:00
Deleted User 8183ef29e4 Translated using Weblate (German)
Currently translated at 100.0% (601 of 601 strings)

Co-authored-by: Deleted User <noreply+280@weblate.org>
Translate-URL: https://weblate.tusky.app/projects/tusky/tusky/de/
Translation: Tusky/Tusky
2023-05-27 16:21:10 +02:00
19 changed files with 213 additions and 68 deletions

View File

@ -6,6 +6,64 @@
### Significant bug fixes
## v22.0
### New features and other improvements
- **View trending hashtags**, [PR#3149](https://github.com/tuskyapp/Tusky/pull/3149) by [@knossos](https://fosstodon.org/@knossos)
- View trending hashtags from the side menu, or by adding them to a new tab.
- **Edit image description and focus point**, [PR#3215](https://github.com/tuskyapp/Tusky/pull/3215) by [@Tak](https://mastodon.gamedev.place/@Tak)
- Edit image descriptions and focus points when editing posts.
- **View profile banner images**, [PR#3274](https://github.com/tuskyapp/Tusky/pull/3274) by [@Tak](https://mastodon.gamedev.place/@Tak)
- Tap the banner image on any profile to view it full size, save, share, etc.
- **Follow new hashtags**, [PR#3275](https://github.com/tuskyapp/Tusky/pull/3275) by [@nikclayton](https://mastodon.social/@nikclayton)
- Follow new hashtags from the "Followed hashtags" screen.
- **Better ordering when selecting languages**, [PR#3293](https://github.com/tuskyapp/Tusky/pull/3293) by [@Tak](https://mastodon.gamedev.place/@Tak)
- Tusky will prioritise the language of the post being replied to, your default posting language, configured Tusky languages, and configured system languages when ordering the list of languages to post in.
- **"Load more" break is more prominent**, [PR#3376](https://github.com/tuskyapp/Tusky/pull/3376) by [@lakoja](https://freiburg.social/@lakoja)
- Adjusted the design so the "Load more" break in a timeline is more obvious.
- **Add "Refresh" menu**, [PR#3121](https://github.com/tuskyapp/Tusky/pull/3121) by [@nikclayton](https://mastodon.social/@nikclayton)
- Tusky timelines can now be refreshed from a menu as well as swiping, making this accessible to assistive devices.
- **Notifications timeline improvements**, [PR#3159](https://github.com/tuskyapp/Tusky/pull/3159) by [@nikclayton](https://mastodon.social/@nikclayton)
- Notifications no longer need to "Load more", they are loaded automatically as you scroll.
- Errors when interacting with notifications are displayed to the user, with a "Retry" option.
- **Show the difference between versions of a post**, [PR#3314](https://github.com/tuskyapp/Tusky/pull/3314) by [@nikclayton](https://mastodon.social/@nikclayton)
- Viewing the edits to a post highlights the differences (text that was added or deleted) between the different versions.
- **Support Mastodon v4 filters**, [PR#3188](https://github.com/tuskyapp/Tusky/pull/3188) by [@Tak](https://mastodon.gamedev.place/@Tak)
- Mastodon v4 introduced additional [filtering controls](https://docs.joinmastodon.org/user/moderating/#filters).
- **Option to show post statistics in the timeline**, [PR#3413](https://github.com/tuskyapp/Tusky/pull/3413)
- Tusky can now (optionally) show the number of replies, reposts, and favourites a post has received, in the timeline.
- **Expanded tappable area for links, hashtags, and mentions in a post**, [PR#3382](https://github.com/tuskyapp/Tusky/pull/3382) by [@nikclayton](https://mastodon.social/@nikclayton)
- Links, hashtags, and mentions in a post now react to taps that are a little above, below, or to the side of the tappable text, making them more accessible.
### Significant bug fixes
- **Remember selected tab and position**, [PR#3255](https://github.com/tuskyapp/Tusky/pull/3255) by [@nikclayton](https://mastodon.social/@nikclayton)
- Changing your tab settings (adding, removing, re-ordering) remembers your reading position in those tabs.
- **Show player controls during audio playback**, [PR#3286](https://github.com/tuskyapp/Tusky/pull/3286) by [@EricFrohnhoefer](https://mastodon.social/@EricFrohnhoefer)
- A regression from v21.0 where the media player controls could not be used.
- **Keep notifications until read**, [PR#3312](https://github.com/tuskyapp/Tusky/pull/3312) by [@lakoja](https://freiburg.social/@lakoja)
- Opening Tusky would dismiss all active Tusky Android notifications.
- **Fix copying URLs at the end of a post**, [PR#3380](https://github.com/tuskyapp/Tusky/pull/3380) by [@nikclayton](https://mastodon.social/@nikclayton)
- Copying a URL from the end of a post could include an extra Unicode whitespace character, making the URL unusable as is.
- **Correctly display mixed RTL and LTR text in profiles**, [PR#3328](https://github.com/tuskyapp/Tusky/pull/3328) by [@nikclayton](https://mastodon.social/@nikclayton)
- Profile text that contained a mix of right-to-left and left-to-right writing directions would display incorrectly.
- **Stop showing duplicates of edited posts in threads**, [PR#3377](https://github.com/tuskyapp/Tusky/pull/3377) by [@Tak](https://mastodon.gamedev.place/@Tak)
- Editing a post in thread view would show the old and new version of the post in the thread.
- **Correct post length calculation**, [PR#3392](https://github.com/tuskyapp/Tusky/pull/3392) by [@nikclayton](https://mastodon.social/@nikclayton)
- In a post that mentioned a user (e.g., `@tusky@mastodon.social`) Tusky was incorrectly including the `@mastodon.social` part when calculating the post's length, leading to incorrect "This post is too long" errors.
- **Always publish image captions**, [PR#3421](https://github.com/tuskyapp/Tusky/pull/3421) by [@lakoja](https://freiburg.social/@lakoja)
- Finishing editing an image caption before the image had finished loading would lose the caption.
- **Clicking "Compose" from a notification would set the wrong account**, [PR#3688](https://github.com/tuskyapp/Tusky/pull/3688)
## v22.0 beta 7
### Significant bug fixes
- **Fetch all outstanding Mastodon notifications when creating Android notifications**, [PR#3700](https://github.com/tuskyapp/Tusky/pull/3700)
- **Clicking "Compose" from a notification would set the wrong account**, [PR#3688](https://github.com/tuskyapp/Tusky/pull/3688)
- **Ensure "last read notification ID" is saved to the correct account**, [PR#3697](https://github.com/tuskyapp/Tusky/pull/3697)
## v22.0 beta 6
### Significant bug fixes

View File

@ -212,6 +212,13 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
}
}
// TODO: This changes the accountManager's activeAccount property, but does not do any
// of the work that AccountManager.setActiveAccount() does. In particular:
//
// - The current active account is not saved
// - The account passed as parameter here goes not have its `isActive` property set
//
// Is that deliberate? Or is this a bug?
public void openAsAccount(@NonNull String url, @NonNull AccountEntity account) {
accountManager.setActiveAccount(account);
Intent intent = new Intent(this, MainActivity.class);

View File

@ -147,6 +147,9 @@ class ComposeActivity :
private lateinit var emojiBehavior: BottomSheetBehavior<*>
private lateinit var scheduleBehavior: BottomSheetBehavior<*>
/** The account that is being used to compose the status */
private lateinit var activeAccount: AccountEntity
private var photoUploadUri: Uri? = null
private val preferences by unsafeLazy { PreferenceManager.getDefaultSharedPreferences(this) }
@ -213,10 +216,15 @@ class ComposeActivity :
notificationManager.cancel(notificationId)
}
val accountId = intent.getLongExtra(ACCOUNT_ID_EXTRA, -1)
if (accountId != -1L) {
accountManager.setActiveAccount(accountId)
}
// If started from an intent then compose as the account ID from the intent.
// Otherwise use the active account. If null then the user is not logged in,
// and return from the activity.
val intentAccountId = intent.getLongExtra(ACCOUNT_ID_EXTRA, -1)
activeAccount = if (intentAccountId != -1L) {
accountManager.getAccountById(intentAccountId)
} else {
accountManager.activeAccount
} ?: return
val theme = preferences.getString("appTheme", APP_THEME_DEFAULT)
if (theme == "black") {
@ -225,8 +233,6 @@ class ComposeActivity :
setContentView(binding.root)
setupActionBar()
// do not do anything when not logged in, activity will be finished in super.onCreate() anyway
val activeAccount = accountManager.activeAccount ?: return
setupAvatar(activeAccount)
val mediaAdapter = MediaPreviewAdapter(
@ -1036,7 +1042,7 @@ class ComposeActivity :
}
lifecycleScope.launch {
viewModel.sendStatus(contentText, spoilerText)
viewModel.sendStatus(contentText, spoilerText, activeAccount.id)
deleteDraftAndFinish()
}
} else {

View File

@ -294,7 +294,8 @@ class ComposeViewModel @Inject constructor(
*/
suspend fun sendStatus(
content: String,
spoilerText: String
spoilerText: String,
accountId: Long
) {
if (!scheduledTootId.isNullOrEmpty()) {
api.deleteScheduledStatus(scheduledTootId!!)
@ -322,7 +323,7 @@ class ComposeViewModel @Inject constructor(
replyingStatusContent = null,
replyingStatusAuthorUsername = null,
quoteId = quoteId,
accountId = accountManager.activeAccount!!.id,
accountId = accountId,
draftId = draftId,
idempotencyKey = randomAlphanumericString(16),
retries = 0,

View File

@ -11,6 +11,7 @@ import com.keylesspalace.tusky.entity.Marker
import com.keylesspalace.tusky.entity.Notification
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.isLessThan
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
import kotlin.math.min
@ -35,10 +36,12 @@ class NotificationFetcher @Inject constructor(
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Create sorted list of new notifications
val notifications = fetchNewNotifications(account)
.filter { filterNotification(notificationManager, account, it) }
.sortedWith(compareBy({ it.id.length }, { it.id })) // oldest notifications first
.toMutableList()
val notifications = runBlocking { // OK, because in a worker thread
fetchNewNotifications(account)
.filter { filterNotification(notificationManager, account, it) }
.sortedWith(compareBy({ it.id.length }, { it.id })) // oldest notifications first
.toMutableList()
}
// There's a maximum limit on the number of notifications an Android app
// can display. If the total number of notifications (current notifications,
@ -114,7 +117,7 @@ class NotificationFetcher @Inject constructor(
* ones that were last fetched here. So `lastNotificationId` takes precedence if it is greater
* than the marker.
*/
private fun fetchNewNotifications(account: AccountEntity): List<Notification> {
private suspend fun fetchNewNotifications(account: AccountEntity): List<Notification> {
val authHeader = String.format("Bearer %s", account.accessToken)
// Figure out where to read from. Choose the most recent notification ID from:
@ -128,21 +131,37 @@ class NotificationFetcher @Inject constructor(
val markerId = if (remoteMarkerId.isLessThan(localMarkerId)) localMarkerId else remoteMarkerId
val readingPosition = account.lastNotificationId
val minId = if (readingPosition.isLessThan(markerId)) markerId else readingPosition
var minId: String? = if (readingPosition.isLessThan(markerId)) markerId else readingPosition
Log.d(TAG, " remoteMarkerId: $remoteMarkerId")
Log.d(TAG, " localMarkerId: $localMarkerId")
Log.d(TAG, " readingPosition: $readingPosition")
Log.d(TAG, "getting Notifications for ${account.fullName}, min_id: $minId")
val notifications = mastodonApi.notificationsWithAuth(
authHeader,
account.domain,
minId
).blockingGet()
// Fetch all outstanding notifications
val notifications = buildList {
while (minId != null) {
val response = mastodonApi.notificationsWithAuth(
authHeader,
account.domain,
minId = minId
)
if (!response.isSuccessful) break
// Notifications are returned in order, most recent first. Save the newest notification ID
// in the marker.
// Notifications are returned in the page in order, newest first,
// (https://github.com/mastodon/documentation/issues/1226), insert the
// new page at the head of the list.
response.body()?.let { addAll(0, it) }
// Get the previous page, which will be chronologically newer
// notifications. If it doesn't exist this is null and the loop
// will exit.
val links = Links.from(response.headers()["link"])
minId = links.prev
}
}
// Save the newest notification ID in the marker.
notifications.firstOrNull()?.let {
val newMarkerId = notifications.first().id
Log.d(TAG, "updating notification marker for ${account.fullName} to: $newMarkerId")
@ -158,13 +177,13 @@ class NotificationFetcher @Inject constructor(
return notifications
}
private fun fetchMarker(authHeader: String, account: AccountEntity): Marker? {
private suspend fun fetchMarker(authHeader: String, account: AccountEntity): Marker? {
return try {
val allMarkers = mastodonApi.markersWithAuth(
authHeader,
account.domain,
listOf("notifications")
).blockingGet()
)
val notificationMarker = allMarkers["notifications"]
Log.d(TAG, "Fetched marker for ${account.fullName}: $notificationMarker")
notificationMarker

View File

@ -606,9 +606,7 @@ public class NotificationHelper {
Log.d(TAG, "disabled notification checks");
}
public static void clearNotificationsForActiveAccount(@NonNull Context context, @NonNull AccountManager accountManager) {
AccountEntity account = accountManager.getActiveAccount();
if (account == null) return;
public static void clearNotificationsForAccount(@NonNull Context context, @NonNull AccountEntity account) {
int accountId = (int) account.getId();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

View File

@ -106,7 +106,7 @@ class NotificationsFragment :
adapter = NotificationsPagingAdapter(
notificationDiffCallback,
accountId = accountManager.activeAccount!!.accountId,
accountId = viewModel.account.accountId,
statusActionListener = this,
notificationActionListener = this,
accountActionListener = this,
@ -460,7 +460,7 @@ class NotificationsFragment :
override fun onRefresh() {
binding.progressBar.isVisible = false
adapter.refresh()
NotificationHelper.clearNotificationsForActiveAccount(requireContext(), accountManager)
NotificationHelper.clearNotificationsForAccount(requireContext(), viewModel.account)
}
override fun onPause() {
@ -477,7 +477,7 @@ class NotificationsFragment :
override fun onResume() {
super.onResume()
NotificationHelper.clearNotificationsForActiveAccount(requireContext(), accountManager)
NotificationHelper.clearNotificationsForAccount(requireContext(), viewModel.account)
}
override fun onReply(position: Int) {
@ -611,7 +611,7 @@ class NotificationsFragment :
override fun onViewReport(reportId: String) {
requireContext().openLink(
"https://${accountManager.activeAccount!!.domain}/admin/reports/$reportId"
"https://${viewModel.account.domain}/admin/reports/$reportId"
)
}
@ -627,7 +627,7 @@ class NotificationsFragment :
}
companion object {
private const val TAG = "NotificationF"
private const val TAG = "NotificationsFragment"
fun newInstance() = NotificationsFragment()
private val notificationDiffCallback: DiffUtil.ItemCallback<NotificationViewData> =

View File

@ -31,7 +31,21 @@ import retrofit2.Response
import javax.inject.Inject
/** Models next/prev links from the "Links" header in an API response */
data class Links(val next: String?, val prev: String?)
data class Links(val next: String?, val prev: String?) {
companion object {
fun from(linkHeader: String?): Links {
val links = HttpHeaderLink.parse(linkHeader)
return Links(
next = HttpHeaderLink.findByRelationType(links, "next")?.uri?.getQueryParameter(
"max_id"
),
prev = HttpHeaderLink.findByRelationType(links, "prev")?.uri?.getQueryParameter(
"min_id"
)
)
}
}
}
/** [PagingSource] for Mastodon Notifications, identified by the Notification ID */
class NotificationsPagingSource @Inject constructor(
@ -79,7 +93,7 @@ class NotificationsPagingSource @Inject constructor(
return LoadResult.Error(Throwable("HTTP $code: $msg"))
}
val links = getPageLinks(response.headers()["link"])
val links = Links.from(response.headers()["link"])
return LoadResult.Page(
data = response.body()!!,
nextKey = links.next,
@ -188,18 +202,6 @@ class NotificationsPagingSource @Inject constructor(
)
}
private fun getPageLinks(linkHeader: String?): Links {
val links = HttpHeaderLink.parse(linkHeader)
return Links(
next = HttpHeaderLink.findByRelationType(links, "next")?.uri?.getQueryParameter(
"max_id"
),
prev = HttpHeaderLink.findByRelationType(links, "prev")?.uri?.getQueryParameter(
"min_id"
)
)
}
override fun getRefreshKey(state: PagingState<String, Notification>): String? {
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)

View File

@ -282,6 +282,8 @@ class NotificationsViewModel @Inject constructor(
private val timelineCases: TimelineCases,
private val eventHub: EventHub
) : ViewModel() {
/** The account to display notifications for */
val account = accountManager.activeAccount!!
val uiState: StateFlow<UiState>
@ -316,16 +318,14 @@ class NotificationsViewModel @Inject constructor(
// Save each change back to the active account
.onEach { action ->
Log.d(TAG, "notificationFilter: $action")
accountManager.activeAccount?.let { account ->
account.notificationsFilter = serialize(action.filter)
accountManager.saveAccount(account)
}
account.notificationsFilter = serialize(action.filter)
accountManager.saveAccount(account)
}
// Load the initial filter from the active account
.onStart {
emit(
InfallibleUiAction.ApplyFilter(
filter = deserialize(accountManager.activeAccount?.notificationsFilter)
filter = deserialize(account.notificationsFilter)
)
)
}
@ -336,11 +336,9 @@ class NotificationsViewModel @Inject constructor(
.filterIsInstance<InfallibleUiAction.SaveVisibleId>()
.distinctUntilChanged()
.collectLatest { action ->
Log.d(TAG, "Saving visible ID: ${action.visibleId}")
accountManager.activeAccount?.let { account ->
account.lastNotificationId = action.visibleId
accountManager.saveAccount(account)
}
Log.d(TAG, "Saving visible ID: ${action.visibleId}, active account = ${account.id}")
account.lastNotificationId = action.visibleId
accountManager.saveAccount(account)
}
}
@ -351,7 +349,7 @@ class NotificationsViewModel @Inject constructor(
statusDisplayOptions = MutableStateFlow(
StatusDisplayOptions.from(
preferences,
accountManager.activeAccount!!
account
)
)
@ -363,7 +361,7 @@ class NotificationsViewModel @Inject constructor(
statusDisplayOptions.value.make(
preferences,
it.preferenceKey,
accountManager.activeAccount!!
account
)
}
.collect {
@ -494,7 +492,7 @@ class NotificationsViewModel @Inject constructor(
// The database stores "0" as the last notification ID if notifications have not been
// fetched. Convert to null to ensure a full fetch in this case
private fun getInitialKey(): String? {
val initialKey = when (val id = accountManager.activeAccount?.lastNotificationId) {
val initialKey = when (val id = account.lastNotificationId) {
"0" -> null
else -> id
}

View File

@ -52,7 +52,7 @@ class AccountManager @Inject constructor(db: AppDatabase) {
/**
* Adds a new account and makes it the active account.
* @param accessToken the access token for the new account
* @param domain the domain of the accounts Mastodon instance
* @param domain the domain of the account's Mastodon instance
* @param clientId the oauth client id used to sign in the account
* @param clientSecret the oauth client secret used to sign in the account
* @param oauthScopes the oauth scopes granted to the account

View File

@ -146,15 +146,15 @@ interface MastodonApi {
): Response<Notification>
@GET("api/v1/markers")
fun markersWithAuth(
suspend fun markersWithAuth(
@Header("Authorization") auth: String,
@Header(DOMAIN_HEADER) domain: String,
@Query("timeline[]") timelines: List<String>
): Single<Map<String, Marker>>
): Map<String, Marker>
@FormUrlEncoded
@POST("api/v1/markers")
fun updateMarkersWithAuth(
suspend fun updateMarkersWithAuth(
@Header("Authorization") auth: String,
@Header(DOMAIN_HEADER) domain: String,
@Field("home[last_read_id]") homeLastReadId: String? = null,
@ -162,11 +162,12 @@ interface MastodonApi {
): NetworkResult<Unit>
@GET("api/v1/notifications")
fun notificationsWithAuth(
suspend fun notificationsWithAuth(
@Header("Authorization") auth: String,
@Header(DOMAIN_HEADER) domain: String,
/** Return results immediately newer than this ID */
@Query("min_id") minId: String?
): Single<List<Notification>>
): Response<List<Notification>>
@POST("api/v1/notifications/clear")
suspend fun clearNotifications(): Response<ResponseBody>

View File

@ -568,7 +568,7 @@
<string name="action_set_focus">Fokuspunkt setzen</string>
<string name="action_add_reaction">Reaktion hinzufügen</string>
<string name="failed_to_remove_from_list">Das Konto konnte nicht aus der Liste entfernt werden</string>
<string name="action_add_or_remove_from_list">Zur Liste hinzufügen / aus Liste entfernen</string>
<string name="action_add_or_remove_from_list">Zur Liste hzfg. / aus Liste entf.</string>
<string name="failed_to_add_to_list">Das Konto konnte nicht zur Liste hinzugefügt werden</string>
<string name="no_lists">Du hast keine Listen.</string>
<string name="notification_summary_report_format">%s · %d Beiträge angehängt</string>

View File

@ -388,7 +388,7 @@
<string name="mute_domain_warning">Sei sicuro di voler bloccare tutto %s\? Non vedrai nessun contenuto da quel dominio in nessuna timeline pubblica o nelle tue notifiche. I tuoi seguaci che stanno in quel dominio saranno rimossi.</string>
<string name="mute_domain_warning_dialog_ok">Nascondi l\'intero dominio</string>
<string name="pref_title_notification_filter_poll">dei sondaggi si sono conclusi</string>
<string name="pref_title_animate_gif_avatars">Riproduci animazioni avatars</string>
<string name="pref_title_animate_gif_avatars">Riproduci avatar animati</string>
<string name="notification_poll_name">Votazioni</string>
<string name="notification_poll_description">Notifiche sui sondaggi che si sono conclusi</string>
<string name="filter_dialog_whole_word">Parola intera</string>
@ -645,4 +645,4 @@
<string name="total_accounts">Account totali</string>
<string name="action_refresh">Aggiorna</string>
<string name="title_public_trending_hashtags">Hashtag di tendenza</string>
</resources>
</resources>

View File

@ -0,0 +1,5 @@
Tusky 22.0 Beta 5
Behobene Fehler:
- »APNG library« auf eine ältere Version zurückgestuft, damit animierte Emojis wieder animiert sind
- Eine Kopie der Benachrichtigungsmarkierung wird lokal gespeichert, falls der Server die entsprechende API nicht unterstützt

View File

@ -0,0 +1,4 @@
Tusky 22.0 Beta 6
Behobener Fehler:
- Leseposition wird im Tab »Benachrichtigung« häufiger gespeichert

View File

@ -0,0 +1,11 @@
Tusky 22.0 beta 7
Fixes:
### Significant bug fixes
- Fetch all outstanding Mastodon notifications when creating Android notifications
- Clicking "Compose" from a notification would set the wrong account
- Ensure "last read notification ID" is saved to the correct account

View File

@ -0,0 +1,20 @@
Tusky 22.0
New features:
- View trending hashtags
- Follow new hashtags
- Better ordering when selecting languages
- Show the difference between versions of a post
- Support Mastodon v4 filters
- Option to show post statistics in the timeline
- And more...
Fixes:
- Remember selected tab and position
- Keep notifications until read
- Correctly display mixed RTL and LTR text in profiles
- Correct post length calculation
- Always publish image captions
- And more...

View File

@ -0,0 +1,5 @@
تاسکی ۲۲٫۰ بتا ۶
رفع اشکال‌ها:
- ذخیرهٔ زودبه‌زودتر مکان خواندن در زبانهٔ آگاهی‌ها

View File

@ -0,0 +1,10 @@
تاسکی ۲۲٫۰ بتا ۷
رفع اشکال:
## رفع اشکال‌های برجسته
- گرفتن تمامی‌آگاهی‌های ماستودون مانده هنگام ساخت آگاهی اندروید
- کلیک‌روی ایجاد از آگاهی حساب اشتباهی را تنظیم می‌کرد
- اطمینان از ذخیرهٔ «آخرین شناسهٔ آگاهی خوانده شده» برای حساب درست