Skip to content

Commit

Permalink
feat(core): template-doc settings for user-worksapce scope
Browse files Browse the repository at this point in the history
  • Loading branch information
CatsJuice committed Jan 8, 2025
1 parent 0905ead commit f8b58fc
Show file tree
Hide file tree
Showing 12 changed files with 239 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ArrowRightSmallIcon } from '@blocksuite/icons/rc';
import { FrameworkScope } from '@toeverything/infra';
import { useCallback } from 'react';

import { TemplateDocSetting } from '../template';
import { DeleteLeaveWorkspace } from './delete-leave-workspace';
import { EnableCloudPanel } from './enable-cloud';
import { DesktopExportPanel } from './export';
Expand Down Expand Up @@ -60,6 +61,7 @@ export const WorkspaceSettingDetail = ({
})}
subtitle={t['com.affine.settings.workspace.description']()}
/>
<TemplateDocSetting />
<SettingWrapper title={t['Info']()}>
<SettingRow
name={t['Workspace Profile']()}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { style } from '@vanilla-extract/css';

export const menuItem = style({
display: 'flex',
alignItems: 'center',
gap: 8,
});
export const menuItemIcon = style({
fontSize: 24,
lineHeight: 0,
});
export const menuItemText = style({
fontSize: 14,
width: 0,
flex: 1,
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
overflow: 'hidden',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { Button, Menu, MenuItem } from '@affine/component';
import { type DocRecord, DocsService } from '@affine/core/modules/doc';
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
import { TemplateDocService } from '@affine/core/modules/template-doc';
import { useLiveData, useService, useServices } from '@toeverything/infra';
import { useCallback, useState } from 'react';

import * as styles from './template.css';

export const TemplateDocSetting = () => {
const { featureFlagService, templateDocService } = useServices({
FeatureFlagService,
TemplateDocService,
});
const setting = templateDocService.setting;

const enabled = useLiveData(featureFlagService.flags.enable_template_doc.$);
const loading = useLiveData(setting.loading$);
const pageTemplateDocId = useLiveData(setting.pageTemplateDocId$);
const journalTemplateDocId = useLiveData(setting.journalTemplateDocId$);

const updatePageTemplate = useCallback(
(id?: string) => {
setting.updatePageTemplateDocId(id);
},
[setting]
);

const updateJournalTemplate = useCallback(
(id?: string) => {
setting.updateJournalTemplateDocId(id);
},
[setting]
);

if (!enabled) return null;
if (loading) return null;

return (
<div>
Normal Page:
<TemplateSelector
current={pageTemplateDocId}
onChange={updatePageTemplate}
/>
<br />
Journal:
<TemplateSelector
current={journalTemplateDocId}
onChange={updateJournalTemplate}
/>
</div>
);
};

interface TemplateSelectorProps {
current?: string;
onChange?: (id?: string) => void;
}
const TemplateSelector = ({ current, onChange }: TemplateSelectorProps) => {
const docsService = useService(DocsService);
const doc = useLiveData(current ? docsService.list.doc$(current) : null);
const isInTrash = useLiveData(doc?.trash$);

return (
<Menu items={<List onChange={onChange} />}>
<Button>{isInTrash ? 'Doc is removed' : (doc?.id ?? 'Unset')}</Button>
</Menu>
);
};

const List = ({ onChange }: { onChange?: (id?: string) => void }) => {
const list = useService(TemplateDocService).list;
const [docs] = useState(list.getTemplateDocs());

const handleClick = useCallback(
(id: string) => {
onChange?.(id);
},
[onChange]
);

return docs.map(doc => {
return <DocItem key={doc.id} doc={doc} onClick={handleClick} />;
});
};

interface DocItemProps {
doc: DocRecord;
onClick?: (id: string) => void;
}
const DocItem = ({ doc, onClick }: DocItemProps) => {
const docDisplayService = useService(DocDisplayMetaService);
const Icon = useLiveData(docDisplayService.icon$(doc.id));
const title = useLiveData(docDisplayService.title$(doc.id));

const handleClick = useCallback(() => {
onClick?.(doc.id);
}, [doc.id, onClick]);

return (
<MenuItem onClick={handleClick}>
<li className={styles.menuItem}>
<Icon className={styles.menuItemIcon} />
<span className={styles.menuItemText}>{title}</span>
</li>
</MenuItem>
);
};
3 changes: 2 additions & 1 deletion packages/frontend/core/src/modules/db/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Framework } from '@toeverything/infra';

import { WorkspaceServerService } from '../cloud';
import { WorkspaceScope, WorkspaceService } from '../workspace';
import { WorkspaceDB } from './entities/db';
import { WorkspaceDBTable } from './entities/table';
Expand All @@ -12,7 +13,7 @@ export { transformWorkspaceDBLocalToCloud } from './services/db';
export function configureWorkspaceDBModule(framework: Framework) {
framework
.scope(WorkspaceScope)
.service(WorkspaceDBService, [WorkspaceService])
.service(WorkspaceDBService, [WorkspaceService, WorkspaceServerService])
.entity(WorkspaceDB)
.entity(WorkspaceDBTable, [WorkspaceService]);
}
4 changes: 4 additions & 0 deletions packages/frontend/core/src/modules/db/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ export const AFFiNE_WORKSPACE_USERDATA_DB_SCHEMA = {
key: f.string().primaryKey(),
index: f.string(),
},
settings: {
key: f.string().primaryKey(),
value: f.json(),
},
} as const satisfies DBSchemaBuilder;
export type AFFiNEWorkspaceUserdataDbSchema =
typeof AFFiNE_WORKSPACE_USERDATA_DB_SCHEMA;
26 changes: 25 additions & 1 deletion packages/frontend/core/src/modules/db/services/db.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import {
createORMClient,
type DocStorage,
LiveData,
ObjectPool,
Service,
YjsDBAdapter,
} from '@toeverything/infra';
import { Doc as YDoc } from 'yjs';

