diff --git a/.all-contributorsrc b/.all-contributorsrc index b08412fb4..74bef4b2b 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -436,6 +436,33 @@ "contributions": [ "code" ] + }, + { + "login": "hezhizhen", + "name": "Zhizhen He", + "avatar_url": "https://avatars.githubusercontent.com/u/7611700?v=4", + "profile": "https://t.me/littlepoint", + "contributions": [ + "code" + ] + }, + { + "login": "AkaraChen", + "name": "AkaraChen", + "avatar_url": "https://avatars.githubusercontent.com/u/85140972?v=4", + "profile": "https://akr.moe/", + "contributions": [ + "code" + ] + }, + { + "login": "suyanhanx", + "name": "Suyan", + "avatar_url": "https://avatars.githubusercontent.com/u/24221472?v=4", + "profile": "https://github.com/suyanhanx", + "contributions": [ + "code" + ] } ] } diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 4e1862166..000000000 --- a/.eslintignore +++ /dev/null @@ -1,7 +0,0 @@ -**/webpack.config.js -**/scripts/*.js -**/node_modules/** -.github/** -**/__tests__/** -**/tests/** - diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..f3c1752f5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,7 @@ + diff --git a/.github/ISSUE_TEMPLATE/.gitkeep b/.github/ISSUE_TEMPLATE/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/.github/ISSUE_TEMPLATE/bug-report-alpha.yml b/.github/ISSUE_TEMPLATE/bug-report-alpha.yml deleted file mode 100644 index 599cf75af..000000000 --- a/.github/ISSUE_TEMPLATE/bug-report-alpha.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: 🐛 Bug report (Alpha) -description: "Report a reproducible bug or regression for https://pathfinder.affine.pro" -title: "[bug]: " -labels: ["bug", "alpha"] -body: - - type: markdown - attributes: - value: Thanks for taking the time to fill out this bug report! - - type: input - id: description - attributes: - label: Describe the bug - placeholder: A clear and concise description of what the bug is. - - type: textarea - id: reproduce - attributes: - label: To Reproduce - placeholder: "Steps to reproduce the behavior\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error" - validations: - required: true - - type: textarea - id: screenshots - attributes: - label: Screenshots - placeholder: If applicable, add screenshots to help explain your problem. - - type: textarea - id: expected - attributes: - label: Expected behavior - placeholder: A clear and concise description of what you expected to happen. - - type: input - id: platform - attributes: - label: Platform - placeholder: e.g. MacOS, Windows10... - - type: input - id: browser - attributes: - label: Browser - placeholder: e.g. Chrome, Safari - - type: textarea - id: additional - attributes: - label: Additional context - placeholder: Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/bug-report-pre-alpha.yml b/.github/ISSUE_TEMPLATE/bug-report-pre-alpha.yml deleted file mode 100644 index a75e3db6c..000000000 --- a/.github/ISSUE_TEMPLATE/bug-report-pre-alpha.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: 🐛 Bug report (Pre-Alpha) -description: "Report a reproducible bug or regression for https://livedemo.affine.pro" -title: "[bug]: " -labels: ["bug", "pre-alpha"] -body: - - type: markdown - attributes: - value: Thanks for taking the time to fill out this bug report! - - type: input - id: description - attributes: - label: Describe the bug - placeholder: A clear and concise description of what the bug is. - - type: textarea - id: reproduce - attributes: - label: To Reproduce - placeholder: "Steps to reproduce the behavior\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error" - validations: - required: true - - type: textarea - id: screenshots - attributes: - label: Screenshots - placeholder: If applicable, add screenshots to help explain your problem. - - type: textarea - id: expected - attributes: - label: Expected behavior - placeholder: A clear and concise description of what you expected to happen. - - type: input - id: platform - attributes: - label: Platform - placeholder: e.g. MacOS, Windows10... - - type: input - id: browser - attributes: - label: Browser - placeholder: e.g. Chrome, Safari - - type: textarea - id: additional - attributes: - label: Additional context - placeholder: Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index a7505c86e..000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,11 +0,0 @@ -blank_issues_enabled: true -contact_links: - - name: 💭 Questions and Help - Reddit - url: https://www.reddit.com/r/Affine/ - about: Please ask and answer questions here. - - name: 💬 Questions and Help - Telegram - url: https://t.me/affineworkos - about: Please ask and answer questions here. - - name: 🗯 Questions and Help - Discord - url: https://discord.gg/yz6tGVsf5p - about: Please ask and answer questions here. diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml deleted file mode 100644 index 54c1caa4f..000000000 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: ✨ Feature request -description: An idea or request for new functionality -title: "[feature]: " -labels: ["enhancement"] -body: - - type: markdown - attributes: - value: Thanks for taking the time to fill out this feature request! - - type: textarea - id: description - attributes: - label: 1~3 main use cases of the proposed feature - description: e.g. As a ..., I have many tasks scattered across documents, and I want to have a unified entry to view these tasks. - placeholder: e.g. As a ... - validations: - required: true - - type: textarea - id: solution - attributes: - label: Ideas for solution - placeholder: e.g. A task view can be added to view all tasks. - - type: input - id: userType - attributes: - label: what types of users can benefit from using your proposed feature - placeholder: busy student - - type: textarea - id: additional - attributes: - label: Additional context - placeholder: Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/improvement-request.yml b/.github/ISSUE_TEMPLATE/improvement-request.yml deleted file mode 100644 index 769e67913..000000000 --- a/.github/ISSUE_TEMPLATE/improvement-request.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: 🪄 Improvement request -description: An improvement to existing functionality -title: "[improvement]: " -labels: ["improvement"] -body: - - type: markdown - attributes: - value: Thanks for taking the time to fill out this improvement request! - - type: textarea - id: description - attributes: - label: 1~3 main use cases of the proposed improvement - description: e.g. As a ..., I have many tasks scattered across documents, and I want to have a unified entry to view these tasks. - placeholder: e.g. As a ... - validations: - required: true - - type: textarea - id: solution - attributes: - label: Ideas for solution - placeholder: e.g. A task view can be added to view all tasks. - - type: input - id: userType - attributes: - label: what types of users can benefit from using your proposed improvement - placeholder: busy student - - type: textarea - id: additional - attributes: - label: Additional context - placeholder: Add any other context or screenshots about the improvement request here. diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index 3bb05d364..000000000 --- a/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: I have a question -about: Feel free to ask us your questions! -title: '[Question]' -labels: '' -assignees: '' ---- diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6d0c26263..a41c84f4c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -67,18 +67,16 @@ jobs: lint: name: Lint and E2E Test - runs-on: ubuntu-latest + runs-on: self-hosted environment: development needs: build steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: pnpm/action-setup@v2 with: version: 'latest' - - - name: Use Node.js - uses: actions/setup-node@v2 + - uses: actions/setup-node@v3 with: node-version: 18.x cache: 'pnpm' @@ -96,8 +94,6 @@ jobs: - name: Install dependencies run: pnpm install - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_GITHUB_AUTH_TOKEN }} - name: Download artifact uses: actions/download-artifact@v3 @@ -105,20 +101,40 @@ jobs: name: artifact path: packages/app/.next/ - - name: Lint & E2E Test + - name: Lint & E2E Test & Unit Test run: | pnpm lint --max-warnings=0 - PLAYWRIGHT_BROWSERS_PATH=0 npx playwright install chromium - PLAYWRIGHT_BROWSERS_PATH=0 pnpm test - PLAYWRIGHT_BROWSERS_PATH=0 pnpm test:dc - env: - NEXT_PUBLIC_FIREBASE_API_KEY: ${{ secrets.NEXT_PUBLIC_FIREBASE_API_KEY }} - NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN: ${{ secrets.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN }} - NEXT_PUBLIC_FIREBASE_PROJECT_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_PROJECT_ID }} - NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET: ${{ secrets.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET }} - NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID }} - NEXT_PUBLIC_FIREBASE_APP_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_APP_ID }} - NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID }} + npx playwright install chromium + pnpm run test:coverage + pnpm run test:unit + # env: + # NEXT_PUBLIC_FIREBASE_API_KEY: ${{ secrets.NEXT_PUBLIC_FIREBASE_API_KEY }} + # NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN: ${{ secrets.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN }} + # NEXT_PUBLIC_FIREBASE_PROJECT_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_PROJECT_ID }} + # NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET: ${{ secrets.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET }} + # NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID }} + # NEXT_PUBLIC_FIREBASE_APP_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_APP_ID }} + # NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID }} + + - name: Collect code coverage report + run: pnpm exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov + + - name: Upload e2e test coverage results + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./.coverage/lcov.info + flags: e2etest + name: affine + fail_ci_if_error: true + + - name: Upload test results + if: ${{ failure() }} + uses: actions/upload-artifact@v2 + with: + name: test-results-main + path: ./test-results + if-no-files-found: ignore build-community: name: Build Community diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index 00ce55b51..000000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: Unit Tests -on: - push: - branches: [master] - pull_request: - branches: [master] -jobs: - test: - timeout-minutes: 60 - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - uses: pnpm/action-setup@v2 - with: - version: 'latest' - - - uses: actions/setup-node@v3 - with: - node-version: 16 - registry-url: https://npm.pkg.github.com - scope: '@toeverything' - cache: 'pnpm' - - - run: node scripts/module-resolve/ci.cjs - - - name: Install dependencies - run: pnpm install --no-frozen-lockfile - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_GITHUB_AUTH_TOKEN }} - - - name: Install Playwright browsers - run: npx playwright install chromium - - # - name: Run E2E tests - # run: pnpm run test:e2e - # env: - # NEXT_PUBLIC_FIREBASE_API_KEY: ${{ secrets.NEXT_PUBLIC_FIREBASE_API_KEY }} - # NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN: ${{ secrets.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN }} - # NEXT_PUBLIC_FIREBASE_PROJECT_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_PROJECT_ID }} - # NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET: ${{ secrets.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET }} - # NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID }} - # NEXT_PUBLIC_FIREBASE_APP_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_APP_ID }} - # NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID: ${{ secrets.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID }} - - - name: Run Unit tests - run: pnpm run test:unit diff --git a/.gitignore b/.gitignore index f9333264d..090865ce9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ *dist /tmp /out-tsc +.nyc_output +.coverage # dependencies node_modules diff --git a/.vscode/settings.json b/.vscode/settings.json index 83567e87e..9045fac98 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,7 +19,9 @@ "explorer.fileNesting.expand": false, "explorer.fileNesting.patterns": { "*.js": "${capture}.js.map, ${capture}.min.js, ${capture}.d.ts, ${capture}.d.ts.map", - "package.json": ".browserslist*, .circleci*, .codecov, .commitlint*, .cz-config.js, .czrc, .dlint.json, .dprint.json, .editorconfig, .eslint*, .firebase*, .flowconfig, .github*, .gitlab*, .gitpod*, .huskyrc*, .jslint*, .lighthouserc.*, .lintstagedrc*, .markdownlint*, .mocha*, .node-version, .nodemon*, .npm*, .nvmrc, .pm2*, .pnp.*, .pnpm*, .prettier*, .releaserc*, .sentry*, .stackblitz*, .styleci*, .stylelint*, .tazerc*, .textlint*, .tool-versions, .travis*, .versionrc*, .vscode*, .watchman*, .xo-config*, .yamllint*, .yarnrc*, Procfile, api-extractor.json, apollo.config.*, appveyor*, ava.config.*, azure-pipelines*, bower.json, build.config.*, commitlint*, crowdin*, cypress.*, dangerfile*, dlint.json, dprint.json, firebase.json, grunt*, gulp*, histoire.config.*, jasmine.*, jenkins*, jest.config.*, jsconfig.*, karma*, lerna*, lighthouserc.*, lint-staged*, nest-cli.*, netlify*, nodemon*, nx.*, package-lock.json, package.nls*.json, phpcs.xml, playwright.config.*, pm2.*, pnpm*, prettier*, pullapprove*, puppeteer.config.*, pyrightconfig.json, release-tasks.sh, renovate*, rollup.config.*, stylelint*, tsconfig.*, tsdoc.*, tslint*, tsup.config.*, turbo*, typedoc*, unlighthouse*, vercel*, vetur.config.*, vitest.config.*, webpack*, workspace.json, xo.config.*, yarn*" + "package.json": ".browserslist*, .circleci*, .codecov, .commitlint*, .cz-config.js, .czrc, .dlint.json, .dprint.json, .editorconfig, .eslint*, .firebase*, .flowconfig, .github*, .gitlab*, .gitpod*, .huskyrc*, .jslint*, .lighthouserc.*, .lintstagedrc*, .markdownlint*, .mocha*, .node-version, .nodemon*, .npm*, .nvmrc, .pm2*, .pnp.*, .pnpm*, .prettier*, .releaserc*, .sentry*, .stackblitz*, .styleci*, .stylelint*, .tazerc*, .textlint*, .tool-versions, .travis*, .versionrc*, .vscode*, .watchman*, .xo-config*, .yamllint*, .yarnrc*, Procfile, api-extractor.json, apollo.config.*, appveyor*, ava.config.*, azure-pipelines*, bower.json, build.config.*, commitlint*, crowdin*, cypress.*, dangerfile*, dlint.json, dprint.json, firebase.json, grunt*, gulp*, histoire.config.*, jasmine.*, jenkins*, jest.config.*, jsconfig.*, karma*, lerna*, lighthouserc.*, lint-staged*, nest-cli.*, netlify*, nodemon*, nx.*, package-lock.json, package.nls*.json, phpcs.xml, playwright.config.*, pm2.*, pnpm*, prettier*, pullapprove*, puppeteer.config.*, pyrightconfig.json, release-tasks.sh, renovate*, rollup.config.*, stylelint*, tsconfig.*, tsdoc.*, tslint*, tsup.config.*, turbo*, typedoc*, unlighthouse*, vercel*, vetur.config.*, vitest.config.*, webpack*, workspace.json, xo.config.*, yarn*, babel.*, .babelrc, project.json", + "Cargo.toml": "Cargo.lock", + "README.md": "LICENSE, CHANGELOG.md, CODE_OF_CONDUCT.md, CONTRIBUTING.md" }, "[rust]": { "editor.defaultFormatter": "rust-lang.rust-analyzer" diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 2e9c0db51..000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,45 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment include: - -- Using welcoming and inclusive language -- Being respectful of differing viewpoints and experiences -- Gracefully accepting constructive criticism -- Focusing on what is best for the community -- Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -- The use of sexualized language or imagery and unwelcome sexual attention or advances -- Trolling, insulting/derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or electronic address, without explicit permission -- Other conduct which could reasonably be considered inappropriate in a professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project maintainer. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4, available at - -For answers to common questions about this code of conduct, see diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 3876d1b89..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,3 +0,0 @@ -# Contributing to AFFiNE - -For information related to contributing to AFFiNE, please check out the [Contributing to AFFiNE](https://docs.affine.pro/affine/developer-docs/contributions) section of the documentation at the AFFiNE site. diff --git a/README.md b/README.md index 97c5d04bc..dc750f48d 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,14 @@ See https://github.com/all-?/all-contributors/issues/361#issuecomment-637166066 --> -[all-contributors-badge]: https://img.shields.io/badge/all_contributors-45-orange.svg?style=flat-square +[all-contributors-badge]: https://img.shields.io/badge/all_contributors-48-orange.svg?style=flat-square [![affine.pro](https://img.shields.io/static/v1?label=live%20demo&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAhpJREFUWEdjZEACtnl3MxgY/0YzMjAaMzAwcCLLUYH9/T/D/7MM/5mXHp6kPANmHiOI4Zx9Xfg3C+tKBob/zlSwiAgjGPey/vkdvneq5luwA+zy7+yhn+Vwv+89NFHFhREU7IyM/6YT4WyqK/n/nymT0Tb/1mFGBkYbqptOhIH/Gf4fYbTLv/2NBgmOCOvBSr6DHPCfWNW0UEe2A2x1uRlakiXBbtpx6jND+7KXZLmPbAdURokzeJjxwi31rrzH8OX7P5IdQbYDtnUoMXBzMMEt7Fj2imH7qU/0cQBy8MNsPHL5K0P13Of0cQB68MNsJScaSI4CHk4mhq3tSnCf3n36k0FZmh3Mn7L+DcPqgx9ICgWSHeBpxsdQESUGtgRk+eqDH+H8O09/MiR3P6atA1qTJRlsdLnhPgYlPOQQCW96wPDi3R+iHUFSCKAHP8wydEeREg0kOQA9+JOgwR1qL8CQEygC9jWp0UCSA+aVysIT3JqDHxgmr38DtlRCiIVhZZ0CPNhB6QDkEGIA0Q4gZAkuxxFyBNEOQA7ml+/+MIQ1PUAxG1kelAhB6YMYQLQDCPmQUAjhcgxRDiDWcEKOxOYIohyQGyjCEGIvANaPLfhhBiNHA6hmBBXNhABRDgCV/aBQAAFQpYMrn4PUgNTCACiXEMoNRDmAkC8okR8UDhjYRumAN8sHvGMCSkAD2jUDOWDAO6ewbDQQ3XMAy/oxKownQR0AAAAASUVORK5CYII=&color=orange&message=→)](https://livedemo.affine.pro) [![stars](https://img.shields.io/github/stars/toeverything/AFFiNE.svg?style=flat&logo=github&colorB=red&label=stars)](https://github.com/toeverything/AFFiNE) [![All Contributors][all-contributors-badge]](#contributors) +![codecov](https://codecov.io/gh/toeverything/affine/branch/master/graphs/badge.svg?branch=master) [![Node](https://img.shields.io/badge/node->=16.0-success)](https://www.typescriptlang.org/) [![React](https://img.shields.io/badge/TypeScript-4.7-3178c6)](https://www.typescriptlang.org/) [![React](https://img.shields.io/badge/React-18-61dafb)](https://reactjs.org/) @@ -80,9 +81,9 @@ Before we tell you how to get started with AFFiNE, we'd like to shamelessly plug [![affine.pro](https://img.shields.io/static/v1?label=Try%20it%20Online&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAhpJREFUWEdjZEACtnl3MxgY/0YzMjAaMzAwcCLLUYH9/T/D/7MM/5mXHp6kPANmHiOI4Zx9Xfg3C+tKBob/zlSwiAgjGPey/vkdvneq5luwA+zy7+yhn+Vwv+89NFHFhREU7IyM/6YT4WyqK/n/nymT0Tb/1mFGBkYbqptOhIH/Gf4fYbTLv/2NBgmOCOvBSr6DHPCfWNW0UEe2A2x1uRlakiXBbtpx6jND+7KXZLmPbAdURokzeJjxwi31rrzH8OX7P5IdQbYDtnUoMXBzMMEt7Fj2imH7qU/0cQBy8MNsPHL5K0P13Of0cQB68MNsJScaSI4CHk4mhq3tSnCf3n36k0FZmh3Mn7L+DcPqgx9ICgWSHeBpxsdQESUGtgRk+eqDH+H8O09/MiR3P6atA1qTJRlsdLnhPgYlPOQQCW96wPDi3R+iHUFSCKAHP8wydEeREg0kOQA9+JOgwR1qL8CQEygC9jWp0UCSA+aVysIT3JqDHxgmr38DtlRCiIVhZZ0CPNhB6QDkEGIA0Q4gZAkuxxFyBNEOQA7ml+/+MIQ1PUAxG1kelAhB6YMYQLQDCPmQUAjhcgxRDiDWcEKOxOYIohyQGyjCEGIvANaPLfhhBiNHA6hmBBXNhABRDgCV/aBQAAFQpYMrn4PUgNTCACiXEMoNRDmAkC8okR8UDhjYRumAN8sHvGMCSkAD2jUDOWDAO6ewbDQQ3XMAy/oxKownQR0AAAAASUVORK5CYII=&message=%E2%86%92&style=for-the-badge)](https://affine.pro) No installation or registration required! Head over to our website and try it out now. -[AFFiNE Documentation](https://docs.affine.pro/affine/) - More detailed documentation on how to use and develop with AFFiNE +[AFFiNE Community](https://community.affine.pro) - Our wonderful community, where you can meet and engage with the team, developers and other like-minded enthusiastic user of AFFiNE. -[Our official communities](https://docs.affine.pro/affine/community-links/official-communities) - Join our friendly communities for more support and discussions +[Our official communities](https://community.affine.pro/c/start-here/) - Join our friendly communities for more support and discussions. ## Contributing @@ -90,19 +91,18 @@ Calling all developers, testers, tech writers and more! Contributions of all typ 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 docs for the [internationalization guide](https://docs.affine.pro/affine/internationalization/welcome). +For **translation** and **language support** you can visit our [i18n General Space](https://community.affine.pro/c/i18n-general). -Looking for **others ways to contribute** and wondering where to start? Check out the [AFFiNE Ambassador program](https://docs.affine.pro/affine/affine-ambassadors/welcome), we work closely with passionate members of our community and provide them with a wide-range of support and resources. +Looking for **others ways to contribute** and wondering where to start? Check out the [AFFiNE Ambassador program](https://community.affine.pro/c/start-here/affine-ambassador), we work closely with passionate community members and provide them with a wide-range of support and resources. -If you have questions, join us across various [**social platforms**](https://docs.affine.pro/affine/community-links/official-communities) where our friendly community can help provide the answers. - -We have done a major refactoring recently, if you want to see our previous version of the code, please go to the [Pre-Alpha](https://github.com/toeverything/AFFiNE/tree/Pre-Alpha) branch to view +If you have questions, you are welcome to contact us. One of the best places to get more info and learn more is in the [AFFiNE Community](https://community.affine.pro) where you can engage with other like-minded individuals. ## Thanks We would also like to give thanks to open-source projects that make AFFiNE possible: -- [BlockSuite](https://github.com/toeverything/BlockSuite) - AFFiNE is built with and powered by BlockSuite. +- [BlockSuite](https://github.com/toeverything/BlockSuite) - 💠 BlockSuite is the open-source collaborative editor project behind AFFiNE. +- [OctoBase](https://github.com/toeverything/OctoBase) - 🐙 OctoBase is the open-source database behind AFFiNE, local-first, yet collaborative. A light-weight, scalable, data engine written in Rust. - [Yjs](https://github.com/yjs/yjs) & [Yrs](https://github.com/y-crdt/y-crdt) -- Fundamental support of CRDTs for our implementation on state management and data sync. - [React](https://github.com/facebook/react) -- View layer support and web GUI framework. - [Rust](https://github.com/rust-lang/rust) -- High performance language that extends the ability and availability of our real-time backend, OctoBase. @@ -184,6 +184,9 @@ Thanks a lot to the community for providing such powerful and simple libraries,
Mohammed Faraz

📖
Pranav Sriram

💻
Reson-a

💻 +
Zhizhen He

💻 +
AkaraChen

💻 +
Suyan

💻 @@ -192,10 +195,26 @@ Thanks a lot to the community for providing such powerful and simple libraries, -## Jobs +## 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). + +## 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). +## Upgrading + +For upgrading information please see our [update page](https://affine.pro/blog?tag=Release%20Note). + +## Feature Request + +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. + ## License See [LICENSE](/LICENSE) for details. diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md index de5d75c15..2e9c0db51 100644 --- a/docs/CODE_OF_CONDUCT.md +++ b/docs/CODE_OF_CONDUCT.md @@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo Examples of behavior that contributes to creating a positive environment include: -- Using welcoming and inclusive language -- Being respectful of differing viewpoints and experiences -- Gracefully accepting constructive criticism -- Focusing on what is best for the community -- Showing empathy towards other community members +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members Examples of unacceptable behavior by participants include: -- The use of sexualized language or imagery and unwelcome sexual attention or advances -- Trolling, insulting/derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or electronic address, without explicit permission -- Other conduct which could reasonably be considered inappropriate in a professional setting +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index c278df5d3..fa4a4f4d7 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -12,8 +12,8 @@ Use the table of contents icon on the top left corner of this document to get to Currently we have two versions of AFFiNE: -- [AFFiNE Pre-Alpha](https://livedemo.affine.pro/). This version users the branch `master`, however is no longer actively developed and will be archived in the future. -- [AFFiNE Alpha](https://pathfinder.affine.pro/). This version uses the 'pathfinder' branch, this is the latest version under active development. We plan to update this to the master branch in the near future. +- [AFFiNE Pre-Alpha](https://livedemo.affine.pro/). This version users the branch `Pre-Alpha`, it is no longer actively developed but contains some different functions and features. +- [AFFiNE Alpha](https://pathfinder.affine.pro/). This version uses the `master` branch, this is the latest version under active development. To get an overview of the project, read the [README](../README.md). Here are some resources to help you get started with open source contributions: diff --git a/docs/contributing/bump-blocksuite.md b/docs/contributing/bump-blocksuite.md new file mode 100644 index 000000000..5558cbdd8 --- /dev/null +++ b/docs/contributing/bump-blocksuite.md @@ -0,0 +1,32 @@ +# Bump Blocksuite + +```shell +./scripts/upgrade-blocksuite.sh +``` + +## Understand the version number + +### Stable + +You can see all the stable version tags [here](https://github.com/toeverything/blocksuite/tags). + +### Nightly + +If it's nightly version, the version will follow `${version}-${date}-${hash}`. + +For example, `0.4.0-20230203030233-b22bea7` means that +the version is based on `0.4.0`, the building date is `20230203030233`, +and the commit hash is `b22bea7`. + +> For the source code, see [here](https://github.com/toeverything/set-build-version/blob/master/src/version.ts) + +Using this version format, you can easily check the diff between each version. + +For example, the diff from old version `0.4.0-20230201063624-4e0463b` to new version `0.4.0-20230203030233-b22bea7` +is . + +## Fix the breaking change + +You can follow this file to keep updated: + +https://github.com/toeverything/blocksuite/blob/master/packages/playground/src/main.ts diff --git a/docs/contributor-add.md b/docs/contributor-add.md index 5cc07bd3c..67768e809 100644 --- a/docs/contributor-add.md +++ b/docs/contributor-add.md @@ -4,6 +4,7 @@ - https://allcontributors.org/docs/en/emoji-key ```shell +all-contributors check all-contributors add tzhangchi code,doc all-contributors generate ``` diff --git a/docs/types-of-contributions.md b/docs/types-of-contributions.md index 9e2a647a0..14ececb84 100644 --- a/docs/types-of-contributions.md +++ b/docs/types-of-contributions.md @@ -26,4 +26,4 @@ You may be able to find additional help and information on our social media plat ### :earth_asia: Translations -AFFiNE is internationalized and available in multiple languages. The source content in this repository is written in English. We integrate with an external localization platform to work with the community in localizing the English content. You can find more info in our [internationalization docs](https://docs.affine.pro/affine/internationalization/welcome). +AFFiNE is internationalized and available in multiple languages. The source content in this repository is written in English. We integrate with an external localization platform to work with the community in localizing the English content. You can find more info on our community page, in our [i18n General Space ](https://community.affine.pro/c/i18n-general). diff --git a/package.json b/package.json index 9ba3fb02b..70b762cf6 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,10 @@ "start": "pnpm --filter @affine/app start", "lint": "pnpm --filter @affine/app lint", "test": "playwright test", - "test:dc": "pnpm --filter @affine/datacenter test", - "test:e2e:codegen": "npx playwright codegen http://localhost:8080", + "test:coverage": "cross-env COVERAGE=true pnpm test -- --forbid-only", "test:unit": "playwright test --config=playwright.config.unit.ts", "postinstall": "husky install", "notify": "node --experimental-modules scripts/notify.mjs", - "check:ci": "pnpm lint & pnpm test", "update:blocksuite": "pnpm i --filter @affine/app --filter @affine/datacenter @blocksuite/blocks@nightly @blocksuite/store@nightly && pnpm i --filter @affine/app @blocksuite/editor@nightly" }, "lint-staged": { @@ -29,13 +27,13 @@ }, "devDependencies": { "@changesets/cli": "^2.26.0", - "@jest/globals": "^29.3.1", "@playwright/test": "^1.29.1", "@types/eslint": "^8.4.10", "@types/node": "^18.11.17", "@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", @@ -44,10 +42,10 @@ "fake-indexeddb": "4.0.1", "got": "^12.5.3", "husky": "^8.0.2", - "jest": "^29.3.1", "lint-staged": "^13.1.0", + "nyc": "^15.1.0", "prettier": "^2.7.1", - "typescript": "^4.9.3" + "typescript": "^4.9.5" }, "eslintConfig": { "root": true, @@ -70,9 +68,16 @@ }, "reportUnusedDisableDirectives": true, "ignorePatterns": [ - "src/**/*.test.ts", "package/**/dist/*", - "package/**/sync.js" + "package/**/.babelrc.js", + "package/**/sync.js", + "src/**/*.test.ts", + "**/webpack.config.js", + "**/scripts/*.js", + "**/node_modules/**", + ".github/**", + "**/__tests__/**", + "**/tests/**" ] } } diff --git a/packages/app/.babelrc.js b/packages/app/.babelrc.js new file mode 100644 index 000000000..e98989a64 --- /dev/null +++ b/packages/app/.babelrc.js @@ -0,0 +1,28 @@ +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, +}; diff --git a/packages/app/next.config.js b/packages/app/next.config.js index 2e54ff005..1ef34b267 100644 --- a/packages/app/next.config.js +++ b/packages/app/next.config.js @@ -68,6 +68,9 @@ const nextConfig = { return profile; }, basePath: process.env.BASE_PATH, + experimental: { + forceSwcTransforms: true, + }, }; const baseDir = process.env.LOCAL_BLOCK_SUITE ?? '/'; @@ -98,6 +101,7 @@ const withDebugLocal = require('next-debug-local')( const withPWA = require('next-pwa')({ dest: 'public', + scope: '/_next', disable: process.env.NODE_ENV !== 'production', }); diff --git a/packages/app/package.json b/packages/app/package.json index 31fa96462..cf19c695c 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -11,10 +11,10 @@ "dependencies": { "@affine/datacenter": "workspace:*", "@affine/i18n": "workspace:*", - "@blocksuite/blocks": "0.4.0-20230111171650-bc63456", - "@blocksuite/editor": "0.4.0-20230111171650-bc63456", + "@blocksuite/blocks": "0.4.0-alpha.2", + "@blocksuite/editor": "0.4.0-alpha.2", "@blocksuite/icons": "^2.0.2", - "@blocksuite/store": "0.4.0-20230111171650-bc63456", + "@blocksuite/store": "0.4.0-alpha.2", "@emotion/css": "^11.10.0", "@emotion/react": "^11.10.4", "@emotion/server": "^11.10.0", @@ -28,7 +28,7 @@ "cmdk": "^0.1.20", "css-spring": "^4.1.0", "dayjs": "^1.11.7", - "lit": "^2.3.1", + "lit": "^2.6.1", "next": "13.1.0", "next-debug-local": "^0.1.5", "prettier": "^2.7.1", @@ -36,9 +36,10 @@ "quill-cursors": "^4.0.0", "react": "18.2.0", "react-dom": "18.2.0", - "yjs": "^13.5.44" + "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", @@ -50,7 +51,7 @@ "eslint-plugin-prettier": "^4.2.1", "next-pwa": "^5.6.0", "raw-loader": "^4.0.2", - "typescript": "4.8.3" + "typescript": "^4.9.5" }, "eslintConfig": { "extends": [ diff --git a/packages/app/public/globals.css b/packages/app/public/globals.css index ca43c3fec..e949cd04c 100644 --- a/packages/app/public/globals.css +++ b/packages/app/public/globals.css @@ -144,12 +144,12 @@ button, select, keygen, legend { - color: var(--affine-text-color); background-color: unset; outline: 0; border: 0; - font-size: 18px; - line-height: 1.5; + font-size: var(--affine-font-base); + line-height: var(--affine-line-height); + color: var(--affine-text-color); font-family: var(--affine-font-family); } body { @@ -173,7 +173,7 @@ input { } input:-webkit-autofill { - -webkit-box-shadow: 0 0 0px 1000px white inset; + -webkit-box-shadow: 0 0 0 1000px white inset; } input[type='number'] { diff --git a/packages/app/src/components/404/index.tsx b/packages/app/src/components/404/index.tsx index 36f1b90a3..ffba2a08d 100644 --- a/packages/app/src/components/404/index.tsx +++ b/packages/app/src/components/404/index.tsx @@ -7,7 +7,7 @@ export const NotfoundPage = () => { const router = useRouter(); return ( - + {t('404 - Page Not Found')}

diff --git a/packages/app/src/components/contact-modal/index.tsx b/packages/app/src/components/contact-modal/index.tsx index 6ec64fc14..d22b2ea6c 100644 --- a/packages/app/src/components/contact-modal/index.tsx +++ b/packages/app/src/components/contact-modal/index.tsx @@ -91,8 +91,6 @@ export const ContactModal = ({ Alpha { onClose(); }} diff --git a/packages/app/src/components/create-workspace/index.tsx b/packages/app/src/components/create-workspace/index.tsx index 9a26b5686..2c374a990 100644 --- a/packages/app/src/components/create-workspace/index.tsx +++ b/packages/app/src/components/create-workspace/index.tsx @@ -53,13 +53,12 @@ export const CreateWorkspaceModal = ({ open, onClose }: ModalProps) => { {t('New Workspace')} -

- Workspace is your virtual space to capture, create and plan as - just one person or together as a team. -

+

{t('Workspace description')}

{ setWorkspaceName(value); }} diff --git a/packages/app/src/components/delete-workspace/index.tsx b/packages/app/src/components/delete-workspace/index.tsx index 5cceb487a..cead4b135 100644 --- a/packages/app/src/components/delete-workspace/index.tsx +++ b/packages/app/src/components/delete-workspace/index.tsx @@ -29,8 +29,6 @@ export const DeleteModal = ({
{ onClose(); }} diff --git a/packages/app/src/components/edgeless-toolbar/index.tsx b/packages/app/src/components/edgeless-toolbar/index.tsx index 33784928b..e26189986 100644 --- a/packages/app/src/components/edgeless-toolbar/index.tsx +++ b/packages/app/src/components/edgeless-toolbar/index.tsx @@ -14,8 +14,8 @@ import { UndoIcon, RedoIcon, } from './Icons'; +import { MuiSlide } from '@/ui/mui'; import { Tooltip } from '@/ui/tooltip'; -import Slide from '@mui/material/Slide'; import useCurrentPageMeta from '@/hooks/use-current-page-meta'; import { useAppState } from '@/providers/app-state-provider'; import useHistoryUpdated from '@/hooks/use-history-update'; @@ -127,7 +127,7 @@ export const EdgelessToolbar = () => { const { mode } = useCurrentPageMeta() || {}; return ( - { - + ); }; diff --git a/packages/app/src/components/editor/index.tsx b/packages/app/src/components/editor/index.tsx index 40aacbe0a..eff0dc21a 100644 --- a/packages/app/src/components/editor/index.tsx +++ b/packages/app/src/components/editor/index.tsx @@ -2,7 +2,7 @@ import { useEffect, useRef } from 'react'; import type { Page, Workspace } from '@blocksuite/store'; import '@blocksuite/blocks'; import { EditorContainer } from '@blocksuite/editor'; -import exampleMarkdown from '@/templates/Welcome-to-AFFiNE-Alpha-Downhill.md'; +import exampleMarkdown from '@/templates/Welcome-to-AFFiNE-Alpha-Downhills.md'; import { styled } from '@/styles'; const StyledEditorContainer = styled('div')(() => { @@ -39,22 +39,21 @@ export const Editor = ({ page, workspace, setEditor }: Props) => { const title = metaTitle ? metaTitle : isFirstPage - ? 'Welcome to AFFiNE Alpha "Downhill"' + ? 'Welcome to AFFiNE Alpha "Downhills"' : ''; workspace?.setPageMeta(page.id, { title }); - - const pageId = page.addBlock({ - flavour: 'affine:page', - title, - }); - page.addBlock({ flavour: 'affine:surface' }); - const frameId = page.addBlock({ flavour: 'affine:frame' }, pageId); - page.addBlock({ flavour: 'affine:frame' }, pageId); + const pageBlockId = page.addBlockByFlavour('affine:page', { title }); + page.addBlockByFlavour('affine:surface', {}, null); + // Add frame block inside page block + const frameId = page.addBlockByFlavour('affine:frame', {}, pageBlockId); + // Add paragraph block inside frame block // If this is a first page in workspace, init an introduction markdown if (isFirstPage) { - editor.clipboard.importMarkdown(exampleMarkdown, `${frameId}`); + editor.clipboard.importMarkdown(exampleMarkdown, frameId); workspace.setPageMeta(page.id, { title }); page.resetHistory(); + } else { + page.addBlockByFlavour('affine:paragraph', {}, frameId); } page.resetHistory(); } diff --git a/packages/app/src/components/enable-workspace/EnableWorkspaceModal.tsx b/packages/app/src/components/enable-workspace/EnableWorkspaceModal.tsx new file mode 100644 index 000000000..95da09511 --- /dev/null +++ b/packages/app/src/components/enable-workspace/EnableWorkspaceModal.tsx @@ -0,0 +1,111 @@ +import { styled } from '@/styles'; +import { Modal, ModalWrapper } from '@/ui/modal'; +import { Button, IconButton } from '@/ui/button'; +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 { CloseIcon } from '@blocksuite/icons'; +interface EnableWorkspaceModalProps { + open: boolean; + onClose: () => void; +} + +export const EnableWorkspaceModal = ({ + open, + onClose, +}: EnableWorkspaceModalProps) => { + const { t } = useTranslation(); + const { user, dataCenter, login, currentWorkspace } = useAppState(); + const [loading, setLoading] = useState(false); + return ( + + +
+ { + onClose(); + }} + > + + +
+ + {t('Enable AFFiNE Cloud')}? + {t('Enable AFFiNE Cloud Description')} + {/* {t('Retain local cached data')} */} +
+ { + setLoading(true); + if (!user) { + await login(); + } + if (currentWorkspace) { + const workspace = await dataCenter.enableWorkspaceCloud( + currentWorkspace + ); + workspace && + router.push(`/workspace/${workspace.id}/setting`); + toast(t('Enabled success')); + } + }} + > + {user ? t('Enable') : t('Sign in and Enable')} + + { + onClose(); + }} + > + {t('Not now')} + +
+
+
+
+ ); +}; + +const Header = styled('div')({ + height: '44px', + display: 'flex', + flexDirection: 'row-reverse', + paddingRight: '10px', + paddingTop: '10px', +}); + +const Content = styled('div')({ + textAlign: 'center', +}); + +const ContentTitle = styled('h1')({ + fontSize: '20px', + lineHeight: '28px', + fontWeight: 600, + textAlign: 'center', +}); + +const StyleTips = styled('div')(() => { + return { + userSelect: 'none', + width: '400px', + margin: 'auto', + marginBottom: '32px', + marginTop: '12px', + }; +}); + +const StyleButton = styled(Button)(() => { + return { + width: '284px', + display: 'block', + margin: 'auto', + marginTop: '16px', + }; +}); diff --git a/packages/app/src/components/enable-workspace/index.tsx b/packages/app/src/components/enable-workspace/index.tsx index 3cc28b25d..cd4d558ff 100644 --- a/packages/app/src/components/enable-workspace/index.tsx +++ b/packages/app/src/components/enable-workspace/index.tsx @@ -1,24 +1,28 @@ -import { useWorkspaceHelper } from '@/hooks/use-workspace-helper'; import { Button } from '@/ui/button'; import { useTranslation } from '@affine/i18n'; import { useState } from 'react'; +import { EnableWorkspaceModal } from './EnableWorkspaceModal'; export const EnableWorkspaceButton = () => { const { t } = useTranslation(); - const { enableWorkspace } = useWorkspaceHelper(); - const [loading, setLoading] = useState(false); + const [enableModalOpen, setEnableModalOpen] = useState(false); return ( - + <> + + { + setEnableModalOpen(false); + }} + > + ); }; diff --git a/packages/app/src/components/header/Header.tsx b/packages/app/src/components/header/Header.tsx index 4fe79467a..4f4f86afc 100644 --- a/packages/app/src/components/header/Header.tsx +++ b/packages/app/src/components/header/Header.tsx @@ -6,7 +6,7 @@ import { StyledBrowserWarning, StyledCloseButton, } from './styles'; -import CloseIcon from '@mui/icons-material/Close'; +import { CloseIcon } from '@blocksuite/icons'; import { useWarningMessage, shouldShowWarning } from './utils'; import EditorOptionMenu from './header-right-items/EditorOptionMenu'; import TrashButtonGroup from './header-right-items/TrashButtonGroup'; diff --git a/packages/app/src/components/header/PageListHeader.tsx b/packages/app/src/components/header/PageListHeader.tsx index 99efff25d..976c86b50 100644 --- a/packages/app/src/components/header/PageListHeader.tsx +++ b/packages/app/src/components/header/PageListHeader.tsx @@ -1,5 +1,6 @@ import { PropsWithChildren, ReactNode } from 'react'; import Header from './Header'; +import QuickSearchButton from './QuickSearchButton'; import { StyledPageListTittleWrapper } from './styles'; // import QuickSearchButton from './QuickSearchButton'; @@ -12,7 +13,7 @@ export const PageListHeader = ({ icon, children }: PageListHeaderProps) => { {icon} {children} - {/* */} +
); diff --git a/packages/app/src/components/header/header-right-items/SyncUser.tsx b/packages/app/src/components/header/header-right-items/SyncUser.tsx index 60af1b461..0923de989 100644 --- a/packages/app/src/components/header/header-right-items/SyncUser.tsx +++ b/packages/app/src/components/header/header-right-items/SyncUser.tsx @@ -1,15 +1,41 @@ -import { CloudUnsyncedIcon, CloudInsyncIcon } from '@blocksuite/icons'; +import { CloudUnsyncedIcon } from '@blocksuite/icons'; import { useModal } from '@/providers/GlobalModalProvider'; import { useAppState } from '@/providers/app-state-provider'; import { IconButton } from '@/ui/button'; +// Temporary solution to use this component, since the @blocksuite/icons has not been published yet +const DefaultSyncIcon = () => { + return ( + + + + + ); +}; export const SyncUser = () => { const { triggerLoginModal } = useModal(); const appState = useAppState(); return appState.user ? ( - + ) : ( { export const StyledPageListTittleWrapper = styled(StyledTitle)(({ theme }) => { return { - fontSize: theme.font.sm, + fontSize: theme.font.base, color: theme.colors.textColor, '>svg': { fontSize: '20px', diff --git a/packages/app/src/components/help-island/index.tsx b/packages/app/src/components/help-island/index.tsx index d6aad0c89..9fb883481 100644 --- a/packages/app/src/components/help-island/index.tsx +++ b/packages/app/src/components/help-island/index.tsx @@ -6,7 +6,7 @@ import { StyledTransformIcon, } from './style'; import { CloseIcon, ContactIcon, HelpIcon, KeyboardIcon } from './Icons'; -import Grow from '@mui/material/Grow'; +import { MuiGrow } from '@/ui/mui'; import { Tooltip } from '@/ui/tooltip'; import { useTranslation } from '@affine/i18n'; import { useModal } from '@/providers/GlobalModalProvider'; @@ -35,7 +35,7 @@ export const HelpIsland = ({ setShowContent(false); }} > - + {showList.includes('contact') && ( @@ -66,7 +66,7 @@ export const HelpIsland = ({ )} - +
{ )} {status === 'importing' && ( - { OOOOPS! Sorry forgot to remind you that we are working on the import function - + )} diff --git a/packages/app/src/components/login-modal/GoogleIcon.tsx b/packages/app/src/components/login-modal/GoogleIcon.tsx new file mode 100644 index 000000000..f0454955c --- /dev/null +++ b/packages/app/src/components/login-modal/GoogleIcon.tsx @@ -0,0 +1,28 @@ +export const GoogleIcon = () => { + return ( + + + + + + + ); +}; diff --git a/packages/app/src/components/login-modal/Icons.tsx b/packages/app/src/components/login-modal/Icons.tsx deleted file mode 100644 index 12356f60d..000000000 --- a/packages/app/src/components/login-modal/Icons.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { CloudUnsyncedIcon } from '@blocksuite/icons'; -import { styled } from '@/styles'; -import GoogleSvg from './google.svg'; - -export const GoogleIcon = () => { - return ( - - - Google - - - ); -}; - -const GoogleIconWrapper = styled('div')(({ theme }) => ({ - background: theme.colors.pageBackground, - display: 'flex', - alignItems: 'center', - justifyContent: 'center', -})); - -export const StayLogOutIcon = () => { - return ( - - - - ); -}; - -const StayLogOutWrapper = styled('div')(({ theme }) => { - return { - width: '48px', - height: '48px', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - fontSize: '24px', - background: theme.colors.hoverBackground, - }; -}); diff --git a/packages/app/src/components/login-modal/LoginOptionButton.tsx b/packages/app/src/components/login-modal/LoginOptionButton.tsx deleted file mode 100644 index f104efc58..000000000 --- a/packages/app/src/components/login-modal/LoginOptionButton.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { styled } from '@/styles'; -import { Button } from '@/ui/button'; -import { GoogleIcon, StayLogOutIcon } from './Icons'; -import { useTranslation } from '@affine/i18n'; -export const GoogleLoginButton = () => { - const { t } = useTranslation(); - - return ( - - - - - - {t('Continue with Google')} - - - ); -}; - -export const StayLogOutButton = () => { - const { t } = useTranslation(); - return ( - - - - - - - {t('Stay logged out')} - {t('All changes are saved locally')} - - - - ); -}; - -const StyledGoogleButton = styled('div')(({ theme }) => { - return { - width: '284px', - height: '40px', - marginTop: '30px', - fontSize: '16px', - cursor: 'pointer', - borderRadius: '40px', - border: `1px solid ${theme.colors.iconColor}`, - overflow: 'hidden', - ':hover': { - border: `1px solid ${theme.colors.primaryColor}`, - }, - }; -}); - -const StyledStayLogOutButton = styled(Button)(() => { - return { - width: '361px', - height: '56px', - padding: '4px', - ':hover': { - borderColor: '#6880FF', - }, - }; -}); - -const ButtonWrapper = styled('div')({ - display: 'flex', - flexDirection: 'row', - width: '100%', -}); - -const IconWrapper = styled('div')({ - flex: '0 48px', - borderRadius: '5px', - overflow: 'hidden', - marginRight: '12px', - marginTop: '8px', -}); - -const TextWrapper = styled('div')({ - flex: 1, - textAlign: 'left', - height: '40px', - lineHeight: '40px', -}); - -const Title = styled('h1')(() => { - return { - fontSize: '18px', - lineHeight: '26px', - fontWeight: 500, - }; -}); - -const Description = styled('p')(() => { - return { - fontSize: '16px', - lineHeight: '22px', - fontWeight: 400, - }; -}); diff --git a/packages/app/src/components/login-modal/google.svg b/packages/app/src/components/login-modal/google.svg deleted file mode 100644 index 23af746fe..000000000 --- a/packages/app/src/components/login-modal/google.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/packages/app/src/components/login-modal/index.tsx b/packages/app/src/components/login-modal/index.tsx index d572190ee..880ebe6e3 100644 --- a/packages/app/src/components/login-modal/index.tsx +++ b/packages/app/src/components/login-modal/index.tsx @@ -1,7 +1,9 @@ -import { styled } from '@/styles'; +import { positionAbsolute, styled } from '@/styles'; import { Modal, ModalWrapper, ModalCloseButton } from '@/ui/modal'; -import { GoogleLoginButton } from './LoginOptionButton'; +import { Button } from '@/ui/button'; import { useAppState } from '@/providers/app-state-provider'; +import { useTranslation } from '@affine/i18n'; +import { GoogleIcon } from './GoogleIcon'; interface LoginModalProps { open: boolean; onClose: () => void; @@ -9,38 +11,44 @@ interface LoginModalProps { export const LoginModal = ({ open, onClose }: LoginModalProps) => { const { login } = useAppState(); + const { t } = useTranslation(); return ( - -
- { - onClose(); - }} - /> -
+ + { + onClose(); + }} + /> - {'Sign in'} - Set up an AFFINE account to sync data - {t('Sign in')} + {t('Set up an AFFiNE account to sync data')} + { await login(); onClose(); }} > - - + + {t('Continue with Google')} +
); }; -const Header = styled('div')({ - position: 'relative', - height: '44px', +const StyledLoginButton = styled(Button)(() => { + return { + width: '284px', + marginTop: '30px', + position: 'relative', + svg: { + ...positionAbsolute({ left: '18px', top: '0', bottom: '0' }), + margin: 'auto', + }, + }; }); const Content = styled('div')({ diff --git a/packages/app/src/components/logout-modal/icon.tsx b/packages/app/src/components/logout-modal/icon.tsx index df23248b7..afca63f57 100644 --- a/packages/app/src/components/logout-modal/icon.tsx +++ b/packages/app/src/components/logout-modal/icon.tsx @@ -8,7 +8,7 @@ export const Check = () => { fill="none" xmlns="http://www.w3.org/2000/svg" > - + void; } export const LogoutModal = ({ open, onClose }: LoginModalProps) => { - const [localCache, setLocalCache] = useState(false); + const [localCache, setLocalCache] = useState(true); + const { blobDataSynced } = useAppState(); + const { t } = useTranslation(); return (
{ onClose(true); }} />
- {'Sign out?'} - Set up an AFFINE account to sync data + {t('Sign out')}? + + {blobDataSynced + ? t('Set up an AFFiNE account to sync data') + : 'All data has been stored in the cloud'} + {localCache ? ( { )} - Retain local cached data + {t('Retain local cached data')} -
- - -
+ {blobDataSynced ? ( +
+ + +
+ ) : ( +
+ + +
+ )}
diff --git a/packages/app/src/components/page-list/Empty.tsx b/packages/app/src/components/page-list/Empty.tsx index 4430b8f1a..f8dfc6745 100644 --- a/packages/app/src/components/page-list/Empty.tsx +++ b/packages/app/src/components/page-list/Empty.tsx @@ -14,7 +14,6 @@ export const PageListEmpty = (props: { listType?: string }) => { {listType === 'all' &&

{t('emptyAllPages')}

} {listType === 'favorite' &&

{t('emptyFavourite')}

} {listType === 'trash' &&

{t('emptyTrash')}

} -

{t('still designed')}

); }; diff --git a/packages/app/src/components/page-list/OperationCell.tsx b/packages/app/src/components/page-list/OperationCell.tsx index ca59efa60..a9efc634c 100644 --- a/packages/app/src/components/page-list/OperationCell.tsx +++ b/packages/app/src/components/page-list/OperationCell.tsx @@ -1,7 +1,7 @@ import { useConfirm } from '@/providers/ConfirmProvider'; import { PageMeta } from '@/providers/app-state-provider'; import { Menu, MenuItem } from '@/ui/menu'; -import { Wrapper } from '@/ui/layout'; +import { FlexWrapper } from '@/ui/layout'; import { IconButton } from '@/ui/button'; import { MoreVerticalIcon, @@ -63,13 +63,13 @@ export const OperationCell = ({ pageMeta }: { pageMeta: PageMeta }) => { ); return ( - + - + ); }; @@ -80,7 +80,7 @@ export const TrashOperationCell = ({ pageMeta }: { pageMeta: PageMeta }) => { const { confirm } = useConfirm(); const { t } = useTranslation(); return ( - + { > - + ); }; diff --git a/packages/app/src/components/quick-search/Footer.tsx b/packages/app/src/components/quick-search/Footer.tsx index 14b37561b..cc84003a5 100644 --- a/packages/app/src/components/quick-search/Footer.tsx +++ b/packages/app/src/components/quick-search/Footer.tsx @@ -1,26 +1,23 @@ import React from 'react'; import { AddIcon } from '@blocksuite/icons'; import { StyledModalFooterContent } from './style'; -import { useModal } from '@/providers/GlobalModalProvider'; import { Command } from 'cmdk'; import { usePageHelper } from '@/hooks/use-page-helper'; import { useTranslation } from '@affine/i18n'; -export const Footer = (props: { query: string }) => { - const { triggerQuickSearchModal } = useModal(); +export const Footer = (props: { query: string; onClose: () => void }) => { const { openPage, createPage } = usePageHelper(); const { t } = useTranslation(); - const query = props.query; + const { query, onClose } = props; return ( { + onClose(); const pageId = await createPage({ title: query }); if (pageId) { openPage(pageId); } - - triggerQuickSearchModal(); }} > diff --git a/packages/app/src/components/quick-search/Input.tsx b/packages/app/src/components/quick-search/Input.tsx index 6d96d239b..0ac61c54c 100644 --- a/packages/app/src/components/quick-search/Input.tsx +++ b/packages/app/src/components/quick-search/Input.tsx @@ -8,32 +8,41 @@ import React, { import { SearchIcon } from '@blocksuite/icons'; import { StyledInputContent, StyledLabel } from './style'; import { Command } from 'cmdk'; -import { useAppState } from '@/providers/app-state-provider'; import { useTranslation } from '@affine/i18n'; export const Input = (props: { + open: boolean; query: string; setQuery: Dispatch>; setLoading: Dispatch>; + isPublic: boolean; + publishWorkspaceName: string | undefined; }) => { + const { open, query, setQuery, setLoading, isPublic, publishWorkspaceName } = + props; const [isComposition, setIsComposition] = useState(false); const [inputValue, setInputValue] = useState(''); const inputRef = useRef(null); - const { currentWorkspace } = useAppState(); const { t } = useTranslation(); useEffect(() => { - inputRef.current?.addEventListener( - 'blur', - () => { - inputRef.current?.focus(); - }, - true - ); - return inputRef.current?.focus(); - }, [inputRef]); + if (open) { + const inputElement = inputRef.current; + return inputElement?.focus(); + } + }, [open]); useEffect(() => { - return setInputValue(props.query); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + const inputElement = inputRef.current; + if (!open) { + return; + } + const handleFocus = () => { + inputElement?.focus(); + }; + inputElement?.addEventListener('blur', handleFocus, true); + return () => inputElement?.removeEventListener('blur', handleFocus, true); + }, [inputRef, open]); + useEffect(() => { + setInputValue(query); + }, [query]); return ( @@ -46,18 +55,18 @@ export const Input = (props: { setIsComposition(true); }} onCompositionEnd={e => { - props.setQuery(e.data); + setQuery(e.data); setIsComposition(false); - if (!props.query) { - props.setLoading(true); + if (!query) { + setLoading(true); } }} onValueChange={str => { setInputValue(str); if (!isComposition) { - props.setQuery(str); - if (!props.query) { - props.setLoading(true); + setQuery(str); + if (!query) { + setLoading(true); } } }} @@ -78,9 +87,9 @@ export const Input = (props: { } }} placeholder={ - currentWorkspace?.isPublish + isPublic ? t('Quick search placeholder2', { - workspace: currentWorkspace?.blocksuiteWorkspace?.meta.name, + workspace: publishWorkspaceName, }) : t('Quick search placeholder') } diff --git a/packages/app/src/components/quick-search/PublishedResults.tsx b/packages/app/src/components/quick-search/PublishedResults.tsx new file mode 100644 index 000000000..d0564b4cd --- /dev/null +++ b/packages/app/src/components/quick-search/PublishedResults.tsx @@ -0,0 +1,95 @@ +import { Command } from 'cmdk'; +import { StyledListItem, StyledNotFound } from './style'; +import { PaperIcon, EdgelessIcon } from '@blocksuite/icons'; +import { Dispatch, SetStateAction, useEffect, useState } from 'react'; +import { useAppState, PageMeta } from '@/providers/app-state-provider'; +import { useRouter } from 'next/router'; +import { NoResultSVG } from './NoResultSVG'; +import { useTranslation } from '@affine/i18n'; +import usePageHelper from '@/hooks/use-page-helper'; +import { Workspace } from '@blocksuite/store'; + +export const PublishedResults = (props: { + query: string; + loading: boolean; + setLoading: Dispatch>; + setPublishWorkspaceName: Dispatch>; + onClose: () => void; +}) => { + const [workspace, setWorkspace] = useState(); + const { query, loading, setLoading, onClose, setPublishWorkspaceName } = + props; + const { search } = usePageHelper(); + const [results, setResults] = useState(new Map()); + const { dataCenter } = useAppState(); + const router = useRouter(); + const [pageList, setPageList] = useState([]); + useEffect(() => { + dataCenter + .loadPublicWorkspace(router.query.workspaceId as string) + .then(data => { + setPageList(data.blocksuiteWorkspace?.meta.pageMetas as PageMeta[]); + if (data.blocksuiteWorkspace) { + setWorkspace(data.blocksuiteWorkspace); + setPublishWorkspaceName(data.blocksuiteWorkspace.meta.name); + } + }) + .catch(() => { + router.push('/404'); + }); + }, [router, dataCenter, setPublishWorkspaceName]); + const { t } = useTranslation(); + useEffect(() => { + setResults(search(query, workspace)); + setLoading(false); + //Save the Map obtained from the search as state + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [query, setResults, setLoading]); + const pageIds = [...results.values()]; + const resultsPageMeta = pageList.filter( + page => pageIds.indexOf(page.id) > -1 && !page.trash + ); + + return loading ? null : ( + <> + {query ? ( + resultsPageMeta.length ? ( + + {resultsPageMeta.map(result => { + return ( + { + router.push( + `/public-workspace/${router.query.workspaceId}/${result.id}` + ); + onClose(); + }} + value={result.id} + > + + {result.mode === 'edgeless' ? ( + + ) : ( + + )} + {result.title} + + + ); + })} + + ) : ( + + {t('Find 0 result')} + + + ) + ) : ( + <> + )} + + ); +}; diff --git a/packages/app/src/components/quick-search/Results.tsx b/packages/app/src/components/quick-search/Results.tsx index 9e23c5f7c..d7c8eddb0 100644 --- a/packages/app/src/components/quick-search/Results.tsx +++ b/packages/app/src/components/quick-search/Results.tsx @@ -1,6 +1,5 @@ import { Command } from 'cmdk'; import { StyledListItem, StyledNotFound } from './style'; -import { useModal } from '@/providers/GlobalModalProvider'; import { PaperIcon, EdgelessIcon } from '@blocksuite/icons'; import { Dispatch, SetStateAction, useEffect, useState } from 'react'; import { useAppState } from '@/providers/app-state-provider'; @@ -12,14 +11,11 @@ import usePageHelper from '@/hooks/use-page-helper'; export const Results = (props: { query: string; loading: boolean; + onClose: () => void; setLoading: Dispatch>; setShowCreatePage: Dispatch>; }) => { - const query = props.query; - const loading = props.loading; - const setLoading = props.setLoading; - const setShowCreatePage = props.setShowCreatePage; - const { triggerQuickSearchModal } = useModal(); + const { query, loading, setLoading, setShowCreatePage, onClose } = props; const { openPage } = usePageHelper(); const router = useRouter(); const { currentWorkspace, pageList } = useAppState(); @@ -55,8 +51,8 @@ export const Results = (props: { { + onClose(); openPage(result.id); - triggerQuickSearchModal(); }} value={result.id} > @@ -86,8 +82,8 @@ export const Results = (props: { key={link.title} value={link.title} onSelect={() => { + onClose(); router.push(link.href); - triggerQuickSearchModal(); }} > diff --git a/packages/app/src/components/quick-search/config.ts b/packages/app/src/components/quick-search/config.ts index 4738e9355..9aa284558 100644 --- a/packages/app/src/components/quick-search/config.ts +++ b/packages/app/src/components/quick-search/config.ts @@ -1,5 +1,10 @@ import { FC, SVGProps } from 'react'; -import { AllPagesIcon, FavouritesIcon, TrashIcon } from '@blocksuite/icons'; +import { + AllPagesIcon, + FavouritesIcon, + TrashIcon, + SettingsIcon, +} from '@blocksuite/icons'; import { useTranslation } from '@affine/i18n'; export const useSwitchToConfig = ( @@ -23,6 +28,13 @@ export const useSwitchToConfig = ( : '', icon: FavouritesIcon, }, + { + title: t('Settings'), + href: currentWorkspaceId + ? `/workspace/${currentWorkspaceId}/setting` + : '', + icon: SettingsIcon, + }, { title: t('Trash'), href: currentWorkspaceId ? `/workspace/${currentWorkspaceId}/trash` : '', diff --git a/packages/app/src/components/quick-search/index.tsx b/packages/app/src/components/quick-search/index.tsx index 9d6e47796..97921efca 100644 --- a/packages/app/src/components/quick-search/index.tsx +++ b/packages/app/src/components/quick-search/index.tsx @@ -13,7 +13,8 @@ import { Command } from 'cmdk'; import { useEffect, useState } from 'react'; import { useModal } from '@/providers/GlobalModalProvider'; import { getUaHelper } from '@/utils'; -import { useAppState } from '@/providers/app-state-provider'; +import { useRouter } from 'next/router'; +import { PublishedResults } from './PublishedResults'; type TransitionsModalProps = { open: boolean; onClose: () => void; @@ -22,18 +23,30 @@ const isMac = () => { return getUaHelper().isMacOs; }; export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => { - const { currentWorkspace } = useAppState(); - + const router = useRouter(); const [query, setQuery] = useState(''); const [loading, setLoading] = useState(true); + const [isPublic, setIsPublic] = useState(false); + const [publishWorkspaceName, setPublishWorkspaceName] = useState(''); const [showCreatePage, setShowCreatePage] = useState(true); const { triggerQuickSearchModal } = useModal(); - + const isPublicAndNoQuery = () => { + return isPublic && query.length === 0; + }; + const handleClose = () => { + setQuery(''); + onClose(); + }; // Add ‘⌘+K’ shortcut keys as switches useEffect(() => { + if (router.pathname.startsWith('/404')) { + triggerQuickSearchModal(false); + return; + } const down = (e: KeyboardEvent) => { if ((e.key === 'k' && e.metaKey) || (e.key === 'k' && e.ctrlKey)) { const selection = window.getSelection(); + setQuery(''); if (selection?.toString()) { triggerQuickSearchModal(false); return; @@ -43,18 +56,23 @@ export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => { } } }; - if (!open) { - setQuery(''); - } document.addEventListener('keydown', down, { capture: true }); return () => document.removeEventListener('keydown', down, { capture: true }); - }, [open, triggerQuickSearchModal]); + }, [open, router.pathname, triggerQuickSearchModal]); + + useEffect(() => { + if (router.pathname.startsWith('/public-workspace')) { + return setIsPublic(true); + } else { + return setIsPublic(false); + } + }, [router]); return ( @@ -62,7 +80,7 @@ export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => { width={620} style={{ maxHeight: '80vh', - minHeight: '350px', + minHeight: isPublicAndNoQuery() ? '72px' : '350px', top: '12vh', }} > @@ -81,28 +99,51 @@ export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => { }} > - + {isMac() ? '⌘ + K' : 'Ctrl + K'} - + - - + + {!isPublic ? ( + + ) : ( + + )} - {currentWorkspace?.published ? ( - <> - ) : showCreatePage ? ( - <> - - -