feat(mobile): add two step confirmation for mobile journal (#13266)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Introduced a new mobile journals page with a two-step confirmation
flow, allowing users to select a date and confirm before creating or
opening a journal.
  * Added a dedicated route for journals on mobile devices.
* Implemented a placeholder view when no journal exists for a selected
date on both desktop and mobile.

* **Enhancements**
* Improved mobile and desktop styling for journals pages, including
responsive adjustments for mobile layouts.
* Updated journal navigation behavior based on a feature flag, enabling
or disabling the two-step confirmation flow.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Cats Juice
2025-07-21 13:30:24 +08:00
committed by GitHub
parent 612c73cab1
commit 52e69e0dde
7 changed files with 228 additions and 44 deletions
@@ -20,12 +20,22 @@ export const body = style({
width: '100%',
height: '100%',
borderTop: `0.5px solid ${cssVarV2.layer.insideBorder.border}`,
selectors: {
'&[data-mobile]': {
borderTop: 'none',
},
},
});
export const content = style({
maxWidth: 944,
padding: '0px 50px',
margin: '0 auto',
selectors: {
'[data-mobile] &': {
padding: '0 24px',
},
},
});
export const docTitleContainer = style({
@@ -33,7 +33,7 @@ import {
import { AllDocSidebarTabs } from '../layouts/all-doc-sidebar-tabs';
import * as styles from './index.css';
function getDateFromUrl(location: Location) {
export function getDateFromUrl(location: Location) {
const searchParams = new URLSearchParams(location.search);
const date = searchParams.get('date')
? dayjs(searchParams.get('date'))
@@ -41,6 +41,49 @@ function getDateFromUrl(location: Location) {
return date.format(JOURNAL_DATE_FORMAT);
}
export const JournalPlaceholder = ({ dateString }: { dateString: string }) => {
const t = useI18n();
const [redirecting, setRedirecting] = useState(false);
const workbench = useService(WorkbenchService).workbench;
const journalService = useService(JournalService);
const createJournal = useCallback(() => {
if (redirecting) return;
setRedirecting(true);
const doc = journalService.ensureJournalByDate(dateString);
workbench.openDoc(doc.id, {
replaceHistory: true,
at: 'active',
});
}, [dateString, journalService, redirecting, workbench]);
return (
<div className={styles.body} data-mobile={BUILD_CONFIG.isMobileEdition}>
<div className={styles.content}>
<BlocksuiteEditorJournalDocTitleUI
date={dateString}
overrideClassName={styles.docTitleContainer}
/>
<div className={styles.placeholder}>
<div className={styles.placeholderIcon}>
<TodayIcon />
</div>
<div className={styles.placeholderText}>
{t['com.affine.journal.placeholder.title']()}
</div>
<Button
variant="primary"
onClick={createJournal}
data-testid="confirm-create-journal-button"
>
{t['com.affine.journal.placeholder.create']()}
</Button>
</div>
</div>
</div>
);
};
const weekStyle = { maxWidth: 800, width: '100%' };
// this route page acts as a redirector to today's journal
export const JournalsPageWithConfirmation = () => {
@@ -55,19 +98,8 @@ export const JournalsPageWithConfirmation = () => {
const todayString = dayjs().format(JOURNAL_DATE_FORMAT);
const isToday = dateString === todayString;
const [redirecting, setRedirecting] = useState(false);
const [ready, setReady] = useState(false);
const createJournal = useCallback(() => {
if (redirecting) return;
setRedirecting(true);
const doc = journalService.ensureJournalByDate(dateString);
workbench.openDoc(doc.id, {
replaceHistory: true,
at: 'active',
});
}, [dateString, journalService, redirecting, workbench]);
const openJournal = useCallback(
(date: string) => {
workbench.open(`/journals?date=${date}`, { at: 'active' });
@@ -118,29 +150,7 @@ export const JournalsPageWithConfirmation = () => {
</div>
</ViewHeader>
<ViewBody>
<div className={styles.body}>
<div className={styles.content}>
<BlocksuiteEditorJournalDocTitleUI
date={dateString}
overrideClassName={styles.docTitleContainer}
/>
<div className={styles.placeholder}>
<div className={styles.placeholderIcon}>
<TodayIcon />
</div>
<div className={styles.placeholderText}>
{t['com.affine.journal.placeholder.title']()}
</div>
<Button
variant="primary"
onClick={createJournal}
data-testid="confirm-create-journal-button"
>
{t['com.affine.journal.placeholder.create']()}
</Button>
</div>
</div>
</div>
<JournalPlaceholder dateString={dateString} />
</ViewBody>
<AllDocSidebarTabs />
</>