diff --git a/package-lock.json b/package-lock.json index 71e1923199..d0c073a297 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16637,6 +16637,15 @@ "mock-require": "^2.0.2" } }, + "tslint-language-service": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/tslint-language-service/-/tslint-language-service-0.9.9.tgz", + "integrity": "sha1-9UbcOEg5eeb7PPpZWErYUls61No=", + "dev": true, + "requires": { + "mock-require": "^2.0.2" + } + }, "tsutils": { "version": "2.29.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", diff --git a/src/common/models/timestampable.ts b/src/common/models/timestampable.ts index 74f4083d89..054b7e8e91 100644 --- a/src/common/models/timestampable.ts +++ b/src/common/models/timestampable.ts @@ -6,6 +6,6 @@ // ==LICENSE-END== export interface Timestampable { - createdAt: string; // ISO 8601 string - updatedAt: string; // ISO 8601 string + createdAt?: string; // ISO 8601 string + updatedAt?: string; // ISO 8601 string } diff --git a/src/common/redux/states/logger.ts b/src/common/redux/states/logger.ts index d54423950a..906e20583e 100644 --- a/src/common/redux/states/logger.ts +++ b/src/common/redux/states/logger.ts @@ -6,5 +6,5 @@ // ==LICENSE-END== export interface LoggerState { - lastMessage: string; + lastMessage: string | null; } diff --git a/src/common/services/opds.ts b/src/common/services/opds.ts index f25c9eb2d8..3ca9ad743a 100644 --- a/src/common/services/opds.ts +++ b/src/common/services/opds.ts @@ -44,9 +44,12 @@ export class OPDSParser { identifier: uuid.v4(), title: entry.title, description: entry.summary.content, - authors: [], - files: [], + // authors: [], + // files: [], }; + // To satisfy the static TypeScript compiler checks (strictNullChecks) + publication.authors = []; + publication.files = []; // Fill authors for (const author of entry.authors) { diff --git a/src/main/api/publication.ts b/src/main/api/publication.ts index 81c80c7771..a88d658fef 100644 --- a/src/main/api/publication.ts +++ b/src/main/api/publication.ts @@ -123,7 +123,9 @@ export class PublicationApi { for (const path of paths) { const newDoc = await this.catalogService.importFile(path); - newDocs.push(newDoc); + if (newDoc) { + newDocs.push(newDoc); + } } return newDocs.map((doc) => { diff --git a/src/main/api/reader.ts b/src/main/api/reader.ts index b7ee82042b..0bd948a0cd 100644 --- a/src/main/api/reader.ts +++ b/src/main/api/reader.ts @@ -54,7 +54,7 @@ export class ReaderApi { return this.locatorViewConverter.convertDocumentToView(savedDoc); } - public async getLastReadingLocation(data: any): Promise { + public async getLastReadingLocation(data: any): Promise { const { publication } = data; const docs = await this.locatorRepository.findByPublicationIdentifierAndLocatorType( publication.identifier, diff --git a/src/main/converter/opds.ts b/src/main/converter/opds.ts index 180e1dcb2f..9101713731 100644 --- a/src/main/converter/opds.ts +++ b/src/main/converter/opds.ts @@ -26,6 +26,8 @@ import { OPDSPublication } from "@r2-opds-js/opds/opds2/opds2-publication"; // Logger const debug = debug_("readium-desktop:main#services/lcp"); +import { CoverView } from "readium-desktop/common/views/publication"; + @injectable() export class OpdsFeedViewConverter { public convertDocumentToView(document: OpdsFeedDocument): OpdsFeedView { @@ -46,7 +48,7 @@ export class OpdsFeedViewConverter { return { title, url: link.Href, - publicationCount: (link.Children) ? link.Children.length : null, + publicationCount: (link.Children) ? link.Children.length : undefined, }; } @@ -61,13 +63,13 @@ export class OpdsFeedViewConverter { tags = metadata.Subject.map((subject) => convertMultiLangStringToString(subject.Name)); } - let publishedAt = null; + let publishedAt: string | undefined; if (metadata.PublicationDate) { publishedAt = moment(metadata.PublicationDate).toISOString(); } - let cover = null; + let cover: CoverView | undefined; if (publication.Images && publication.Images.length > 0) { cover = { @@ -76,8 +78,8 @@ export class OpdsFeedViewConverter { } // Get odps entry - let url = null; - let sampleUrl = null; + let url: string | undefined; + let sampleUrl: string | undefined; const links = publication.Links.filter( (link: any) => { return (link.TypeLink.indexOf(";type=entry;profile=opds-catalog") > 0); @@ -94,7 +96,7 @@ export class OpdsFeedViewConverter { sampleUrl = sampleLinks[0].Href; } - let base64OpdsPublication = null; + let base64OpdsPublication: string | undefined; if (links.length > 0) { url = links[0].Href; } else { @@ -142,7 +144,7 @@ export class OpdsFeedViewConverter { buyUrl: buyLink && buyLink.Href, borrowUrl: borrowLink && borrowLink.Href, subscribeUrl: subscribeLink && subscribeLink.Href, - hasSample: sampleUrl && true, + hasSample: (typeof sampleUrl !== "undefined") && true, base64OpdsPublication, isFree, }; diff --git a/src/main/db/document/publication.ts b/src/main/db/document/publication.ts index ef425d868a..202ddc8995 100644 --- a/src/main/db/document/publication.ts +++ b/src/main/db/document/publication.ts @@ -14,12 +14,12 @@ import { IHttpGetResult } from "readium-desktop/common/utils/http"; interface Resources { filePublication: string; - opdsPublication: string; + opdsPublication?: string; } export interface PublicationDocument extends Identifiable, Timestampable { resources: Resources; - opdsPublication: any; + opdsPublication?: any; title: string; tags?: string[]; files?: File[]; diff --git a/src/main/redux/sagas/i18n.ts b/src/main/redux/sagas/i18n.ts index cb1bd600b3..88d8244198 100644 --- a/src/main/redux/sagas/i18n.ts +++ b/src/main/redux/sagas/i18n.ts @@ -13,6 +13,8 @@ import { all, call, take } from "redux-saga/effects"; import { I18NState } from "readium-desktop/common/redux/states/i18n"; +import { I18NState } from "readium-desktop/common/redux/states/i18n"; + export function* localeWatcher() { while (true) { const action: i18nActions.ActionLocale = yield take(i18nActions.ActionType.Set); diff --git a/src/main/services/catalog.ts b/src/main/services/catalog.ts index 3766b62974..1a36cb62a2 100644 --- a/src/main/services/catalog.ts +++ b/src/main/services/catalog.ts @@ -56,7 +56,7 @@ export class CatalogService { @inject("publication-repository") private readonly publicationRepository!: PublicationRepository; - public async importFile(filePath: string, isLcpFile?: boolean): Promise { + public async importFile(filePath: string, isLcpFile?: boolean): Promise { const ext = path.extname(filePath); if (ext === ".lcpl" || (ext === ".part" && isLcpFile)) { @@ -65,7 +65,7 @@ export class CatalogService { return this.importEpubFile(filePath); } - return null; + return Promise.resolve(null); } public async importOpdsEntry(url: string, downloadSample: boolean): Promise { @@ -156,6 +156,9 @@ export class CatalogService { debug("[END] Download publication", downloadUrl, newDownload); // Import downloaded publication in catalog let publicationDocument = await this.importFile(download.dstPath, isLcpFile); + if (!publicationDocument) { + return Promise.reject("!publicationDocument"); + } // Add opds publication serialization to resources const jsonOpdsPublication = TAJSON.serialize(opdsPublication); @@ -200,6 +203,9 @@ export class CatalogService { * @return: Refreshed publication */ public async refreshPublicationMetadata(publication: Publication) { + if (!publication.files) { + return; + } const pubPath = path.join( this.publicationStorage.getRootPath(), publication.files[0].url.substr(6), @@ -238,7 +244,7 @@ export class CatalogService { const lcpl = JSON.parse(buffer.toString()); // search the path of the epub file - let download: Download = null; + let download: Download | undefined; if (lcpl.links) { for (const link of lcpl.links) { @@ -280,18 +286,18 @@ export class CatalogService { .from(JSON.stringify(jsonParsedPublication)) .toString("base64"); - const pubDocument = { + const pubDocument: PublicationDocument = { identifier: uuid.v4(), resources: { filePublication: b64ParsedPublication, - opdsPublication: null, + opdsPublication: undefined, }, title: convertMultiLangStringToString(parsedPublication.Metadata.Title), tags: [], - files: [], - coverFile: null, - customCover: null, - } as PublicationDocument; + coverFile: undefined, + customCover: undefined, + }; + pubDocument.files = []; // Store publication on filesystem debug("[START] Store publication on filesystem", filePath); diff --git a/src/main/services/lcp.ts b/src/main/services/lcp.ts index fd67e92644..becd496202 100644 --- a/src/main/services/lcp.ts +++ b/src/main/services/lcp.ts @@ -98,7 +98,7 @@ export class LcpManager { debug("Parse publication - START", epubPath); const parsedPublication: Epub = await EpubParsePromise(epubPath); - let lcpInfo: LcpInfo = null; + let lcpInfo: LcpInfo | undefined; if (parsedPublication.LCP) { // Add Lcp info @@ -286,7 +286,7 @@ export class LcpManager { lsdStatus: any, ): Promise { // Search LCPL license url - let lcplUrl: string = null; + let lcplUrl: string | undefined; for (const link of lsdStatus.links) { if (link.rel === "license") { diff --git a/src/renderer/components/dialog/SameFileImportConfirm.tsx b/src/renderer/components/dialog/SameFileImportConfirm.tsx index fcb97c3949..6d136746e3 100644 --- a/src/renderer/components/dialog/SameFileImportConfirm.tsx +++ b/src/renderer/components/dialog/SameFileImportConfirm.tsx @@ -52,6 +52,9 @@ class SameFileImportConfirm extends React.Component { } private addToCatalog() { + if (!this.props.importOpdsEntry) { + return; + } this.props.importOpdsEntry( { url: this.props.publication.url, diff --git a/tsconfig.json b/tsconfig.json index 4ff9a3f692..cf77bfdd3b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,6 +15,8 @@ "noImplicitAny": true, "noImplicitThis": true, "alwaysStrict": true, + "strictNullChecks": true, + "strictPropertyInitialization": true, "strictFunctionTypes": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true,