diff --git a/app/main/services/contextmenu-service.ts b/app/main/services/contextmenu-service.ts index 71316b98..946cdf5d 100644 --- a/app/main/services/contextmenu-service.ts +++ b/app/main/services/contextmenu-service.ts @@ -163,6 +163,7 @@ export class ContextMenuService extends Eventable { /** * Shows the context menu for paper data. * @param {boolean} allowEdit - Whether editing is allowed. + * @param {CategorizerMenuItem[]} categorizeList - The list of categorizers. */ @errorcatching( "Failed to show the contextmenu for papers.", diff --git a/app/renderer/services/file-service.ts b/app/renderer/services/file-service.ts index 957c39e4..6cbe66d0 100644 --- a/app/renderer/services/file-service.ts +++ b/app/renderer/services/file-service.ts @@ -212,6 +212,8 @@ export class FileService extends Eventable { /** * Move files of a paper entity to the library folder * @param paperEntity - Paper entity to move + * @param moveMain - Move the main file + * @param moveSups - Move the supplementary files * @returns */ async move( diff --git a/app/renderer/services/paper-service.ts b/app/renderer/services/paper-service.ts index dc910398..b44e74e6 100644 --- a/app/renderer/services/paper-service.ts +++ b/app/renderer/services/paper-service.ts @@ -443,6 +443,19 @@ export class PaperService extends Eventable { await this.update(paperEntityDrafts, false, true); } + /** + * Update the main file of a paper entity. + * @param paperEntity - The paper entity. + * @param url - The URL of the main file. + * @returns The updated paper entity. + */ + @processing(ProcessingKey.General) + @errorcatching( + "Failed to update paper entitiy's main file.", + true, + "PaperService", + [] + ) async updateMainURL(paperEntity: PaperEntity, url: string) { if (this._databaseCore.getState("dbInitializing")) { return; @@ -460,6 +473,17 @@ export class PaperService extends Eventable { return updatedPaperEntities[0]; } + /** + * Update the supplementary files of a paper entity. + * @param paperEntity - The paper entity. + * @param urls - The URLs of the supplementary files. + */ + @processing(ProcessingKey.General) + @errorcatching( + "Failed to update paper entity's supplementary files.", + true, + "PaperService" + ) async updateSupURLs(paperEntity: PaperEntity, urls: string[]) { if (this._databaseCore.getState("dbInitializing")) { return; diff --git a/app/repositories/file-repository/local-backend.ts b/app/repositories/file-repository/local-backend.ts index 0404bb69..118bd0cc 100644 --- a/app/repositories/file-repository/local-backend.ts +++ b/app/repositories/file-repository/local-backend.ts @@ -96,6 +96,10 @@ export class LocalFileBackend implements IFileBackend { } } + /** + * Check base folder, if not exist, create it. + * @param folderPath - Folder path + */ async checkBaseFolder(folderPath: string): Promise { const _folderPath = eraseProtocol(folderPath); if (!existsSync(_folderPath)) { @@ -107,8 +111,6 @@ export class LocalFileBackend implements IFileBackend { * Move file from sourceURL to targetURL * @param sourceURL - Source URL, also can be a file name in the app library folder * @param targetURL - Target URL, also can be a file name in the app library folder - * @param forceDelete - Force delete source file - * @param forceNotLink - Force not to use link * @returns Target file name in the app library folder */ async moveFile(sourceURL: string, targetURL: string): Promise { @@ -139,6 +141,11 @@ export class LocalFileBackend implements IFileBackend { } } + /** + * Remove file from sourceURL + * @param sourceURL - Source URL, also can be a file name in the app library folder + * @returns void + */ async removeFile(sourceURL: string): Promise { sourceURL = constructFileURL(sourceURL, true, false, this._appLibFolder); if (existsSync(sourceURL)) { diff --git a/paperlib-api/dist/api.d.ts b/paperlib-api/dist/api.d.ts index 70daecc5..22f07643 100644 --- a/paperlib-api/dist/api.d.ts +++ b/paperlib-api/dist/api.d.ts @@ -12,6 +12,7 @@ import { OpenDialogReturnValue } from 'electron'; import { default as Realm_2 } from 'realm'; import { Results } from 'realm'; import { Store } from 'pinia'; +import Watcher from 'watcher'; import { XMLParser } from 'fast-xml-parser'; declare enum APPTheme { @@ -132,6 +133,12 @@ declare class Categorizer { initialize(object: ICategorizerDraft): this; } +declare type CategorizerMenuItem = { + type: CategorizerType; + name: string; + id: OID; +}; + declare class CategorizerRepository extends Eventable { constructor(); /** @@ -326,8 +333,9 @@ declare class ContextMenuService extends Eventable { /** * Shows the context menu for paper data. * @param {boolean} allowEdit - Whether editing is allowed. + * @param {CategorizerMenuItem[]} categorizeList - The list of categorizers. */ - showPaperDataMenu(allowEdit: boolean): void; + showPaperDataMenu(allowEdit: boolean, categorizeList: CategorizerMenuItem[]): void; /** * Shows the context menu for feed data. */ @@ -964,6 +972,7 @@ declare class FileService extends Eventable { */ initialize(): Promise; private _initBackend; + backend(): Promise; /** * Start watching file changes. (Only for WebDAV file backend) */ @@ -976,23 +985,26 @@ declare class FileService extends Eventable { * Check if the file backend is available. */ check(): Promise; + /** + * Infer the relative path of a paper entity. + * @param paperEntity - Paper entity to infer the relative path + */ + inferRelativeFileName(paperEntity: PaperEntity): Promise; /** * Move files of a paper entity to the library folder * @param paperEntity - Paper entity to move - * @param fourceDelete - Force to delete the source file - * @param forceNotLink - Force to do not use link + * @param moveMain - Move the main file + * @param moveSups - Move the supplementary files * @returns */ - move(paperEntity: PaperEntity, fourceDelete?: boolean, forceNotLink?: boolean): Promise; + move(paperEntity: PaperEntity, moveMain?: boolean, moveSups?: boolean): Promise; /** * Move a file * @param sourceURL - Source file URL * @param targetURL - Target file URL - * @param fourceDelete - Force to delete the source file - * @param forceNotLink - Force to do not use link * @returns */ - moveFile(sourceURL: string, targetURL: string, fourceDelete?: boolean, forceNotLink?: boolean): Promise; + moveFile(sourceURL: string, targetURL: string): Promise; /** * Remove files of a paper entity * @param paperEntity - Paper entity to remove @@ -1175,6 +1187,10 @@ declare interface ICommand { } declare interface IContextMenuServiceState { + dataContextMenuRemoveFromClicked: { + type: CategorizerType; + id: OID; + }; dataContextMenuScrapeFromClicked: string; dataContextMenuOpenClicked: number; dataContextMenuShowInFinderClicked: number; @@ -1345,9 +1361,21 @@ declare interface IFeedServiceState { entitiesUpdated: number; } +declare interface IFileBackend { + watcher?: Watcher; + check(): Promise; + access(url: string, download: boolean): Promise; + startWatch(): Promise; + stopWatch(): Promise; + moveFile(sourceURL: string, targetURL: string): Promise; + removeFile(sourceURL: string): Promise; +} + declare interface IFileServiceState { backend: string; available: boolean; + backendInitializing: boolean; + backendInitialized: boolean; } declare interface ILogEventState { @@ -1391,6 +1419,7 @@ declare interface IMenuServiceState { "View-preview": number; "View-next": number; "View-previous": number; + "File-delete": number; } declare type IPaperEntityCollection = Results | List | Array; @@ -1500,6 +1529,7 @@ declare interface IPreferenceStore { shortcutEdit: string; shortcutFlag: string; shortcutCopyKey: string; + shortcutDelete: string; sidebarWidth: number; detailPanelWidth: number; mainviewSortBy: string; @@ -1663,11 +1693,18 @@ declare class MenuService extends Eventable { * @param key */ click(key: keyof IMenuServiceState): void; + /** + * Enable all global shortcuts. + */ + enableGlobalShortcuts(): void; } declare class NetworkTool { private _agent; private _donwloadProgress; + private _caCert; + private _caClientKey; + private _caClinetCert; constructor(); /** * Set proxy agent @@ -1862,13 +1899,11 @@ declare class PaperEntityRepository extends Eventable { * Update paper entities. * @param paperEntityDrafts - paper entity drafts * @param updateCache - Update cache, default is true - * @param isUpdate - Is update, default is false, if true, it is insert. This is for PDF file operation. + * @param isUpdate - Is update, default is false, if false, it is insert. This is for preventing insert duplicated papers. * @returns Updated paper entities */ update(paperEntityDrafts: IPaperEntityCollection, updateCache?: boolean, isUpdate?: boolean): Promise; @@ -1937,6 +1972,19 @@ declare class PaperService extends Eventable { * @param type - The type of the categorizer. */ updateWithCategorizer(ids: OID[], categorizer: Categorizer, type: CategorizerType): Promise; + /** + * Update the main file of a paper entity. + * @param paperEntity - The paper entity. + * @param url - The URL of the main file. + * @returns The updated paper entity. + */ + updateMainURL(paperEntity: PaperEntity, url: string): Promise; + /** + * Update the supplementary files of a paper entity. + * @param paperEntity - The paper entity. + * @param urls - The URLs of the supplementary files. + */ + updateSupURLs(paperEntity: PaperEntity, urls: string[]): Promise; /** * Delete paper entities. * @param ids - Paper entity ids diff --git a/paperlib-api/dist/utils.mjs b/paperlib-api/dist/utils.mjs index 99f68e40..446530b8 100644 --- a/paperlib-api/dist/utils.mjs +++ b/paperlib-api/dist/utils.mjs @@ -153,12 +153,12 @@ function constructFileURL(url, joined, withProtocol = true, root = "", protocol } if (withProtocol) { if (outURL.startsWith(protocol)) { - return outURL; + return outURL.replace(/\\/g, "/"); } else { - return protocol + outURL; + return (protocol + outURL).replace(/\\/g, "/"); } } else { - return outURL.replace(protocol, ""); + return outURL.replace(protocol, "").replace(/\\/g, "/"); } } function listAllFiles(folderURL, arrayOfFiles = null) {