mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-17 10:06:17 +08:00
fix AF-1790 fix AF-1774 added `DateSelectorDialog` for selecting a date through blocksuite; added `AtMenuConfigService` for constructing the at menu config fix AF-1776 fix PD-1942 resubmitted to replace #8877
This commit is contained in:
+4
-191
@@ -1,196 +1,9 @@
|
||||
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
|
||||
import { JournalService } from '@affine/core/modules/journal';
|
||||
import { I18n } from '@affine/i18n';
|
||||
import { track } from '@affine/track';
|
||||
import type { EditorHost } from '@blocksuite/affine/block-std';
|
||||
import type {
|
||||
AffineInlineEditor,
|
||||
LinkedWidgetConfig,
|
||||
} from '@blocksuite/affine/blocks';
|
||||
import { LinkedWidgetUtils } from '@blocksuite/affine/blocks';
|
||||
import type { DocMeta } from '@blocksuite/affine/store';
|
||||
import { type FrameworkProvider, WorkspaceService } from '@toeverything/infra';
|
||||
import { AtMenuConfigService } from '@affine/core/modules/at-menu-config/services';
|
||||
import type { LinkedWidgetConfig } from '@blocksuite/affine/blocks';
|
||||
import { type FrameworkProvider } from '@toeverything/infra';
|
||||
|
||||
function createNewDocMenuGroup(
|
||||
framework: FrameworkProvider,
|
||||
query: string,
|
||||
abort: () => void,
|
||||
editorHost: EditorHost,
|
||||
inlineEditor: AffineInlineEditor
|
||||
) {
|
||||
const originalNewDocMenuGroup = LinkedWidgetUtils.createNewDocMenuGroup(
|
||||
query,
|
||||
abort,
|
||||
editorHost,
|
||||
inlineEditor
|
||||
);
|
||||
|
||||
const items = Array.isArray(originalNewDocMenuGroup.items)
|
||||
? originalNewDocMenuGroup.items
|
||||
: originalNewDocMenuGroup.items.value;
|
||||
|
||||
// Patch the import item, to use the custom import dialog.
|
||||
const importItemIndex = items.findIndex(item => item.key === 'import');
|
||||
if (importItemIndex === -1) {
|
||||
return originalNewDocMenuGroup;
|
||||
}
|
||||
|
||||
const originalImportItem = items[importItemIndex];
|
||||
const customImportItem = {
|
||||
...originalImportItem,
|
||||
action: () => {
|
||||
abort();
|
||||
track.doc.editor.atMenu.import();
|
||||
framework
|
||||
.get(WorkspaceDialogService)
|
||||
.open('import', undefined, payload => {
|
||||
if (!payload) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the imported file is a workspace file, insert the entry page node.
|
||||
const { docIds, entryId, isWorkspaceFile } = payload;
|
||||
if (isWorkspaceFile && entryId) {
|
||||
LinkedWidgetUtils.insertLinkedNode({
|
||||
inlineEditor,
|
||||
docId: entryId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, insert all the doc nodes.
|
||||
for (const docId of docIds) {
|
||||
LinkedWidgetUtils.insertLinkedNode({
|
||||
inlineEditor,
|
||||
docId,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// only replace the original import item
|
||||
items.splice(importItemIndex, 1, customImportItem);
|
||||
return originalNewDocMenuGroup;
|
||||
}
|
||||
|
||||
// TODO: fix the type
|
||||
export function createLinkedWidgetConfig(
|
||||
framework: FrameworkProvider
|
||||
): Partial<LinkedWidgetConfig> {
|
||||
return {
|
||||
getMenus: (
|
||||
query: string,
|
||||
abort: () => void,
|
||||
editorHost: EditorHost,
|
||||
inlineEditor: AffineInlineEditor
|
||||
) => {
|
||||
const currentWorkspace = framework.get(WorkspaceService).workspace;
|
||||
const rawMetas = currentWorkspace.docCollection.meta.docMetas;
|
||||
const journalService = framework.get(JournalService);
|
||||
const isJournal = (d: DocMeta) =>
|
||||
!!journalService.journalDate$(d.id).value;
|
||||
|
||||
const docDisplayMetaService = framework.get(DocDisplayMetaService);
|
||||
const docMetas = rawMetas
|
||||
.filter(meta => {
|
||||
if (isJournal(meta) && !meta.updatedDate) {
|
||||
return false;
|
||||
}
|
||||
return !meta.trash;
|
||||
})
|
||||
.map(meta => {
|
||||
const title = docDisplayMetaService.title$(meta.id, {
|
||||
reference: true,
|
||||
}).value;
|
||||
return {
|
||||
...meta,
|
||||
title: I18n.t(title),
|
||||
};
|
||||
})
|
||||
.filter(({ title }) => isFuzzyMatch(title, query));
|
||||
|
||||
// TODO need i18n if BlockSuite supported
|
||||
const MAX_DOCS = 6;
|
||||
return Promise.resolve([
|
||||
{
|
||||
name: 'Link to Doc',
|
||||
items: docMetas.map(doc => ({
|
||||
key: doc.id,
|
||||
name: doc.title,
|
||||
icon: docDisplayMetaService
|
||||
.icon$(doc.id, {
|
||||
type: 'lit',
|
||||
reference: true,
|
||||
})
|
||||
.value(),
|
||||
action: () => {
|
||||
abort();
|
||||
LinkedWidgetUtils.insertLinkedNode({
|
||||
inlineEditor,
|
||||
docId: doc.id,
|
||||
});
|
||||
track.doc.editor.atMenu.linkDoc();
|
||||
},
|
||||
})),
|
||||
maxDisplay: MAX_DOCS,
|
||||
overflowText: `${docMetas.length - MAX_DOCS} more docs`,
|
||||
},
|
||||
createNewDocMenuGroup(
|
||||
framework,
|
||||
query,
|
||||
abort,
|
||||
editorHost,
|
||||
inlineEditor
|
||||
),
|
||||
]);
|
||||
},
|
||||
mobile: {
|
||||
useScreenHeight: BUILD_CONFIG.isIOS,
|
||||
scrollContainer: window,
|
||||
scrollTopOffset: () => {
|
||||
const header = document.querySelector('header');
|
||||
if (!header) return 0;
|
||||
|
||||
const { y, height } = header.getBoundingClientRect();
|
||||
return y + height;
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the name is a fuzzy match of the query.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const name = 'John Smith';
|
||||
* const query = 'js';
|
||||
* const isMatch = isFuzzyMatch(name, query);
|
||||
* // isMatch: true
|
||||
* ```
|
||||
*/
|
||||
function isFuzzyMatch(name: string, query: string) {
|
||||
const pureName = name
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.split('')
|
||||
.filter(char => char !== ' ')
|
||||
.join('');
|
||||
|
||||
const regex = new RegExp(
|
||||
query
|
||||
.split('')
|
||||
.filter(char => char !== ' ')
|
||||
.map(item => `${escapeRegExp(item)}.*`)
|
||||
.join(''),
|
||||
'i'
|
||||
);
|
||||
return regex.test(pureName);
|
||||
}
|
||||
|
||||
function escapeRegExp(input: string) {
|
||||
// escape regex characters in the input string to prevent regex format errors
|
||||
return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
return framework.get(AtMenuConfigService).getConfig();
|
||||
}
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
import { EditorSettingService } from '@affine/core/modules/editor-setting';
|
||||
import { JournalService } from '@affine/core/modules/journal';
|
||||
import {
|
||||
JOURNAL_DATE_FORMAT,
|
||||
JournalService,
|
||||
type MaybeDate,
|
||||
} from '@affine/core/modules/journal';
|
||||
import { i18nTime } from '@affine/i18n';
|
||||
import { track } from '@affine/track';
|
||||
import { Text } from '@blocksuite/affine/store';
|
||||
import {
|
||||
type DocProps,
|
||||
DocsService,
|
||||
initDocFromProps,
|
||||
useService,
|
||||
useServices,
|
||||
} from '@toeverything/infra';
|
||||
import { DocsService, useService, useServices } from '@toeverything/infra';
|
||||
import dayjs from 'dayjs';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { WorkbenchService } from '../../modules/workbench';
|
||||
|
||||
type MaybeDate = Date | string | number;
|
||||
export const JOURNAL_DATE_FORMAT = 'YYYY-MM-DD';
|
||||
|
||||
function isJournalString(j?: string | false) {
|
||||
return j ? !!j?.match(/^\d{4}-\d{2}-\d{2}$/) : false;
|
||||
}
|
||||
@@ -33,73 +27,27 @@ function toDayjs(j?: string | false) {
|
||||
* @deprecated use `JournalService` directly
|
||||
*/
|
||||
export const useJournalHelper = () => {
|
||||
const { docsService, editorSettingService, journalService } = useServices({
|
||||
const { journalService } = useServices({
|
||||
DocsService,
|
||||
EditorSettingService,
|
||||
JournalService,
|
||||
});
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const _createJournal = useCallback(
|
||||
(maybeDate: MaybeDate) => {
|
||||
const day = dayjs(maybeDate);
|
||||
const title = day.format(JOURNAL_DATE_FORMAT);
|
||||
const docRecord = docsService.createDoc();
|
||||
const { doc, release } = docsService.open(docRecord.id);
|
||||
docsService.list.setPrimaryMode(docRecord.id, 'page');
|
||||
// set created date to match the journal date
|
||||
docRecord.setMeta({
|
||||
createDate: dayjs()
|
||||
.set('year', day.year())
|
||||
.set('month', day.month())
|
||||
.set('date', day.date())
|
||||
.toDate()
|
||||
.getTime(),
|
||||
});
|
||||
const docProps: DocProps = {
|
||||
page: { title: new Text(title) },
|
||||
note: editorSettingService.editorSetting.get('affine:note'),
|
||||
};
|
||||
initDocFromProps(doc.blockSuiteDoc, docProps);
|
||||
release();
|
||||
journalService.setJournalDate(docRecord.id, title);
|
||||
return docRecord;
|
||||
},
|
||||
[docsService, editorSettingService.editorSetting, journalService]
|
||||
);
|
||||
|
||||
/**
|
||||
* query all journals by date
|
||||
*/
|
||||
const getJournalsByDate = useCallback(
|
||||
(maybeDate: MaybeDate) => {
|
||||
return journalService.getJournalsByDate(
|
||||
dayjs(maybeDate).format(JOURNAL_DATE_FORMAT)
|
||||
);
|
||||
},
|
||||
[journalService]
|
||||
);
|
||||
|
||||
/**
|
||||
* get journal by date, create one if not exist
|
||||
*/
|
||||
const getJournalByDate = useCallback(
|
||||
(maybeDate: MaybeDate) => {
|
||||
const pages = getJournalsByDate(maybeDate);
|
||||
if (pages.length) return pages[0];
|
||||
return _createJournal(maybeDate);
|
||||
return journalService.ensureJournalByDate(maybeDate);
|
||||
},
|
||||
[_createJournal, getJournalsByDate]
|
||||
[journalService]
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
getJournalsByDate,
|
||||
getJournalByDate,
|
||||
}),
|
||||
[getJournalsByDate, getJournalByDate]
|
||||
[getJournalByDate]
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user