mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-15 09:06:19 +08:00
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"translation": {
|
||||
"Blog": "Blog",
|
||||
"AboutUs": "About AboutUs",
|
||||
"Open Source": "Open Source",
|
||||
"Privacy First": "Privacy First",
|
||||
"Alternative": "Alternative",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"translation": {
|
||||
"Blog": "博客",
|
||||
"AboutUs": "关于我们",
|
||||
"Open Source": "开源",
|
||||
"Privacy First": "隐私第一",
|
||||
"Alternative": "的另一种选择",
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
"@mui/icons-material": "^5.8.4",
|
||||
"clsx": "^1.2.1",
|
||||
"date-fns": "^2.29.2",
|
||||
"i18next": "^21.9.1",
|
||||
"jotai": "^1.8.1",
|
||||
"react-i18next": "^11.18.4",
|
||||
"tinycolor2": "^1.4.2",
|
||||
"turndown": "7.1.1"
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@ import { CloseIcon } from '@toeverything/components/common';
|
||||
import { IconButton, MuiSnackbar, styled } from '@toeverything/components/ui';
|
||||
import { services } from '@toeverything/datasource/db-service';
|
||||
import { useLocalTrigger } from '@toeverything/datasource/state';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
const cleanupWorkspace = (workspace: string) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const req = indexedDB.deleteDatabase(workspace);
|
||||
@@ -75,7 +75,7 @@ export const FileSystem = () => {
|
||||
setError(true);
|
||||
setTimeout(() => setError(false), 3000);
|
||||
}, []);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const apiSupported = useMemo(() => fsApiSupported(), []);
|
||||
|
||||
if (apiSupported && !selected) {
|
||||
@@ -105,7 +105,7 @@ export const FileSystem = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
Sync to Disk
|
||||
{t('Sync to Disk')}
|
||||
</StyledFileSystem>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
useShowSettingsSidebar,
|
||||
} from '@toeverything/datasource/state';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { EditorBoardSwitcher } from './EditorBoardSwitcher';
|
||||
import { FileSystem, fsApiSupported } from './FileSystem';
|
||||
import { CurrentPageTitle } from './Title';
|
||||
@@ -20,16 +21,17 @@ export const LayoutHeader = () => {
|
||||
const [isLocalWorkspace] = useLocalTrigger();
|
||||
const { toggleSettingsSidebar: toggleInfoSidebar, showSettingsSidebar } =
|
||||
useShowSettingsSidebar();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const warningTips = useMemo(() => {
|
||||
if (!fsApiSupported()) {
|
||||
return 'Welcome to the AFFiNE demo. To begin saving changes you can SYNC DATA TO DISK with the latest version of Chromium based browser like Chrome/Edge';
|
||||
return t('WarningTips.IsNotfsApiSupported');
|
||||
} else if (!isLocalWorkspace) {
|
||||
return 'Welcome to the AFFiNE demo. To begin saving changes you can SYNC TO DISK.';
|
||||
return t('WarningTips.IsNotLocalWorkspace');
|
||||
} else {
|
||||
return 'AFFiNE is under active development and the current version is UNSTABLE. Please DO NOT store information or data';
|
||||
return t('WarningTips.DoNotStore');
|
||||
}
|
||||
}, [isLocalWorkspace]);
|
||||
}, [isLocalWorkspace, t]);
|
||||
|
||||
return (
|
||||
<StyledContainerForHeaderRoot>
|
||||
@@ -43,7 +45,7 @@ export const LayoutHeader = () => {
|
||||
<FlexContainer>
|
||||
<StyledHelper>
|
||||
<FileSystem />
|
||||
<StyledShare disabled={true}>Share</StyledShare>
|
||||
<StyledShare disabled={true}>{t('Share')}</StyledShare>
|
||||
<div style={{ margin: '0px 12px' }}>
|
||||
<IconButton
|
||||
size="large"
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import i18next from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import en_US from './resources/en.json';
|
||||
import zh_CN from './resources/zh.json';
|
||||
|
||||
// See https://react.i18next.com/latest/typescript
|
||||
declare module 'react-i18next' {
|
||||
// and extend them!
|
||||
interface CustomTypeOptions {
|
||||
// custom namespace type if you changed it
|
||||
defaultNS: 'ns1';
|
||||
// custom resources type
|
||||
resources: {
|
||||
en: typeof en_US.translation;
|
||||
zh: typeof zh_CN.translation;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const resources = {
|
||||
en: en_US,
|
||||
zh: zh_CN,
|
||||
} as const;
|
||||
|
||||
i18next.use(initReactI18next).init({
|
||||
lng: 'en',
|
||||
fallbackLng: 'en',
|
||||
|
||||
resources,
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
},
|
||||
});
|
||||
|
||||
export const options = [
|
||||
{ value: 'en', text: 'English' },
|
||||
{ value: 'zh', text: '简体中文' },
|
||||
] as const;
|
||||
|
||||
export { i18next };
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"translation": {
|
||||
"Sync to Disk": "Sync to Disk",
|
||||
"Share": "Share",
|
||||
"WarningTips": {
|
||||
"IsNotfsApiSupported": "Welcome to the AFFiNE demo. To begin saving changes you can SYNC DATA TO DISK with the latest version of Chromium based browser like Chrome/Edge",
|
||||
"IsNotLocalWorkspace": "Welcome to the AFFiNE demo. To begin saving changes you can SYNC TO DISK.",
|
||||
"DoNotStore": "AFFiNE is under active development and the current version is UNSTABLE. Please DO NOT store information or data"
|
||||
},
|
||||
"Layout": "Layout",
|
||||
"Comment": "Comment",
|
||||
"Settings": "Settings",
|
||||
"ComingSoon": "Layout Settings Coming Soon...",
|
||||
"Duplicate Page": "Duplicate Page",
|
||||
"Copy Page Link": "Copy Page Link",
|
||||
"Language": "Language",
|
||||
"Clear Workspace": "Clear Workspace",
|
||||
"Export As Markdown": "Export As Markdown",
|
||||
"Export As HTML": "Export As HTML",
|
||||
"Export As PDF (Unsupported)": "Export As PDF (Unsupported)",
|
||||
"Import Workspace": "Import Workspace",
|
||||
"Export Workspace": "Export Workspace",
|
||||
"Last edited by": "Last edited by {{name}}",
|
||||
"Logout": "Logout"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"translation": {
|
||||
"Sync to Disk": "同步到磁盘",
|
||||
"Share": "分享",
|
||||
"WarningTips": {
|
||||
"IsNotfsApiSupported": "欢迎来到AFFiNE 的演示界面。您可以使用最新版本的基于Chrome的浏览器(如Chrome/Edge)将数据同步到磁盘来进行保存",
|
||||
"IsNotLocalWorkspace": "欢迎来到AFFiNE 的演示界面,您可以同步到磁盘来进行保存操作。",
|
||||
"DoNotStore": "AFFINE 正在积极开发中,当前版本不稳定。请不要存储信息或数据。"
|
||||
},
|
||||
"ComingSoon": "布局设置即将到来",
|
||||
"Layout": "布局",
|
||||
"Comment": "评论",
|
||||
"Settings": "设置",
|
||||
"Duplicate Page": "复制页面",
|
||||
"Copy Page Link": "复制页面链接",
|
||||
"Language": "当前语言",
|
||||
"Clear Workspace": "清空工作区域",
|
||||
"Export As Markdown": "导出 markdown",
|
||||
"Export As HTML": "导出 HTML",
|
||||
"Export As PDF (Unsupported)": "导出 PDF (暂不支持)",
|
||||
"Import Workspace": "导入 Workspace",
|
||||
"Export Workspace": "导出 Workspace",
|
||||
"Last edited by": "最后编辑者为 {{name}}",
|
||||
"Logout": "退出登录"
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './header';
|
||||
export * from './i18n';
|
||||
export * from './settings-sidebar';
|
||||
export * from './workspace-sidebar';
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from '@toeverything/components/icons';
|
||||
import { styled } from '@toeverything/components/ui';
|
||||
import { cloneElement, useCallback, useMemo, type ReactElement } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Comments } from '../Comments';
|
||||
import { useActiveComment } from '../Comments/use-comments';
|
||||
import { LayoutSettings } from '../Layout';
|
||||
@@ -16,12 +17,13 @@ const _defaultTabsKeys = ['layout', 'comment', 'settings'] as const;
|
||||
|
||||
export const ContainerTabs = () => {
|
||||
const { activeCommentId, resolveComment } = useActiveComment();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const getSettingsTabsData = useCallback((): SettingsTabItemType[] => {
|
||||
return [
|
||||
{
|
||||
type: 'layout',
|
||||
text: 'Layout',
|
||||
text: t('Layout'),
|
||||
icon: (
|
||||
<IconWrapper>
|
||||
<LayoutIcon />
|
||||
@@ -31,7 +33,7 @@ export const ContainerTabs = () => {
|
||||
},
|
||||
{
|
||||
type: 'comment',
|
||||
text: 'Comment',
|
||||
text: t('Comment'),
|
||||
icon: (
|
||||
<IconWrapper>
|
||||
<CommentIcon />
|
||||
@@ -48,7 +50,7 @@ export const ContainerTabs = () => {
|
||||
},
|
||||
{
|
||||
type: 'settings',
|
||||
text: 'Settings',
|
||||
text: t('Settings'),
|
||||
icon: (
|
||||
<IconWrapper>
|
||||
<SettingsIcon />
|
||||
@@ -57,7 +59,7 @@ export const ContainerTabs = () => {
|
||||
panel: <SettingsPanel />,
|
||||
},
|
||||
];
|
||||
}, [activeCommentId, resolveComment]);
|
||||
}, [activeCommentId, resolveComment, t]);
|
||||
|
||||
const settingsTabsData = useMemo(() => {
|
||||
return getSettingsTabsData();
|
||||
@@ -67,7 +69,6 @@ export const ContainerTabs = () => {
|
||||
_defaultTabsKeys as unknown as string[],
|
||||
'settings'
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledTabsTitlesContainer>
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
import { Divider, ListItem, styled, Switch } from '@toeverything/components/ui';
|
||||
import {
|
||||
Divider,
|
||||
ListItem,
|
||||
Option,
|
||||
Select,
|
||||
styled,
|
||||
Switch,
|
||||
} from '@toeverything/components/ui';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { options } from '../../i18n';
|
||||
import { useSettings } from './use-settings';
|
||||
|
||||
export const SettingsList = () => {
|
||||
const settings = useSettings();
|
||||
const { i18n } = useTranslation();
|
||||
const changeLanguage = event => {
|
||||
i18n.changeLanguage(event);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledSettingsList>
|
||||
@@ -15,7 +28,7 @@ export const SettingsList = () => {
|
||||
if (type === 'switch') {
|
||||
return (
|
||||
<SwitchItemContainer
|
||||
key={item.name}
|
||||
key={item.key}
|
||||
onClick={() => {
|
||||
item.onChange(!item.value);
|
||||
}}
|
||||
@@ -31,8 +44,25 @@ export const SettingsList = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<ListItem key={item.name} onClick={() => item.onClick()}>
|
||||
<ListItem key={item.key} onClick={() => item.onClick()}>
|
||||
{item.name}
|
||||
{item.key === 'Language' ? (
|
||||
<div style={{ marginLeft: '12em' }}>
|
||||
<Select
|
||||
defaultValue={options[0].value}
|
||||
onChange={changeLanguage}
|
||||
>
|
||||
{options.map(option => (
|
||||
<Option
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
>
|
||||
{option.text}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
) : null}
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { styled, Typography } from '@toeverything/components/ui';
|
||||
import { useUserAndSpaces } from '@toeverything/datasource/state';
|
||||
import format from 'date-fns/format';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { usePageLastUpdated, useWorkspaceAndPageId } from '../util';
|
||||
|
||||
export const LastModified = () => {
|
||||
@@ -9,12 +10,12 @@ export const LastModified = () => {
|
||||
const { workspaceId, pageId } = useWorkspaceAndPageId();
|
||||
const lastModified = usePageLastUpdated({ workspaceId, pageId });
|
||||
const formatLastModified = format(lastModified, 'MM/dd/yyyy hh:mm');
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<ContentText type="xs">
|
||||
<span>Last edited by </span>
|
||||
<span>{username}</span>
|
||||
<span>{t('Last edited by', { name: username })}</span>
|
||||
</ContentText>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { MoveToIcon } from '@toeverything/components/icons';
|
||||
import { ListItem, styled, Typography } from '@toeverything/components/ui';
|
||||
import { LOGOUT_COOKIES, LOGOUT_LOCAL_STORAGE } from '@toeverything/utils';
|
||||
import { getAuth, signOut } from 'firebase/auth';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const logout = () => {
|
||||
LOGOUT_LOCAL_STORAGE.forEach(name => localStorage.removeItem(name));
|
||||
@@ -16,10 +17,11 @@ const logout = () => {
|
||||
};
|
||||
|
||||
export const Logout = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<ListItem onClick={logout}>
|
||||
<StyledIcon />
|
||||
<ContentText type="base">Logout</ContentText>
|
||||
<ContentText type="base">{t('Logout')}</ContentText>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { message } from '@toeverything/components/ui';
|
||||
import { copyToClipboard } from '@toeverything/utils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useSettingFlags, type SettingFlags } from './use-setting-flags';
|
||||
|
||||
import {
|
||||
// useReadingMode,
|
||||
clearWorkspace,
|
||||
duplicatePage,
|
||||
exportHtml,
|
||||
@@ -16,6 +17,7 @@ import {
|
||||
|
||||
interface BaseSettingItem {
|
||||
flag?: keyof SettingFlags;
|
||||
key: string;
|
||||
}
|
||||
|
||||
interface SwitchItem extends BaseSettingItem {
|
||||
@@ -64,6 +66,7 @@ export const useSettings = (): SettingItem[] => {
|
||||
const { workspaceId, pageId } = useWorkspaceAndPageId();
|
||||
const navigate = useNavigate();
|
||||
const settingFlags = useSettingFlags();
|
||||
const { t } = useTranslation();
|
||||
// const { toggleReadingMode, readingMode } = useReadingMode();
|
||||
|
||||
const settings: SettingItem[] = [
|
||||
@@ -80,7 +83,8 @@ export const useSettings = (): SettingItem[] => {
|
||||
// },
|
||||
{
|
||||
type: 'button',
|
||||
name: 'Duplicate Page',
|
||||
name: t('Duplicate Page'),
|
||||
key: 'Duplicate Page',
|
||||
onClick: async () => {
|
||||
const newPageInfo = await duplicatePage({
|
||||
workspaceId,
|
||||
@@ -91,18 +95,29 @@ export const useSettings = (): SettingItem[] => {
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
name: 'Copy Page Link',
|
||||
name: t('Copy Page Link'),
|
||||
key: 'Copy Page Link',
|
||||
onClick: () => {
|
||||
copyToClipboard(window.location.href);
|
||||
message.success('Page link copied successfully');
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
name: t('Language'),
|
||||
key: 'Language',
|
||||
onClick: () => {
|
||||
// Do noting
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'separator',
|
||||
key: 'separator1',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
name: 'Export As Markdown',
|
||||
name: t('Export As Markdown'),
|
||||
key: 'Export As Markdown',
|
||||
onClick: async () => {
|
||||
const title = await getPageTitle({ workspaceId, pageId });
|
||||
exportMarkdown({ workspaceId, rootBlockId: pageId, title });
|
||||
@@ -111,7 +126,8 @@ export const useSettings = (): SettingItem[] => {
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
name: 'Export As HTML',
|
||||
name: t('Export As HTML'),
|
||||
key: 'Export As HTML',
|
||||
onClick: async () => {
|
||||
const title = await getPageTitle({ workspaceId, pageId });
|
||||
exportHtml({ workspaceId, rootBlockId: pageId, title });
|
||||
@@ -120,28 +136,33 @@ export const useSettings = (): SettingItem[] => {
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
name: 'Export As PDF (Unsupported)',
|
||||
name: t('Export As PDF (Unsupported)'),
|
||||
key: 'Export As PDF (Unsupported)',
|
||||
onClick: () => console.log('Export As PDF'),
|
||||
flag: 'booleanExportPdf',
|
||||
},
|
||||
{
|
||||
type: 'separator',
|
||||
key: 'separator2',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
name: 'Import Workspace',
|
||||
name: t('Import Workspace'),
|
||||
key: 'Import Workspace',
|
||||
onClick: () => importWorkspace(workspaceId),
|
||||
flag: 'booleanImportWorkspace',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
name: 'Export Workspace',
|
||||
name: t('Export Workspace'),
|
||||
key: 'Export Workspace',
|
||||
onClick: () => exportWorkspace(),
|
||||
flag: 'booleanExportWorkspace',
|
||||
},
|
||||
{
|
||||
type: 'button',
|
||||
name: 'Clear Workspace',
|
||||
name: t('Clear Workspace'),
|
||||
key: 'Clear Workspace',
|
||||
onClick: () => clearWorkspace(workspaceId),
|
||||
flag: 'booleanClearWorkspace',
|
||||
},
|
||||
|
||||
Generated
+10
-4
@@ -196,10 +196,14 @@ importers:
|
||||
specifiers:
|
||||
'@mui/icons-material': ^5.8.4
|
||||
firebase: ^9.9.3
|
||||
i18next: ^21.9.1
|
||||
mini-css-extract-plugin: ^2.6.1
|
||||
react-i18next: ^11.18.4
|
||||
webpack: ^5.74.0
|
||||
dependencies:
|
||||
'@mui/icons-material': 5.8.4
|
||||
i18next: 21.9.1
|
||||
react-i18next: 11.18.4_i18next@21.9.1
|
||||
devDependencies:
|
||||
firebase: 9.9.3
|
||||
mini-css-extract-plugin: 2.6.1_webpack@5.74.0
|
||||
@@ -501,7 +505,9 @@ importers:
|
||||
'@types/turndown': ^5.0.1
|
||||
clsx: ^1.2.1
|
||||
date-fns: ^2.29.2
|
||||
i18next: ^21.9.1
|
||||
jotai: ^1.8.1
|
||||
react-i18next: ^11.18.4
|
||||
tinycolor2: ^1.4.2
|
||||
turndown: 7.1.1
|
||||
dependencies:
|
||||
@@ -513,7 +519,9 @@ importers:
|
||||
'@mui/icons-material': 5.8.4
|
||||
clsx: 1.2.1
|
||||
date-fns: 2.29.2
|
||||
i18next: 21.9.1
|
||||
jotai: 1.8.1
|
||||
react-i18next: 11.18.4_i18next@21.9.1
|
||||
tinycolor2: 1.4.2
|
||||
turndown: 7.1.1
|
||||
devDependencies:
|
||||
@@ -7383,10 +7391,8 @@ packages:
|
||||
indent-string: 4.0.0
|
||||
dev: true
|
||||
|
||||
/ajv-formats/2.1.1_ajv@8.11.0:
|
||||
/ajv-formats/2.1.1:
|
||||
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
|
||||
peerDependencies:
|
||||
ajv: ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
ajv:
|
||||
optional: true
|
||||
@@ -16953,7 +16959,7 @@ packages:
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.11
|
||||
ajv: 8.11.0
|
||||
ajv-formats: 2.1.1_ajv@8.11.0
|
||||
ajv-formats: 2.1.1
|
||||
ajv-keywords: 5.1.0_ajv@8.11.0
|
||||
dev: true
|
||||
|
||||
|
||||
Reference in New Issue
Block a user