mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-11-24 09:53:06 +03:00
fix: a series of setting issues (#3032)
This commit is contained in:
parent
dec0c0d3d1
commit
87ba71e77e
@ -1,35 +0,0 @@
|
||||
import { Button } from '@affine/component';
|
||||
import type { ContactModalProps } from '@affine/component/contact-modal';
|
||||
import { ContactModal } from '@affine/component/contact-modal';
|
||||
import type { StoryFn } from '@storybook/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default {
|
||||
title: 'AFFiNE/ContactModal',
|
||||
component: ContactModal,
|
||||
};
|
||||
|
||||
export const Basic: StoryFn<ContactModalProps> = args => {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setOpen(true);
|
||||
}}
|
||||
>
|
||||
Open
|
||||
</Button>
|
||||
<ContactModal
|
||||
{...args}
|
||||
open={open}
|
||||
onClose={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
Basic.args = {
|
||||
logoSrc: '/imgs/affine-text-logo.png',
|
||||
};
|
@ -2,13 +2,21 @@ import { atom } from 'jotai';
|
||||
import { atomFamily, atomWithStorage } from 'jotai/utils';
|
||||
|
||||
import type { CreateWorkspaceMode } from '../components/affine/create-workspace-modal';
|
||||
import type { SettingProps } from '../components/affine/setting-modal';
|
||||
|
||||
// modal atoms
|
||||
export const openWorkspacesModalAtom = atom(false);
|
||||
export const openCreateWorkspaceModalAtom = atom<CreateWorkspaceMode>(false);
|
||||
export const openQuickSearchModalAtom = atom(false);
|
||||
export const openOnboardingModalAtom = atom(false);
|
||||
export const openSettingModalAtom = atom(false);
|
||||
|
||||
export type SettingAtom = Pick<SettingProps, 'activeTab' | 'workspace'> & {
|
||||
open: boolean;
|
||||
};
|
||||
|
||||
export const openSettingModalAtom = atom<SettingAtom>({
|
||||
open: false,
|
||||
});
|
||||
|
||||
export const openDisableCloudAlertModalAtom = atom(false);
|
||||
|
||||
|
@ -1,4 +1,10 @@
|
||||
import { Menu, MenuItem, MenuTrigger, styled } from '@affine/component';
|
||||
import {
|
||||
type ButtonProps,
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuTrigger,
|
||||
styled,
|
||||
} from '@affine/component';
|
||||
import { LOCALES } from '@affine/i18n';
|
||||
import { useI18N } from '@affine/i18n';
|
||||
import type { FC, ReactElement } from 'react';
|
||||
@ -41,7 +47,9 @@ const LanguageMenuContent: FC<{
|
||||
</>
|
||||
);
|
||||
};
|
||||
export const LanguageMenu: FC = () => {
|
||||
export const LanguageMenu: FC<{ triggerProps: ButtonProps }> = ({
|
||||
triggerProps,
|
||||
}) => {
|
||||
const i18n = useI18N();
|
||||
|
||||
const currentLanguage = LOCALES.find(item => item.tag === i18n.language);
|
||||
@ -62,6 +70,7 @@ export const LanguageMenu: FC = () => {
|
||||
<MenuTrigger
|
||||
data-testid="language-menu-button"
|
||||
style={{ textTransform: 'capitalize' }}
|
||||
{...triggerProps}
|
||||
>
|
||||
{currentLanguage?.originalName}
|
||||
</MenuTrigger>
|
||||
|
@ -0,0 +1,35 @@
|
||||
import {
|
||||
DiscordIcon,
|
||||
GithubIcon,
|
||||
RedditIcon,
|
||||
TelegramIcon,
|
||||
TwitterIcon,
|
||||
} from './icons';
|
||||
|
||||
export const relatedLinks = [
|
||||
{
|
||||
icon: <GithubIcon />,
|
||||
title: 'GitHub',
|
||||
link: 'https://github.com/toeverything/AFFiNE',
|
||||
},
|
||||
{
|
||||
icon: <RedditIcon />,
|
||||
title: 'Reddit',
|
||||
link: 'https://www.reddit.com/r/Affine/',
|
||||
},
|
||||
{
|
||||
icon: <TwitterIcon />,
|
||||
title: 'Twitter',
|
||||
link: 'https://twitter.com/AffineOfficial',
|
||||
},
|
||||
{
|
||||
icon: <TelegramIcon />,
|
||||
title: 'Telegram',
|
||||
link: 'https://t.me/affineworkos',
|
||||
},
|
||||
{
|
||||
icon: <DiscordIcon />,
|
||||
title: 'Discord',
|
||||
link: 'https://discord.gg/Arn7TqJBvG',
|
||||
},
|
||||
];
|
@ -1,5 +1,4 @@
|
||||
import { Switch } from '@affine/component';
|
||||
import { relatedLinks } from '@affine/component/contact-modal';
|
||||
import { SettingHeader } from '@affine/component/setting-components';
|
||||
import { SettingRow } from '@affine/component/setting-components';
|
||||
import { SettingWrapper } from '@affine/component/setting-components';
|
||||
@ -8,6 +7,7 @@ import { ArrowRightSmallIcon, OpenInNewIcon } from '@blocksuite/icons';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { type AppSetting, useAppSetting } from '../../../../../atoms/settings';
|
||||
import { relatedLinks } from './config';
|
||||
import { communityItem, communityWrapper, link } from './style.css';
|
||||
|
||||
export const AboutAffine = () => {
|
||||
|
@ -21,6 +21,7 @@ export const ThemeSettings = () => {
|
||||
|
||||
return (
|
||||
<RadioButtonGroup
|
||||
width={250}
|
||||
className={settingWrapper}
|
||||
defaultValue={theme}
|
||||
onValueChange={useCallback(
|
||||
@ -30,13 +31,17 @@ export const ThemeSettings = () => {
|
||||
[setTheme]
|
||||
)}
|
||||
>
|
||||
<RadioButton value="system" data-testid="system-theme-trigger">
|
||||
<RadioButton
|
||||
bold={true}
|
||||
value="system"
|
||||
data-testid="system-theme-trigger"
|
||||
>
|
||||
{t['system']()}
|
||||
</RadioButton>
|
||||
<RadioButton value="light" data-testid="light-theme-trigger">
|
||||
<RadioButton bold={true} value="light" data-testid="light-theme-trigger">
|
||||
{t['light']()}
|
||||
</RadioButton>
|
||||
<RadioButton value="dark" data-testid="dark-theme-trigger">
|
||||
<RadioButton bold={true} value="dark" data-testid="dark-theme-trigger">
|
||||
{t['dark']()}
|
||||
</RadioButton>
|
||||
</RadioButtonGroup>
|
||||
@ -72,7 +77,7 @@ export const AppearanceSettings = () => {
|
||||
desc={t['Select the language for the interface.']()}
|
||||
>
|
||||
<div className={settingWrapper}>
|
||||
<LanguageMenu />
|
||||
<LanguageMenu triggerProps={{ size: 'small' }} />
|
||||
</div>
|
||||
</SettingRow>
|
||||
{runtimeConfig.enableNewSettingUnstableApi && environment.isDesktop ? (
|
||||
@ -104,6 +109,7 @@ export const AppearanceSettings = () => {
|
||||
>
|
||||
<RadioButtonGroup
|
||||
className={settingWrapper}
|
||||
width={250}
|
||||
defaultValue={appSettings.windowFrameStyle}
|
||||
onValueChange={(value: AppSetting['windowFrameStyle']) => {
|
||||
setAppSettings({ windowFrameStyle: value });
|
||||
|
@ -2,18 +2,15 @@ import {
|
||||
SettingModal as SettingModalBase,
|
||||
type SettingModalProps,
|
||||
} from '@affine/component/setting-components';
|
||||
import type {
|
||||
AffineCloudWorkspace,
|
||||
LocalWorkspace,
|
||||
} from '@affine/env/workspace';
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { ContactWithUsIcon } from '@blocksuite/icons';
|
||||
import type React from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
|
||||
import { useWorkspaces } from '../../../hooks/use-workspaces';
|
||||
import type { AllWorkspace } from '../../../shared';
|
||||
import { AccountSetting } from './account-setting';
|
||||
import {
|
||||
GeneralSetting,
|
||||
@ -22,80 +19,79 @@ import {
|
||||
} from './general-setting';
|
||||
import { SettingSidebar } from './setting-sidebar';
|
||||
import { settingContent } from './style.css';
|
||||
import type { Workspace } from './type';
|
||||
import { WorkSpaceSetting } from './workspace-setting';
|
||||
|
||||
export const SettingModal: React.FC<SettingModalProps> = ({
|
||||
type ActiveTab = GeneralSettingKeys | 'workspace' | 'account';
|
||||
export type SettingProps = {
|
||||
activeTab?: ActiveTab;
|
||||
workspace?: AllWorkspace;
|
||||
onSettingClick: (params: {
|
||||
activeTab: ActiveTab;
|
||||
workspace?: AllWorkspace;
|
||||
}) => void;
|
||||
};
|
||||
export const SettingModal: React.FC<SettingModalProps & SettingProps> = ({
|
||||
open,
|
||||
setOpen,
|
||||
activeTab = 'appearance',
|
||||
workspace = null,
|
||||
onSettingClick,
|
||||
}) => {
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
const workspaces = useWorkspaces();
|
||||
const [currentWorkspace] = useCurrentWorkspace();
|
||||
const generalSettingList = useGeneralSettingList();
|
||||
const workspaceList = useMemo(() => {
|
||||
return workspaces.filter(
|
||||
({ flavour }) => flavour !== WorkspaceFlavour.PUBLIC
|
||||
) as Workspace[];
|
||||
) as AllWorkspace[];
|
||||
}, [workspaces]);
|
||||
|
||||
const [currentRef, setCurrentRef] = useState<{
|
||||
workspace: Workspace | null;
|
||||
generalKey: GeneralSettingKeys | null;
|
||||
isAccount: boolean;
|
||||
}>({
|
||||
workspace: null,
|
||||
generalKey: generalSettingList[0].key,
|
||||
isAccount: false,
|
||||
});
|
||||
|
||||
const onGeneralSettingClick = useCallback((key: GeneralSettingKeys) => {
|
||||
setCurrentRef({
|
||||
workspace: null,
|
||||
generalKey: key,
|
||||
isAccount: false,
|
||||
});
|
||||
}, []);
|
||||
const onWorkspaceSettingClick = useCallback((workspace: Workspace) => {
|
||||
setCurrentRef({
|
||||
workspace: workspace,
|
||||
generalKey: null,
|
||||
isAccount: false,
|
||||
});
|
||||
}, []);
|
||||
const onGeneralSettingClick = useCallback(
|
||||
(key: GeneralSettingKeys) => {
|
||||
onSettingClick({
|
||||
activeTab: key,
|
||||
});
|
||||
},
|
||||
[onSettingClick]
|
||||
);
|
||||
const onWorkspaceSettingClick = useCallback(
|
||||
(workspace: AllWorkspace) => {
|
||||
onSettingClick({
|
||||
activeTab: 'workspace',
|
||||
workspace,
|
||||
});
|
||||
},
|
||||
[onSettingClick]
|
||||
);
|
||||
const onAccountSettingClick = useCallback(() => {
|
||||
setCurrentRef({
|
||||
workspace: null,
|
||||
generalKey: null,
|
||||
isAccount: true,
|
||||
});
|
||||
}, []);
|
||||
onSettingClick({ activeTab: 'account' });
|
||||
}, [onSettingClick]);
|
||||
|
||||
return (
|
||||
<SettingModalBase open={open} setOpen={setOpen}>
|
||||
<SettingSidebar
|
||||
generalSettingList={generalSettingList}
|
||||
onGeneralSettingClick={onGeneralSettingClick}
|
||||
currentWorkspace={
|
||||
currentWorkspace as AffineCloudWorkspace | LocalWorkspace
|
||||
}
|
||||
currentWorkspace={currentWorkspace as AllWorkspace}
|
||||
workspaceList={workspaceList}
|
||||
onWorkspaceSettingClick={onWorkspaceSettingClick}
|
||||
selectedGeneralKey={currentRef.generalKey}
|
||||
selectedWorkspace={currentRef.workspace}
|
||||
selectedGeneralKey={activeTab}
|
||||
selectedWorkspace={workspace}
|
||||
onAccountSettingClick={onAccountSettingClick}
|
||||
/>
|
||||
|
||||
<div className={settingContent}>
|
||||
<div className="wrapper">
|
||||
<div className="content">
|
||||
{currentRef.workspace ? (
|
||||
<WorkSpaceSetting workspace={currentRef.workspace} />
|
||||
{activeTab === 'workspace' && workspace ? (
|
||||
<WorkSpaceSetting workspace={workspace} />
|
||||
) : null}
|
||||
{currentRef.generalKey ? (
|
||||
<GeneralSetting generalKey={currentRef.generalKey} />
|
||||
{generalSettingList.find(v => v.key === activeTab) ? (
|
||||
<GeneralSetting generalKey={activeTab as GeneralSettingKeys} />
|
||||
) : null}
|
||||
{currentRef.isAccount ? <AccountSetting /> : null}
|
||||
{activeTab === 'account' ? <AccountSetting /> : null}
|
||||
</div>
|
||||
<div className="footer">
|
||||
<ContactWithUsIcon />
|
||||
|
@ -1,18 +1,14 @@
|
||||
import { UserAvatar } from '@affine/component/user-avatar';
|
||||
import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
|
||||
import type {
|
||||
AffineCloudWorkspace,
|
||||
LocalWorkspace,
|
||||
} from '@affine/env/workspace';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import type { AllWorkspace } from '../../../../shared';
|
||||
import type {
|
||||
GeneralSettingKeys,
|
||||
GeneralSettingList,
|
||||
} from '../general-setting';
|
||||
import type { Workspace } from '../type';
|
||||
import {
|
||||
accountButton,
|
||||
settingSlideBar,
|
||||
@ -34,13 +30,11 @@ export const SettingSidebar = ({
|
||||
}: {
|
||||
generalSettingList: GeneralSettingList;
|
||||
onGeneralSettingClick: (key: GeneralSettingKeys) => void;
|
||||
currentWorkspace: Workspace;
|
||||
workspaceList: Workspace[];
|
||||
onWorkspaceSettingClick: (
|
||||
workspace: AffineCloudWorkspace | LocalWorkspace
|
||||
) => void;
|
||||
currentWorkspace: AllWorkspace;
|
||||
workspaceList: AllWorkspace[];
|
||||
onWorkspaceSettingClick: (workspace: AllWorkspace) => void;
|
||||
|
||||
selectedWorkspace: Workspace | null;
|
||||
selectedWorkspace: AllWorkspace | null;
|
||||
selectedGeneralKey: string | null;
|
||||
onAccountSettingClick: () => void;
|
||||
}) => {
|
||||
@ -118,7 +112,7 @@ const WorkspaceListItem = ({
|
||||
isCurrent,
|
||||
isActive,
|
||||
}: {
|
||||
workspace: AffineCloudWorkspace | LocalWorkspace;
|
||||
workspace: AllWorkspace;
|
||||
onClick: () => void;
|
||||
isCurrent: boolean;
|
||||
isActive: boolean;
|
||||
|
@ -34,6 +34,7 @@ export const sidebarItemsWrapper = style({
|
||||
selectors: {
|
||||
'&.scroll': {
|
||||
flexGrow: 1,
|
||||
overflowY: 'auto',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -1,6 +0,0 @@
|
||||
import type {
|
||||
AffineCloudWorkspace,
|
||||
LocalWorkspace,
|
||||
} from '@affine/env/workspace';
|
||||
|
||||
export type Workspace = AffineCloudWorkspace | LocalWorkspace;
|
@ -3,9 +3,13 @@ import { Suspense, useCallback } from 'react';
|
||||
import { getUIAdapter } from '../../../../adapters/workspace';
|
||||
import { useOnTransformWorkspace } from '../../../../hooks/root/use-on-transform-workspace';
|
||||
import { useAppHelper } from '../../../../hooks/use-workspaces';
|
||||
import type { Workspace } from '../type';
|
||||
import type { AllWorkspace } from '../../../../shared';
|
||||
|
||||
export const WorkSpaceSetting = ({ workspace }: { workspace: Workspace }) => {
|
||||
export const WorkSpaceSetting = ({
|
||||
workspace,
|
||||
}: {
|
||||
workspace: AllWorkspace;
|
||||
}) => {
|
||||
const helper = useAppHelper();
|
||||
const { NewSettingsDetail } = getUIAdapter(workspace.flavour);
|
||||
|
||||
|
@ -2,9 +2,9 @@ import { MuiFade, Tooltip } from '@affine/component';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { CloseIcon, NewIcon, UserGuideIcon } from '@blocksuite/icons';
|
||||
import { useAtom } from 'jotai';
|
||||
import { lazy, Suspense, useState } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import { openOnboardingModalAtom } from '../../../atoms';
|
||||
import { openOnboardingModalAtom, openSettingModalAtom } from '../../../atoms';
|
||||
import { useCurrentMode } from '../../../hooks/current/use-current-mode';
|
||||
import { ShortcutsModal } from '../shortcuts-modal';
|
||||
import { ContactIcon, HelpIcon, KeyboardIcon } from './icons';
|
||||
@ -14,11 +14,7 @@ import {
|
||||
StyledIsland,
|
||||
StyledTriggerWrapper,
|
||||
} from './style';
|
||||
const ContactModal = lazy(() =>
|
||||
import('@affine/component/contact-modal').then(({ ContactModal }) => ({
|
||||
default: ContactModal,
|
||||
}))
|
||||
);
|
||||
|
||||
const DEFAULT_SHOW_LIST: IslandItemNames[] = [
|
||||
'whatNew',
|
||||
'contact',
|
||||
@ -33,6 +29,7 @@ export const HelpIsland = ({
|
||||
}) => {
|
||||
const mode = useCurrentMode();
|
||||
const [, setOpenOnboarding] = useAtom(openOnboardingModalAtom);
|
||||
const [, setOpenSettingModalAtom] = useAtom(openSettingModalAtom);
|
||||
const [spread, setShowSpread] = useState(false);
|
||||
// const { triggerShortcutsModal, triggerContactModal } = useModal();
|
||||
// const blockHub = useGlobalState(store => store.blockHub);
|
||||
@ -52,8 +49,18 @@ export const HelpIsland = ({
|
||||
// useEffect(() => {
|
||||
// spread && blockHub?.toggleMenu(false);
|
||||
// }, [blockHub, spread]);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const [openShortCut, setOpenShortCut] = useState(false);
|
||||
|
||||
const openAbout = useCallback(() => {
|
||||
setShowSpread(false);
|
||||
|
||||
setOpenSettingModalAtom({
|
||||
open: true,
|
||||
activeTab: 'about',
|
||||
});
|
||||
}, [setOpenSettingModalAtom]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledIsland
|
||||
@ -83,10 +90,7 @@ export const HelpIsland = ({
|
||||
<Tooltip content={t['Contact Us']()} placement="left-end">
|
||||
<StyledIconWrapper
|
||||
data-testid="right-bottom-contact-us-icon"
|
||||
onClick={() => {
|
||||
setShowSpread(false);
|
||||
setOpen(true);
|
||||
}}
|
||||
onClick={openAbout}
|
||||
>
|
||||
<ContactIcon />
|
||||
</StyledIconWrapper>
|
||||
@ -136,13 +140,6 @@ export const HelpIsland = ({
|
||||
</StyledTriggerWrapper>
|
||||
</MuiFade>
|
||||
</StyledIsland>
|
||||
<Suspense>
|
||||
<ContactModal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
logoSrc="/imgs/affine-text-logo.png"
|
||||
/>
|
||||
</Suspense>
|
||||
<ShortcutsModal
|
||||
open={openShortCut}
|
||||
onClose={() => setOpenShortCut(false)}
|
||||
|
@ -54,6 +54,7 @@ export const WorkspaceModeFilterTab = ({ ...props }: WorkspaceTitleProps) => {
|
||||
<Header {...props}>
|
||||
<div className={styles.allPageListTitleWrapper}>
|
||||
<RadioButtonGroup
|
||||
width={300}
|
||||
defaultValue={value}
|
||||
onValueChange={handleValueChange}
|
||||
>
|
||||
|
@ -38,6 +38,7 @@ import type { FC, PropsWithChildren, ReactElement } from 'react';
|
||||
import { lazy, Suspense, useCallback, useEffect, useMemo } from 'react';
|
||||
|
||||
import { WorkspaceAdapters } from '../adapters/workspace';
|
||||
import type { SettingAtom } from '../atoms';
|
||||
import {
|
||||
openQuickSearchModalAtom,
|
||||
openSettingModalAtom,
|
||||
@ -100,14 +101,33 @@ export const QuickSearch: FC = () => {
|
||||
};
|
||||
export const Setting: FC = () => {
|
||||
const [currentWorkspace] = useCurrentWorkspace();
|
||||
const [openSettingModal, setOpenSettingModalAtom] =
|
||||
const [{ open, workspace, activeTab }, setOpenSettingModalAtom] =
|
||||
useAtom(openSettingModalAtom);
|
||||
const blockSuiteWorkspace = currentWorkspace?.blockSuiteWorkspace;
|
||||
|
||||
const onSettingClick = useCallback(
|
||||
({
|
||||
activeTab,
|
||||
workspace,
|
||||
}: Pick<SettingAtom, 'activeTab' | 'workspace'>) => {
|
||||
setOpenSettingModalAtom(prev => ({ ...prev, activeTab, workspace }));
|
||||
},
|
||||
[setOpenSettingModalAtom]
|
||||
);
|
||||
|
||||
if (!blockSuiteWorkspace) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<SettingModal open={openSettingModal} setOpen={setOpenSettingModalAtom} />
|
||||
<SettingModal
|
||||
open={open}
|
||||
activeTab={activeTab || 'appearance'}
|
||||
workspace={workspace}
|
||||
onSettingClick={onSettingClick}
|
||||
setOpen={open => {
|
||||
setOpenSettingModalAtom(prev => ({ ...prev, open }));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -336,7 +356,7 @@ export const WorkspaceLayoutInner: FC<PropsWithChildren> = ({ children }) => {
|
||||
const [, setOpenSettingModalAtom] = useAtom(openSettingModalAtom);
|
||||
|
||||
const handleOpenSettingModal = useCallback(() => {
|
||||
setOpenSettingModalAtom(true);
|
||||
setOpenSettingModalAtom({ activeTab: 'appearance', open: true });
|
||||
}, [setOpenSettingModalAtom]);
|
||||
|
||||
const resizing = useAtomValue(appSidebarResizingAtom);
|
||||
|
@ -13,10 +13,12 @@ import {
|
||||
openCreateWorkspaceModalAtom,
|
||||
openDisableCloudAlertModalAtom,
|
||||
openOnboardingModalAtom,
|
||||
openSettingModalAtom,
|
||||
openWorkspacesModalAtom,
|
||||
} from '../atoms';
|
||||
import { useRouterHelper } from '../hooks/use-router-helper';
|
||||
import { useWorkspaces } from '../hooks/use-workspaces';
|
||||
import type { AllWorkspace } from '../shared';
|
||||
|
||||
const WorkspaceListModal = lazy(() =>
|
||||
import('../components/pure/workspace-list-modal').then(module => ({
|
||||
@ -93,6 +95,20 @@ export const AllWorkspaceModals = (): ReactElement => {
|
||||
rootCurrentWorkspaceIdAtom
|
||||
);
|
||||
const [transitioning, transition] = useTransition();
|
||||
const [, setOpenSettingModalAtom] = useAtom(openSettingModalAtom);
|
||||
|
||||
const handleOpenSettingModal = useCallback(
|
||||
(workspace: AllWorkspace) => {
|
||||
setOpenWorkspacesModal(false);
|
||||
|
||||
setOpenSettingModalAtom({
|
||||
open: true,
|
||||
activeTab: 'workspace',
|
||||
workspace,
|
||||
});
|
||||
},
|
||||
[setOpenSettingModalAtom, setOpenWorkspacesModal]
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Suspense>
|
||||
@ -129,18 +145,7 @@ export const AllWorkspaceModals = (): ReactElement => {
|
||||
},
|
||||
[jumpToSubPath, setCurrentWorkspaceId, setOpenWorkspacesModal]
|
||||
)}
|
||||
onClickWorkspaceSetting={useCallback(
|
||||
workspace => {
|
||||
setOpenWorkspacesModal(false);
|
||||
setCurrentWorkspaceId(workspace.id);
|
||||
jumpToSubPath(workspace.id, WorkspaceSubPath.SETTING).catch(
|
||||
error => {
|
||||
console.error(error);
|
||||
}
|
||||
);
|
||||
},
|
||||
[jumpToSubPath, setCurrentWorkspaceId, setOpenWorkspacesModal]
|
||||
)}
|
||||
onClickWorkspaceSetting={handleOpenSettingModal}
|
||||
onNewWorkspace={useCallback(() => {
|
||||
setOpenCreateWorkspaceModal('new');
|
||||
}, [setOpenCreateWorkspaceModal])}
|
||||
|
@ -1,138 +0,0 @@
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
|
||||
import { FlexWrapper, Modal, ModalCloseButton, ModalWrapper } from '../..';
|
||||
import {
|
||||
DiscordIcon,
|
||||
DocIcon,
|
||||
GithubIcon,
|
||||
LinkIcon,
|
||||
LogoIcon,
|
||||
RedditIcon,
|
||||
TelegramIcon,
|
||||
TwitterIcon,
|
||||
} from './icons';
|
||||
import {
|
||||
StyledBigLink,
|
||||
StyledLogo,
|
||||
StyledModalFooter,
|
||||
StyledModalHeader,
|
||||
StyledPrivacyContainer,
|
||||
StyledSmallLink,
|
||||
StyledSubTitle,
|
||||
} from './style';
|
||||
export const relatedLinks = [
|
||||
{
|
||||
icon: <GithubIcon />,
|
||||
title: 'GitHub',
|
||||
link: 'https://github.com/toeverything/AFFiNE',
|
||||
},
|
||||
{
|
||||
icon: <RedditIcon />,
|
||||
title: 'Reddit',
|
||||
link: 'https://www.reddit.com/r/Affine/',
|
||||
},
|
||||
{
|
||||
icon: <TwitterIcon />,
|
||||
title: 'Twitter',
|
||||
link: 'https://twitter.com/AffineOfficial',
|
||||
},
|
||||
{
|
||||
icon: <TelegramIcon />,
|
||||
title: 'Telegram',
|
||||
link: 'https://t.me/affineworkos',
|
||||
},
|
||||
{
|
||||
icon: <DiscordIcon />,
|
||||
title: 'Discord',
|
||||
link: 'https://discord.gg/Arn7TqJBvG',
|
||||
},
|
||||
];
|
||||
|
||||
export type ContactModalProps = {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
logoSrc: string;
|
||||
};
|
||||
|
||||
export const ContactModal = ({
|
||||
open,
|
||||
onClose,
|
||||
logoSrc,
|
||||
}: ContactModalProps): JSX.Element => {
|
||||
const t = useAFFiNEI18N();
|
||||
const topLinkList = [
|
||||
{
|
||||
icon: <LogoIcon />,
|
||||
title: t['Official Website'](),
|
||||
subTitle: 'AFFiNE.pro',
|
||||
link: 'https://affine.pro',
|
||||
},
|
||||
{
|
||||
icon: <DocIcon />,
|
||||
title: t['Check Our Docs'](),
|
||||
subTitle: 'Open Source',
|
||||
link: 'https://community.affine.pro',
|
||||
},
|
||||
];
|
||||
const date = new Date();
|
||||
const year = date.getFullYear();
|
||||
return (
|
||||
<Modal open={open} onClose={onClose} data-testid="contact-us-modal-content">
|
||||
<ModalWrapper width={720} height={436} style={{ letterSpacing: '1px' }}>
|
||||
<StyledModalHeader>
|
||||
<StyledLogo src={logoSrc} alt="" />
|
||||
|
||||
<ModalCloseButton
|
||||
onClick={() => {
|
||||
onClose();
|
||||
}}
|
||||
/>
|
||||
</StyledModalHeader>
|
||||
|
||||
<FlexWrapper alignItems="center" justifyContent="center">
|
||||
{topLinkList.map(({ icon, title, subTitle, link }) => {
|
||||
return (
|
||||
<StyledBigLink key={title} href={link} target="_blank">
|
||||
{icon}
|
||||
<p>{title}</p>
|
||||
<p>
|
||||
{subTitle}
|
||||
<LinkIcon />
|
||||
</p>
|
||||
</StyledBigLink>
|
||||
);
|
||||
})}
|
||||
</FlexWrapper>
|
||||
<StyledSubTitle>
|
||||
{t['Get in touch! Join our communities']()}
|
||||
</StyledSubTitle>
|
||||
<FlexWrapper justifyContent="center">
|
||||
{relatedLinks.map(({ icon, title, link }) => {
|
||||
return (
|
||||
<StyledSmallLink key={title} href={link} target="_blank">
|
||||
{icon}
|
||||
<p>{title}</p>
|
||||
</StyledSmallLink>
|
||||
);
|
||||
})}
|
||||
</FlexWrapper>
|
||||
|
||||
<StyledModalFooter>
|
||||
<p>Copyright © {year} Toeverything</p>
|
||||
<StyledPrivacyContainer>
|
||||
<a href="https://affine.pro/terms" target="_blank" rel="noreferrer">
|
||||
Terms
|
||||
</a>
|
||||
<a
|
||||
href="https://affine.pro/privacy"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Privacy
|
||||
</a>
|
||||
</StyledPrivacyContainer>
|
||||
</StyledModalFooter>
|
||||
</ModalWrapper>
|
||||
</Modal>
|
||||
);
|
||||
};
|
@ -1,144 +0,0 @@
|
||||
import { absoluteCenter, displayFlex, styled } from '../..';
|
||||
|
||||
export const StyledBigLink = styled('a')(() => {
|
||||
return {
|
||||
width: '268px',
|
||||
height: '76px',
|
||||
paddingLeft: '96px',
|
||||
fontSize: '24px',
|
||||
lineHeight: '36px',
|
||||
color: 'var(--affine-text-primary-color)',
|
||||
borderRadius: '10px',
|
||||
flexDirection: 'column',
|
||||
...displayFlex('center'),
|
||||
position: 'relative',
|
||||
transition: 'background .15s',
|
||||
letterSpacing: '1px',
|
||||
|
||||
':visited': {
|
||||
color: 'var(--affine-text-primary-color)',
|
||||
},
|
||||
':hover': {
|
||||
background: 'rgba(68, 97, 242, 0.1)',
|
||||
},
|
||||
':not(:last-of-type)': {
|
||||
marginRight: '48px',
|
||||
},
|
||||
svg: {
|
||||
width: '48px',
|
||||
height: '48px',
|
||||
marginRight: '24px',
|
||||
color: 'var(--affine-primary-color)',
|
||||
...absoluteCenter({ vertical: true, position: { left: '26px' } }),
|
||||
},
|
||||
p: {
|
||||
width: '100%',
|
||||
height: '24px',
|
||||
lineHeight: '24px',
|
||||
|
||||
...displayFlex('flex-start', 'center'),
|
||||
':first-of-type': {
|
||||
marginBottom: '4px',
|
||||
fontSize: '18px',
|
||||
fontWeight: '600',
|
||||
},
|
||||
':last-of-type': {
|
||||
fontSize: '16px',
|
||||
color: 'var(--affine-primary-color)',
|
||||
fontWeight: '500',
|
||||
},
|
||||
svg: {
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
position: 'static',
|
||||
transform: 'translate(0,0)',
|
||||
marginLeft: '4px',
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
export const StyledSmallLink = styled('a')(() => {
|
||||
return {
|
||||
width: '124px',
|
||||
height: '76px',
|
||||
fontSize: '16px',
|
||||
fontWeight: '500',
|
||||
borderRadius: '5px',
|
||||
color: 'var(--affine-text-primary-color)',
|
||||
transition: 'background .15s, color .15s',
|
||||
|
||||
...displayFlex('center', 'center'),
|
||||
flexWrap: 'wrap',
|
||||
':visited': {
|
||||
color: 'var(--affine-text-primary-color)',
|
||||
},
|
||||
':hover': {
|
||||
color: 'var(--affine-primary-color)',
|
||||
background: 'var(--affine-hover-color)',
|
||||
},
|
||||
svg: {
|
||||
width: '22px',
|
||||
color: 'var(--affine-primary-color)',
|
||||
},
|
||||
p: {
|
||||
width: '100%',
|
||||
textAlign: 'center',
|
||||
},
|
||||
};
|
||||
});
|
||||
export const StyledSubTitle = styled('div')(() => {
|
||||
return {
|
||||
fontSize: '18px',
|
||||
fontWeight: '600',
|
||||
color: 'var(--affine-text-primary-color)',
|
||||
marginTop: '52px',
|
||||
marginBottom: '8px',
|
||||
textAlign: 'center',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledLogo = styled('img')({
|
||||
height: '18px',
|
||||
width: 'auto',
|
||||
marginTop: '24px',
|
||||
});
|
||||
|
||||
export const StyledModalHeader = styled('div')(() => {
|
||||
return {
|
||||
height: '72px',
|
||||
padding: '0 40px',
|
||||
marginBottom: '24px',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledModalFooter = styled('div')(() => {
|
||||
return {
|
||||
fontSize: '14px',
|
||||
lineHeight: '20px',
|
||||
textAlign: 'center',
|
||||
color: 'var(--affine-text-primary-color)',
|
||||
marginTop: '40px',
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledPrivacyContainer = styled('div')(() => {
|
||||
return {
|
||||
marginTop: '4px',
|
||||
position: 'relative',
|
||||
a: {
|
||||
height: '16px',
|
||||
lineHeight: '16px',
|
||||
color: 'var(--affine-icon-color)',
|
||||
padding: '0 8px',
|
||||
':visited': {
|
||||
color: 'var(--affine-icon-color)',
|
||||
},
|
||||
':first-of-type': {
|
||||
borderRight: '1px solid var(--affine-border-color)',
|
||||
},
|
||||
':hover': {
|
||||
color: 'var(--affine-primary-color)',
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
@ -31,7 +31,8 @@ export const SettingModal: FC<PropsWithChildren<SettingModalProps>> = ({
|
||||
maxWidth: '70vw',
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
backgroundColor: 'var(--affine-white)',
|
||||
backgroundColor: 'var(--affine-background-overlay-panel-color)',
|
||||
boxShadow: 'var(--affine-popover-shadow)',
|
||||
}}
|
||||
>
|
||||
<ModalCloseButton top={16} right={20} onClick={handleClose} />
|
||||
|
@ -3,33 +3,45 @@ import type {
|
||||
RadioGroupProps,
|
||||
} from '@radix-ui/react-radio-group';
|
||||
import * as RadioGroup from '@radix-ui/react-radio-group';
|
||||
import { forwardRef } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { type CSSProperties, forwardRef } from 'react';
|
||||
|
||||
import * as styles from './styles.css';
|
||||
|
||||
export const RadioButton = forwardRef<HTMLButtonElement, RadioGroupItemProps>(
|
||||
({ children, ...props }, ref) => {
|
||||
return (
|
||||
<RadioGroup.Item ref={ref} {...props}>
|
||||
<span className={styles.radioUncheckedButton}>{children}</span>
|
||||
<RadioGroup.Indicator className={styles.radioButton}>
|
||||
{children}
|
||||
</RadioGroup.Indicator>
|
||||
</RadioGroup.Item>
|
||||
);
|
||||
}
|
||||
);
|
||||
export const RadioButton = forwardRef<
|
||||
HTMLButtonElement,
|
||||
RadioGroupItemProps & { bold?: boolean }
|
||||
>(({ children, bold, className, ...props }, ref) => {
|
||||
return (
|
||||
<RadioGroup.Item
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={clsx(styles.radioButton, className)}
|
||||
>
|
||||
<span className={clsx(styles.radioUncheckedButton, { bold })}>
|
||||
{children}
|
||||
</span>
|
||||
<RadioGroup.Indicator
|
||||
className={clsx(styles.radioButtonContent, { bold })}
|
||||
>
|
||||
{children}
|
||||
</RadioGroup.Indicator>
|
||||
</RadioGroup.Item>
|
||||
);
|
||||
});
|
||||
RadioButton.displayName = 'RadioButton';
|
||||
|
||||
export const RadioButtonGroup = forwardRef<HTMLDivElement, RadioGroupProps>(
|
||||
({ ...props }, ref) => {
|
||||
return (
|
||||
<RadioGroup.Root
|
||||
ref={ref}
|
||||
className={styles.radioButtonGroup}
|
||||
{...props}
|
||||
></RadioGroup.Root>
|
||||
);
|
||||
}
|
||||
);
|
||||
export const RadioButtonGroup = forwardRef<
|
||||
HTMLDivElement,
|
||||
RadioGroupProps & { width?: CSSProperties['width'] }
|
||||
>(({ className, style, width, ...props }, ref) => {
|
||||
return (
|
||||
<RadioGroup.Root
|
||||
ref={ref}
|
||||
className={clsx(styles.radioButtonGroup, className)}
|
||||
style={{ width, ...style }}
|
||||
{...props}
|
||||
></RadioGroup.Root>
|
||||
);
|
||||
});
|
||||
RadioButtonGroup.displayName = 'RadioButtonGroup';
|
||||
|
@ -54,11 +54,13 @@ export const dropdownIcon = style({
|
||||
});
|
||||
|
||||
export const radioButton = style({
|
||||
flexGrow: 1,
|
||||
});
|
||||
export const radioButtonContent = style({
|
||||
fontSize: 'var(--affine-font-xs)',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: '0 30px',
|
||||
height: '24px',
|
||||
borderRadius: '8px',
|
||||
filter: 'drop-shadow(0px 0px 4px rgba(0, 0, 0, 0.1))',
|
||||
@ -71,11 +73,14 @@ export const radioButton = style({
|
||||
'&[data-state="checked"]': {
|
||||
background: 'var(--affine-white)',
|
||||
},
|
||||
'&.bold': {
|
||||
fontWeight: 600,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const radioUncheckedButton = style([
|
||||
radioButton,
|
||||
radioButtonContent,
|
||||
{
|
||||
selectors: {
|
||||
'[data-state="checked"] > &': {
|
||||
@ -87,7 +92,8 @@ export const radioUncheckedButton = style([
|
||||
|
||||
export const radioButtonGroup = style({
|
||||
display: 'inline-flex',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
background: 'var(--affine-hover-color)',
|
||||
borderRadius: '10px',
|
||||
padding: '2px',
|
||||
|
@ -7,21 +7,21 @@ import { SIZE_DEFAULT, SIZE_MIDDLE, SIZE_SMALL } from './interface';
|
||||
export const SIZE_CONFIG = {
|
||||
[SIZE_SMALL]: {
|
||||
iconSize: 16,
|
||||
fontSize: 16,
|
||||
fontSize: 'var(--affine-font-xs)',
|
||||
borderRadius: 4,
|
||||
height: 26,
|
||||
height: 28,
|
||||
padding: 6,
|
||||
},
|
||||
[SIZE_MIDDLE]: {
|
||||
iconSize: 20,
|
||||
fontSize: 16,
|
||||
fontSize: 'var(--affine-font-sm)',
|
||||
borderRadius: 4,
|
||||
height: 32,
|
||||
padding: 12,
|
||||
},
|
||||
[SIZE_DEFAULT]: {
|
||||
iconSize: 24,
|
||||
fontSize: 16,
|
||||
fontSize: 'var(--affine-font-base)',
|
||||
height: 38,
|
||||
padding: 24,
|
||||
borderRadius: 4,
|
||||
|
@ -105,12 +105,11 @@ export const StyledMenuItem = styled('button')<{
|
||||
export const StyledButton = styled(Button)(() => {
|
||||
return {
|
||||
width: '100%',
|
||||
height: '32px',
|
||||
// height: '32px',
|
||||
borderRadius: '8px',
|
||||
backgroundColor: 'transparent',
|
||||
...displayFlex('space-between', 'center'),
|
||||
border: `1px solid var(--affine-border-color)`,
|
||||
padding: '0 10px',
|
||||
fontSize: 'var(--affine-font-base)',
|
||||
};
|
||||
});
|
||||
|
@ -14,6 +14,7 @@ test('Click right-bottom corner contact icon', async ({ page }) => {
|
||||
expect(await rightBottomContactUs.isVisible()).toEqual(true);
|
||||
|
||||
await rightBottomContactUs.click();
|
||||
const contactUsModal = page.locator('[data-testid=contact-us-modal-content]');
|
||||
await expect(contactUsModal).toContainText('Check Our Docs');
|
||||
|
||||
const title = await page.getByTestId('about-title');
|
||||
await expect(title).toBeVisible();
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user