Merge branch 'master' into feat/client-app

This commit is contained in:
linonetwo 2023-02-09 22:39:08 +08:00
commit 6b31af0854
176 changed files with 10442 additions and 2571 deletions

48
.github/CLA.md vendored Normal file
View File

@ -0,0 +1,48 @@
<!-- To indicate your agreement, simply edit this file and submit a pull request. -->
# AFFiNE Contributor License Agreement
To clarify the intellectual property license granted with contributions from any person or entity, AFFiNE must have on file a signed Contributor License Agreement ("CLA") from each contributor, indicating agreement with the license terms below. This agreement is for your protection as a contributor as well as the protection of the AFFiNE and its users; it does not change your rights to use your own contributions for any other purpose.
You accept and agree to the following terms and conditions for your past, present and future contributions submitted to AFFiNE. You should sign this agreement before submitting your first contribution. Except for the license granted herein to AFFiNE and recipients of software distributed by AFFiNE, You reserve all right, title, and interest in and to Your Contributions.
1. Parties.
(a) "AFFiNE" refers to the project's operator, TOEVERYTHING PTE. LTD registered in Republic of Singapore.
(b) "You" (or "Your") means the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with AFFiNE.
2. Definitions. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to AFFiNE for inclusion in, or documentation of, any of the products owned or managed by AFFiNE (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to AFFiNE or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, AFFiNE for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution".
3. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to AFFiNE and to recipients of software distributed by AFFiNE a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to use, copy, reproduce, prepare derivative works of, distribute, sublicense, and publicly perform and display the Contribution and such derivative works on any licensing terms, including without limitation open source licenses and binary, proprietary, or commercial licenses.
4. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to AFFiNE and to recipients of software distributed by AFFiNE a perpetual, irrevocable, non-exclusive, worldwide, no-charge, royalty-free patent license to make, have made, use, sell, offer to sell, import, and otherwise transfer your Contribution in whole or in part, alone or in combination with or included in any product, work or materials arising out of the project to which your contribution was submitted, and to sublicense these same rights to third parties through multiple levels of sublicensees or other licensing arrangements.
5. Except as set out above, You keep all right, title, and interest in your contribution. The rights that you grant to AFFiNE under these terms are effective on the date you first submitted a contribution to AFFiNE, even if your submission took place before the date you sign these terms.
6. You promise that:
- Each of Your Contributions is Your original work and that you are legally entitled to grant the above license.
- Each of Your Contributions does not to the best of your knowledge violate any third party's copyrights, trademarks, patents, or other intellectual property rights;
- Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions.
- If You are an individual and if your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to AFFiNE, or that your employer has executed a separate Corporate CLA with AFFiNE.
7. You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
8. You agree to notify AFFiNE of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect.
9. This Agreement will be governed by the laws of Republic of Singapore without reference to conflict of laws principles.
## List of Contributors
The below-signed are contributors to a code repository that is part of the project named "AFFiNE". Each below-signed contributor has read, understand and agrees to the terms above in the section within this document entitled "AFFiNE Contributor License Agreement" as of the date beside their real name (or entity name) and GitHub account name.
---
<!--
Example:
- Dark Sky, @darkskygit, 2022/07/22
-->
- Dark Sky, @darkskygit, 2022/07/22

1
.gitignore vendored
View File

@ -48,6 +48,7 @@ Thumbs.db
.next
out/
storybook-static
module-resolve.js
module-resolve.cjs

View File

@ -89,6 +89,8 @@ Before we tell you how to get started with AFFiNE, we'd like to shamelessly plug
Calling all developers, testers, tech writers and more! Contributions of all types are more than welcome, you can read more in [docs/types-of-contributions.md](docs/types-of-contributions.md). If you are interested in contributing code, read our [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) and feel free to check out our GitHub issues to get stuck in to show us what youre made of.
**Before you start contributing, please make sure you have read and accepted our [Contributor License Agreement]. To indicate your agreement, simply edit this file and submit a pull request.**
For **bug reports**, **feature requests** and other **suggestions** you can also [create a new issue](https://github.com/toeverything/AFFiNE/issues/new/choose) and choose the most appropiate template for your feedback.
For **translation** and **language support** you can visit our [i18n General Space](https://community.affine.pro/c/i18n-general).
@ -197,15 +199,15 @@ Thanks a lot to the community for providing such powerful and simple libraries,
## Self-Host
Get started with Docker and deploy your own feature-rich, restriction-free deployment of AFFiNE - check the [latest packages](https://github.com/toeverything/AFFiNE/pkgs/container/affine-self-hosted).
Get started with Docker and deploy your own feature-rich, restriction-free deployment of AFFiNE - check the [latest packages].
## Hiring
Some amazing companies including AFFiNE are looking for developers! Are you interested in helping build with AFFiNE and/or its partners? Check out some of the latest [jobs available](./docs/jobs/summary.md).
Some amazing companies including AFFiNE are looking for developers! Are you interested in helping build with AFFiNE and/or its partners? Check out some of the latest [jobs available].
## Upgrading
For upgrading information please see our [update page](https://affine.pro/blog?tag=Release%20Note).
For upgrading information please see our [update page].
## Feature Request
@ -213,8 +215,20 @@ For feature request please see https://community.affine.pro/c/feature-requests/
## Is it awesome?
[These people](https://twitter.com/AffineOfficial/followers) seem to like it.
[These people] seem to like it.
## Building
See [BUILDING.md] for instructions on how to build AFFiNE from source code.
## License
See [LICENSE](/LICENSE) for details.
See [LICENSE] for details.
[license]: ./LICENSE
[building.md]: ./docs/BUILDING.md
[these people]: https://twitter.com/AffineOfficial/followers
[update page]: https://affine.pro/blog?tag=Release%20Note
[jobs available]: ./docs/jobs/summary.md
[latest packages]: https://github.com/toeverything/AFFiNE/pkgs/container/affine-self-hosted
[contributor license agreement]: https://github.com/toeverything/affine/edit/master/.github/CLA.md

55
docs/BUILDING.md Normal file
View File

@ -0,0 +1,55 @@
# Building AFFiNE
## Table of Contents
- [Prerequisites](#prerequisites)
- [Setup Environment](#setup-environment)
- [Play with Playground](#play-with-playground)
- [Testing](#testing)
## Prerequisites
We suggest develop our product under node.js LTS(Long-term support) version
### Option 1: Manually install node.js
install [Node LTS version](https://nodejs.org/en/download)
> Up to now, the major node.js version is 18.x
### Option 2: Use node version manager
install [nvm](https://github.com/nvm-sh/nvm)
```sh
nvm install --lts
nvm use --lts
```
## Setup Environment
```sh
# install dependencies
pnpm install
```
## Play with Playground
```sh
pnpm dev
```
The playground page should work at [http://localhost:8080/](http://localhost:8080/)
## Testing
Adding test cases is strongly encouraged when you contribute new features and bug fixes.
We use [Playwright](https://playwright.dev/) for E2E test, and [vitest](https://vitest.dev/) for unit test.
To test locally, please make sure browser binaries are already installed via `npx playwright install`. Then there are multi commands to choose from:
```sh
# run tests in headless mode in another terminal window
pnpm test
```

View File

@ -11,6 +11,7 @@
"dev:local": "pnpm --filter=!@affine/app build && cross-env NODE_API_SERVER=local pnpm --filter @affine/app dev",
"build": " pnpm --filter=!@affine/app build && pnpm --filter!=@affine/datacenter -r build",
"build:client": " pnpm --filter=@affine/client-app build:app",
"build:storybook": " pnpm -r build-storybook",
"export": "pnpm --filter @affine/app export",
"start": "pnpm --filter @affine/app start",
"lint": "pnpm --filter @affine/app lint",
@ -33,7 +34,6 @@
"@typescript-eslint/eslint-plugin": "^5.47.0",
"@typescript-eslint/parser": "^5.47.0",
"concurrently": "^7.6.0",
"babel-plugin-istanbul": "^6.1.1",
"cross-env": "^7.0.3",
"eslint": "^8.30.0",
"eslint-config-next": "12.3.1",

View File

@ -1,28 +0,0 @@
const plugins = [];
if (process.env.NODE_ENV === 'development' || process.env.COVERAGE === 'true') {
console.log(
'Detected development environment. Instrumenting code for coverage.'
);
plugins.push('istanbul');
}
plugins.push([
'@emotion',
{
// See https://emotion.sh/docs/@emotion/babel-plugin
importMap: {
'@/styles': {
styled: {
canonicalImport: ['@emotion/styled', 'default'],
styledBaseImport: ['@/styles', 'styled'],
},
},
},
},
]);
module.exports = {
presets: ['next/babel'],
plugins,
};

View File

@ -11,8 +11,8 @@ const EDITOR_VERSION = enableDebugLocal
const profileTarget = {
ac: '100.85.73.88:12001',
dev: '100.77.180.48:11001',
test: '100.77.180.48:11001',
dev: '100.84.105.99:11001',
test: '100.84.105.99:11001',
stage: '',
pro: 'http://pathfinder.affine.pro',
local: '127.0.0.1:3000',
@ -47,6 +47,7 @@ const nextConfig = {
COMMIT_HASH: getCommitHash(),
EDITOR_VERSION,
},
transpilePackages: ['@affine/component'],
webpack: config => {
config.experiments = { ...config.experiments, topLevelAwait: true };
config.resolve.alias['yjs'] = require.resolve('yjs');

View File

@ -9,21 +9,22 @@
"lint": "next lint"
},
"dependencies": {
"@affine/component": "workspace:*",
"@affine/datacenter": "workspace:*",
"@affine/i18n": "workspace:*",
"@blocksuite/blocks": "0.4.0-alpha.2",
"@blocksuite/editor": "0.4.0-alpha.2",
"@blocksuite/icons": "^2.0.2",
"@blocksuite/store": "0.4.0-alpha.2",
"@emotion/css": "^11.10.0",
"@emotion/react": "^11.10.4",
"@emotion/css": "^11.10.5",
"@emotion/react": "^11.10.5",
"@emotion/server": "^11.10.0",
"@emotion/styled": "^11.10.4",
"@emotion/styled": "^11.10.5",
"@fontsource/poppins": "^4.5.10",
"@fontsource/space-mono": "^4.5.10",
"@mui/base": "^5.0.0-alpha.87",
"@mui/icons-material": "^5.10.9",
"@mui/material": "^5.8.6",
"@mui/base": "=5.0.0-alpha.101",
"@mui/icons-material": "=5.10.9",
"@mui/material": "=5.8.6",
"@toeverything/pathfinder-logger": "workspace:@affine/logger@*",
"cmdk": "^0.1.20",
"css-spring": "^4.1.0",
@ -39,7 +40,6 @@
"yjs": "^13.5.45"
},
"devDependencies": {
"@emotion/babel-plugin": "^11.10.5",
"@types/node": "18.7.18",
"@types/react": "18.0.20",
"@types/react-dom": "18.0.6",

View File

@ -1,6 +1,6 @@
import { NotFoundTitle, PageContainer } from './styles';
import { useTranslation } from '@affine/i18n';
import { Button } from '@/ui/button';
import { Button } from '@affine/component';
import { useRouter } from 'next/router';
export const NotfoundPage = () => {
const { t } = useTranslation();

View File

@ -1,4 +1,4 @@
import { styled } from '@/styles';
import { styled } from '@affine/component';
export const PageContainer = styled('div')(({ theme }) => {
return {

View File

@ -1,4 +1,4 @@
import { Modal, ModalCloseButton, ModalWrapper } from '@/ui/modal';
import { Modal, ModalCloseButton, ModalWrapper } from '@affine/component';
import {
LogoIcon,
DocIcon,

View File

@ -1,4 +1,4 @@
import { absoluteCenter, displayFlex, styled } from '@/styles';
import { absoluteCenter, displayFlex, styled } from '@affine/component';
export const StyledBigLink = styled('a')(({ theme }) => {
return {

View File

@ -1,13 +1,13 @@
import { styled } from '@/styles';
import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal';
import { Button } from '@/ui/button';
import { styled } from '@affine/component';
import { Modal, ModalWrapper, ModalCloseButton } from '@affine/component';
import { Button } from '@affine/component';
import { useState } from 'react';
import Input from '@/ui/input';
import { Input } from '@affine/component';
import { KeyboardEvent } from 'react';
import { useTranslation } from '@affine/i18n';
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
import { useRouter } from 'next/router';
import { toast } from '@/ui/toast';
import { toast } from '@affine/component';
interface ModalProps {
open: boolean;

View File

@ -1,7 +1,7 @@
import { styled } from '@/styles';
import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal';
import { Button } from '@/ui/button';
import Input from '@/ui/input';
import { styled } from '@affine/component';
import { Modal, ModalWrapper, ModalCloseButton } from '@affine/component';
import { Button } from '@affine/component';
import { Input } from '@affine/component';
import { useState } from 'react';
interface LoginModalProps {

View File

@ -14,8 +14,8 @@ import {
UndoIcon,
RedoIcon,
} from './Icons';
import { MuiSlide } from '@/ui/mui';
import { Tooltip } from '@/ui/tooltip';
import { MuiSlide } from '@affine/component';
import { Tooltip } from '@affine/component';
import useCurrentPageMeta from '@/hooks/use-current-page-meta';
import { useAppState } from '@/providers/app-state-provider';
import useHistoryUpdated from '@/hooks/use-history-update';

View File

@ -1,4 +1,4 @@
import { styled, displayFlex } from '@/styles';
import { styled, displayFlex } from '@affine/component';
export const StyledEdgelessToolbar = styled.div(({ theme }) => ({
height: '320px',

View File

@ -1,4 +1,4 @@
import { displayFlex, keyframes, styled } from '@/styles';
import { displayFlex, keyframes, styled } from '@affine/component';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import spring, { toString } from 'css-spring';

View File

@ -3,7 +3,7 @@ import type { Page, Workspace } from '@blocksuite/store';
import '@blocksuite/blocks';
import { EditorContainer } from '@blocksuite/editor';
import exampleMarkdown from '@/templates/Welcome-to-AFFiNE-Alpha-Downhills.md';
import { styled } from '@/styles';
import { styled } from '@affine/component';
const StyledEditorContainer = styled('div')(() => {
return {
@ -59,7 +59,6 @@ export const Editor = ({ page, workspace, setEditor }: Props) => {
}
setEditor(editor);
document.title = page.meta.title || 'Untitled';
return ret;
}, [workspace, page, setEditor]);

View File

@ -1,11 +1,11 @@
import { styled } from '@/styles';
import { Modal, ModalWrapper } from '@/ui/modal';
import { Button, IconButton } from '@/ui/button';
import { styled } from '@affine/component';
import { Modal, ModalWrapper } from '@affine/component';
import { Button, IconButton } from '@affine/component';
import { useTranslation } from '@affine/i18n';
import { useAppState } from '@/providers/app-state-provider';
import { useState } from 'react';
import router from 'next/router';
import { toast } from '@/ui/toast';
import { toast } from '@affine/component';
import { CloseIcon } from '@blocksuite/icons';
interface EnableWorkspaceModalProps {
open: boolean;

View File

@ -1,4 +1,4 @@
import { Button } from '@/ui/button';
import { Button } from '@affine/component';
import { useTranslation } from '@affine/i18n';
import { useState } from 'react';
import { EnableWorkspaceModal } from './EnableWorkspaceModal';

View File

@ -1,6 +1,6 @@
import { Button } from '@/ui/button';
import { Button } from '@affine/component';
import { FC, useRef, ChangeEvent, ReactElement } from 'react';
import { styled } from '@/styles';
import { styled } from '@affine/component';
import { useTranslation } from '@affine/i18n';
interface Props {
uploadType?: string;

View File

@ -5,7 +5,7 @@ import {
StyledTitle,
StyledTitleWrapper,
} from './styles';
import { Content } from '@/ui/layout';
import { Content } from '@affine/component';
import { useAppState } from '@/providers/app-state-provider';
import EditorModeSwitch from '@/components/editor-mode-switch';
import QuickSearchButton from './QuickSearchButton';

View File

@ -1,8 +1,8 @@
import React from 'react';
import { IconButton, IconButtonProps } from '@/ui/button';
import { IconButton, IconButtonProps } from '@affine/component';
import { ArrowDownIcon } from '@blocksuite/icons';
import { useModal } from '@/providers/GlobalModalProvider';
import { styled } from '@/styles';
import { styled } from '@affine/component';
const StyledIconButtonWithAnimate = styled(IconButton)(({ theme }) => {
return {

View File

@ -1,5 +1,5 @@
import { Menu, MenuItem } from '@/ui/menu';
import { IconButton } from '@/ui/button';
import { Menu, MenuItem } from '@affine/component';
import { IconButton } from '@affine/component';
import {
EdgelessIcon,
ExportIcon,
@ -15,7 +15,7 @@ import { useAppState } from '@/providers/app-state-provider';
import { usePageHelper } from '@/hooks/use-page-helper';
import { useConfirm } from '@/providers/ConfirmProvider';
import useCurrentPageMeta from '@/hooks/use-current-page-meta';
import { toast } from '@/ui/toast';
import { toast } from '@affine/component';
import { useTranslation } from '@affine/i18n';
const PopoverContent = () => {
const { editor } = useAppState();
@ -37,12 +37,12 @@ const PopoverContent = () => {
onClick={() => {
toggleFavoritePage(id);
toast(
favorite ? t('Removed from Favourites') : t('Added to Favourites')
favorite ? t('Removed from Favorites') : t('Added to Favorites')
);
}}
icon={favorite ? <FavouritedIcon /> : <FavouritesIcon />}
>
{favorite ? t('Remove from favourites') : t('Add to favourites')}
{favorite ? t('Remove from favorites') : t('Add to favorites')}
</MenuItem>
<MenuItem
icon={mode === 'page' ? <EdgelessIcon /> : <PaperIcon />}

View File

@ -1,7 +1,7 @@
import { CloudUnsyncedIcon } from '@blocksuite/icons';
import { useModal } from '@/providers/GlobalModalProvider';
import { useAppState } from '@/providers/app-state-provider';
import { IconButton } from '@/ui/button';
import { IconButton } from '@affine/component';
// Temporary solution to use this component, since the @blocksuite/icons has not been published yet
const DefaultSyncIcon = () => {

View File

@ -1,4 +1,4 @@
import { Button } from '@/ui/button';
import { Button } from '@affine/component';
import { usePageHelper } from '@/hooks/use-page-helper';
import { useAppState } from '@/providers/app-state-provider';
import { useConfirm } from '@/providers/ConfirmProvider';

View File

@ -1,4 +1,4 @@
import { displayFlex, keyframes, styled } from '@/styles';
import { displayFlex, keyframes, styled } from '@affine/component';
import { CSSProperties } from 'react';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore

View File

@ -1,4 +1,4 @@
import { displayFlex, styled } from '@/styles';
import { displayFlex, styled } from '@affine/component';
export const StyledHeaderContainer = styled.div<{ hasWarning: boolean }>(
({ hasWarning }) => {

View File

@ -6,8 +6,8 @@ import {
StyledTransformIcon,
} from './style';
import { CloseIcon, ContactIcon, HelpIcon, KeyboardIcon } from './Icons';
import { MuiGrow } from '@/ui/mui';
import { Tooltip } from '@/ui/tooltip';
import { MuiGrow } from '@affine/component';
import { Tooltip } from '@affine/component';
import { useTranslation } from '@affine/i18n';
import { useModal } from '@/providers/GlobalModalProvider';
import { useTheme } from '@/providers/ThemeProvider';

View File

@ -1,4 +1,4 @@
import { displayFlex, styled } from '@/styles';
import { displayFlex, styled } from '@affine/component';
export const StyledIsland = styled('div')(({ theme }) => {
return {

View File

@ -1,13 +1,13 @@
import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal';
import { Modal, ModalWrapper, ModalCloseButton } from '@affine/component';
import { StyledButtonWrapper, StyledTitle } from './styles';
import { Button } from '@/ui/button';
import { Content, FlexWrapper } from '@/ui/layout';
import { Button } from '@affine/component';
import { Content, FlexWrapper } from '@affine/component';
import Loading from '@/components/loading';
import { usePageHelper } from '@/hooks/use-page-helper';
import { useAppState } from '@/providers/app-state-provider';
import { useEffect, useState } from 'react';
import { useTranslation } from '@affine/i18n';
// import { Tooltip } from '@/ui/tooltip';
// import { Tooltip } from '@affine/component';
type ImportModalProps = {
open: boolean;
onClose: () => void;

View File

@ -1,4 +1,4 @@
import { styled } from '@/styles';
import { styled } from '@affine/component';
export const StyledTitle = styled.div(({ theme }) => {
return {

View File

@ -1,4 +1,4 @@
import { styled } from '@/styles';
import { styled } from '@affine/component';
import Loading from './Loading';
import { useTranslation } from '@affine/i18n';

View File

@ -1,4 +1,4 @@
import { styled } from '@/styles';
import { styled } from '@affine/component';
// Inspired by https://codepen.io/graphilla/pen/rNvBMYY
export const StyledLoadingWrapper = styled('div', {

View File

@ -1,6 +1,6 @@
import { positionAbsolute, styled } from '@/styles';
import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal';
import { Button } from '@/ui/button';
import { positionAbsolute, styled } from '@affine/component';
import { Modal, ModalWrapper, ModalCloseButton } from '@affine/component';
import { Button } from '@affine/component';
import { useAppState } from '@/providers/app-state-provider';
import { useTranslation } from '@affine/i18n';
import { GoogleIcon } from './GoogleIcon';

View File

@ -1,6 +1,6 @@
import { styled } from '@/styles';
import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal';
import { Button } from '@/ui/button';
import { styled } from '@affine/component';
import { Modal, ModalWrapper, ModalCloseButton } from '@affine/component';
import { Button } from '@affine/component';
import { Check, UnCheck } from './icon';
import { useState } from 'react';
import { useTranslation } from '@affine/i18n';

View File

@ -1,5 +1,5 @@
import React, { useState } from 'react';
import Modal, { ModalCloseButton, ModalWrapper } from '@/ui/modal';
import { Modal, ModalCloseButton, ModalWrapper } from '@affine/component';
import getIsMobile from '@/utils/get-is-mobile';
import { StyledButton, StyledContent, StyledTitle } from './styles';
import bg from './bg.png';

View File

@ -1,4 +1,4 @@
import { displayFlex, styled } from '@/styles';
import { displayFlex, styled } from '@affine/component';
export const StyledTitle = styled.div(() => {
return {

View File

@ -1,7 +1,7 @@
import localizedFormat from 'dayjs/plugin/localizedFormat';
import dayjs from 'dayjs';
import { PageMeta } from '@/providers/app-state-provider';
import { TableCell } from '@/ui/table';
import { TableCell } from '@affine/component';
import React from 'react';
dayjs.extend(localizedFormat);

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Empty } from '@/ui/empty';
import { Empty } from '@affine/component';
import { useTranslation } from '@affine/i18n';
export const PageListEmpty = (props: { listType?: string }) => {
const { listType } = props;
@ -12,7 +12,7 @@ export const PageListEmpty = (props: { listType?: string }) => {
sx={{ marginTop: '100px', marginBottom: '30px' }}
/>
{listType === 'all' && <p>{t('emptyAllPages')}</p>}
{listType === 'favorite' && <p>{t('emptyFavourite')}</p>}
{listType === 'favorite' && <p>{t('emptyFavorite')}</p>}
{listType === 'trash' && <p>{t('emptyTrash')}</p>}
</div>
);

View File

@ -1,8 +1,8 @@
import { useConfirm } from '@/providers/ConfirmProvider';
import { PageMeta } from '@/providers/app-state-provider';
import { Menu, MenuItem } from '@/ui/menu';
import { FlexWrapper } from '@/ui/layout';
import { IconButton } from '@/ui/button';
import { Menu, MenuItem } from '@affine/component';
import { FlexWrapper } from '@affine/component';
import { IconButton } from '@affine/component';
import {
MoreVerticalIcon,
RestoreIcon,
@ -12,7 +12,7 @@ import {
OpenInNewIcon,
TrashIcon,
} from '@blocksuite/icons';
import { toast } from '@/ui/toast';
import { toast } from '@affine/component';
import { usePageHelper } from '@/hooks/use-page-helper';
import { useTranslation } from '@affine/i18n';
export const OperationCell = ({ pageMeta }: { pageMeta: PageMeta }) => {
@ -27,12 +27,12 @@ export const OperationCell = ({ pageMeta }: { pageMeta: PageMeta }) => {
onClick={() => {
toggleFavoritePage(id);
toast(
favorite ? t('Removed from Favourites') : t('Added to Favourites')
favorite ? t('Removed from Favorites') : t('Added to Favorites')
);
}}
icon={favorite ? <FavouritedIcon /> : <FavouritesIcon />}
>
{favorite ? t('Remove from favourites') : t('Add to favourites')}
{favorite ? t('Remove from favorites') : t('Add to favorites')}
</MenuItem>
<MenuItem
onClick={() => {

View File

@ -11,17 +11,23 @@ import {
StyledTitleLink,
StyledTitleWrapper,
} from './styles';
import { Table, TableBody, TableCell, TableHead, TableRow } from '@/ui/table';
import {
Table,
TableBody,
TableCell,
TableHead,
TableRow,
} from '@affine/component';
import { OperationCell, TrashOperationCell } from './OperationCell';
import Empty from './Empty';
import { Content } from '@/ui/layout';
import { Content } from '@affine/component';
import React from 'react';
import DateCell from '@/components/page-list/DateCell';
import { IconButton } from '@/ui/button';
import { Tooltip } from '@/ui/tooltip';
import { IconButton } from '@affine/component';
import { Tooltip } from '@affine/component';
import { useRouter } from 'next/router';
import { useAppState } from '@/providers/app-state-provider';
import { toast } from '@/ui/toast';
import { toast } from '@affine/component';
import { usePageHelper } from '@/hooks/use-page-helper';
import { useTheme } from '@/providers/ThemeProvider';
import { useTranslation } from '@affine/i18n';
@ -35,7 +41,7 @@ const FavoriteTag = ({
const { t } = useTranslation();
return (
<Tooltip
content={favorite ? t('Favourited') : t('Favourite')}
content={favorite ? t('Favorited') : t('Favorite')}
placement="top-start"
>
<IconButton
@ -45,7 +51,7 @@ const FavoriteTag = ({
e.stopPropagation();
toggleFavoritePage(id);
toast(
favorite ? t('Removed from Favourites') : t('Added to Favourites')
favorite ? t('Removed from Favorites') : t('Added to Favorites')
);
}}
style={{
@ -54,7 +60,7 @@ const FavoriteTag = ({
className={favorite ? '' : 'favorite-button'}
>
{favorite ? (
<FavouritedIcon data-testid="favourited-icon" />
<FavouritedIcon data-testid="favorited-icon" />
) : (
<FavouritesIcon />
)}
@ -100,6 +106,7 @@ export const PageList = ({
{pageList.map((pageMeta, index) => {
return (
<StyledTableRow
data-testid="page-list-item"
key={`${pageMeta.id}-${index}`}
onClick={() => {
if (isPublic) {

View File

@ -1,5 +1,5 @@
import { displayFlex, styled } from '@/styles';
import { TableRow } from '@/ui/table';
import { displayFlex, styled } from '@affine/component';
import { TableRow } from '@affine/component';
export const StyledTableContainer = styled.div(() => {
return {

View File

@ -22,7 +22,7 @@ export const useSwitchToConfig = (
icon: AllPagesIcon,
},
{
title: t('Favourites'),
title: t('Favorites'),
href: currentWorkspaceId
? `/workspace/${currentWorkspaceId}/favorite`
: '',

View File

@ -1,4 +1,4 @@
import { Modal, ModalWrapper } from '@/ui/modal';
import { Modal, ModalWrapper } from '@affine/component';
import {
StyledContent,
StyledModalHeader,
@ -40,7 +40,6 @@ export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => {
// Add ‘⌘+K shortcut keys as switches
useEffect(() => {
if (router.pathname.startsWith('/404')) {
triggerQuickSearchModal(false);
return;
}
const down = (e: KeyboardEvent) => {
@ -59,7 +58,7 @@ export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => {
document.addEventListener('keydown', down, { capture: true });
return () =>
document.removeEventListener('keydown', down, { capture: true });
}, [open, router.pathname, triggerQuickSearchModal]);
}, [open, router, triggerQuickSearchModal]);
useEffect(() => {
if (router.pathname.startsWith('/public-workspace')) {
@ -68,6 +67,12 @@ export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => {
return setIsPublic(false);
}
}, [router]);
useEffect(() => {
if (router.pathname.startsWith('/404')) {
return onClose();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<Modal

View File

@ -1,4 +1,4 @@
import { displayFlex, styled } from '@/styles';
import { displayFlex, styled } from '@affine/component';
export const StyledContent = styled('div')(({ theme }) => {
return {
@ -13,7 +13,7 @@ export const StyledContent = styled('div')(({ theme }) => {
letterSpacing: '0.06em',
'[cmdk-group-heading]': {
margin: '5px 16px',
fontSize: theme.font.sm,
fontSize: theme.font.base,
fontWeight: '500',
},
'[aria-selected="true"]': {
@ -28,7 +28,7 @@ export const StyledJumpTo = styled('div')(({ theme }) => {
...displayFlex('center', 'start'),
flexDirection: 'column',
padding: '10px 10px 10px 0',
fontSize: theme.font.sm,
fontSize: theme.font.base,
strong: {
fontWeight: '500',
marginBottom: '10px',
@ -41,7 +41,7 @@ export const StyledNotFound = styled('div')(({ theme }) => {
...displayFlex('center', 'center'),
flexDirection: 'column',
padding: '5px 16px',
fontSize: theme.font.sm,
fontSize: theme.font.base,
span: {
width: '100%',
fontWeight: '500',
@ -75,7 +75,7 @@ export const StyledInputContent = styled('div')(({ theme }) => {
export const StyledShortcut = styled('div')(({ theme }) => {
return {
color: theme.colors.placeHolderColor,
fontSize: theme.font.sm,
fontSize: theme.font.base,
whiteSpace: 'nowrap',
};
});
@ -109,7 +109,7 @@ export const StyledModalDivider = styled('div')(({ theme }) => {
export const StyledModalFooter = styled('div')(({ theme }) => {
return {
fontSize: theme.font.sm,
fontSize: theme.font.base,
lineHeight: '22px',
marginBottom: '8px',
textAlign: 'center',
@ -127,7 +127,7 @@ export const StyledModalFooterContent = styled.button(({ theme }) => {
return {
width: '612px',
height: '32px',
fontSize: theme.font.sm,
fontSize: theme.font.base,
lineHeight: '22px',
textAlign: 'center',
...displayFlex('center', 'center'),
@ -144,7 +144,7 @@ export const StyledListItem = styled.button(({ theme }) => {
return {
width: '612px',
height: '32px',
fontSize: theme.font.sm,
fontSize: theme.font.base,
color: 'inherit',
paddingLeft: '12px',
borderRadius: '5px',

View File

@ -13,8 +13,8 @@ import {
useWindowsKeyboardShortcuts,
useWinMarkdownShortcuts,
} from '@/components/shortcuts-modal/config';
import { MuiSlide } from '@/ui/mui';
import { ModalCloseButton } from '@/ui/modal';
import { MuiSlide } from '@affine/component';
import { ModalCloseButton } from '@affine/component';
import { getUaHelper } from '@/utils';
import { useTranslation } from '@affine/i18n';
type ModalProps = {

View File

@ -1,4 +1,4 @@
import { displayFlex, styled } from '@/styles';
import { displayFlex, styled } from '@affine/component';
export const StyledShortcutsModal = styled.div(({ theme }) => ({
width: '288px',

View File

@ -1,4 +1,4 @@
import { styled } from '@/styles';
import { styled } from '@affine/component';
export const StyledPage = styled('div')(({ theme }) => {
return {

View File

@ -1,11 +1,11 @@
import { CloudInsyncIcon, LogOutIcon } from '@blocksuite/icons';
import { FlexWrapper } from '@/ui/layout';
import { FlexWrapper } from '@affine/component';
import { WorkspaceAvatar } from '@/components/workspace-avatar';
import { IconButton } from '@/ui/button';
import { IconButton } from '@affine/component';
import { useAppState } from '@/providers/app-state-provider';
import { StyledFooter, StyleUserInfo, StyledSignInButton } from './styles';
import { useTranslation } from '@affine/i18n';
import { Tooltip } from '@/ui/tooltip';
import { Tooltip } from '@affine/component';
export const Footer = ({
onLogin,
onLogout,

View File

@ -1,9 +1,9 @@
import { LOCALES } from '@affine/i18n';
import { styled } from '@/styles';
import { styled } from '@affine/component';
import { useTranslation } from '@affine/i18n';
import { ArrowDownIcon } from '@blocksuite/icons';
import { Button } from '@/ui/button';
import { Menu, MenuItem } from '@/ui/menu';
import { Button } from '@affine/component';
import { Menu, MenuItem } from '@affine/component';
const LanguageMenuContent = () => {
const { i18n } = useTranslation();

View File

@ -10,7 +10,7 @@ import { WorkspaceUnit } from '@affine/datacenter';
import { useAppState } from '@/providers/app-state-provider';
import { StyleWorkspaceInfo, StyleWorkspaceTitle, StyledCard } from './styles';
import { useTranslation } from '@affine/i18n';
import { FlexWrapper } from '@/ui/layout';
import { FlexWrapper } from '@affine/component';
const WorkspaceType = ({ workspaceData }: { workspaceData: WorkspaceUnit }) => {
const { user } = useAppState();

View File

@ -1,9 +1,9 @@
import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal';
import { FlexWrapper } from '@/ui/layout';
import { Modal, ModalWrapper, ModalCloseButton } from '@affine/component';
import { FlexWrapper } from '@affine/component';
import { useState } from 'react';
import { CreateWorkspaceModal } from '../create-workspace';
import { Tooltip } from '@/ui/tooltip';
import { Tooltip } from '@affine/component';
import { AddIcon, HelpCenterIcon } from '@blocksuite/icons';
@ -71,6 +71,7 @@ export const WorkspaceModal = ({ open, onClose }: WorkspaceModalProps) => {
<LanguageMenu />
<StyledSplitLine />
<ModalCloseButton
data-testid="close-workspace-modal"
onClick={() => {
onClose();
}}

View File

@ -1,5 +1,10 @@
import { displayFlex, displayInlineFlex, styled, textEllipsis } from '@/styles';
import { Button } from '@/ui/button';
import {
displayFlex,
displayInlineFlex,
styled,
textEllipsis,
} from '@affine/component';
import { Button } from '@affine/component';
export const StyledSplitLine = styled.div(({ theme }) => {
return {

View File

@ -1,7 +1,7 @@
import { WorkspaceUnit } from '@affine/datacenter';
import { useTranslation } from '@affine/i18n';
import { Wrapper } from '@/ui/layout';
import { Button } from '@/ui/button';
import { Wrapper } from '@affine/component';
import { Button } from '@affine/component';
export const ExportPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
const { t } = useTranslation();
console.log(workspace);

View File

@ -1,12 +1,12 @@
import { useState } from 'react';
import { Button } from '@/ui/button';
import Input from '@/ui/input';
import { toast } from '@/ui/toast';
import { Button } from '@affine/component';
import { Input } from '@affine/component';
import { toast } from '@affine/component';
import { WorkspaceUnit } from '@affine/datacenter';
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
import { useTranslation } from '@affine/i18n';
import { EnableWorkspaceButton } from '../enable-workspace';
import { Wrapper, Content, FlexWrapper } from '@/ui/layout';
import { Wrapper, Content, FlexWrapper } from '@affine/component';
export const PublishPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
const shareUrl = window.location.host + '/public-workspace/' + workspace.id;
const { publishWorkspace } = useWorkspaceHelper();

View File

@ -9,7 +9,7 @@ import { useTranslation, Trans } from '@affine/i18n';
import { WorkspaceUnitAvatar } from '@/components/workspace-avatar';
import { EnableWorkspaceButton } from '../enable-workspace';
import { useAppState } from '@/providers/app-state-provider';
import { FlexWrapper, Content, Wrapper } from '@/ui/layout';
import { FlexWrapper, Content, Wrapper } from '@affine/component';
// // FIXME: Temporary solution, since the @blocksuite/icons is broken
// const ActiveIcon = () => {

View File

@ -1,9 +1,9 @@
import { StyledInput, StyledProviderInfo, StyledAvatar } from './style';
import { StyledSettingKey, StyledRow } from '../style';
import { FlexWrapper, Content } from '@/ui/layout';
import { FlexWrapper, Content } from '@affine/component';
import { useState } from 'react';
import { Button } from '@/ui/button';
import { Button } from '@affine/component';
import { useAppState } from '@/providers/app-state-provider';
import { WorkspaceDelete } from './delete';
import { WorkspaceLeave } from './leave';
@ -18,7 +18,7 @@ import { Upload } from '@/components/file-upload';
export const GeneralPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
const [showDelete, setShowDelete] = useState<boolean>(false);
const [showLeave, setShowLeave] = useState<boolean>(false);
const [workspaceName, setWorkspaceName] = useState<string>(workspace.name);
const [workspaceName, setWorkspaceName] = useState<string>(workspace?.name);
const { currentWorkspace, isOwner } = useAppState();
const { updateWorkspace } = useWorkspaceHelper();
const { t } = useTranslation();
@ -33,7 +33,6 @@ export const GeneralPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
currentWorkspace &&
(await updateWorkspace({ avatarBlob: blob }, currentWorkspace));
};
if (!workspace) {
return null;
}
@ -42,22 +41,30 @@ export const GeneralPage = ({ workspace }: { workspace: WorkspaceUnit }) => {
<>
<StyledRow>
<StyledSettingKey>{t('Workspace Avatar')}</StyledSettingKey>
<StyledAvatar>
<Upload
accept="image/gif,image/jpeg,image/jpg,image/png,image/svg"
fileChange={fileChange}
>
<>
<div className="camera-icon">
<CameraIcon></CameraIcon>
</div>
<WorkspaceUnitAvatar
size={72}
name={workspace.name}
workspaceUnit={workspace}
/>
</>
</Upload>
<StyledAvatar disabled={!isOwner}>
{isOwner ? (
<Upload
accept="image/gif,image/jpeg,image/jpg,image/png,image/svg"
fileChange={fileChange}
>
<>
<div className="camera-icon">
<CameraIcon></CameraIcon>
</div>
<WorkspaceUnitAvatar
size={72}
name={workspace.name}
workspaceUnit={workspace}
/>
</>
</Upload>
) : (
<WorkspaceUnitAvatar
size={72}
name={workspace.name}
workspaceUnit={workspace}
/>
)}
</StyledAvatar>
</StyledRow>

View File

@ -1,6 +1,6 @@
import { useRouter } from 'next/router';
import Modal from '@/ui/modal';
import Input from '@/ui/input';
import { Modal } from '@affine/component';
import { Input } from '@affine/component';
import {
StyledModalHeader,
StyledTextContent,
@ -10,8 +10,8 @@ import {
StyledWorkspaceName,
} from './style';
import { useState } from 'react';
import { ModalCloseButton } from '@/ui/modal';
import { Button } from '@/ui/button';
import { ModalCloseButton } from '@affine/component';
import { Button } from '@affine/component';
import { WorkspaceUnit } from '@affine/datacenter';
import { Trans, useTranslation } from '@affine/i18n';

View File

@ -1,4 +1,4 @@
import { styled } from '@/styles';
import { styled } from '@affine/component';
export const StyledModalWrapper = styled('div')(({ theme }) => {
return {

View File

@ -1,12 +1,12 @@
import Modal from '@/ui/modal';
import { Modal } from '@affine/component';
import {
StyledModalHeader,
StyledTextContent,
StyledModalWrapper,
StyledButtonContent,
} from './style';
import { ModalCloseButton } from '@/ui/modal';
import { Button } from '@/ui/button';
import { ModalCloseButton } from '@affine/component';
import { Button } from '@affine/component';
import { useTranslation } from '@affine/i18n';
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
// import { getDataCenter } from '@affine/datacenter';

View File

@ -1,4 +1,4 @@
import { styled } from '@/styles';
import { styled } from '@affine/component';
export const StyledModalWrapper = styled('div')(({ theme }) => {
return {

View File

@ -1,5 +1,5 @@
import { styled } from '@/styles';
import Input from '@/ui/input';
import { styled } from '@affine/component';
import { Input } from '@affine/component';
export const StyledInput = styled(Input)(({ theme }) => {
return {
@ -19,26 +19,29 @@ export const StyledProviderInfo = styled('p')(({ theme }) => {
};
});
export const StyledAvatar = styled('div')(() => {
return {
position: 'relative',
cursor: 'pointer',
':hover': {
'.camera-icon': {
display: 'block',
export const StyledAvatar = styled('div')(
({ disabled }: { disabled: boolean }) => {
return {
position: 'relative',
marginRight: '20px',
cursor: disabled ? 'default' : 'pointer',
':hover': {
'.camera-icon': {
display: 'block',
},
},
},
'.camera-icon': {
position: 'absolute',
display: 'none',
width: '100%',
height: '100%',
borderRadius: '50%',
backgroundColor: 'rgba(60, 61, 63, 0.5)',
top: 0,
left: 0,
textAlign: 'center',
lineHeight: '72px',
},
};
});
'.camera-icon': {
position: 'absolute',
display: 'none',
width: '100%',
height: '100%',
borderRadius: '50%',
backgroundColor: 'rgba(60, 61, 63, 0.5)',
top: 0,
left: 0,
textAlign: 'center',
lineHeight: '72px',
},
};
}
);

View File

@ -1,10 +1,10 @@
import { EmailIcon } from '@blocksuite/icons';
import { styled } from '@/styles';
import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal';
import { Button } from '@/ui/button';
import Input from '@/ui/input';
import { styled } from '@affine/component';
import { Modal, ModalWrapper, ModalCloseButton } from '@affine/component';
import { Button } from '@affine/component';
import { Input } from '@affine/component';
import { useState } from 'react';
import { MuiAvatar } from '@/ui/mui';
import { MuiAvatar } from '@affine/component';
import useMembers from '@/hooks/use-members';
import { User } from '@affine/datacenter';
import { useTranslation } from '@affine/i18n';

View File

@ -12,19 +12,19 @@ import {
StyledMoreVerticalButton,
StyledMemberContainer,
} from './style';
import { Wrapper } from '@/ui/layout';
import { Wrapper } from '@affine/component';
import { MoreVerticalIcon, EmailIcon, TrashIcon } from '@blocksuite/icons';
import { useState } from 'react';
import { Button, IconButton } from '@/ui/button';
import { Button, IconButton } from '@affine/component';
import { InviteMemberModal } from './InviteMemberModal';
import { Menu, MenuItem } from '@/ui/menu';
import { Empty } from '@/ui/empty';
import { Menu, MenuItem } from '@affine/component';
import { Empty } from '@affine/component';
import { WorkspaceUnit } from '@affine/datacenter';
import { useConfirm } from '@/providers/ConfirmProvider';
import { toast } from '@/ui/toast';
import { toast } from '@affine/component';
import useMembers from '@/hooks/use-members';
import Loading from '@/components/loading';
import { FlexWrapper } from '@/ui/layout';
import { FlexWrapper } from '@affine/component';
import { useTranslation } from '@affine/i18n';
import { EnableWorkspaceButton } from '@/components/enable-workspace';

View File

@ -1,5 +1,5 @@
import { styled } from '@/styles';
import { MuiAvatar } from '@/ui/mui';
import { styled } from '@affine/component';
import { MuiAvatar } from '@affine/component';
export const StyledMemberTitleContainer = styled('li')(() => {
return {

View File

@ -1,5 +1,5 @@
import { styled } from '@/styles';
import { FlexWrapper } from '@/ui/layout';
import { styled } from '@affine/component';
import { FlexWrapper } from '@affine/component';
export const StyledSettingContainer = styled('div')(() => {
return {
display: 'flex',

View File

@ -1,5 +1,5 @@
import type { ReactNode } from 'react';
import { styled } from '@/styles';
import { styled } from '@affine/component';
import { WorkspaceItemWrapper, WorkspaceItemContent } from './styles';
interface ListItemProps {

View File

@ -1,5 +1,5 @@
import { useModal } from '@/providers/GlobalModalProvider';
import { styled } from '@/styles';
import { styled } from '@affine/component';
import { AffineIcon } from '../../icons/Icons';
import {
WorkspaceItemAvatar,

View File

@ -1,5 +1,5 @@
import { MuiAvatar } from '@/ui/mui';
import { styled } from '@/styles';
import { MuiAvatar } from '@affine/component';
import { styled } from '@affine/component';
export const WorkspaceItemWrapper = styled('div')(({ theme }) => ({
display: 'flex',

View File

@ -1,6 +1,6 @@
import { MuiAvatar } from '@/ui/mui';
import { styled } from '@/styles';
import { StyledPopperContainer } from '@/ui/shared/Container';
import { MuiAvatar } from '@affine/component';
import { styled } from '@affine/component';
import { StyledPopperContainer } from '@affine/component';
export const SelectorWrapper = styled('div')({
width: '100%',

View File

@ -21,11 +21,11 @@ import {
SettingsIcon,
} from '@blocksuite/icons';
import Link from 'next/link';
import { MuiCollapse } from '@/ui/mui';
import { Tooltip } from '@/ui/tooltip';
import { MuiCollapse } from '@affine/component';
import { Tooltip } from '@affine/component';
import { useModal } from '@/providers/GlobalModalProvider';
import { useAppState } from '@/providers/app-state-provider';
import { IconButton } from '@/ui/button';
import { IconButton } from '@affine/component';
import useLocalStorage from '@/hooks/use-local-storage';
import { usePageHelper } from '@/hooks/use-page-helper';
import { useTranslation } from '@affine/i18n';
@ -125,13 +125,14 @@ export const WorkSpaceSliderBar = () => {
</StyledListItem>
<Link href={{ pathname: paths.all }}>
<StyledListItem active={router.asPath === paths.all}>
<AllPagesIcon /> <span>{t('All pages')}</span>
<AllPagesIcon />
<span data-testid="all-pages">{t('All pages')}</span>
</StyledListItem>
</Link>
<StyledListItem active={router.asPath === paths.favorite}>
<StyledLink href={{ pathname: paths.favorite }}>
<FavouritesIcon />
{t('Favourites')}
{t('Favorites')}
</StyledLink>
<IconButton
darker={true}

View File

@ -1,4 +1,4 @@
import { displayFlex, styled, textEllipsis } from '@/styles';
import { displayFlex, styled, textEllipsis } from '@affine/component';
import Link from 'next/link';
export const StyledSliderBar = styled.div<{ show: boolean }>(

View File

@ -1,5 +1,13 @@
import NotfoundPage from '@/components/404';
import Head from 'next/head';
export default function Custom404() {
return <NotfoundPage></NotfoundPage>;
return (
<>
<Head>
<title>404 - AFFiNE</title>
</Head>
<NotfoundPage></NotfoundPage>
</>
);
}

View File

@ -50,6 +50,7 @@ const App = ({ Component, pageProps }: AppPropsWithLayout) => {
sizes="180x180"
href="/icons/apple-touch-icon.png"
/>
<title>AFFiNE</title>
</Head>
<Logger />
<ProviderComposer

View File

@ -1,67 +0,0 @@
import { displayFlex, styled } from '@/styles';
import Loading from '@/components/loading';
import Modal from '@/ui/modal';
import { useState } from 'react';
import { Button } from '@/ui/button';
import { FavouritedIcon } from '@blocksuite/icons';
import { toast } from '@/ui/toast';
export const StyledHeader = styled('div')({
height: '60px',
width: '100vw',
...displayFlex('space-between', 'center'),
position: 'relative',
padding: '0 22px',
borderBottom: '1px solid #e5e5e5',
});
const Affine = () => {
const [show, setShow] = useState(false);
return (
<>
<StyledHeader>
<button
onClick={() => {
setShow(true);
}}
>
click me!
</button>
</StyledHeader>
<Modal
open={show}
onClose={() => {
setShow(false);
}}
>
<div>hi</div>
</Modal>
<Loading />
<Button
icon={<FavouritedIcon />}
onClick={() => {
toast('hello, world!!');
}}
>
click me!
</Button>
<Button icon={<FavouritedIcon />} type={'primary'}>
click me!
</Button>
<Button icon={<FavouritedIcon />} type={'light'}>
click me!
</Button>
<Button icon={<FavouritedIcon />} type={'warning'}>
click me!
</Button>
<Button icon={<FavouritedIcon />} type={'danger'}>
click me!
</Button>
<Button icon={<FavouritedIcon />}></Button>
<Button icon={<FavouritedIcon />} shape="round"></Button>
<Button loading={true}></Button>
<Button loading={true} type="primary"></Button>
</>
);
};
export default Affine;

View File

@ -1,20 +0,0 @@
// for dynamic route get workspace id maybe path will change
import { useRouter } from 'next/router';
const Post = () => {
const router = useRouter();
const { workspace_id } = router.query;
return (
<p
style={{
height: 'calc(100vh)',
color: 'gray',
}}
>
workspace_id: {workspace_id},
</p>
);
};
export default Post;

View File

@ -1,6 +1,6 @@
import { useWorkspaceHelper } from '@/hooks/use-workspace-helper';
import { styled } from '@/styles';
import { Empty } from '@/ui/empty';
import { styled } from '@affine/component';
import { Empty } from '@affine/component';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import { PageLoading } from '@/components/loading';

View File

@ -1,123 +0,0 @@
import { ReactElement } from 'react';
import WorkspaceLayout from '@/components/workspace-layout';
import exampleMarkdown1 from '@/templates/Welcome-to-the-AFFiNE-Alpha.md';
import exampleMarkdown2 from '@/templates/AFFiNE-Docs.md';
import { usePageHelper } from '@/hooks/use-page-helper';
import { useAppState } from '@/providers/app-state-provider';
import { Button } from '@/ui/button';
interface Template {
name: string;
source: string;
}
const TemplateItemContainer = styled('div')(() => {
return {
color: 'blue',
padding: '10px 15px',
borderBottom: '1px solid #eee',
cursor: 'pointer',
'&:hover': {
background: '#eee',
},
};
});
import { styled } from '@/styles';
const TEMPLATES: Template[] = [
{
name: 'Welcome-to-the-AFFiNE-Alpha.md',
source: exampleMarkdown1,
},
{
name: 'AFFiNE-Docs.md',
source: exampleMarkdown2,
},
];
const All = () => {
const { openPage, createPage } = usePageHelper();
const { currentWorkspace } = useAppState();
const _applyTemplate = function (pageId: string, template: Template) {
const page = currentWorkspace?.blocksuiteWorkspace?.getPage(pageId);
const title = template.name;
if (page) {
currentWorkspace?.blocksuiteWorkspace?.setPageMeta(page.id, { title });
if (page && page.root === null) {
setTimeout(async () => {
const editor = document.querySelector('editor-container');
if (editor) {
page.addBlock({ flavour: 'affine:surface' }, null);
const frameId = page.addBlock({ flavour: 'affine:frame' }, pageId);
// TODO blocksuite should offer a method to import markdown from store
await editor.clipboard.importMarkdown(
template.source,
`${frameId}`
);
page.resetHistory();
editor.requestUpdate();
}
}, 300);
}
}
};
const _handleAppleTemplate = async function (template: Template) {
const pageId = await createPage();
if (pageId) {
openPage(pageId);
_applyTemplate(pageId, template);
}
};
const _handleAppleTemplateFromFilePicker = async () => {
if (!window.showOpenFilePicker) {
return;
}
const arrFileHandle = await window.showOpenFilePicker({
types: [
{
accept: {
'image/*': ['.md'],
},
},
],
multiple: false,
});
for (const fileHandle of arrFileHandle) {
const file = await fileHandle.getFile();
const text = await file.text();
_handleAppleTemplate({
name: file.name,
source: text,
});
}
};
return (
<div style={{ padding: '50px' }}>
<div>
<h2>Templates</h2>
{TEMPLATES.map(template => {
return (
<TemplateItemContainer
key={template.name}
onClick={() => _handleAppleTemplate(template)}
>
{template.name}
<Button style={{ marginLeft: '20px' }}> Apply Template</Button>
</TemplateItemContainer>
);
})}
<br />
<h2>Import Markdown</h2>
<Button onClick={() => _handleAppleTemplateFromFilePicker()}>
<a style={{ marginLeft: '20px' }}>Select File To Import Markdown</a>
</Button>
</div>
</div>
);
};
All.getLayout = function getLayout(page: ReactElement) {
return <WorkspaceLayout>{page}</WorkspaceLayout>;
};
export default All;

View File

@ -1,13 +1,13 @@
import { ReactElement, useEffect, useState } from 'react';
import { useAppState } from '@/providers/app-state-provider';
import type { NextPageWithLayout } from '../..//_app';
import { displayFlex, styled } from '@/styles';
import { displayFlex, styled } from '@affine/component';
import dynamic from 'next/dynamic';
import { useRouter } from 'next/router';
import { Page as PageStore, Workspace } from '@blocksuite/store';
import { PageLoading } from '@/components/loading';
import { Breadcrumbs } from '@/ui/breadcrumbs';
import { IconButton } from '@/ui/button';
import { Breadcrumbs } from '@affine/component';
import { IconButton } from '@affine/component';
import NextLink from 'next/link';
import { PaperIcon, SearchIcon } from '@blocksuite/icons';
import { WorkspaceUnitAvatar } from '@/components/workspace-avatar';

View File

@ -8,7 +8,7 @@ import {
StyledBreadcrumbs,
SearchButton,
} from './[pageId]';
import { Breadcrumbs } from '@/ui/breadcrumbs';
import { Breadcrumbs } from '@affine/component';
import { WorkspaceUnitAvatar } from '@/components/workspace-avatar';
import { SearchIcon } from '@blocksuite/icons';
import { useModal } from '@/providers/GlobalModalProvider';

View File

@ -1,18 +0,0 @@
import { useEffect } from 'react';
import { getDataCenter } from '@affine/datacenter';
/**
* testing only when development
*/
const Page = () => {
useEffect(() => {
getDataCenter().then(dc => {
// @ts-expect-error global variable
window.dc = dc;
});
}, []);
return <div>Testing only</div>;
};
export default Page;

View File

@ -15,6 +15,8 @@ import { useRouter } from 'next/router';
import { usePageHelper } from '@/hooks/use-page-helper';
import dynamic from 'next/dynamic';
import { EditorContainer } from '@blocksuite/editor';
import Head from 'next/head';
import { useTranslation } from '@affine/i18n';
const DynamicBlocksuite = dynamic(() => import('@/components/editor'), {
ssr: false,
});
@ -25,8 +27,12 @@ const Page: NextPageWithLayout = () => {
(editor: EditorContainer) => setEditor.current(editor),
[setEditor]
);
const { t } = useTranslation();
return (
<>
<Head>
<title>{currentPage?.meta?.title || t('Untitled')} - AFFiNE</title>
</Head>
<EditorHeader />
<MobileModal />

View File

@ -5,6 +5,8 @@ import { ReactElement } from 'react';
import WorkspaceLayout from '@/components/workspace-layout';
import { useTranslation } from '@affine/i18n';
import { PageMeta, useAppState } from '@/providers/app-state-provider';
import Head from 'next/head';
const All = () => {
const { currentWorkspace } = useAppState();
const pageList = (currentWorkspace?.blocksuiteWorkspace?.meta.pageMetas ||
@ -12,6 +14,9 @@ const All = () => {
const { t } = useTranslation();
return (
<>
<Head>
<title>{t('All pages')} - AFFiNE</title>
</Head>
<PageListHeader icon={<AllPagesIcon />}>{t('All pages')}</PageListHeader>
<PageList
pageList={pageList.filter(p => !p.trash)}

View File

@ -5,13 +5,17 @@ import { ReactElement } from 'react';
import WorkspaceLayout from '@/components/workspace-layout';
import { useTranslation } from '@affine/i18n';
import { useAppState } from '@/providers/app-state-provider';
import Head from 'next/head';
export const Favorite = () => {
const { pageList } = useAppState();
const { t } = useTranslation();
return (
<>
<Head>
<title>{t('Favorites')} - AFFiNE</title>
</Head>
<PageListHeader icon={<FavouritesIcon />}>
{t('Favourites')}
{t('Favorites')}
</PageListHeader>
<PageList
pageList={pageList.filter(p => p.favorite && !p.trash)}

View File

@ -1,8 +1,8 @@
import { styled } from '@/styles';
import { styled } from '@affine/component';
import { ReactElement, ReactNode } from 'react';
import WorkspaceLayout from '@/components/workspace-layout';
import { Button } from '@/ui/button';
import { Button } from '@affine/component';
export const FeatureCardDiv = styled('section')({
width: '800px',

View File

@ -19,6 +19,7 @@ import WorkspaceLayout from '@/components/workspace-layout';
import { WorkspaceUnit } from '@affine/datacenter';
import { useTranslation } from '@affine/i18n';
import { PageListHeader } from '@/components/header';
import Head from 'next/head';
const useTabMap = () => {
const { t } = useTranslation();
@ -79,6 +80,9 @@ const WorkspaceSetting = () => {
useTabMap();
return (
<>
<Head>
<title>{t('Settings')} - AFFiNE</title>
</Head>
<StyledSettingContainer>
<PageListHeader icon={<SettingsIcon />}>{t('Settings')}</PageListHeader>
<StyledSettingSidebar>

View File

@ -5,11 +5,15 @@ import { ReactElement } from 'react';
import WorkspaceLayout from '@/components/workspace-layout';
import { useTranslation } from '@affine/i18n';
import { useAppState } from '@/providers/app-state-provider';
import Head from 'next/head';
export const Trash = () => {
const { pageList } = useAppState();
const { t } = useTranslation();
return (
<>
<Head>
<title>{t('Trash')} - AFFiNE</title>
</Head>
<PageListHeader icon={<TrashIcon />}>{t('Trash')}</PageListHeader>
<PageList
pageList={pageList.filter(p => p.trash)}

View File

@ -1,6 +1,6 @@
import { createContext, useContext, useState, ReactNode } from 'react';
import type { PropsWithChildren } from 'react';
import { Confirm, ConfirmProps } from '@/ui/confirm';
import { Confirm, ConfirmProps } from '@affine/component';
type ConfirmContextValue = {
confirm: (props: ConfirmProps) => Promise<boolean>;

View File

@ -1,9 +1,5 @@
import { createContext, useContext, useEffect, useState } from 'react';
import {
ThemeProvider as EmotionThemeProvider,
Global,
css,
} from '@emotion/react';
import { Global, css } from '@emotion/react';
import {
ThemeProvider as MuiThemeProvider,
createTheme as MuiCreateTheme,
@ -14,13 +10,14 @@ import {
ThemeMode,
ThemeProviderProps,
ThemeProviderValue,
} from '@/styles/types';
} from '@affine/component';
import {
getLightTheme,
getDarkTheme,
globalThemeVariables,
} from '@/styles/theme';
import { SystemThemeHelper, localStorageThemeHelper } from '@/styles/utils';
ThemeProvider as ComponentThemeProvider,
} from '@affine/component';
import { SystemThemeHelper, localStorageThemeHelper } from '@affine/component';
import useCurrentPageMeta from '@/hooks/use-current-page-meta';
export const ThemeContext = createContext<ThemeProviderValue>({
@ -106,9 +103,9 @@ export const ThemeProvider = ({
}
`}
/>
<EmotionThemeProvider theme={themeStyle}>
<ComponentThemeProvider theme={themeStyle}>
{children}
</EmotionThemeProvider>
</ComponentThemeProvider>
</ThemeContext.Provider>
</MuiThemeProvider>
);

View File

@ -1,3 +0,0 @@
import emotionStyled from '@emotion/styled';
export { css, keyframes } from '@emotion/react';
export const styled = emotionStyled;

View File

@ -22,7 +22,7 @@ docker run -it --name affine -d -v [YOUR_PATH]:/app/data -p 3000:3000 ghcr.io/to
### In this release, you can now:
- Manage your pages from the collapsible **sidebar**, which allows you to add **favourites** and restore deleted files from the **trash**
- Manage your pages from the collapsible **sidebar**, which allows you to add **favorites** and restore deleted files from the **trash**
- Search through all your content with the quick search - activate with `Ctrl/⌘ + K`
- A friendly Reminder:
- In the case of unselected text, `Ctrl/⌘ + K` activates quick search;
@ -53,7 +53,7 @@ console.log('Hello world');
[] Send a page to trash
[] Favourite a page
[] Favorite a page
**Have an enjoyable editing experience !!!** 😃

View File

@ -1,4 +1,5 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"target": "ES2020",
"lib": ["dom", "dom.iterable", "esnext"],
@ -17,7 +18,8 @@
"experimentalDecorators": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
"@/*": ["src/*"],
"@affine/component": ["../component/src/index"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],

View File

@ -0,0 +1,44 @@
const path = require('node:path');
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
staticDirs: ['../../app/public'],
addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
core: {
builder: '@storybook/builder-webpack5',
},
webpackFinal: config => {
const transpile = config.module.rules.find(x =>
x.test.toString().includes('tsx')
).use;
transpile.push({
loader: require.resolve('swc-loader'),
options: {
parseMap: true,
jsc: {
parser: {
syntax: 'typescript',
dynamicImport: true,
tsx: true,
},
target: 'es2022',
transform: {
react: {
runtime: 'automatic',
},
},
},
},
});
config.resolve.alias = {
...config.resolve.alias,
'@': path.resolve(__dirname, '..', 'src'),
'@affine/i18n': path.resolve(__dirname, '..', '..', 'i18n', 'src'),
};
return config;
},
reactOptions: {
fastRefresh: true,
},
};

View File

@ -0,0 +1,9 @@
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
};

View File

@ -0,0 +1,36 @@
{
"name": "@affine/component",
"private": true,
"version": "0.3.1",
"scripts": {
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
},
"dependencies": {
"@affine/i18n": "workspace:*",
"@blocksuite/editor": "0.4.0-alpha.2",
"@blocksuite/icons": "^2.0.2",
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@mui/base": "=5.0.0-alpha.101",
"@mui/icons-material": "=5.10.9",
"@mui/material": "=5.8.6",
"lit": "^2.6.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@storybook/addon-actions": "^6.5.16",
"@storybook/addon-essentials": "^6.5.16",
"@storybook/addon-links": "^6.5.16",
"@storybook/builder-webpack5": "^6.5.16",
"@storybook/manager-webpack5": "^6.5.16",
"@storybook/react": "^6.5.16",
"@types/react": "^18.0.27",
"@types/react-dom": "18.0.10",
"swc": "^1.0.11",
"swc-loader": "^0.2.3",
"typescript": "^4.9.5",
"webpack": "^5.75.0"
}
}

View File

@ -0,0 +1,16 @@
export * from './ui/breadcrumbs';
export * from './ui/button';
export * from './ui/confirm';
export * from './ui/divider';
export * from './ui/empty';
export * from './ui/input';
export * from './ui/layout';
export * from './ui/menu';
export * from './ui/modal';
export * from './ui/popper';
export * from './ui/shared/Container';
export * from './ui/table';
export * from './ui/toast';
export * from './ui/tooltip';
export * from './ui/mui';
export * from './styles';

View File

@ -0,0 +1,19 @@
import React, { useMemo } from 'react';
import { Meta, Story } from '@storybook/react';
import { Breadcrumbs, getLightTheme, ThemeProvider } from '..';
export default {
title: 'AFFiNE/Breadcrumbs',
component: Breadcrumbs,
} as Meta;
const Template: Story = args => (
<ThemeProvider theme={useMemo(() => getLightTheme('page'), [])}>
<Breadcrumbs {...args} />
</ThemeProvider>
);
export const Primary = Template.bind({});
Primary.args = {
children: [<span>1</span>, <span>2</span>, <span>3</span>],
};

View File

@ -0,0 +1,20 @@
import React, { useMemo } from 'react';
import { Meta, Story } from '@storybook/react';
import { Button, getLightTheme, ThemeProvider } from '..';
export default {
title: 'AFFiNE/Button',
component: Button,
} as Meta;
const Template: Story = args => (
<ThemeProvider theme={useMemo(() => getLightTheme('page'), [])}>
<Button {...args} />
</ThemeProvider>
);
export const Primary = Template.bind({});
Primary.args = {
type: 'primary',
children: 'This is a button',
};

View File

@ -60,8 +60,8 @@ export const absoluteCenter = ({
position: 'absolute',
left: left ? left : horizontal ? '50%' : 'auto',
top: top ? top : vertical ? '50%' : 'auto',
right: right ? right : horizontal ? 'auto' : 'auto',
bottom: bottom ? bottom : vertical ? 'auto' : 'auto',
right: right ? right : 'auto',
bottom: bottom ? bottom : 'auto',
transform: `translate(${horizontal ? '-50%' : '0'}, ${
vertical ? '-50%' : '0'
})`,
@ -92,8 +92,8 @@ export const fixedCenter = ({
position: 'fixed',
left: left ? left : horizontal ? '50%' : 'auto',
top: top ? top : vertical ? '50%' : 'auto',
right: right ? right : horizontal ? 'auto' : 'auto',
bottom: bottom ? bottom : vertical ? 'auto' : 'auto',
right: right ? right : 'auto',
bottom: bottom ? bottom : 'auto',
transform: `translate(${horizontal ? '-50%' : '0'}, ${
vertical ? '-50%' : '0'
})`,

Some files were not shown because too many files have changed in this diff Show More