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

Add mail translation #10453

Merged
merged 1 commit into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ The rating depends on the installed text processing backend. See [the rating ove

Learn more about the Nextcloud Ethical AI Rating [in our blog](https://nextcloud.com/blog/nextcloud-ethical-ai-rating/).
]]></description>
<version>4.2.0-alpha.1</version>
<version>4.2.0-alpha.2</version>
<licence>agpl</licence>
<author homepage="https://github.com/ChristophWurst">Christoph Wurst</author>
<author homepage="https://github.com/GretaD">GretaD</author>
Expand Down
5 changes: 5 additions & 0 deletions lib/Controller/PageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,11 @@ public function index(): TemplateResponse {
$this->aiIntegrationsService->isLlmProcessingEnabled() && $this->aiIntegrationsService->isLlmAvailable(SummaryTaskType::class)
);

$this->initialStateService->provideInitialState(
'llm_translation_enabled',
$this->aiIntegrationsService->isTaskAvailable('core:text2text:translate')
);

$this->initialStateService->provideInitialState(
'llm_freeprompt_available',
$this->aiIntegrationsService->isLlmProcessingEnabled() && $this->aiIntegrationsService->isLlmAvailable(FreePromptTaskType::class)
Expand Down
7 changes: 6 additions & 1 deletion lib/Service/AiIntegrations/AiIntegrationsService.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
$this->logger->info('No text summary provider available');
return;
}

