Skip to content

Commit

Permalink
Improve translating posts via Mastodon api (#4463)
Browse files Browse the repository at this point in the history
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"/>
  • Loading branch information
connyduck committed May 26, 2024
1 parent 9805bc2 commit 32dea86
Show file tree
Hide file tree
Showing 14 changed files with 97 additions and 109 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ class ConversationsFragment :
adapter.peek(position)?.let { conversation ->
viewMedia(
attachmentIndex,
AttachmentViewData.list(conversation.lastStatus.status),
AttachmentViewData.list(conversation.lastStatus),
view
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -83,28 +84,26 @@ 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) {
Attachment.Type.GIFV, Attachment.Type.VIDEO, Attachment.Type.IMAGE, Attachment.Type.AUDIO -> {
val attachments = AttachmentViewData.list(actionable)
val intent = ViewMediaActivity.newIntent(context, attachments, idx)
if (v != null) {
val url = actionable.attachments[idx].url
ViewCompat.setTransitionName(v, url)
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
requireActivity(),
v,
url
)
startActivity(intent, options.toBundle())
} else {
startActivity(intent)
}
}
Attachment.Type.UNKNOWN -> {
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(status)
val intent = ViewMediaActivity.newIntent(context, attachments, idx)
if (v != null) {
val url = status.attachments[idx].url
ViewCompat.setTransitionName(v, url)
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
requireActivity(),
v,
url
)
startActivity(intent, options.toBundle())
} else {
startActivity(intent)
}
}
Attachment.Type.UNKNOWN -> {
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -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)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ class TimelineFragment :
val status = adapter.peek(position)?.asStatusOrNull() ?: return
super.viewMedia(
attachmentIndex,
AttachmentViewData.list(status.actionable),
AttachmentViewData.list(status),
view
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
59 changes: 24 additions & 35 deletions app/src/main/res/layout/item_status.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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" />

Expand Down Expand Up @@ -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"
Expand Down
52 changes: 21 additions & 31 deletions app/src/main/res/layout/item_status_detailed.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_avatar"
app:layout_constraintEnd_toStartOf="@id/status_button_untranslate"
android:minLines="2"
android:layout_marginTop="8dp"
android:layout_marginEnd="14dp"
android:lineSpacingMultiplier="1.1"
android:gravity="center_vertical"
android:layout_marginTop="4dp"
android:visibility="gone"
tools:visibility="visible"
android:layout_marginEnd="4dp"/>
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_avatar"
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"
Expand All @@ -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
Expand Down

0 comments on commit 32dea86

Please sign in to comment.