Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

translations via mastodon api #3228

Closed
wants to merge 11 commits into from
2 changes: 1 addition & 1 deletion app/schemas/com.keylesspalace.tusky.db.AppDatabase/47.json
Original file line number Diff line number Diff line change
Expand Up @@ -986,4 +986,4 @@
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '496e1f2135a296e49eef88551ecbdd2c')"
]
}
}
}
20 changes: 16 additions & 4 deletions app/schemas/com.keylesspalace.tusky.db.AppDatabase/53.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 53,
"identityHash": "233a8680f540e9a89950da21532ce85d",
"identityHash": "3d69c7c69750e7f6cf2c99ccda4fd323",
"entities": [
{
"tableName": "DraftEntity",
Expand Down Expand Up @@ -499,7 +499,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, `editedAt` INTEGER, `emojis` TEXT, `reblogsCount` INTEGER NOT NULL, `favouritesCount` INTEGER NOT NULL, `repliesCount` 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, `language` TEXT, `filtered` 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, `editedAt` INTEGER, `emojis` TEXT, `reblogsCount` INTEGER NOT NULL, `favouritesCount` INTEGER NOT NULL, `repliesCount` 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, `language` TEXT, `filtered` TEXT, `translationResult` TEXT, PRIMARY KEY(`serverId`, `timelineUserId`), FOREIGN KEY(`authorServerId`, `timelineUserId`) REFERENCES `TimelineAccountEntity`(`serverId`, `timelineUserId`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "serverId",
Expand Down Expand Up @@ -704,6 +704,12 @@
"columnName": "filtered",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "translationResult",
"columnName": "translationResult",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
Expand Down Expand Up @@ -812,7 +818,7 @@
},
{
"tableName": "ConversationEntity",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `id` TEXT NOT NULL, `order` INTEGER NOT NULL, `accounts` TEXT NOT NULL, `unread` INTEGER NOT NULL, `s_id` TEXT NOT NULL, `s_url` TEXT, `s_inReplyToId` TEXT, `s_inReplyToAccountId` TEXT, `s_account` TEXT NOT NULL, `s_content` TEXT NOT NULL, `s_createdAt` INTEGER NOT NULL, `s_editedAt` INTEGER, `s_emojis` TEXT NOT NULL, `s_favouritesCount` INTEGER NOT NULL, `s_repliesCount` INTEGER NOT NULL, `s_favourited` INTEGER NOT NULL, `s_bookmarked` INTEGER NOT NULL, `s_sensitive` INTEGER NOT NULL, `s_spoilerText` TEXT NOT NULL, `s_attachments` TEXT NOT NULL, `s_mentions` TEXT NOT NULL, `s_tags` TEXT, `s_showingHiddenContent` INTEGER NOT NULL, `s_expanded` INTEGER NOT NULL, `s_collapsed` INTEGER NOT NULL, `s_muted` INTEGER NOT NULL, `s_poll` TEXT, `s_language` TEXT, PRIMARY KEY(`id`, `accountId`))",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `id` TEXT NOT NULL, `order` INTEGER NOT NULL, `accounts` TEXT NOT NULL, `unread` INTEGER NOT NULL, `s_id` TEXT NOT NULL, `s_url` TEXT, `s_inReplyToId` TEXT, `s_inReplyToAccountId` TEXT, `s_account` TEXT NOT NULL, `s_content` TEXT NOT NULL, `s_createdAt` INTEGER NOT NULL, `s_editedAt` INTEGER, `s_emojis` TEXT NOT NULL, `s_favouritesCount` INTEGER NOT NULL, `s_repliesCount` INTEGER NOT NULL, `s_favourited` INTEGER NOT NULL, `s_bookmarked` INTEGER NOT NULL, `s_sensitive` INTEGER NOT NULL, `s_spoilerText` TEXT NOT NULL, `s_attachments` TEXT NOT NULL, `s_mentions` TEXT NOT NULL, `s_tags` TEXT, `s_showingHiddenContent` INTEGER NOT NULL, `s_expanded` INTEGER NOT NULL, `s_collapsed` INTEGER NOT NULL, `s_muted` INTEGER NOT NULL, `s_poll` TEXT, `s_language` TEXT, `s_translationResult` TEXT, PRIMARY KEY(`id`, `accountId`))",
"fields": [
{
"fieldPath": "accountId",
Expand Down Expand Up @@ -987,6 +993,12 @@
"columnName": "s_language",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "lastStatus.translationResult",
"columnName": "s_translationResult",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
Expand All @@ -1003,7 +1015,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, '233a8680f540e9a89950da21532ce85d')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3d69c7c69750e7f6cf2c99ccda4fd323')"
]
}
}
2 changes: 2 additions & 0 deletions app/src/main/java/com/keylesspalace/tusky/appstore/Events.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.keylesspalace.tusky.TabData
import com.keylesspalace.tusky.entity.Account
import com.keylesspalace.tusky.entity.Poll
import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.entity.TranslationResult

