feat(core): add outgoing links to doc info (#7955)

close AF-1270

![CleanShot 2024-08-23 at 13 22 38@2x](https://github.com/user-attachments/assets/7eb21db5-ab33-41ad-a51a-fd2cf46a0e30)
This commit is contained in:
JimmFly 2024-08-29 06:41:41 +00:00
parent 3ce92f2abc
commit 5e8683c9be
No known key found for this signature in database
GPG Key ID: 126E0320FEB0D05C
8 changed files with 52 additions and 31 deletions

View File

@ -5,6 +5,7 @@ import {
Scrollable, Scrollable,
} from '@affine/component'; } from '@affine/component';
import { DocsSearchService } from '@affine/core/modules/docs-search'; import { DocsSearchService } from '@affine/core/modules/docs-search';
import { useI18n } from '@affine/i18n';
import { LiveData, useLiveData, useServices } from '@toeverything/infra'; import { LiveData, useLiveData, useServices } from '@toeverything/infra';
import { Suspense, useCallback, useContext, useMemo, useRef } from 'react'; import { Suspense, useCallback, useContext, useMemo, useRef } from 'react';
@ -16,8 +17,8 @@ import {
SortableProperties, SortableProperties,
usePagePropertiesManager, usePagePropertiesManager,
} from '../table'; } from '../table';
import { BackLinksRow } from './back-links-row';
import * as styles from './info-modal.css'; import * as styles from './info-modal.css';
import { LinksRow } from './links-row';
import { TagsRow } from './tags-row'; import { TagsRow } from './tags-row';
import { TimeRow } from './time-row'; import { TimeRow } from './time-row';
@ -30,22 +31,12 @@ export const InfoModal = ({
onOpenChange: (open: boolean) => void; onOpenChange: (open: boolean) => void;
docId: string; docId: string;
}) => { }) => {
const { docsSearchService } = useServices({
DocsSearchService,
});
const titleInputHandleRef = useRef<InlineEditHandle>(null); const titleInputHandleRef = useRef<InlineEditHandle>(null);
const manager = usePagePropertiesManager(docId); const manager = usePagePropertiesManager(docId);
const handleClose = useCallback(() => { const handleClose = useCallback(() => {
onOpenChange(false); onOpenChange(false);
}, [onOpenChange]); }, [onOpenChange]);
const references = useLiveData(
useMemo(
() => LiveData.from(docsSearchService.watchRefsFrom(docId), null),
[docId, docsSearchService]
)
);
if (!manager.page || manager.readonly) { if (!manager.page || manager.readonly) {
return null; return null;
} }
@ -76,7 +67,6 @@ export const InfoModal = ({
<InfoTable <InfoTable
docId={docId} docId={docId}
onClose={handleClose} onClose={handleClose}
references={references}
readonly={manager.readonly} readonly={manager.readonly}
/> />
</Suspense> </Suspense>
@ -90,29 +80,52 @@ export const InfoModal = ({
const InfoTable = ({ const InfoTable = ({
onClose, onClose,
references,
docId, docId,
readonly, readonly,
}: { }: {
docId: string; docId: string;
onClose: () => void; onClose: () => void;
readonly: boolean; readonly: boolean;
references:
| {
docId: string;
title: string;
}[]
| null;
}) => { }) => {
const t = useI18n();
const manager = useContext(managerContext); const manager = useContext(managerContext);
const { docsSearchService } = useServices({
DocsSearchService,
});
const links = useLiveData(
useMemo(
() => LiveData.from(docsSearchService.watchRefsFrom(docId), null),
[docId, docsSearchService]
)
);
const backlinks = useLiveData(
useMemo(
() => LiveData.from(docsSearchService.watchRefsTo(docId), null),
[docId, docsSearchService]
)
);
return ( return (
<div> <div>
<TimeRow docId={docId} /> <TimeRow docId={docId} />
<Divider size="thinner" /> <Divider size="thinner" />
{references && references.length > 0 ? ( {backlinks && backlinks.length > 0 ? (
<> <>
<BackLinksRow references={references} onClick={onClose} /> <LinksRow
references={backlinks}
onClick={onClose}
label={t['com.affine.page-properties.backlinks']()}
/>
<Divider size="thinner" />
</>
) : null}
{links && links.length > 0 ? (
<>
<LinksRow
references={links}
onClick={onClose}
label={t['com.affine.page-properties.outgoing-links']()}
/>
<Divider size="thinner" /> <Divider size="thinner" />
</> </>
) : null} ) : null}

View File

@ -1,22 +1,24 @@
import { useI18n } from '@affine/i18n'; import type { Backlink, Link } from '@affine/core/modules/doc-link';
import { useContext } from 'react'; import { useContext } from 'react';
import { AffinePageReference } from '../../reference-link'; import { AffinePageReference } from '../../reference-link';
import { managerContext } from '../common'; import { managerContext } from '../common';
import * as styles from './back-links-row.css'; import * as styles from './links-row.css';
export const BackLinksRow = ({
export const LinksRow = ({
references, references,
label,
onClick, onClick,
}: { }: {
references: { docId: string; title: string }[]; references: Backlink[] | Link[];
label: string;
onClick?: () => void; onClick?: () => void;
}) => { }) => {
const manager = useContext(managerContext); const manager = useContext(managerContext);
const t = useI18n();
return ( return (
<div> <div>
<div className={styles.title}> <div className={styles.title}>
{t['com.affine.page-properties.backlinks']()} · {references.length} {label} · {references.length}
</div> </div>
{references.map(link => ( {references.map(link => (
<AffinePageReference <AffinePageReference

View File

@ -1,4 +1,5 @@
import { DocLinksService } from '@affine/core/modules/doc-link'; import { DocLinksService } from '@affine/core/modules/doc-link';
import { useI18n } from '@affine/i18n';
import { import {
useLiveData, useLiveData,
useServices, useServices,
@ -15,6 +16,7 @@ export const BiDirectionalLinkPanel = () => {
DocLinksService, DocLinksService,
WorkspaceService, WorkspaceService,
}); });
const t = useI18n();
const links = useLiveData(docLinksService.links.links$); const links = useLiveData(docLinksService.links.links$);
const backlinks = useLiveData(docLinksService.backlinks.backlinks$); const backlinks = useLiveData(docLinksService.backlinks.backlinks$);
@ -44,7 +46,7 @@ export const BiDirectionalLinkPanel = () => {
</div> </div>
<div className={styles.linksContainer}> <div className={styles.linksContainer}>
<div className={styles.linksTitles}> <div className={styles.linksTitles}>
Backlinks · {backlinks.length} {t['com.affine.page-properties.backlinks']()} · {backlinks.length}
</div> </div>
{backlinks.map(link => ( {backlinks.map(link => (
<div key={link.docId} className={styles.link}> <div key={link.docId} className={styles.link}>
@ -58,7 +60,8 @@ export const BiDirectionalLinkPanel = () => {
</div> </div>
<div className={styles.linksContainer}> <div className={styles.linksContainer}>
<div className={styles.linksTitles}> <div className={styles.linksTitles}>
Outgoing links · {links.length} {t['com.affine.page-properties.outgoing-links']()} ·{' '}
{links.length}
</div> </div>
{links.map(link => ( {links.map(link => (
<div key={link.docId} className={styles.link}> <div key={link.docId} className={styles.link}>

View File

@ -3,7 +3,7 @@ import { Entity, LiveData } from '@toeverything/infra';
import type { DocsSearchService } from '../../docs-search'; import type { DocsSearchService } from '../../docs-search';
interface Backlink { export interface Backlink {
docId: string; docId: string;
blockId: string; blockId: string;
title: string; title: string;

View File

@ -3,7 +3,7 @@ import { Entity, LiveData } from '@toeverything/infra';
import type { DocsSearchService } from '../../docs-search'; import type { DocsSearchService } from '../../docs-search';
interface Link { export interface Link {
docId: string; docId: string;
title: string; title: string;
} }

View File

@ -10,6 +10,8 @@ import { DocBacklinks } from './entities/doc-backlinks';
import { DocLinks } from './entities/doc-links'; import { DocLinks } from './entities/doc-links';
import { DocLinksService } from './services/doc-links'; import { DocLinksService } from './services/doc-links';
export type { Backlink } from './entities/doc-backlinks';
export type { Link } from './entities/doc-links';
export { DocLinksService } from './services/doc-links'; export { DocLinksService } from './services/doc-links';
export function configureDocLinksModule(framework: Framework) { export function configureDocLinksModule(framework: Framework) {

View File

@ -851,6 +851,7 @@
"com.affine.page-properties.add-property.menu.create": "Create property", "com.affine.page-properties.add-property.menu.create": "Create property",
"com.affine.page-properties.add-property.menu.header": "Properties", "com.affine.page-properties.add-property.menu.header": "Properties",
"com.affine.page-properties.backlinks": "Backlinks", "com.affine.page-properties.backlinks": "Backlinks",
"com.affine.page-properties.outgoing-links": "Outgoing links",
"com.affine.page-properties.create-property.menu.header": "Type", "com.affine.page-properties.create-property.menu.header": "Type",
"com.affine.page-properties.icons": "Icons", "com.affine.page-properties.icons": "Icons",
"com.affine.page-properties.page-info": "Info", "com.affine.page-properties.page-info": "Info",