Machine translation of posts (#4307)
This commit is contained in:
parent
80982d061e
commit
fbb22799dc
38 changed files with 1912 additions and 180 deletions
|
|
@ -109,6 +109,7 @@ import at.connyduck.sparkbutton.helpers.Utils;
|
|||
import kotlin.Unit;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import kotlin.jvm.functions.Function2;
|
||||
import kotlinx.coroutines.Job;
|
||||
|
||||
public class NotificationsFragment extends SFragment implements
|
||||
|
|
@ -408,6 +409,12 @@ public class NotificationsFragment extends SFragment implements
|
|||
sendFetchNotificationsRequest(null, topId, FetchEnd.TOP, -1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Function2<Boolean, Integer, Unit> getOnMoreTranslate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReply(int position) {
|
||||
super.reply(notifications.get(position).asRight().getStatus());
|
||||
|
|
@ -490,7 +497,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
@Override
|
||||
public void onMore(@NonNull View view, int position) {
|
||||
Notification notification = notifications.get(position).asRight();
|
||||
super.more(notification.getStatus(), view, position);
|
||||
super.more(notification.getStatus(), view, position, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -525,10 +532,6 @@ public class NotificationsFragment extends SFragment implements
|
|||
updateViewDataAt(position, (vd) -> vd.copyWithShowingContent(isShowing));
|
||||
}
|
||||
|
||||
private void setPinForStatus(String statusId, boolean pinned) {
|
||||
updateStatus(statusId, status -> status.copyWithPinned(pinned));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadMore(int position) {
|
||||
// Check bounds before accessing list,
|
||||
|
|
@ -555,6 +558,11 @@ public class NotificationsFragment extends SFragment implements
|
|||
updateViewDataAt(position, (vd) -> vd.copyWithCollapsed(isCollapsed));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUntranslate(int position) {
|
||||
// not needed
|
||||
}
|
||||
|
||||
private void updateStatus(String statusId, Function<Status, Status> mapper) {
|
||||
int index = CollectionsKt.indexOfFirst(this.notifications, (s) -> s.isRight() &&
|
||||
s.asRight().getStatus() != null &&
|
||||
|
|
|
|||
|
|
@ -46,12 +46,14 @@ import com.keylesspalace.tusky.ViewMediaActivity.Companion.newIntent
|
|||
import com.keylesspalace.tusky.components.compose.ComposeActivity
|
||||
import com.keylesspalace.tusky.components.compose.ComposeActivity.Companion.startIntent
|
||||
import com.keylesspalace.tusky.components.compose.ComposeActivity.ComposeOptions
|
||||
import com.keylesspalace.tusky.components.instanceinfo.InstanceInfoRepository
|
||||
import com.keylesspalace.tusky.components.report.ReportActivity.Companion.getIntent
|
||||
import com.keylesspalace.tusky.db.AccountEntity
|
||||
import com.keylesspalace.tusky.db.AccountManager
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.entity.Translation
|
||||
import com.keylesspalace.tusky.interfaces.AccountSelectionListener
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.usecase.TimelineCases
|
||||
|
|
@ -60,6 +62,7 @@ import com.keylesspalace.tusky.util.parseAsMastodonHtml
|
|||
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
|
||||
import com.keylesspalace.tusky.view.showMuteAccountDialog
|
||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
|
@ -72,6 +75,10 @@ import kotlinx.coroutines.launch
|
|||
abstract class SFragment : Fragment(), Injectable {
|
||||
protected abstract fun removeItem(position: Int)
|
||||
protected abstract fun onReblog(reblog: Boolean, position: Int)
|
||||
|
||||
/** `null` if translation is not supported on this screen */
|
||||
protected abstract val onMoreTranslate: ((translate: Boolean, position: Int) -> Unit)?
|
||||
|
||||
private lateinit var bottomSheetActivity: BottomSheetActivity
|
||||
|
||||
@Inject
|
||||
|
|
@ -83,6 +90,9 @@ abstract class SFragment : Fragment(), Injectable {
|
|||
@Inject
|
||||
lateinit var timelineCases: TimelineCases
|
||||
|
||||
@Inject
|
||||
lateinit var instanceInfoRepository: InstanceInfoRepository
|
||||
|
||||
override fun startActivity(intent: Intent) {
|
||||
requireActivity().startActivityWithSlideInAnimation(intent)
|
||||
}
|
||||
|
|
@ -96,6 +106,13 @@ abstract class SFragment : Fragment(), Injectable {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
// make sure we have instance info for when we'll need it
|
||||
instanceInfoRepository.precache()
|
||||
}
|
||||
|
||||
protected fun openReblog(status: Status?) {
|
||||
if (status == null) return
|
||||
bottomSheetActivity.viewAccount(status.account.id)
|
||||
|
|
@ -140,7 +157,7 @@ abstract class SFragment : Fragment(), Injectable {
|
|||
requireActivity().startActivity(intent)
|
||||
}
|
||||
|
||||
protected fun more(status: Status, view: View, position: Int) {
|
||||
protected fun more(status: Status, view: View, position: Int, translation: Translation?) {
|
||||
val id = status.actionableId
|
||||
val accountId = status.actionableStatus.account.id
|
||||
val accountUsername = status.actionableStatus.account.username
|
||||
|
|
@ -167,16 +184,19 @@ abstract class SFragment : Fragment(), Injectable {
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
Status.Visibility.PRIVATE -> {
|
||||
val reblogged = status.reblog?.reblogged ?: status.reblogged
|
||||
menu.findItem(R.id.status_reblog_private).isVisible = !reblogged
|
||||
menu.findItem(R.id.status_unreblog_private).isVisible = reblogged
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
} else {
|
||||
popup.inflate(R.menu.status_more)
|
||||
popup.menu.findItem(R.id.status_download_media).isVisible = status.attachments.isNotEmpty()
|
||||
popup.menu.findItem(R.id.status_download_media).isVisible =
|
||||
status.attachments.isNotEmpty()
|
||||
}
|
||||
val menu = popup.menu
|
||||
val openAsItem = menu.findItem(R.id.status_open_as)
|
||||
|
|
@ -187,7 +207,8 @@ abstract class SFragment : Fragment(), Injectable {
|
|||
openAsItem.title = openAsText
|
||||
}
|
||||
val muteConversationItem = menu.findItem(R.id.status_mute_conversation)
|
||||
val mutable = statusIsByCurrentUser || accountIsInMentions(activeAccount, status.mentions)
|
||||
val mutable =
|
||||
statusIsByCurrentUser || accountIsInMentions(activeAccount, status.mentions)
|
||||
muteConversationItem.isVisible = mutable
|
||||
if (mutable) {
|
||||
muteConversationItem.setTitle(
|
||||
|
|
@ -198,6 +219,15 @@ abstract class SFragment : Fragment(), Injectable {
|
|||
}
|
||||
)
|
||||
}
|
||||
|
||||
// translation not there for your own 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
|
||||
translateItem.setTitle(if (translation != null) R.string.action_show_original else R.string.action_translate)
|
||||
}
|
||||
|
||||
popup.setOnMenuItemClickListener { item: MenuItem ->
|
||||
when (item.itemId) {
|
||||
R.id.post_share_content -> {
|
||||
|
|
@ -219,6 +249,7 @@ abstract class SFragment : Fragment(), Injectable {
|
|||
)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
R.id.post_share_link -> {
|
||||
val sendIntent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
|
|
@ -233,6 +264,7 @@ abstract class SFragment : Fragment(), Injectable {
|
|||
)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
R.id.status_copy_link -> {
|
||||
(
|
||||
requireActivity().getSystemService(
|
||||
|
|
@ -243,62 +275,80 @@ abstract class SFragment : Fragment(), Injectable {
|
|||
}
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
R.id.status_open_as -> {
|
||||
showOpenAsDialog(statusUrl, item.title)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
R.id.status_download_media -> {
|
||||
requestDownloadAllMedia(status)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
R.id.status_mute -> {
|
||||
onMute(accountId, accountUsername)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
R.id.status_block -> {
|
||||
onBlock(accountId, accountUsername)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
R.id.status_report -> {
|
||||
openReportPage(accountId, accountUsername, id)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
R.id.status_unreblog_private -> {
|
||||
onReblog(false, position)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
R.id.status_reblog_private -> {
|
||||
onReblog(true, position)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
R.id.status_delete -> {
|
||||
showConfirmDeleteDialog(id, position)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
R.id.status_delete_and_redraft -> {
|
||||
showConfirmEditDialog(id, position, status)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
R.id.status_edit -> {
|
||||
editStatus(id, status)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
R.id.pin -> {
|
||||
lifecycleScope.launch {
|
||||
timelineCases.pin(status.id, !status.isPinned()).onFailure { e: Throwable ->
|
||||
val message = e.message
|
||||
?: getString(if (status.isPinned()) R.string.failed_to_unpin else R.string.failed_to_pin)
|
||||
Snackbar.make(requireView(), message, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
timelineCases.pin(status.id, !status.isPinned())
|
||||
.onFailure { e: Throwable ->
|
||||
val message = e.message
|
||||
?: getString(if (status.isPinned()) R.string.failed_to_unpin else R.string.failed_to_pin)
|
||||
Snackbar.make(requireView(), message, Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
R.id.status_mute_conversation -> {
|
||||
lifecycleScope.launch {
|
||||
timelineCases.muteConversation(status.id, status.muted != true)
|
||||
}
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
R.id.status_translate -> {
|
||||
onMoreTranslate?.invoke(translation == null, position)
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
@ -346,6 +396,7 @@ abstract class SFragment : Fragment(), Injectable {
|
|||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
Attachment.Type.UNKNOWN -> {
|
||||
requireContext().openLink(attachment.url)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue