mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-23 05:25:53 +08:00
+46
-22
@@ -2,6 +2,7 @@ import {
|
||||
Loading,
|
||||
Menu,
|
||||
MenuItem,
|
||||
type MenuProps,
|
||||
MenuSeparator,
|
||||
MenuTrigger,
|
||||
RadioGroup,
|
||||
@@ -19,6 +20,7 @@ import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hoo
|
||||
import { ServerService } from '@affine/core/modules/cloud';
|
||||
import { DesktopApiService } from '@affine/core/modules/desktop-api';
|
||||
import {
|
||||
type EditorSettingSchema,
|
||||
EditorSettingService,
|
||||
type FontFamily,
|
||||
fontStyleOptions,
|
||||
@@ -30,7 +32,6 @@ import {
|
||||
SystemFontFamilyService,
|
||||
} from '@affine/core/modules/system-font-family';
|
||||
import { Trans, useI18n } from '@affine/i18n';
|
||||
import type { DocMode } from '@blocksuite/affine/blocks';
|
||||
import { DoneIcon, SearchIcon } from '@blocksuite/icons/rc';
|
||||
import { useLiveData, useService, useServices } from '@toeverything/infra';
|
||||
import clsx from 'clsx';
|
||||
@@ -306,27 +307,39 @@ const CustomFontFamilySettings = () => {
|
||||
</SettingRow>
|
||||
);
|
||||
};
|
||||
|
||||
const menuContentOptions: MenuProps['contentOptions'] = {
|
||||
align: 'end',
|
||||
sideOffset: 16,
|
||||
style: { width: 250 },
|
||||
};
|
||||
const NewDocDefaultModeSettings = () => {
|
||||
const t = useI18n();
|
||||
const { editorSettingService } = useServices({ EditorSettingService });
|
||||
const settings = useLiveData(editorSettingService.editorSetting.settings$);
|
||||
const radioItems = useMemo<RadioItem[]>(
|
||||
() => [
|
||||
{
|
||||
value: 'page',
|
||||
label: t['Page'](),
|
||||
testId: 'page-mode-trigger',
|
||||
},
|
||||
{
|
||||
value: 'edgeless',
|
||||
label: t['Edgeless'](),
|
||||
testId: 'edgeless-mode-trigger',
|
||||
},
|
||||
],
|
||||
const items = useMemo(
|
||||
() =>
|
||||
[
|
||||
{
|
||||
value: 'page',
|
||||
label: t['Page'](),
|
||||
testId: 'page-mode-trigger',
|
||||
},
|
||||
{
|
||||
value: 'edgeless',
|
||||
label: t['Edgeless'](),
|
||||
testId: 'edgeless-mode-trigger',
|
||||
},
|
||||
{
|
||||
value: 'ask',
|
||||
label: 'Ask every time',
|
||||
testId: 'ask-every-time-trigger',
|
||||
},
|
||||
] as const,
|
||||
[t]
|
||||
);
|
||||
const updateNewDocDefaultMode = useCallback(
|
||||
(value: DocMode) => {
|
||||
(value: EditorSettingSchema['newDocDefaultMode']) => {
|
||||
editorSettingService.editorSetting.set('newDocDefaultMode', value);
|
||||
},
|
||||
[editorSettingService.editorSetting]
|
||||
@@ -340,13 +353,24 @@ const NewDocDefaultModeSettings = () => {
|
||||
'com.affine.settings.editorSettings.general.default-new-doc.description'
|
||||
]()}
|
||||
>
|
||||
<RadioGroup
|
||||
items={radioItems}
|
||||
value={settings.newDocDefaultMode}
|
||||
width={250}
|
||||
className={styles.settingWrapper}
|
||||
onChange={updateNewDocDefaultMode}
|
||||
/>
|
||||
<Menu
|
||||
contentOptions={menuContentOptions}
|
||||
items={items.map(item => {
|
||||
return (
|
||||
<MenuItem
|
||||
key={item.value}
|
||||
selected={item.value === settings.newDocDefaultMode}
|
||||
onSelect={() => updateNewDocDefaultMode(item.value)}
|
||||
>
|
||||
{item.label}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
>
|
||||
<MenuTrigger className={styles.menuTrigger}>
|
||||
{items.find(item => item.value === settings.newDocDefaultMode)?.label}
|
||||
</MenuTrigger>
|
||||
</Menu>
|
||||
</SettingRow>
|
||||
);
|
||||
};
|
||||
|
||||
+2
@@ -19,6 +19,7 @@ import { LabelsPanel } from './labels';
|
||||
import { MembersPanel } from './members';
|
||||
import { ProfilePanel } from './profile';
|
||||
import { SharingPanel } from './sharing';
|
||||
import { TemplateDocSetting } from './template';
|
||||
import type { WorkspaceSettingDetailProps } from './types';
|
||||
import { WorkspaceQuotaPanel } from './workspace-quota';
|
||||
|
||||
@@ -70,6 +71,7 @@ export const WorkspaceSettingDetail = ({
|
||||
<LabelsPanel />
|
||||
</SettingRow>
|
||||
</SettingWrapper>
|
||||
<TemplateDocSetting />
|
||||
<SettingWrapper title={t['com.affine.brand.affineCloud']()}>
|
||||
<EnableCloudPanel onCloseSetting={onCloseSetting} />
|
||||
<WorkspaceQuotaPanel />
|
||||
|
||||
+4
-4
@@ -5,10 +5,7 @@ export const menuItem = style({
|
||||
alignItems: 'center',
|
||||
gap: 8,
|
||||
});
|
||||
export const menuItemIcon = style({
|
||||
fontSize: 24,
|
||||
lineHeight: 0,
|
||||
});
|
||||
export const menuItemIcon = style({});
|
||||
export const menuItemText = style({
|
||||
fontSize: 14,
|
||||
width: 0,
|
||||
@@ -17,3 +14,6 @@ export const menuItemText = style({
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
});
|
||||
export const menuTrigger = style({
|
||||
width: 250,
|
||||
});
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
import {
|
||||
MenuItem,
|
||||
MenuSeparator,
|
||||
MenuTrigger,
|
||||
Switch,
|
||||
} from '@affine/component';
|
||||
import {
|
||||
SettingRow,
|
||||
SettingWrapper,
|
||||
} from '@affine/component/setting-components';
|
||||
import { 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,
|
||||
TemplateListMenu,
|
||||
} from '@affine/core/modules/template-doc';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { DeleteIcon } from '@blocksuite/icons/rc';
|
||||
import { useLiveData, useService, useServices } from '@toeverything/infra';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import * as styles from './template.css';
|
||||
|
||||
export const TemplateDocSetting = () => {
|
||||
const t = useI18n();
|
||||
const { featureFlagService, templateDocService } = useServices({
|
||||
FeatureFlagService,
|
||||
TemplateDocService,
|
||||
});
|
||||
const setting = templateDocService.setting;
|
||||
|
||||
const enabled = useLiveData(featureFlagService.flags.enable_template_doc.$);
|
||||
|
||||
const enablePageTemplate = useLiveData(setting.enablePageTemplate$);
|
||||
const pageTemplateDocId = useLiveData(setting.pageTemplateDocId$);
|
||||
const journalTemplateDocId = useLiveData(setting.journalTemplateDocId$);
|
||||
|
||||
const togglePageTemplate = useCallback(
|
||||
(enable: boolean) => {
|
||||
setting.togglePageTemplate(enable);
|
||||
},
|
||||
[setting]
|
||||
);
|
||||
|
||||
const updatePageTemplate = useCallback(
|
||||
(id?: string) => {
|
||||
setting.updatePageTemplateDocId(id);
|
||||
},
|
||||
[setting]
|
||||
);
|
||||
|
||||
const updateJournalTemplate = useCallback(
|
||||
(id?: string) => {
|
||||
setting.updateJournalTemplateDocId(id);
|
||||
},
|
||||
[setting]
|
||||
);
|
||||
|
||||
if (!enabled) return null;
|
||||
|
||||
return (
|
||||
<SettingWrapper title={t['com.affine.settings.workspace.template.title']()}>
|
||||
<SettingRow
|
||||
name={t['com.affine.settings.workspace.template.journal']()}
|
||||
desc={t['com.affine.settings.workspace.template.journal-desc']()}
|
||||
>
|
||||
<TemplateSelector
|
||||
current={journalTemplateDocId}
|
||||
onChange={updateJournalTemplate}
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingRow
|
||||
name={t['com.affine.settings.workspace.template.page']()}
|
||||
desc={t['com.affine.settings.workspace.template.page-desc']()}
|
||||
>
|
||||
<Switch checked={enablePageTemplate} onChange={togglePageTemplate} />
|
||||
</SettingRow>
|
||||
{enablePageTemplate ? (
|
||||
<SettingRow
|
||||
name={t['com.affine.settings.workspace.template.journal']()}
|
||||
desc={t['com.affine.settings.workspace.template.journal-desc']()}
|
||||
>
|
||||
<TemplateSelector
|
||||
current={pageTemplateDocId}
|
||||
onChange={updatePageTemplate}
|
||||
/>
|
||||
</SettingRow>
|
||||
) : null}
|
||||
</SettingWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
interface TemplateSelectorProps {
|
||||
current?: string;
|
||||
onChange?: (id?: string) => void;
|
||||
}
|
||||
const TemplateSelector = ({ current, onChange }: TemplateSelectorProps) => {
|
||||
const t = useI18n();
|
||||
const docsService = useService(DocsService);
|
||||
const docDisplayService = useService(DocDisplayMetaService);
|
||||
const doc = useLiveData(current ? docsService.list.doc$(current) : null);
|
||||
const title = useLiveData(doc ? docDisplayService.title$(doc.id) : null);
|
||||
// const isInTrash = useLiveData(doc?.trash$);
|
||||
|
||||
return (
|
||||
<TemplateListMenu
|
||||
onSelect={onChange}
|
||||
contentOptions={{ align: 'end' }}
|
||||
suffixItems={
|
||||
<>
|
||||
<MenuSeparator />
|
||||
<MenuItem
|
||||
prefixIcon={<DeleteIcon className={styles.menuItemIcon} />}
|
||||
onClick={() => onChange?.()}
|
||||
type="danger"
|
||||
>
|
||||
{t['com.affine.settings.workspace.template.remove']()}
|
||||
</MenuItem>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<MenuTrigger className={styles.menuTrigger}>
|
||||
{/* TODO: in trash design */}
|
||||
{title ?? t['com.affine.settings.workspace.template.keep-empty']()}
|
||||
</MenuTrigger>
|
||||
</TemplateListMenu>
|
||||
);
|
||||
};
|
||||
@@ -1,110 +0,0 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user