data class FavoriteEvent(val statusId: String, val favourite: Boolean) : Event
data class ReblogEvent(val statusId: String, val reblog: Boolean) : Event
Expand All @@ -23,3 +24,4 @@ data class PollVoteEvent(val statusId: String, val poll: Poll) : Event
data class DomainMuteEvent(val instance: String) : Event
data class AnnouncementReadEvent(val announcementId: String) : Event
data class PinEvent(val statusId: String, val pinned: Boolean) : Event
data class TranslationEvent(val statusId: String, val translation: TranslationResult?) : Event
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.keylesspalace.tusky.entity.HashTag
import com.keylesspalace.tusky.entity.Poll
import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.entity.TimelineAccount
import com.keylesspalace.tusky.entity.TranslationResult
import com.keylesspalace.tusky.viewdata.StatusViewData
import java.util.Date

Expand Down Expand Up @@ -97,7 +98,8 @@ data class ConversationStatusEntity(
val collapsed: Boolean,
val muted: Boolean,
val poll: Poll?,
val language: String?
val language: String?,
val translationResult: TranslationResult?,
) {

fun toViewData(): StatusViewData.Concrete {
Expand Down Expand Up @@ -131,11 +133,12 @@ data class ConversationStatusEntity(
poll = poll,
card = null,
language = language,
filtered = null
filtered = null,
),
isExpanded = expanded,
isShowingContent = showingHiddenContent,
isCollapsed = collapsed
isCollapsed = collapsed,
translationResult = translationResult,
)
}
}
Expand Down Expand Up @@ -179,7 +182,8 @@ fun Status.toEntity(
collapsed = contentCollapsed,
muted = muted ?: false,
poll = poll,
language = language
language = language,
translationResult = null,
)

