mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2025-12-06 02:30:47 -08:00
feat: deeplink
This commit is contained in:
parent
bf8a2995b6
commit
bd2b8a4449
12 changed files with 153 additions and 43 deletions
|
|
@ -89,6 +89,7 @@ const config = {
|
||||||
executableName: 'tidgi',
|
executableName: 'tidgi',
|
||||||
config: {
|
config: {
|
||||||
maintainer: 'Lin Onetwo <linonetwo012@gmail.com>',
|
maintainer: 'Lin Onetwo <linonetwo012@gmail.com>',
|
||||||
|
mimeType: ['x-scheme-handler/tidgi'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -97,6 +98,7 @@ const config = {
|
||||||
executableName: 'tidgi',
|
executableName: 'tidgi',
|
||||||
config: {
|
config: {
|
||||||
maintainer: 'Lin Onetwo <linonetwo012@gmail.com>',
|
maintainer: 'Lin Onetwo <linonetwo012@gmail.com>',
|
||||||
|
mimeType: ['x-scheme-handler/tidgi'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import serviceIdentifier from '@services/serviceIdentifier';
|
||||||
import { WindowNames } from '@services/windows/WindowProperties';
|
import { WindowNames } from '@services/windows/WindowProperties';
|
||||||
|
|
||||||
import { IDatabaseService } from '@services/database/interface';
|
import { IDatabaseService } from '@services/database/interface';
|
||||||
|
import { IDeepLinkService } from '@services/deepLink/interface';
|
||||||
import { initializeObservables } from '@services/libs/initializeObservables';
|
import { initializeObservables } from '@services/libs/initializeObservables';
|
||||||
import { reportErrorToGithubWithTemplates } from '@services/native/reportError';
|
import { reportErrorToGithubWithTemplates } from '@services/native/reportError';
|
||||||
import type { IUpdaterService } from '@services/updater/interface';
|
import type { IUpdaterService } from '@services/updater/interface';
|
||||||
|
|
@ -51,8 +52,6 @@ protocol.registerSchemesAsPrivileged([
|
||||||
{ scheme: 'file', privileges: { bypassCSP: true, allowServiceWorkers: true, supportFetchAPI: true, corsEnabled: true, stream: true } },
|
{ scheme: 'file', privileges: { bypassCSP: true, allowServiceWorkers: true, supportFetchAPI: true, corsEnabled: true, stream: true } },
|
||||||
{ scheme: 'mailto', privileges: { standard: true } },
|
{ scheme: 'mailto', privileges: { standard: true } },
|
||||||
]);
|
]);
|
||||||
// TODO: handle workspace name + tiddler name in uri https://www.electronjs.org/docs/latest/tutorial/launch-app-from-url-in-another-app
|
|
||||||
app.setAsDefaultProtocolClient('tidgi');
|
|
||||||
bindServiceAndProxy();
|
bindServiceAndProxy();
|
||||||
const preferenceService = container.get<IPreferenceService>(serviceIdentifier.Preference);
|
const preferenceService = container.get<IPreferenceService>(serviceIdentifier.Preference);
|
||||||
const updaterService = container.get<IUpdaterService>(serviceIdentifier.Updater);
|
const updaterService = container.get<IUpdaterService>(serviceIdentifier.Updater);
|
||||||
|
|
@ -61,6 +60,7 @@ const wikiService = container.get<IWikiService>(serviceIdentifier.Wiki);
|
||||||
const windowService = container.get<IWindowService>(serviceIdentifier.Window);
|
const windowService = container.get<IWindowService>(serviceIdentifier.Window);
|
||||||
const workspaceViewService = container.get<IWorkspaceViewService>(serviceIdentifier.WorkspaceView);
|
const workspaceViewService = container.get<IWorkspaceViewService>(serviceIdentifier.WorkspaceView);
|
||||||
const databaseService = container.get<IDatabaseService>(serviceIdentifier.Database);
|
const databaseService = container.get<IDatabaseService>(serviceIdentifier.Database);
|
||||||
|
const deepLinkService = container.get<IDeepLinkService>(serviceIdentifier.DeepLink);
|
||||||
app.on('second-instance', async () => {
|
app.on('second-instance', async () => {
|
||||||
// see also src/helpers/singleInstance.ts
|
// see also src/helpers/singleInstance.ts
|
||||||
// Someone tried to run a second instance, for example, when `runOnBackground` is true, we should focus our window.
|
// Someone tried to run a second instance, for example, when `runOnBackground` is true, we should focus our window.
|
||||||
|
|
@ -83,6 +83,8 @@ void preferenceService.get('ignoreCertificateErrors').then((ignoreCertificateErr
|
||||||
const commonInit = async (): Promise<void> => {
|
const commonInit = async (): Promise<void> => {
|
||||||
await app.whenReady();
|
await app.whenReady();
|
||||||
// if user want a menubar, we create a new window for that
|
// if user want a menubar, we create a new window for that
|
||||||
|
// handle workspace name + tiddler name in uri https://www.electronjs.org/docs/latest/tutorial/launch-app-from-url-in-another-app
|
||||||
|
deepLinkService.initializeDeepLink('tidgi');
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
windowService.open(WindowNames.main),
|
windowService.open(WindowNames.main),
|
||||||
preferenceService.get('attachToMenubar').then(async (attachToMenubar) => {
|
preferenceService.get('attachToMenubar').then(async (attachToMenubar) => {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { useSortable } from '@dnd-kit/sortable';
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
import { CSS } from '@dnd-kit/utilities';
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
import { getWorkspaceMenuTemplate, openWorkspaceTagTiddler } from '@services/workspaces/getWorkspaceMenuTemplate';
|
import { getWorkspaceMenuTemplate } from '@services/workspaces/getWorkspaceMenuTemplate';
|
||||||
import { IWorkspaceWithMetadata } from '@services/workspaces/interface';
|
import { IWorkspaceWithMetadata } from '@services/workspaces/interface';
|
||||||
import { MouseEvent, useCallback, useState } from 'react';
|
import { MouseEvent, useCallback, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
@ -31,7 +31,7 @@ export function SortableWorkspaceSelectorButton({ index, workspace, showSidebarT
|
||||||
workspaceClickedLoadingSetter(true);
|
workspaceClickedLoadingSetter(true);
|
||||||
try {
|
try {
|
||||||
setLocation(`/${WindowNames.main}/${PageType.wiki}/${id}/`);
|
setLocation(`/${WindowNames.main}/${PageType.wiki}/${id}/`);
|
||||||
await openWorkspaceTagTiddler(workspace, window.service);
|
await window.service.workspace.openWorkspaceTiddler(workspace);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
await window.service.native.log('error', error.message);
|
await window.service.native.log('error', error.message);
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { AsyncifyProxy } from 'electron-ipc-cat/common';
|
||||||
|
|
||||||
import { AuthenticationServiceIPCDescriptor, IAuthenticationService } from '@services/auth/interface';
|
import { AuthenticationServiceIPCDescriptor, IAuthenticationService } from '@services/auth/interface';
|
||||||
import { ContextServiceIPCDescriptor, IContextService } from '@services/context/interface';
|
import { ContextServiceIPCDescriptor, IContextService } from '@services/context/interface';
|
||||||
|
import { DeepLinkServiceIPCDescriptor, IDeepLinkService } from '@services/deepLink/interface';
|
||||||
import { GitServiceIPCDescriptor, IGitService } from '@services/git/interface';
|
import { GitServiceIPCDescriptor, IGitService } from '@services/git/interface';
|
||||||
import { IMenuService, MenuServiceIPCDescriptor } from '@services/menu/interface';
|
import { IMenuService, MenuServiceIPCDescriptor } from '@services/menu/interface';
|
||||||
import { INativeService, NativeServiceIPCDescriptor } from '@services/native/interface';
|
import { INativeService, NativeServiceIPCDescriptor } from '@services/native/interface';
|
||||||
|
|
@ -44,6 +45,7 @@ export const wikiGitWorkspace = createProxy<IWikiGitWorkspaceService>(WikiGitWor
|
||||||
export const window = createProxy<IWindowService>(WindowServiceIPCDescriptor);
|
export const window = createProxy<IWindowService>(WindowServiceIPCDescriptor);
|
||||||
export const workspace = createProxy<AsyncifyProxy<IWorkspaceService>>(WorkspaceServiceIPCDescriptor);
|
export const workspace = createProxy<AsyncifyProxy<IWorkspaceService>>(WorkspaceServiceIPCDescriptor);
|
||||||
export const workspaceView = createProxy<IWorkspaceViewService>(WorkspaceViewServiceIPCDescriptor);
|
export const workspaceView = createProxy<IWorkspaceViewService>(WorkspaceViewServiceIPCDescriptor);
|
||||||
|
export const deepLink = createProxy<IDeepLinkService>(DeepLinkServiceIPCDescriptor);
|
||||||
|
|
||||||
export const descriptors = {
|
export const descriptors = {
|
||||||
auth: AuthenticationServiceIPCDescriptor,
|
auth: AuthenticationServiceIPCDescriptor,
|
||||||
|
|
@ -64,4 +66,5 @@ export const descriptors = {
|
||||||
window: WindowServiceIPCDescriptor,
|
window: WindowServiceIPCDescriptor,
|
||||||
workspace: WorkspaceServiceIPCDescriptor,
|
workspace: WorkspaceServiceIPCDescriptor,
|
||||||
workspaceView: WorkspaceViewServiceIPCDescriptor,
|
workspaceView: WorkspaceViewServiceIPCDescriptor,
|
||||||
|
deepLink: DeepLinkServiceIPCDescriptor,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
73
src/services/deepLink/index.ts
Normal file
73
src/services/deepLink/index.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { lazyInject } from '@services/container';
|
||||||
|
import { logger } from '@services/libs/log';
|
||||||
|
import serviceIdentifier from '@services/serviceIdentifier';
|
||||||
|
import { IWorkspaceService } from '@services/workspaces/interface';
|
||||||
|
import { app } from 'electron';
|
||||||
|
import { injectable } from 'inversify';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { IDeepLinkService } from './interface';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class DeepLinkService implements IDeepLinkService {
|
||||||
|
@lazyInject(serviceIdentifier.Workspace)
|
||||||
|
private readonly workspaceService!: IWorkspaceService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle link and open the workspace.
|
||||||
|
* @param requestUrl like `tidgi://lxqsftvfppu_z4zbaadc0/#:Index` or `tidgi://lxqsftvfppu_z4zbaadc0/#%E6%96%B0%E6%9D%A1%E7%9B%AE`
|
||||||
|
*/
|
||||||
|
private readonly deepLinkHandler: (requestUrl: string) => Promise<void> = async (requestUrl) => {
|
||||||
|
logger.info(`Receiving deep link`, { requestUrl, function: 'deepLinkHandler' });
|
||||||
|
const url = new URL(requestUrl);
|
||||||
|
const workspaceId = url.hostname;
|
||||||
|
const workspace = await this.workspaceService.get(workspaceId);
|
||||||
|
if (workspace === undefined) {
|
||||||
|
logger.error(`Workspace not found`, { workspaceId, function: 'deepLinkHandler' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let tiddlerName = url.hash.substring(1); // remove '#:'
|
||||||
|
if (tiddlerName.includes(':')) {
|
||||||
|
tiddlerName = tiddlerName.split(':')[1];
|
||||||
|
}
|
||||||
|
logger.info(`Open deep link`, { workspaceId, tiddlerName, function: 'deepLinkHandler' });
|
||||||
|
await this.workspaceService.openWorkspaceTiddler(workspace, tiddlerName);
|
||||||
|
};
|
||||||
|
|
||||||
|
public initializeDeepLink(protocol: string) {
|
||||||
|
if (process.defaultApp) {
|
||||||
|
if (process.argv.length >= 2) {
|
||||||
|
app.setAsDefaultProtocolClient(protocol, process.execPath, [path.resolve(process.argv[1])]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
app.setAsDefaultProtocolClient(protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
this.setupMacOSHandler();
|
||||||
|
} else {
|
||||||
|
this.setupWindowsLinuxHandler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupMacOSHandler(): void {
|
||||||
|
app.on('open-url', (event, url) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.deepLinkHandler(url);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupWindowsLinuxHandler(): void {
|
||||||
|
const gotTheLock = app.requestSingleInstanceLock();
|
||||||
|
|
||||||
|
if (gotTheLock) {
|
||||||
|
app.on('second-instance', (event, commandLine) => {
|
||||||
|
const url = commandLine.pop();
|
||||||
|
if (url !== undefined && url !== '') {
|
||||||
|
this.deepLinkHandler(url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/services/deepLink/interface.ts
Normal file
16
src/services/deepLink/interface.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { ProxyPropertyType } from 'electron-ipc-cat/common';
|
||||||
|
|
||||||
|
export interface IDeepLinkService {
|
||||||
|
/**
|
||||||
|
* Initialize deep link service.
|
||||||
|
* @param protocol The protocol to be used for deep linking.
|
||||||
|
*/
|
||||||
|
initializeDeepLink(protocol: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DeepLinkServiceIPCDescriptor = {
|
||||||
|
channel: 'DeepLinkChannel',
|
||||||
|
properties: {
|
||||||
|
initializeDeepLink: ProxyPropertyType.Function,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -9,6 +9,7 @@ import serviceIdentifier from '@services/serviceIdentifier';
|
||||||
import { Authentication } from '@services/auth';
|
import { Authentication } from '@services/auth';
|
||||||
import { ContextService } from '@services/context';
|
import { ContextService } from '@services/context';
|
||||||
import { DatabaseService } from '@services/database';
|
import { DatabaseService } from '@services/database';
|
||||||
|
import { DeepLinkService } from '@services/deepLink';
|
||||||
import { Git } from '@services/git';
|
import { Git } from '@services/git';
|
||||||
import { MenuService } from '@services/menu';
|
import { MenuService } from '@services/menu';
|
||||||
import { NativeService } from '@services/native';
|
import { NativeService } from '@services/native';
|
||||||
|
|
@ -32,6 +33,8 @@ import type { IContextService } from '@services/context/interface';
|
||||||
import { ContextServiceIPCDescriptor } from '@services/context/interface';
|
import { ContextServiceIPCDescriptor } from '@services/context/interface';
|
||||||
import type { IDatabaseService } from '@services/database/interface';
|
import type { IDatabaseService } from '@services/database/interface';
|
||||||
import { DatabaseServiceIPCDescriptor } from '@services/database/interface';
|
import { DatabaseServiceIPCDescriptor } from '@services/database/interface';
|
||||||
|
import type { IDeepLinkService } from '@services/deepLink/interface';
|
||||||
|
import { DeepLinkServiceIPCDescriptor } from '@services/deepLink/interface';
|
||||||
import type { IGitService } from '@services/git/interface';
|
import type { IGitService } from '@services/git/interface';
|
||||||
import { GitServiceIPCDescriptor } from '@services/git/interface';
|
import { GitServiceIPCDescriptor } from '@services/git/interface';
|
||||||
import type { IMenuService } from '@services/menu/interface';
|
import type { IMenuService } from '@services/menu/interface';
|
||||||
|
|
@ -85,6 +88,7 @@ export function bindServiceAndProxy(): void {
|
||||||
container.bind<IWindowService>(serviceIdentifier.Window).to(Window).inSingletonScope();
|
container.bind<IWindowService>(serviceIdentifier.Window).to(Window).inSingletonScope();
|
||||||
container.bind<IWorkspaceService>(serviceIdentifier.Workspace).to(Workspace).inSingletonScope();
|
container.bind<IWorkspaceService>(serviceIdentifier.Workspace).to(Workspace).inSingletonScope();
|
||||||
container.bind<IWorkspaceViewService>(serviceIdentifier.WorkspaceView).to(WorkspaceView).inSingletonScope();
|
container.bind<IWorkspaceViewService>(serviceIdentifier.WorkspaceView).to(WorkspaceView).inSingletonScope();
|
||||||
|
container.bind<IDeepLinkService>(serviceIdentifier.DeepLink).to(DeepLinkService).inSingletonScope();
|
||||||
|
|
||||||
const authService = container.get<IAuthenticationService>(serviceIdentifier.Authentication);
|
const authService = container.get<IAuthenticationService>(serviceIdentifier.Authentication);
|
||||||
const contextService = container.get<IContextService>(serviceIdentifier.Context);
|
const contextService = container.get<IContextService>(serviceIdentifier.Context);
|
||||||
|
|
@ -105,6 +109,7 @@ export function bindServiceAndProxy(): void {
|
||||||
const windowService = container.get<IWindowService>(serviceIdentifier.Window);
|
const windowService = container.get<IWindowService>(serviceIdentifier.Window);
|
||||||
const workspaceService = container.get<IWorkspaceService>(serviceIdentifier.Workspace);
|
const workspaceService = container.get<IWorkspaceService>(serviceIdentifier.Workspace);
|
||||||
const workspaceViewService = container.get<IWorkspaceViewService>(serviceIdentifier.WorkspaceView);
|
const workspaceViewService = container.get<IWorkspaceViewService>(serviceIdentifier.WorkspaceView);
|
||||||
|
const deepLinkService = container.get<IDeepLinkService>(serviceIdentifier.DeepLink);
|
||||||
|
|
||||||
registerProxy(authService, AuthenticationServiceIPCDescriptor);
|
registerProxy(authService, AuthenticationServiceIPCDescriptor);
|
||||||
registerProxy(contextService, ContextServiceIPCDescriptor);
|
registerProxy(contextService, ContextServiceIPCDescriptor);
|
||||||
|
|
@ -125,4 +130,5 @@ export function bindServiceAndProxy(): void {
|
||||||
registerProxy(windowService, WindowServiceIPCDescriptor);
|
registerProxy(windowService, WindowServiceIPCDescriptor);
|
||||||
registerProxy(workspaceService, WorkspaceServiceIPCDescriptor);
|
registerProxy(workspaceService, WorkspaceServiceIPCDescriptor);
|
||||||
registerProxy(workspaceViewService, WorkspaceViewServiceIPCDescriptor);
|
registerProxy(workspaceViewService, WorkspaceViewServiceIPCDescriptor);
|
||||||
|
registerProxy(deepLinkService, DeepLinkServiceIPCDescriptor);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,12 @@ import type { IPagesService } from '@services/pages/interface';
|
||||||
import type { IPreferenceService } from '@services/preferences/interface';
|
import type { IPreferenceService } from '@services/preferences/interface';
|
||||||
import serviceIdentifier from '@services/serviceIdentifier';
|
import serviceIdentifier from '@services/serviceIdentifier';
|
||||||
import { ISyncService } from '@services/sync/interface';
|
import { ISyncService } from '@services/sync/interface';
|
||||||
import type { IUpdaterService } from '@services/updater/interface';
|
|
||||||
import type { IViewService } from '@services/view/interface';
|
import type { IViewService } from '@services/view/interface';
|
||||||
import type { IWikiService } from '@services/wiki/interface';
|
import type { IWikiService } from '@services/wiki/interface';
|
||||||
import type { IWikiGitWorkspaceService } from '@services/wikiGitWorkspace/interface';
|
import type { IWikiGitWorkspaceService } from '@services/wikiGitWorkspace/interface';
|
||||||
import type { IWindowService } from '@services/windows/interface';
|
import type { IWindowService } from '@services/windows/interface';
|
||||||
import { WindowNames } from '@services/windows/WindowProperties';
|
import { WindowNames } from '@services/windows/WindowProperties';
|
||||||
import { getWorkspaceMenuTemplate, openWorkspaceTagTiddler } from '@services/workspaces/getWorkspaceMenuTemplate';
|
import { getWorkspaceMenuTemplate } from '@services/workspaces/getWorkspaceMenuTemplate';
|
||||||
import type { IWorkspaceService } from '@services/workspaces/interface';
|
import type { IWorkspaceService } from '@services/workspaces/interface';
|
||||||
import type { IWorkspaceViewService } from '@services/workspacesView/interface';
|
import type { IWorkspaceViewService } from '@services/workspacesView/interface';
|
||||||
import { app, ContextMenuParams, Menu, MenuItem, MenuItemConstructorOptions, shell, WebContents } from 'electron';
|
import { app, ContextMenuParams, Menu, MenuItem, MenuItemConstructorOptions, shell, WebContents } from 'electron';
|
||||||
|
|
@ -51,9 +50,6 @@ export class MenuService implements IMenuService {
|
||||||
@lazyInject(serviceIdentifier.Preference)
|
@lazyInject(serviceIdentifier.Preference)
|
||||||
private readonly preferenceService!: IPreferenceService;
|
private readonly preferenceService!: IPreferenceService;
|
||||||
|
|
||||||
@lazyInject(serviceIdentifier.Updater)
|
|
||||||
private readonly updaterService!: IUpdaterService;
|
|
||||||
|
|
||||||
@lazyInject(serviceIdentifier.View)
|
@lazyInject(serviceIdentifier.View)
|
||||||
private readonly viewService!: IViewService;
|
private readonly viewService!: IViewService;
|
||||||
|
|
||||||
|
|
@ -384,7 +380,7 @@ export class MenuService implements IMenuService {
|
||||||
tagName: workspace.tagName ?? (workspace.isSubWiki ? workspace.name : `${workspace.name} ${i18n.t('WorkspaceSelector.DefaultTiddlers')}`),
|
tagName: workspace.tagName ?? (workspace.isSubWiki ? workspace.name : `${workspace.name} ${i18n.t('WorkspaceSelector.DefaultTiddlers')}`),
|
||||||
}),
|
}),
|
||||||
click: async () => {
|
click: async () => {
|
||||||
await openWorkspaceTagTiddler(workspace, services);
|
await this.workspaceService.openWorkspaceTiddler(workspace);
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -18,4 +18,5 @@ export default {
|
||||||
Window: Symbol.for('Window'),
|
Window: Symbol.for('Window'),
|
||||||
Workspace: Symbol.for('Workspace'),
|
Workspace: Symbol.for('Workspace'),
|
||||||
WorkspaceView: Symbol.for('WorkspaceView'),
|
WorkspaceView: Symbol.for('WorkspaceView'),
|
||||||
|
DeepLink: Symbol.for('DeepLinkService'),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||||
import { WikiChannel } from '@/constants/channels';
|
|
||||||
import { getDefaultHTTPServerIP } from '@/constants/urls';
|
import { getDefaultHTTPServerIP } from '@/constants/urls';
|
||||||
import type { IAuthenticationService } from '@services/auth/interface';
|
import type { IAuthenticationService } from '@services/auth/interface';
|
||||||
import { IContextService } from '@services/context/interface';
|
import { IContextService } from '@services/context/interface';
|
||||||
import { IGitService } from '@services/git/interface';
|
import { IGitService } from '@services/git/interface';
|
||||||
import type { INativeService } from '@services/native/interface';
|
import type { INativeService } from '@services/native/interface';
|
||||||
import { IPagesService, PageType } from '@services/pages/interface';
|
import { IPagesService } from '@services/pages/interface';
|
||||||
import { ISyncService } from '@services/sync/interface';
|
import { ISyncService } from '@services/sync/interface';
|
||||||
import { SupportedStorageServices } from '@services/types';
|
import { SupportedStorageServices } from '@services/types';
|
||||||
import type { IViewService } from '@services/view/interface';
|
import type { IViewService } from '@services/view/interface';
|
||||||
|
|
@ -30,7 +29,7 @@ interface IWorkspaceMenuRequiredServices {
|
||||||
wiki: Pick<IWikiService, 'wikiOperationInBrowser' | 'wikiOperationInServer'>;
|
wiki: Pick<IWikiService, 'wikiOperationInBrowser' | 'wikiOperationInServer'>;
|
||||||
wikiGitWorkspace: Pick<IWikiGitWorkspaceService, 'removeWorkspace'>;
|
wikiGitWorkspace: Pick<IWikiGitWorkspaceService, 'removeWorkspace'>;
|
||||||
window: Pick<IWindowService, 'open'>;
|
window: Pick<IWindowService, 'open'>;
|
||||||
workspace: Pick<IWorkspaceService, 'getActiveWorkspace' | 'getSubWorkspacesAsList'>;
|
workspace: Pick<IWorkspaceService, 'getActiveWorkspace' | 'getSubWorkspacesAsList' | 'openWorkspaceTiddler'>;
|
||||||
workspaceView: Pick<
|
workspaceView: Pick<
|
||||||
IWorkspaceViewService,
|
IWorkspaceViewService,
|
||||||
| 'wakeUpWorkspaceView'
|
| 'wakeUpWorkspaceView'
|
||||||
|
|
@ -43,33 +42,6 @@ interface IWorkspaceMenuRequiredServices {
|
||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function openWorkspaceTagTiddler(workspace: IWorkspace, service: IWorkspaceMenuRequiredServices): Promise<void> {
|
|
||||||
const { id: idToActive, isSubWiki, tagName, mainWikiID } = workspace;
|
|
||||||
// switch to workspace page
|
|
||||||
const [oldActiveWorkspace] = await Promise.all([
|
|
||||||
service.workspace.getActiveWorkspace(),
|
|
||||||
service.pages.setActivePage(PageType.wiki),
|
|
||||||
service.native.log('debug', 'openWorkspaceTagTiddler', { workspace }),
|
|
||||||
]);
|
|
||||||
// if is a new main workspace, active its browser view first
|
|
||||||
if (!isSubWiki && idToActive) {
|
|
||||||
if (oldActiveWorkspace?.id !== idToActive) {
|
|
||||||
await service.workspaceView.setActiveWorkspaceView(idToActive);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// is not a new main workspace
|
|
||||||
// open tiddler in the active view
|
|
||||||
if (isSubWiki && mainWikiID) {
|
|
||||||
if (oldActiveWorkspace?.id !== mainWikiID) {
|
|
||||||
await service.workspaceView.setActiveWorkspaceView(mainWikiID);
|
|
||||||
}
|
|
||||||
if (tagName) {
|
|
||||||
await service.wiki.wikiOperationInBrowser(WikiChannel.openTiddler, mainWikiID, [tagName]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getWorkspaceMenuTemplate(
|
export async function getWorkspaceMenuTemplate(
|
||||||
workspace: IWorkspace,
|
workspace: IWorkspace,
|
||||||
t: TFunction<[_DefaultNamespace, ...Array<Exclude<FlatNamespace, _DefaultNamespace>>]>,
|
t: TFunction<[_DefaultNamespace, ...Array<Exclude<FlatNamespace, _DefaultNamespace>>]>,
|
||||||
|
|
@ -83,7 +55,7 @@ export async function getWorkspaceMenuTemplate(
|
||||||
tagName: tagName ?? (isSubWiki ? name : `${name} ${t('WorkspaceSelector.DefaultTiddlers')}`),
|
tagName: tagName ?? (isSubWiki ? name : `${name} ${t('WorkspaceSelector.DefaultTiddlers')}`),
|
||||||
}),
|
}),
|
||||||
click: async () => {
|
click: async () => {
|
||||||
await openWorkspaceTagTiddler(workspace, service);
|
await service.workspace.openWorkspaceTiddler(workspace);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import path from 'path';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { WikiChannel } from '@/constants/channels';
|
||||||
import { DELAY_MENU_REGISTER } from '@/constants/parameters';
|
import { DELAY_MENU_REGISTER } from '@/constants/parameters';
|
||||||
import { getDefaultTidGiUrl } from '@/constants/urls';
|
import { getDefaultTidGiUrl } from '@/constants/urls';
|
||||||
import { IAuthenticationService } from '@services/auth/interface';
|
import { IAuthenticationService } from '@services/auth/interface';
|
||||||
|
|
@ -20,7 +21,7 @@ import { IDatabaseService } from '@services/database/interface';
|
||||||
import { i18n } from '@services/libs/i18n';
|
import { i18n } from '@services/libs/i18n';
|
||||||
import { logger } from '@services/libs/log';
|
import { logger } from '@services/libs/log';
|
||||||
import type { IMenuService } from '@services/menu/interface';
|
import type { IMenuService } from '@services/menu/interface';
|
||||||
import { IPagesService } from '@services/pages/interface';
|
import { IPagesService, PageType } from '@services/pages/interface';
|
||||||
import serviceIdentifier from '@services/serviceIdentifier';
|
import serviceIdentifier from '@services/serviceIdentifier';
|
||||||
import { SupportedStorageServices } from '@services/types';
|
import { SupportedStorageServices } from '@services/types';
|
||||||
import type { IViewService } from '@services/view/interface';
|
import type { IViewService } from '@services/view/interface';
|
||||||
|
|
@ -167,7 +168,13 @@ export class Workspace implements IWorkspaceService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getSync(id: string): IWorkspace | undefined {
|
private getSync(id: string): IWorkspace | undefined {
|
||||||
return this.getWorkspacesSync()[id];
|
const workspaces = this.getWorkspacesSync();
|
||||||
|
if (id in workspaces) {
|
||||||
|
return workspaces[id];
|
||||||
|
}
|
||||||
|
// Try find with lowercased key. sometimes user will use id that is all lowercased. Because tidgi:// url is somehow lowercased.
|
||||||
|
const foundKey = Object.keys(workspaces).find((key) => key.toLowerCase() === id.toLowerCase());
|
||||||
|
return foundKey ? workspaces[foundKey] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get$(id: string): Observable<IWorkspace | undefined> {
|
public get$(id: string): Observable<IWorkspace | undefined> {
|
||||||
|
|
@ -470,4 +477,31 @@ export class Workspace implements IWorkspaceService {
|
||||||
const workspaceMetaData = this.getMetaDataSync(id);
|
const workspaceMetaData = this.getMetaDataSync(id);
|
||||||
return typeof workspaceMetaData?.didFailLoadErrorMessage === 'string' && workspaceMetaData.didFailLoadErrorMessage.length > 0;
|
return typeof workspaceMetaData?.didFailLoadErrorMessage === 'string' && workspaceMetaData.didFailLoadErrorMessage.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async openWorkspaceTiddler(workspace: IWorkspace, title?: string): Promise<void> {
|
||||||
|
const { id: idToActive, isSubWiki, mainWikiID } = workspace;
|
||||||
|
const oldActiveWorkspace = await this.getActiveWorkspace();
|
||||||
|
await this.pagesService.setActivePage(PageType.wiki);
|
||||||
|
logger.log('debug', 'openWorkspaceTiddler', { workspace });
|
||||||
|
// If is main wiki, open the wiki, and open provided title, or simply switch to it if no title provided
|
||||||
|
if (!isSubWiki && idToActive) {
|
||||||
|
if (oldActiveWorkspace?.id !== idToActive) {
|
||||||
|
await this.workspaceViewService.setActiveWorkspaceView(idToActive);
|
||||||
|
}
|
||||||
|
if (title) {
|
||||||
|
await this.wikiService.wikiOperationInBrowser(WikiChannel.openTiddler, idToActive, [title]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If is sub wiki, open the main wiki first and open the tag or provided title
|
||||||
|
if (isSubWiki && mainWikiID) {
|
||||||
|
if (oldActiveWorkspace?.id !== mainWikiID) {
|
||||||
|
await this.workspaceViewService.setActiveWorkspaceView(mainWikiID);
|
||||||
|
}
|
||||||
|
const subWikiTag = title ?? workspace.tagName;
|
||||||
|
if (subWikiTag) {
|
||||||
|
await this.wikiService.wikiOperationInBrowser(WikiChannel.openTiddler, mainWikiID, [subWikiTag]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -203,6 +203,10 @@ export interface IWorkspaceService {
|
||||||
getWorkspaces(): Promise<Record<string, IWorkspace>>;
|
getWorkspaces(): Promise<Record<string, IWorkspace>>;
|
||||||
getWorkspacesAsList(): Promise<IWorkspace[]>;
|
getWorkspacesAsList(): Promise<IWorkspace[]>;
|
||||||
getWorkspacesWithMetadata(): IWorkspacesWithMetadata;
|
getWorkspacesWithMetadata(): IWorkspacesWithMetadata;
|
||||||
|
/**
|
||||||
|
* Open a tiddler in the workspace, open workspace's tag by default.
|
||||||
|
*/
|
||||||
|
openWorkspaceTiddler(workspace: IWorkspace, title?: string): Promise<void>;
|
||||||
remove(id: string): Promise<void>;
|
remove(id: string): Promise<void>;
|
||||||
removeWorkspacePicture(id: string): Promise<void>;
|
removeWorkspacePicture(id: string): Promise<void>;
|
||||||
set(id: string, workspace: IWorkspace, immediate?: boolean): Promise<void>;
|
set(id: string, workspace: IWorkspace, immediate?: boolean): Promise<void>;
|
||||||
|
|
@ -241,6 +245,7 @@ export const WorkspaceServiceIPCDescriptor = {
|
||||||
getWorkspaces: ProxyPropertyType.Function,
|
getWorkspaces: ProxyPropertyType.Function,
|
||||||
getWorkspacesAsList: ProxyPropertyType.Function,
|
getWorkspacesAsList: ProxyPropertyType.Function,
|
||||||
getWorkspacesWithMetadata: ProxyPropertyType.Function,
|
getWorkspacesWithMetadata: ProxyPropertyType.Function,
|
||||||
|
openWorkspaceTiddler: ProxyPropertyType.Function,
|
||||||
remove: ProxyPropertyType.Function,
|
remove: ProxyPropertyType.Function,
|
||||||
removeWorkspacePicture: ProxyPropertyType.Function,
|
removeWorkspacePicture: ProxyPropertyType.Function,
|
||||||
set: ProxyPropertyType.Function,
|
set: ProxyPropertyType.Function,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue