Skip to content
Draft
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
20 changes: 20 additions & 0 deletions nx/blocks/loc/views/translate/translate.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@ svg {
display: none;
}

.nx-loading-spinner {
display: block;
width: 14px;
height: 14px;
border: 2px solid currentcolor;
border-top-color: transparent;
border-radius: 50%;
animation: da-spin 0.8s linear infinite;
}

@keyframes da-spin {
to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
.nx-loading-spinner {
animation-duration: 1.5s;
}
}

p {
margin: 0;
line-height: 1;
Expand Down
146 changes: 102 additions & 44 deletions nx/blocks/loc/views/translate/translate.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ class NxLocTranslate extends LitElement {
_translateLangs: { state: true },
_copyLangs: { state: true },
_message: { state: true },
_connectBusy: { state: true },
_sendAllBusy: { state: true },
_getStatusBusy: { state: true },
_cancelAllBusy: { state: true },
_copyAllBusy: { state: true },
_cancelingLangs: { state: true },
};

connectedCallback() {
Expand Down Expand Up @@ -92,7 +98,12 @@ class NxLocTranslate extends LitElement {

async handleConnect() {
const sendMessage = this.handleMessage.bind(this);
this._connected = await this._service.connector.connect(this._service, sendMessage);
this._connectBusy = true;
try {
this._connected = await this._service.connector.connect(this._service, sendMessage);
} finally {
this._connectBusy = false;
}
}

async fetchUrls(service, fetchContent, langs) {
Expand Down Expand Up @@ -213,67 +224,91 @@ class NxLocTranslate extends LitElement {
// return;
// }

const conf = await this.getBaseTranslationConf(false);
this._getStatusBusy = true;
try {
const conf = await this.getBaseTranslationConf(false);

await this._service.connector.getStatusAll(removeWaitingLanguagesFromConf(conf));
await this._service.connector.getStatusAll(removeWaitingLanguagesFromConf(conf));

await this.checkAndSaveLangs(conf);
await this.checkAndSaveLangs(conf);

this.handleSaveLangs();
this.handleSaveLangs();
} finally {
this._getStatusBusy = false;
}
}

async handleCancelAll() {
const sendMessage = this.handleMessage.bind(this);
this._cancelAllBusy = true;
try {
const sendMessage = this.handleMessage.bind(this);

const { cancelTranslation } = this._service.connector;
const { cancelTranslation } = this._service.connector;

let shouldRefresh = false;
for (const lang of this._translateLangs) {
const result = await cancelTranslation({ service: this._service, lang, sendMessage });
if (result?.ok !== false) shouldRefresh = true;
}
let shouldRefresh = false;
for (const lang of this._translateLangs) {
const result = await cancelTranslation({ service: this._service, lang, sendMessage });
if (result?.ok !== false) shouldRefresh = true;
}

if (shouldRefresh) {
// Refresh locales GLaaS accepted; skip when every cancel was rejected.
await this.handleGetStatus();
if (shouldRefresh) {
// Refresh locales GLaaS accepted; skip when every cancel was rejected.
await this.handleGetStatus();
}
} finally {
this._cancelAllBusy = false;
}
}

async handleCancelLang(lang) {
const sendMessage = this.handleMessage.bind(this);
this._cancelingLangs ??= new Set();
this._cancelingLangs.add(lang.code);
this.requestUpdate();

try {
const sendMessage = this.handleMessage.bind(this);

const { cancelTranslation } = this._service.connector;
const { cancelTranslation } = this._service.connector;

const result = await cancelTranslation({ service: this._service, lang, sendMessage });
const result = await cancelTranslation({ service: this._service, lang, sendMessage });

if (result?.ok !== false) {
await this.handleGetStatus();
if (result?.ok !== false) {
await this.handleGetStatus();
}
} finally {
this._cancelingLangs.delete(lang.code);
this.requestUpdate();
}
}

async handleCopyAll() {
const { _copyLangs: langs } = this;
this._copyAllBusy = true;
try {
const { _copyLangs: langs } = this;

// langsWithUrls is an in-memory object that contains all URL fetches.
const { langsWithUrls, urls } = await this.fetchUrls({}, true, langs);

langsWithUrls.forEach((lang) => {
const errors = lang.urls.filter((url) => url.error);
if (errors.length) {
// Create an errors array if it doesn't exist
this._urlErrors ??= [];
this._urlErrors.push(...errors);
}
});
// langsWithUrls is an in-memory object that contains all URL fetches.
const { langsWithUrls, urls } = await this.fetchUrls({}, true, langs);

// Do not continue if any errors
if (this._urlErrors?.length) return;
langsWithUrls.forEach((lang) => {
const errors = lang.urls.filter((url) => url.error);
if (errors.length) {
// Create an errors array if it doesn't exist
this._urlErrors ??= [];
this._urlErrors.push(...errors);
}
});

const { org, site, title, options } = this.project;
// Do not continue if any errors
if (this._urlErrors?.length) return;

await copySourceLangs(org, site, title, options, this._copyLangs, urls, langsWithUrls);
this.handleSaveLangs();
this.requestUpdate();
const { org, site, title, options } = this.project;

await copySourceLangs(org, site, title, options, this._copyLangs, urls, langsWithUrls);
this.handleSaveLangs();
this.requestUpdate();
} finally {
this._copyAllBusy = false;
}
}

get _project() {
Expand All @@ -300,11 +335,17 @@ class NxLocTranslate extends LitElement {
return html`<p><strong>Conflict behavior:</strong> ${this._options['translate.conflict.behavior']}</p>`;
}

renderSpinner() {
return html`<span class="nx-loading-spinner" aria-hidden="true"></span>`;
}

renderTranslateAction() {
if (this._connected === false) {
return html`
${this.renderBehavior()}
<sl-button @click=${this.handleConnect} class="accent">Connect</sl-button>
<sl-button @click=${this.handleConnect} class="accent" ?disabled=${this._connectBusy}>
${this._connectBusy ? this.renderSpinner() : nothing} Connect
</sl-button>
`;
}

Expand All @@ -317,14 +358,24 @@ class NxLocTranslate extends LitElement {
if (sent) {
return html`
${this.renderBehavior()}
${this.canCancel ? html`<sl-button @click=${this.handleCancelAll} class="primary outline">Cancel project</sl-button>` : nothing}
${this.incompleteLangs ? html`<sl-button @click=${this.handleGetStatus} class="accent">Get status</sl-button>` : nothing}
${this.canCancel ? html`
<sl-button @click=${this.handleCancelAll} class="primary outline" ?disabled=${this._cancelAllBusy}>
${this._cancelAllBusy ? this.renderSpinner() : nothing} Cancel project
</sl-button>
` : nothing}
${this.incompleteLangs ? html`
<sl-button @click=${this.handleGetStatus} class="accent" ?disabled=${this._getStatusBusy}>
${this._getStatusBusy ? this.renderSpinner() : nothing} Get status
</sl-button>
` : nothing}
`;
}

return html`
${this.renderBehavior()}
<sl-button @click=${this.handleSendAll} class="accent">Translate all</sl-button>
<sl-button @click=${this.handleSendAll} class="accent" ?disabled=${this._sendAllBusy}>
${this._sendAllBusy ? this.renderSpinner() : nothing} Translate all
</sl-button>
`;
}
}
Expand All @@ -346,7 +397,12 @@ class NxLocTranslate extends LitElement {

renderCancelLang(lang) {
if (!this.canCancel || !this._connected || !lang.translation || lang.translation?.status === 'cancelled') return nothing;
return html`<sl-button @click=${() => this.handleCancelLang(lang)} class="primary outline">Cancel</sl-button>`;
const busy = this._cancelingLangs?.has(lang.code);
return html`
<sl-button @click=${() => this.handleCancelLang(lang)} class="primary outline" ?disabled=${busy}>
${busy ? this.renderSpinner() : nothing} Cancel
</sl-button>
`;
}

renderUrlErrors() {
Expand Down Expand Up @@ -417,7 +473,9 @@ class NxLocTranslate extends LitElement {
<p class="nx-loc-list-actions-header">Copy (${this._options['source.language'].name})</p>
<div class="actions">
<p><strong>Conflict behavior:</strong> ${this._options['copy.conflict.behavior']}</p>
<sl-button @click=${this.handleCopyAll} class="accent">Copy all</sl-button>
<sl-button @click=${this.handleCopyAll} class="accent" ?disabled=${this._copyAllBusy}>
${this._copyAllBusy ? this.renderSpinner() : nothing} Copy all
</sl-button>
</div>
</div>
<div class="nx-loc-list-header">
Expand Down
Loading