refactor(core): adapter to new journal and property (#8508)

remove old WorkspacePropertyAdapter
add JournalService
This commit is contained in:
EYHN
2024-10-16 08:46:38 +00:00
parent 72e1489c62
commit 140ac723e6
39 changed files with 269 additions and 255 deletions
@@ -43,6 +43,12 @@ export class DocRecord extends Entity<{ id: string }> {
}); });
} }
setProperty(propertyId: string, value: string) {
this.docPropertiesStore.updateDocProperties(this.id, {
[propertyId]: value,
});
}
setMeta(meta: Partial<DocMeta>): void { setMeta(meta: Partial<DocMeta>): void {
this.docsStore.setDocMeta(this.id, meta); this.docsStore.setDocMeta(this.id, meta);
} }
@@ -1,5 +1,5 @@
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta'; import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
import { WorkspacePropertiesAdapter } from '@affine/core/modules/properties'; import { JournalService } from '@affine/core/modules/journal';
import { I18n } from '@affine/i18n'; import { I18n } from '@affine/i18n';
import { track } from '@affine/track'; import { track } from '@affine/track';
import type { EditorHost } from '@blocksuite/affine/block-std'; import type { EditorHost } from '@blocksuite/affine/block-std';
@@ -21,9 +21,9 @@ export function createLinkedWidgetConfig(
) => { ) => {
const currentWorkspace = framework.get(WorkspaceService).workspace; const currentWorkspace = framework.get(WorkspaceService).workspace;
const rawMetas = currentWorkspace.docCollection.meta.docMetas; const rawMetas = currentWorkspace.docCollection.meta.docMetas;
const adapter = framework.get(WorkspacePropertiesAdapter); const journalService = framework.get(JournalService);
const isJournal = (d: DocMeta) => const isJournal = (d: DocMeta) =>
!!adapter.getJournalPageDateString(d.id); !!journalService.journalDate$(d.id).value;
const docDisplayMetaService = framework.get(DocDisplayMetaService); const docDisplayMetaService = framework.get(DocDisplayMetaService);
const docMetas = rawMetas const docMetas = rawMetas
@@ -1,5 +1,5 @@
import { FavoriteTag } from '@affine/core/components/page-list'; import { FavoriteTag } from '@affine/core/components/page-list';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { toast } from '@affine/core/utils'; import { toast } from '@affine/core/utils';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { track } from '@affine/track'; import { track } from '@affine/track';
@@ -9,12 +9,11 @@ import {
Scrollable, Scrollable,
} from '@affine/component'; } from '@affine/component';
import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper'; import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper';
import { WorkspaceLegacyProperties } from '@affine/core/modules/properties';
import type { Tag } from '@affine/core/modules/tag'; import type { Tag } from '@affine/core/modules/tag';
import { DeleteTagConfirmModal, TagService } from '@affine/core/modules/tag'; import { DeleteTagConfirmModal, TagService } from '@affine/core/modules/tag';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { DeleteIcon, MoreHorizontalIcon, TagsIcon } from '@blocksuite/icons/rc'; import { DeleteIcon, MoreHorizontalIcon, TagsIcon } from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra'; import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
import clsx from 'clsx'; import clsx from 'clsx';
import { clamp } from 'lodash-es'; import { clamp } from 'lodash-es';
import type { HTMLAttributes, PropsWithChildren } from 'react'; import type { HTMLAttributes, PropsWithChildren } from 'react';
@@ -84,7 +83,7 @@ export const EditTagMenu = ({
onTagDelete: (tagIds: string[]) => void; onTagDelete: (tagIds: string[]) => void;
}>) => { }>) => {
const t = useI18n(); const t = useI18n();
const legacyProperties = useService(WorkspaceLegacyProperties); const workspaceService = useService(WorkspaceService);
const tagService = useService(TagService); const tagService = useService(TagService);
const tagList = tagService.tagList; const tagList = tagService.tagList;
const tag = useLiveData(tagList.tagByTagId$(tagId)); const tag = useLiveData(tagList.tagByTagId$(tagId));
@@ -135,7 +134,7 @@ export const EditTagMenu = ({
<MenuItem <MenuItem
prefixIcon={<TagsIcon />} prefixIcon={<TagsIcon />}
onClick={() => { onClick={() => {
navigate.jumpToTag(legacyProperties.workspaceId, tag?.id || ''); navigate.jumpToTag(workspaceService.workspace.id, tag?.id || '');
}} }}
> >
{t['com.affine.page-properties.tags.open-tags-page']()} {t['com.affine.page-properties.tags.open-tags-page']()}
@@ -171,7 +170,7 @@ export const EditTagMenu = ({
), ),
} satisfies Partial<MenuProps>; } satisfies Partial<MenuProps>;
}, [ }, [
legacyProperties.workspaceId, workspaceService,
navigate, navigate,
onTagDelete, onTagDelete,
t, t,
@@ -1,7 +1,7 @@
import { toast } from '@affine/component'; import { toast } from '@affine/component';
import { useBlockSuiteDocMeta } from '@affine/core/components/hooks/use-block-suite-page-meta'; import { useBlockSuiteDocMeta } from '@affine/core/components/hooks/use-block-suite-page-meta';
import { FavoriteTag } from '@affine/core/components/page-list/components/favorite-tag'; import { FavoriteTag } from '@affine/core/components/page-list/components/favorite-tag';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { ShareDocsListService } from '@affine/core/modules/share-doc'; import { ShareDocsListService } from '@affine/core/modules/share-doc';
import { PublicPageMode } from '@affine/graphql'; import { PublicPageMode } from '@affine/graphql';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
@@ -5,7 +5,7 @@ import {
} from '@affine/core/commands'; } from '@affine/core/commands';
import { DocInfoService } from '@affine/core/modules/doc-info'; import { DocInfoService } from '@affine/core/modules/doc-info';
import type { Editor } from '@affine/core/modules/editor'; import type { Editor } from '@affine/core/modules/editor';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { WorkspaceFlavour } from '@affine/env/workspace'; import { WorkspaceFlavour } from '@affine/env/workspace';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { track } from '@affine/track'; import { track } from '@affine/track';
@@ -1,39 +0,0 @@
import { useService } from '@toeverything/infra';
import { useDebouncedState } from 'foxact/use-debounced-state';
import { useEffect } from 'react';
import { WorkspacePropertiesAdapter } from '../../modules/properties';
function getProxy<T extends object>(obj: T) {
return new Proxy(obj, {});
}
const useReactiveAdapter = (adapter: WorkspacePropertiesAdapter) => {
// hack: delay proxy creation to avoid unnecessary re-render + render in another component issue
const [proxy, setProxy] = useDebouncedState(adapter, 0);
useEffect(() => {
// TODO(@Peng): track which properties are used and then filter by property path change
// using Y.YEvent.path
function observe() {
setProxy(getProxy(adapter));
}
const disposables: (() => void)[] = [];
disposables.push(
adapter.workspace.docCollection.meta.docMetaUpdated.on(observe).dispose
);
adapter.properties.observeDeep(observe);
disposables.push(() => adapter.properties.unobserveDeep(observe));
return () => {
for (const dispose of disposables) {
dispose();
}
};
}, [adapter, setProxy]);
return proxy;
};
export function useCurrentWorkspacePropertiesAdapter() {
const adapter = useService(WorkspacePropertiesAdapter);
return useReactiveAdapter(adapter);
}
@@ -1,4 +1,5 @@
import { EditorSettingService } from '@affine/core/modules/editor-settting'; import { EditorSettingService } from '@affine/core/modules/editor-settting';
import { JournalService } from '@affine/core/modules/journal';
import { i18nTime } from '@affine/i18n'; import { i18nTime } from '@affine/i18n';
import { track } from '@affine/track'; import { track } from '@affine/track';
import { type DocCollection, Text } from '@blocksuite/affine/store'; import { type DocCollection, Text } from '@blocksuite/affine/store';
@@ -13,7 +14,6 @@ import dayjs from 'dayjs';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { WorkbenchService } from '../../modules/workbench'; import { WorkbenchService } from '../../modules/workbench';
import { useCurrentWorkspacePropertiesAdapter } from './use-affine-adapter';
import { useDocCollectionHelper } from './use-block-suite-workspace-helper'; import { useDocCollectionHelper } from './use-block-suite-workspace-helper';
type MaybeDate = Date | string | number; type MaybeDate = Date | string | number;
@@ -30,14 +30,16 @@ function toDayjs(j?: string | false) {
return day; return day;
} }
/**
* @deprecated use `JournalService` directly
*/
export const useJournalHelper = (docCollection: DocCollection) => { export const useJournalHelper = (docCollection: DocCollection) => {
const bsWorkspaceHelper = useDocCollectionHelper(docCollection); const bsWorkspaceHelper = useDocCollectionHelper(docCollection);
const { docsService, editorSettingService } = useServices({ const { docsService, editorSettingService, journalService } = useServices({
DocsService, DocsService,
EditorSettingService, EditorSettingService,
JournalService,
}); });
const adapter = useCurrentWorkspacePropertiesAdapter();
const { isPageJournal } = useJournalInfoHelper();
/** /**
* @internal * @internal
@@ -62,10 +64,10 @@ export const useJournalHelper = (docCollection: DocCollection) => {
note: editorSettingService.editorSetting.get('affine:note'), note: editorSettingService.editorSetting.get('affine:note'),
}; };
initDocFromProps(page, docProps); initDocFromProps(page, docProps);
adapter.setJournalPageDateString(page.id, title); journalService.setJournalDate(page.id, title);
return page; return page;
}, },
[adapter, bsWorkspaceHelper, docsService.list, editorSettingService] [journalService, bsWorkspaceHelper, docsService.list, editorSettingService]
); );
/** /**
@@ -73,17 +75,11 @@ export const useJournalHelper = (docCollection: DocCollection) => {
*/ */
const getJournalsByDate = useCallback( const getJournalsByDate = useCallback(
(maybeDate: MaybeDate) => { (maybeDate: MaybeDate) => {
const day = dayjs(maybeDate); return journalService.getJournalsByDate(
return Array.from(docCollection.docs.values()).filter(page => { dayjs(maybeDate).format(JOURNAL_DATE_FORMAT)
const pageId = page.id; );
if (!isPageJournal(pageId)) return false;
if (page.meta?.trash) return false;
const journalDate = adapter.getJournalPageDateString(page.id);
if (!journalDate) return false;
return day.isSame(journalDate, 'day');
});
}, },
[adapter, isPageJournal, docCollection.docs] [journalService]
); );
/** /**
@@ -92,34 +88,18 @@ export const useJournalHelper = (docCollection: DocCollection) => {
const getJournalByDate = useCallback( const getJournalByDate = useCallback(
(maybeDate: MaybeDate) => { (maybeDate: MaybeDate) => {
const pages = getJournalsByDate(maybeDate); const pages = getJournalsByDate(maybeDate);
if (pages.length) return pages[0].getDoc(); if (pages.length) return pages[0];
return _createJournal(maybeDate); return _createJournal(maybeDate);
}, },
[_createJournal, getJournalsByDate] [_createJournal, getJournalsByDate]
); );
const appendContentToToday = useCallback(
async (content: string) => {
if (!content) return;
const page = getJournalByDate(dayjs().format(JOURNAL_DATE_FORMAT));
if (!page) return;
const blockId = page.addBlock(
'affine:paragraph',
{ text: new page.Text(content) },
page.getBlockByFlavour('affine:note')[0].id
);
return { page, blockId };
},
[getJournalByDate]
);
return useMemo( return useMemo(
() => ({ () => ({
getJournalsByDate, getJournalsByDate,
getJournalByDate, getJournalByDate,
appendContentToToday,
}), }),
[getJournalsByDate, getJournalByDate, appendContentToToday] [getJournalsByDate, getJournalByDate]
); );
}; };
@@ -164,31 +144,33 @@ export const useJournalRouteHelper = (docCollection: DocCollection) => {
); );
}; };
// get journal info that don't rely on `docCollection` /**
* @deprecated use `JournalService` directly
*/
export const useJournalInfoHelper = (pageId?: string | null) => { export const useJournalInfoHelper = (pageId?: string | null) => {
const adapter = useCurrentWorkspacePropertiesAdapter(); const journalService = useService(JournalService);
const isPageJournal = useCallback( const isPageJournal = useCallback(
(pageId: string) => { (pageId: string) => {
return !!adapter.getJournalPageDateString(pageId); return !!journalService.journalDate$(pageId).value;
}, },
[adapter] [journalService]
); );
const isPageTodayJournal = useCallback( const isPageTodayJournal = useCallback(
(pageId: string) => { (pageId: string) => {
const date = dayjs().format(JOURNAL_DATE_FORMAT); const date = dayjs().format(JOURNAL_DATE_FORMAT);
const d = adapter.getJournalPageDateString(pageId); const d = journalService.journalDate$(pageId).value;
return isPageJournal(pageId) && d === date; return isPageJournal(pageId) && d === date;
}, },
[adapter, isPageJournal] [isPageJournal, journalService]
); );
const getJournalDateString = useCallback( const getJournalDateString = useCallback(
(pageId: string) => { (pageId: string) => {
return adapter.getJournalPageDateString(pageId); return journalService.journalDate$(pageId).value;
}, },
[adapter] [journalService]
); );
const getLocalizedJournalDateString = useCallback( const getLocalizedJournalDateString = useCallback(
@@ -1,6 +1,6 @@
import { toast } from '@affine/component'; import { toast } from '@affine/component';
import { CollectionService } from '@affine/core/modules/collection'; import { CollectionService } from '@affine/core/modules/collection';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra'; import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
@@ -1,4 +1,4 @@
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import type { Tag } from '@affine/core/modules/tag'; import type { Tag } from '@affine/core/modules/tag';
import { TagService } from '@affine/core/modules/tag'; import { TagService } from '@affine/core/modules/tag';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
@@ -9,8 +9,10 @@ import { useBlockSuiteMetaHelper } from '@affine/core/components/hooks/affine/us
import { useTrashModalHelper } from '@affine/core/components/hooks/affine/use-trash-modal-helper'; import { useTrashModalHelper } from '@affine/core/components/hooks/affine/use-trash-modal-helper';
import { useCatchEventCallback } from '@affine/core/components/hooks/use-catch-event-hook'; import { useCatchEventCallback } from '@affine/core/components/hooks/use-catch-event-hook';
import { DocInfoService } from '@affine/core/modules/doc-info'; import { DocInfoService } from '@affine/core/modules/doc-info';
import { FavoriteService } from '@affine/core/modules/favorite'; import {
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; CompatibleFavoriteItemsAdapter,
FavoriteService,
} from '@affine/core/modules/favorite';
import { WorkbenchService } from '@affine/core/modules/workbench'; import { WorkbenchService } from '@affine/core/modules/workbench';
import type { Collection, DeleteCollectionInfo } from '@affine/env/filter'; import type { Collection, DeleteCollectionInfo } from '@affine/env/filter';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
@@ -1,4 +1,4 @@
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { ShareDocsListService } from '@affine/core/modules/share-doc'; import { ShareDocsListService } from '@affine/core/modules/share-doc';
import type { Collection, Filter } from '@affine/env/filter'; import type { Collection, Filter } from '@affine/env/filter';
import { PublicPageMode } from '@affine/graphql'; import { PublicPageMode } from '@affine/graphql';
@@ -1,7 +1,7 @@
import type { MenuItemProps } from '@affine/component'; import type { MenuItemProps } from '@affine/component';
import { Menu, MenuItem } from '@affine/component'; import { Menu, MenuItem } from '@affine/component';
import { useDeleteCollectionInfo } from '@affine/core/components/hooks/affine/use-delete-collection-info'; import { useDeleteCollectionInfo } from '@affine/core/components/hooks/affine/use-delete-collection-info';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { WorkbenchService } from '@affine/core/modules/workbench'; import { WorkbenchService } from '@affine/core/modules/workbench';
import type { Collection } from '@affine/env/filter'; import type { Collection } from '@affine/env/filter';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
@@ -1,5 +1,5 @@
import { Button, IconButton, Tooltip } from '@affine/component'; import { Button, IconButton, Tooltip } from '@affine/component';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import type { Collection } from '@affine/env/filter'; import type { Collection } from '@affine/env/filter';
import { Trans, useI18n } from '@affine/i18n'; import { Trans, useI18n } from '@affine/i18n';
import type { DocMeta } from '@blocksuite/affine/store'; import type { DocMeta } from '@blocksuite/affine/store';
@@ -1,6 +1,6 @@
import { IconButton, Menu, toast } from '@affine/component'; import { IconButton, Menu, toast } from '@affine/component';
import { useBlockSuiteDocMeta } from '@affine/core/components/hooks/use-block-suite-page-meta'; import { useBlockSuiteDocMeta } from '@affine/core/components/hooks/use-block-suite-page-meta';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { ShareDocsListService } from '@affine/core/modules/share-doc'; import { ShareDocsListService } from '@affine/core/modules/share-doc';
import { PublicPageMode } from '@affine/graphql'; import { PublicPageMode } from '@affine/graphql';
import { Trans, useI18n } from '@affine/i18n'; import { Trans, useI18n } from '@affine/i18n';
@@ -2,7 +2,7 @@ import { IconButton } from '@affine/component';
import { useCatchEventCallback } from '@affine/core/components/hooks/use-catch-event-hook'; import { useCatchEventCallback } from '@affine/core/components/hooks/use-catch-event-hook';
import { PagePreview } from '@affine/core/components/page-list/page-content-preview'; import { PagePreview } from '@affine/core/components/page-list/page-content-preview';
import { IsFavoriteIcon } from '@affine/core/components/pure/icons'; import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { import {
WorkbenchLink, WorkbenchLink,
type WorkbenchLinkProps, type WorkbenchLinkProps,
@@ -1,7 +1,7 @@
import { IconButton } from '@affine/component'; import { IconButton } from '@affine/component';
import type { CollectionMeta } from '@affine/core/components/page-list'; import type { CollectionMeta } from '@affine/core/components/page-list';
import { IsFavoriteIcon } from '@affine/core/components/pure/icons'; import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { WorkbenchLink } from '@affine/core/modules/workbench'; import { WorkbenchLink } from '@affine/core/modules/workbench';
import { ViewLayersIcon } from '@blocksuite/icons/rc'; import { ViewLayersIcon } from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra'; import { useLiveData, useService } from '@toeverything/infra';
@@ -1,6 +1,6 @@
import { IconButton } from '@affine/component'; import { IconButton } from '@affine/component';
import { IsFavoriteIcon } from '@affine/core/components/pure/icons'; import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import type { Tag } from '@affine/core/modules/tag'; import type { Tag } from '@affine/core/modules/tag';
import { WorkbenchLink } from '@affine/core/modules/workbench'; import { WorkbenchLink } from '@affine/core/modules/workbench';
import { useLiveData, useService } from '@toeverything/infra'; import { useLiveData, useService } from '@toeverything/infra';
@@ -5,7 +5,7 @@ import {
WorkspaceScope, WorkspaceScope,
} from '@toeverything/infra'; } from '@toeverything/infra';
import { WorkspacePropertiesAdapter } from '../properties'; import { JournalService } from '../journal';
import { DocDisplayMetaService } from './services/doc-display-meta'; import { DocDisplayMetaService } from './services/doc-display-meta';
export { DocDisplayMetaService }; export { DocDisplayMetaService };
@@ -14,7 +14,7 @@ export function configureDocDisplayMetaModule(framework: Framework) {
framework framework
.scope(WorkspaceScope) .scope(WorkspaceScope)
.service(DocDisplayMetaService, [ .service(DocDisplayMetaService, [
WorkspacePropertiesAdapter, JournalService,
DocsService, DocsService,
FeatureFlagService, FeatureFlagService,
]); ]);
@@ -29,7 +29,7 @@ import { LiveData, Service } from '@toeverything/infra';
import type { Dayjs } from 'dayjs'; import type { Dayjs } from 'dayjs';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import type { WorkspacePropertiesAdapter } from '../../properties'; import type { JournalService } from '../../journal';
type IconType = 'rc' | 'lit'; type IconType = 'rc' | 'lit';
interface DocDisplayIconOptions<T extends IconType> { interface DocDisplayIconOptions<T extends IconType> {
@@ -83,7 +83,7 @@ const icons = { rc: rcIcons, lit: litIcons } as {
export class DocDisplayMetaService extends Service { export class DocDisplayMetaService extends Service {
constructor( constructor(
private readonly propertiesAdapter: WorkspacePropertiesAdapter, private readonly journalService: JournalService,
private readonly docsService: DocsService, private readonly docsService: DocsService,
private readonly featureFlagService: FeatureFlagService private readonly featureFlagService: FeatureFlagService
) { ) {
@@ -110,7 +110,7 @@ export class DocDisplayMetaService extends Service {
// journal icon // journal icon
const journalDate = this._toDayjs( const journalDate = this._toDayjs(
this.propertiesAdapter.getJournalPageDateString(docId) this.journalService.journalDate$(docId).value
); );
if (journalDate) { if (journalDate) {
if (!options?.compareDate) return iconSet.TodayIcon; if (!options?.compareDate) return iconSet.TodayIcon;
@@ -148,8 +148,7 @@ export class DocDisplayMetaService extends Service {
const doc = get(this.docsService.list.doc$(docId)); const doc = get(this.docsService.list.doc$(docId));
const docTitle = doc ? get(doc.title$) : undefined; const docTitle = doc ? get(doc.title$) : undefined;
const journalDateString = const journalDateString = get(this.journalService.journalDate$(docId));
this.propertiesAdapter.getJournalPageDateString(docId);
// journal // journal
if (journalDateString) { if (journalDateString) {
@@ -10,7 +10,7 @@ import {
useEditCollection, useEditCollection,
} from '@affine/core/components/page-list'; } from '@affine/core/components/page-list';
import { CollectionService } from '@affine/core/modules/collection'; import { CollectionService } from '@affine/core/modules/collection';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { ShareDocsListService } from '@affine/core/modules/share-doc'; import { ShareDocsListService } from '@affine/core/modules/share-doc';
import type { AffineDNDData } from '@affine/core/types/dnd'; import type { AffineDNDData } from '@affine/core/types/dnd';
import type { Collection } from '@affine/env/filter'; import type { Collection } from '@affine/env/filter';
@@ -8,7 +8,7 @@ import { usePageHelper } from '@affine/core/components/blocksuite/block-suite-pa
import { useDeleteCollectionInfo } from '@affine/core/components/hooks/affine/use-delete-collection-info'; import { useDeleteCollectionInfo } from '@affine/core/components/hooks/affine/use-delete-collection-info';
import { IsFavoriteIcon } from '@affine/core/components/pure/icons'; import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
import { CollectionService } from '@affine/core/modules/collection'; import { CollectionService } from '@affine/core/modules/collection';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { WorkbenchService } from '@affine/core/modules/workbench'; import { WorkbenchService } from '@affine/core/modules/workbench';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { track } from '@affine/track'; import { track } from '@affine/track';
@@ -56,7 +56,6 @@ export const ExplorerDocNode = ({
DocDisplayMetaService, DocDisplayMetaService,
FeatureFlagService, FeatureFlagService,
}); });
// const pageInfoAdapter = useCurrentWorkspacePropertiesAdapter();
const active = const active =
useLiveData(globalContextService.globalContext.docId.$) === docId; useLiveData(globalContextService.globalContext.docId.$) === docId;
@@ -9,7 +9,7 @@ import { usePageHelper } from '@affine/core/components/blocksuite/block-suite-pa
import { useBlockSuiteMetaHelper } from '@affine/core/components/hooks/affine/use-block-suite-meta-helper'; import { useBlockSuiteMetaHelper } from '@affine/core/components/hooks/affine/use-block-suite-meta-helper';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks'; import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import { IsFavoriteIcon } from '@affine/core/components/pure/icons'; import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { WorkbenchService } from '@affine/core/modules/workbench'; import { WorkbenchService } from '@affine/core/modules/workbench';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { track } from '@affine/track'; import { track } from '@affine/track';
@@ -15,11 +15,11 @@ import {
useSelectDoc, useSelectDoc,
useSelectTag, useSelectTag,
} from '@affine/core/components/page-list/selector'; } from '@affine/core/components/page-list/selector';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { import {
type FolderNode, type FolderNode,
OrganizeService, OrganizeService,
} from '@affine/core/modules/organize'; } from '@affine/core/modules/organize';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
import { WorkbenchService } from '@affine/core/modules/workbench'; import { WorkbenchService } from '@affine/core/modules/workbench';
import type { AffineDNDData } from '@affine/core/types/dnd'; import type { AffineDNDData } from '@affine/core/types/dnd';
import { Unreachable } from '@affine/env/constant'; import { Unreachable } from '@affine/env/constant';
@@ -1,6 +1,6 @@
import { MenuItem } from '@affine/component'; import { MenuItem } from '@affine/component';
import { IsFavoriteIcon } from '@affine/core/components/pure/icons'; import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { useLiveData, useService } from '@toeverything/infra'; import { useLiveData, useService } from '@toeverything/infra';
import { useMemo } from 'react'; import { useMemo } from 'react';
@@ -1,6 +1,6 @@
import { IconButton, useConfirmModal } from '@affine/component'; import { IconButton, useConfirmModal } from '@affine/component';
import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree'; import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree';
import { MigrationFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { MigrationFavoriteItemsAdapter } from '@affine/core/modules/favorite';
import { Trans, useI18n } from '@affine/i18n'; import { Trans, useI18n } from '@affine/i18n';
import { track } from '@affine/track'; import { track } from '@affine/track';
import { BroomIcon, HelpIcon } from '@blocksuite/icons/rc'; import { BroomIcon, HelpIcon } from '@blocksuite/icons/rc';
@@ -8,16 +8,26 @@ import {
import { AuthService } from '../cloud'; import { AuthService } from '../cloud';
import { FavoriteList } from './entities/favorite-list'; import { FavoriteList } from './entities/favorite-list';
import { FavoriteService } from './services/favorite'; import { FavoriteService } from './services/favorite';
import {
CompatibleFavoriteItemsAdapter,
MigrationFavoriteItemsAdapter,
} from './services/old/adapter';
import { FavoriteStore } from './stores/favorite'; import { FavoriteStore } from './stores/favorite';
export { FavoriteSupportType, isFavoriteSupportType } from './constant'; export { FavoriteSupportType, isFavoriteSupportType } from './constant';
export type { FavoriteList } from './entities/favorite-list'; export type { FavoriteList } from './entities/favorite-list';
export { FavoriteService } from './services/favorite'; export { FavoriteService } from './services/favorite';
export {
CompatibleFavoriteItemsAdapter,
MigrationFavoriteItemsAdapter,
} from './services/old/adapter';
export function configureFavoriteModule(framework: Framework) { export function configureFavoriteModule(framework: Framework) {
framework framework
.scope(WorkspaceScope) .scope(WorkspaceScope)
.service(FavoriteService) .service(FavoriteService)
.entity(FavoriteList, [FavoriteStore]) .entity(FavoriteList, [FavoriteStore])
.store(FavoriteStore, [AuthService, WorkspaceDBService, WorkspaceService]); .store(FavoriteStore, [AuthService, WorkspaceDBService, WorkspaceService])
.service(MigrationFavoriteItemsAdapter, [WorkspaceService])
.service(CompatibleFavoriteItemsAdapter, [FavoriteService]);
} }
@@ -6,7 +6,8 @@ import { LiveData, Service } from '@toeverything/infra';
import { defaultsDeep } from 'lodash-es'; import { defaultsDeep } from 'lodash-es';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import type { FavoriteService, FavoriteSupportType } from '../../favorite'; import type { FavoriteSupportType } from '../../constant';
import type { FavoriteService } from '../favorite';
import { import {
PagePropertyType, PagePropertyType,
PageSystemPropertyId, PageSystemPropertyId,
@@ -23,8 +24,10 @@ const AFFINE_PROPERTIES_ID = 'affine:workspace-properties';
* May abstract the adapter for each property type, e.g. PagePropertiesAdapter, SchemaAdapter, etc. * May abstract the adapter for each property type, e.g. PagePropertiesAdapter, SchemaAdapter, etc.
* So that the adapter could be more focused and easier to maintain (like assigning default values) * So that the adapter could be more focused and easier to maintain (like assigning default values)
* However the properties for an abstraction may not be limited to a single yjs map. * However the properties for an abstraction may not be limited to a single yjs map.
*
* @deprecated use docService.doc.properties$
*/ */
export class WorkspacePropertiesAdapter extends Service { class WorkspacePropertiesAdapter {
// provides a easy-to-use interface for workspace properties // provides a easy-to-use interface for workspace properties
public readonly proxy: WorkspaceAffineProperties; public readonly proxy: WorkspaceAffineProperties;
public readonly properties: Y.Map<any>; public readonly properties: Y.Map<any>;
@@ -38,7 +41,6 @@ export class WorkspacePropertiesAdapter extends Service {
} }
constructor(public readonly workspaceService: WorkspaceService) { constructor(public readonly workspaceService: WorkspaceService) {
super();
// check if properties exists, if not, create one // check if properties exists, if not, create one
const rootDoc = workspaceService.workspace.docCollection.doc; const rootDoc = workspaceService.workspace.docCollection.doc;
this.properties = rootDoc.getMap(AFFINE_PROPERTIES_ID); this.properties = rootDoc.getMap(AFFINE_PROPERTIES_ID);
@@ -165,7 +167,9 @@ export class WorkspacePropertiesAdapter extends Service {
} }
export class MigrationFavoriteItemsAdapter extends Service { export class MigrationFavoriteItemsAdapter extends Service {
constructor(private readonly adapter: WorkspacePropertiesAdapter) { adapter = new WorkspacePropertiesAdapter(this.workspaceService);
constructor(public readonly workspaceService: WorkspaceService) {
super(); super();
} }
+2 -2
View File
@@ -16,11 +16,11 @@ import { configureFavoriteModule } from './favorite';
import { configureFindInPageModule } from './find-in-page'; import { configureFindInPageModule } from './find-in-page';
import { configureI18nModule } from './i18n'; import { configureI18nModule } from './i18n';
import { configureImportTemplateModule } from './import-template'; import { configureImportTemplateModule } from './import-template';
import { configureJournalModule } from './journal';
import { configureNavigationModule } from './navigation'; import { configureNavigationModule } from './navigation';
import { configureOrganizeModule } from './organize'; import { configureOrganizeModule } from './organize';
import { configurePeekViewModule } from './peek-view'; import { configurePeekViewModule } from './peek-view';
import { configurePermissionsModule } from './permissions'; import { configurePermissionsModule } from './permissions';
import { configureWorkspacePropertiesModule } from './properties';
import { configureQuickSearchModule } from './quicksearch'; import { configureQuickSearchModule } from './quicksearch';
import { configureShareDocsModule } from './share-doc'; import { configureShareDocsModule } from './share-doc';
import { configureShareSettingModule } from './share-setting'; import { configureShareSettingModule } from './share-setting';
@@ -36,7 +36,6 @@ export function configureCommonModules(framework: Framework) {
configureCollectionModule(framework); configureCollectionModule(framework);
configureNavigationModule(framework); configureNavigationModule(framework);
configureTagModule(framework); configureTagModule(framework);
configureWorkspacePropertiesModule(framework);
configureCloudModule(framework); configureCloudModule(framework);
configureQuotaModule(framework); configureQuotaModule(framework);
configurePermissionsModule(framework); configurePermissionsModule(framework);
@@ -61,4 +60,5 @@ export function configureCommonModules(framework: Framework) {
configureUserspaceModule(framework); configureUserspaceModule(framework);
configureDocInfoModule(framework); configureDocInfoModule(framework);
configureAppSidebarModule(framework); configureAppSidebarModule(framework);
configureJournalModule(framework);
} }
@@ -0,0 +1,23 @@
import {
DocScope,
DocService,
DocsService,
type Framework,
WorkspaceScope,
} from '@toeverything/infra';
import { JournalService } from './services/journal';
import { JournalDocService } from './services/journal-doc';
import { JournalStore } from './store/journal';
export { JournalService } from './services/journal';
export { JournalDocService } from './services/journal-doc';
export function configureJournalModule(framework: Framework) {
framework
.scope(WorkspaceScope)
.service(JournalService, [JournalStore])
.store(JournalStore, [DocsService])
.scope(DocScope)
.service(JournalDocService, [DocService, JournalService]);
}
@@ -0,0 +1,20 @@
import { type DocService, Service } from '@toeverything/infra';
import type { JournalService } from './journal';
export class JournalDocService extends Service {
constructor(
private readonly docService: DocService,
private readonly journalService: JournalService
) {
super();
}
readonly journalDate$ = this.journalService.journalDate$(
this.docService.doc.id
);
setJournalDate(date: string) {
this.journalService.setJournalDate(this.docService.doc.id, date);
}
}
@@ -0,0 +1,21 @@
import { LiveData, Service } from '@toeverything/infra';
import type { JournalStore } from '../store/journal';
export class JournalService extends Service {
constructor(private readonly store: JournalStore) {
super();
}
journalDate$(docId: string) {
return LiveData.from(this.store.watchDocJournalDate(docId), undefined);
}
setJournalDate(docId: string, date: string) {
this.store.setDocJournalDate(docId, date);
}
getJournalsByDate(date: string) {
return this.store.getDocsByJournalDate(date);
}
}
@@ -0,0 +1,43 @@
import type { DocsService } from '@toeverything/infra';
import { LiveData, Store } from '@toeverything/infra';
import type { Observable } from 'rxjs';
function isJournalString(j?: string | false) {
return j ? !!j?.match(/^\d{4}-\d{2}-\d{2}$/) : false;
}
export class JournalStore extends Store {
constructor(private readonly docsService: DocsService) {
super();
}
watchDocJournalDate(docId: string): Observable<string | undefined> {
return LiveData.computed(get => {
const doc = get(this.docsService.list.doc$(docId));
if (!doc) {
// if doc not exists
return undefined;
}
const journal = get(doc.properties$.selector(p => p.journal));
if (journal && !isJournalString(journal)) {
return undefined;
}
return journal ?? undefined;
});
}
setDocJournalDate(docId: string, date: string) {
const doc = this.docsService.list.doc$(docId).value;
if (!doc) {
// doc not exists, do nothing
return;
}
doc.setProperty('journal', date);
}
getDocsByJournalDate(date: string) {
return this.docsService.list.docs$.value.filter(
doc => doc.properties$.value.journal === date
);
}
}
@@ -1,29 +0,0 @@
export {
CompatibleFavoriteItemsAdapter,
MigrationFavoriteItemsAdapter,
WorkspacePropertiesAdapter,
} from './services/adapter';
export { WorkspaceLegacyProperties } from './services/legacy-properties';
import {
type Framework,
WorkspaceScope,
WorkspaceService,
} from '@toeverything/infra';
import { FavoriteService } from '../favorite';
import {
CompatibleFavoriteItemsAdapter,
MigrationFavoriteItemsAdapter,
WorkspacePropertiesAdapter,
} from './services/adapter';
import { WorkspaceLegacyProperties } from './services/legacy-properties';
export function configureWorkspacePropertiesModule(framework: Framework) {
framework
.scope(WorkspaceScope)
.service(WorkspaceLegacyProperties, [WorkspaceService])
.service(WorkspacePropertiesAdapter, [WorkspaceService])
.service(MigrationFavoriteItemsAdapter, [WorkspacePropertiesAdapter])
.service(CompatibleFavoriteItemsAdapter, [FavoriteService]);
}
@@ -1,86 +0,0 @@
import type { Tag } from '@affine/env/filter';
import type { DocsPropertiesMeta } from '@blocksuite/affine/store';
import type { WorkspaceService } from '@toeverything/infra';
import { LiveData, Service } from '@toeverything/infra';
import { Observable } from 'rxjs';
/**
* @deprecated use WorkspacePropertiesAdapter instead (later)
*/
export class WorkspaceLegacyProperties extends Service {
constructor(private readonly workspaceService: WorkspaceService) {
super();
}
get workspaceId() {
return this.workspaceService.workspace.id;
}
get properties() {
return this.workspaceService.workspace.docCollection.meta.properties;
}
get tagOptions() {
return this.properties.tags?.options ?? [];
}
updateProperties = (properties: DocsPropertiesMeta) => {
this.workspaceService.workspace.docCollection.meta.setProperties(
properties
);
};
subscribe(cb: () => void) {
const disposable =
this.workspaceService.workspace.docCollection.meta.docMetaUpdated.on(cb);
return disposable.dispose;
}
properties$ = LiveData.from(
new Observable<DocsPropertiesMeta>(sub => {
return this.subscribe(() => sub.next(this.properties));
}),
this.properties
);
tagOptions$ = LiveData.from(
new Observable<Tag[]>(sub => {
return this.subscribe(() => sub.next(this.tagOptions));
}),
this.tagOptions
);
updateTagOptions = (options: Tag[]) => {
this.updateProperties({
...this.properties,
tags: {
options,
},
});
};
updateTagOption = (id: string, option: Tag) => {
this.updateTagOptions(this.tagOptions.map(o => (o.id === id ? option : o)));
};
removeTagOption = (id: string) => {
this.workspaceService.workspace.docCollection.doc.transact(() => {
this.updateTagOptions(this.tagOptions.filter(o => o.id !== id));
// need to remove tag from all pages
this.workspaceService.workspace.docCollection.docs.forEach(doc => {
const tags = doc.meta?.tags ?? [];
if (tags.includes(id)) {
this.updatePageTags(
doc.id,
tags.filter(t => t !== id)
);
}
});
});
};
updatePageTags = (pageId: string, tags: string[]) => {
this.workspaceService.workspace.docCollection.setDocMeta(pageId, {
tags,
});
};
}
@@ -7,9 +7,9 @@ import {
DocsService, DocsService,
type Framework, type Framework,
WorkspaceScope, WorkspaceScope,
WorkspaceService,
} from '@toeverything/infra'; } from '@toeverything/infra';
import { WorkspaceLegacyProperties } from '../properties';
import { Tag } from './entities/tag'; import { Tag } from './entities/tag';
import { TagList } from './entities/tag-list'; import { TagList } from './entities/tag-list';
import { TagService } from './service/tag'; import { TagService } from './service/tag';
@@ -19,7 +19,7 @@ export function configureTagModule(framework: Framework) {
framework framework
.scope(WorkspaceScope) .scope(WorkspaceScope)
.service(TagService) .service(TagService)
.store(TagStore, [WorkspaceLegacyProperties]) .store(TagStore, [WorkspaceService])
.entity(TagList, [TagStore, DocsService]) .entity(TagList, [TagStore, DocsService])
.entity(Tag, [TagStore, DocsService]); .entity(Tag, [TagStore, DocsService]);
} }
@@ -1,24 +1,43 @@
import type { Tag as TagSchema } from '@affine/env/filter'; import type { Tag, Tag as TagSchema } from '@affine/env/filter';
import { Store } from '@toeverything/infra'; import type { DocsPropertiesMeta } from '@blocksuite/affine/store';
import type { WorkspaceService } from '@toeverything/infra';
import { LiveData, Store } from '@toeverything/infra';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
import { Observable } from 'rxjs';
import type { WorkspaceLegacyProperties } from '../../properties';
export class TagStore extends Store { export class TagStore extends Store {
constructor(private readonly properties: WorkspaceLegacyProperties) { get properties() {
return this.workspaceService.workspace.docCollection.meta.properties;
}
get tagOptions() {
return this.properties.tags?.options ?? [];
}
tagOptions$ = LiveData.from(
new Observable<Tag[]>(sub => {
return this.subscribe(() => sub.next(this.tagOptions));
}),
this.tagOptions
);
subscribe(cb: () => void) {
const disposable =
this.workspaceService.workspace.docCollection.meta.docMetaUpdated.on(cb);
return disposable.dispose;
}
constructor(private readonly workspaceService: WorkspaceService) {
super(); super();
} }
watchTagIds() { watchTagIds() {
return this.properties.tagOptions$ return this.tagOptions$.map(tags => tags.map(tag => tag.id)).asObservable();
.map(tags => tags.map(tag => tag.id))
.asObservable();
} }
createNewTag(value: string, color: string) { createNewTag(value: string, color: string) {
const newId = nanoid(); const newId = nanoid();
this.properties.updateTagOptions([ this.updateTagOptions([
...this.properties.tagOptions$.value, ...this.tagOptions$.value,
{ {
id: newId, id: newId,
value, value,
@@ -30,24 +49,65 @@ export class TagStore extends Store {
return newId; return newId;
} }
updateProperties = (properties: DocsPropertiesMeta) => {
this.workspaceService.workspace.docCollection.meta.setProperties(
properties
);
};
updateTagOptions = (options: Tag[]) => {
this.updateProperties({
...this.properties,
tags: {
options,
},
});
};
updateTagOption = (id: string, option: Tag) => {
this.updateTagOptions(this.tagOptions.map(o => (o.id === id ? option : o)));
};
removeTagOption = (id: string) => {
this.workspaceService.workspace.docCollection.doc.transact(() => {
this.updateTagOptions(this.tagOptions.filter(o => o.id !== id));
// need to remove tag from all pages
this.workspaceService.workspace.docCollection.docs.forEach(doc => {
const tags = doc.meta?.tags ?? [];
if (tags.includes(id)) {
this.updatePageTags(
doc.id,
tags.filter(t => t !== id)
);
}
});
});
};
updatePageTags = (pageId: string, tags: string[]) => {
this.workspaceService.workspace.docCollection.setDocMeta(pageId, {
tags,
});
};
deleteTag(id: string) { deleteTag(id: string) {
this.properties.removeTagOption(id); this.removeTagOption(id);
} }
watchTagInfo(id: string) { watchTagInfo(id: string) {
return this.properties.tagOptions$.map( return this.tagOptions$.map(
tags => tags.find(tag => tag.id === id) as TagSchema | undefined tags => tags.find(tag => tag.id === id) as TagSchema | undefined
); );
} }
updateTagInfo(id: string, tagInfo: Partial<TagSchema>) { updateTagInfo(id: string, tagInfo: Partial<TagSchema>) {
const tag = this.properties.tagOptions$.value.find(tag => tag.id === id) as const tag = this.tagOptions$.value.find(tag => tag.id === id) as
| TagSchema | TagSchema
| undefined; | undefined;
if (!tag) { if (!tag) {
return; return;
} }
this.properties.updateTagOption(id, { this.updateTagOption(id, {
id: id, id: id,
value: tag.value, value: tag.value,
color: tag.color, color: tag.color,