diff --git a/packages/common/env/src/global.ts b/packages/common/env/src/global.ts
index e074fac47b..0532ff1248 100644
--- a/packages/common/env/src/global.ts
+++ b/packages/common/env/src/global.ts
@@ -18,6 +18,7 @@ export const runtimeFlagsSchema = z.object({
enableBroadcastChannelProvider: z.boolean(),
enableDebugPage: z.boolean(),
changelogUrl: z.string(),
+ downloadUrl: z.string(),
// see: tools/workers
imageProxyUrl: z.string(),
enablePreloading: z.boolean(),
diff --git a/packages/frontend/component/src/components/affine-banner/index.css.ts b/packages/frontend/component/src/components/affine-banner/index.css.ts
index 345f9eb30f..b105bdeef1 100644
--- a/packages/frontend/component/src/components/affine-banner/index.css.ts
+++ b/packages/frontend/component/src/components/affine-banner/index.css.ts
@@ -40,6 +40,11 @@ export const tipsContainer = style({
position: 'sticky',
gap: '16px',
containerType: 'inline-size',
+ '@media': {
+ 'screen and (max-width: 520px)': {
+ flexWrap: 'wrap',
+ },
+ },
});
export const tipsMessage = style({
@@ -54,4 +59,9 @@ export const tipsRightItem = style({
justifyContent: 'space-between',
alignItems: 'center',
gap: '16px',
+ '@media': {
+ 'screen and (max-width: 520px)': {
+ width: '100%',
+ },
+ },
});
diff --git a/packages/frontend/component/src/components/affine-other-page-layout/desktop-navbar.tsx b/packages/frontend/component/src/components/affine-other-page-layout/desktop-navbar.tsx
new file mode 100644
index 0000000000..5b8aeb860b
--- /dev/null
+++ b/packages/frontend/component/src/components/affine-other-page-layout/desktop-navbar.tsx
@@ -0,0 +1,24 @@
+import * as styles from './index.css';
+import { useNavConfig } from './use-nav-config';
+
+export const DesktopNavbar = () => {
+ const config = useNavConfig();
+
+ return (
+
+ );
+};
diff --git a/packages/frontend/component/src/components/affine-other-page-layout/index.css.ts b/packages/frontend/component/src/components/affine-other-page-layout/index.css.ts
new file mode 100644
index 0000000000..0910728906
--- /dev/null
+++ b/packages/frontend/component/src/components/affine-other-page-layout/index.css.ts
@@ -0,0 +1,89 @@
+import { style } from '@vanilla-extract/css';
+
+export const root = style({
+ height: '100vh',
+ width: '100vw',
+ display: 'flex',
+ flexDirection: 'column',
+ fontSize: 'var(--affine-font-base)',
+ position: 'relative',
+ background: 'var(--affine-background-primary-color)',
+});
+
+export const affineLogo = style({
+ color: 'inherit',
+});
+
+export const topNav = style({
+ top: 0,
+ left: 0,
+ right: 0,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ padding: '16px 120px',
+ selectors: {
+ '&.mobile': {
+ padding: '16px 20px',
+ },
+ },
+});
+
+export const topNavLinks = style({
+ display: 'flex',
+ columnGap: 4,
+});
+
+export const topNavLink = style({
+ color: 'var(--affine-text-primary-color)',
+ fontSize: 'var(--affine-font-sm)',
+ fontWeight: 500,
+ textDecoration: 'none',
+ padding: '4px 18px',
+});
+
+export const iconButton = style({
+ fontSize: '24px',
+ pointerEvents: 'auto',
+ selectors: {
+ '&.plain': {
+ color: 'var(--affine-text-primary-color)',
+ },
+ },
+});
+
+export const menu = style({
+ width: '100vw',
+ height: '100vh',
+ padding: '0',
+ background: 'var(--affine-background-primary-color)',
+ borderRadius: '0',
+ border: 'none',
+ boxShadow: 'none',
+});
+
+export const menuItem = style({
+ color: 'var(--affine-text-primary-color)',
+ fontSize: 'var(--affine-font-sm)',
+ fontWeight: 500,
+ textDecoration: 'none',
+ padding: '12px 20px',
+ maxWidth: '100%',
+ position: 'relative',
+ borderRadius: '0',
+ transition: 'background 0.3s ease',
+ selectors: {
+ '&:after': {
+ position: 'absolute',
+ content: '""',
+ bottom: 0,
+ display: 'block',
+ width: 'calc(100% - 40px)',
+ height: '0.5px',
+ background: 'var(--affine-black-10)',
+ },
+ '&:not(:last-of-type)': {
+ marginBottom: '0',
+ },
+ },
+});
diff --git a/packages/frontend/component/src/components/affine-other-page-layout/index.tsx b/packages/frontend/component/src/components/affine-other-page-layout/index.tsx
new file mode 100644
index 0000000000..5d15fe1b3c
--- /dev/null
+++ b/packages/frontend/component/src/components/affine-other-page-layout/index.tsx
@@ -0,0 +1 @@
+export * from './layout';
diff --git a/packages/frontend/component/src/components/affine-other-page-layout/layout.tsx b/packages/frontend/component/src/components/affine-other-page-layout/layout.tsx
new file mode 100644
index 0000000000..c58891324c
--- /dev/null
+++ b/packages/frontend/component/src/components/affine-other-page-layout/layout.tsx
@@ -0,0 +1,49 @@
+import { Button } from '@affine/component/ui/button';
+import { useAFFiNEI18N } from '@affine/i18n/hooks';
+import { Logo1Icon } from '@blocksuite/icons';
+import clsx from 'clsx';
+import { useCallback } from 'react';
+
+import { DesktopNavbar } from './desktop-navbar';
+import * as styles from './index.css';
+import { MobileNavbar } from './mobile-navbar';
+
+export const AffineOtherPageLayout = ({
+ isSmallScreen,
+ children,
+}: {
+ isSmallScreen: boolean;
+ children: React.ReactNode;
+}) => {
+ const t = useAFFiNEI18N();
+
+ const openDownloadLink = useCallback(() => {
+ open(runtimeConfig.downloadUrl, '_blank');
+ }, []);
+
+ return (
+
+
+
+
+
+ {isSmallScreen ? (
+
+ ) : (
+ <>
+
+
+ >
+ )}
+
+
+ {children}
+
+ );
+};
diff --git a/packages/frontend/component/src/components/affine-other-page-layout/mobile-navbar.tsx b/packages/frontend/component/src/components/affine-other-page-layout/mobile-navbar.tsx
new file mode 100644
index 0000000000..1d544b27fd
--- /dev/null
+++ b/packages/frontend/component/src/components/affine-other-page-layout/mobile-navbar.tsx
@@ -0,0 +1,50 @@
+import { IconButton } from '@affine/component/ui/button';
+import { Menu, MenuItem } from '@affine/component/ui/menu';
+import { CloseIcon, PropertyIcon } from '@blocksuite/icons';
+import { useState } from 'react';
+
+import * as styles from './index.css';
+import { useNavConfig } from './use-nav-config';
+
+export const MobileNavbar = () => {
+ const [openMenu, setOpenMenu] = useState(false);
+ const navConfig = useNavConfig();
+
+ const menuItems = (
+ <>
+ {navConfig.map(item => {
+ return (
+
+ );
+ })}
+ >
+ );
+
+ return (
+
+
+
+ );
+};
diff --git a/packages/frontend/component/src/components/affine-other-page-layout/use-nav-config.ts b/packages/frontend/component/src/components/affine-other-page-layout/use-nav-config.ts
new file mode 100644
index 0000000000..379da19bf7
--- /dev/null
+++ b/packages/frontend/component/src/components/affine-other-page-layout/use-nav-config.ts
@@ -0,0 +1,27 @@
+import { useAFFiNEI18N } from '@affine/i18n/hooks';
+import { useMemo } from 'react';
+
+export const useNavConfig = () => {
+ const t = useAFFiNEI18N();
+ return useMemo(
+ () => [
+ {
+ title: t['com.affine.other-page.nav.official-website'](),
+ path: 'https://affine.pro',
+ },
+ {
+ title: t['com.affine.other-page.nav.affine-community'](),
+ path: 'https://community.affine.pro/home',
+ },
+ {
+ title: t['com.affine.other-page.nav.blog'](),
+ path: 'https://affine.pro/blog',
+ },
+ {
+ title: t['com.affine.other-page.nav.contact-us'](),
+ path: 'https://affine.pro/about-us',
+ },
+ ],
+ [t]
+ );
+};
diff --git a/packages/frontend/component/src/components/auth-components/auth-input.tsx b/packages/frontend/component/src/components/auth-components/auth-input.tsx
index c2565f1f74..523b47cb43 100644
--- a/packages/frontend/component/src/components/auth-components/auth-input.tsx
+++ b/packages/frontend/component/src/components/auth-components/auth-input.tsx
@@ -2,7 +2,7 @@ import clsx from 'clsx';
import type { FC, HTMLAttributes } from 'react';
import { Input, type InputProps } from '../../ui/input';
-import { authInputWrapper, formHint } from './share.css';
+import * as styles from './share.css';
export type AuthInputProps = InputProps & {
label?: string;
error?: boolean;
@@ -22,13 +22,14 @@ export const AuthInput: FC = ({
}) => {
return (
{label ?
: null}
{
@@ -40,7 +41,7 @@ export const AuthInput: FC
= ({
/>
{error && errorHint && !withoutHint ? (
diff --git a/packages/frontend/component/src/components/auth-components/auth-page-container.tsx b/packages/frontend/component/src/components/auth-components/auth-page-container.tsx
index 10f6d29ba6..5cea173e53 100644
--- a/packages/frontend/component/src/components/auth-components/auth-page-container.tsx
+++ b/packages/frontend/component/src/components/auth-components/auth-page-container.tsx
@@ -1,32 +1,41 @@
-import type { FC, PropsWithChildren, ReactNode } from 'react';
+import {
+ type FC,
+ type PropsWithChildren,
+ type ReactNode,
+ useEffect,
+ useState,
+} from 'react';
import { Empty } from '../../ui/empty';
-import { Wrapper } from '../../ui/layout';
-import { Logo } from './logo';
+import { AffineOtherPageLayout } from '../affine-other-page-layout';
import { authPageContainer } from './share.css';
export const AuthPageContainer: FC<
PropsWithChildren<{ title?: ReactNode; subtitle?: ReactNode }>
> = ({ children, title, subtitle }) => {
+ const [isSmallScreen, setIsSmallScreen] = useState(false);
+
+ useEffect(() => {
+ const checkScreenSize = () => {
+ setIsSmallScreen(window.innerWidth <= 1024);
+ };
+ checkScreenSize();
+ window.addEventListener('resize', checkScreenSize);
+ return () => window.removeEventListener('resize', checkScreenSize);
+ }, []);
+
return (
-
-
-
-
-
-
-
{title}
-
{subtitle}
- {children}
+
+
+
+
+
{title}
+
{subtitle}
+ {children}
+
+ {isSmallScreen ? null :
}
-
-
+
);
};
diff --git a/packages/frontend/component/src/components/auth-components/change-email-page.tsx b/packages/frontend/component/src/components/auth-components/change-email-page.tsx
index e8560c2df9..c7b6291eb4 100644
--- a/packages/frontend/component/src/components/auth-components/change-email-page.tsx
+++ b/packages/frontend/component/src/components/auth-components/change-email-page.tsx
@@ -44,7 +44,6 @@ export const ChangeEmailPage = ({
>
<>
{
setPasswordPass(true);
passwordRef.current = password;
diff --git a/packages/frontend/component/src/components/auth-components/share.css.ts b/packages/frontend/component/src/components/auth-components/share.css.ts
index 0f5d8bcc5d..a1fde37f4d 100644
--- a/packages/frontend/component/src/components/auth-components/share.css.ts
+++ b/packages/frontend/component/src/components/auth-components/share.css.ts
@@ -151,14 +151,25 @@ export const authPageContainer = style({
justifyContent: 'center',
alignItems: 'center',
fontSize: 'var(--affine-font-base)',
+ '@media': {
+ 'screen and (max-width: 1024px)': {
+ flexDirection: 'column',
+ padding: '100px 20px',
+ justifyContent: 'flex-start',
+ },
+ },
});
globalStyle(`${authPageContainer} .wrapper`, {
display: 'flex',
alignItems: 'center',
+ '@media': {
+ 'screen and (max-width: 1024px)': {
+ flexDirection: 'column',
+ },
+ },
});
globalStyle(`${authPageContainer} .content`, {
maxWidth: '700px',
- minWidth: '550px',
});
globalStyle(`${authPageContainer} .title`, {
@@ -178,3 +189,12 @@ export const signInPageContainer = style({
width: '400px',
margin: '205px auto 0',
});
+
+export const input = style({
+ width: '330px',
+ '@media': {
+ 'screen and (max-width: 520px)': {
+ width: '100%',
+ },
+ },
+});
diff --git a/packages/frontend/component/src/components/not-found-page/styles.css.ts b/packages/frontend/component/src/components/not-found-page/styles.css.ts
index c1f4467c34..7ecf6cb4ac 100644
--- a/packages/frontend/component/src/components/not-found-page/styles.css.ts
+++ b/packages/frontend/component/src/components/not-found-page/styles.css.ts
@@ -8,6 +8,7 @@ export const notFoundPageContainer = style({
alignItems: 'center',
justifyContent: 'center',
width: '100vw',
+ padding: '0 20px',
});
export const wrapper = style({
diff --git a/packages/frontend/core/.webpack/runtime-config.ts b/packages/frontend/core/.webpack/runtime-config.ts
index f660d9fa30..459f5ec857 100644
--- a/packages/frontend/core/.webpack/runtime-config.ts
+++ b/packages/frontend/core/.webpack/runtime-config.ts
@@ -20,6 +20,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
enableBroadcastChannelProvider: true,
enableDebugPage: true,
changelogUrl: 'https://affine.pro/what-is-new',
+ downloadUrl: 'https://affine.pro/download',
imageProxyUrl: 'https://workers.toeverything.workers.dev/proxy/image',
enablePreloading: true,
enableNewSettingModal: true,
@@ -67,6 +68,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
enableBroadcastChannelProvider: true,
enableDebugPage: true,
changelogUrl: 'https://github.com/toeverything/AFFiNE/releases',
+ downloadUrl: 'https://affine.pro/download',
imageProxyUrl: 'https://workers.toeverything.workers.dev/proxy/image',
enablePreloading: true,
enableNewSettingModal: true,
diff --git a/packages/frontend/core/src/pages/upgrade-success.css.ts b/packages/frontend/core/src/pages/upgrade-success.css.ts
index 4183c5ae7f..654f56d424 100644
--- a/packages/frontend/core/src/pages/upgrade-success.css.ts
+++ b/packages/frontend/core/src/pages/upgrade-success.css.ts
@@ -1,87 +1,9 @@
import { style } from '@vanilla-extract/css';
-
-export const root = style({
- height: '100vh',
- width: '100vw',
- display: 'flex',
- justifyContent: 'center',
- alignItems: 'center',
- fontSize: 'var(--affine-font-base)',
- position: 'relative',
-});
-
-export const affineLogo = style({
- color: 'inherit',
-});
-
-export const topNav = style({
- position: 'absolute',
- top: 0,
- left: 0,
- right: 0,
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'space-between',
- padding: '16px 120px',
-});
-
-export const topNavLinks = style({
- display: 'flex',
- columnGap: 4,
-});
-
-export const topNavLink = style({
- color: 'var(--affine-text-primary-color)',
- fontSize: 'var(--affine-font-sm)',
- fontWeight: 500,
- textDecoration: 'none',
- padding: '4px 18px',
-});
-
-export const tryAgainLink = style({
- color: 'var(--affine-link-color)',
- fontWeight: 500,
- textDecoration: 'none',
- fontSize: 'var(--affine-font-sm)',
-});
-
-export const centerContent = style({
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'center',
- marginTop: 40,
-});
-
-export const prompt = style({
- marginTop: 20,
- marginBottom: 12,
-});
-
-export const body = style({
- display: 'flex',
- justifyContent: 'center',
- alignItems: 'center',
- width: '100%',
- flexWrap: 'wrap',
- gap: '48px',
- padding: '0 20px',
-});
-
-export const leftContainer = style({
- display: 'flex',
- flexDirection: 'column',
- width: '548px',
- gap: '28px',
-});
-export const leftContentTitle = style({
- fontSize: 'var(--affine-font-title)',
- fontWeight: 700,
- minHeight: '44px',
-});
export const leftContentText = style({
fontSize: 'var(--affine-font-base)',
fontWeight: 400,
lineHeight: '1.6',
+ maxWidth: '548px',
});
export const mail = style({
diff --git a/packages/frontend/core/src/pages/upgrade-success.tsx b/packages/frontend/core/src/pages/upgrade-success.tsx
index ab0cdd168f..ce67a89711 100644
--- a/packages/frontend/core/src/pages/upgrade-success.tsx
+++ b/packages/frontend/core/src/pages/upgrade-success.tsx
@@ -1,8 +1,7 @@
-import { Empty } from '@affine/component';
+import { AuthPageContainer } from '@affine/component/auth-components';
import { Button } from '@affine/component/ui/button';
import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
-import { Logo1Icon } from '@blocksuite/icons';
import { useCallback } from 'react';
import { useNavigateHelper } from '../hooks/use-navigate-helper';
@@ -11,93 +10,40 @@ import * as styles from './upgrade-success.css';
export const UpgradeSuccess = () => {
const t = useAFFiNEI18N();
- const openDownloadLink = useCallback(() => {
- const url = `https://affine.pro/download`;
- open(url, '_blank');
- }, []);
-
const { jumpToIndex } = useNavigateHelper();
const openAffine = useCallback(() => {
jumpToIndex();
}, [jumpToIndex]);
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
- {t['com.affine.payment.upgrade-success-page.title']()}
-
-
- {t['com.affine.payment.upgrade-success-page.text']()}
-
-
- ),
- }}
+ const subtitle = (
+
+ {t['com.affine.payment.upgrade-success-page.text']()}
+
+
-
-
-
-
-
-
-
+ ),
+ }}
+ />
);
+
+ return (
+
+
+
+ );
};
export const Component = () => {