fun Conversation.toEntity(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.keylesspalace.tusky.components.conversation

import com.keylesspalace.tusky.entity.Poll
import com.keylesspalace.tusky.entity.TranslationResult
import com.keylesspalace.tusky.viewdata.StatusViewData

data class ConversationViewData(
Expand All @@ -33,7 +34,8 @@ data class ConversationViewData(
poll: Poll? = lastStatus.status.poll,
expanded: Boolean = lastStatus.isExpanded,
collapsed: Boolean = lastStatus.isCollapsed,
showingHiddenContent: Boolean = lastStatus.isShowingContent
showingHiddenContent: Boolean = lastStatus.isShowingContent,
translationResult: TranslationResult? = lastStatus.translationResult,
): ConversationEntity {
return ConversationEntity(
accountId = accountId,
Expand All @@ -48,8 +50,9 @@ data class ConversationViewData(
poll = poll,
expanded = expanded,
collapsed = collapsed,
showingHiddenContent = showingHiddenContent
)
showingHiddenContent = showingHiddenContent,
translation = translationResult,
),
)
}
}
Expand All @@ -61,7 +64,8 @@ fun StatusViewData.Concrete.toConversationStatusEntity(
poll: Poll? = status.poll,
expanded: Boolean = isExpanded,
collapsed: Boolean = isCollapsed,
showingHiddenContent: Boolean = isShowingContent
showingHiddenContent: Boolean = isShowingContent,
translation: TranslationResult? = translationResult,
): ConversationStatusEntity {
return ConversationStatusEntity(
id = id,
Expand All @@ -87,6 +91,7 @@ fun StatusViewData.Concrete.toConversationStatusEntity(
collapsed = collapsed,
muted = muted,
poll = poll,
language = status.language
language = status.language,
translationResult = translation,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,12 @@ class ConversationsFragment :
}
}

override fun onTranslate(translate: Boolean, position: Int) {
adapter.peek(position)?.let { conversation ->
viewModel.translate(translate, conversation)
}
}

override fun onMore(view: View, position: Int) {
adapter.peek(position)?.let { conversation ->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,25 @@ class ConversationsViewModel @Inject constructor(
}
}

fun translate(translate: Boolean, conversation: ConversationViewData) {
viewModelScope.launch {
try {
val translationResult = timelineCases.translate(
conversation.lastStatus.actionableId, translate
)

val newConversation = conversation.toEntity(
accountId = accountManager.activeAccount!!.id,
translationResult = translationResult,
)

saveConversationToDb(newConversation)
} catch (e: Exception) {
Log.w(TAG, "failed to bookmark status", e)
}
}
}

fun voteInPoll(choices: List<Int>, conversation: ConversationViewData) {
viewModelScope.launch {
timelineCases.voteInPoll(conversation.lastStatus.id, conversation.lastStatus.status.poll?.id!!, choices)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,13 @@ class SearchViewModel @Inject constructor(
}
}

fun translate(statusViewData: StatusViewData.Concrete, translate: Boolean) {
viewModelScope.launch {
val translation = timelineCases.translate(statusViewData.actionableId, translate)
updateStatusViewData(statusViewData.copyWithTranslationResult(translation))
}
}

fun muteAccount(accountId: String, notifications: Boolean, duration: Int?) {
viewModelScope.launch {
timelineCases.mute(accountId, notifications, duration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
}
}

override fun onTranslate(translate: Boolean, position: Int) {
searchAdapter.peek(position)?.let { status ->
viewModel.translate(status, translate)
}
}

override fun onMore(view: View, position: Int) {
searchAdapter.peek(position)?.status?.let {
more(it, view, position)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,11 @@ class TimelineFragment :
viewModel.bookmark(bookmark, status)
}

override fun onTranslate(translate: Boolean, position: Int) {
val status = adapter.peek(position)?.asStatusOrNull() ?: return
viewModel.translate(translate, status)
}

override fun onVoteInPoll(position: Int, choices: List<Int>) {
val status = adapter.peek(position)?.asStatusOrNull() ?: return
viewModel.voteInPoll(choices, status)
Expand All @@ -434,7 +439,7 @@ class TimelineFragment :

override fun onMore(view: View, position: Int) {
val status = adapter.peek(position)?.asStatusOrNull() ?: return
super.more(status.status, view, position)
super.more(status.status, view, position, status.translationResult != null)
}

override fun onOpenReblog(position: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.keylesspalace.tusky.entity.HashTag
import com.keylesspalace.tusky.entity.Poll
import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.entity.TimelineAccount
import com.keylesspalace.tusky.entity.TranslationResult
import com.keylesspalace.tusky.viewdata.StatusViewData
import java.util.Date

Expand Down Expand Up @@ -106,7 +107,8 @@ fun Placeholder.toEntity(timelineUserId: Long): TimelineStatusEntity {
card = null,
repliesCount = 0,
language = null,
filtered = null
filtered = null,
translationResult = null,
)
}

Expand All @@ -115,7 +117,8 @@ fun Status.toEntity(
gson: Gson,
expanded: Boolean,
contentShowing: Boolean,
contentCollapsed: Boolean
contentCollapsed: Boolean,
translationResult: TranslationResult?,
): TimelineStatusEntity {
return TimelineStatusEntity(
serverId = this.id,
Expand Down Expand Up @@ -151,7 +154,8 @@ fun Status.toEntity(
card = actionableStatus.card?.let(gson::toJson),
repliesCount = actionableStatus.repliesCount,
language = actionableStatus.language,
filtered = actionableStatus.filtered
filtered = actionableStatus.filtered,
translationResult = translationResult,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ class CachedTimelineRemoteMediator(
gson = gson,
expanded = expanded,
contentShowing = contentShowing,
contentCollapsed = contentCollapsed
contentCollapsed = contentCollapsed,
translationResult = null,
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ import javax.inject.Inject
* TimelineViewModel that caches all statuses in a local database
*/
class CachedTimelineViewModel @Inject constructor(
timelineCases: TimelineCases,
val timelineCases: TimelineCases,
private val api: MastodonApi,
eventHub: EventHub,
accountManager: AccountManager,
Expand Down Expand Up @@ -215,7 +215,8 @@ class CachedTimelineViewModel @Inject constructor(
gson = gson,
expanded = activeAccount.alwaysOpenSpoiler,
contentShowing = activeAccount.alwaysShowSensitiveMedia || !status.actionableStatus.sensitive,
contentCollapsed = true
contentCollapsed = true,
translationResult = null,
)
)
}
Expand Down Expand Up @@ -269,6 +270,13 @@ class CachedTimelineViewModel @Inject constructor(
// handled by CacheUpdater
}

override fun updateStatusById(
id: String,
updater: (StatusViewData.Concrete) -> StatusViewData.Concrete
) {

}

override fun fullReload() {
viewModelScope.launch {
val activeAccount = accountManager.activeAccount!!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ class NetworkTimelineViewModel @Inject constructor(
currentSource?.invalidate()
}

private inline fun updateStatusById(
override fun updateStatusById(
id: String,
updater: (StatusViewData.Concrete) -> StatusViewData.Concrete
) {
Expand Down
Loading