$client = $this->clientFactory->getClient($account);
try {
foreach ($messages as $entry) {
Expand Down Expand Up @@ -356,6 +356,11 @@
return in_array($taskType, $manager->getAvailableTaskTypes(), true);
}

public function isTaskAvailable(string $taskName): bool {
$availableTasks = $this->taskProcessingManager->getAvailableTaskTypes();
return array_key_exists($taskName, $availableTasks);

Check warning on line 361 in lib/Service/AiIntegrations/AiIntegrationsService.php

View check run for this annotation

Codecov / codecov/patch

lib/Service/AiIntegrations/AiIntegrationsService.php#L359-L361

Added lines #L359 - L361 were not covered by tests
}

/**
* Whether the llm_processing admin setting is enabled globally on this instance.
*/
Expand Down
4 changes: 2 additions & 2 deletions src/components/Envelope.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
:class="{ 'important-one-line': oneLineLayout, 'icon-important': !oneLineLayout }"
:data-starred="isImportant ? 'true' : 'false'"
@click.prevent="hasWriteAcl ? onToggleImportant() : false"
v-html="importantSvg" />

Check warning on line 39 in src/components/Envelope.vue

View workflow job for this annotation

GitHub Actions / NPM lint

'v-html' directive can lead to XSS attack
<JunkIcon v-if="data.flags.$junk"
:size="18"
class="app-content-list-item-star junk-icon-style"
Expand Down Expand Up @@ -249,13 +249,13 @@
</template>
</NcActionInput>

<ActionButton :aria-label="t('spreed', 'Set custom snooze')"
<ActionButton :aria-label="t('mail', 'Set custom snooze')"
close-after-click
@click.stop="setCustomSnooze(customSnoozeDateTime)">
<template #icon>
<CheckIcon :size="16" />
</template>
{{ t('spreed', 'Set custom snooze') }}
{{ t('mail', 'Set custom snooze') }}
</ActionButton>
</template>
<template v-if="moreActionsOpen">
Expand Down Expand Up @@ -480,7 +480,7 @@

window.addEventListener('resize', this.onWindowResize)
},
computed: {

Check warning on line 483 in src/components/Envelope.vue

View workflow job for this annotation

GitHub Actions / NPM lint

The "computed" property should be above the "mounted" property on line 478
...mapGetters([
'isSnoozeDisabled',
]),
Expand Down
17 changes: 17 additions & 0 deletions src/components/MenuEnvelope.vue
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@
</template>
{{ t('mail', 'Unsnooze') }}
</ActionButton>
<ActionButton v-if="isTranslationEnabled ?? false"
:close-after-click="true"
@click.prevent="$emit('open-translation-modal')">
<template #icon>
<TranslationIcon :title="t('mail', 'Translate')"
:size="16" />
</template>
{{ t('mail', 'Translate') }}
</ActionButton>
<ActionButton :close-after-click="false"
@click="localMoreActionsOpen=true">
<template #icon>
Expand Down Expand Up @@ -214,6 +223,7 @@ import ChevronLeft from 'vue-material-design-icons/ChevronLeft.vue'
import DotsHorizontalIcon from 'vue-material-design-icons/DotsHorizontal.vue'
import DownloadIcon from 'vue-material-design-icons/Download.vue'
import PrinterIcon from 'vue-material-design-icons/Printer.vue'
import TranslationIcon from 'vue-material-design-icons/Translate.vue'
import { mailboxHasRights } from '../util/acl.js'
import { generateUrl } from '@nextcloud/router'
import InformationIcon from 'vue-material-design-icons/Information.vue'
Expand Down Expand Up @@ -247,6 +257,7 @@ export default {
ChevronLeft,
CheckIcon,
DotsHorizontalIcon,
TranslationIcon,
DownloadIcon,
InformationIcon,
OpenInNewIcon,
Expand Down Expand Up @@ -283,6 +294,11 @@ export default {
type: Boolean,
default: true,
},
isTranslationAvailable: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
Expand All @@ -296,6 +312,7 @@ export default {
computed: {
...mapGetters([
'isSnoozeDisabled',
'isTranslationEnabled',
]),
account() {
const accountId = this.envelope.accountId ?? this.mailbox.accountId
Expand Down
29 changes: 28 additions & 1 deletion src/components/ThreadEnvelope.vue
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@
@open-tag-modal="onOpenTagModal"
@open-move-modal="onOpenMoveModal"
@open-event-modal="onOpenEventModal"
@open-task-modal="onOpenTaskModal" />
@open-task-modal="onOpenTaskModal"
@open-translation-modal="onOpenTranslationModal" />
</NcActions>
<NcModal v-if="showSourceModal" class="source-modal" @close="onCloseSourceModal">
<div class="source-modal-content">
Expand All @@ -217,6 +218,10 @@
:account="account"
:envelopes="[envelope]"
@close="onCloseTagModal" />
<TranslationModal v-if="showTranslationModal"
:rich-parameters="{}"
:message="plainTextBody"
@close="onCloseTranslationModal" />
</template>
</div>
</div>
Expand Down Expand Up @@ -299,6 +304,7 @@ import TagModal from './TagModal.vue'
import MoveModal from './MoveModal.vue'
import TaskModal from './TaskModal.vue'
import EventModal from './EventModal.vue'
import TranslationModal from './TranslationModal.vue'
import axios from '@nextcloud/axios'
import { generateUrl } from '@nextcloud/router'
import { loadState } from '@nextcloud/initial-state'
Expand All @@ -307,6 +313,7 @@ import { mapStores } from 'pinia'
import moment from '@nextcloud/moment'
import { translateTagDisplayName } from '../util/tag.js'
import { FOLLOW_UP_TAG_LABEL } from '../store/constants.js'
import { Text, toPlain } from '../util/text.js'

// Ternary loading state
const LOADING_DONE = 0
Expand All @@ -321,6 +328,7 @@ export default {
TaskModal,
MoveModal,
TagModal,
TranslationModal,
ConfirmModal,
Avatar,
NcActionButton,
Expand Down Expand Up @@ -398,6 +406,8 @@ export default {
showEventModal: false,
showTaskModal: false,
showTagModal: false,
showTranslationModal: false,
plainTextBody: '',
rawMessage: '', // Will hold the raw source of the message when requested
isInternal: true,
enabledSmartReply: loadState('mail', 'llm_freeprompt_available', false),
Expand Down Expand Up @@ -863,6 +873,23 @@ export default {
onCloseTagModal() {
this.showTagModal = false
},
onOpenTranslationModal() {
try {
if (this.message.hasHtmlBody) {
let text = new Text('html', this.message.body)
text = toPlain(text)
this.plainTextBody = text.value
} else {
this.plainTextBody = this.message.body
}
this.showTranslationModal = true
} catch (error) {
showError(t('mail', 'Please wait for the message to load'))
}
},
onCloseTranslationModal() {
this.showTranslationModal = false
},
async onShowSourceModal() {
if (this.rawMessage.length === 0) {
const resp = await axios.get(
Expand Down
Loading
Loading