Improve translating posts via Mastodon api (#4463)
This does 4 things: - Alt text is now translated when opening media of translated posts. Previously only the long-press alt text was translated. - The translate button is now hidden on non-public posts. The Mastodon api returns 403 there. - Translated posts will only be collapsible when the original was collapsible as well. It is just weird when an "show more" button suddenly appears because the post got longer by translating it. - The translation status and the untranslate button are now shown below each other instead of next to each other. Looks way better on smaller display or long texts. Before / After <img src="https://github.com/tuskyapp/Tusky/assets/10157047/2cadd15b-2e28-4989-9bd3-d3bdd4c75329" width="320"/> <img src="https://github.com/tuskyapp/Tusky/assets/10157047/0ecab094-6c96-49a5-bc99-aa56b7fe2ec2" width="320"/>
This commit is contained in:
parent
9805bc2cce
commit
32dea86502
|
@ -59,7 +59,15 @@ class AccountMediaRemoteMediator(
|
|||
}
|
||||
|
||||
val attachments = statuses.flatMap { status ->
|
||||
AttachmentViewData.list(status, activeAccount.alwaysShowSensitiveMedia)
|
||||
status.attachments.map { attachment ->
|
||||
AttachmentViewData(
|
||||
attachment = attachment,
|
||||
statusId = status.id,
|
||||
statusUrl = status.url.orEmpty(),
|
||||
sensitive = status.sensitive,
|
||||
isRevealed = activeAccount.alwaysShowSensitiveMedia || !status.sensitive
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (loadType == LoadType.REFRESH) {
|
||||
|
|
|
@ -319,7 +319,7 @@ class ConversationsFragment :
|
|||
adapter.peek(position)?.let { conversation ->
|
||||
viewMedia(
|
||||
attachmentIndex,
|
||||
AttachmentViewData.list(conversation.lastStatus.status),
|
||||
AttachmentViewData.list(conversation.lastStatus),
|
||||
view
|
||||
)
|
||||
}
|
||||
|
|
|
@ -397,7 +397,7 @@ class NotificationsFragment :
|
|||
}
|
||||
|
||||
override fun onViewMedia(position: Int, attachmentIndex: Int, view: View?) {
|
||||
val status = adapter.peek(position)?.asStatusOrNull()?.status ?: return
|
||||
val status = adapter.peek(position)?.asStatusOrNull() ?: return
|
||||
super.viewMedia(attachmentIndex, AttachmentViewData.list(status), view)
|
||||
}
|
||||
|
||||
|
|
|
@ -18,9 +18,10 @@ package com.keylesspalace.tusky.components.report.adapter
|
|||
import android.view.View
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.interfaces.LinkListener
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||
|
||||
interface AdapterHandler : LinkListener {
|
||||
fun showMedia(v: View?, status: Status?, idx: Int)
|
||||
fun showMedia(v: View?, status: StatusViewData.Concrete, idx: Int)
|
||||
fun setStatusChecked(status: Status, isChecked: Boolean)
|
||||
fun isStatusChecked(id: String): Boolean
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ class StatusViewHolder(
|
|||
private val previewListener = object : StatusViewHelper.MediaPreviewListener {
|
||||
override fun onViewMedia(v: View?, idx: Int) {
|
||||
viewdata()?.let { viewdata ->
|
||||
adapterHandler.showMedia(v, viewdata.status, idx)
|
||||
adapterHandler.showMedia(v, viewdata, idx)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ import com.keylesspalace.tusky.util.StatusDisplayOptions
|
|||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
|
@ -83,14 +84,13 @@ class ReportStatusesFragment :
|
|||
|
||||
private var snackbarErrorRetry: Snackbar? = null
|
||||
|
||||
override fun showMedia(v: View?, status: Status?, idx: Int) {
|
||||
status?.actionableStatus?.let { actionable ->
|
||||
when (actionable.attachments[idx].type) {
|
||||
override fun showMedia(v: View?, status: StatusViewData.Concrete, idx: Int) {
|
||||
when (status.attachments[idx].type) {
|
||||
Attachment.Type.GIFV, Attachment.Type.VIDEO, Attachment.Type.IMAGE, Attachment.Type.AUDIO -> {
|
||||
val attachments = AttachmentViewData.list(actionable)
|
||||
val attachments = AttachmentViewData.list(status)
|
||||
val intent = ViewMediaActivity.newIntent(context, attachments, idx)
|
||||
if (v != null) {
|
||||
val url = actionable.attachments[idx].url
|
||||
val url = status.attachments[idx].url
|
||||
ViewCompat.setTransitionName(v, url)
|
||||
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
|
||||
requireActivity(),
|
||||
|
@ -106,7 +106,6 @@ class ReportStatusesFragment :
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
|
||||
|
|
|
@ -182,17 +182,17 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
|||
}
|
||||
|
||||
override fun onViewMedia(position: Int, attachmentIndex: Int, view: View?) {
|
||||
searchAdapter.peek(position)?.status?.actionableStatus?.let { actionable ->
|
||||
when (actionable.attachments[attachmentIndex].type) {
|
||||
searchAdapter.peek(position)?.let { status ->
|
||||
when (status.attachments[attachmentIndex].type) {
|
||||
Attachment.Type.GIFV, Attachment.Type.VIDEO, Attachment.Type.IMAGE, Attachment.Type.AUDIO -> {
|
||||
val attachments = AttachmentViewData.list(actionable)
|
||||
val attachments = AttachmentViewData.list(status)
|
||||
val intent = ViewMediaActivity.newIntent(
|
||||
context,
|
||||
attachments,
|
||||
attachmentIndex
|
||||
)
|
||||
if (view != null) {
|
||||
val url = actionable.attachments[attachmentIndex].url
|
||||
val url = status.attachments[attachmentIndex].url
|
||||
ViewCompat.setTransitionName(view, url)
|
||||
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
|
||||
requireActivity(),
|
||||
|
@ -206,7 +206,7 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
|||
}
|
||||
|
||||
Attachment.Type.UNKNOWN -> {
|
||||
context?.openLink(actionable.attachments[attachmentIndex].url)
|
||||
context?.openLink(status.attachments[attachmentIndex].url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -534,7 +534,7 @@ class TimelineFragment :
|
|||
val status = adapter.peek(position)?.asStatusOrNull() ?: return
|
||||
super.viewMedia(
|
||||
attachmentIndex,
|
||||
AttachmentViewData.list(status.actionable),
|
||||
AttachmentViewData.list(status),
|
||||
view
|
||||
)
|
||||
}
|
||||
|
|
|
@ -381,7 +381,7 @@ class ViewThreadFragment :
|
|||
}
|
||||
|
||||
override fun onViewMedia(position: Int, attachmentIndex: Int, view: View?) {
|
||||
val status = adapter.currentList[position].status
|
||||
val status = adapter.currentList[position]
|
||||
super.viewMedia(
|
||||
attachmentIndex,
|
||||
list(status, alwaysShowSensitiveMedia),
|
||||
|
|
|
@ -249,11 +249,12 @@ abstract class SFragment : Fragment() {
|
|||
)
|
||||
}
|
||||
|
||||
// translation not there for your own posts
|
||||
// translation not there for your own posts, posts already in your language or non-public posts
|
||||
menu.findItem(R.id.status_translate)?.let { translateItem ->
|
||||
translateItem.isVisible = onMoreTranslate != null &&
|
||||
!status.language.equals(Locale.getDefault().language, ignoreCase = true) &&
|
||||
instanceInfoRepository.cachedInstanceInfoOrFallback.translationEnabled == true
|
||||
instanceInfoRepository.cachedInstanceInfoOrFallback.translationEnabled == true &&
|
||||
(status.visibility == Status.Visibility.PUBLIC || status.visibility == Status.Visibility.UNLISTED)
|
||||
translateItem.setTitle(if (translation != null) R.string.action_show_original else R.string.action_translate)
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ package com.keylesspalace.tusky.viewdata
|
|||
|
||||
import android.os.Parcelable
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
|
@ -36,17 +35,16 @@ data class AttachmentViewData(
|
|||
companion object {
|
||||
@JvmStatic
|
||||
fun list(
|
||||
status: Status,
|
||||
status: StatusViewData.Concrete,
|
||||
alwaysShowSensitiveMedia: Boolean = false
|
||||
): List<AttachmentViewData> {
|
||||
val actionable = status.actionableStatus
|
||||
return actionable.attachments.map { attachment ->
|
||||
return status.attachments.map { attachment ->
|
||||
AttachmentViewData(
|
||||
attachment = attachment,
|
||||
statusId = actionable.id,
|
||||
statusUrl = actionable.url!!,
|
||||
sensitive = actionable.sensitive,
|
||||
isRevealed = alwaysShowSensitiveMedia || !actionable.sensitive
|
||||
statusId = status.actionableId,
|
||||
statusUrl = status.actionable.url!!,
|
||||
sensitive = status.actionable.sensitive,
|
||||
isRevealed = alwaysShowSensitiveMedia || !status.actionable.sensitive
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,10 +82,12 @@ sealed class StatusViewData {
|
|||
/**
|
||||
* Specifies whether the content of this post is long enough to be automatically
|
||||
* collapsed or if it should show all content regardless.
|
||||
* Translated posts only show the button if the original post had it as well.
|
||||
*
|
||||
* @return Whether the post is collapsible or never collapsed.
|
||||
*/
|
||||
val isCollapsible: Boolean = shouldTrimStatus(this.content)
|
||||
val isCollapsible: Boolean = shouldTrimStatus(this.content) &&
|
||||
(translation == null || shouldTrimStatus(actionable.content.parseAsMastodonHtml()))
|
||||
|
||||
val actionable: Status
|
||||
get() = status.actionableStatus
|
||||
|
|
|
@ -103,46 +103,35 @@
|
|||
app:layout_constraintTop_toTopOf="@id/status_display_name"
|
||||
tools:text="13:37" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/status_button_untranslate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/action_show_original"
|
||||
style="@style/TuskyButton.TextButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/status_translation_status"
|
||||
app:layout_constraintBottom_toBottomOf="@id/status_translation_status"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:minHeight="0dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_translation_status"
|
||||
style="@style/TextSizeSmall"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextSizeSmall"
|
||||
tools:text="Translated from Lang by Service"
|
||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||
app:layout_constraintEnd_toStartOf="@id/status_button_untranslate"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_username"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:lineSpacingMultiplier="1.1"
|
||||
android:maxLines="4"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:minLines="2"
|
||||
android:lineSpacingMultiplier="1.1"
|
||||
android:layout_marginTop="4dp"
|
||||
android:gravity="center_vertical"
|
||||
app:layout_constraintBottom_toTopOf="@id/status_translation_barrier"
|
||||
/>
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_username"
|
||||
tools:text="Translated from Lang by Service"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/status_translation_barrier"
|
||||
android:layout_width="match_parent"
|
||||
<Button
|
||||
android:id="@+id/status_button_untranslate"
|
||||
style="@style/TuskyButton.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="status_translation_status, status_button_untranslate" />
|
||||
android:paddingHorizontal="0dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:minHeight="0dp"
|
||||
android:text="@string/action_show_original"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_translation_status"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.keylesspalace.tusky.view.ClickableSpanTextView
|
||||
android:id="@+id/status_content_warning_description"
|
||||
|
@ -157,7 +146,7 @@
|
|||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_translation_barrier"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_button_untranslate"
|
||||
tools:text="content warning which is very long and it doesn't fit"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
@ -359,13 +348,13 @@
|
|||
android:layout_marginStart="-14dp"
|
||||
android:contentDescription="@string/action_reply"
|
||||
android:importantForAccessibility="no"
|
||||
tools:ignore="NegativeMargin"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/status_inset"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_poll_description"
|
||||
app:srcCompat="@drawable/ic_reply_24dp" />
|
||||
app:srcCompat="@drawable/ic_reply_24dp"
|
||||
tools:ignore="NegativeMargin" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_replies"
|
||||
|
|
|
@ -78,45 +78,35 @@
|
|||
app:layout_constraintTop_toBottomOf="@id/status_display_name"
|
||||
tools:text="\@ConnyDuck\@mastodon.social" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/status_button_untranslate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/action_show_original"
|
||||
app:layout_constraintTop_toTopOf="@id/status_translation_status"
|
||||
app:layout_constraintBottom_toBottomOf="@id/status_translation_status"
|
||||
style="@style/TuskyButton.TextButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginEnd="14dp"
|
||||
android:minHeight="0dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_translation_status"
|
||||
style="@style/TextSizeSmall"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextSizeSmall"
|
||||
android:layout_marginStart="14dp"
|
||||
tools:text="Translated from blah using service"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="14dp"
|
||||
android:lineSpacingMultiplier="1.1"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_avatar"
|
||||
app:layout_constraintEnd_toStartOf="@id/status_button_untranslate"
|
||||
android:minLines="2"
|
||||
android:lineSpacingMultiplier="1.1"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginTop="4dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:layout_marginEnd="4dp"/>
|
||||
tools:text="Translated from blah using service"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/status_translation_barrier"
|
||||
android:layout_width="match_parent"
|
||||
<Button
|
||||
android:id="@+id/status_button_untranslate"
|
||||
style="@style/TuskyButton.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="status_translation_status, status_button_untranslate" />
|
||||
|
||||
android:layout_marginStart="14dp"
|
||||
android:layout_marginEnd="14dp"
|
||||
android:minHeight="0dp"
|
||||
android:paddingHorizontal="0dp"
|
||||
android:text="@string/action_show_original"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_translation_status"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.keylesspalace.tusky.view.ClickableSpanTextView
|
||||
android:id="@+id/status_content_warning_description"
|
||||
|
@ -133,7 +123,7 @@
|
|||
android:textSize="?attr/status_text_large"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_translation_barrier"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_button_untranslate"
|
||||
tools:text="CW this is a long long long long long long long long content warning" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
|
|
Loading…
Reference in New Issue