diff --git a/app/schemas/com.keylesspalace.tusky.db.AppDatabase/35.json b/app/schemas/com.keylesspalace.tusky.db.AppDatabase/35.json index a163beddc..7653fbffd 100644 --- a/app/schemas/com.keylesspalace.tusky.db.AppDatabase/35.json +++ b/app/schemas/com.keylesspalace.tusky.db.AppDatabase/35.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 35, - "identityHash": "60458dd94b60b1b86f7759420340f707", + "identityHash": "6714e8c4ae8f64bcd1e65ef6d8a3f958", "entities": [ { "tableName": "DraftEntity", @@ -359,7 +359,7 @@ }, { "tableName": "TimelineStatusEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `url` TEXT, `timelineUserId` INTEGER NOT NULL, `authorServerId` TEXT, `inReplyToId` TEXT, `inReplyToAccountId` TEXT, `content` TEXT, `createdAt` INTEGER NOT NULL, `emojis` TEXT, `reblogsCount` INTEGER NOT NULL, `favouritesCount` INTEGER NOT NULL, `reblogged` INTEGER NOT NULL, `bookmarked` INTEGER NOT NULL, `favourited` INTEGER NOT NULL, `sensitive` INTEGER NOT NULL, `spoilerText` TEXT NOT NULL, `visibility` INTEGER NOT NULL, `attachments` TEXT, `mentions` TEXT, `tags` TEXT, `application` TEXT, `reblogServerId` TEXT, `reblogAccountId` TEXT, `poll` TEXT, `muted` INTEGER, `expanded` INTEGER NOT NULL, `contentCollapsed` INTEGER NOT NULL, `contentShowing` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `quote` TEXT, PRIMARY KEY(`serverId`, `timelineUserId`), FOREIGN KEY(`authorServerId`, `timelineUserId`) REFERENCES `TimelineAccountEntity`(`serverId`, `timelineUserId`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `url` TEXT, `timelineUserId` INTEGER NOT NULL, `authorServerId` TEXT, `inReplyToId` TEXT, `inReplyToAccountId` TEXT, `content` TEXT, `createdAt` INTEGER NOT NULL, `emojis` TEXT, `reblogsCount` INTEGER NOT NULL, `favouritesCount` INTEGER NOT NULL, `reblogged` INTEGER NOT NULL, `bookmarked` INTEGER NOT NULL, `favourited` INTEGER NOT NULL, `sensitive` INTEGER NOT NULL, `spoilerText` TEXT NOT NULL, `visibility` INTEGER NOT NULL, `attachments` TEXT, `mentions` TEXT, `tags` TEXT, `application` TEXT, `reblogServerId` TEXT, `reblogAccountId` TEXT, `poll` TEXT, `muted` INTEGER, `expanded` INTEGER NOT NULL, `contentCollapsed` INTEGER NOT NULL, `contentShowing` INTEGER NOT NULL, `pinned` INTEGER NOT NULL, `card` TEXT, `quote` TEXT, PRIMARY KEY(`serverId`, `timelineUserId`), FOREIGN KEY(`authorServerId`, `timelineUserId`) REFERENCES `TimelineAccountEntity`(`serverId`, `timelineUserId`) ON UPDATE NO ACTION ON DELETE NO ACTION )", "fields": [ { "fieldPath": "serverId", @@ -535,6 +535,12 @@ "affinity": "INTEGER", "notNull": true }, + { + "fieldPath": "card", + "columnName": "card", + "affinity": "TEXT", + "notNull": false + }, { "fieldPath": "quote", "columnName": "quote", @@ -815,7 +821,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '60458dd94b60b1b86f7759420340f707')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6714e8c4ae8f64bcd1e65ef6d8a3f958')" ] } } \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt index 9ffeca9ea..ef366795f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/PollAdapter.kt @@ -19,7 +19,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat -import androidx.emoji2.text.EmojiCompat import androidx.recyclerview.widget.RecyclerView import com.keylesspalace.tusky.R import com.keylesspalace.tusky.databinding.ItemPollBinding @@ -87,9 +86,8 @@ class PollAdapter : RecyclerView.Adapter>() { when (mode) { RESULT -> { val percent = calculatePercent(option.votesCount, votersCount, voteCount) - val emojifiedPollOptionText = buildDescription(option.title, percent, option.voted, resultTextView.context) + resultTextView.text = buildDescription(option.title, percent, option.voted, resultTextView.context) .emojify(emojis, resultTextView, animateEmojis) - resultTextView.text = EmojiCompat.get().process(emojifiedPollOptionText) val level = percent * 100 val optionColor = if (option.voted) { @@ -103,8 +101,7 @@ class PollAdapter : RecyclerView.Adapter>() { resultTextView.setOnClickListener(resultClickListener) } SINGLE -> { - val emojifiedPollOptionText = option.title.emojify(emojis, radioButton, animateEmojis) - radioButton.text = EmojiCompat.get().process(emojifiedPollOptionText) + radioButton.text = option.title.emojify(emojis, radioButton, animateEmojis) radioButton.isChecked = option.selected radioButton.setOnClickListener { pollOptions.forEachIndexed { index, pollOption -> @@ -114,8 +111,7 @@ class PollAdapter : RecyclerView.Adapter>() { } } MULTIPLE -> { - val emojifiedPollOptionText = option.title.emojify(emojis, checkBox, animateEmojis) - checkBox.text = EmojiCompat.get().process(emojifiedPollOptionText) + checkBox.text = option.title.emojify(emojis, checkBox, animateEmojis) checkBox.isChecked = option.selected checkBox.setOnCheckedChangeListener { _, isChecked -> pollOptions[holder.bindingAdapterPosition].selected = isChecked diff --git a/app/src/main/java/com/keylesspalace/tusky/components/account/AccountActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/account/AccountActivity.kt index a22f90f30..e60fa13fd 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/account/AccountActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/account/AccountActivity.kt @@ -37,7 +37,6 @@ import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat.Type.systemBars import androidx.core.view.updatePadding -import androidx.emoji2.text.EmojiCompat import androidx.preference.PreferenceManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.viewpager2.widget.MarginPageTransformer @@ -464,14 +463,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI */ private fun updateToolbar() { loadedAccount?.let { account -> - - val emojifiedName = account.name.emojify(account.emojis, binding.accountToolbar, animateEmojis) - - try { - supportActionBar?.title = EmojiCompat.get().process(emojifiedName) - } catch (e: IllegalStateException) { - supportActionBar?.title = emojifiedName - } + supportActionBar?.title = account.name.emojify(account.emojis, binding.accountToolbar, animateEmojis) supportActionBar?.subtitle = String.format(getString(R.string.post_username_format), account.username) } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt index 8aaa54cff..d6abb1a8f 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt @@ -53,6 +53,7 @@ import androidx.core.view.OnReceiveContentListener import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.core.widget.doAfterTextChanged +import androidx.lifecycle.asLiveData import androidx.lifecycle.lifecycleScope import androidx.preference.PreferenceManager import androidx.recyclerview.widget.LinearLayoutManager @@ -398,14 +399,17 @@ class ComposeActivity : viewModel.statusVisibility.observe { visibility -> setStatusVisibility(visibility) } - viewModel.media.observe { media -> - mediaAdapter.submitList(media) - if (media.size != mediaCount) { - mediaCount = media.size - binding.composeMediaPreviewBar.visible(media.isNotEmpty()) - updateSensitiveMediaToggle(viewModel.markMediaAsSensitive.value != false, viewModel.showContentWarning.value != false) + lifecycleScope.launch { + viewModel.media.collect { media -> + mediaAdapter.submitList(media) + if (media.size != mediaCount) { + mediaCount = media.size + binding.composeMediaPreviewBar.visible(media.isNotEmpty()) + updateSensitiveMediaToggle(viewModel.markMediaAsSensitive.value != false, viewModel.showContentWarning.value != false) + } } } + viewModel.poll.observe { poll -> binding.pollPreview.visible(poll != null) poll?.let(binding.pollPreview::setPoll) @@ -418,7 +422,7 @@ class ComposeActivity : } updateScheduleButton() } - combineOptionalLiveData(viewModel.media, viewModel.poll) { media, poll -> + combineOptionalLiveData(viewModel.media.asLiveData(), viewModel.poll) { media, poll -> val active = poll == null && media!!.size != 4 && (media.isEmpty() || media.first().type == QueuedMedia.Type.IMAGE) @@ -858,7 +862,7 @@ class ComposeActivity : spoilerText = binding.composeContentWarningField.text.toString() } val characterCount = calculateTextLength() - if ((characterCount <= 0 || contentText.isBlank()) && viewModel.media.value!!.isEmpty()) { + if ((characterCount <= 0 || contentText.isBlank()) && viewModel.media.value.isEmpty()) { binding.composeEditField.error = getString(R.string.error_empty) enableButtons(true) } else if (characterCount <= maximumTootCharacters) { @@ -866,7 +870,7 @@ class ComposeActivity : contentText += " ${binding.editTextDefaultText.text}" } - if (viewModel.media.value!!.isNotEmpty()) { + if (viewModel.media.value.isNotEmpty()) { finishingUploadDialog = ProgressDialog.show( this, getString(R.string.dialog_title_finishing_media_upload), getString(R.string.dialog_message_uploading_media), true, true @@ -1044,8 +1048,19 @@ class ComposeActivity : } private fun saveDraftAndFinish(contentText: String, contentWarning: String) { - viewModel.saveDraft(contentText, contentWarning) - finishWithoutSlideOutAnimation() + lifecycleScope.launch { + val dialog = if (viewModel.shouldShowSaveDraftDialog()) { + ProgressDialog.show( + this@ComposeActivity, null, + getString(R.string.saving_draft), true, false + ) + } else { + null + } + viewModel.saveDraft(contentText, contentWarning) + dialog?.cancel() + finishWithoutSlideOutAnimation() + } } override fun search(token: String): List { @@ -1064,7 +1079,7 @@ class ComposeActivity : } data class QueuedMedia( - val localId: Long, + val localId: Int, val uri: Uri, val type: Type, val mediaSize: Long, diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt index 4d88ec279..0fa769cdf 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeViewModel.kt @@ -21,6 +21,7 @@ import androidx.core.net.toUri import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import com.keylesspalace.tusky.components.compose.ComposeActivity.QueuedMedia import com.keylesspalace.tusky.components.drafts.DraftHelper @@ -36,15 +37,17 @@ import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.service.ServiceClient import com.keylesspalace.tusky.service.StatusToSend import com.keylesspalace.tusky.util.combineLiveData -import com.keylesspalace.tusky.util.filter -import com.keylesspalace.tusky.util.map import com.keylesspalace.tusky.util.randomAlphanumericString import com.keylesspalace.tusky.util.toLiveData -import com.keylesspalace.tusky.util.withoutFirstWhich import io.reactivex.rxjava3.core.Observable import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.flow.updateAndGet import kotlinx.coroutines.launch import kotlinx.coroutines.rx3.rxSingle import kotlinx.coroutines.withContext @@ -87,12 +90,12 @@ class ComposeViewModel @Inject constructor( val poll: MutableLiveData = mutableLiveData(null) val scheduledAt: MutableLiveData = mutableLiveData(null) - val media = mutableLiveData>(listOf()) + val media: MutableStateFlow> = MutableStateFlow(emptyList()) val uploadError = MutableLiveData() val domain = accountManager.activeAccount?.domain!! - private val mediaToJob = mutableMapOf() + private val mediaToJob = mutableMapOf() private val isEditingScheduledToot get() = !scheduledTootId.isNullOrEmpty() @@ -114,7 +117,7 @@ class ComposeViewModel @Inject constructor( suspend fun pickMedia(mediaUri: Uri, description: String? = null): Result = withContext(Dispatchers.IO) { try { val (type, uri, size) = mediaUploader.prepareMedia(mediaUri) - val mediaItems = media.value!! + val mediaItems = media.value if (type != QueuedMedia.Type.IMAGE && mediaItems.isNotEmpty() && mediaItems[0].type == QueuedMedia.Type.IMAGE @@ -129,29 +132,31 @@ class ComposeViewModel @Inject constructor( } } - private fun addMediaToQueue( + private suspend fun addMediaToQueue( type: QueuedMedia.Type, uri: Uri, mediaSize: Long, description: String? = null ): QueuedMedia { - val mediaItem = QueuedMedia( - localId = System.currentTimeMillis(), - uri = uri, - type = type, - mediaSize = mediaSize, - description = description - ) - media.postValue(media.value!! + mediaItem) + val mediaItem = media.updateAndGet { mediaValue -> + val mediaItem = QueuedMedia( + localId = (mediaValue.maxOfOrNull { it.localId } ?: 0) + 1, + uri = uri, + type = type, + mediaSize = mediaSize, + description = description + ) + mediaValue + mediaItem + }.last() mediaToJob[mediaItem.localId] = viewModelScope.launch { mediaUploader .uploadMedia(mediaItem) .catch { error -> - media.postValue(media.value?.filter { it.localId != mediaItem.localId } ?: emptyList()) + media.update { mediaValue -> mediaValue.filter { it.localId != mediaItem.localId } } uploadError.postValue(error) } .collect { event -> - val item = media.value?.find { it.localId == mediaItem.localId } + val item = media.value.find { it.localId == mediaItem.localId } ?: return@collect val newMediaItem = when (event) { is UploadEvent.ProgressEvent -> @@ -159,16 +164,14 @@ class ComposeViewModel @Inject constructor( is UploadEvent.FinishedEvent -> item.copy(id = event.mediaId, uploadPercent = -1) } - synchronized(media) { - val mediaValue = media.value!! - val index = mediaValue.indexOfFirst { it.localId == newMediaItem.localId } - media.postValue( - if (index == -1) { - mediaValue + newMediaItem + media.update { mediaValue -> + mediaValue.map { mediaItem -> + if (mediaItem.localId == newMediaItem.localId) { + newMediaItem } else { - mediaValue.toMutableList().also { it[index] = newMediaItem } + mediaItem } - ) + } } } } @@ -176,13 +179,23 @@ class ComposeViewModel @Inject constructor( } private fun addUploadedMedia(id: String, type: QueuedMedia.Type, uri: Uri, description: String?) { - val mediaItem = QueuedMedia(System.currentTimeMillis(), uri, type, 0, -1, id, description) - media.value = media.value!! + mediaItem + media.update { mediaValue -> + val mediaItem = QueuedMedia( + localId = (mediaValue.maxOfOrNull { it.localId } ?: 0) + 1, + uri = uri, + type = type, + mediaSize = 0, + uploadPercent = -1, + id = id, + description = description + ) + mediaValue + mediaItem + } } fun removeMediaFromQueue(item: QueuedMedia) { mediaToJob[item.localId]?.cancel() - media.value = media.value!!.withoutFirstWhich { it.localId == item.localId } + media.update { mediaValue -> mediaValue.filter { it.localId == item.localId } } } fun toggleMarkSensitive() { @@ -218,31 +231,36 @@ class ComposeViewModel @Inject constructor( } } - fun saveDraft(content: String, contentWarning: String) { - viewModelScope.launch { - val mediaUris: MutableList = mutableListOf() - val mediaDescriptions: MutableList = mutableListOf() - media.value?.forEach { item -> - mediaUris.add(item.uri.toString()) - mediaDescriptions.add(item.description) - } - - draftHelper.saveDraft( - draftId = draftId, - accountId = accountManager.activeAccount?.id!!, - inReplyToId = inReplyToId, - content = content, - contentWarning = contentWarning, - sensitive = markMediaAsSensitive.value!!, - visibility = statusVisibility.value!!, - mediaUris = mediaUris, - mediaDescriptions = mediaDescriptions, - poll = poll.value, - failedToSend = false - ) + fun shouldShowSaveDraftDialog(): Boolean { + // if any of the media files need to be downloaded first it could take a while, so show a loading dialog + return media.value.any { mediaValue -> + mediaValue.uri.scheme == "https" } } + suspend fun saveDraft(content: String, contentWarning: String) { + val mediaUris: MutableList = mutableListOf() + val mediaDescriptions: MutableList = mutableListOf() + media.value.forEach { item -> + mediaUris.add(item.uri.toString()) + mediaDescriptions.add(item.description) + } + + draftHelper.saveDraft( + draftId = draftId, + accountId = accountManager.activeAccount?.id!!, + inReplyToId = inReplyToId, + content = content, + contentWarning = contentWarning, + sensitive = markMediaAsSensitive.value!!, + visibility = statusVisibility.value!!, + mediaUris = mediaUris, + mediaDescriptions = mediaDescriptions, + poll = poll.value, + failedToSend = false + ) + } + /** * Send status to the server. * Uses current state plus provided arguments. @@ -259,14 +277,14 @@ class ComposeViewModel @Inject constructor( Observable.just(Unit) }.toLiveData() - val sendObservable = media + val sendFlow = media .filter { items -> items.all { it.uploadPercent == -1 } } .map { val mediaIds: MutableList = mutableListOf() val mediaUris: MutableList = mutableListOf() val mediaDescriptions: MutableList = mutableListOf() val mediaProcessed: MutableList = mutableListOf() - for (item in media.value!!) { + for (item in media.value) { mediaIds.add(item.id!!) mediaUris.add(item.uri) mediaDescriptions.add(item.description ?: "") @@ -297,17 +315,21 @@ class ComposeViewModel @Inject constructor( serviceClient.sendToot(tootToSend) } - return combineLiveData(deletionObservable, sendObservable) { _, _ -> } + return combineLiveData(deletionObservable, sendFlow.asLiveData()) { _, _ -> } } - suspend fun updateDescription(localId: Long, description: String): Boolean { - val newList = media.value!!.toMutableList() - val index = newList.indexOfFirst { it.localId == localId } - if (index != -1) { - newList[index] = newList[index].copy(description = description) + suspend fun updateDescription(localId: Int, description: String): Boolean { + val newMediaList = media.updateAndGet { mediaValue -> + mediaValue.map { mediaItem -> + if (mediaItem.localId == localId) { + mediaItem.copy(description = description) + } else { + mediaItem + } + } } - media.value = newList - val updatedItem = newList.find { it.localId == localId } + + val updatedItem = newMediaList.find { it.localId == localId } if (updatedItem?.id != null) { return api.updateMedia(updatedItem.id, description) .fold({ @@ -403,8 +425,8 @@ class ComposeViewModel @Inject constructor( val draftAttachments = composeOptions?.draftAttachments if (draftAttachments != null) { // when coming from DraftActivity - draftAttachments.forEach { attachment -> - viewModelScope.launch { + viewModelScope.launch { + draftAttachments.forEach { attachment -> pickMedia(attachment.uri, attachment.description) } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/compose/MediaUploader.kt b/app/src/main/java/com/keylesspalace/tusky/components/compose/MediaUploader.kt index dfa7e6bf8..54bdf035d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/compose/MediaUploader.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/compose/MediaUploader.kt @@ -26,7 +26,7 @@ import androidx.core.net.toUri import com.keylesspalace.tusky.BuildConfig import com.keylesspalace.tusky.R import com.keylesspalace.tusky.components.compose.ComposeActivity.QueuedMedia -import com.keylesspalace.tusky.network.MastodonApi +import com.keylesspalace.tusky.network.MediaUploadApi import com.keylesspalace.tusky.network.ProgressRequestBody import com.keylesspalace.tusky.util.MEDIA_SIZE_UNKNOWN import com.keylesspalace.tusky.util.getImageSquarePixels @@ -75,7 +75,7 @@ class CouldNotOpenFileException : Exception() class MediaUploader @Inject constructor( private val context: Context, - private val mastodonApi: MastodonApi + private val mediaUploadApi: MediaUploadApi ) { @OptIn(ExperimentalCoroutinesApi::class) @@ -222,7 +222,7 @@ class MediaUploader @Inject constructor( null } - val result = mastodonApi.uploadMedia(body, description).getOrThrow() + val result = mediaUploadApi.uploadMedia(body, description).getOrThrow() if (media.uri.scheme == "file") { media.uri.path?.let { File(it).delete() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftHelper.kt b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftHelper.kt index 7511dc3c3..a6cd3fcd7 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftHelper.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftHelper.kt @@ -30,7 +30,12 @@ import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.util.IOUtils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import okhttp3.OkHttpClient +import okhttp3.Request +import okio.buffer +import okio.sink import java.io.File +import java.io.IOException import java.text.SimpleDateFormat import java.util.Date import java.util.Locale @@ -38,6 +43,7 @@ import javax.inject.Inject class DraftHelper @Inject constructor( val context: Context, + val okHttpClient: OkHttpClient, db: AppDatabase ) { @@ -71,11 +77,11 @@ class DraftHelper @Inject constructor( val uris = mediaUris.map { uriString -> uriString.toUri() - }.map { uri -> - if (uri.isNotInFolder(draftDirectory)) { - uri.copyToFolder(draftDirectory) - } else { + }.mapNotNull { uri -> + if (uri.isInFolder(draftDirectory)) { uri + } else { + uri.copyToFolder(draftDirectory) } } @@ -114,6 +120,7 @@ class DraftHelper @Inject constructor( ) draftDao.insertOrReplace(draft) + Log.d("DraftHelper", "saved draft to db") } suspend fun deleteDraftAndAttachments(draftId: Int) { @@ -133,33 +140,55 @@ class DraftHelper @Inject constructor( } } - suspend fun deleteAttachments(draft: DraftEntity) { - withContext(Dispatchers.IO) { - draft.attachments.forEach { attachment -> - if (context.contentResolver.delete(attachment.uri, null, null) == 0) { - Log.e("DraftHelper", "Did not delete file ${attachment.uriString}") - } + suspend fun deleteAttachments(draft: DraftEntity) = withContext(Dispatchers.IO) { + draft.attachments.forEach { attachment -> + if (context.contentResolver.delete(attachment.uri, null, null) == 0) { + Log.e("DraftHelper", "Did not delete file ${attachment.uriString}") } } } - private fun Uri.isNotInFolder(folder: File): Boolean { + private fun Uri.isInFolder(folder: File): Boolean { val filePath = path ?: return true return File(filePath).parentFile == folder } - private fun Uri.copyToFolder(folder: File): Uri { + private fun Uri.copyToFolder(folder: File): Uri? { val contentResolver = context.contentResolver - val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date()) - val mimeType = contentResolver.getType(this) - val map = MimeTypeMap.getSingleton() - val fileExtension = map.getExtensionFromMimeType(mimeType) + val fileExtension = if (scheme == "https") { + lastPathSegment?.substringAfterLast('.', "tmp") + } else { + val mimeType = contentResolver.getType(this) + val map = MimeTypeMap.getSingleton() + map.getExtensionFromMimeType(mimeType) + } val filename = String.format("Tusky_Draft_Media_%s.%s", timeStamp, fileExtension) val file = File(folder, filename) - IOUtils.copyToFile(contentResolver, this, file) + + if (scheme == "https") { + // saving redrafted media + try { + val request = Request.Builder().url(toString()).build() + + val response = okHttpClient.newCall(request).execute() + + val sink = file.sink().buffer() + + response.body?.source()?.use { input -> + sink.use { output -> + output.writeAll(input) + } + } + } catch (ex: IOException) { + Log.w("DraftHelper", "failed to save media", ex) + return null + } + } else { + IOUtils.copyToFile(contentResolver, this, file) + } return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", file) } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/login/LoginActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/login/LoginActivity.kt index dc3c16962..1dafa9522 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/login/LoginActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/login/LoginActivity.kt @@ -66,7 +66,9 @@ class LoginActivity : BaseActivity(), Injectable { // Authorization failed. Put the error response where the user can read it and they // can try again. setLoading(false) - binding.domainTextInputLayout.error = getString(R.string.error_authorization_denied) + // Use error returned by the server or fall back to the generic message + binding.domainTextInputLayout.error = + result.errorMessage.ifBlank { getString(R.string.error_authorization_denied) } Log.e( TAG, "%s %s".format( diff --git a/app/src/main/java/com/keylesspalace/tusky/components/login/LoginWebViewActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/login/LoginWebViewActivity.kt index 58f745e79..2ed38720b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/login/LoginWebViewActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/login/LoginWebViewActivity.kt @@ -20,7 +20,7 @@ import androidx.core.net.toUri import com.keylesspalace.tusky.BaseActivity import com.keylesspalace.tusky.BuildConfig import com.keylesspalace.tusky.R -import com.keylesspalace.tusky.databinding.LoginWebviewBinding +import com.keylesspalace.tusky.databinding.ActivityLoginWebviewBinding import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.viewBinding @@ -78,7 +78,7 @@ sealed class LoginResult : Parcelable { /** Activity to do Oauth process using WebView. */ class LoginWebViewActivity : BaseActivity(), Injectable { - private val binding by viewBinding(LoginWebviewBinding::inflate) + private val binding by viewBinding(ActivityLoginWebviewBinding::inflate) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -117,7 +117,7 @@ class LoginWebViewActivity : BaseActivity(), Injectable { error: WebResourceError ) { Log.d("LoginWeb", "Failed to load ${data.url}: $error") - finishWithoutSlideOutAnimation() + sendResult(LoginResult.Err(getString(R.string.error_could_not_load_login_page))) } override fun shouldOverrideUrlLoading( diff --git a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineTypeMappers.kt b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineTypeMappers.kt index c351f6afb..12a0774d4 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineTypeMappers.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineTypeMappers.kt @@ -21,6 +21,7 @@ import com.keylesspalace.tusky.db.TimelineAccountEntity import com.keylesspalace.tusky.db.TimelineStatusEntity import com.keylesspalace.tusky.db.TimelineStatusWithAccount import com.keylesspalace.tusky.entity.Attachment +import com.keylesspalace.tusky.entity.Card import com.keylesspalace.tusky.entity.Emoji import com.keylesspalace.tusky.entity.HashTag import com.keylesspalace.tusky.entity.Poll @@ -97,6 +98,7 @@ fun Placeholder.toEntity(timelineUserId: Long): TimelineStatusEntity { contentCollapsed = false, contentShowing = false, pinned = false, + card = null, quote = null, ) } @@ -138,6 +140,7 @@ fun Status.toEntity( contentShowing = contentShowing, contentCollapsed = contentCollapsed, pinned = actionableStatus.pinned == true, + card = actionableStatus.card?.let(gson::toJson), quote = actionableStatus.quote?.let(gson::toJson), ) } @@ -153,6 +156,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson): StatusViewData { val application = gson.fromJson(status.application, Status.Application::class.java) val emojis: List = gson.fromJson(status.emojis, emojisListType) ?: emptyList() val poll: Poll? = gson.fromJson(status.poll, Poll::class.java) + val card: Card? = gson.fromJson(status.card, Card::class.java) val quote: Status? = gson.fromJson(status.quote, Status::class.java) val reblog = status.reblogServerId?.let { id -> @@ -181,7 +185,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson): StatusViewData { pinned = false, muted = status.muted, poll = poll, - card = null, + card = card, quote = quote, ) } @@ -240,7 +244,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson): StatusViewData { pinned = status.pinned, muted = status.muted, poll = poll, - card = null, + card = card, quote = quote, ) } diff --git a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java index 63ca59304..050d4b2ab 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java +++ b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java @@ -539,6 +539,7 @@ public abstract class AppDatabase extends RoomDatabase { @Override public void migrate(@NonNull SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE `TimelineStatusEntity` ADD COLUMN `quote` TEXT DEFAULT ''"); + database.execSQL("ALTER TABLE `TimelineStatusEntity` ADD COLUMN `card` TEXT"); } }; } diff --git a/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt b/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt index 9f304c909..8c4f5147c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/TimelineDao.kt @@ -36,7 +36,7 @@ SELECT s.serverId, s.url, s.timelineUserId, s.authorServerId, s.inReplyToId, s.inReplyToAccountId, s.createdAt, s.emojis, s.reblogsCount, s.favouritesCount, s.reblogged, s.favourited, s.bookmarked, s.sensitive, s.spoilerText, s.visibility, s.mentions, s.tags, s.application, s.reblogServerId,s.reblogAccountId, -s.content, s.attachments, s.poll, s.muted, s.expanded, s.contentShowing, s.contentCollapsed, s.pinned, +s.content, s.attachments, s.poll, s.card, s.muted, s.expanded, s.contentShowing, s.contentCollapsed, s.pinned, s.quote, a.serverId as 'a_serverId', a.timelineUserId as 'a_timelineUserId', a.localUsername as 'a_localUsername', a.username as 'a_username', diff --git a/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt b/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt index 39612a67c..942a5ce04 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/TimelineStatusEntity.kt @@ -79,6 +79,7 @@ data class TimelineStatusEntity( val contentCollapsed: Boolean, val contentShowing: Boolean, val pinned: Boolean, + val card: String?, val quote: String?, ) diff --git a/app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt index 0c2458208..0a79044ae 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/NetworkModule.kt @@ -24,6 +24,7 @@ import com.keylesspalace.tusky.BuildConfig import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.network.InstanceSwitchAuthInterceptor import com.keylesspalace.tusky.network.MastodonApi +import com.keylesspalace.tusky.network.MediaUploadApi import com.keylesspalace.tusky.network.NotestockApi import com.keylesspalace.tusky.util.getNonNullString import dagger.Module @@ -116,6 +117,20 @@ class NetworkModule { @Singleton fun providesApi(retrofit: Retrofit): MastodonApi = retrofit.create() + @Provides + @Singleton + fun providesMediaUploadApi(retrofit: Retrofit, okHttpClient: OkHttpClient): MediaUploadApi { + val longTimeOutOkHttpClient = okHttpClient.newBuilder() + .readTimeout(100, TimeUnit.SECONDS) + .writeTimeout(100, TimeUnit.SECONDS) + .build() + + return retrofit.newBuilder() + .client(longTimeOutOkHttpClient) + .build() + .create() + } + @Provides @Singleton fun providesNotestockApi( diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java index 1a1c880e3..e77b6de20 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/SFragment.java @@ -58,6 +58,7 @@ import com.keylesspalace.tusky.entity.Status; import com.keylesspalace.tusky.network.MastodonApi; import com.keylesspalace.tusky.network.TimelineCases; import com.keylesspalace.tusky.util.LinkHelper; +import com.keylesspalace.tusky.util.StatusParsingHelper; import com.keylesspalace.tusky.view.MuteAccountDialog; import com.keylesspalace.tusky.viewdata.AttachmentViewData; @@ -255,7 +256,7 @@ public abstract class SFragment extends Fragment implements Injectable { String stringToShare = statusToShare.getAccount().getUsername() + " - " + - statusToShare.getContent().toString(); + StatusParsingHelper.parseAsMastodonHtml(statusToShare.getContent()).toString(); sendIntent.putExtra(Intent.EXTRA_TEXT, stringToShare); sendIntent.putExtra(Intent.EXTRA_SUBJECT, statusUrl); sendIntent.setType("text/plain"); diff --git a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt index 25fe03d4d..7357293b5 100644 --- a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt +++ b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt @@ -142,13 +142,6 @@ interface MastodonApi { @POST("api/v1/notifications/clear") fun clearNotifications(): Single - @Multipart - @POST("api/v2/media") - suspend fun uploadMedia( - @Part file: MultipartBody.Part, - @Part description: MultipartBody.Part? = null - ): Result - @FormUrlEncoded @PUT("api/v1/media/{mediaId}") suspend fun updateMedia( diff --git a/app/src/main/java/com/keylesspalace/tusky/network/MediaUploadApi.kt b/app/src/main/java/com/keylesspalace/tusky/network/MediaUploadApi.kt new file mode 100644 index 000000000..c7e9633f6 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/network/MediaUploadApi.kt @@ -0,0 +1,19 @@ +package com.keylesspalace.tusky.network + +import com.keylesspalace.tusky.entity.MediaUploadResult +import okhttp3.MultipartBody +import retrofit2.http.Multipart +import retrofit2.http.POST +import retrofit2.http.Part + +/** endpoints defined in this interface will be called with a higher timeout than usual + * which is necessary for media uploads to succeed on some servers + */ +interface MediaUploadApi { + @Multipart + @POST("api/v2/media") + suspend fun uploadMedia( + @Part file: MultipartBody.Part, + @Part description: MultipartBody.Part? = null + ): Result +} diff --git a/app/src/main/res/layout/login_webview.xml b/app/src/main/res/layout/activity_login_webview.xml similarity index 88% rename from app/src/main/res/layout/login_webview.xml rename to app/src/main/res/layout/activity_login_webview.xml index 67d47d6f8..1630239ef 100644 --- a/app/src/main/res/layout/login_webview.xml +++ b/app/src/main/res/layout/activity_login_webview.xml @@ -11,8 +11,7 @@ + android:layout_height="wrap_content" /> diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index a6050df45..5ad736979 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -551,4 +551,10 @@ 180 يومًا 365 يومًا تحرير منشور + حسابات جديدة + لِج + قام %s بإنشاء حساب + أحدهم أنشأ حسابا جديدا + منشورات تم تعديلها + قام %s بتعديل منشوره \ No newline at end of file diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 8e3be5637..38c4dc962 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -20,8 +20,8 @@ Notificacions Local Federació - Toot - Posts + Fil + Publicacions Seguits Seguidors Preferits @@ -31,19 +31,19 @@ Edita el perfil Esborranys \@%s - %s tootejat + %s ha impulsat Contingut sensible Fes clic per a visualitzar-lo Mostra\'n més Mostra\'n menys No hi res aquí. Llisca avall per a actualitzar! - %s ha impulsat el teu toot - %s ha marcat com a preferit el teu toot + %s ha impulsat la teva publicació + %s ha marcat com a preferida la teva publicació %s et segueix Denuncia @%s Cap comentari addicional? Respon - Retooteja + Impulsa Preferit Més Escriure @@ -55,8 +55,8 @@ Deixa de blocar Denuncia Elimina - TOOT - TOOT! + PUBLICA + PUBLICA! Torna a intentar-ho Tanca Perfil @@ -83,8 +83,8 @@ Esborranys S\'està baixant %1$s Copia l\'enllaç - Comparteix l\'URL del toot a… - Comparteix el toot a… + Comparteix l\'URL de la publicació a… + Comparteix la publicació a… Enviat! Usuari desblocat Usuari sense silenciar @@ -132,10 +132,10 @@ Amaga el botó de redacció en desplaçament Filtre de la cronologia Pestanyes - Mostra els retoots + Mostra els impulsos Mostra les respostes Mostra les previsualitzacions - Privacitat predeterminada dels toots + Privacitat per defecte de les publicacions Publicació Pública Sense llistar @@ -145,7 +145,7 @@ Notificacions sobre mencions noves Seguidors nous Notificacions sobre nous seguidors - Retoots + Impulsos Notificacions si retootejents els teus toots Preferits Notificacions si marquen com a preferits els teus toots @@ -172,8 +172,8 @@ https://github.com/accelforce/Yuito/issues Perfil del Yuito - Comparteix el contingut del toot - Comparteix l\'enllaç al toot + Comparteix el contingut de la publicació + Comparteix l\'enllaç a la publicació Imatges Vídeo @@ -193,7 +193,7 @@ En resposta a @%s carrega\'n més Vota - S\'ha produït un error en enviar el tut. + S\'ha produït un error en publicar. Pestanyes Llicències Amplia @@ -209,11 +209,11 @@ Multimèdia amagada Amaga Estàs segur de tancar la sessió de %1$s\? - Amaga els retoots + Amaga els impulsos Mostra els impulsos Elimina i reecririu Obre el menú - Visibilitat del toot + Visibilitat de la publicació Contingut sensible Afegir una pestanya Enllaços @@ -227,17 +227,17 @@ Baixa el fitxer Compartir la imatge a … Enviat! - S\'ha enviat la petició de seguiment + Petició enviada Amb respostes Teclat d\'emojis Obrir el media #%d - Obrir com %s + Obre com a %s S\'està Descarregant media Resposta enviada correctament. Resposta … Revocar la petició de seguiment\? Vols eliminar aquest toot\? - Esborrar i reescriure aquest toot\? + Vols eliminar i reescriure aquesta publicació\? Finalització de les enquetes Tema Cronologia @@ -267,7 +267,7 @@ Eliminar Afegir un compte Obre l\'autor de l\'impuls - Mostra els retoots + Mostra els impulsos Notificacions d\'enquestes que han finalitzat Línia de temps públiques Actualització @@ -297,11 +297,11 @@ Protegir el compte S\'haurà d\'admetre els seguidors manualment Guardar l\'esborrany\? - Enviant toot… - Error enviant el toot - Enviant toots + S\'està publicant… + Error en publicar + S\'esatan enviant les publicacions Envio anul·lat - Una copia del toot s\'ha guardat a esborranys + S\'ha guardat una còpia de la publicació als esborranys Escriure La teva instància %s no te emojis personalitzats Estil dels emojis @@ -309,7 +309,7 @@ Hauràs de descarregar el joc d\'emojis Cercant… Expandir/ocultar tots els estats - Obrir toot + Obre la publicació Cal reiniciar l\'aplicació Has de reiniciar l\'aplicació per tal d\'aplicar aquests canvis Més tard @@ -360,7 +360,7 @@ Netejar Filtrar Aplicar - Escriure un toot + Escriure una publicació Escriure Mostra l\'indicador dels bots Vols netejar totes les notificacions permanentment\? @@ -374,8 +374,8 @@ L\'enquesta on has votat està tancada La enquesta que heu creat ha finalitzat Advertència: %s - Toot fixat - Toot no fixat + Fixat + No fixis Fixar Respost Accions per a la imatge %s @@ -385,7 +385,7 @@ Silenciar %s %s visible Amagar el domini sencer - Obrir sempre els toots marcats amb contingut sensible + Mostra sempre obertes les publicacions marcades amb avisos de contingut Paraula sencera Ventall actual d\'emojis de Google Enquesta amb opcions: %1$s, %2$s, %3$s, %4$s; %5$s @@ -418,12 +418,12 @@ Múltiples tries Tria %d Preferits - Toots programats + Publicacions programades Preferit Edita Preferits - Toots programats - Programar el toot + Publicacions programades + Programa la publicació Reiniciar Desenvolupat per Tusky S\'ha afegit a les adreces d\'interès @@ -449,7 +449,7 @@ Silenciar @%s\? Bloquejar @%s\? No silenciar la conversació - Conversació muda + Silencia la conversa %s ha sol·licitat seguir-te A baix A dalt @@ -487,15 +487,15 @@ Adjuncions Àudio Notificacions quan algú a qui esteu subscrit publica un tut nou - Tuts nous + Publicacions noves emojis personalitzats animats algú a qui estic subscrit acaba de publicar un tut nou %s acaba de fer una publicació Avisos - S\'ha esborrat el tut del qual en vau fer un esborrany de resposta + S\'ha eliminat la publicació a la qual vau fer un esborrany de resposta S\'ha eliminat l\'esborrany No s\'ha pogut carregar la informació de la resposta - No s\'ha pogut enviar aquest tut! + No s\'ha pogut publicar! Segur que voleu esborrar la llista %s\? No podeu pujar més de %1$d adjunts multimèdia. diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 699ba3789..21172ccea 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -25,7 +25,7 @@ Föderiert Direktnachrichten Tabs - Beitrag + Konversation Beiträge mit Antworten Angeheftet @@ -490,7 +490,7 @@ Für immer Anhänge Audio - Benachrichtigungen, wenn jemand, den ich abonniert habe, etwas Neues veröffentlicht + Benachrichtigungen, wenn jemand, den ich abonniert habe, eine neue Nachricht veröffentlicht Neue Beiträge GIF-Emojis animieren Jemand, den ich abonniert habe, hat etwas Neues veröffentlicht @@ -534,4 +534,7 @@ %s hat sich registriert Jemand hat sich registriert Benachrichtigungen, wenn Beiträge bearbeitet werden, mit denen du interagiert hast + Anmelden + Die Anmeldeseite konnte nicht geladen werden. + Beitragsbearbeitungen diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 4734fea97..85583a2b2 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -3,4 +3,106 @@ Αυτό δεν μπορεί να είναι κενό. Προέκυψε σφάλμα δικτύου! Παρακαλώ ελέγξτε τη σύνδεσή σας και προσπαθήστε ξανά! Προέκυψε ένα σφάλμα. + Αποκλεισμένοι χρήστες + Ακύρωση αιτήματος ακολούθησης; + Διαγραφή αυτής της συζήτησης; + Δεν υπάρχουν αποτελέσματα + Επεργασία προφίλ + Ακολουθεί + Επαναφορά + ο/η %s σας ακολούθησε + Χρήστες σε σίγαση + Αποσύνδεση + Μην ακολουθείτε + Άρση σίγασης του %s + Διαγραφή και αναδιατύπωση + Επεξεργασία προφίλ + Κοινοποίηση + Άδειες + Ανοίξτε σε browser + Αιτήματα ακολούθησης + Προσθήκη σελιδοδείκτη + Περισσότερα + Σελιδοδείκτες + Σελιδοδείκτες + Ακόλουθοι + Άρση αποκλεισμού + Αγαπημένα + Η δημοσίευση είναι πολύ μεγάλη! + Πληκτρολόγιο emoji + Είστε σίγουροι ότι θέλετε να αποσυνδεθείτε από τον λογαριασμό %1$s; + Προσχέδια + Αγαπημένα + Απάντηση… + Απόρριψη + Αποκλεισμένοι χρήστες + Αφαίρεση προώθησης + Επεξεργασία + Σίγαση του %s + Αποκλεισμός + Αναίρεση + ο/η %s ζήτησε να σας ακολουθήσει + Απάντηση + Καρτέλες + ο/η %s προώθησε τη δημοσίευσή σας + στον/στην %s άρεσε η δημοσίευσή σας + Ακολουθήστε + Αναφορά + Σίγαση + Τα μουσικά αρχεία πρέπει να είναι μικρότερα από 40MB. + Αφαίρεση αγαπημένου + Αναφορά του/της %s + Προτιμήσεις Λογαριασμού + Προσθήκη καρτέλας + Αντιγραφή συνδέσμου + Αναζήτηση… + Αποδοχή + Εμφάνιση προωθήσεων + Προφίλ + Αιτήματα ακολούθησης + Αναζήτηση + Διαγραφή συζήτησης + Διαγραφή + ο/η %s μόλις δημοσίευσε + Αποθήκευση + Γρήγορη Απάντηση + Χρήστες σε σίγαση + Το αρχείο πρέπει να είναι μικρότερο από 8MB. + Απόκρυψη προωθήσεων + Προτιμήσεις + Σύνδεση + Ανακοινώσεις + Προσχέδια + ο/η %s έκανε εγγραφή + Προσπαθήστε ξανά + Διαγραφή αυτής της δημοσίευσης; + Άρση σίγασης + Αγαπημένο + Σύνδεσμοι + Κλείσιμο + Ειδοποιήσεις + Γράψτε + Σύνδεση με Mastodon + Επεξεργασία + Προώθηση + Άρση ακολούθησης αυτού του λογαριασμού; + Απόκρυψη ειδοποιήσεων + Αφαίρεση σελιδοδείκτη + Προειδοποίηση περιεχομένου + Σύνδεσμοι + Σύνδεση… + Προγραμματισμένες δημοσιεύσεις + Προγραμματισμός δημοσίευσης + Προγραμματισμένες δημοσιεύσεις + Δημοσιεύσεις + Καρφιτσωμένο + Ευαίσθητο περιεχόμενο + Κρυμμένα μέσα + ο/η %s το προώθησε + Με απαντήσεις + Δείτε περισσότερα + Δείτε λιγότερα + Κλικ για να δείτε + ο/η %s επεξεργάστηκε τη δημοσίευσή του/της + Διαγραφή και αναδιατύπωση αυτής της δημοσίευσης; \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index dda7fc418..62e667601 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -547,4 +547,5 @@ un message avec lequel j\'ai interagi est modifié Messages modifiés Notifications quand un post avec lequel vous avez interagi est modifié + Se connecter diff --git a/app/src/main/res/values-gd/strings.xml b/app/src/main/res/values-gd/strings.xml index 10d64e0c1..e27b677aa 100644 --- a/app/src/main/res/values-gd/strings.xml +++ b/app/src/main/res/values-gd/strings.xml @@ -555,4 +555,5 @@ Brathan nuair a thèid postaichean a rinn thu conaltradh leotha a dheasachadh chaidh post a rinn mi conaltradh leis a deasachadh Clàraich a-steach + Cha b’ urrainn dhuinn duilleag a’ chlàraidh a-steach fhosgladh. \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 45d426f97..82604ae68 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -2,33 +2,33 @@ Si è verificato un errore. Si è verificato un errore di rete! Per favore controlla la tua connessione e riprova! - Questo non può esser vuoto. - Inserito un dominio non valido - Autenticazione fallita con quell\'istanza. - Non riesco a trovare un browser web da usare. + Questo non può essere vuoto. + Inserito dominio non valido + Autenticazione con quell\'istanza fallita. + Nessun browser web utilizzabile trovato. Si è verificato un errore di autenticazione non identificato. - L\'autorizzazione è stata negata. - Errore nell\'acquisizione del token di accesso. - Lo stato è troppo lungo! - La dimensione dei file immagine deve essere inferiore a 8 MB. - La dimensione dei file video deve essere inferiore a 40 MB. - Questo tipo di file non può essere caricato. - Questo file non può essere aperto. - Il permesso di lettura della scheda sd è richiesto. - È richiesta l\'autorizzazione di archiviazione. - Immagini e video non possono essere allegati allo stesso stato. - Il caricamento non è riuscito. - Errore nell\'invio del toot. + Autorizzazione negata. + Acquisizione token di accesso fallita. + Il post è troppo lungo! + Il file deve essere più piccolo di 8 MB. + I video devono essere più piccoli di 40 MB. + Quel tipo di file non può essere caricato. + Non è stato possibile aprire quel file. + È richiesto il permesso di leggere file. + È richiesto il permesso di salvare file. + Non è possibile allegare allo stesso post immagini e video. + Il caricamento è fallito. + Errore nell\'invio del post. Home Notifiche Locale Federata - Messaggi Diretti + Messaggi diretti Schede - Toot + Conversazione Post Con risposte - Fissati in alto + Fissati Seguiti Seguono Preferiti @@ -43,15 +43,15 @@ Contenuto sensibile Media nascosto Clicca per visualizzare - Mostra di Più - Mostra Meno + Mostra di più + Mostra di meno Espandi Riduci - Qui non c\'è niente. - Qui non c\'è niente. Trascina verso il basso per aggiornare! - %s ha boostato il tuo toot - %s ha messo il tuo toot nei preferiti - %s ti segue + Qui non c\'è nulla. + Qui non c\'è nulla. Trascina verso il basso per aggiornare! + %s ha boostato il tuo post + %s ha messo il tuo post nei preferiti + %s ti ha seguito Segnala @%s Commenti aggiuntivi? Risposta veloce @@ -79,7 +79,7 @@ Chiudi Profilo Preferenze - Preferenze Account + Preferenze account Preferiti Utenti silenziati Utenti bloccati @@ -102,14 +102,14 @@ Rifiuta Cerca Bozze - Visibilità dei toot - Avviso per il contenuto + Visibilità dei post + Avviso di contenuto sensibile Tastiera emoji - Aggiungi Scheda + Aggiungi scheda Collegamenti Menzioni Hashtag - Apri autore del boost + Vai all\'autore del boost Mostra boost Mostra preferiti Hashtag @@ -117,11 +117,11 @@ Collegamenti Apri media #%d Scaricando %1$s - Copia il link + Copia link Apri come %s Condividi come … - Condividi URL del toot su… - Condividi toot su… + Condividi URL del post su… + Condividi post su… Condividi media su… Inviato! Utente sbloccato @@ -132,7 +132,7 @@ Cosa succede? Avviso di contenuto sensibile Mostra nome - Bio + Biografia Cerca… Nessun risultato Rispondi… @@ -152,22 +152,22 @@ Scarica Revocare la richiesta di seguire? Smettere di seguire questo account? - Eliminare questo toot? + Eliminare questo post\? Pubblico: visibile sulla timeline pubblica - Non Elencato: non visibile sulla timeline pubblica e locale - Solo Follower: visibile solo dai tuoi follower + Non in elenco: non visibile sulla timeline pubblica e locale + Solo follower: visibile solo dai tuoi follower Diretto: visibile solo agli utenti menzionati - Modifica Notifiche + Notifiche Notifiche Allarmi Notifica con suoneria Notifica con vibrazione Notifica con luce Notificami quando - sono stato menzionato - sono stato seguito - i miei post sono boostati - i miei post sono messi nei preferiti + vengo menzionato + vengo seguito + i miei post vengono boostati + i miei post vengono messi nei preferiti Aspetto Tema dell\'app Timeline @@ -176,10 +176,10 @@ Chiaro Nero Automatico al tramonto - Usa Tema di Sistema + Usa tema di sistema Browser - Usa Tab Personalizzate di Chrome - Nascondi il pulsante componi mentre scorri + Usa Custom Tabs di Chrome + Nascondi il pulsante Componi mentre scorri Lingua Filtraggio della timeline Schede @@ -193,25 +193,25 @@ Porta proxy HTTP Privacy di default dei post Segna sempre media come contenuto sensibile - Pubblicando (sincronizzato con il server) + Pubblicazione (sincronizzato con il server) Sincronizzazione delle impostazioni fallita Pubblico - Non elencato - Solo per chi ti segue - Dimensione del testo degli stati + Non in elenco + Solo follower + Dimensione del testo dei post Piccolissimo Piccolo Normale Grande Grandissimo - Nuove Menzioni - Notifiche quando qualcuno ti menziona - Nuove persone che ti seguono - Notifiche su nuove persone che ti seguono + Nuove menzioni + Notifiche di quando vieni menzionato da qualcuno + Nuovi follower + Notifiche su nuovi follower Boost - Notifiche quando i tuoi toot vengono boostati + Notifiche sui tuoi post che vengono boostati Preferiti - Notifiche quando i tuoi toot vengono segnati come preferiti + Notifiche sui tuoi post che vengono segnati come preferiti %s ti ha menzionato %1$s, %2$s, %3$s e %4$d altri %1$s, %2$s e %3$s @@ -234,14 +234,14 @@ --> Sito web del progetto:\n https://accelf.net/yuito - Segnala problemi & richiedi funzionalità:\n - https://github.com/accelforce/Yuito/issues + Segnala problemi e richiedi funzionalità: +\n https://github.com/accelforce/Yuito/issues Profilo di Yuito - Condividi contenuto del toot - Condividi link al toot + Condividi contenuto del post + Condividi link al post Immagini Video - In attesa di approvazione + Richiesta inviata in %d a in %dg @@ -250,14 +250,14 @@ in %ds %da %dg - %d o - %d min - %d s - Seguono te - Mostra sempre tutto il contenuto sensibile + %do + %dmin + %ds + Ti segue + Mostra sempre tutti i contenuti sensibili Media Rispondendo a @%s - carica di più + carica altri Timeline pubbliche Conversazioni Aggiungi filtro @@ -265,7 +265,7 @@ Rimuovi Aggiorna Frase da filtrare - Aggiungi Account + Aggiungi account Aggiungi un nuovo Account Mastodon Liste Liste @@ -288,29 +288,29 @@ Inserisci descrizione Rimuovi Blocca account - Richiede la tua approvazione manuale di chi ti segue + Richiedi una tua approvazione manuale per seguirti Salvare bozza? - Inviando il Toot… + Inviando il post… Errore durante l\'invio - Invio Toot + Invio post Invio annullato - Una copia del toot è stata salvata nelle tue bozze + Una copia del post è stata salvata nelle tue bozze Componi La tua istanza %s non ha nessuna emoji personalizzata - Stile di emoji - Predefiniti del sistema + Stile delle emoji + Predefinite del sistema Dovrai prima scaricare questo pacchetto di emoji - Eseguendo una ricerca… - Espandi/Riduci tutti gli stati - Apri toot + Ricerca in corso… + Espandi/riduci tutti i post + Apri post Riavvio dell\'app richiesto Devi riavviare Yuito per applicare queste modifiche Più tardi Riavvia Le emoji predefinite del tuo dispositivo - Le emoji Blob conosciute da Android 4.4-7.1 + Le emoji Blob di Android 4.4-7.1 Le emoji standard di Mastodon - Scaricamento fallito + Download fallito Bot %1$s si è spostato su: Boost con la visibilità del post di origine @@ -323,63 +323,60 @@ aggiungi dati Etichetta Contenuto - Usa tempo assoluto + Usa ora assoluta Il profilo dell\'utente mostrato qui sotto potrebbe essere incompleto. Premi per aprire il profilo completo nel browser. - Non fissare + Smetti di fissare Fissa - %1$s Mi piace - %1$s Mi piace + %1$s Preferito + %1$s Preferiti <b>%s</b> Boost <b>%s</b> Boost Boostato da - Preferito da + Aggiunto ai preferiti da %1$s %1$s e %2$s %1$s, %2$s ed altri %3$d - limite massimo di %1$d tab raggiunto - limite massimo di %1$d tab raggiunto + limite massimo di %1$d scheda raggiunto + limite massimo di %1$d schede raggiunto - Media: %s - + Media: %s Contenuto sensibile: %s Nessuna descrizione Ribloggato - Apprezzato - + Messo nei preferiti Pubblico - Non elencato - - Seguaci + Non in elenco + Solo follower Diretti Nome della lista Scarica media Scaricando media - Componi Toot + Componi post Hashtag senza # Componi - Pulisci + Svuota Filtra Applica - Mostra indicatore per bot + Mostra indicatore bot Sei sicuro di voler permanentemente eliminare tutte le tue notifiche\? Cancella e riscrivi - Cancellare e riscrivere questo toot\? + Cancellare e riscrivere questo post\? %s voto %s voti - termina alle %s - terminato + si conclude alle %s + concluso Vota Domini nascosti Domini nascosti @@ -387,37 +384,37 @@ %s mostrati 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. Nascondi l\'intero dominio - Le votazioni sono finite - Mostra le animazioni delle GIF negli avatar + dei sondaggi si sono conclusi + Riproduci animazioni avatar Votazioni - Notifiche sulle votazioni che sono concluse + Notifiche sulle votazioni che si sono concluse Parola intera Quando la parola chiave o la frase sono composte da soli caratteri alfanumerici, sarà applicata solo se corrisponde alla parola completa - Insieme di emoji di Google + Set di emoji di Google Segnalibri Segnalibro Modifica Segnalibri Aggiungi sondaggio - Fatto con Tusky - Espandi sempre i toot segnalati come contenuto sensibile - Messo nei segalibri + Fatto usando Tusky + Espandi sempre i post segnalati come contenuto sensibile + Messo nei segnalibri Sondaggio con scelte: %1$s, %2$s, %3$s, %4$s; %5$s Scegli lista Lista Azioni per l\'immagine %s - Un sondaggio che hai votato è terminato - Un sondaggio che hai creato è terminato + Un sondaggio che hai votato si è concluso + Un sondaggio che hai creato si è concluso - %d giorno rimasti + %d giorno rimasto %d giorni rimasti - %d ora rimasti + %d ora rimasta %d ore rimasti - %d minuto rimasti + %d minuto rimasto %d minuti rimasti @@ -427,12 +424,12 @@ Continua Indietro Fatto - Inviato con successo @%s + Segnalato @%s con successo Altri commenti Inoltra a %s - Errore durante l\'invio - Errore durante lo scaricamento degli aggiornamenti - La segnalazione sarà inviata al moderatore del tuo server. Puoi spiegare perchè vuoi segnalare questo utente qui sotto: + Segnalazione fallita + Scaricamento dei post fallito + La segnalazione sarà inviata al moderatore del tuo server. Puoi spiegare perchè stai segnalando questo utente qui sotto: L\'utente è su un altro server. Mandare una copia della segnalazione anche lì\? Utenti Errore durante la ricerca @@ -450,10 +447,10 @@ Scelta %d Modifica Errore nella ricerca del post %s - Toot programmati - Toot programmati - Programma un toot - RIpristina + Post programmati + Post programmati + Programma un post + Ripristina %1$s • %2$s Non hai bozze. @@ -464,66 +461,66 @@ Aggiungi hashtag Silenziare @%s\? Bloccare @%s\? - Non silenziare più %s - Smetti di silenziare conversazione + Smetti di silenziare %s + Smetti di silenziare la conversazione Silenzia conversazione %s ha chiesto di seguirti - La dimensione dei file audio deve essere inferiore a 40 MB. + I file audio devono essere più piccoli di 40 MB. Smetti di silenziare %s Richieste di seguirti Salvato! La tua nota privata su questo account Nascondi il titolo della barra degli strumenti in alto - Mostra la finestra di dialogo di conferma prima del boosting + Mostra la finestra di conferma prima di boostare Mostra le anteprime dei collegamenti nelle timelines - Mastodon ha un intervallo minimo di programmazione di 5 minuti. + Mastodon ha un intervallo di programmazione minimo di 5 minuti. Non ci sono annunci. - Non hai stati pianificati. + Non hai post pianificati. Abilita il gesto di scorrimento per passare da una scheda all\'altra Notifiche sulle richieste di essere seguiti - Parte inferiore + In fondo In cima - Posizione di navigazione principale - Mostra sfumature colorate per i media nascosti + Posizione barra di navigazione principale + Mostra gradienti colorati per i media nascosti Nascondi notifiche Disattiva le notifiche da %s Riattiva le notifiche da %s Annunci - Richieste di seguirti + mi viene richiesto di seguirmi Nascondi statistiche quantitative sui profili Nascondi le statistiche quantitative sui post - Limita le notifiche della timeline - Revisiona le notifiche + Limita le notifiche dalla timeline + Rivedi le notifiche Benessere - Notifiche di quando qualcuno a cui sei iscritto ha pubblicato un nuovo toot - Nuovi toots - qualcuno a cui sono iscritto ha pubblicato un nuovo toot - %s appena pubblicato + Notifiche di nuovi post di qualcuno a cui sei iscritto + Nuovi post + qualcuno che seguo ha pubblicato un nuovo post + %s ha appena pubblicato Non puoi caricare più di %1$d allegato multimediale. Non puoi caricare più di %1$d allegati multimediali. - Il toot a cui hai scritto una risposta è stato rimosso - Bozza cancellata - L\'invio di questo toot è fallito! + Il post a cui hai scritto una risposta è stato rimosso + Bozza eliminata + L\'invio di questo post è fallito! Sei sicuro di voler cancellare la lista %s\? Indefinita Durata Allegati Audio - Mostra le animazioni delle emojis personalizzate + Riproduci emoji animate Iscriviti Rimuovere questa conversazione\? - Errore nel recuperare le informazioni sulla risposta + Errore nel recupero delle informazioni sulla risposta Disiscriviti - Rimuovi conversazione + Elimina conversazione Alcune informazioni che potrebbero influenzare il tuo benessere mentale saranno nascoste. Questo include: \n \n - Notifiche riguardo a Preferiti/Boost/Following -\n - Conteggio dei Preferiti/Boost nei toot -\n - Statistiche riguardo a Preferiti e Post nei profili +\n - Conteggio dei Preferiti/Boost nei post +\n - Statistiche riguardo a Preferiti/Post nei profili \n -\n Le notifiche push non saranno influenzate, ma puoi rivedere le tue impostazioni delle notifiche manualmente. +\n Le notifiche push non saranno influenzate, ma puoi modificare le tue impostazioni delle notifiche manualmente. Rimuovi segnalibro Chiedi conferma prima di boostare 14 giorni @@ -532,5 +529,16 @@ 90 giorni 180 giorni 365 giorni - Anche se il tuo account non è bloccato, lo staff di %1$s ha pensato che potresti voler controllare queste richieste di following da parte questi account manualmente. + Anche se il tuo account non è bloccato, lo staff di %1$s ha pensato che potresti voler verificare le richieste di seguirti da parte questi account manualmente. + %s si è registrato + qualcuno si è registrato + Login + %s ha modificato il suo post + un post con cui ho interagito è stato modificato + Componi post + Registrazioni + Notifiche di quando qualcuno si è registrato + Modifiche ai post + Notifiche di quando i post con cui hai interagito vengono modificati + Non è stato possibile caricare la pagina di login. diff --git a/app/src/main/res/values-no-rNB/strings.xml b/app/src/main/res/values-no-rNB/strings.xml index 53ab17742..4549fadaa 100644 --- a/app/src/main/res/values-no-rNB/strings.xml +++ b/app/src/main/res/values-no-rNB/strings.xml @@ -527,4 +527,5 @@ Redigerte innlegg Varslinger når et innlegg du har hatt en interaksjon med er redigert Innlogging + Klarte ikke å laste innloggingssiden. diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index e501ed2ac..6be06b0c2 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -10,20 +10,20 @@ A responder a @%s Editar a lista - Exige a aprovação manual de seguidores + Necessita de aprovar manualmente os seguidores Guardar rascunho\? Depois Desafixar %1$s e %2$s Bem-estar Escrever Toot - Deseja excluir a lista %s\? - Apesar do seu perfil não ser trancado, %1$s exige que você revise a solicitação para te seguir destes perfis manualmente. - Notificar - Cancelar + Pretende remover a lista %s\? + Apesar do seu perfil não ser privado, %1$s exige que você reveja manualmente as solicitações para te seguir destes perfis. + Subscrever + Remover subscrição Autorização negada. Erro ao adquirir token de login. - O toot é muito longo! + O toot é muito extenso! O ficheiro deve ter menor de 8MB. Os ficheiros de vídeo devem ter menor de 40MB. Os ficheiros de áudio devem ter menor de 40MB. @@ -48,13 +48,13 @@ Seguidores Favoritos Itens guardados - Utilizadors silenciados - Utilizadors bloqueados + Utilizadores silenciados + Utilizadores bloqueados Instâncias bloqueadas Seguidores Pendentes Conteúdo sensível Editar perfil - Conteúdo ocultado + Conteúdo multimédia ocultado Rascunhos Toque para ver Mostrar Mais @@ -67,7 +67,7 @@ \@%s %s fez boost Nada aqui. - Nada aqui. Arraste para baixo para atualizar! + Nada para ver aqui. Arraste para baixo para atualizar! %s fez boost ao seu toot %s adicionou o seu toot aos favoritos %s está a seguir-te @@ -77,7 +77,7 @@ %s editou um toot Denunciar @%s Comentários adicionais\? - Resposta rápida + Resposta Rápida Responder Fazer boost Desfazer boost @@ -89,7 +89,7 @@ Escrever Entrar com Mastodon Sair - Tem certeza de que deseja sair da conta %1$s\? + Tem a certeza que deseja sair da conta %1$s\? Seguir Deixar de seguir Bloquear @@ -106,12 +106,12 @@ Tentar novamente Fechar Perfil - Preferências - Preferências da Conta + Configurações + Configurações da Conta Favoritos - Guardados - utilizadors silenciados - utilizadors bloqueados + Itens Guardados + Utilizadores silenciados + Utilizadores bloqueados Instâncias bloqueadas Seguidores Pendentes Conteúdo multimédia @@ -138,13 +138,13 @@ Privacidade do toot Aviso de conteúdo Teclado de emojis - Agendar toot + Agendar Toot Redefinir Adicionar Separador Hiperligações Menções Hashtags - Ver quem fez boost + Ver autor do boost Mostrar boosts Mostrar favoritos Hashtags @@ -152,7 +152,7 @@ Hiperligações Abrir conteúdo multimédia #%d A descarregar %1$s - Copiar hiperligação + Copiar a hiperligação Abrir como %s Partilhar como… Descarregar conteúdo multimédia @@ -178,22 +178,22 @@ Cabeçalho O que é uma instância\? A ligar… - O endereço IP ou domínio de qualquer instância pode ser inserido aqui, como por exemplo mastodon.social, masto.donte.com.br, colorid.es ou qualquer outro! + O endereço IP ou domínio de qualquer instância pode ser inserido aqui, como por exemplo mastodon.social, masto.pt, pleroma.pt ou qualquer outro! \n -\n Se ainda não tem uma conta, insira o nome da instância onde pretende participar e crie uma conta lá. +\nSe ainda não tem uma conta, insira o nome da instância onde pretende participar e crie uma conta lá. \n -\n Uma instância é um lugar onde sua conta é hospedada, mas pode facilmente seguir e comunicar com pessoas de outras instâncias como se todos estivessem no mesmo site. +\nUma instância é o local onde sua conta é criada, mas pode facilmente seguir e comunicar com pessoas de outras instâncias como se estivessem todos no mesmo site. \n \nMais informações disponíveis em joinmastodon.org. - Envio de Conteúdo Multimédia Terminando + A Terminar Envio de Conteúdo Multimédia A enviar… Descarregar - Cancelar pedido para seguir\? + Cancelar o pedido para seguir\? Deixar de seguir esta conta\? Apagar este toot\? - Apagar e criar novo rascunho\? + Apagar e rescrever este toot\? Apagar esta conversa\? - Tem certeza que pretende bloquear a instância %s\? Deixará de poder ver quaisquer conteúdos dessa instância em qualquer timeline pública ou nas suas notificações. Os seus seguidores dessa instância serão removidos. + Tem a certeza que pretende bloquear a instância %s\? Deixará de poder ver quaisquer conteúdos dessa instância em qualquer timeline pública ou nas suas notificações. Os seus seguidores dessa instância serão removidos. Bloquear instância Bloquear @%s\? Silenciar @%s\? @@ -202,7 +202,7 @@ Não listado: Não publicar em timelines públicas Privado: Publicar apenas para os seguidores Direto: Publicar apenas para os utilizadores mencionados - Editar notificações + Notificações Notificações Alertas Notificar com som @@ -213,17 +213,17 @@ for seguido alguém para quem ativei os alertas publicar um toot novo fizerem pedido para me seguir - derem boosts nos meus toots + fizerem boosts aos meus toots adicionarem os meus toots aos favoritos votações terminarem alguém criar conta um toot com o qual interagi for editado Aparência - Temas + Tema da Aplicação Timelines Filtros - Noturno - Diurno + Escuro + Claro AMOLED Automático ao pôr-do-sol Usar o Tema do Sistema @@ -232,11 +232,11 @@ Esconder o botão de criação de toots ao fazer scroll Idioma Mostrar indicador para bots - Reproduzir avatares em GIFs + Reproduzir avatars em GIF Mostrar desfocagem em conteúdo multimédia sensível Animar emojis personalizados Filtro da timeline - Separadors + Separadores Mostrar boosts Mostrar respostas Mostrar pré-visualização de conteúdo multimédia @@ -268,23 +268,23 @@ Notificações para seguidores pendentes Boosts Votações - Notificações para votações que terminaram - Notificações quando alguém para quem ativei os alertas publicar um toot novo + Notificações para votações terminadas + Notificações quando alguém para quem ativou os alertas publicar um toot novo Notificações para novos utilizadores - Edições a toots + Edições de toots Notificações para boosts recebidos Favoritos - Notificações quando os teus toots são adicionados aos favoritos - Notificações quando toots com os quais interagi foram editados + Notificações quando os seus toots são adicionados aos favoritos + Notificações quando toots com os quais interagiu forem editados %s mencionou-te %1$s, %2$s, %3$s e %4$d outros %1$s, %2$s e %3$s - Perfil Bloqueado + Perfil Privado Sobre Tusky %s A correr o Tusky Atualizar - Tusky é um software livre e de código aberto e é ljcenciado com a versão 3 da GNU General Public License. Leia a licença aqui: https://www.gnu.org/licenses/gpl-3.0.pt-br.html + Tusky é um software livre e de código aberto, licenciado com a versão 3 da GNU General Public License. Pode ler a licença aqui: https://www.gnu.org/licenses/gpl-3.0.pt-br.html Página do projeto: \n https://tusky.app Reporte de erros e pedidos de funcionalidades: @@ -296,13 +296,13 @@ Vídeo Áudio Anexos - Pedido enviado + Pedido para seguir enviado em %dy em %dd em %dh em %dm em %ds - %da + %dy %dd %dh %dm @@ -325,7 +325,7 @@ Listas Não foi possível renomear a lista Listas - Lista da timeline + Cronologia da timeline Não foi possível criar a lista Não foi possível apagar a lista Criar uma lista @@ -338,10 +338,11 @@ Erro ao incluir descrição Descrição para deficientes visuais +\n(até %d letra) + Descrição para deficientes visuais \n(até %d caracteres) - - Descrever + Escrever descrição Remover Bloquear perfil A enviar o toot… @@ -351,34 +352,34 @@ Uma cópia do toot foi guardada nos seus rascunhos Escrever A sua instância, %s, não tem emojis personalizados - Estilo de emoji + Estilo dos emojis Padrão do sistema É necessário descarregar estes pacotes de emojis primeiro A fazer pesquisa… Expandir/Contrair todos os toots Abrir toot É necessário reiniciar a aplicação - É necessário reiniciar o aplicativo para aplicar as alterações + É necessário reiniciar o Tusky para aplicar as alterações Reiniciar Pacote de emojis padrão do seu dispositivo - Emojis padrão do Android da versão 4.4 até 7.1 + Emojis padrão do Android 4.4 até ao 7.1 Pacote de emojis padrão do Mastodon - Pacote de emojis atual do Google - Erro ao baixar + Pacote de emojis atuais da Google + Erro ao descarregar Robô %1$s mudou-se para: - Dar boost para o mesmo público + Dar boost para o público inicial Desfazer boost O Tusky contém código e recursos dos seguintes projetos de código aberto: - Licenciado sob a licença Apache (cópia separadorixo) + Licenciado sob a licença Apache (cópia abaixo) CC-BY 4.0 CC-BY-SA 4.0 Metadados do perfil - Adicionar + adicionar dados Rótulo Conteúdo - Usar tempo absoluto - As informações separadorixo podem refletir incompletamente o perfil do utilizador. Toque aqui para abrir o perfil completo no navegador. + Usar data absoluta + As informações abaixo podem refletir, de forma incompleta, o perfil do utilizador. Toque aqui para abrir o perfil completo no navegador. Fixar %1$s Favorito @@ -388,25 +389,25 @@ %s Boost %s Boosts - Levou boost de - Favoritado por + Boost dado por + Adicionado aos favoritos por %1$s - %1$s, %2$s e %3$d outros + %1$s, %2$s e %3$d mais - excedeu o máximo de %1$d separador - excedeu o máximo de %1$d separadors + atingiu o máximo de %1$d separador + atingiu o máximo de %1$d separadores - conteúdo multimédia: %s + Conteúdo multimédia: %s Aviso de Conteúdo: %s Sem descrição - Você fez boost - Favoritado - Salvo + Replicado + Adicionado aos favoritos + Guardado Público Não-listado Privado Direto - Enquete com as opções: %1$s, %2$s, %3$s, %4$s; %5$s + Votação com as opções: %1$s, %2$s, %3$s, %4$s; %5$s Nome da lista Adicionar hashtag Hashtag sem # @@ -414,11 +415,11 @@ Selecionar lista Lista Limpar - Filtro - Salvar - Compor toot - Compor - Tem certeza de que deseja limpar permanentemente todas as suas notificações\? + Filtrar + Aplicar + Escrever toot + Escrever + Tem certeza que pretende limpar permanentemente todas as suas notificações\? Opções para imagem %s %1$s • %2$s @@ -430,10 +431,10 @@ %s pessoas termina em %s - Terminou + terminada Votar - Uma enquete que você votou terminou - Sua enquete terminou + Uma votação em que votou terminou + A sua votação terminou %d dia restante %d dias restantes @@ -451,20 +452,20 @@ %d segundos restantes Continuar - Voltar - Ok + Retroceder + Feito \@%s denunciado com sucesso Comentários adicionais Encaminhar para %s Erro ao denunciar Erro ao carregar toots - A denúncia será enviada aos moderadores da instância. Explique por que denunciou a conta: - A conta está em outra instância. Enviar uma cópia anônima da denúncia para lá\? + A denúncia será enviada aos moderadores da instância. Pode adicionar abaixo uma explicação para a sua denúncia: + A conta está noutra instância. Quer enviar uma cópia anónima da denúncia para lá\? Contas Erro ao pesquisar - Mostrar filtro de notificações - Ativar deslizar para alternar entre separadors - Enquete + Mostrar Filtro das Notificações + Ativar gesto de deslizar para alternar entre separadores + Votação Duração Indefinido 5 minutos @@ -481,46 +482,46 @@ 180 dias 365 dias Adicionar opção - Múltiplas opções + Escolha múltipla Opção %d Editar - Erro ao pesquisar %s - Sem rascunhos. - Sem toots agendados. - Salvo! + Erro ao pesquisar toot %s + Não tem rascunhos. + Não tem toots agendados. + Guardado! Algumas informações que podem afetar seu bem-estar serão ocultadas. Isso inclui: \n \n- Notificações de favoritos, boosts e seguidores \n- Número de favoritos e boosts nos toots \n- Status de toots e seguidores nos perfis \n -\nNotificações push não serão afetadas, mas é possível revisar sua preferência manualmente. - Revisar notificações +\nNotificações push não serão afetadas, mas é possível rever as configurações das notificações manualmente. + Rever Notificações Limitar notificações da timeline - Sem comunicados. - Mastodon possui um intervalo mínimo de 5 minutos para agendar. - Mostrar prévias de Hiperligações nas linhas - Solicitar confirmação antes de dar boost - Solicitar confirmação antes de favoritar - Esconder o título da barra superior de tarefas - Nota pessoal sobre este perfil aqui - Esconder status dos toots - Esconder status dos perfis + Sem anúncios. + O Mastodon tem um intervalo mínimo de agendamento de 5 minutos. + Mostrar pré-visualização de hiperligações nas timelines + Mostrar janela de confirmação antes de dar boost + Mostrar janela de confirmação antes de adicionar aos favoritos + Esconder o título da barra superior + Nota pessoal sobre este perfil + Esconder estatísticas quantitativas nos toots + Esconder estatísticas quantitativas nos perfis - Não é possível anexar mais de %1$d arquivo de conteúdo multimédia. - Não é possível anexar mais de %1$d arquivos de conteúdo multimédia. + Não é possível enviar mais de %1$d arquivo de conteúdo multimédia. + Não é possível enviar mais de %1$d arquivos de conteúdo multimédia. Erro ao enviar o toot! - Erro ao carregar toot para responder - Rascunho excluído - O toot em que se rascunhou uma resposta foi excluído + Erro ao carregar informação de resposta + Rascunho apagado + O toot para o qual escreveu um rascunho foi apagado Ocorreu um erro. Ocorreu um erro de conetividade! Por favor, verifique a sua ligação e tente novamente! Isto não pode estar vazio. - Instância inválida inserida + A instância inserida é inválida Erro ao autenticar com esta instância. - Nao foi possível encontrar um navegador. - Ocorreu um erro não identificado de autorização. + Não foi possível encontrar um navegador. + Ocorreu um erro de autorização não identificado. Entrar Guardar Editar perfil @@ -528,4 +529,5 @@ Desfazer Aceitar Rejeitar + Não foi possível carregar a página de login \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 7d4b806ec..6c5100970 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -100,7 +100,7 @@ %s надсилає запит на підписку %s підписується на вас Тут нічого немає. Потягніть вниз, щоб оновити! - Тут нічого немає. + Тут порожньо. Згорнути Розгорнути Натисніть для перегляду @@ -549,4 +549,5 @@ Сповіщення, коли редагується повідомлення, з яким ви взаємодіяли Редакції допису Вхід + Не вдалося завантажити сторінку входу. \ No newline at end of file diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 1ec9ef803..0c8109db4 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -515,4 +515,6 @@ khi một tút mà tôi tương tác bị sửa Sửa tút Thông báo khi tút mà tôi tương tác bị sửa + Đăng nhập + Không thể tải trang đăng nhập. \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 84fbc05c2..301493aaf 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -535,4 +535,5 @@ 我进行过互动的嘟文被编辑了 嘟文编辑 当你进行过互动的嘟文被编辑时发出通知 + 无法加载登录页。 diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index 9be356c7e..1dffff614 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -57,6 +57,7 @@ Occitan Polski Português (Brasil) + Português (Portugal) Slovenščina Svenska Taqbaylit @@ -106,6 +107,7 @@ oc pl pt-BR + pt-PT sl sv kab diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0f32dd0af..17a56282d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -9,6 +9,7 @@ An unidentified authorization error occurred. Authorization was denied. Failed getting a login token. + Could not load the login page. The post is too long! The file must be less than 8MB. Video files must be less than 40MB. @@ -671,5 +672,6 @@ Unsubscribe Compose Post + Saving draft… diff --git a/app/src/test/java/com/keylesspalace/tusky/db/TimelineDaoTest.kt b/app/src/test/java/com/keylesspalace/tusky/db/TimelineDaoTest.kt index 889e5f989..ed652418a 100644 --- a/app/src/test/java/com/keylesspalace/tusky/db/TimelineDaoTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/db/TimelineDaoTest.kt @@ -369,13 +369,36 @@ class TimelineDaoTest { assertEquals("99", timelineDao.getTopPlaceholderId(1)) } + @Test + fun `preview card survives roundtrip`() = runBlocking { + val setOne = makeStatus(statusId = 3, cardUrl = "https://foo.bar") + + for ((status, author, reblogger) in listOf(setOne)) { + timelineDao.insertAccount(author) + reblogger?.let { + timelineDao.insertAccount(it) + } + timelineDao.insertStatus(status) + } + + val pagingSource = timelineDao.getStatuses(setOne.first.timelineUserId) + + val loadResult = pagingSource.load(PagingSource.LoadParams.Refresh(null, 2, false)) + + val loadedStatuses = (loadResult as PagingSource.LoadResult.Page).data + + assertEquals(1, loadedStatuses.size) + assertStatuses(listOf(setOne), loadedStatuses) + } + private fun makeStatus( accountId: Long = 1, statusId: Long = 10, reblog: Boolean = false, createdAt: Long = statusId, authorServerId: String = "20", - domain: String = "mastodon.example" + domain: String = "mastodon.example", + cardUrl: String? = null, ): Triple { val author = TimelineAccountEntity( serverId = authorServerId, @@ -403,6 +426,10 @@ class TimelineDaoTest { ) } else null + val card = when (cardUrl) { + null -> null + else -> "{ url: \"$cardUrl\" }" + } val even = accountId % 2 == 0L val status = TimelineStatusEntity( serverId = statusId.toString(), @@ -433,7 +460,8 @@ class TimelineDaoTest { expanded = false, contentCollapsed = false, contentShowing = true, - pinned = false + pinned = false, + card = card, ) return Triple(status, author, reblogAuthor) } diff --git a/fastlane/metadata/android/de/changelogs/89.txt b/fastlane/metadata/android/de/changelogs/89.txt new file mode 100644 index 000000000..cb92453b8 --- /dev/null +++ b/fastlane/metadata/android/de/changelogs/89.txt @@ -0,0 +1,7 @@ +Tusky v17.0 + +- "Öffnen als..." ist jetzt im Menü in Konto Profilen auch verfügbar, wenn mehrere Konten genutzt werden +- Die Anmeldung wird jetzt über die WebView innerhalb der App abgewickelt +- Unterstützung für Android 12 +- Unterstützung für die neue Mastodon instance configuration API +- und einige andere kleine Fehlerbehebungen und Verbesserungen diff --git a/fastlane/metadata/android/en-US/changelogs/91.txt b/fastlane/metadata/android/en-US/changelogs/91.txt new file mode 100644 index 000000000..e1d983039 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/91.txt @@ -0,0 +1,6 @@ +Tusky v18.0 + +- Support for new Mastodon 3.5 notification types +- The bot badge now looks better and adjusts to the selected theme +- Text can now be selected on the post detail view +- Fixed a lot of bugs, including one that prevented logins on Android 6 and lower diff --git a/fastlane/metadata/android/is/changelogs/89.txt b/fastlane/metadata/android/is/changelogs/89.txt new file mode 100644 index 000000000..e379fc723 --- /dev/null +++ b/fastlane/metadata/android/is/changelogs/89.txt @@ -0,0 +1,7 @@ +Tusky útg.17.0 + +- "Opna sem..." er núna líka á valmyndinni í notendasniðum þegar verið er að nota marga aðganga +- Innskráning er núna meðhöndluð í WebView innan forritsins +- Stuðningur við Android 12 +- Stuðningur við API-kerfisviðmót fyrir nýja uppsetningu Mastodon-tilvika +- og mökkur af smærri endurbótum og lagfæringum diff --git a/fastlane/metadata/android/pl/changelogs/89.txt b/fastlane/metadata/android/pl/changelogs/89.txt new file mode 100644 index 000000000..edfc6f0ab --- /dev/null +++ b/fastlane/metadata/android/pl/changelogs/89.txt @@ -0,0 +1,7 @@ +Tusky v17.0 + +- "Otwórz jako..." teraz jest także dostępne w menu na profilach kont gdy używane jest kilka kont +- Login teraz jest obsługiwany w WebView w aplikacji +- Wsparcie dla Androida 12 +- Wsparcie nowego API konfiguracji instancji Mastodon +- i wiele innych małych poprawek i ulepszeń diff --git a/fastlane/metadata/android/pt-PT/changelogs/58.txt b/fastlane/metadata/android/pt-PT/changelogs/58.txt new file mode 100644 index 000000000..24aad2f26 --- /dev/null +++ b/fastlane/metadata/android/pt-PT/changelogs/58.txt @@ -0,0 +1,12 @@ +Tusky v6.0 + +- Os filtros de timeline passaram para "Preferências da Conta" e sincronizam com servidor +- Pode ter uma hashtag personalizada como separador +- Suporte a edição de listas +- O editor sugere emojis personalizados ao escrever +- Nova configuração: "seguir tema do sistema" +- Melhor acessibilidade da timeline +- O Tusky ignora notificações desconhecidas, deixando de crashar +- Nova opção: trocar o idioma do sistema por outro +- Novas traduções +- Muitas outras melhorias e correções diff --git a/fastlane/metadata/android/pt-PT/changelogs/61.txt b/fastlane/metadata/android/pt-PT/changelogs/61.txt new file mode 100644 index 000000000..3cc7097e1 --- /dev/null +++ b/fastlane/metadata/android/pt-PT/changelogs/61.txt @@ -0,0 +1,7 @@ +Tusky v7.0 + +- Suporte para mostragem de votações, para votação e notificação de votações +- Botões novos para filtrar notificações e excluí-las +- Exclua e rascunhe os seus toots +- Novo indicador que mostra, na foto de perfil, se uma conta é um bot (pode ser desativado nas preferências) +- Novas traduções: Norueguês, Bokmål e Esloveno. diff --git a/fastlane/metadata/android/pt-PT/changelogs/67.txt b/fastlane/metadata/android/pt-PT/changelogs/67.txt new file mode 100644 index 000000000..5d3d38494 --- /dev/null +++ b/fastlane/metadata/android/pt-PT/changelogs/67.txt @@ -0,0 +1,9 @@ +Tusky v9.0 + +- Agora pode criar votações no Tusky +- Pesquisa melhorada +- Nova opção em "Preferências da Conta": "Expandir sempre os toots com Aviso de Conteúdo" +- Avatars em formato quadrado com cantos arredondados +- Agora é possível denunciar utilizadores, mesmo que não tenham toots +- O Tusky vai recusar a ligação através de ligações simples (não encriptadas) em Android 6+ +- Muitas outras pequenas melhorias e correções de bugs diff --git a/fastlane/metadata/android/pt-PT/changelogs/68.txt b/fastlane/metadata/android/pt-PT/changelogs/68.txt new file mode 100644 index 000000000..917921132 --- /dev/null +++ b/fastlane/metadata/android/pt-PT/changelogs/68.txt @@ -0,0 +1,3 @@ +Tusky v9.1 + +Esta atualização garante compatibilidade com Mastodon 3 e melhora a performance e estabilidade. diff --git a/fastlane/metadata/android/pt-PT/changelogs/70.txt b/fastlane/metadata/android/pt-PT/changelogs/70.txt new file mode 100644 index 000000000..6ac528b12 --- /dev/null +++ b/fastlane/metadata/android/pt-PT/changelogs/70.txt @@ -0,0 +1,8 @@ +Tusky v10.0 + +- Agora é possível adicionar toots aos favoritos e ver a lista de favoritos no Tusky. +- Já pode agendar toots, no entanto é necessário agendá-los para pelo menos 5 minutos depois do momento da escrita. +- Já pode adicionar listas na barra lateral do Tusky! +- Já pode partilhar ficheiros de som nos teus toots! + +E muitas outras pequenas melhorias e correções de bugs! diff --git a/fastlane/metadata/android/pt-PT/changelogs/72.txt b/fastlane/metadata/android/pt-PT/changelogs/72.txt new file mode 100644 index 000000000..f42b0a8e2 --- /dev/null +++ b/fastlane/metadata/android/pt-PT/changelogs/72.txt @@ -0,0 +1,11 @@ +Tusky v11.0 + +- Notificações de seguidores pendentes quando a conta está trancada! +- Novas funcionalidades nas "Preferências": + * desativação do gesto que alterna entre separadores + * diálogo de confirmação antes de dar boost + * mostragem da pré-visualização de links nas timelines +- As conversas agora podem ser silenciadas +- As votações passam a ser calculadas pelo número de votantes e não pelo número de votos +- Várias correções relacionadas com a escrita de toots + - Traduções melhoradas diff --git a/fastlane/metadata/android/pt-PT/changelogs/74.txt b/fastlane/metadata/android/pt-PT/changelogs/74.txt new file mode 100644 index 000000000..9595cb1f3 --- /dev/null +++ b/fastlane/metadata/android/pt-PT/changelogs/74.txt @@ -0,0 +1,8 @@ +Tusky v.12.0 + +- Interface principal melhorada - passa a ser possível mover os separadores para baixo! +- Ao silenciar um utilizador, pode também escolher se também pretende silenciar as notificações +- Agora dá para seguir quantas hashtags quiser num único separador! +- A exibição da descrição dos conteúdos multimédia foi melhorada para suportar descrições super longas + +Registo completo de alterações: https://github.com/tuskyapp/Tusky/releases diff --git a/fastlane/metadata/android/pt-PT/changelogs/77.txt b/fastlane/metadata/android/pt-PT/changelogs/77.txt new file mode 100644 index 000000000..01c57b339 --- /dev/null +++ b/fastlane/metadata/android/pt-PT/changelogs/77.txt @@ -0,0 +1,10 @@ +Tusky v13.0 + +- Suporte para anotações em perfis (novidade do Mastodon 3.2.0) +- Suporte para anúncios do(s) administrador(es) de instâncias (novidade do Mastodon 3.1.0) + +- O avatar da sua conta selecionada passa a ficar visível na barra de ferramentas principal (canto superior esquerdo) +- Tocar no nome de utilizador na timeline abrirá o perfil em questão + +- Várias pequenas melhorias e correções +- Traduções melhoradas diff --git a/fastlane/metadata/android/pt-PT/changelogs/80.txt b/fastlane/metadata/android/pt-PT/changelogs/80.txt new file mode 100644 index 000000000..866dc8e53 --- /dev/null +++ b/fastlane/metadata/android/pt-PT/changelogs/80.txt @@ -0,0 +1,7 @@ +Tusky v14.0 + +- Receba notificações quando um utilizador que segue publicar um toot - basta clicar no ícone do sino (novidade do Mastodon 3.3.0) +- O suporte para rascunhos do Tusky foi reescrito para ser mais rápido, simples e menos propenso a erros. +- Foi adicionado uma funcionalidade de bem-estar, que permite limitar algumas funcionalidades no Tusky. +- O Tusky já consegue animar os emojis personalizados +Registo completo de alterações: https://github.com/tuskyapp/Tusky/releases diff --git a/fastlane/metadata/android/pt-PT/changelogs/82.txt b/fastlane/metadata/android/pt-PT/changelogs/82.txt new file mode 100644 index 000000000..0ee9e8760 --- /dev/null +++ b/fastlane/metadata/android/pt-PT/changelogs/82.txt @@ -0,0 +1,5 @@ +Tusky v15.0 + +- O menu principal passa a mostrar uma opção para ver os utilizadores que pediram para o seguir! +- O relógio para agendar toots ganhou um aspeto mais consistente com o resto do Tusky +Registo completo de alterações: https://github.com/tuskyapp/Tusky/releases diff --git a/fastlane/metadata/android/pt-PT/changelogs/83.txt b/fastlane/metadata/android/pt-PT/changelogs/83.txt new file mode 100644 index 000000000..4c71e64d8 --- /dev/null +++ b/fastlane/metadata/android/pt-PT/changelogs/83.txt @@ -0,0 +1,3 @@ +Tusky v15.1 + +O Tusky já não crasha ao adicionar descrição às imagens diff --git a/fastlane/metadata/android/pt-PT/changelogs/87.txt b/fastlane/metadata/android/pt-PT/changelogs/87.txt new file mode 100644 index 000000000..79a811570 --- /dev/null +++ b/fastlane/metadata/android/pt-PT/changelogs/87.txt @@ -0,0 +1,8 @@ +Tusky v16.0 + +- O algoritmo de carregamento da timeline foi completamente reescrito para ser mais rápida, mais estável e mais fácil de manter. +- O Tusky passa a poder animar emojis personalizados no formato APNG & WebP Animated. +- Muitas correções de bugs +- Suporte para Android 11 +- Novas traduções: gaélico escocês, galego, ucraniano +- Traduções melhoradas diff --git a/fastlane/metadata/android/pt-PT/changelogs/89.txt b/fastlane/metadata/android/pt-PT/changelogs/89.txt new file mode 100644 index 000000000..28cebc1b3 --- /dev/null +++ b/fastlane/metadata/android/pt-PT/changelogs/89.txt @@ -0,0 +1,7 @@ +Tusky v17.0 + +- "Abrir como..." está disponível no menu de perfis de contas quando estão várias contas configuradas +- O login passa a ser feito numa WebView dentro da aplicação +- Suporte para Android 12 +- Suporte para a nova API de configuração de instâncias do Mastodon +- Várias pequenas melhorias e correções diff --git a/fastlane/metadata/android/pt-PT/full_description.txt b/fastlane/metadata/android/pt-PT/full_description.txt new file mode 100644 index 000000000..52d67d811 --- /dev/null +++ b/fastlane/metadata/android/pt-PT/full_description.txt @@ -0,0 +1,12 @@ +Tusky é um cliente leve para Mastodon, um servidor de rede social de código aberto e livre. + +• Design Material +• Maioria das APIs do Mastodon implementadas +• Suporte para várias contas +• Temas diurno e noturno, com possibilidade de troca automática de acordo com o horário +• Rascunhos - Escreva os seus toots e guarde-os para mais tarde +• Escolha entre estilos diferentes de emoji +• Otimizado para todos os tamanhos de ecrã +• Código totalmente aberto, sem dependências não-livres como Google Play Services + +Para ler mais sobre o Mastodon, visite o endereço https://joinmastodon.org/ diff --git a/fastlane/metadata/android/pt-PT/short_description.txt b/fastlane/metadata/android/pt-PT/short_description.txt new file mode 100644 index 000000000..38a439d85 --- /dev/null +++ b/fastlane/metadata/android/pt-PT/short_description.txt @@ -0,0 +1 @@ +Um cliente multi-contas para a rede social Mastodon diff --git a/fastlane/metadata/android/pt-PT/title.txt b/fastlane/metadata/android/pt-PT/title.txt new file mode 100644 index 000000000..0238ffc0a --- /dev/null +++ b/fastlane/metadata/android/pt-PT/title.txt @@ -0,0 +1 @@ +Tusky