mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
feat(core): support block links on Bi-Directional Links (#8169)
Clsoes [AF-1348](https://linear.app/affine-design/issue/AF-1348/修复-bi-directional-links-里面的链接地址) * Links to the current document should be ignored on `Backlinks` * Links to the current document should be ignored on `Outgoing links` https://github.com/user-attachments/assets/dbc43cea-5aca-4c6f-886a-356e3a91c1f1
This commit is contained in:
@@ -35,9 +35,8 @@ export interface PageReferenceRendererOptions {
|
|||||||
journalHelper: ReturnType<typeof useJournalHelper>;
|
journalHelper: ReturnType<typeof useJournalHelper>;
|
||||||
t: ReturnType<typeof useI18n>;
|
t: ReturnType<typeof useI18n>;
|
||||||
docMode?: DocMode;
|
docMode?: DocMode;
|
||||||
// linking doc with block or element
|
// Link to block or element
|
||||||
blockIds?: string[];
|
linkToNode?: boolean;
|
||||||
elementIds?: string[];
|
|
||||||
}
|
}
|
||||||
// use a function to be rendered in the lit renderer
|
// use a function to be rendered in the lit renderer
|
||||||
export function pageReferenceRenderer({
|
export function pageReferenceRenderer({
|
||||||
@@ -46,8 +45,7 @@ export function pageReferenceRenderer({
|
|||||||
journalHelper,
|
journalHelper,
|
||||||
t,
|
t,
|
||||||
docMode,
|
docMode,
|
||||||
blockIds,
|
linkToNode = false,
|
||||||
elementIds,
|
|
||||||
}: PageReferenceRendererOptions) {
|
}: PageReferenceRendererOptions) {
|
||||||
const { isPageJournal, getLocalizedJournalDateString } = journalHelper;
|
const { isPageJournal, getLocalizedJournalDateString } = journalHelper;
|
||||||
const referencedPage = pageMetaHelper.getDocMeta(pageId);
|
const referencedPage = pageMetaHelper.getDocMeta(pageId);
|
||||||
@@ -62,7 +60,7 @@ export function pageReferenceRenderer({
|
|||||||
} else {
|
} else {
|
||||||
Icon = LinkedPageIcon;
|
Icon = LinkedPageIcon;
|
||||||
}
|
}
|
||||||
if (blockIds?.length || elementIds?.length) {
|
if (linkToNode) {
|
||||||
Icon = BlockLinkIcon;
|
Icon = BlockLinkIcon;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,33 +87,33 @@ export function AffinePageReference({
|
|||||||
docCollection,
|
docCollection,
|
||||||
wrapper: Wrapper,
|
wrapper: Wrapper,
|
||||||
mode = 'page',
|
mode = 'page',
|
||||||
params = {},
|
params,
|
||||||
}: {
|
}: {
|
||||||
pageId: string;
|
pageId: string;
|
||||||
docCollection: DocCollection;
|
docCollection: DocCollection;
|
||||||
wrapper?: React.ComponentType<PropsWithChildren>;
|
wrapper?: React.ComponentType<PropsWithChildren>;
|
||||||
mode?: DocMode;
|
mode?: DocMode;
|
||||||
params?: {
|
params?: URLSearchParams;
|
||||||
mode?: DocMode;
|
|
||||||
blockIds?: string[];
|
|
||||||
elementIds?: string[];
|
|
||||||
};
|
|
||||||
}) {
|
}) {
|
||||||
const pageMetaHelper = useDocMetaHelper(docCollection);
|
const pageMetaHelper = useDocMetaHelper(docCollection);
|
||||||
const journalHelper = useJournalHelper(docCollection);
|
const journalHelper = useJournalHelper(docCollection);
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|
||||||
const { mode: linkedWithMode, blockIds, elementIds } = params;
|
let linkWithMode: DocMode | null = null;
|
||||||
|
let linkToNode = false;
|
||||||
|
if (params) {
|
||||||
|
linkWithMode = params.get('mode') as DocMode;
|
||||||
|
linkToNode = params.has('blockIds') || params.has('elementIds');
|
||||||
|
}
|
||||||
|
|
||||||
const el = pageReferenceRenderer({
|
const el = pageReferenceRenderer({
|
||||||
docMode: linkedWithMode ?? mode,
|
docMode: linkWithMode ?? mode,
|
||||||
pageId,
|
pageId,
|
||||||
pageMetaHelper,
|
pageMetaHelper,
|
||||||
journalHelper,
|
journalHelper,
|
||||||
docCollection,
|
docCollection,
|
||||||
t,
|
t,
|
||||||
blockIds,
|
linkToNode,
|
||||||
elementIds,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const ref = useRef<HTMLAnchorElement>(null);
|
const ref = useRef<HTMLAnchorElement>(null);
|
||||||
@@ -154,20 +152,11 @@ export function AffinePageReference({
|
|||||||
|
|
||||||
const query = useMemo(() => {
|
const query = useMemo(() => {
|
||||||
// A block/element reference link
|
// A block/element reference link
|
||||||
const search = new URLSearchParams();
|
let str = params?.toString() ?? '';
|
||||||
if (linkedWithMode) {
|
if (str.length) str += '&';
|
||||||
search.set('mode', linkedWithMode);
|
str += `refreshKey=${refreshKey}`;
|
||||||
}
|
return '?' + str;
|
||||||
if (blockIds?.length) {
|
}, [params, refreshKey]);
|
||||||
search.set('blockIds', blockIds.join(','));
|
|
||||||
}
|
|
||||||
if (elementIds?.length) {
|
|
||||||
search.set('elementIds', elementIds.join(','));
|
|
||||||
}
|
|
||||||
search.set('refreshKey', refreshKey);
|
|
||||||
|
|
||||||
return search.size > 0 ? `?${search.toString()}` : '';
|
|
||||||
}, [blockIds, elementIds, linkedWithMode, refreshKey]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WorkbenchLink
|
<WorkbenchLink
|
||||||
|
|||||||
@@ -63,10 +63,14 @@ export const BiDirectionalLinkPanel = () => {
|
|||||||
{t['com.affine.page-properties.outgoing-links']()} ·{' '}
|
{t['com.affine.page-properties.outgoing-links']()} ·{' '}
|
||||||
{links.length}
|
{links.length}
|
||||||
</div>
|
</div>
|
||||||
{links.map(link => (
|
{links.map((link, i) => (
|
||||||
<div key={link.docId} className={styles.link}>
|
<div
|
||||||
|
key={`${link.docId}-${link.params?.toString()}-${i}`}
|
||||||
|
className={styles.link}
|
||||||
|
>
|
||||||
<AffinePageReference
|
<AffinePageReference
|
||||||
pageId={link.docId}
|
pageId={link.docId}
|
||||||
|
params={link.params}
|
||||||
docCollection={workspaceService.workspace.docCollection}
|
docCollection={workspaceService.workspace.docCollection}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { useJournalInfoHelper } from '@affine/core/hooks/use-journal';
|
|||||||
import { EditorService } from '@affine/core/modules/editor';
|
import { EditorService } from '@affine/core/modules/editor';
|
||||||
import { EditorSettingService } from '@affine/core/modules/editor-settting';
|
import { EditorSettingService } from '@affine/core/modules/editor-settting';
|
||||||
import { PeekViewService } from '@affine/core/modules/peek-view';
|
import { PeekViewService } from '@affine/core/modules/peek-view';
|
||||||
|
import { toURLSearchParams } from '@affine/core/utils';
|
||||||
import type { DocMode } from '@blocksuite/blocks';
|
import type { DocMode } from '@blocksuite/blocks';
|
||||||
import { DocTitle, EdgelessEditor, PageEditor } from '@blocksuite/presets';
|
import { DocTitle, EdgelessEditor, PageEditor } from '@blocksuite/presets';
|
||||||
import type { Doc } from '@blocksuite/store';
|
import type { Doc } from '@blocksuite/store';
|
||||||
@@ -90,12 +91,14 @@ const usePatchSpecs = (page: Doc, shared: boolean, mode: DocMode) => {
|
|||||||
const pageId = data.pageId;
|
const pageId = data.pageId;
|
||||||
if (!pageId) return <span />;
|
if (!pageId) return <span />;
|
||||||
|
|
||||||
|
const params = toURLSearchParams(data.params);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AffinePageReference
|
<AffinePageReference
|
||||||
docCollection={page.collection}
|
docCollection={page.collection}
|
||||||
pageId={pageId}
|
pageId={pageId}
|
||||||
mode={mode}
|
mode={mode}
|
||||||
params={data.params}
|
params={params}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type { DocsSearchService } from '../../docs-search';
|
|||||||
export interface Link {
|
export interface Link {
|
||||||
docId: string;
|
docId: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
params?: URLSearchParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DocLinks extends Entity {
|
export class DocLinks extends Entity {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export class DocsIndexer extends Entity {
|
|||||||
/**
|
/**
|
||||||
* increase this number to re-index all docs
|
* increase this number to re-index all docs
|
||||||
*/
|
*/
|
||||||
static INDEXER_VERSION = 1;
|
static INDEXER_VERSION = 2;
|
||||||
|
|
||||||
private readonly jobQueue: JobQueue<IndexerJobPayload> =
|
private readonly jobQueue: JobQueue<IndexerJobPayload> =
|
||||||
new IndexedDBJobQueue<IndexerJobPayload>(
|
new IndexedDBJobQueue<IndexerJobPayload>(
|
||||||
|
|||||||
@@ -11,8 +11,13 @@ export const blockIndexSchema = defineSchema({
|
|||||||
blockId: 'String',
|
blockId: 'String',
|
||||||
content: 'FullText',
|
content: 'FullText',
|
||||||
flavour: 'String',
|
flavour: 'String',
|
||||||
ref: 'String',
|
|
||||||
blob: 'String',
|
blob: 'String',
|
||||||
|
// reference doc id
|
||||||
|
// ['xxx','yyy']
|
||||||
|
refDocId: 'String',
|
||||||
|
// reference info
|
||||||
|
// [{"docId":"xxx","mode":"page","blockIds":["gt5Yfq1maYvgNgpi13rIq"]},{"docId":"yyy","mode":"edgeless","blockIds":["k5prpOlDF-9CzfatmO0W7"]}]
|
||||||
|
ref: 'String',
|
||||||
});
|
});
|
||||||
|
|
||||||
export type BlockIndexSchema = typeof blockIndexSchema;
|
export type BlockIndexSchema = typeof blockIndexSchema;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { toURLSearchParams } from '@affine/core/utils';
|
||||||
import type { WorkspaceService } from '@toeverything/infra';
|
import type { WorkspaceService } from '@toeverything/infra';
|
||||||
import {
|
import {
|
||||||
fromPromise,
|
fromPromise,
|
||||||
@@ -5,6 +6,7 @@ import {
|
|||||||
Service,
|
Service,
|
||||||
WorkspaceEngineBeforeStart,
|
WorkspaceEngineBeforeStart,
|
||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
|
import { isEmpty, omit } from 'lodash-es';
|
||||||
import { type Observable, switchMap } from 'rxjs';
|
import { type Observable, switchMap } from 'rxjs';
|
||||||
|
|
||||||
import { DocsIndexer } from '../entities/docs-indexer';
|
import { DocsIndexer } from '../entities/docs-indexer';
|
||||||
@@ -250,36 +252,64 @@ export class DocsSearchService extends Service {
|
|||||||
field: 'docId',
|
field: 'docId',
|
||||||
match: docId,
|
match: docId,
|
||||||
},
|
},
|
||||||
|
// Ignore if it is a link to the current document.
|
||||||
|
{
|
||||||
|
type: 'boolean',
|
||||||
|
occur: 'must_not',
|
||||||
|
queries: [
|
||||||
|
{
|
||||||
|
type: 'match',
|
||||||
|
field: 'refDocId',
|
||||||
|
match: docId,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'exists',
|
type: 'exists',
|
||||||
field: 'ref',
|
field: 'refDocId',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fields: ['ref'],
|
fields: ['refDocId', 'ref'],
|
||||||
pagination: {
|
pagination: {
|
||||||
limit: 100,
|
limit: 100,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const docIds = new Set(
|
const refs: {
|
||||||
nodes.flatMap(node => {
|
docId: string;
|
||||||
const refs = node.fields.ref;
|
mode?: string;
|
||||||
return typeof refs === 'string' ? [refs] : refs;
|
blockIds?: string[];
|
||||||
})
|
elementIds?: string[];
|
||||||
|
}[] = nodes.flatMap(node => {
|
||||||
|
const { ref } = node.fields;
|
||||||
|
return typeof ref === 'string'
|
||||||
|
? [JSON.parse(ref)]
|
||||||
|
: ref.map(item => JSON.parse(item));
|
||||||
|
});
|
||||||
|
|
||||||
|
const docData = await this.indexer.docIndex.getAll(
|
||||||
|
Array.from(new Set(refs.map(ref => ref.docId)))
|
||||||
);
|
);
|
||||||
|
|
||||||
const docData = await this.indexer.docIndex.getAll(Array.from(docIds));
|
return refs
|
||||||
|
.flatMap(ref => {
|
||||||
|
const doc = docData.find(doc => doc.id === ref.docId);
|
||||||
|
if (!doc) return null;
|
||||||
|
|
||||||
return docData.map(doc => {
|
const titles = doc.get('title');
|
||||||
const title = doc.get('title');
|
const title = (Array.isArray(titles) ? titles[0] : titles) ?? '';
|
||||||
return {
|
const params = omit(ref, ['docId']);
|
||||||
docId: doc.id,
|
|
||||||
title: title ? (typeof title === 'string' ? title : title[0]) : '',
|
return {
|
||||||
};
|
title,
|
||||||
});
|
docId: doc.id,
|
||||||
|
params: isEmpty(params) ? undefined : toURLSearchParams(params),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter(ref => !!ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
watchRefsFrom(docId: string) {
|
watchRefsFrom(docId: string) {
|
||||||
@@ -294,14 +324,26 @@ export class DocsSearchService extends Service {
|
|||||||
field: 'docId',
|
field: 'docId',
|
||||||
match: docId,
|
match: docId,
|
||||||
},
|
},
|
||||||
|
// Ignore if it is a link to the current document.
|
||||||
|
{
|
||||||
|
type: 'boolean',
|
||||||
|
occur: 'must_not',
|
||||||
|
queries: [
|
||||||
|
{
|
||||||
|
type: 'match',
|
||||||
|
field: 'refDocId',
|
||||||
|
match: docId,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'exists',
|
type: 'exists',
|
||||||
field: 'ref',
|
field: 'refDocId',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fields: ['ref'],
|
fields: ['refDocId', 'ref'],
|
||||||
pagination: {
|
pagination: {
|
||||||
limit: 100,
|
limit: 100,
|
||||||
},
|
},
|
||||||
@@ -310,28 +352,41 @@ export class DocsSearchService extends Service {
|
|||||||
.pipe(
|
.pipe(
|
||||||
switchMap(({ nodes }) => {
|
switchMap(({ nodes }) => {
|
||||||
return fromPromise(async () => {
|
return fromPromise(async () => {
|
||||||
const docIds = new Set(
|
const refs: {
|
||||||
nodes.flatMap(node => {
|
docId: string;
|
||||||
const refs = node.fields.ref;
|
mode?: string;
|
||||||
return typeof refs === 'string' ? [refs] : refs;
|
blockIds?: string[];
|
||||||
})
|
elementIds?: string[];
|
||||||
);
|
}[] = nodes.flatMap(node => {
|
||||||
|
const { ref } = node.fields;
|
||||||
|
return typeof ref === 'string'
|
||||||
|
? [JSON.parse(ref)]
|
||||||
|
: ref.map(item => JSON.parse(item));
|
||||||
|
});
|
||||||
|
|
||||||
const docData = await this.indexer.docIndex.getAll(
|
const docData = await this.indexer.docIndex.getAll(
|
||||||
Array.from(docIds)
|
Array.from(new Set(refs.map(ref => ref.docId)))
|
||||||
);
|
);
|
||||||
|
|
||||||
return docData.map(doc => {
|
return refs
|
||||||
const title = doc.get('title');
|
.flatMap(ref => {
|
||||||
return {
|
const doc = docData.find(doc => doc.id === ref.docId);
|
||||||
docId: doc.id,
|
if (!doc) return null;
|
||||||
title: title
|
|
||||||
? typeof title === 'string'
|
const titles = doc.get('title');
|
||||||
? title
|
const title =
|
||||||
: title[0]
|
(Array.isArray(titles) ? titles[0] : titles) ?? '';
|
||||||
: '',
|
const params = omit(ref, ['docId']);
|
||||||
};
|
|
||||||
});
|
return {
|
||||||
|
title,
|
||||||
|
docId: doc.id,
|
||||||
|
params: isEmpty(params)
|
||||||
|
? undefined
|
||||||
|
: toURLSearchParams(params),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter(ref => !!ref);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -346,9 +401,27 @@ export class DocsSearchService extends Service {
|
|||||||
> {
|
> {
|
||||||
const { buckets } = await this.indexer.blockIndex.aggregate(
|
const { buckets } = await this.indexer.blockIndex.aggregate(
|
||||||
{
|
{
|
||||||
type: 'match',
|
type: 'boolean',
|
||||||
field: 'ref',
|
occur: 'must',
|
||||||
match: docId,
|
queries: [
|
||||||
|
{
|
||||||
|
type: 'match',
|
||||||
|
field: 'refDocId',
|
||||||
|
match: docId,
|
||||||
|
},
|
||||||
|
// Ignore if it is a link to the current document.
|
||||||
|
{
|
||||||
|
type: 'boolean',
|
||||||
|
occur: 'must_not',
|
||||||
|
queries: [
|
||||||
|
{
|
||||||
|
type: 'match',
|
||||||
|
field: 'docId',
|
||||||
|
match: docId,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
'docId',
|
'docId',
|
||||||
{
|
{
|
||||||
@@ -384,9 +457,27 @@ export class DocsSearchService extends Service {
|
|||||||
return this.indexer.blockIndex
|
return this.indexer.blockIndex
|
||||||
.aggregate$(
|
.aggregate$(
|
||||||
{
|
{
|
||||||
type: 'match',
|
type: 'boolean',
|
||||||
field: 'ref',
|
occur: 'must',
|
||||||
match: docId,
|
queries: [
|
||||||
|
{
|
||||||
|
type: 'match',
|
||||||
|
field: 'refDocId',
|
||||||
|
match: docId,
|
||||||
|
},
|
||||||
|
// Ignore if it is a link to the current document.
|
||||||
|
{
|
||||||
|
type: 'boolean',
|
||||||
|
occur: 'must_not',
|
||||||
|
queries: [
|
||||||
|
{
|
||||||
|
type: 'match',
|
||||||
|
field: 'docId',
|
||||||
|
match: docId,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
'docId',
|
'docId',
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { DeltaInsert } from '@blocksuite/inline';
|
|||||||
import { Document } from '@toeverything/infra';
|
import { Document } from '@toeverything/infra';
|
||||||
import { toHexString } from 'lib0/buffer.js';
|
import { toHexString } from 'lib0/buffer.js';
|
||||||
import { digest as lib0Digest } from 'lib0/hash/sha256';
|
import { digest as lib0Digest } from 'lib0/hash/sha256';
|
||||||
import { difference } from 'lodash-es';
|
import { difference, uniq } from 'lodash-es';
|
||||||
import {
|
import {
|
||||||
applyUpdate,
|
applyUpdate,
|
||||||
Array as YArray,
|
Array as YArray,
|
||||||
@@ -130,18 +130,25 @@ async function crawlingDocData({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const deltas: DeltaInsert<AffineTextAttributes>[] = text.toDelta();
|
const deltas: DeltaInsert<AffineTextAttributes>[] = text.toDelta();
|
||||||
const ref = deltas
|
const refs = uniq(
|
||||||
.map(delta => {
|
deltas
|
||||||
if (
|
.flatMap(delta => {
|
||||||
delta.attributes &&
|
if (
|
||||||
delta.attributes.reference &&
|
delta.attributes &&
|
||||||
delta.attributes.reference.pageId
|
delta.attributes.reference &&
|
||||||
) {
|
delta.attributes.reference.pageId
|
||||||
return delta.attributes.reference.pageId;
|
) {
|
||||||
}
|
const { pageId: refDocId, params = {} } =
|
||||||
return null;
|
delta.attributes.reference;
|
||||||
})
|
return {
|
||||||
.filter((link): link is string => !!link);
|
refDocId,
|
||||||
|
ref: JSON.stringify({ docId: refDocId, ...params }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter(ref => !!ref)
|
||||||
|
);
|
||||||
|
|
||||||
blockDocuments.push(
|
blockDocuments.push(
|
||||||
Document.from<BlockIndexSchema>(`${docId}:${blockId}`, {
|
Document.from<BlockIndexSchema>(`${docId}:${blockId}`, {
|
||||||
@@ -149,7 +156,14 @@ async function crawlingDocData({
|
|||||||
flavour,
|
flavour,
|
||||||
blockId,
|
blockId,
|
||||||
content: text.toString(),
|
content: text.toString(),
|
||||||
ref,
|
...refs.reduce<{ refDocId: string[]; ref: string[] }>(
|
||||||
|
(prev, curr) => {
|
||||||
|
prev.refDocId.push(curr.refDocId);
|
||||||
|
prev.ref.push(curr.ref);
|
||||||
|
return prev;
|
||||||
|
},
|
||||||
|
{ refDocId: [], ref: [] }
|
||||||
|
),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -160,12 +174,15 @@ async function crawlingDocData({
|
|||||||
) {
|
) {
|
||||||
const pageId = block.get('prop:pageId');
|
const pageId = block.get('prop:pageId');
|
||||||
if (typeof pageId === 'string') {
|
if (typeof pageId === 'string') {
|
||||||
|
// reference info
|
||||||
|
const params = block.get('prop:params') ?? {};
|
||||||
blockDocuments.push(
|
blockDocuments.push(
|
||||||
Document.from<BlockIndexSchema>(`${docId}:${blockId}`, {
|
Document.from<BlockIndexSchema>(`${docId}:${blockId}`, {
|
||||||
docId,
|
docId,
|
||||||
flavour,
|
flavour,
|
||||||
blockId,
|
blockId,
|
||||||
ref: pageId,
|
refDocId: [pageId],
|
||||||
|
ref: [JSON.stringify({ docId: pageId, ...params })],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,25 +55,28 @@ export class DocsQuickSearchSession
|
|||||||
if (!query) {
|
if (!query) {
|
||||||
out = of([] as QuickSearchItem<'docs', DocsPayload>[]);
|
out = of([] as QuickSearchItem<'docs', DocsPayload>[]);
|
||||||
} else {
|
} else {
|
||||||
|
const resolvedDoc = resolveLinkToDoc(query);
|
||||||
|
const resolvedDocId = resolvedDoc?.docId;
|
||||||
|
const resolvedBlockId = resolvedDoc?.blockIds?.[0];
|
||||||
|
|
||||||
out = this.docsSearchService.search$(query).pipe(
|
out = this.docsSearchService.search$(query).pipe(
|
||||||
map(docs => {
|
map(docs => {
|
||||||
const resolvedDoc = resolveLinkToDoc(query);
|
|
||||||
if (
|
if (
|
||||||
resolvedDoc &&
|
resolvedDocId &&
|
||||||
!docs.some(doc => doc.docId === resolvedDoc.docId)
|
!docs.some(doc => doc.docId === resolvedDocId)
|
||||||
) {
|
) {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
docId: resolvedDoc.docId,
|
docId: resolvedDocId,
|
||||||
score: 100,
|
score: 100,
|
||||||
blockId: resolvedDoc.blockIds?.[0],
|
blockId: resolvedBlockId,
|
||||||
blockContent: '',
|
blockContent: '',
|
||||||
},
|
},
|
||||||
...docs,
|
...docs,
|
||||||
];
|
];
|
||||||
} else {
|
|
||||||
return docs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return docs;
|
||||||
}),
|
}),
|
||||||
map(docs =>
|
map(docs =>
|
||||||
docs
|
docs
|
||||||
|
|||||||
@@ -31,3 +31,13 @@ export function buildAppUrl(path: string, opts: AppUrlOptions = {}) {
|
|||||||
return new URL(path, webBase).toString();
|
return new URL(path, webBase).toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toURLSearchParams(params?: Record<string, string | string[]>) {
|
||||||
|
if (!params) return;
|
||||||
|
return new URLSearchParams(
|
||||||
|
Object.entries(params).map(([k, v]) => [
|
||||||
|
k,
|
||||||
|
Array.isArray(v) ? v.join(',') : v,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user