diff --git a/src/common/redux/actions/toast/openRequest.ts b/src/common/redux/actions/toast/openRequest.ts index a75c111b94..cc2c348f5d 100644 --- a/src/common/redux/actions/toast/openRequest.ts +++ b/src/common/redux/actions/toast/openRequest.ts @@ -13,10 +13,11 @@ export const ID = "TOAST_OPEN_REQUEST"; export interface Payload { type: ToastType; data: string | undefined; + title: string | undefined; publicationIdentifier: string | undefined; } -export function build(type: ToastType, data: string, publicationIdentifier?: string): +export function build(type: ToastType, data: string, title?: string, publicationIdentifier?: string): Action { return { @@ -24,6 +25,7 @@ export function build(type: ToastType, data: string, publicationIdentifier?: str payload: { type, data, + title, publicationIdentifier, }, }; diff --git a/src/common/redux/reducers/toast.ts b/src/common/redux/reducers/toast.ts index 14ad6e21c1..f3f54dc54e 100644 --- a/src/common/redux/reducers/toast.ts +++ b/src/common/redux/reducers/toast.ts @@ -19,6 +19,7 @@ const initialState: ToastState = { open: false, type: ToastType.Success, data: undefined, + title: undefined, publicationIdentifier: undefined, }; @@ -35,6 +36,7 @@ function toastReducer_( open: true, type: action.payload.type, data: action.payload.data, + title: action.payload.title, publicationIdentifier: action.payload.publicationIdentifier, }, ); diff --git a/src/common/redux/states/toast.ts b/src/common/redux/states/toast.ts index c9aa66c9c5..ab327aef31 100644 --- a/src/common/redux/states/toast.ts +++ b/src/common/redux/states/toast.ts @@ -11,5 +11,6 @@ export interface ToastState { open: boolean; type: ToastType; data: string; + title?: string; publicationIdentifier: string | undefined; } diff --git a/src/main/redux/sagas/api/publication/import/importLcplFromFs.ts b/src/main/redux/sagas/api/publication/import/importLcplFromFs.ts index a6c554b144..ef45ac93a5 100644 --- a/src/main/redux/sagas/api/publication/import/importLcplFromFs.ts +++ b/src/main/redux/sagas/api/publication/import/importLcplFromFs.ts @@ -10,13 +10,13 @@ import * as fs from "fs"; import moment from "moment"; import * as path from "path"; import { lcpLicenseIsNotWellFormed } from "readium-desktop/common/lcp"; -import { ToastType } from "readium-desktop/common/models/toast"; -import { toastActions } from "readium-desktop/common/redux/actions"; +// import { ToastType } from "readium-desktop/common/models/toast"; +// import { toastActions } from "readium-desktop/common/redux/actions"; import { extractCrc32OnZip } from "readium-desktop/main/tools/crc"; import { PublicationDocument } from "readium-desktop/main/db/document/publication"; import { diMainGet } from "readium-desktop/main/di"; // eslint-disable-next-line local-rules/typed-redux-saga-use-typed-effects -import { call, put } from "redux-saga/effects"; +import { call } from "redux-saga/effects"; import { SagaGenerator } from "typed-redux-saga"; import { call as callTyped } from "typed-redux-saga/macro"; @@ -25,10 +25,13 @@ import { TaJsonDeserialize } from "@r2-lcp-js/serializable"; import { downloader } from "../../../downloader"; import { importPublicationFromFS } from "./importPublicationFromFs"; +// import { getTranslator } from "readium-desktop/common/services/translator"; // Logger const debug = debug_("readium-desktop:main#saga/api/publication/import/publicationLcplFromFs"); +// const translate = getTranslator().translate; + export function* importLcplFromFS( filePath: string, lcpHashedPassphrase?: string, @@ -47,7 +50,6 @@ export function* importLcplFromFS( const r2LCP = TaJsonDeserialize(r2LCPJson, LCP); r2LCP.JsonSource = r2LCPStr; r2LCP.init(); - // LCP license checks to avoid unnecessary download: // CERTIFICATE_SIGNATURE_INVALID = 102 // CERTIFICATE_REVOKED = 101 @@ -74,13 +76,12 @@ export function* importLcplFromFS( debug(err); } if (res) { - const msg = lcpManager.convertUnlockPublicationResultToString(res, r2LCP.Issued?.toISOString() || ""); - yield put( - toastActions.openRequest.build( - ToastType.Error, msg, - ), - ); - throw new Error(`[${msg}] (${filePath})`); + const msg = lcpManager.convertUnlockPublicationResultToString(res, r2LCP.Issued?.toISOString() || ""); + // yield put( + // toastActions.openRequest.build( + // ToastType.Error, msg, title) + // ); + throw new Error(msg); } } @@ -94,12 +95,12 @@ export function* importLcplFromFS( // LICENSE_CERTIFICATE_DATE_INVALID (was LICENSE_SIGNATURE_DATE_INVALID) = 111 // LICENSE_SIGNATURE_INVALID = 112 const msg = lcpManager.convertUnlockPublicationResultToString(err, r2LCP.Issued?.toISOString() || ""); - yield put( - toastActions.openRequest.build( - ToastType.Error, msg, - ), - ); - throw new Error(`[${msg}] (${filePath})`); + // yield put( + // toastActions.openRequest.build( + // ToastType.Error, msg, title + // ), + // ); + throw new Error(msg); } } } @@ -107,7 +108,6 @@ export function* importLcplFromFS( const link = r2LCP?.Links?.reduce((pv, cv) => cv.Rel === "publication" ? cv : pv); if (link?.Href) { - const title = link.Title || path.basename(filePath); const [downloadFilePath] = yield* callTyped(downloader, [{ href: link.Href, type: link.Type }], title); diff --git a/src/main/redux/sagas/api/publication/import/index.ts b/src/main/redux/sagas/api/publication/import/index.ts index f14c79f3fd..9f5082a9aa 100644 --- a/src/main/redux/sagas/api/publication/import/index.ts +++ b/src/main/redux/sagas/api/publication/import/index.ts @@ -113,7 +113,7 @@ export function* importFromLink( toastActions.openRequest.build( ToastType.Error, translate("message.import.fail", - { path: link.url, err: e?.toString() }), + { title: link.title, err: e?.toString() }), ), ); } @@ -231,11 +231,13 @@ export function* importFromFs( } catch (error) { debug("importFromFs (hash + import) fail with :" + filePath, error); + + const fileTitle = fpath.split("/").at(-1); + const title = translate("message.import.fail", { title: fileTitle }); + const msg = error?.message; yield put( toastActions.openRequest.build( - ToastType.Error, - translate("message.import.fail", - { path: filePath, err: error?.toString() }), + ToastType.Error, msg, title, ), ); } diff --git a/src/main/redux/sagas/reader.ts b/src/main/redux/sagas/reader.ts index 72d5cc5277..47de6b2d61 100644 --- a/src/main/redux/sagas/reader.ts +++ b/src/main/redux/sagas/reader.ts @@ -177,7 +177,7 @@ function* readerOpenRequest(action: readerActions.openRequest.TAction) { } catch (e) { - const errMsg = e.toString(); + const errMsg = e.message; if (errMsg === ERROR_MESSAGE_ENCRYPTED_NO_LICENSE) { yield put( toastActions.openRequest.build( diff --git a/src/main/storage/publication-storage.ts b/src/main/storage/publication-storage.ts index ba74a11e11..124f68e158 100644 --- a/src/main/storage/publication-storage.ts +++ b/src/main/storage/publication-storage.ts @@ -130,7 +130,8 @@ export class PublicationStorage { } debug("Error GetPublicationEpubPath not found with", identifier, "Throw new Error"); - throw new Error(`getPublicationEpubPath() FAIL ${identifier} (cannot find book.epub|audiobook|etc.)`); + // throw new Error(`getPublicationEpubPath() FAIL ${identifier} (cannot find book.epub|audiobook|etc.)`); + throw new Error("This publication is missing in the storage folder."); } public async getPublicationFilename(publicationView: PublicationView) { diff --git a/src/renderer/assets/styles/components/toasts.scss b/src/renderer/assets/styles/components/toasts.scss index 2c98b00e16..f22e45adaa 100644 --- a/src/renderer/assets/styles/components/toasts.scss +++ b/src/renderer/assets/styles/components/toasts.scss @@ -16,7 +16,7 @@ padding: 1rem; border: 1px var(--color-success-text) solid; border-radius: 6px; - width: 15rem; + width: 23rem; height: fit-content; background: white; display: flex; @@ -85,7 +85,7 @@ } .leave { - transform: translateX(-100%); + transform: translateX(calc(-100% - 20px)); transition: transform 0.5s; } @@ -100,7 +100,7 @@ @keyframes start { from { - transform: translateX(-100%); + transform: translateX(calc(-100% - 20px)); } to { diff --git a/src/renderer/common/components/toast/Toast.tsx b/src/renderer/common/components/toast/Toast.tsx index 519cb6c30f..ee8c64eb8e 100644 --- a/src/renderer/common/components/toast/Toast.tsx +++ b/src/renderer/common/components/toast/Toast.tsx @@ -31,6 +31,7 @@ interface IBaseProps extends TranslatorProps { message?: string; displaySystemNotification?: boolean; type?: ToastType; + title?: string; } // IProps may typically extend: @@ -85,7 +86,7 @@ export class Toast extends React.Component { this.timer = window.setTimeout(() => { this.timer = undefined; this.handleClose(); - }, fast ? 500 : 5000); + }, fast ? 500 : 8000); } public componentDidMount() { @@ -155,10 +156,13 @@ export class Toast extends React.Component { toRemove && stylesToasts.toRemove, typeClassName, )} + style={{display: "flex", flexDirection: "column", alignItems: "flex-start"}} > { // icon && } + {this.props.title ? +
{this.props.title}
: <>}

