fix(core): add toDocSearchParams for better typeschecking doc search params (#11383)
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import type { SerializedXYWH } from '@blocksuite/global/gfx';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
export type DocMode = 'edgeless' | 'page';
|
export type DocMode = 'edgeless' | 'page';
|
||||||
@@ -26,6 +27,14 @@ export const AliasInfoSchema = z
|
|||||||
|
|
||||||
export type AliasInfo = z.infer<typeof AliasInfoSchema>;
|
export type AliasInfo = z.infer<typeof AliasInfoSchema>;
|
||||||
|
|
||||||
|
export const SerializedXYWHSchema = z.custom<SerializedXYWH>(val => {
|
||||||
|
if (typeof val !== 'string') return false;
|
||||||
|
const match = val.match(/^\[(\d+),(\d+),(\d+),(\d+)\]$/);
|
||||||
|
if (!match) return false;
|
||||||
|
// Ensure all numbers are valid
|
||||||
|
return match.slice(1).every(num => !isNaN(Number(num)));
|
||||||
|
}, 'Invalid XYWH format. Expected [number,number,number,number]');
|
||||||
|
|
||||||
export const ReferenceParamsSchema = z
|
export const ReferenceParamsSchema = z
|
||||||
.object({
|
.object({
|
||||||
mode: z.enum(DocModes),
|
mode: z.enum(DocModes),
|
||||||
@@ -33,6 +42,7 @@ export const ReferenceParamsSchema = z
|
|||||||
elementIds: z.string().array(),
|
elementIds: z.string().array(),
|
||||||
databaseId: z.string().optional(),
|
databaseId: z.string().optional(),
|
||||||
databaseRowId: z.string().optional(),
|
databaseRowId: z.string().optional(),
|
||||||
|
xywh: SerializedXYWHSchema.optional(),
|
||||||
})
|
})
|
||||||
.partial();
|
.partial();
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
DocLinksService,
|
DocLinksService,
|
||||||
type Link,
|
type Link,
|
||||||
} from '@affine/core/modules/doc-link';
|
} from '@affine/core/modules/doc-link';
|
||||||
import { toURLSearchParams } from '@affine/core/modules/navigation';
|
import { toDocSearchParams } from '@affine/core/modules/navigation/utils';
|
||||||
import { GlobalSessionStateService } from '@affine/core/modules/storage';
|
import { GlobalSessionStateService } from '@affine/core/modules/storage';
|
||||||
import { WorkbenchLink } from '@affine/core/modules/workbench';
|
import { WorkbenchLink } from '@affine/core/modules/workbench';
|
||||||
import {
|
import {
|
||||||
@@ -144,7 +144,7 @@ const usePreviewExtensions = () => {
|
|||||||
const pageId = data.pageId;
|
const pageId = data.pageId;
|
||||||
if (!pageId) return <span />;
|
if (!pageId) return <span />;
|
||||||
|
|
||||||
const params = toURLSearchParams(data.params);
|
const params = toDocSearchParams(data.params);
|
||||||
|
|
||||||
if (workspaceService.workspace.openOptions.isSharedMode) {
|
if (workspaceService.workspace.openOptions.isSharedMode) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { EditorService } from '@affine/core/modules/editor';
|
|||||||
import { EditorSettingService } from '@affine/core/modules/editor-setting';
|
import { EditorSettingService } from '@affine/core/modules/editor-setting';
|
||||||
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||||
import { JournalService } from '@affine/core/modules/journal';
|
import { JournalService } from '@affine/core/modules/journal';
|
||||||
import { toURLSearchParams } from '@affine/core/modules/navigation';
|
import { toDocSearchParams } from '@affine/core/modules/navigation';
|
||||||
import { useInsidePeekView } from '@affine/core/modules/peek-view';
|
import { useInsidePeekView } from '@affine/core/modules/peek-view';
|
||||||
import { PeekViewService } from '@affine/core/modules/peek-view/services/peek-view';
|
import { PeekViewService } from '@affine/core/modules/peek-view/services/peek-view';
|
||||||
import { MemberSearchService } from '@affine/core/modules/permissions';
|
import { MemberSearchService } from '@affine/core/modules/permissions';
|
||||||
@@ -121,7 +121,7 @@ const usePatchSpecs = (mode: DocMode) => {
|
|||||||
|
|
||||||
// title alias
|
// title alias
|
||||||
const title = data.title;
|
const title = data.title;
|
||||||
const params = toURLSearchParams(data.params);
|
const params = toDocSearchParams(data.params);
|
||||||
|
|
||||||
if (workspaceService.workspace.openOptions.isSharedMode) {
|
if (workspaceService.workspace.openOptions.isSharedMode) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { notify } from '@affine/component';
|
import { notify } from '@affine/component';
|
||||||
import { ServerService } from '@affine/core/modules/cloud';
|
import { ServerService } from '@affine/core/modules/cloud';
|
||||||
import { toURLSearchParams } from '@affine/core/modules/navigation';
|
import { toDocSearchParams } from '@affine/core/modules/navigation';
|
||||||
import { copyTextToClipboard } from '@affine/core/utils/clipboard';
|
import { copyTextToClipboard } from '@affine/core/utils/clipboard';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
|
import type { SerializedXYWH } from '@blocksuite/affine/global/gfx';
|
||||||
import { type DocMode } from '@blocksuite/affine/model';
|
import { type DocMode } from '@blocksuite/affine/model';
|
||||||
import {
|
import {
|
||||||
getBlockSelectionsCommand,
|
getBlockSelectionsCommand,
|
||||||
@@ -25,7 +26,7 @@ export type UseSharingUrl = {
|
|||||||
mode?: DocMode;
|
mode?: DocMode;
|
||||||
blockIds?: string[];
|
blockIds?: string[];
|
||||||
elementIds?: string[];
|
elementIds?: string[];
|
||||||
xywh?: string; // not needed currently
|
xywh?: SerializedXYWH; // not needed currently
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,7 +45,7 @@ export const generateUrl = ({
|
|||||||
}: UseSharingUrl & { baseUrl: string }) => {
|
}: UseSharingUrl & { baseUrl: string }) => {
|
||||||
try {
|
try {
|
||||||
const url = new URL(`/workspace/${workspaceId}/${pageId}`, baseUrl);
|
const url = new URL(`/workspace/${workspaceId}/${pageId}`, baseUrl);
|
||||||
const search = toURLSearchParams({ mode, blockIds, elementIds, xywh });
|
const search = toDocSearchParams({ mode, blockIds, elementIds, xywh });
|
||||||
if (search?.size) url.search = search.toString();
|
if (search?.size) url.search = search.toString();
|
||||||
return url.toString();
|
return url.toString();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { SettingTab } from '@affine/core/modules/dialogs/constant';
|
import type { SettingTab } from '@affine/core/modules/dialogs/constant';
|
||||||
import { toURLSearchParams } from '@affine/core/modules/navigation';
|
import { toDocSearchParams } from '@affine/core/modules/navigation';
|
||||||
import { getOpenUrlInDesktopAppLink } from '@affine/core/modules/open-in-app';
|
import { getOpenUrlInDesktopAppLink } from '@affine/core/modules/open-in-app';
|
||||||
import type { DocMode } from '@blocksuite/affine/model';
|
import type { DocMode } from '@blocksuite/affine/model';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
@@ -49,7 +49,7 @@ export function useNavigateHelper() {
|
|||||||
elementIds?: string[],
|
elementIds?: string[],
|
||||||
logic: RouteLogic = RouteLogic.PUSH
|
logic: RouteLogic = RouteLogic.PUSH
|
||||||
) => {
|
) => {
|
||||||
const search = toURLSearchParams({
|
const search = toDocSearchParams({
|
||||||
mode,
|
mode,
|
||||||
blockIds,
|
blockIds,
|
||||||
elementIds,
|
elementIds,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { toURLSearchParams } from '@affine/core/modules/navigation';
|
import { toDocSearchParams } from '@affine/core/modules/navigation';
|
||||||
import type { IndexerSyncState } from '@affine/nbstore';
|
import type { IndexerSyncState } from '@affine/nbstore';
|
||||||
import type { ReferenceParams } from '@blocksuite/affine/model';
|
import type { ReferenceParams } from '@blocksuite/affine/model';
|
||||||
import { fromPromise, LiveData, Service } from '@toeverything/infra';
|
import { fromPromise, LiveData, Service } from '@toeverything/infra';
|
||||||
@@ -193,7 +193,7 @@ export class DocsSearchService extends Service {
|
|||||||
docId: doc.id,
|
docId: doc.id,
|
||||||
params: isEmpty(params)
|
params: isEmpty(params)
|
||||||
? undefined
|
? undefined
|
||||||
: toURLSearchParams(params),
|
: toDocSearchParams(params),
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter(ref => !!ref);
|
.filter(ref => !!ref);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ export { Navigator } from './entities/navigator';
|
|||||||
export {
|
export {
|
||||||
resolveLinkToDoc,
|
resolveLinkToDoc,
|
||||||
resolveRouteLinkMeta,
|
resolveRouteLinkMeta,
|
||||||
|
toDocSearchParams,
|
||||||
toURLSearchParams,
|
toURLSearchParams,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
export { NavigationButtons } from './view/navigation-buttons';
|
export { NavigationButtons } from './view/navigation-buttons';
|
||||||
|
|||||||
@@ -189,3 +189,12 @@ export function toURLSearchParams(
|
|||||||
.map(([k, v]) => [k, Array.isArray(v) ? v.join(',') : v])
|
.map(([k, v]) => [k, Array.isArray(v) ? v.join(',') : v])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// a type safe version of toURLSearchParams for doc search params
|
||||||
|
export function toDocSearchParams(
|
||||||
|
params?: ReferenceParams & {
|
||||||
|
refreshKey?: string;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
return toURLSearchParams(params);
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import {
|
|||||||
import { ServerService } from '../../cloud';
|
import { ServerService } from '../../cloud';
|
||||||
import { WorkspaceDialogService } from '../../dialogs';
|
import { WorkspaceDialogService } from '../../dialogs';
|
||||||
import { DocsService } from '../../doc/services/docs';
|
import { DocsService } from '../../doc/services/docs';
|
||||||
import { toURLSearchParams } from '../../navigation';
|
import { toDocSearchParams } from '../../navigation';
|
||||||
import { WorkbenchService } from '../../workbench';
|
import { WorkbenchService } from '../../workbench';
|
||||||
import type {
|
import type {
|
||||||
AttachmentPeekViewInfo,
|
AttachmentPeekViewInfo,
|
||||||
@@ -153,7 +153,7 @@ export const DocPeekViewControls = ({
|
|||||||
name: t['com.affine.peek-view-controls.copy-link'](),
|
name: t['com.affine.peek-view-controls.copy-link'](),
|
||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
const preferredMode = docsService.list.getPrimaryMode(docRef.docId);
|
const preferredMode = docsService.list.getPrimaryMode(docRef.docId);
|
||||||
const search = toURLSearchParams({
|
const search = toDocSearchParams({
|
||||||
mode: docRef.mode || preferredMode,
|
mode: docRef.mode || preferredMode,
|
||||||
blockIds: docRef.blockIds,
|
blockIds: docRef.blockIds,
|
||||||
elementIds: docRef.elementIds,
|
elementIds: docRef.elementIds,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { toURLSearchParams } from '@affine/core/modules/navigation/utils';
|
import { toDocSearchParams } from '@affine/core/modules/navigation/utils';
|
||||||
import { Unreachable } from '@affine/env/constant';
|
import { Unreachable } from '@affine/env/constant';
|
||||||
import type { ReferenceParams } from '@blocksuite/affine/model';
|
import type { ReferenceParams } from '@blocksuite/affine/model';
|
||||||
import { Entity, LiveData } from '@toeverything/infra';
|
import { Entity, LiveData } from '@toeverything/infra';
|
||||||
@@ -156,10 +156,11 @@ export class Workbench extends Entity {
|
|||||||
openDoc(
|
openDoc(
|
||||||
id:
|
id:
|
||||||
| string
|
| string
|
||||||
| ({ docId: string } & (
|
| ({
|
||||||
| ReferenceParams
|
docId: string;
|
||||||
| Record<string, string | undefined>
|
refreshKey?: string;
|
||||||
)),
|
fromTab?: string;
|
||||||
|
} & ReferenceParams),
|
||||||
options?: WorkbenchOpenOptions
|
options?: WorkbenchOpenOptions
|
||||||
) {
|
) {
|
||||||
const isString = typeof id === 'string';
|
const isString = typeof id === 'string';
|
||||||
@@ -167,7 +168,7 @@ export class Workbench extends Entity {
|
|||||||
|
|
||||||
let query = '';
|
let query = '';
|
||||||
if (!isString) {
|
if (!isString) {
|
||||||
const search = toURLSearchParams(omit(id, ['docId']));
|
const search = toDocSearchParams(omit(id, ['docId']));
|
||||||
if (search?.size) {
|
if (search?.size) {
|
||||||
query = `?${search.toString()}`;
|
query = `?${search.toString()}`;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user