import { AuthService, type WorkspaceServerService } from '../../cloud';
import type { WorkspaceService } from '../../workspace';
import { WorkspaceDB, type WorkspaceDBWithTables } from '../entities/db';
import {
Expand All @@ -32,7 +34,10 @@ export class WorkspaceDBService extends Service {
},
});

constructor(private readonly workspaceService: WorkspaceService) {
constructor(
private readonly workspaceService: WorkspaceService,
private readonly workspaceServerService: WorkspaceServerService
) {
super();
this.db = this.framework.createEntity(
WorkspaceDB<AFFiNEWorkspaceDbSchema>,
Expand Down Expand Up @@ -96,6 +101,25 @@ export class WorkspaceDBService extends Service {
return newDB as WorkspaceDBWithTables<AFFiNEWorkspaceUserdataDbSchema>;
}

authService = this.workspaceServerService.server?.scope.get(AuthService);
public get userdataDB$() {
// if is local workspace or no account, use __local__ userdata
// sometimes we may have cloud workspace but no account for a short time, we also use __local__ userdata
if (
this.workspaceService.workspace.meta.flavour === 'local' ||
!this.authService
) {
return new LiveData(this.userdataDB('__local__'));
} else {
return this.authService.session.account$.map(account => {
if (!account) {
return this.userdataDB('__local__');
}
return this.userdataDB(account.id);
});
}
}

static isDBDocId(docId: string) {
return docId.startsWith('db$') || docId.startsWith('userdata$');
}
Expand Down
7 changes: 1 addition & 6 deletions packages/frontend/core/src/modules/favorite/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { type Framework } from '@toeverything/infra';

import { WorkspaceServerService } from '../cloud';
import { WorkspaceDBService } from '../db';
import { WorkspaceScope, WorkspaceService } from '../workspace';
import { FavoriteList } from './entities/favorite-list';
Expand Down Expand Up @@ -28,11 +27,7 @@ export function configureFavoriteModule(framework: Framework) {
.scope(WorkspaceScope)
.service(FavoriteService)
.entity(FavoriteList, [FavoriteStore])
.store(FavoriteStore, [
WorkspaceDBService,
WorkspaceService,
WorkspaceServerService,
])
.store(FavoriteStore, [WorkspaceDBService])
.service(MigrationFavoriteItemsAdapter, [WorkspaceService])
.service(CompatibleFavoriteItemsAdapter, [FavoriteService]);
}
39 changes: 7 additions & 32 deletions packages/frontend/core/src/modules/favorite/stores/favorite.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { LiveData, Store } from '@toeverything/infra';
import { map } from 'rxjs';

import { AuthService, type WorkspaceServerService } from '../../cloud';
import type { WorkspaceDBService } from '../../db';
import type { WorkspaceService } from '../../workspace';
import type { FavoriteSupportTypeUnion } from '../constant';
import { isFavoriteSupportType } from '../constant';

Expand All @@ -14,41 +12,18 @@ export interface FavoriteRecord {
}

export class FavoriteStore extends Store {
authService = this.workspaceServerService.server?.scope.get(AuthService);
constructor(
private readonly workspaceDBService: WorkspaceDBService,
private readonly workspaceService: WorkspaceService,
private readonly workspaceServerService: WorkspaceServerService
) {
constructor(private readonly workspaceDBService: WorkspaceDBService) {
super();
}

private get userdataDB$() {
// if is local workspace or no account, use __local__ userdata
// sometimes we may have cloud workspace but no account for a short time, we also use __local__ userdata
if (
this.workspaceService.workspace.meta.flavour === 'local' ||
!this.authService
) {
return new LiveData(this.workspaceDBService.userdataDB('__local__'));
} else {
return this.authService.session.account$.map(account => {
if (!account) {
return this.workspaceDBService.userdataDB('__local__');
}
return this.workspaceDBService.userdataDB(account.id);
});
}
}

watchIsLoading() {
return this.userdataDB$
return this.workspaceDBService.userdataDB$
.map(db => LiveData.from(db.favorite.isLoading$, false))
.flat();
}

watchFavorites() {
return this.userdataDB$
return this.workspaceDBService.userdataDB$
.map(db => LiveData.from(db.favorite.find$(), []))
.flat()
.map(raw => {
Expand All @@ -63,7 +38,7 @@ export class FavoriteStore extends Store {
id: string,
index: string
): FavoriteRecord {
const db = this.userdataDB$.value;
const db = this.workspaceDBService.userdataDB$.value;
const raw = db.favorite.create({
key: this.encodeKey(type, id),
index,
Expand All @@ -72,17 +47,17 @@ export class FavoriteStore extends Store {
}

reorderFavorite(type: FavoriteSupportTypeUnion, id: string, index: string) {
const db = this.userdataDB$.value;
const db = this.workspaceDBService.userdataDB$.value;
db.favorite.update(this.encodeKey(type, id), { index });
}

removeFavorite(type: FavoriteSupportTypeUnion, id: string) {
const db = this.userdataDB$.value;
const db = this.workspaceDBService.userdataDB$.value;
db.favorite.delete(this.encodeKey(type, id));
}

watchFavorite(type: FavoriteSupportTypeUnion, id: string) {
const db = this.userdataDB$.value;
const db = this.workspaceDBService.userdataDB$.value;
return LiveData.from<FavoriteRecord | undefined>(
db.favorite
.get$(this.encodeKey(type, id))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import { Entity } from '@toeverything/infra';

export type TemplateDocSettings = {
templateId?: string;
journalTemplateId?: string;
};
import type { TemplateDocSettingStore } from '../store/setting';

export class TemplateDocSetting extends Entity {
constructor() {
constructor(private readonly store: TemplateDocSettingStore) {
super();
}

loading$ = this.store.watchIsLoading();
setting$ = this.store.watchSetting();
pageTemplateDocId$ = this.store.watchSettingKey('pageTemplateId');
journalTemplateDocId$ = this.store.watchSettingKey('journalTemplateId');

updatePageTemplateDocId(id?: string) {
this.store.updateSetting('pageTemplateId', id);
}

updateJournalTemplateDocId(id?: string) {
this.store.updateSetting('journalTemplateId', id);
}
}
4 changes: 3 additions & 1 deletion packages/frontend/core/src/modules/template-doc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { TemplateDocList } from './entities/list';
import { TemplateDocSetting } from './entities/setting';
import { TemplateDocService } from './services/template-doc';
import { TemplateDocListStore } from './store/list';
import { TemplateDocSettingStore } from './store/setting';

export { TemplateDocService };
export * from './view/template-list-menu';
Expand All @@ -17,5 +18,6 @@ export const configureTemplateDocModule = (framework: Framework) => {
.service(TemplateDocService)
.store(TemplateDocListStore, [WorkspaceDBService])
.entity(TemplateDocList, [TemplateDocListStore, DocsService])
.entity(TemplateDocSetting);
.store(TemplateDocSettingStore, [WorkspaceDBService])
.entity(TemplateDocSetting, [TemplateDocSettingStore]);
};
Loading

0 comments on commit f8b58fc

Please sign in to comment.