{ close={ () => this.close(id) } type={toast.type} displaySystemNotification={false} + title={toast.title} />; default: return (<>); diff --git a/src/resources/locales/en.json b/src/resources/locales/en.json index 6ec5047d7b..017a47434e 100644 --- a/src/resources/locales/en.json +++ b/src/resources/locales/en.json @@ -222,15 +222,15 @@ "success": "Import completed successfully." }, "download": { - "error": "Downloading [{{- title}}] failed: [{{- err}}]" + "error": "Downloading \"{{- title}}\" failed: [{{- err}}]" }, "import": { - "alreadyImport": "[{{- title}}] was already imported.", - "fail": "Importing [{{- path}}] failed: [{{- err}}]", - "success": "Importing [{{- title}}] completed." + "alreadyImport": "\"{{- title}}\" was already imported.", + "fail": "Error while importing \"{{- title}}.\"", + "success": "\"{{- title}}\" successfully imported." }, "open": { - "error": "Loading publication failed: [{{- err}}]" + "error": "{{- err}}." }, "wipeData": "Login data was cleared (access tokens and cookies)" }, @@ -606,9 +606,9 @@ "tracks": "Tracks" }, "author": "Author", - "cancelledLcp": "This publication's LCP license is cancelled.", - "certificateRevoked": "Revoked LCP license certificate.", - "certificateSignatureInvalid": "Invalid LCP license certificate signature.", + "cancelledLcp": "Your license to read this publication has been cancelled.", + "certificateRevoked": "This publication cannot be opened (X509 cert was revoked).", + "certificateSignatureInvalid": "This publication cannot be opened (invalid X509 cert signature).", "cover": { "img": "cover image" }, @@ -617,18 +617,18 @@ "duration": { "title": "Duration" }, - "encryptedNoLicense": "Publication is encrypted but lacks an LCP license!", + "encryptedNoLicense": "This publication cannot be opened (no license in encrypted file).", "expired": "Loan expired", - "expiredLcp": "This publication's LCP license is expired.", + "expiredLcp": "Your license to read this publication has expired.", "incorrectPassphrase": "No stored passphrase fits the publication, or the entered passphrase is wrong.", "lcpEnd": "End", "lcpRightsCopy": "Copied characters", "lcpRightsPrint": "Printed pages", "lcpStart": "Start", "licenceLCP": "License (LCP)", - "licenseCertificateDateInvalid": "The LCP provider certificate expired before the license issue date ({{- dateTime}}).", - "licenseOutOfDate": "Out of date LCP License.", - "licenseSignatureInvalid": "Invalid LCP license signature.", + "licenseCertificateDateInvalid": "This publication cannot be opened (X509 cert expired before license issue date).", + "licenseOutOfDate": "Your license to read this publication has expired or has not started.", + "licenseSignatureInvalid": "This publication cannot be opened (invalid license signature).", "licensed": "Licensed", "markAsRead": "Mark as read", "notStarted": "Not Started", @@ -640,8 +640,8 @@ "remainingTime": "Loan (remaining time)", "renewButton": "Renew", "returnButton": "Return", - "returnedLcp": "This LCP-protected publication is returned.", - "revokedLcp": "This publication's LCP license is revoked.", + "returnedLcp": "You already have returned this publication.", + "revokedLcp": "Your license to read this publication has been revoked.", "seeLess": "See less", "seeMore": "See more", "timeLeft": "Remaining time", diff --git a/src/resources/locales/fr.json b/src/resources/locales/fr.json index 1b3181a905..b000be3bd9 100644 --- a/src/resources/locales/fr.json +++ b/src/resources/locales/fr.json @@ -227,15 +227,15 @@ "success": "Importation réussie" }, "download": { - "error": "Le téléchargement de {{- title}} a échoué: [{{- err}}]" + "error": "Le téléchargement de '{{- title}}' a échoué: [{{- err}}]" }, "import": { - "alreadyImport": "{{- title}} a déjà été ajouté.", - "fail": "L'ajout de {{- path}} a échoué. {{- err}}", - "success": "L'ajout de {{- title}} est achevé." + "alreadyImport": "\"{{- title}}\" a déjà été importé.", + "fail": "Erreur lors de l'import de \"{{- title}}\".", + "success": "\"{{- title}}\" importé avec succès." }, "open": { - "error": "Le livre ne peut pas être ouvert : {{- err}}" + "error": "{{- err}}." }, "wipeData": "Données d'authentification supprimées avec succès !" },