Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
8 changes: 4 additions & 4 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,12 @@ A channel's outgoing language, set from the panel and persisted alongside the re
_Avoid_: send language, outgoing switch

**the strip**:
The `.translate-bar` above the input (`ChatInput.vue`) that shows a draft's outgoing translation: the chip, the streaming text, Check/Send/Edit, and the read-back row underneath. A different draft, a channel switch, Escape or the strip's own Edit takes it down.
The `.translate-bar` above the input (`ChatInput.vue`) that shows a draft's outgoing translation while it is typed (the live preview): the "Will send in …" label, the streaming text, Copy, and the read-back row underneath. A changed draft dims it until the next preview; a send, a channel switch or Escape takes it down.
_Avoid_: translation bar, preview box

**the first Enter / the second Enter**:
The first Enter on a draft with a write target starts its translation (`translateOutgoing`) instead of sending; the second Enter, once the strip shows a result (or a failure), sends what the strip holds.
_Avoid_: initial submit, confirm submit
**the one-step send**:
Return on a draft with a write target sends what the strip holds; before the preview is done it waits for it and sends then (`sendQueued` in `ChatInput.vue`). After a failure the next Return sends the draft as written. (It replaced the first Enter / second Enter pair.)
_Avoid_: initial submit, confirm submit, second Enter

**read back**:
The round trip: a done translation translated once more, back toward the user's reading language, shown under the strip so a failed or a subtly wrong translation can be caught before sending. `"auto"` runs it as soon as the translation ends; `"button"` waits for Check.
Expand Down
110 changes: 1 addition & 109 deletions client/components/Chat.vue
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,6 @@
:aria-label="connectingLabel"
/>
</span>
<span
v-if="translationAvailable"
class="translate-tooltip tooltipped tooltipped-w tooltipped-no-touch"
:data-tooltip="translateLabel"
>
<button
class="translate"
:class="{on: channelReads, unavailable: translationBlocked}"
:aria-label="translateLabel"
:aria-pressed="touch ? undefined : channelReads"
:aria-expanded="translationPanelOpen"
@click="onTranslateClick"
@contextmenu.prevent="openTranslationPanel"
/>
</span>
<button class="mentions" :aria-label="mentionsLabel" @click="openMentions">
<span v-if="mentionCount" class="mentions-badge" aria-hidden="true">{{
mentionBadge
Expand Down Expand Up @@ -194,14 +179,10 @@ import {
channelTranslation,
readTopic,
readingLanguage,
setReading,
showOriginal,
translationAvailable as translationAvailableNow,
currentTranslationBlock,
} from "../js/translate/reader";
import {topicEntryId} from "../js/translate/topic";
import {languageName} from "../js/translate/languages";
import {hasVirtualKeyboard} from "../js/helpers/device";

export default defineComponent({
name: "Chat",
Expand Down Expand Up @@ -423,95 +404,12 @@ export default defineComponent({
};

const translationPanelOpen = ref(false);
const translationState = computed(() => channelTranslation(props.network, props.channel));
const translationAvailable = computed(
() =>
(props.channel.type === ChanType.CHANNEL ||
props.channel.type === ChanType.QUERY) &&
translationAvailableNow()
);
// The light means reading; a write target shows in the tooltip.
const channelReads = computed(() => translationState.value.read);
// A touch device has no right-click, so the tap is what opens the
// panel there and the button says so; the click keeps toggling
// reading where there is a pointer.
const touch = hasVirtualKeyboard();
// Nothing here can translate (router.ts `translationBlock`): the button
// stays, says so, and opens the panel, whose banner gives the reason.
const translationBlocked = computed(() => !!currentTranslationBlock());
const translateLabel = computed(() => {
// Before the touch label: a tap opens the panel either way, but
// the button's name should not promise settings when nothing
// here can translate.
if (translationBlocked.value) {
return t("translate.unavailable.lead");
}

if (touch) {
return t("translate.header.settings");
}

// Every language the tooltip names is named in the language the
// reader reads this channel in (F40), like every other label.
const name = (code: string) => languageName(code, readingLanguage());
const {read, write} = translationState.value;
const paused = store.state.translation.paused;
// One whole phrase per state (reading on/off × a write target ×
// paused): the tooltip is never assembled from translated pieces,
// and every key is a literal the pot check can see.
const vars = {
reading: name(readingLanguage()),
writing: write ? name(write) : "",
// The engine's own text: a verbatim value, never translated.
reason: paused ? paused.message : "",
};

if (paused) {
if (read) {
return write
? t("translate.header.readWritePaused", vars)
: t("translate.header.readPaused", vars);
}

return write
? t("translate.header.offWritePaused", vars)
: t("translate.header.offPaused", vars);
}

if (read) {
return write
? t("translate.header.readWrite", vars)
: t("translate.header.read", vars);
}

return write ? t("translate.header.offWrite", vars) : t("translate.header.off", vars);
});

const openTranslationPanel = () => {
translationPanelOpen.value = true;
};

const toggleTranslation = () => {
const wasOn = translationState.value.read;

setReading(props.network, props.channel, !wasOn);

// Switching a channel on opens its panel with it: the reader sees
// which languages they just asked for and can adjust them at once,
// where the click alone would silently pick the global target.
// Switching off is the whole of what switching off means.
if (!wasOn) {
openTranslationPanel();
}
};

const onTranslateClick = () => {
if (touch || translationBlocked.value) {
openTranslationPanel();
} else {
toggleTranslation();
}
};

watch(
() => props.channel.id,
Expand All @@ -521,7 +419,7 @@ export default defineComponent({
);

// Translation switched off (or nothing here can run it any more):
// the panel goes with the button, and does not come back with it.
// the panel closes, and does not come back with it.
watch(translationAvailable, (available) => {
if (!available) {
translationPanelOpen.value = false;
Expand Down Expand Up @@ -601,12 +499,6 @@ export default defineComponent({
openMentions,
translationPanelOpen,
translationAvailable,
channelReads,
translationBlocked,
translateLabel,
touch,
onTranslateClick,
openTranslationPanel,
mentionCount,
mentionBadge,
};
Expand Down
Loading
Loading