Skip to content

Commit 7ae141b

Browse files
committed
feat(mobile): add mobile detail page (#7993)
fix AF-1241
1 parent f8e6f1f commit 7ae141b

File tree

22 files changed

+865
-153
lines changed

22 files changed

+865
-153
lines changed

packages/frontend/core/src/components/affine/page-properties/info-modal/info-modal.css.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,8 @@ export const hiddenInput = style({
4343
height: '0',
4444
position: 'absolute',
4545
});
46+
47+
export const timeRow = style({
48+
marginTop: 20,
49+
borderBottom: 4,
50+
});

packages/frontend/core/src/components/affine/page-properties/info-modal/info-modal.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export const InfoModal = ({
7878
);
7979
};
8080

81-
const InfoTable = ({
81+
export const InfoTable = ({
8282
onClose,
8383
docId,
8484
readonly,
@@ -106,8 +106,8 @@ const InfoTable = ({
106106
);
107107

108108
return (
109-
<div>
110-
<TimeRow docId={docId} />
109+
<div className={styles.container}>
110+
<TimeRow className={styles.timeRow} docId={docId} />
111111
<Divider size="thinner" />
112112
{backlinks && backlinks.length > 0 ? (
113113
<>

packages/frontend/core/src/components/affine/page-properties/info-modal/links-row.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@ import * as styles from './links-row.css';
88
export const LinksRow = ({
99
references,
1010
label,
11+
className,
1112
onClick,
1213
}: {
1314
references: Backlink[] | Link[];
1415
label: string;
16+
className?: string;
1517
onClick?: () => void;
1618
}) => {
1719
const manager = useContext(managerContext);
1820
return (
19-
<div>
21+
<div className={className}>
2022
<div className={styles.title}>
2123
{label} · {references.length}
2224
</div>

packages/frontend/core/src/components/affine/page-properties/info-modal/tags-row.css.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { cssVar } from '@toeverything/theme';
2-
import { style } from '@vanilla-extract/css';
2+
import { fallbackVar, style } from '@vanilla-extract/css';
3+
4+
import { rowHPadding } from '../styles.css';
35

46
export const icon = style({
57
fontSize: 16,
@@ -65,7 +67,7 @@ export const rowValueCell = style({
6567
':hover': {
6668
backgroundColor: cssVar('hoverColor'),
6769
},
68-
padding: '6px 8px',
70+
padding: `6px ${fallbackVar(rowHPadding, '8px')} 6px 8px`,
6971
border: `1px solid transparent`,
7072
color: cssVar('textPrimaryColor'),
7173
':focus': {

packages/frontend/core/src/components/affine/page-properties/info-modal/tags-row.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,21 @@ import * as styles from './tags-row.css';
1111
export const TagsRow = ({
1212
docId,
1313
readonly,
14+
className,
1415
}: {
1516
docId: string;
1617
readonly: boolean;
18+
className?: string;
1719
}) => {
1820
const t = useI18n();
1921
const tagList = useService(TagService).tagList;
2022
const tagIds = useLiveData(tagList.tagIdsByPageId$(docId));
2123
const empty = !tagIds || tagIds.length === 0;
2224
return (
23-
<div className={styles.rowCell} data-testid="info-modal-tags-row">
25+
<div
26+
className={clsx(styles.rowCell, className)}
27+
data-testid="info-modal-tags-row"
28+
>
2429
<div className={styles.rowNameContainer}>
2530
<div className={styles.icon}>
2631
<TagsIcon />

packages/frontend/core/src/components/affine/page-properties/info-modal/time-row.css.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,4 @@ export const rowCell = style({
4646
export const container = style({
4747
display: 'flex',
4848
flexDirection: 'column',
49-
marginTop: 20,
50-
marginBottom: 4,
5149
});

packages/frontend/core/src/components/affine/page-properties/info-modal/time-row.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { i18nTime, useI18n } from '@affine/i18n';
22
import { DateTimeIcon, HistoryIcon } from '@blocksuite/icons/rc';
33
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
4+
import clsx from 'clsx';
45
import type { ConfigType } from 'dayjs';
56
import { useDebouncedValue } from 'foxact/use-debounced-value';
67
import { type ReactNode, useContext, useMemo } from 'react';
@@ -28,7 +29,13 @@ const RowComponent = ({
2829
);
2930
};
3031

31-
export const TimeRow = ({ docId }: { docId: string }) => {
32+
export const TimeRow = ({
33+
docId,
34+
className,
35+
}: {
36+
docId: string;
37+
className?: string;
38+
}) => {
3239
const t = useI18n();
3340
const manager = useContext(managerContext);
3441
const workspaceService = useService(WorkspaceService);
@@ -88,5 +95,7 @@ export const TimeRow = ({ docId }: { docId: string }) => {
8895

8996
const dTimestampElement = useDebouncedValue(timestampElement, 500);
9097

91-
return <div className={styles.container}>{dTimestampElement}</div>;
98+
return (
99+
<div className={clsx(styles.container, className)}>{dTimestampElement}</div>
100+
);
92101
};

packages/frontend/core/src/components/affine/page-properties/styles.css.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { cssVar } from '@toeverything/theme';
22
import { createVar, globalStyle, style } from '@vanilla-extract/css';
33

44
const propertyNameCellWidth = createVar();
5+
export const rowHPadding = createVar();
6+
export const fontSize = createVar();
57

68
export const root = style({
79
display: 'flex',
@@ -10,6 +12,16 @@ export const root = style({
1012
fontFamily: cssVar('fontSansFamily'),
1113
vars: {
1214
[propertyNameCellWidth]: '160px',
15+
[rowHPadding]: '6px',
16+
[fontSize]: cssVar('fontSm'),
17+
},
18+
'@container': {
19+
[`viewport (width <= 640px)`]: {
20+
vars: {
21+
[rowHPadding]: '0px',
22+
[fontSize]: cssVar('fontXs'),
23+
},
24+
},
1325
},
1426
});
1527

@@ -22,7 +34,7 @@ export const rootCentered = style({
2234
padding: `0 ${cssVar('editorSidePadding', '24px')}`,
2335
'@container': {
2436
[`viewport (width <= 640px)`]: {
25-
padding: '0 24px',
37+
padding: '0 16px',
2638
},
2739
},
2840
});
@@ -39,7 +51,7 @@ export const tableHeaderInfoRow = style({
3951
justifyContent: 'space-between',
4052
alignItems: 'center',
4153
color: cssVar('textSecondaryColor'),
42-
fontSize: cssVar('fontSm'),
54+
fontSize: fontSize,
4355
fontWeight: 500,
4456
minHeight: 34,
4557
'@media': {
@@ -54,9 +66,9 @@ export const tableHeaderSecondaryRow = style({
5466
flexDirection: 'row',
5567
alignItems: 'center',
5668
color: cssVar('textPrimaryColor'),
57-
fontSize: cssVar('fontSm'),
69+
fontSize: fontSize,
5870
fontWeight: 500,
59-
padding: '0 6px',
71+
padding: `0 ${rowHPadding}`,
6072
gap: '8px',
6173
height: 24,
6274
'@media': {
@@ -82,7 +94,7 @@ export const spacer = style({
8294
});
8395

8496
export const tableHeaderBacklinksHint = style({
85-
padding: '6px',
97+
padding: `0 ${rowHPadding}`,
8698
cursor: 'pointer',
8799
borderRadius: '4px',
88100
':hover': {
@@ -103,7 +115,7 @@ export const tableHeaderTimestamp = style({
103115
alignItems: 'start',
104116
gap: '8px',
105117
cursor: 'default',
106-
padding: '0 6px',
118+
padding: `0 ${rowHPadding}`,
107119
});
108120

109121
export const tableHeaderDivider = style({
@@ -273,7 +285,7 @@ export const editablePropertyRowCell = style([
273285
export const propertyRowNameCell = style([
274286
propertyRowCell,
275287
{
276-
padding: 6,
288+
padding: `6px ${rowHPadding}`,
277289
flexShrink: 0,
278290
color: cssVar('textSecondaryColor'),
279291
width: propertyNameCellWidth,
@@ -316,7 +328,7 @@ export const propertyRowValueCell = style([
316328
propertyRowCell,
317329
editablePropertyRowCell,
318330
{
319-
padding: '6px 8px',
331+
padding: `6px ${rowHPadding} 6px 6px`,
320332
border: `1px solid transparent`,
321333
color: cssVar('textPrimaryColor'),
322334
':focus': {
@@ -353,7 +365,7 @@ export const propertyRowValueTextarea = style([
353365
propertyRowValueCell,
354366
{
355367
border: 'none',
356-
padding: '6px 8px',
368+
padding: `6px ${rowHPadding} 6px 8px`,
357369
height: '100%',
358370
position: 'absolute',
359371
top: 0,
@@ -368,7 +380,7 @@ export const propertyRowValueTextareaInvisible = style([
368380
propertyRowValueCell,
369381
{
370382
border: 'none',
371-
padding: '6px 8px',
383+
padding: `6px ${rowHPadding} 6px 8px`,
372384
visibility: 'hidden',
373385
whiteSpace: 'break-spaces',
374386
wordBreak: 'break-all',
@@ -379,7 +391,7 @@ export const propertyRowValueTextareaInvisible = style([
379391
export const propertyRowValueNumberCell = style([
380392
propertyRowValueTextCell,
381393
{
382-
padding: '6px 8px',
394+
padding: `6px ${rowHPadding} 6px 8px`,
383395
},
384396
]);
385397

packages/frontend/core/src/components/blocksuite/block-suite-editor/lit-adaper.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ export const BlocksuiteDocEditor = forwardRef<
191191
) : (
192192
<BlocksuiteEditorJournalDocTitle page={page} />
193193
)}
194-
<PagePropertiesTable docId={page.id} />
194+
{!shared ? <PagePropertiesTable docId={page.id} /> : null}
195195
<adapted.DocEditor
196196
className={styles.docContainer}
197197
ref={onDocRef}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton';
2+
import type { Editor } from '@affine/core/modules/editor';
3+
import { EditorsService } from '@affine/core/modules/editor';
4+
import { ViewService } from '@affine/core/modules/workbench/services/view';
5+
import type { DocMode } from '@blocksuite/blocks';
6+
import type { Doc } from '@toeverything/infra';
7+
import {
8+
DocsService,
9+
FrameworkScope,
10+
useLiveData,
11+
useService,
12+
WorkspaceService,
13+
} from '@toeverything/infra';
14+
import {
15+
type PropsWithChildren,
16+
useEffect,
17+
useLayoutEffect,
18+
useState,
19+
} from 'react';
20+
21+
import { PageNotFound } from '../../404';
22+
23+
const useLoadDoc = (pageId: string) => {
24+
const currentWorkspace = useService(WorkspaceService).workspace;
25+
const docsService = useService(DocsService);
26+
const docRecordList = docsService.list;
27+
const docListReady = useLiveData(docRecordList.isReady$);
28+
const docRecord = useLiveData(docRecordList.doc$(pageId));
29+
const viewService = useService(ViewService);
30+
31+
const queryString = useLiveData(
32+
viewService.view.queryString$<{
33+
mode?: string;
34+
}>()
35+
);
36+
37+
const queryStringMode =
38+
queryString.mode && ['edgeless', 'page'].includes(queryString.mode)
39+
? (queryString.mode as DocMode)
40+
: null;
41+
42+
// We only read the querystring mode when entering, so use useState here.
43+
const [initialQueryStringMode] = useState(() => queryStringMode);
44+
45+
const [doc, setDoc] = useState<Doc | null>(null);
46+
const [editor, setEditor] = useState<Editor | null>(null);
47+
const editorMode = useLiveData(editor?.mode$);
48+
49+
useLayoutEffect(() => {
50+
if (!docRecord) {
51+
return;
52+
}
53+
const { doc: opened, release } = docsService.open(pageId);
54+
setDoc(opened);
55+
return () => {
56+
release();
57+
};
58+
}, [docRecord, docsService, pageId]);
59+
60+
useLayoutEffect(() => {
61+
if (!doc) {
62+
return;
63+
}
64+
const editor = doc.scope
65+
.get(EditorsService)
66+
.createEditor(initialQueryStringMode || doc.getPrimaryMode() || 'page');
67+
setEditor(editor);
68+
return () => {
69+
editor.dispose();
70+
};
71+
}, [doc, initialQueryStringMode]);
72+
73+
// update editor mode to queryString
74+
useEffect(() => {
75+
if (editorMode) {
76+
viewService.view.updateQueryString(
77+
{
78+
mode: editorMode,
79+
},
80+
{
81+
replace: true,
82+
}
83+
);
84+
}
85+
}, [editorMode, viewService.view]);
86+
87+
// set sync engine priority target
88+
useEffect(() => {
89+
currentWorkspace.engine.doc.setPriority(pageId, 10);
90+
return () => {
91+
currentWorkspace.engine.doc.setPriority(pageId, 5);
92+
};
93+
}, [currentWorkspace, pageId]);
94+
95+
const isInTrash = useLiveData(doc?.meta$.map(meta => meta.trash));
96+
97+
useEffect(() => {
98+
if (doc && isInTrash) {
99+
currentWorkspace.docCollection.awarenessStore.setReadonly(
100+
doc.blockSuiteDoc.blockCollection,
101+
true
102+
);
103+
}
104+
}, [currentWorkspace.docCollection.awarenessStore, doc, isInTrash]);
105+
106+
return {
107+
doc,
108+
editor,
109+
docListReady,
110+
};
111+
};
112+
113+
/**
114+
* A common wrapper for detail page for both mobile and desktop page.
115+
* It only contains the logic for page loading, context setup, but not the page content.
116+
*/
117+
export const DetailPageWrapper = ({
118+
pageId,
119+
children,
120+
}: PropsWithChildren<{ pageId: string }>) => {
121+
const { doc, editor, docListReady } = useLoadDoc(pageId);
122+
// if sync engine has been synced and the page is null, show 404 page.
123+
if (docListReady && !doc) {
124+
return <PageNotFound noPermission />;
125+
}
126+
127+
if (!doc || !editor) {
128+
return <PageDetailSkeleton key="current-page-is-null" />;
129+
}
130+
131+
return (
132+
<FrameworkScope scope={doc.scope}>
133+
<FrameworkScope scope={editor.scope}>{children}</FrameworkScope>
134+
</FrameworkScope>
135+
);
136+
};

0 commit comments

Comments
 (0)