Compare commits

...

402 Commits

Author SHA1 Message Date
李华桥
b20e91bee0 v0.10.3-beta.1 2023-11-25 14:14:40 +08:00
李华桥
9a4e5ec8c3 Merge branch 'canary' into stable 2023-11-25 14:14:14 +08:00
liuyi
9dc2d55a5a fix(server): add guid compatibility of :space:page variant (#5062) 2023-11-24 15:46:09 +00:00
liuyi
91efca107a refactor(server): standarderlize metrics and trace with OTEL (#5054)
you can now export span to Zipkin and metrics to Prometheus when developing locally
follow the docs of OTEL: https://opentelemetry.io/docs/instrumentation/js/exporters/

<img width="2357" alt="image" src="https://github.com/toeverything/AFFiNE/assets/8281226/ec615e1f-3e91-43f7-9111-d7d2629e9679">
2023-11-24 15:19:22 +00:00
liuyi
cf65a5cd93 fix(server): never throw in websocket gateways (#5050) 2023-11-24 07:26:40 +00:00
LongYinan
42f4045ad6 chore: adjust the request memory size and replica count (#5046) 2023-11-24 06:19:38 +00:00
李华桥
2019838ae7 v0.10.3-beta.0 2023-11-24 11:39:23 +08:00
李华桥
30ff25f400 Merge branch 'canary' into stable 2023-11-23 23:40:32 +08:00
Joooye_34
317ca7f4e7 ci: fix storybook publish problem (#5047) 2023-11-23 23:38:09 +08:00
李华桥
e766208c18 chore: reset merge wrong codes 2023-11-23 22:53:06 +08:00
李华桥
8742f28148 Merge branch 'canary' into stable 2023-11-23 21:31:42 +08:00
JimmFly
4168551783 chore: bump icons version (#5042) 2023-11-23 12:00:51 +00:00
LongYinan
55c6477bcc fix(electron): appimage forge builder (#5043) 2023-11-23 11:46:50 +00:00
Peng Xiao
ae8329c590 chore(core): update react-resizable-panels (#5041)
`react-resizable-panels` will throw some errors sometime when showing history modal dialog.
I haven't checked the root cause, but upgrade it to the latest will get rid of the error.
2023-11-23 09:20:12 +00:00
LongYinan
25eda22af6 v0.10.3-canary.2 2023-11-23 16:47:40 +08:00
EYHN
23e0137ed8 refactor(workspace): blob sync (#5037)
This pr implements a blob engine.
It exposes a single `BlobStorage` to the `blocksuite`, and in it we sync blobs between multiple storages.

The implement still have few issues, but we can merge this pr first and fix them in future.

* BlobEngine currently **do nothing when delete**, because synchronization logic conflicts with deletion logic.
* BlobEngine sync between storages by querying the blob list at regular intervals. This will **cause many queries**, we can avoid this in the future by subscribing to remote changes.
2023-11-23 07:56:19 +00:00
liuyi
1740e7efa1 fix(server): check state changes before saving history record (#5038) 2023-11-23 07:39:02 +00:00
Peng Xiao
7463e87742 fix(electron): clone db file when enable cloud for desktop (#5028)
At the moment on desktop the user's local blob data will be lost after enable cloud.
This is because blob data is only synced from old idb to new idb, but not sync into sqlitedb.

This pr will simply clone the db file for desktop app. It should also speed up the time when enabling cloud for a large local workspace.
2023-11-23 07:23:16 +00:00
李华桥
9ded6afb4b chore: v0.10.3-canary.1 2023-11-23 14:39:55 +08:00
JimmFly
ad2d3b9167 feat(core): add download app button to web (#5023) 2023-11-23 14:33:25 +08:00
LongYinan
3499dbbb7f feat: upgrade dependencies and lockfile (#5016)
- Close https://github.com/toeverything/AFFiNE/security/dependabot/47
2023-11-23 05:18:05 +00:00
Joooye_34
4c8d54b3a7 refactor(core): use manual upgrade to replace auto migration when web setup (#5022)
1. Split logic in `packages/common/infra/src/blocksuite/index.ts` to multiple single files
2. Move migration logic from setup to upgrade module, to prevent auto migration problems and loading problem
2023-11-23 02:26:06 +00:00
liuyi
3710bcdc14 fix(server): use iso date string as history query input (#5035) 2023-11-23 01:59:08 +00:00
Peng Xiao
ca07b143ef fix(core): should not reset page preset on rerender (#5034)
Should not reset editor preset when re-render.

See ce7ac88fc7/packages/editor/src/components/editor-container.ts (L197). If these props changes, it will trigger some unexpected side effects.
2023-11-22 18:29:34 +00:00
EYHN
e8616acfe4 fix(workspace): fast check svg buffer (#5032) 2023-11-22 14:53:33 +00:00
EYHN
06203498da fix(core): fix page loading shimmer (#5027) 2023-11-22 12:55:10 +00:00
Flrande
d7d47853fe chore: bump blocksuite (#5030) 2023-11-22 20:51:35 +08:00
Flrande
a3d880daa3 chore: bump blocksuite (#5026) 2023-11-22 20:12:52 +08:00
liuyi
d1476495ae feat(server): impl doc history (#5004) 2023-11-22 07:56:59 +00:00
liuyi
946b7b4004 feat(server): event on snapshot upserted (#5002) 2023-11-22 07:23:44 +00:00
liuyi
525b196cae feat(server): reduce duplidated merge with cache (#4975) 2023-11-22 04:09:07 +00:00
liuyi
c69e542b98 feat(server): add cache module (#4973) 2023-11-22 04:09:00 +00:00
liuyi
85bee72e6b chore(server): remove deprecated redis manager (#4971) 2023-11-22 03:51:18 +00:00
liuyi
b7d6237c20 feat(server): add doc history support (#4970) 2023-11-22 03:31:22 +00:00
LongYinan
5f1a124b53 fix(core): add error boundary for workspace layout (#5014)
https://github.com/toeverything/AFFiNE/assets/3468483/d478bf4f-2be3-4d7d-8d94-aa95c1f74c8e
2023-11-22 09:58:33 +08:00
Peng Xiao
3839a9bd15 build(electron): asar (#4965)
Due to restrictions on how Electron package works, the `node_modules` should not be hoisted and not to use s/h-links at all. This is why we need to have two separate installs for electron and non-electron packages in the build.

Tested via the following script

```bash
#!/bin/bash

echo "step 1: clean up"
find . -name "node_modules" -prune -exec rm -rf '{}' +
# git clean -dfX
build_type=canary

echo "step 2: install web dependencies"
# firstly, build web static
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 SENTRYCLI_SKIP_DOWNLOAD=1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 HUSKY=1 yarn

echo "step 3: generate assets"
BUILD_TYPE="$build_type" yarn workspace @affine/electron generate-assets

# cleanup node_modules
find . -name "node_modules" -prune -exec rm -rf '{}' +

echo "step 4: install electron dependencies"
# install electron deps
yarn config set nmHoistingLimits workspaces
yarn config set enableScripts false
yarn config set nmMode classic
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 HUSKY=0 yarn workspaces focus @affine/electron @affine/monorepo

echo "step 5: build native"
# build native
yarn workspace @affine/native build
yarn workspace @affine/storage build

echo "step 6: build electron"
# build electron
yarn workspace @affine/electron build

echo "step 7: package electron"
# package
SKIP_GENERATE_ASSETS=1 BUILD_TYPE="$build_type" HOIST_NODE_MODULES=1 yarn workspace @affine/electron package
```
2023-11-21 17:44:30 +00:00
Peng Xiao
f33c49b27e fix(core): hmr issue on dev (#5006)
I suspect HMR does not working properly on dev because we have multiple entries.
One relative issue: https://github.com/webpack/webpack-dev-server/issues/2792/

I think we do not need multiple entries for polyfills & plugins after all. They could be in the same chunk, and could be later optimized through splitChunks option.

`ses.ts` is changed to `ses-lockdown.ts` because `ses.ts` does not pass circular dependency check by madge. I haven't looked through the real root cause though. See https://github.com/pahen/madge/issues/355
2023-11-21 17:27:16 +00:00
Peng Xiao
615255706d fix: invisible button should not be interactive (#5017) 2023-11-22 01:05:38 +08:00
EYHN
5e8103adbd chore: faster lint-staged (#5013)
Co-authored-by: EYHN <13579374+EYHN@users.noreply.github.com>
2023-11-21 22:24:24 +08:00
JimmFly
f06bdd9a39 fix(core): cmdk crash when entering double quotes (#5008)
Due to a bug in the upstream repository, a temporary fix was implemented until the issue in the upstream repository is resolved.
https://github.com/pacocoursey/cmdk/issues/189
2023-11-21 12:51:22 +00:00
李华桥
00c11d40cf v0.10.3-canary.0 2023-11-21 10:02:46 +08:00
李华桥
0f6b28fd06 c0.11.0-canary.0 2023-11-20 23:53:50 +08:00
EYHN
90c130cf15 fix(core): merge updates before push to storage (#4986) 2023-11-20 23:26:19 +08:00
EYHN
9370110cdc feat(workspace): more status for SyncEngine (#4984) 2023-11-20 22:51:20 +08:00
EYHN
c9f1fd9649 feat(workspace): more status for SyncPeer (#4983) 2023-11-20 20:37:12 +08:00
EYHN
70e71bd43e fix(core): make e2e more stable (#4987) 2023-11-20 20:17:30 +08:00
EYHN
899e46b1fa fix(core): rerender (#4988) 2023-11-20 17:32:40 +08:00
dependabot[bot]
c127d449a1 chore: bump the all-cargo-dependencies group with 1 update (#4997)
Bumps the all-cargo-dependencies group with 1 update: [uuid](https://github.com/uuid-rs/uuid).

<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/uuid-rs/uuid/releases">uuid's releases</a>.</em></p>
<blockquote>
<h2>1.6.0</h2>
<h2>What's Changed</h2>
<ul>
<li>doc: fix links in v6 module by <a href="https://github.com/metalalive"><code>@​metalalive</code></a> in <a href="https://redirect.github.com/uuid-rs/uuid/pull/714">uuid-rs/uuid#714</a></li>
<li>Stabilize UUIDv6-v8 support by <a href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a href="https://redirect.github.com/uuid-rs/uuid/pull/718">uuid-rs/uuid#718</a></li>
<li>Prepare for 1.6.0 release by <a href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a href="https://redirect.github.com/uuid-rs/uuid/pull/719">uuid-rs/uuid#719</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/metalalive"><code>@​metalalive</code></a> made their first contribution in <a href="https://redirect.github.com/uuid-rs/uuid/pull/714">uuid-rs/uuid#714</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/uuid-rs/uuid/compare/1.5.0...1.6.0">https://github.com/uuid-rs/uuid/compare/1.5.0...1.6.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="4609e61794"><code>4609e61</code></a> Merge pull request <a href="https://redirect.github.com/uuid-rs/uuid/issues/719">#719</a> from uuid-rs/cargo/1.6.0</li>
<li><a href="24330666ec"><code>2433066</code></a> prepare for 1.6.0 release</li>
<li><a href="9787ea1d0b"><code>9787ea1</code></a> Merge pull request <a href="https://redirect.github.com/uuid-rs/uuid/issues/718">#718</a> from uuid-rs/feat/stabilize-v6-plus</li>
<li><a href="90b0bc0a1c"><code>90b0bc0</code></a> Merge pull request <a href="https://redirect.github.com/uuid-rs/uuid/issues/714">#714</a> from metalalive/doc/fix-v6-links</li>
<li><a href="1eebe7d299"><code>1eebe7d</code></a> bump msrv to 1.60.0</li>
<li><a href="6bade3ae59"><code>6bade3a</code></a> just test lib with miri</li>
<li><a href="3df0aaa80d"><code>3df0aaa</code></a> stabilize UUIDv6-v8 support</li>
<li><a href="003dc57994"><code>003dc57</code></a> doc: fix links to timestamp module</li>
<li>See full diff in <a href="https://github.com/uuid-rs/uuid/compare/1.5.0...1.6.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=uuid&package-manager=cargo&previous-version=1.5.0&new-version=1.6.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions


</details>
2023-11-20 14:43:11 +08:00
Peng Xiao
add20ec2f8 fix(core): blob key issue for cloud blob provider (#4907)
There are some resources that only exists on `/static`. Current prefix check is incorrect since it could start with `/static`
2023-11-20 14:06:24 +08:00
LongYinan
34c5e7d83d build: remove useless source-map-loader to speedup webpack (#4910) 2023-11-20 11:04:57 +08:00
LongYinan
7f09652cca fix(core): handle the getSession network error properly (#4909)
If network offline or API error happens, the `session` returned by the `useSession` hook will be null, so we can't assume it is not null.

There should be following changes:
1. create a page in ErrorBoundary to let the user refetch the session.
2. The `SessionProvider` stop to pull the new session once the session is null, we need to figure out a way to pull the new session when the network is back or the user click the refetch button.
2023-11-20 11:04:39 +08:00
LongYinan
cd291bb60e build: remove useless source-map-loader to speedup webpack (#4910) 2023-11-20 10:52:28 +08:00
Cats Juice
57d42bf491 refactor(core): remove all MUI related components and utilities (#4941) 2023-11-20 10:51:28 +08:00
JimmFly
4ef1f4c046 fix(core): escape cmdk value (#4947)
Co-authored-by: LongYinan <lynweklm@gmail.com>
2023-11-20 10:49:32 +08:00
JimmFly
9bab1b5dff feat(core): keep the latest toast showing when multiple call (#4961) 2023-11-20 10:47:09 +08:00
JimmFly
f09c717413 fix(core): adjust cmdk list scroll padding block (#4972) 2023-11-20 10:39:45 +08:00
Cats Juice
134428f38d style(core): update pro plan card style (#4960) 2023-11-18 00:36:10 +08:00
Cats Juice
ce7a691eef fix(component): stack notification cards expand animation (#4962) 2023-11-18 00:32:06 +08:00
EYHN
5fea0102fb chore: add devcontainer config (#4974)
Co-authored-by: Reese <3253971+figadore@users.noreply.github.com>
2023-11-17 17:54:19 +08:00
JimmFly
ce2eeeffbe feat(i18n): update translation (#4923) 2023-11-17 17:39:33 +08:00
LongYinan
62c0efcfd1 fix(core): handle the getSession network error properly (#4909)
If network offline or API error happens, the `session` returned by the `useSession` hook will be null, so we can't assume it is not null.

There should be following changes:
1. create a page in ErrorBoundary to let the user refetch the session.
2. The `SessionProvider` stop to pull the new session once the session is null, we need to figure out a way to pull the new session when the network is back or the user click the refetch button.
2023-11-17 16:50:48 +08:00
EYHN
aa4c7407de refactor: new provider (#4900) 2023-11-17 15:50:01 +08:00
liuyi
9baad36e41 fix(server): all viewers can share public link (#4968) 2023-11-17 13:48:09 +08:00
liuyi
87248b3337 fix(server): all viewers can share public link (#4968) 2023-11-17 12:34:15 +08:00
Flrande
8b2c3d4c41 chore: bump blocksuite (#4958) 2023-11-16 22:01:03 +08:00
LongYinan
703fad6a0d ci: prevent error if rust build is cached by nx (#4951)
If Rust build was cached by nx, only the output file will be presented. The chmod command will be failed in this case like: https://github.com/toeverything/AFFiNE/actions/runs/6874496337/job/18697360212
2023-11-16 21:57:22 +08:00
Peng Xiao
791eb75ca8 fix(infra): page id compat fix for page ids in workspace.meta (#4950)
since we strip `page:` in keys of workspacedoc.spaces, we should also strip the prefix in meta.pages as well.
2023-11-16 21:57:17 +08:00
Joooye_34
00c940f7df chore: bump affine version to 0.10.2 (#4959) 2023-11-16 15:48:37 +08:00
Flrande
931b459fbd chore: bump blocksuite (#4958) 2023-11-16 14:27:39 +08:00
LongYinan
51e71f4a0a ci: prevent error if rust build is cached by nx (#4951)
If Rust build was cached by nx, only the output file will be presented. The chmod command will be failed in this case like: https://github.com/toeverything/AFFiNE/actions/runs/6874496337/job/18697360212
2023-11-16 10:31:51 +08:00
Peng Xiao
9b631f2328 fix(infra): page id compat fix for page ids in workspace.meta (#4950)
since we strip `page:` in keys of workspacedoc.spaces, we should also strip the prefix in meta.pages as well.
2023-11-15 17:36:08 +08:00
JimmFly
ddd7cab414 feat(core): support share edgeless mode (#4856)
Close #3287

<!--
copilot:all
-->
### <samp>🤖 Generated by Copilot at d3fdf86</samp>

### Summary
📄🚀🔗

<!--
1.  📄 - This emoji represents the page and edgeless modes of sharing a page, as well as the GraphQL operations and types related to public pages.
2.  🚀 - This emoji represents the functionality of publishing and revoking public pages, as well as the confirmation modal and the notifications for the user.
3.  🔗 - This emoji represents the sharing URL and the query parameter for the share mode, as well as the hooks and functions that generate and use the URL.
-->
This pull request adds a feature to the frontend component of AFFiNE that allows the user to share a page in either `page` or `edgeless` mode, which affects the appearance and functionality of the page. It also adds the necessary GraphQL operations, types, and schema to support this feature in the backend, and updates the tests and the storybook stories accordingly.

*  Modify the `useIsSharedPage` hook to accept an optional `shareMode` argument and use the `getWorkspacePublicPagesQuery`, `publishPageMutation`, and `revokePublicPageMutation` from `@affine/graphql`
2023-11-15 16:02:58 +08:00
LongYinan
e7e617a791 chore: change default branch to canary (#4948) 2023-11-15 07:46:50 +00:00
LongYinan
cc2ade601c ci: only disable postinstall on macOS in nightly desktop build (#4938) 2023-11-14 23:03:49 +08:00
Joooye_34
ea4f5ffc83 fix(infra): workspace migration without blockVersions (#4936) 2023-11-14 23:03:40 +08:00
Peng Xiao
9ac8a32e00 perf(component): use png instead of svg for rendering noise svg (#4935) 2023-11-14 23:03:19 +08:00
DarkSky
8d55e5cdf9 fix: change password token check (#4934) (#4932) 2023-11-14 23:03:01 +08:00
LongYinan
01f481a9b6 ci: only disable postinstall on macOS in nightly desktop build (#4938) 2023-11-14 23:00:30 +08:00
Joooye_34
0177ab5c87 fix(infra): workspace migration without blockVersions (#4936) 2023-11-14 14:38:11 +01:00
Peng Xiao
4db35d341c perf(component): use png instead of svg for rendering noise svg (#4935) 2023-11-14 11:52:51 +00:00
DarkSky
3c4a803c97 fix: change password token check (#4934) (#4932) 2023-11-14 11:15:54 +00:00
LongYinan
8bcc886b46 ci: disable postinstall in nightly desktop build (#4930)
Should be part of https://github.com/toeverything/AFFiNE/pull/4885
2023-11-14 14:45:52 +08:00
Peng Xiao
f9971ba922 fix(core): change server url of stable to insider (#4902) (#4926) 2023-11-14 14:40:06 +08:00
LongYinan
5b0b8cf216 test(e2e): add subdoc migration test (#4921)
test(e2e): add subdoc migration test

fix: remove .only
2023-11-14 14:39:59 +08:00
Peng Xiao
16488d594c fix(infra): compatibility fix for space prefix (#4912)
It seems there are some cases that [this upstream PR](https://github.com/toeverything/blocksuite/pull/4747) will cause data loss.

Because of some historical reasons, the page id could be different with its doc id.
It might be caused by subdoc migration in the following (not 100% sure if all white screen issue is caused by it) 0714c12703/packages/common/infra/src/blocksuite/index.ts (L538-L540)

In version 0.10, page id in spaces no longer has prefix "space:"
The data flow for fetching a doc's updates is:
- page id in `meta.pages` -> find `${page-id}` in `doc.spaces` -> `doc` -> `doc.guid`
if `doc` is not found in `doc.spaces`, a new doc will be created and its `doc.guid` is the same with its pageId
- because of guid logic change, the doc that previously prefixed with `space:` will not be found in `doc.spaces`
- when fetching the rows of this doc using the doc id === page id,
  it will return EMPTY since there is no updates associated with the page id

The provided fix in the PR will patch the `spaces` field of the root doc so that after 0.10 the page doc can still be found in the `spaces` map. It shall apply to both of the idb & sqlite datasources.

Special thanks to @lawvs 's db file for investigation!
2023-11-14 14:39:50 +08:00
liuyi
c44a9a4903 fix(server): wrap updates applying in a transaction (#4922) 2023-11-14 14:39:39 +08:00
LongYinan
05154dc7ca ci: disable postinstall in nightly desktop build (#4930)
Should be part of https://github.com/toeverything/AFFiNE/pull/4885
2023-11-14 14:13:55 +08:00
Peng Xiao
c90b477f60 fix(core): change server url of stable to insider (#4902) (#4926) 2023-11-14 12:05:52 +08:00
Peng Xiao
76b585d1ef fix(storybook): page tags display (#4924) 2023-11-14 09:45:12 +08:00
李华桥
6f18ddbe85 v0.10.1 2023-11-13 19:49:26 +08:00
LongYinan
dde779a71d test(e2e): add subdoc migration test (#4921)
test(e2e): add subdoc migration test

fix: remove .only
2023-11-13 18:00:40 +08:00
Peng Xiao
bd9f66fbc7 fix(infra): compatibility fix for space prefix (#4912)
It seems there are some cases that [this upstream PR](https://github.com/toeverything/blocksuite/pull/4747) will cause data loss.

Because of some historical reasons, the page id could be different with its doc id.
It might be caused by subdoc migration in the following (not 100% sure if all white screen issue is caused by it) 0714c12703/packages/common/infra/src/blocksuite/index.ts (L538-L540)

In version 0.10, page id in spaces no longer has prefix "space:"
The data flow for fetching a doc's updates is:
- page id in `meta.pages` -> find `${page-id}` in `doc.spaces` -> `doc` -> `doc.guid`
if `doc` is not found in `doc.spaces`, a new doc will be created and its `doc.guid` is the same with its pageId
- because of guid logic change, the doc that previously prefixed with `space:` will not be found in `doc.spaces`
- when fetching the rows of this doc using the doc id === page id,
  it will return EMPTY since there is no updates associated with the page id

The provided fix in the PR will patch the `spaces` field of the root doc so that after 0.10 the page doc can still be found in the `spaces` map. It shall apply to both of the idb & sqlite datasources.

Special thanks to @lawvs 's db file for investigation!
2023-11-13 17:57:56 +08:00
liuyi
92f1f40bfa fix(server): wrap updates applying in a transaction (#4922) 2023-11-13 08:49:30 +00:00
dependabot[bot]
993974d20d chore: bump the all-cargo-dependencies group with 5 updates (#4918)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-13 16:01:07 +08:00
LongYinan
f17c0e1268 Merge pull request #4915 from toeverything/chore/hotfix-back-to-master
fix: cherry pick hotfix back to master
2023-11-13 10:12:06 +08:00
Whitewater
eded501123 fix: get page preview based on block order (#4888)
Co-authored-by: Peng Xiao <pengxiao@outlook.com>
2023-11-12 15:09:57 +00:00
DarkSky
ac3756ea23 chore: cleanup deployment 2023-11-12 11:22:21 +08:00
forehalo
dc8e84df31 fix(server): increase server acceptable websocket payload size 2023-11-12 11:22:00 +08:00
LongYinan
48dc1049b3 Merge pull request #4913 from toeverything/darksky/cleanup-depolyment
chore: cleanup deployment
2023-11-12 11:20:02 +08:00
Peng Xiao
a8d89254ce fix(electron): dev reload (#4911) 2023-11-12 03:19:27 +00:00
DarkSky
9add530370 chore: cleanup deployment 2023-11-12 11:03:25 +08:00
LongYinan
b77460d871 Merge pull request #4908 from toeverything/61/hotfix-websocket-payload
fix(server): increase server acceptable websocket payload size
2023-11-10 22:01:48 +08:00
forehalo
42db41776b fix(server): increase server acceptable websocket payload size 2023-11-10 21:31:45 +08:00
李华桥
7525126d89 fix(core): change server url of stable to insider 2023-11-10 20:07:18 +08:00
李华桥
075439c74f fix(core): change server url of stable to insider 2023-11-10 18:32:53 +08:00
Joooye_34
30bac7dce2 ci(core): eslint errors for core (#4662) 2023-11-10 10:25:59 +00:00
Joooye_34
b98a258083 fix(core): change server url of stable to insider (#4902) 2023-11-10 15:46:31 +08:00
Yifeng Wang
28177657ef chore: bump theme (#4904)
Co-authored-by: 李华桥 <joooye1991@gmail.com>
2023-11-10 15:42:06 +08:00
Yifeng Wang
fc6c553ece chore: bump theme (#4904)
Co-authored-by: 李华桥 <joooye1991@gmail.com>
2023-11-10 15:40:38 +08:00
Joooye_34
59cb3d5df1 fix(core): change server url of stable to insider (#4902) 2023-11-10 14:50:57 +08:00
Peng Xiao
5c2d958e2b fix(core): svg blob syncing issue (#4886) 2023-11-10 05:32:51 +00:00
Yifeng Wang
2117d6b232 chore: bump blocksuite (#4901) 2023-11-10 13:23:29 +08:00
李华桥
5c48c83301 v0.10.0 2023-11-10 11:42:04 +08:00
fourdim
af7d331610 feat(core): add global loading state (#4840) 2023-11-10 03:25:05 +00:00
JimmFly
1fe5a0fffa feat(core): auto select block when jump to block (#4858)
Co-authored-by: Peng Xiao <pengxiao@outlook.com>
2023-11-10 03:02:56 +00:00
fourdim
f1e32aab66 fix(hooks): push success toast after save (#4830) 2023-11-10 02:40:52 +00:00
liuyi
7698a6ac8e chore(server): bump octobase versions (#4893)
Co-authored-by: DarkSky <darksky2048@gmail.com>
2023-11-10 02:25:28 +00:00
李华桥
063f5a683e v0.10.0-canary.16 2023-11-10 10:10:54 +08:00
Peng Xiao
fd74776abe fix(core): disable sync doc/blob on start (#4897) 2023-11-09 18:00:53 +00:00
LongYinan
9d89e4f7f5 fix(server): earlyAccessPreview env override (#4898) 2023-11-09 17:20:41 +00:00
LongYinan
7182b85bd0 chore: add performance logger (#4896)
Add more performance log to debug loading problem
2023-11-10 00:42:30 +08:00
LongYinan
91f1d0081b ci: adjust the beta cluster configuration (#4895)
- Scale the pod counts to 10
- Set `earlyAccessPreview` to false
2023-11-10 00:42:19 +08:00
李华桥
09c3a8828f chore: add performance logger 2023-11-10 00:33:48 +08:00
LongYinan
927a6489f9 ci: adjust the beta cluster configuration 2023-11-10 00:22:45 +08:00
李华桥
075eead9fa v0.10.0-canary.15 2023-11-09 23:27:12 +08:00
Joooye_34
15b0c127f5 chore: disable outline, copilot and hello world plugin (#4894) 2023-11-09 14:53:45 +00:00
Peng Xiao
d0b014543c fix(core): select all in page list group header does not need to enable selection (#4869) 2023-11-09 12:01:36 +00:00
LongYinan
839f500979 ci: disable postinstall on macOS build (#4885) 2023-11-09 11:58:55 +00:00
Joooye_34
af72bf0f69 feat(core): support signup set password before goto stripe payment url (#4892) 2023-11-09 11:58:16 +00:00
liuyi
405167854b perf(server): avoid auto select blob data when upsert (#4891) 2023-11-09 10:45:31 +00:00
liuyi
f9654bb1f8 feat(core): unify all new created page IDs to nanoid (#4884) 2023-11-09 09:22:02 +00:00
EYHN
e3e0553c56 fix(hooks): missing page preview and references (#4863) 2023-11-09 08:59:36 +00:00
liuyi
248fb1fa69 fix(server): token set with id instead of email (#4883) 2023-11-09 08:23:03 +00:00
Joooye_34
12a2ccf1a5 fix(core): visit /signin pay when already logged and subscribed (#4882)
Co-authored-by: Peng Xiao <pengxiao@outlook.com>
2023-11-09 07:38:52 +00:00
Peng Xiao
227b8b0061 fix(component): dragged component disappear when dragging (#4870) 2023-11-09 06:20:15 +00:00
Peng Xiao
442115632b chore(component): bump bs (#4880) 2023-11-09 04:47:10 +00:00
Peng Xiao
ad82376890 chore: update delete icon (#4871) 2023-11-09 02:49:48 +00:00
Joooye_34
a8bff81a7e ci: use resolutions to resolve building error in mac-os (#4878) 2023-11-09 02:43:47 +00:00
3720
da2821eaac fix(core): collections data migration without blocking data reads (#4866) 2023-11-08 12:38:43 +00:00
Joooye_34
c55565ee71 chore: update changelog url (#4868) 2023-11-08 12:08:24 +00:00
EYHN
ecd5db2952 ci: add oxlint (#4867) 2023-11-08 17:54:41 +08:00
Joooye_34
744cd47481 v0.10.0-canary.14 2023-11-08 15:36:54 +08:00
Hongtao Lye
12c72e63b1 chore: bump blocksuite version (#4862) 2023-11-08 07:12:48 +00:00
EYHN
b7edaab387 chore: fix code style issues (#4857) 2023-11-08 05:51:08 +00:00
JimmFly
83472cc682 fix(core): duplicate registration in edgeless (#4864) 2023-11-08 02:47:46 +00:00
3720
08e7fa3486 fix(core): improve the UX for edit collection (#4827)
Co-authored-by: Peng Xiao <pengxiao@outlook.com>
2023-11-07 10:18:56 +00:00
liuyi
7305530d97 fix(server): wrong data migration (#4855) 2023-11-07 09:20:42 +00:00
liuyi
b99ac51624 chore(server): decrease amount of batch updates merging (#4860) 2023-11-07 09:18:02 +00:00
liuyi
01d7fe4597 fix(server): avoid saving invalid data (#4859) 2023-11-07 08:27:17 +00:00
singhjatin297
ac0a3aab3e docs: update CONTRIBUTING.md (#4848) 2023-11-07 16:29:45 +08:00
singhjatin297
7fe23a31b2 docs: update behind-the-code.md (#4849) 2023-11-07 06:17:01 +00:00
Joooye_34
f50b8002b3 v0.10.0-canary.13 2023-11-07 09:55:30 +08:00
Joooye_34
7a4669a6aa feat(core): implement parts of workspace upgrade design (#4850) 2023-11-06 17:02:25 +00:00
Joooye_34
8554d5d791 v0.10.0-canary.12 2023-11-07 00:13:51 +08:00
Peng Xiao
da9934fbdc fix(electron): add search to proxied url (#4852) 2023-11-06 14:47:44 +00:00
liuyi
cb6974f263 fix(server): avoid server overloading by too many updates (#4846) 2023-11-06 10:21:19 +00:00
liuyi
f75684d6f6 fix(server): failed to share again if disable once (#4844) 2023-11-06 10:19:21 +00:00
JimmFly
acf0734c95 fix(core): adjust discount display (#4847) 2023-11-06 10:17:24 +00:00
Peng Xiao
cfffcad1b8 fix(core): adapt blob in sqlite for svg type (#4845) 2023-11-06 10:09:48 +00:00
Joooye_34
3b74ff2b92 v0.10.0-canary.11 2023-11-06 18:10:28 +08:00
JimmFly
2b34e1a9cd feat(core): adjust filter logic (#4831) 2023-11-06 06:41:18 +00:00
JimmFly
9664d142ad feat(core): temporary expansion files are limited to 100M (#4833) 2023-11-06 06:38:46 +00:00
JimmFly
e7106b7393 chore(i18n): adjust text (#4832) 2023-11-06 06:36:49 +00:00
liuyi
f491ff94cc refactor(server): separate page visibility from workspace permission (#4836) 2023-11-06 03:49:39 +00:00
dependabot[bot]
e8987457ab chore: bump the all-cargo-dependencies group with 1 update (#4841)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-06 03:43:18 +00:00
Cats Juice
7a8150398c feat(core): payment plans error boundary (#4744) 2023-11-06 02:42:21 +00:00
Cats Juice
3c4dbed16b fix(core): payment UI fix (#4839) 2023-11-06 02:40:52 +00:00
Peng Xiao
7e381e830a perf(core): load all pages after 10s (#4834) 2023-11-03 09:42:29 +00:00
Cats Juice
61dc4a56f9 fix(core): implement pricing plans scroll area with radix (#4824) 2023-11-03 06:16:25 +00:00
JimmFly
788c445f2b chore(i18n): add new key for empty trash page (#4829) 2023-11-03 06:15:13 +00:00
Joooye_34
97db941749 fix(core): remove responsive editor width (#4821) 2023-11-03 04:47:51 +00:00
liuyi
f6cfe7c8a1 fix(server): only treat active subscription as existing (#4826) 2023-11-03 04:00:58 +00:00
Joooye_34
fb0aaabe53 v0.10.0-canary.10 2023-11-02 23:40:34 +08:00
Cats Juice
de33967a73 fix(component): flex setting-modal header & footer (#4818) 2023-11-02 15:11:20 +00:00
Peng Xiao
65321e39cc refactor(component): virtual rendering page list (#4775)
Co-authored-by: Joooye_34 <Joooye1991@gmail.com>
2023-11-02 14:21:01 +00:00
liuyi
a3906bf92b fix(server): do not return subscription if not active (#4820) 2023-11-02 14:17:26 +00:00
JimmFly
0a88be7771 feat(core): add jump to block for cmdk (#4802) 2023-11-02 11:49:49 +00:00
3720
7068d5f38a feat(core): remove mode and pages field from Collection (#4817) 2023-11-02 10:47:43 +00:00
LongYinan
bf17b4789b Merge pull request #4819 from toeverything/61/hotfix
fix(server): remove awareness states cache
2023-11-02 18:31:19 +08:00
forehalo
5e9efbffa3 fix(server): page variant may exist 2023-11-02 18:25:30 +08:00
forehalo
7e516236f5 fix(workspace): remove awareness states cache 2023-11-02 18:24:00 +08:00
Hongtao Lye
15024c6c8a chore: bump blocksuite (#4801) 2023-11-02 09:12:23 +00:00
liuyi
6a93203d68 feat(server): sync data with ack (#4791) 2023-11-02 09:05:28 +00:00
JimmFly
af9663d3e7 fix(core): adjust payment related text (#4797) 2023-11-02 08:22:54 +00:00
EYHN
1d7e3dd570 fix(core): currentUser undefined when all workspaces deleted (#4812) 2023-11-02 07:20:08 +00:00
EYHN
6ef02fbc38 fix(core): fix block suite edit mode switch shortcut (#4813) 2023-11-02 07:17:44 +00:00
EYHN
604c3da9fe docs: change yarn version in BUILDING.md (#4811) 2023-11-02 03:56:02 +00:00
Joooye_34
75c8dd75e3 v0.10.0-canary.9 2023-11-01 22:03:47 +08:00
Cats Juice
e5c86a9249 fix(core): billing cancel confirm dialog (#4795) 2023-11-01 09:38:43 +00:00
3720
e5be570f54 fix(core): text color of search input, style for the multi-select tag, date-picker autofocus issue (#4799) 2023-11-01 09:28:00 +00:00
LongYinan
6aaf550241 ci: fix wrangler deploy (#4803) 2023-11-01 09:12:00 +00:00
LongYinan
2d62ec72a7 ci: dynamic setup server deploy environment by release tag (#4790) 2023-11-01 07:38:15 +00:00
Joooye_34
0273ea8b00 v0.10.0-canary.8 2023-11-01 00:03:04 +08:00
JimmFly
97d189f1c8 fix(component): adjust autofill style (#4773) 2023-10-31 15:30:35 +00:00
Joooye_34
db36f81d24 feat(core): support subscribe plan after login (#4788) 2023-10-31 15:29:23 +00:00
DarkSky
c30d2550ff fix: request prevented when re-downgrade subscribe (#4786) 2023-10-31 11:17:28 +00:00
Cats Juice
93e286177f feat(core): billing history pagination (#4787) 2023-10-31 09:47:59 +00:00
Peng Xiao
8ca53326a7 fix(electron): update app icons (#4789)
Co-authored-by: Joooye_34 <Joooye1991@gmail.com>
2023-10-31 09:09:42 +00:00
Cats Juice
7d6c096462 feat(core): payment billing loading (#4763) 2023-10-31 09:00:19 +00:00
Peng Xiao
f08408ebe5 fix(core): possible crash issues (#4783) 2023-10-31 08:56:14 +00:00
Cats Juice
0ad0ab50d0 fix(core): adjust setting-modal layout to center tip (#4776) 2023-10-31 08:10:41 +00:00
Joooye_34
563863005f v0.10.0-canary.6 2023-10-31 14:40:47 +08:00
DarkSky
57d71ad6cf fix: idempotencyKey used (#4774) 2023-10-31 05:26:42 +00:00
Joooye_34
37ec552f74 v0.10.0-canary.5 2023-10-31 11:54:25 +08:00
Peng Xiao
9e3c79526c fix(core): selectAtom crash on isEqual (#4780) 2023-10-31 03:29:18 +00:00
JimmFly
a015dc42bb feat(core): support fuzzy highlighting (#4765) 2023-10-31 03:02:53 +00:00
Joooye_34
17afe218fe v0.10.0-canary.4 2023-10-31 01:49:15 +08:00
Joooye_34
7b204cc611 Merge pull request #4766 from toeverything/chore/stripe-env
chore(server): add stripe env to deploy.yml
2023-10-30 17:25:23 +00:00
Joooye_34
9102f1f9a9 Merge pull request #4768 from toeverything/zzj/new-collections-i18n
feat(i18n): add i18n support for new collections
2023-10-30 16:39:14 +00:00
LongYinan
f6b53a1167 Merge pull request #4770 from toeverything/fix-html-template
fix(core): description field in html template
2023-10-30 16:37:45 +00:00
Joooye_34
6fcdb05925 chore(server): add stripe env 2023-10-31 00:12:17 +08:00
LongYinan
99b35c7a93 ci: reduce deployment events on pull requests 2023-10-30 19:57:54 +08:00
LongYinan
fc27a2e906 fix(core): description field in html template 2023-10-30 19:57:54 +08:00
liuyi
b4d8f1428c Merge pull request #4771 from toeverything/jimmfly/1030/add-i18n
feat(i18n): add new key for billing
2023-10-30 10:56:51 +00:00
JimmFly
1b0c604c02 feat(i18n): add new key for billing 2023-10-30 17:46:48 +08:00
LongYinan
581635f40b Merge pull request #4709 from toeverything/61/doc-perf
perf(server): opmitize updates table
2023-10-30 09:27:19 +00:00
LongYinan
d752086846 Merge pull request #4767 from toeverything/61/fix/data-migration
fix(server): wrong prod data migration scripts filter
2023-10-30 09:21:37 +00:00
zzj3720
f23ec9063c feat(collections): completion i18n 2023-10-30 17:06:59 +08:00
forehalo
26b953ce57 fix(server): wrong prod data migration scripts filter 2023-10-30 16:50:39 +08:00
LongYinan
72babe9157 Merge pull request #4751 from toeverything/yarn-4.0.1
chore: upgrade to yarn@4.0.1
2023-10-30 07:49:29 +00:00
LongYinan
b6ca81821e Merge pull request #4758 from toeverything/dependabot/cargo/all-cargo-dependencies-114856a603
chore: bump the all-cargo-dependencies group with 1 update
2023-10-30 07:46:35 +00:00
DarkSky
f11cc40ae3 Merge pull request #4410 from toeverything/payment-system
feat: payment system
2023-10-30 07:15:52 +00:00
DarkSky
95c1a44a0d Merge branch 'master' into payment-system 2023-10-30 01:55:51 -05:00
LongYinan
198befb006 Merge pull request #4748 from fourdim/doc-0627
docs: update README.md
2023-10-30 06:37:25 +00:00
LongYinan
fc3516acfb Merge pull request #4746 from toeverything/data-migrations-system
feat(server): add data migration system
2023-10-30 06:36:18 +00:00
DarkSky
de9e7f97a4 feat: add idempotent request support for payment apis (#4753) 2023-10-30 05:54:09 +00:00
dependabot[bot]
05ad6eb450 chore: bump the all-cargo-dependencies group with 1 update
Bumps the all-cargo-dependencies group with 1 update: [serde](https://github.com/serde-rs/serde).

- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.189...v1.0.190)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-cargo-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-30 03:39:55 +00:00
forehalo
98d0ac3c90 feat(server): add data migration system 2023-10-30 11:12:09 +08:00
Flrande
2aa4b4c1f3 fix: remove awareness state before window unload (#4752) 2023-10-29 19:50:18 +00:00
DarkSky
3798293d3e fix: error handle in payment resolver (#4754) 2023-10-30 00:33:21 +08:00
LongYinan
fd76d33421 chore: upgrade to yarn@4.0.1 2023-10-29 16:06:29 +08:00
dependabot[bot]
2a4495f7ee ci: bump cloudflare/wrangler-action from 3.3.1 to 3.3.2 (#4717)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-28 12:08:49 +00:00
DarkSky
1775138228 feat: bump up blob size limit temporarily (#4747) 2023-10-28 07:51:02 +00:00
Cats Juice
8c194ab8b0 feat(core): confirm before cancel in billing page (#4749) 2023-10-28 14:12:25 +08:00
fourdim
5ba1c0dbdb fix: prettier issue 2023-10-28 00:50:03 -04:00
fourdim
59ec122940 docs: update README.md 2023-10-27 21:42:17 -04:00
DarkSky
588f63505d fix: password reset token (#4743) 2023-10-27 09:52:29 +00:00
3720
ef8024c657 feat: new collections (#4530)
Co-authored-by: Peng Xiao <pengxiao@outlook.com>
2023-10-27 09:06:59 +00:00
Joooye_34
abbd8235aa chore(core): enable payment in canary (#4745) 2023-10-27 16:59:13 +08:00
Joooye_34
385de7d33b Merge remote-tracking branch 'origin/master' into payment-system 2023-10-27 16:04:48 +08:00
Cats Juice
87571a0879 chore(core): replace setting-modal sidebar icons (#4742) 2023-10-27 15:59:41 +08:00
JimmFly
af24334264 feat(core): add upgrade success page (#4738) 2023-10-27 15:49:32 +08:00
Joooye_34
9fc0152cb1 fix: directory error when publish npm lib (#4735) 2023-10-27 07:08:54 +00:00
JimmFly
35dbbe561a feat(core): adjust member tips (#4737) 2023-10-27 14:25:48 +08:00
liuyi
50563dcb6e feat(server): auto attach early access coupon (#4728) 2023-10-27 10:28:22 +08:00
Cats Juice
edb6e0fd69 feat(core): pricing plans actions (#4724) 2023-10-26 22:00:14 +08:00
liuyi
9334a363c7 chore(server): upgrade stripe sdk (#4733) 2023-10-26 12:37:52 +00:00
JimmFly
e0f7ac426c feat(core): add translation key for payment (#4723) 2023-10-26 16:15:12 +08:00
Joooye_34
1deb6bffd3 feat(core): disable payment in canary (#4722) 2023-10-26 00:50:39 +08:00
JimmFly
ae6376edee chore(core): update communities icon (#4719) 2023-10-25 10:45:54 +00:00
JimmFly
780c164cc8 fix(core): retrieve missing search result titles (#4718) 2023-10-25 10:45:14 +00:00
JimmFly
df69c908fe feat(core): adapt storage progress to payment system (#4713) 2023-10-25 16:18:30 +08:00
Cats Juice
eaa90c9fb6 feat(core): payment plans skeleton (#4715) 2023-10-25 16:16:50 +08:00
Cats Juice
e8a88da9e4 feat(core): auto scroll to current payment plan (#4714) 2023-10-25 10:58:19 +08:00
joooye34
559ec3956f v0.10.0-canary.3 2023-10-24 20:18:21 +08:00
Cats Juice
3749125907 feat(core): full width scroll area for plans (#4708) 2023-10-24 18:38:31 +08:00
liuyi
97d06432f0 fix(server): wrong invoice recurring value saved (#4712) 2023-10-24 18:32:52 +08:00
LongYinan
b13705ba3d ci: remove setup-maker to reduce release duration (#4710) 2023-10-24 10:09:40 +00:00
JimmFly
df77ffde9a feat(core): add account subscription status (#4707) 2023-10-24 18:05:56 +08:00
forehalo
ef1228dcb4 perf(server): opmitize updates table 2023-10-24 17:54:59 +08:00
dependabot[bot]
551287ab44 ci: bump actions/upload-artifact from 2 to 3 (#4701)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-24 09:36:07 +00:00
dependabot[bot]
113105181d ci: bump docker/login-action from 2 to 3 (#4702)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-24 09:01:09 +00:00
Flrande
627e5dfbb5 feat: bump blocksuite (#4706) 2023-10-24 09:00:27 +00:00
Joooye_34
21604a2cad Merge pull request #4699 from toeverything/61/subscription-edge-case
fix(server): subscription edge case
2023-10-24 16:11:37 +08:00
dependabot[bot]
fd6536ea90 ci: bump actions/checkout from 2 to 4 (#4700)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-24 06:00:00 +00:00
dependabot[bot]
a42d218962 ci: bump actions/setup-node from 3 to 4 (#4704)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-24 05:59:16 +00:00
JimmFly
5226d6c568 feat(core): add search result highlighting (#4667)
Co-authored-by: Peng Xiao <pengxiao@outlook.com>
2023-10-24 05:54:37 +00:00
dependabot[bot]
14bee1811c ci: bump docker/setup-qemu-action from 2 to 3 (#4703)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-24 05:13:58 +00:00
forehalo
7ecee01d20 fix(server): respond stripe webhook immediately 2023-10-24 12:09:14 +08:00
forehalo
2e4f6ef2ed feat(server): combine plan and recurring as stripe lookup key 2023-10-24 12:09:14 +08:00
forehalo
9b43380b05 fix(server): cancel scheduled subscription 2023-10-24 12:09:14 +08:00
liuyi
303dade2ef fix cancel subscription edge cases (#4691) 2023-10-24 11:40:46 +08:00
liuyi
113b20f669 fix(core): payment ui issues (#4672) 2023-10-24 11:40:46 +08:00
liuyi
95d37fc63f refactor(core): make subscription hook (#4669) 2023-10-24 11:40:46 +08:00
liuyi
858a1da35f feat(core): impl billing settings (#4652) 2023-10-24 11:40:46 +08:00
forehalo
1d62133f4f feat(core): impl subscription plans setting 2023-10-24 11:40:46 +08:00
forehalo
df054ac7f6 feat(core): payment backend 2023-10-24 11:40:44 +08:00
Alex Yang
493b815b7b fix(plugin-cli): use relative path (#4698) 2023-10-23 12:14:40 -05:00
dependabot[bot]
e75a0743f8 chore: bump @mui/material from 5.14.13 to 5.14.14 (#4678)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-23 08:07:04 +00:00
dependabot[bot]
5573afc8d5 ci: bump styfle/cancel-workflow-action from 0.11.0 to 0.12.0 (#4686)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-23 07:39:51 +00:00
dependabot[bot]
8b703fd9ad ci: bump docker/build-push-action from 4 to 5 (#4690)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-23 07:23:04 +00:00
dependabot[bot]
2a97194c75 ci: bump docker/setup-buildx-action from 2 to 3 (#4689)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-23 07:22:34 +00:00
dependabot[bot]
13b6bb7ee3 ci: bump kentaro-m/auto-assign-action from 1.2.4 to 1.2.5 (#4687)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-23 07:22:02 +00:00
dependabot[bot]
43220f6db6 ci: bump cloudflare/wrangler-action from 2.0.0 to 3.3.1 (#4688)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-23 07:21:54 +00:00
dependabot[bot]
b52e006bfe chore: bump @mui/icons-material from 5.14.13 to 5.14.14 (#4680)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-23 07:04:49 +00:00
Peng Xiao
a8b10c64b8 fix(component): disable CMDK list animation (#4685) 2023-10-23 06:46:11 +00:00
dependabot[bot]
9d6b335829 chore: bump @aws-sdk/client-s3 from 3.428.0 to 3.433.0 (#4682)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-23 06:38:37 +00:00
LongYinan
9820c80ee2 ci: dependabot group config 2023-10-23 15:03:50 +08:00
LongYinan
1dd17c6334 ci: fix electron app nightly build workflow (#4684) 2023-10-23 06:30:10 +00:00
LongYinan
b6d21c0945 fix(storage): rustix security fix (#4677) 2023-10-23 03:00:15 +00:00
LongYinan
aba771e99c feat: upgrade to yarn@4 (#4676) 2023-10-23 02:55:45 +00:00
Alex Yang
d9e2d17a26 refactor: cleanup code 2023-10-20 19:28:04 -05:00
DarkSky
779ac39b36 fix(server): captcha guard (#4673) 2023-10-20 08:54:08 +00:00
Peng Xiao
817463c40e feat(component): checkbox (#4665) 2023-10-20 03:46:44 +00:00
LongYinan
890905ed0e ci: fix electron make script on Windows 2023-10-19 22:44:55 +08:00
LongYinan
54aad58388 ci: fix electron make script on Windows (#4666) 2023-10-19 13:00:11 +00:00
LongYinan
37c6560dd6 v0.10.0-canary.2 2023-10-19 17:18:18 +08:00
LongYinan
352420b881 build: fix native module in aarch64 docker image (#4656) 2023-10-19 08:59:02 +00:00
Joooye_34
61d9958a4c feat(core): change favicon (#4663) 2023-10-19 08:46:59 +00:00
liuyi
7275d417b2 fix(server): avoid workspace subdoc guid conflict (#4664) 2023-10-19 08:12:57 +00:00
Joooye_34
d835be90cb chore: prettier ignore file generated by napi-rs (#4661) 2023-10-19 07:17:04 +00:00
JimmFly
c54489ba6e fix: wrong confirm button position (#4657) 2023-10-19 03:16:12 +00:00
Alex Yang
9958baa843 fix(hooks): use uuid as pageId (#4658) 2023-10-18 22:14:41 -05:00
Alex Yang
97d8660a54 refactor(electron): fix vitest and add behavior test (#4655) 2023-10-18 22:14:30 -05:00
Alex Yang
b14a6bc29e build: fix generate-assets.ts 2023-10-18 16:33:09 -05:00
Alex Yang
1d29c26284 docs: update README.md 2023-10-18 15:33:28 -05:00
Joooye_34
bed9310519 refactor(infra): directory structure (#4615) 2023-10-18 15:30:08 +00:00
Flrande
814d552be8 feat: bump blocksuite and fix bug in migration (#4653)
Co-authored-by: JimmFly <yangjinfei001@gmail.com>
2023-10-18 14:45:07 +00:00
DarkSky
63ca9671be feat: add captcha support for sign in/up (#4582) 2023-10-18 08:06:07 +00:00
Alex Yang
524e48c8e6 refactor(electron): typescript check on build scripts (#4650) 2023-10-18 06:13:47 +00:00
Peng Xiao
9b3e6bf1f5 fix(docs): update docs 2023-10-18 14:00:44 +08:00
Peng Xiao
4135cfd243 fix(docs): remove secret in readme 2023-10-18 13:45:41 +08:00
Alex Yang
be6bcfdb9a chore: fix version 2023-10-17 20:11:09 -05:00
Alex Yang
bb046a12dc refactor(native): remove unused code (#4651) 2023-10-17 18:19:06 -05:00
Alex Yang
a430266389 refactor: use jotai-effect (#4641) 2023-10-17 16:09:37 -05:00
Peng Xiao
62d2b09e3c fix(core): remove redundant providers (#4648) 2023-10-17 20:24:41 +00:00
LongYinan
e831f612e8 fix(electron): release desktop app workflow (#4594) 2023-10-17 06:15:55 +00:00
liuyi
01987990ee fix: make server guid consistent (#4341) 2023-10-17 02:34:13 +00:00
Alex Yang
b3dc4dce9c test: await setTimeout (#4643) 2023-10-16 21:14:45 -05:00
Peng Xiao
5e9eeaddbd build(electron): allow customizing channel type for internal build (#4511) 2023-10-17 01:15:19 +00:00
Alex Yang
a0095496d7 chore: release y-provider package (#4642) 2023-10-16 19:47:05 -05:00
Alex Yang
77efcad89d v0.10.0-canary.1 2023-10-16 16:30:17 -05:00
Alex Yang
1d06114f00 build(y-indexeddb): fix output package.json (#4640) 2023-10-16 16:25:09 -05:00
dependabot[bot]
579fa1ae4c chore: bump @faker-js/faker from 8.1.0 to 8.2.0 (#4631)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-16 19:23:30 +00:00
Peng Xiao
fea6b81a53 fix(electron): app image icon (#4442) 2023-10-16 15:54:28 +00:00
Alex Yang
7911d67439 chore: bump version (#4604) 2023-10-16 12:47:06 +00:00
JimmFly
efca651429 feat(core): add history shortcut (#4595) 2023-10-16 12:27:06 +00:00
JimmFly
07b5d18441 feat(core): add sign out confirm modal (#4592) 2023-10-16 08:44:09 +00:00
electron97
c1d386d932 fix: disabled form item in the settings can still be operated via keyboard (#4605) 2023-10-16 05:14:11 +00:00
Alex Yang
710a2f2c97 chore: bump version 2023-10-13 15:40:07 -05:00
JimmFly
2e1e486bc6 chore: bump playwright version (#4602) 2023-10-13 14:37:18 -05:00
Alex Yang
227017625d test(core): fix flaky (#4597) 2023-10-13 13:46:32 -05:00
Joooye_34
6ea10860b4 refactor(infra): record legacy data to improve testing stability (#4590) 2023-10-13 03:03:42 +00:00
LongYinan
286347420d 0.10.0-canary.0 2023-10-12 15:21:07 +08:00
JimmFly
daa976ca62 fix(component): adjust dialog and input style (#4566) 2023-10-12 05:49:39 +00:00
Alex Yang
d05897b724 chore: prohibit unnecessary await (#4586) 2023-10-12 05:04:58 +00:00
JimmFly
5ebd82dc04 feat(core): adjust share menu style (#4584) 2023-10-12 03:26:13 +00:00
Alex Yang
ae4322b75f chore: bump version (#4587) 2023-10-12 02:46:52 +00:00
Alex Yang
a0e6ff9bd1 test: fix migration (#4588) 2023-10-11 19:30:37 -05:00
Alex Yang
491cd75fe0 fix(infra): create template workspace with isolated nanoid (#4569) 2023-10-11 13:48:30 -05:00
LongYinan
5be5863693 chore: upgrade yarn@3.6.4 (#4585) 2023-10-11 17:23:22 +00:00
LongYinan
23abb97ccb fix: dependabot security issues (#4579) 2023-10-11 07:36:45 +00:00
dependabot[bot]
b09d5f3e18 chore: bump @types/eslint from 8.44.2 to 8.44.3 (#4551)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-11 07:35:41 +00:00
Alex Yang
a731499024 chore: remove unused file (#4576) 2023-10-11 06:05:21 +00:00
Alex Yang
8f5ee9234c test: remove deprecated api usage (#4577) 2023-10-11 06:05:06 +00:00
JimmFly
1f6a105e5c feat(core): add setting commands (#4568)
Co-authored-by: Peng Xiao <pengxiao@outlook.com>
2023-10-11 03:31:04 +00:00
LongYinan
b1eb926b7b style: enable no-extraneous-dependencies lint rule (#4575) 2023-10-10 17:51:47 +00:00
Alex Yang
c8d1de3a59 chore: bump version (#4571) 2023-10-10 17:43:41 +00:00
dependabot[bot]
aa7e0dd85b chore: bump @vitest/ui from 0.34.5 to 0.34.6 (#4553)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-10 03:47:44 +00:00
liuyi
0092a19812 refactor(server): deprecate unstable redis manager (#4567) 2023-10-10 03:23:12 +00:00
dependabot[bot]
4a6cfedc4a chore: bump react-i18next from 13.2.1 to 13.2.2 (#4562)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-10 03:11:07 +00:00
dependabot[bot]
8c97fd1d28 chore: bump sinon from 16.0.0 to 16.1.0 (#4563)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-10 03:10:21 +00:00
Peng Xiao
d9fe3e73d5 fix: list page storybook not rendering issue (#4560) 2023-10-09 07:54:05 +00:00
dependabot[bot]
59a4b3bc31 chore: bump electron from 26.2.2 to 26.3.0 (#4564)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-09 07:14:50 +00:00
Zero King
0161c98d65 chore: reword template galleries introduction (#4548) 2023-10-06 16:11:08 +08:00
dependabot[bot]
d3ffa2c5f2 chore: bump marked from 7.0.5 to 9.0.3 (#4554)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 15:28:38 +00:00
dependabot[bot]
0a6859a1d7 chore: bump esbuild from 0.19.3 to 0.19.4 (#4550)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-03 15:28:10 +00:00
JimmFly
69db99636b feat(core): add editor commanads (#4514)
Co-authored-by: Peng Xiao <pengxiao@outlook.com>
2023-10-02 03:22:12 +00:00
Alex Yang
aab1a1e50a fix(electron): output check (#4547) 2023-09-30 02:02:17 -05:00
Alex Yang
19646a97af fix: twitter preview (#4545) 2023-09-30 01:19:54 -05:00
Qinghao Huang
f59a35d8d2 fix: spacing issue in getting-started template (#4540) 2023-09-30 01:02:43 -05:00
Alex Yang
c911806062 refactor: remove bookmark plugin (#4544) 2023-09-30 00:48:33 -05:00
Qinghao Huang
b440c3a820 docs: update CLA.md (#4541) 2023-09-30 00:26:50 +00:00
Alex Yang
98cabc44e4 ci: remove unstable nx.yml (#4543) 2023-09-29 18:57:31 -05:00
LongYinan
dd94ea5b45 ci: speedup ci by reduce installation packages in certain job (#4457) 2023-09-29 03:02:26 +00:00
Joooye_34
b012e615ba fix(component): content should subtract height of the header (#4507) 2023-09-28 07:04:12 +00:00
Alex Yang
603f82ffc2 refactor: using unified nanoid (#4519) 2023-09-28 02:53:04 +00:00
Alex Yang
56f75160f3 refactor: remove unused packages (#4532) 2023-09-28 01:33:42 +00:00
dependabot[bot]
a860cf8e43 chore: bump electron from 26.1.0 to 26.2.1 (#4527)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-27 10:40:30 +00:00
liuyi
504dda2092 fix(core): setting ui regression (#4525) 2023-09-27 09:56:01 +00:00
JimmFly
1df8b6edfb feat(component): add private copy link button (#4508) 2023-09-27 04:54:02 +00:00
Alex Yang
ddfa5d394d chore: bump version (#4518) 2023-09-27 02:02:54 +00:00
Alex Yang
a69820e4ca fix: type in pluginImportsFunctionMap (#4517) 2023-09-27 01:53:01 +00:00
Peng Xiao
369db3fea5 fix(component): cmdk flaky (#4512) 2023-09-27 01:37:00 +00:00
liuyi
4a03fa65d1 fix(server): wrong member count query (#4506) 2023-09-26 15:36:08 +00:00
Alex Yang
a633fb6dea fix: current page atom (#4515) 2023-09-26 14:53:01 +00:00
JimmFly
1b6cd70247 chore(core): temporarily remove set-syncing-mode (#4489) 2023-09-26 14:11:04 +00:00
Peng Xiao
29fa237dfb fix: storybook previews (#4504) 2023-09-26 05:51:39 +00:00
Alex Yang
61044d91a8 fix(core): page update date (#4502) 2023-09-26 04:09:52 +00:00
Peng Xiao
eb728f7ef2 fix: give content match a lower score (#4499) 2023-09-26 03:15:40 +00:00
JimmFly
1bdc402b7b fix: adjust 404 page style (#4491) 2023-09-26 02:51:58 +00:00
Alex Yang
127a84b4e1 refactor(plugin-cli): use @plugxjs/vite-plugin (#4501) 2023-09-26 02:49:23 +00:00
JimmFly
2e1acec3c0 fix: unexpected pop ups (#4468) 2023-09-26 02:41:45 +00:00
dependabot[bot]
672a01b385 chore: bump sinon from 15.2.0 to 16.0.0 (#4480)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-25 20:17:25 +00:00
Joooye_34
ad63c5a525 fix(component): background animation is different (#4495) 2023-09-25 16:50:39 +00:00
Alex Yang
3a79346ce0 test: workspace provider (#4497) 2023-09-25 16:49:23 +00:00
dependabot[bot]
bf729df7fe chore: bump vite-tsconfig-paths from 4.2.0 to 4.2.1 (#4481)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-25 16:05:09 +00:00
dependabot[bot]
b785840d91 chore: bump marked-gfm-heading-id from 3.0.6 to 3.1.0 (#4479)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-25 16:04:47 +00:00
Peng Xiao
e8410b948d chore(component): bump themes (#4484) 2023-09-25 12:03:28 +00:00
Peng Xiao
3f09ba92bc fix: cmdk scrollbar gutter (#4488) 2023-09-25 12:03:02 +00:00
JimmFly
35dc6d6687 fix: unexpected hover behavior of collection sidebar (#4490) 2023-09-25 10:22:59 +00:00
JimmFly
5b4ce75e13 feat: add commands (#4477) 2023-09-25 10:10:53 +00:00
Peng Xiao
dc6b66c32f fix: register command re-rendering (#4476) 2023-09-25 02:40:53 +00:00
LongYinan
5f7f5b74ca fix(core): error state for non early access user while signing in with email (#4467) 2023-09-23 00:00:09 -07:00
LongYinan
7b5157aa89 fix(server): missing dependency in sync app (#4465) 2023-09-22 21:32:45 +00:00
Alex Yang
bd0ed7f474 test: fix flaky (#4463) 2023-09-22 20:18:41 +00:00
Alex Yang
2da6702991 refactor(infra): simplify currentWorkspaceAtom (#4462) 2023-09-22 20:07:26 +00:00
Alex Yang
56d8fa5d29 docs: upload LICENSE.md 2023-09-22 14:38:19 -05:00
Alex Yang
4e5e48ce9f docs: update README.md
There are no core members actually, people is just a paid guy.
2023-09-22 14:31:15 -05:00
Peng Xiao
e0063ebc9b feat: new CMD-K (#4408) 2023-09-22 14:31:26 +00:00
Peng Xiao
27e4599c94 chore: bump components version (#4454) 2023-09-22 08:56:10 +00:00
JimmFly
edd7d00104 refactor: workspace list (#4432) 2023-09-22 15:02:31 +08:00
1642 changed files with 63504 additions and 39702 deletions

9
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1,9 @@
FROM mcr.microsoft.com/devcontainers/base:bookworm
# Install Homebrew For Linux
RUN /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" && \
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" && \
echo "eval \"\$($(brew --prefix)/bin/brew shellenv)\"" >> /home/vscode/.zshrc && \
echo "eval \"\$($(brew --prefix)/bin/brew shellenv)\"" >> /home/vscode/.bashrc && \
# Install Graphite
brew install withgraphite/tap/graphite && gt --version

12
.devcontainer/build.sh Normal file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
# This is a script used by the devcontainer to build the project
#Enable yarn
corepack enable
corepack prepare yarn@stable --activate
# install dependencies
yarn install
# Create database
yarn workspace @affine/server prisma db push

View File

@@ -0,0 +1,25 @@
// For format details, see https://aka.ms/devcontainer.json.
{
"name": "Debian",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "18"
},
"ghcr.io/devcontainers/features/rust:1": {}
},
// Configure tool-specific properties.
"customizations": {
"vscode": {
"extensions": [
"ms-playwright.playwright",
"esbenp.prettier-vscode",
"streetsidesoftware.code-spell-checker"
]
}
},
"updateContentCommand": "bash ./.devcontainer/build.sh",
"postCreateCommand": "bash ./.devcontainer/setup-user.sh"
}

View File

@@ -0,0 +1,26 @@
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
volumes:
- ../..:/workspaces:cached
command: sleep infinity
network_mode: service:db
environment:
DATABASE_URL: postgresql://affine:affine@db:5432/affine
db:
image: postgres:latest
restart: unless-stopped
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: affine
POSTGRES_USER: affine
POSTGRES_DB: affine
volumes:
postgres-data:

7
.devcontainer/setup-user.sh Executable file
View File

@@ -0,0 +1,7 @@
if [ -v GRAPHITE_TOKEN ];then
gt auth --token $GRAPHITE_TOKEN
fi
git fetch
git branch canary -t origin/canary
gt init --trunk canary

View File

@@ -11,3 +11,4 @@ ENABLE_CLOUD=
ENABLE_MOVE_DATABASE=
SHOULD_REPORT_TRACE=
TRACE_REPORT_ENDPOINT=
CAPTCHA_SITE_KEY=

View File

@@ -7,10 +7,10 @@ affine-out
_next
lib
.eslintrc.js
packages/i18n/src/i18n-generated.ts
e2e-dist-*
static
web-static
public
packages/sdk/src/*.d.ts
packages/sdk/src/*.js
packages/common/sdk/src/*.d.ts
packages/common/sdk/src/*.js
packages/frontend/i18n/src/i18n-generated.ts

View File

@@ -56,26 +56,25 @@ const createPattern = packageName => [
];
const allPackages = [
'packages/cli',
'packages/component',
'packages/debug',
'packages/env',
'packages/graphql',
'packages/hooks',
'packages/i18n',
'packages/native',
'packages/infra',
'packages/sdk',
'packages/templates',
'packages/theme',
'packages/workspace',
'packages/y-indexeddb',
'apps/web',
'apps/server',
'apps/electron',
'apps/storybook',
'plugins/copilot',
'plugins/bookmark-block',
'packages/backend/server',
'packages/frontend/component',
'packages/frontend/core',
'packages/frontend/electron',
'packages/frontend/graphql',
'packages/frontend/hooks',
'packages/frontend/i18n',
'packages/frontend/native',
'packages/frontend/templates',
'packages/frontend/workspace',
'packages/common/debug',
'packages/common/env',
'packages/common/infra',
'packages/common/sdk',
'packages/common/theme',
'packages/common/y-indexeddb',
'packages/plugins/copilot',
'tools/cli',
'tests/storybook',
];
/**
@@ -88,7 +87,7 @@ const config = {
version: 'detect',
},
next: {
rootDir: 'apps/web',
rootDir: 'packages/frontend/core',
},
},
extends: [
@@ -128,10 +127,12 @@ const config = {
'no-constant-binary-expression': 'error',
'no-constructor-return': 'error',
'react/prop-types': 'off',
'react/jsx-no-useless-fragment': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/no-non-null-assertion': 'error',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/await-thenable': 'error',
'@typescript-eslint/no-unused-vars': [
'error',
{
@@ -202,6 +203,7 @@ const config = {
ignore: ['^\\[[a-zA-Z0-9-_]+\\]\\.tsx$'],
},
],
'unicorn/no-unnecessary-await': 'error',
'sonarjs/no-all-duplicated-branches': 'error',
'sonarjs/no-element-overwrite': 'error',
'sonarjs/no-empty-collection': 'error',
@@ -221,7 +223,7 @@ const config = {
},
overrides: [
{
files: 'apps/server/**/*.ts',
files: 'packages/backend/server/**/*.ts',
rules: {
'@typescript-eslint/consistent-type-imports': 0,
},
@@ -252,6 +254,13 @@ const config = {
},
],
'@typescript-eslint/no-misused-promises': ['error'],
'i/no-extraneous-dependencies': ['error'],
'react-hooks/exhaustive-deps': [
'warn',
{
additionalHooks: 'useAsyncCallback',
},
],
},
})),
{

1
.github/CLA.md vendored
View File

@@ -61,3 +61,4 @@ Example:
- Shishu, @shishudesu, 2023/05/19
- Kushagra Singh, @kush002, 2023/06/28
- Sarvesh Kumar, @sarvesh521 2023/08/25
- 微扰理论 Qinghao Huang, @wfnuser 2023/09/29

View File

@@ -58,6 +58,6 @@ body:
label: Are you willing to submit a PR?
description: >
(Optional) We encourage you to submit a [Pull Request](https://github.com/toeverything/affine/pulls) (PR) to help improve AFFiNE for everyone, especially if you have a good understanding of how to implement a fix or feature.
See the AFFiNE [Contributing Guide](https://github.com/toeverything/affine/blob/master/CONTRIBUTING.md) to get started.
See the AFFiNE [Contributing Guide](https://github.com/toeverything/affine/blob/canary/CONTRIBUTING.md) to get started.
options:
- label: Yes I'd like to help by submitting a PR!

View File

@@ -31,6 +31,6 @@ body:
label: Are you willing to submit a PR?
description: >
(Optional) We encourage you to submit a [Pull Request](https://github.com/toeverything/affine/pulls) (PR) to help improve AFFiNE for everyone, especially if you have a good understanding of how to implement a fix or feature.
See the AFFiNE [Contributing Guide](https://github.com/toeverything/affine/blob/master/CONTRIBUTING.md) to get started.
See the AFFiNE [Contributing Guide](https://github.com/toeverything/affine/blob/canary/CONTRIBUTING.md) to get started.
options:
- label: Yes I'd like to help by submitting a PR!

View File

@@ -19,6 +19,8 @@ runs:
with:
toolchain: stable
targets: ${{ inputs.target }}
env:
CARGO_INCREMENTAL: '1'
- name: Cache cargo
uses: actions/cache@v3
@@ -34,7 +36,7 @@ runs:
if: ${{ inputs.target != 'x86_64-unknown-linux-gnu' && inputs.target != 'aarch64-unknown-linux-gnu' }}
shell: bash
run: |
yarn nx build ${{ inputs.package }} --target ${{ inputs.target }}
yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }}
env:
NX_CLOUD_ACCESS_TOKEN: ${{ inputs.nx_token }}
@@ -48,9 +50,13 @@ runs:
export CC=x86_64-unknown-linux-gnu-gcc
export CC_x86_64_unknown_linux_gnu=x86_64-unknown-linux-gnu-gcc
export RUSTFLAGS="-C debuginfo=1"
yarn nx build ${{ inputs.package }} --target ${{ inputs.target }}
chmod -R 777 node_modules/.cache
chmod -R 777 target
yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }}
if [ -d "node_modules/.cache" ]; then
chmod -R 777 node_modules/.cache
fi
if [ -d "target" ]; then
chmod -R 777 target;
fi
- name: Build
if: ${{ inputs.target == 'aarch64-unknown-linux-gnu' }}
@@ -60,6 +66,10 @@ runs:
options: --user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build -e NX_CLOUD_ACCESS_TOKEN=${{ inputs.nx_token }}
run: |
export RUSTFLAGS="-C debuginfo=1"
yarn nx build ${{ inputs.package }} --target ${{ inputs.target }}
chmod -R 777 node_modules/.cache
chmod -R 777 target
yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }}
if [ -d "node_modules/.cache" ]; then
chmod -R 777 node_modules/.cache
fi
if [ -d "target" ]; then
chmod -R 777 target;
fi

View File

@@ -13,6 +13,8 @@ const {
R2_ACCESS_KEY_ID,
R2_SECRET_ACCESS_KEY,
R2_BUCKET,
ENABLE_CAPTCHA,
CAPTCHA_TURNSTILE_SECRET,
OAUTH_EMAIL_SENDER,
OAUTH_EMAIL_LOGIN,
OAUTH_EMAIL_PASSWORD,
@@ -23,6 +25,8 @@ const {
GCLOUD_CLOUD_SQL_INTERNAL_ENDPOINT,
REDIS_HOST,
REDIS_PASSWORD,
STRIPE_API_KEY,
STRIPE_WEBHOOK_KEY,
} = process.env;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -37,8 +41,8 @@ const createHelmCommand = ({ isDryRun }) => {
const staticIpName = isProduction
? 'affine-cluster-production'
: isBeta
? 'affine-cluster-beta'
: 'affine-cluster-dev';
? 'affine-cluster-beta'
: 'affine-cluster-dev';
const redisAndPostgres =
isProduction || isBeta
? [
@@ -64,8 +68,8 @@ const createHelmCommand = ({ isDryRun }) => {
]
: [];
const webReplicaCount = isProduction ? 3 : isBeta ? 2 : 2;
const graphqlReplicaCount = isProduction ? 3 : isBeta ? 2 : 2;
const syncReplicaCount = isProduction ? 6 : isBeta ? 3 : 2;
const graphqlReplicaCount = isProduction ? 10 : isBeta ? 5 : 2;
const syncReplicaCount = isProduction ? 10 : isBeta ? 5 : 2;
const namespace = isProduction ? 'production' : isBeta ? 'beta' : 'dev';
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const host = DEPLOY_HOST || CANARY_DEPLOY_HOST;
@@ -81,6 +85,8 @@ const createHelmCommand = ({ isDryRun }) => {
`--set graphql.replicaCount=${graphqlReplicaCount}`,
`--set-string graphql.image.tag="${imageTag}"`,
`--set graphql.app.host=${host}`,
`--set graphql.app.captcha.enabled=${ENABLE_CAPTCHA}`,
`--set-string graphql.app.captcha.turnstile.secret="${CAPTCHA_TURNSTILE_SECRET}"`,
`--set graphql.app.objectStorage.r2.enabled=true`,
`--set-string graphql.app.objectStorage.r2.accountId="${R2_ACCOUNT_ID}"`,
`--set-string graphql.app.objectStorage.r2.accessKeyId="${R2_ACCESS_KEY_ID}"`,
@@ -92,7 +98,10 @@ const createHelmCommand = ({ isDryRun }) => {
`--set-string graphql.app.oauth.google.enabled=true`,
`--set-string graphql.app.oauth.google.clientId="${AFFINE_GOOGLE_CLIENT_ID}"`,
`--set-string graphql.app.oauth.google.clientSecret="${AFFINE_GOOGLE_CLIENT_SECRET}"`,
`--set-string graphql.app.payment.stripe.apiKey="${STRIPE_API_KEY}"`,
`--set-string graphql.app.payment.stripe.webhookKey="${STRIPE_WEBHOOK_KEY}"`,
`--set graphql.app.experimental.enableJwstCodec=true`,
`--set graphql.app.features.earlyAccessPreview=false`,
`--set sync.replicaCount=${syncReplicaCount}`,
`--set-string sync.image.tag="${imageTag}"`,
...serviceAnnotations,

View File

@@ -1,16 +0,0 @@
name: Setup maker
description: 'Setup maker dmg for electron'
runs:
using: 'composite'
steps:
- name: 'Install @electron-forge/maker-dmg'
if: runner.os == 'macos'
shell: bash
working-directory: ./apps/electron
run: yarn add @electron-forge/maker-dmg --dev
env:
HUSKY: '0'
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
ELECTRON_SKIP_BINARY_DOWNLOAD: '1'
SENTRYCLI_SKIP_DOWNLOAD: '1'

View File

@@ -21,6 +21,21 @@ inputs:
description: 'set nmMode to hardlinks-local in .yarnrc.yml'
required: false
default: 'true'
build-infra:
description: 'Build infra'
required: false
default: 'true'
build-plugins:
description: 'Build plugins'
required: false
default: 'true'
nmHoistingLimits:
description: 'Set nmHoistingLimits in .yarnrc.yml'
required: false
enableScripts:
description: 'Set enableScripts in .yarnrc.yml'
required: false
default: 'true'
runs:
using: 'composite'
@@ -34,30 +49,42 @@ runs:
cache: 'yarn'
- name: Set nmMode
if: ${{ inputs.hard-link-nm == 'true' }}
if: ${{ inputs.hard-link-nm == 'false' }}
shell: bash
run: yarn config set nmMode hardlinks-local
run: yarn config set nmMode classic
- name: Set nmHoistingLimits
if: ${{ inputs.nmHoistingLimits }}
shell: bash
run: yarn config set nmHoistingLimits ${{ inputs.nmHoistingLimits }}
- name: Set enableScripts
if: ${{ inputs.enableScripts == 'false' }}
shell: bash
run: yarn config set enableScripts false
- name: yarn install
if: ${{ inputs.package-install == 'true' }}
continue-on-error: true
shell: bash
run: yarn install ${{ inputs.extra-flags }}
run: yarn ${{ inputs.extra-flags }}
env:
HUSKY: '0'
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
ELECTRON_SKIP_BINARY_DOWNLOAD: '1'
SENTRYCLI_SKIP_DOWNLOAD: '1'
DEBUG: '*'
- name: yarn install (try again)
if: ${{ steps.install.outcome == 'failure' }}
shell: bash
run: yarn install ${{ inputs.extra-flags }}
run: yarn ${{ inputs.extra-flags }}
env:
HUSKY: '0'
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
ELECTRON_SKIP_BINARY_DOWNLOAD: '1'
SENTRYCLI_SKIP_DOWNLOAD: '1'
DEBUG: '*'
- name: Get installed Playwright version
id: playwright-version
@@ -89,11 +116,11 @@ runs:
${{ runner.os }}-${{ runner.arch }}-playwright-
# If the Playwright browser binaries weren't able to be restored, we tell
# paywright to install everything for us.
# playwright to install everything for us.
- name: Install Playwright's dependencies
shell: bash
if: inputs.playwright-install == 'true' && steps.playwright-cache.outputs.cache-hit != 'true'
run: yarn playwright install --with-deps
if: inputs.playwright-install == 'true'
run: yarn playwright install --with-deps chromium
- name: Get installed Electron version
id: electron-version
@@ -114,14 +141,16 @@ runs:
- name: Install Electron binary
shell: bash
if: inputs.electron-install == 'true'
run: node apps/electron/node_modules/electron/install.js
run: node ./node_modules/electron/install.js
env:
ELECTRON_OVERRIDE_DIST_PATH: ./node_modules/.cache/electron
- name: Build Infra
shell: bash
if: inputs.build-infra == 'true'
run: yarn run build:infra
- name: Build Plugins
if: inputs.build-plugins == 'true'
shell: bash
run: yarn run build:plugins

View File

@@ -2,8 +2,30 @@ version: 2
updates:
- package-ecosystem: 'npm'
directory: '/'
groups:
all-npm-dependencies:
patterns:
- '*'
schedule:
interval: 'weekly'
versioning-strategy: increase
commit-message:
prefix: 'chore'
- package-ecosystem: 'cargo'
directory: '/'
schedule:
interval: 'weekly'
versioning-strategy: auto
commit-message:
prefix: 'chore'
groups:
all-cargo-dependencies:
patterns:
- '*'
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
interval: 'daily'
commit-message:
prefix: 'ci'

View File

@@ -1,6 +1,6 @@
FROM openresty/openresty:1.21.4.1-0-buster
WORKDIR /app
COPY ./apps/core/dist ./dist
COPY ./packages/frontend/core/dist ./dist
COPY ./.github/deployment/front/nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
COPY ./.github/deployment/front/affine.nginx.conf /etc/nginx/conf.d/affine.nginx.conf

View File

@@ -1,6 +1,6 @@
FROM node:18-bookworm-slim
COPY ./apps/server /app
COPY ./packages/backend/server /app
WORKDIR /app
RUN apt-get update && \

View File

@@ -0,0 +1,9 @@
{{- if .Values.app.captcha.enabled -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.captcha.secretName }}"
type: Opaque
data:
turnstileSecret: {{ .Values.app.captcha.turnstile.secret | b64enc }}
{{- end }}

View File

@@ -35,6 +35,8 @@ spec:
key: key
- name: NODE_ENV
value: "{{ .Values.env }}"
- name: NODE_OPTIONS
value: "--max-old-space-size=4096"
- name: NO_COLOR
value: "1"
- name: SERVER_FLAVOR
@@ -73,6 +75,10 @@ spec:
value: "{{ .Values.app.host }}"
- name: ENABLE_R2_OBJECT_STORAGE
value: "{{ .Values.app.objectStorage.r2.enabled }}"
- name: ENABLE_CAPTCHA
value: "{{ .Values.app.captcha.enabled }}"
- name: FEATURES_EARLY_ACCESS_PREVIEW
value: "{{ .Values.app.features.earlyAccessPreview }}"
- name: OAUTH_EMAIL_SENDER
valueFrom:
secretKeyRef:
@@ -98,6 +104,16 @@ spec:
secretKeyRef:
name: "{{ .Values.app.oauth.email.secretName }}"
key: password
- name: STRIPE_API_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.app.payment.stripe.secretName }}"
key: stripeAPIKey
- name: STRIPE_WEBHOOK_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.app.payment.stripe.secretName }}"
key: stripeWebhookKey
- name: DOC_MERGE_INTERVAL
value: "{{ .Values.app.doc.mergeInterval }}"
{{ if .Values.app.experimental.enableJwstCodec }}
@@ -126,6 +142,13 @@ spec:
name: "{{ .Values.app.objectStorage.r2.secretName }}"
key: bucket
{{ end }}
{{ if .Values.app.captcha.enabled }}
- name: CAPTCHA_TURNSTILE_SECRET
valueFrom:
secretKeyRef:
name: "{{ .Values.app.captcha.secretName }}"
key: turnstileSecret
{{ end }}
{{ if .Values.app.oauth.google.enabled }}
- name: OAUTH_GOOGLE_CLIENT_ID
valueFrom:

View File

@@ -16,7 +16,7 @@ spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
command: ["yarn", "prisma", "migrate", "deploy"]
command: ["yarn", "predeploy"]
env:
- name: NODE_ENV
value: "{{ .Values.env }}"

View File

@@ -1,13 +0,0 @@
{{- if .Values.global.gke.enabled -}}
apiVersion: monitoring.googleapis.com/v1
kind: PodMonitoring
metadata:
name: "{{ .Chart.Name }}-monitoring"
spec:
selector:
matchLabels:
app.kubernetes.io/name: "{{ include "graphql.name" . }}"
endpoints:
- port: {{ .Values.service.port }}
interval: 30s
{{- end }}

View File

@@ -0,0 +1,8 @@
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.payment.stripe.secretName }}"
type: Opaque
data:
stripeAPIKey: "{{ .Values.app.payment.stripe.apiKey | b64enc }}"
stripeWebhookKey: "{{ .Values.app.payment.stripe.webhookKey | b64enc }}"

View File

@@ -22,6 +22,11 @@ app:
secretName: jwt-private-key
# base64 encoded ecdsa private key
privateKey: ''
captcha:
enable: false
secretName: captcha
turnstile:
secret: ''
objectStorage:
r2:
enabled: false
@@ -48,6 +53,13 @@ app:
secretName: oauth-github
clientId: ''
clientSecret: ''
payment:
stripe:
secretName: 'stripe'
apiKey: ''
webhookKey: ''
features:
earlyAccessPreview: false
serviceAccount:
create: true
@@ -60,11 +72,8 @@ podSecurityContext:
fsGroup: 2000
resources:
limits:
cpu: '4'
memory: 8Gi
requests:
cpu: '2'
cpu: '4'
memory: 4Gi
probe:

View File

@@ -1,13 +0,0 @@
{{- if .Values.global.gke.enabled -}}
apiVersion: monitoring.googleapis.com/v1
kind: PodMonitoring
metadata:
name: "{{ .Chart.Name }}-monitoring"
spec:
selector:
matchLabels:
app.kubernetes.io/name: "{{ include "sync.name" . }}"
endpoints:
- port: {{ .Values.service.port }}
interval: 30s
{{- end }}

43
.github/labeler.yml vendored
View File

@@ -1,7 +1,7 @@
docs:
- 'docs/**/*'
- '**/README.md'
- 'packages/templates/**/*'
- 'packages/frontend/templates/**/*'
test:
- 'tests/**/*'
@@ -10,40 +10,37 @@ test:
mod:dev:
- 'scripts/**/*'
- 'packages/cli/**/*'
- 'packages/debug/**/*'
- 'tools/cli/**/*'
- 'packages/common/debug/**/*'
mod:plugin:
- 'plugins/**/*'
plugin:bookmark-block:
- 'plugins/bookmark-block/**/*'
- 'packages/plugins/**/*'
plugin:copilot:
- 'plugins/copilot/**/*'
- 'packages/plugins/copilot/**/*'
mod:infra:
- 'packages/infra/**/*'
- 'packages/common/infra/**/*'
mod:sdk:
- 'packages/sdk/**/*'
- 'packages/common/sdk/**/*'
mod:plugin-cli:
- 'packages/plugin-cli/**/*'
- 'tools/plugin-cli/**/*'
mod:workspace: 'packages/workspace/**/*'
mod:workspace: 'packages/frontend/workspace/**/*'
mod:i18n: 'packages/i18n/**/*'
mod:i18n: 'packages/frontend/i18n/**/*'
mod:env: 'packages/env/**/*'
mod:env: 'packages/common/env/**/*'
mod:hooks: 'packages/hooks/**/*'
mod:hooks: 'packages/frontend/hooks/**/*'
mod:component: 'packages/component/**/*'
mod:component: 'packages/frontend/component/**/*'
mod:storage: 'packages/storage/**/*'
mod:storage: 'packages/backend/storage/**/*'
mod:native: 'packages/native/**/*'
mod:native: 'packages/frontend/native/**/*'
mod:store:
- '**/atoms/**/*'
@@ -56,12 +53,10 @@ rust:
- '**/rust-toolchain.toml'
- '**/rustfmt.toml'
package:y-indexeddb: 'packages/y-indexeddb/**/*'
package:y-indexeddb: 'packages/common/y-indexeddb/**/*'
app:core: 'apps/core/**/*'
app:core: 'packages/frontend/core/**/*'
app:electron: 'apps/electron/**/*'
app:electron: 'packages/frontend/electron/**/*'
app:server: 'apps/server/**/*'
app:docs: 'apps/docs/**/*'
app:server: 'packages/backend/server/**/*'

View File

@@ -3,7 +3,7 @@ name: Build(Desktop) & Test
on:
push:
branches:
- master
- canary
- v[0-9]+.[0-9]+.x-staging
- v[0-9]+.[0-9]+.x
paths-ignore:
@@ -15,7 +15,7 @@ on:
pull_request:
merge_group:
branches:
- master
- canary
- v[0-9]+.[0-9]+.x-staging
- v[0-9]+.[0-9]+.x
paths-ignore:
@@ -38,27 +38,43 @@ jobs:
build-core:
name: Build @affine/core
runs-on: ubuntu-latest
environment: development
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build Plugins
run: yarn run build:plugins
with:
electron-install: false
- name: Build Core
run: yarn nx build @affine/core
- name: Upload core artifact
uses: actions/upload-artifact@v3
with:
name: core
path: ./apps/core/dist
path: ./packages/frontend/core/dist
if-no-files-found: error
build-native:
name: Build Native
runs-on: ubuntu-latest
needs: build-core
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build AFFiNE native
uses: ./.github/actions/build-rust
with:
target: x86_64-unknown-linux-gnu
package: '@affine/native'
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
- name: Run tests
run: yarn test
working-directory: ./packages/frontend/native
desktop-test:
name: Desktop Test
runs-on: ${{ matrix.spec.os }}
environment: development
strategy:
fail-fast: false
# all combinations: macos-latest x64, macos-latest arm64, windows-latest x64, ubuntu-latest x64
@@ -94,13 +110,15 @@ jobs:
}
needs: build-core
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
timeout-minutes: 10
with:
extra-flags: workspaces focus @affine/electron @affine/monorepo @affine-test/affine-desktop
playwright-install: true
hard-link-nm: false
enableScripts: false
- name: Build AFFiNE native
uses: ./.github/actions/build-rust
@@ -108,20 +126,18 @@ jobs:
target: ${{ matrix.spec.target }}
package: '@affine/native'
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
- name: Run unit tests
if: ${{ matrix.spec.test }}
shell: bash
run: yarn vitest
working-directory: ./apps/electron
working-directory: packages/frontend/electron
- name: Download core artifact
uses: actions/download-artifact@v3
with:
name: core
path: apps/electron/resources/web-static
- name: Build Plugins
run: yarn run build:plugins
path: packages/frontend/electron/resources/web-static
- name: Build Desktop Layers
run: yarn workspace @affine/electron build
@@ -142,13 +158,14 @@ jobs:
if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }}
env:
SKIP_BUNDLE: true
run: yarn workspace @affine/electron make --platform=darwin --arch=arm64
SKIP_WEB_BUILD: true
HOIST_NODE_MODULES: 1
run: yarn workspace @affine/electron package --platform=darwin --arch=arm64
- name: Output check
if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }}
run: |
yarn ts-node-esm ./scripts/macos-arm64-output-check.mts
working-directory: apps/electron
yarn workspace @affine/electron ts-node ./scripts/macos-arm64-output-check.ts
- name: Collect code coverage report
if: ${{ matrix.spec.test }}

View File

@@ -3,7 +3,7 @@ name: Build(Server) & Test
on:
push:
branches:
- master
- canary
- v[0-9]+.[0-9]+.x-staging
- v[0-9]+.[0-9]+.x
paths-ignore:
@@ -15,7 +15,7 @@ on:
pull_request:
merge_group:
branches:
- master
- canary
- v[0-9]+.[0-9]+.x-staging
- v[0-9]+.[0-9]+.x
paths-ignore:
@@ -39,13 +39,17 @@ jobs:
runs-on: ubuntu-latest
env:
RUSTFLAGS: '-C debuginfo=1'
environment: development
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Setup Rust
with:
extra-flags: workspaces focus @affine/storage
electron-install: false
build-infra: false
build-plugins: false
- name: Build Rust
uses: ./.github/actions/build-rust
with:
target: 'x86_64-unknown-linux-gnu'
@@ -55,13 +59,12 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: storage.node
path: ./packages/storage/storage.node
path: ./packages/backend/storage/storage.node
if-no-files-found: error
server-test:
name: Server Test
runs-on: ubuntu-latest
environment: development
needs: build-storage
services:
postgres:
@@ -81,10 +84,12 @@ jobs:
- 1025:1025
- 8025:8025
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
- name: Initialize database
run: |
@@ -102,7 +107,7 @@ jobs:
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
- name: Run init-db script
run: yarn workspace @affine/server exec ts-node-esm ./scripts/init-db.ts
run: yarn workspace @affine/server exec ts-node ./scripts/init-db.ts
env:
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
@@ -110,7 +115,7 @@ jobs:
uses: actions/download-artifact@v3
with:
name: storage.node
path: ./apps/server
path: ./packages/backend/server
- name: Run server tests
run: yarn workspace @affine/server test:coverage
@@ -122,7 +127,7 @@ jobs:
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./apps/server/.coverage/lcov.info
files: ./packages/backend/server/.coverage/lcov.info
flags: server-test
name: affine
fail_ci_if_error: false
@@ -130,7 +135,6 @@ jobs:
server-e2e-test:
name: Server E2E Test
runs-on: ubuntu-latest
environment: development
needs: build-storage
services:
postgres:
@@ -150,7 +154,7 @@ jobs:
- 1025:1025
- 8025:8025
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
@@ -173,7 +177,7 @@ jobs:
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
- name: Run init-db script
run: yarn workspace @affine/server exec ts-node-esm ./scripts/init-db.ts
run: yarn workspace @affine/server exec ts-node ./scripts/init-db.ts
env:
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
@@ -181,7 +185,7 @@ jobs:
uses: actions/download-artifact@v3
with:
name: storage.node
path: ./apps/server
path: ./packages/backend/server
- name: Run playwright tests
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine-test/affine-cloud e2e --forbid-only
@@ -212,7 +216,6 @@ jobs:
server-desktop-e2e-test:
name: Server Desktop E2E Test
runs-on: ubuntu-latest
environment: development
needs: build-storage
services:
postgres:
@@ -232,7 +235,7 @@ jobs:
- 1025:1025
- 8025:8025
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
@@ -257,15 +260,13 @@ jobs:
- name: Generate prisma client
run: |
yarn exec prisma generate
yarn exec prisma db push
working-directory: apps/server
yarn workspace @affine/server exec prisma generate
yarn workspace @affine/server prisma db push
env:
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
- name: Run init-db script
run: yarn exec ts-node-esm ./scripts/init-db.ts
working-directory: apps/server
run: yarn workspace @affine/server exec ts-node ./scripts/init-db.ts
env:
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
@@ -273,7 +274,7 @@ jobs:
uses: actions/download-artifact@v3
with:
name: storage.node
path: ./apps/server
path: ./packages/backend/server
- name: Build Plugins
run: yarn run build:plugins

View File

@@ -3,7 +3,7 @@ name: Build & Test
on:
push:
branches:
- master
- canary
- v[0-9]+.[0-9]+.x-staging
- v[0-9]+.[0-9]+.x
paths-ignore:
@@ -15,7 +15,7 @@ on:
pull_request:
merge_group:
branches:
- master
- canary
- v[0-9]+.[0-9]+.x-staging
- v[0-9]+.[0-9]+.x
paths-ignore:
@@ -39,10 +39,12 @@ jobs:
lint:
name: Lint
runs-on: ubuntu-latest
environment: development
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Run oxlint
# oxlint is fast, so wrong code will fail quickly
run: yarn dlx oxlint@latest .
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
@@ -64,54 +66,18 @@ jobs:
check-yarn-binary:
name: Check yarn binary
runs-on: ubuntu-latest
environment: development
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Run check
run: |
yarn set version $(node -e "console.log(require('./package.json').packageManager.split('@')[1])")
git diff --exit-code
build-prototype:
name: Build Prototype
runs-on: ubuntu-latest
environment: development
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
- name: Build Prototype
run: yarn nx build prototype
- name: Upload prototype artifact
uses: actions/upload-artifact@v3
with:
name: prototype
path: ./apps/prototype/dist
if-no-files-found: error
build-docs:
name: Build Docs
runs-on: ubuntu-latest
environment: development
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
- run: yarn nx build @affine/docs
env:
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
e2e-plugin-test:
name: E2E Plugin Test
runs-on: ubuntu-latest
environment: development
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
@@ -142,49 +108,6 @@ jobs:
path: ./test-results
if-no-files-found: ignore
e2e-prototype-test:
name: E2E Prototype Test
runs-on: ubuntu-latest
environment: development
needs: build-prototype
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
playwright-install: true
electron-install: false
- name: Download prototype artifact
uses: actions/download-artifact@v3
with:
name: prototype
path: ./apps/prototype/dist
- name: Run playwright tests
run: yarn e2e --forbid-only
working-directory: tests/affine-prototype
env:
COVERAGE: true
# - name: Collect code coverage report
# run: yarn 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-prototype
# name: affine
# fail_ci_if_error: false
- name: Upload test results
if: ${{ failure() }}
uses: actions/upload-artifact@v3
with:
name: test-results-e2e-prototype
path: ./test-results
if-no-files-found: ignore
e2e-test:
name: E2E Test
runs-on: ubuntu-latest
@@ -192,9 +115,8 @@ jobs:
fail-fast: false
matrix:
shard: [1, 2, 3, 4, 5]
environment: development
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
@@ -230,48 +152,42 @@ jobs:
e2e-migration-test:
name: E2E Migration Test
runs-on: ubuntu-latest
environment: development
strategy:
matrix:
spec:
- { package: 0.7.0-canary.18 }
- { package: 0.8.0-canary.7 }
- { package: 0.8.3 }
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
playwright-install: true
electron-install: false
- name: Unzip
run: yarn unzip
working-directory: ./tests/affine-legacy/${{ matrix.spec.package }}
- name: Run playwright tests
run: yarn e2e --forbid-only
working-directory: ./tests/affine-legacy/${{ matrix.spec.package }}
run: yarn workspace @affine-test/affine-migration e2e --forbid-only
- name: Upload test results
if: ${{ failure() }}
uses: actions/upload-artifact@v3
with:
name: test-results-e2e-migration-${{ matrix.spec.package }}
path: ./tests/affine-legacy/${{ matrix.spec.package }}/test-results
name: test-results-e2e-migration
path: ./tests/affine-migration/test-results
if-no-files-found: ignore
unit-test:
name: Unit Test
runs-on: ubuntu-latest
environment: development
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
- name: Build AFFiNE native
uses: ./.github/actions/build-rust
with:
target: x86_64-unknown-linux-gnu
package: '@affine/native'
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
- name: Unit Test
run: yarn nx test:coverage @affine/monorepo

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Cleanup
run: |

View File

@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- uses: styfle/cancel-workflow-action@0.11.0
- uses: styfle/cancel-workflow-action@0.12.0
with:
# See https://api.github.com/repos/toeverything/AFFiNE/actions/workflows
workflow_id: 44038251, 61883931, 65188160, 66789140

View File

@@ -13,11 +13,11 @@ name: 'CodeQL'
on:
push:
branches: [master]
branches: [canary]
pull_request:
merge_group:
# The branches below must be a subset of the branches above
branches: [master]
branches: [canary]
jobs:
analyze:
@@ -37,7 +37,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

View File

@@ -1,15 +1,10 @@
name: Deploy
on:
push:
branches:
- master
tags:
- 'v[0-9]+.[0-9]+.[0-9]+-canary.[0-9]+'
workflow_dispatch:
inputs:
flavor:
description: 'Build type (canary, beta, internal or stable)'
description: 'Build type (canary, beta, or stable)'
type: string
default: canary
@@ -24,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.flavor }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
@@ -35,15 +30,14 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: server-dist
path: ./apps/server/dist
path: ./packages/backend/server/dist
if-no-files-found: error
build-core:
name: Build @affine/core
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build Plugins
@@ -57,20 +51,20 @@ jobs:
BUILD_TYPE_OVERRIDE: ${{ github.event.inputs.flavor }}
SHOULD_REPORT_TRACE: true
TRACE_REPORT_ENDPOINT: ${{ secrets.TRACE_REPORT_ENDPOINT }}
CAPTCHA_SITE_KEY: ${{ secrets.CAPTCHA_SITE_KEY }}
- name: Upload core artifact
uses: actions/upload-artifact@v3
with:
name: core
path: ./apps/core/dist
path: ./packages/frontend/core/dist
if-no-files-found: error
build-storage:
name: Build Storage
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.flavor }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Setup Rust
@@ -83,34 +77,62 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: storage.node
path: ./packages/storage/storage.node
path: ./packages/backend/storage/storage.node
if-no-files-found: error
build-storage-arm64:
name: Build Storage arm64
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Setup Rust
uses: ./.github/actions/build-rust
with:
target: 'aarch64-unknown-linux-gnu'
package: '@affine/storage'
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
- name: Upload storage.node
uses: actions/upload-artifact@v3
with:
name: storage.arm64.node
path: ./packages/backend/storage/storage.node
if-no-files-found: error
build-docker:
name: Build Docker
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.flavor }}
needs:
- build-server
- build-core
- build-storage
- build-storage-arm64
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Download core artifact
uses: actions/download-artifact@v3
with:
name: core
path: ./apps/core/dist
path: ./packages/frontend/core/dist
- name: Download server dist
uses: actions/download-artifact@v3
with:
name: server-dist
path: ./apps/server/dist
path: ./packages/backend/server/dist
- name: Download storage.node
uses: actions/download-artifact@v3
with:
name: storage.node
path: ./apps/server
path: ./packages/backend/server
- name: Download storage.node arm64
uses: actions/download-artifact@v3
with:
name: storage.arm64.node
path: ./packages/backend/storage
- name: move storage.arm64.node
run: mv ./packages/backend/storage/storage.node ./packages/backend/server/storage.arm64.node
- name: Setup env
run: |
echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
@@ -122,18 +144,18 @@ jobs:
fi
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ghcr.io
logout: false
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Build front Dockerfile
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
context: .
push: true
@@ -146,20 +168,22 @@ jobs:
# setup node without cache configuration
# Prisma cache is not compatible with docker build cache
- name: Setup Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
registry-url: https://npm.pkg.github.com
scope: '@toeverything'
- name: Install Node.js dependencies
run: yarn workspaces focus @affine/server --production
run: |
yarn config set --json supportedArchitectures.cpu '["x64", "arm64"]'
yarn workspaces focus @affine/server --production
- name: Generate Prisma client
run: yarn workspace @affine/server prisma generate
- name: Build graphql Dockerfile
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
context: .
push: true
@@ -167,11 +191,11 @@ jobs:
platforms: linux/amd64,linux/arm64
provenance: true
file: .github/deployment/node/Dockerfile
tags: ghcr.io/toeverything/affine-graphql:${{env.RELEASE_FLAVOR}}-${{ env.GIT_SHORT_HASH }},ghcr.io/toeverything/affine-front:${{env.RELEASE_FLAVOR}}
tags: ghcr.io/toeverything/affine-graphql:${{env.RELEASE_FLAVOR}}-${{ env.GIT_SHORT_HASH }},ghcr.io/toeverything/affine-graphql:${{env.RELEASE_FLAVOR}}
deploy:
name: Deploy to cluster
if: ${{ github.event_name == 'workflow_dispatch' || github.ref_type == 'tag' }}
if: ${{ github.event_name == 'workflow_dispatch' }}
environment: ${{ github.event.inputs.flavor }}
permissions:
contents: 'write'
@@ -180,7 +204,7 @@ jobs:
- build-docker
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Deploy to dev
uses: ./.github/actions/deploy
with:
@@ -197,6 +221,8 @@ jobs:
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
R2_BUCKET: ${{ secrets.R2_BUCKET }}
ENABLE_CAPTCHA: true
CAPTCHA_TURNSTILE_SECRET: ${{ secrets.CAPTCHA_TURNSTILE_SECRET }}
OAUTH_EMAIL_SENDER: ${{ secrets.OAUTH_EMAIL_SENDER }}
OAUTH_EMAIL_LOGIN: ${{ secrets.OAUTH_EMAIL_LOGIN }}
OAUTH_EMAIL_PASSWORD: ${{ secrets.OAUTH_EMAIL_PASSWORD }}
@@ -212,3 +238,5 @@ jobs:
REDIS_HOST: ${{ secrets.REDIS_HOST }}
REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }}
CLOUD_SQL_IAM_ACCOUNT: ${{ secrets.CLOUD_SQL_IAM_ACCOUNT }}
STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }}
STRIPE_WEBHOOK_KEY: ${{ secrets.STRIPE_WEBHOOK_KEY }}

30
.github/workflows/dispatch-deploy.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: Dispatch Deploy by tag
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+-canary.[0-9]+'
jobs:
dispatch-deploy-by-tag:
runs-on: ubuntu-latest
name: Setup deploy environment
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
extra-flags: 'workspaces focus @affine/monorepo'
hard-link-nm: false
electron-install: false
build-infra: false
build-plugins: false
- name: Setup output value
id: flavor
run: |
node -e "const env = require('semver').parse('${{ github.ref_name }}').prerelease[0] ?? 'stable'; console.log(`flavor=${env}`)" >> "$GITHUB_OUTPUT"
- name: dispatch deploy
uses: benc-uk/workflow-dispatch@v1
with:
workflow: deploy.yml
inputs: '{ "flavor": "${{ steps.flavor.outputs.flavor }}" }'

View File

@@ -2,7 +2,7 @@ name: Release Charts
on:
push:
branches: [master]
branches: [canary]
paths:
- '.github/helm/**/Chart.yml'
@@ -11,12 +11,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Checkout Helm chart repo
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: toeverything/helm-charts
path: .helm-chart-repo

View File

@@ -6,7 +6,7 @@ on:
- labeled
- unlabeled
branches:
- master
- canary
jobs:
check_labels:

View File

@@ -2,15 +2,15 @@ name: Languages Sync
on:
push:
branches: ['master']
branches: ['canary']
paths:
- 'packages/i18n/**'
- 'packages/frontend/i18n/**'
- '.github/workflows/languages-sync.yml'
- '!.github/actions/setup-node/action.yml'
pull_request_target:
branches: ['master']
branches: ['canary']
paths:
- 'packages/i18n/**'
- 'packages/frontend/i18n/**'
- '.github/workflows/languages-sync.yml'
- '!.github/actions/setup-node/action.yml'
workflow_dispatch:
@@ -19,17 +19,17 @@ jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Check Language Key
if: github.ref != 'refs/heads/master'
if: github.ref != 'refs/heads/canary'
run: yarn workspace @affine/i18n run sync-languages:check
env:
TOLGEE_API_KEY: ${{ secrets.TOLGEE_API_KEY }}
- name: Sync Languages
if: github.ref == 'refs/heads/master'
if: github.ref == 'refs/heads/canary'
run: yarn workspace @affine/i18n run sync-languages
env:
TOLGEE_API_KEY: ${{ secrets.TOLGEE_API_KEY }}

View File

@@ -2,6 +2,15 @@ name: Build Canary Desktop App on Staging Branch
on:
workflow_dispatch:
inputs:
channel_override:
description: 'channel type (canary, beta, or stable)'
type: choice
default: beta
options:
- canary
- beta
- stable
push:
branches:
# 0.6.x-staging
@@ -27,27 +36,28 @@ concurrency:
cancel-in-progress: true
env:
# BUILD_TYPE => app icon, app name, etc
BUILD_TYPE: internal
# BUILD_TYPE_OVERRIDE => channel type (canary, beta, or stable) - get the channel type (the api configs)
BUILD_TYPE_OVERRIDE: ${{ github.event.inputs.channel_override || 'beta' }}
jobs:
set-build-version:
runs-on: ubuntu-latest
environment: production
outputs:
version: 0.0.0-internal.${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: toeverything/set-build-version@latest
- id: version
run: echo ::set-output name=version::${{ env.BUILD_VERSION }}
before-make:
runs-on: ubuntu-latest
environment: production
needs:
- set-build-version
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Setup @sentry/cli
@@ -55,7 +65,7 @@ jobs:
- name: Replace Version
run: ./scripts/set-version.sh ${{ needs.set-build-version.outputs.version }}
- name: generate-assets
working-directory: apps/electron
working-directory: packages/frontend/electron
run: yarn generate-assets
env:
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
@@ -68,10 +78,9 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: core
path: apps/electron/resources/web-static
path: packages/frontend/electron/resources/web-static
make-distribution:
environment: production
strategy:
# all combinations: macos-latest x64, macos-latest arm64, ubuntu-latest x64
# For windows, we need a separate approach
@@ -103,13 +112,26 @@ jobs:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
SKIP_GENERATE_ASSETS: 1
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
timeout-minutes: 10
if: ${{ matrix.spec.platform == 'darwin' }}
uses: ./.github/actions/setup-node
- name: Setup Maker
with:
extra-flags: workspaces focus @affine/electron @affine/monorepo
hard-link-nm: false
build-plugins: false
nmHoistingLimits: workspaces
enableScripts: false
- name: Setup Node.js
timeout-minutes: 10
uses: ./.github/actions/setup-maker
if: ${{ matrix.spec.platform != 'darwin' }}
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/electron @affine/monorepo
hard-link-nm: false
build-plugins: false
nmHoistingLimits: workspaces
- name: Build AFFiNE native
uses: ./.github/actions/build-rust
with:
@@ -121,7 +143,7 @@ jobs:
- uses: actions/download-artifact@v3
with:
name: core
path: apps/electron/resources/web-static
path: packages/frontend/electron/resources/web-static
- name: Build Plugins
run: yarn run build:plugins
@@ -138,28 +160,32 @@ jobs:
- name: make
run: yarn workspace @affine/electron make --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
env:
SKIP_PLUGIN_BUILD: 1
SKIP_WEB_BUILD: 1
HOIST_NODE_MODULES: 1
- name: Save artifacts (mac)
if: ${{ matrix.spec.platform == 'darwin' }}
run: |
mkdir -p builds
mv apps/electron/out/*/make/*.dmg ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.dmg
mv apps/electron/out/*/make/zip/darwin/${{ matrix.spec.arch }}/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.zip
mv packages/frontend/electron/out/*/make/*.dmg ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.dmg
mv packages/frontend/electron/out/*/make/zip/darwin/${{ matrix.spec.arch }}/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.zip
- name: Save artifacts (windows)
if: ${{ matrix.spec.platform == 'win32' }}
run: |
mkdir -p builds
mv apps/electron/out/*/make/zip/win32/x64/AFFiNE*-win32-x64-*.zip ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.zip
mv apps/electron/out/*/make/squirrel.windows/x64/*.exe ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.exe
mv apps/electron/out/*/make/squirrel.windows/x64/*.msi ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.msi
mv apps/electron/out/*/make/squirrel.windows/x64/*.nupkg ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.nupkg
mv packages/frontend/electron/out/*/make/zip/win32/x64/AFFiNE*-win32-x64-*.zip ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.zip
mv packages/frontend/electron/out/*/make/squirrel.windows/x64/*.exe ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.exe
mv packages/frontend/electron/out/*/make/squirrel.windows/x64/*.msi ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.msi
mv packages/frontend/electron/out/*/make/squirrel.windows/x64/*.nupkg ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.nupkg
- name: Save artifacts (linux)
if: ${{ matrix.spec.platform == 'linux' }}
run: |
mkdir -p builds
mv apps/electron/out/*/make/zip/linux/x64/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.zip
mv apps/electron/out/*/make/AppImage/x64/*.AppImage ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.AppImage
mv packages/frontend/electron/out/*/make/zip/linux/x64/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.zip
mv packages/frontend/electron/out/*/make/AppImage/x64/*.AppImage ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.AppImage
- name: Upload Artifact
uses: actions/upload-artifact@v3
@@ -174,7 +200,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Download Artifacts (macos-x64)
uses: actions/download-artifact@v3
with:
@@ -196,13 +222,12 @@ jobs:
name: affine-linux-x64-builds
path: ./
- name: Setup Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 18
- name: Generate Release yml
run: |
cp ./apps/electron/scripts/generate-yml.js .
node generate-yml.js
node ./packages/frontend/electron/scripts/generate-yml.js
env:
RELEASE_VERSION: ${{ needs.set-build-version.outputs.version }}
- name: Create Release Draft

View File

@@ -1,54 +0,0 @@
name: NX
on:
push:
branches:
- master
- v[0-9]+.[0-9]+.x-staging
- v[0-9]+.[0-9]+.x
paths-ignore:
- README.md
- .github/**
- '!.github/workflows/nx.yml'
- '!.github/actions/build-rust/action.yml'
- '!.github/actions/setup-node/action.yml'
pull_request:
merge_group:
branches:
- master
- v[0-9]+.[0-9]+.x-staging
- v[0-9]+.[0-9]+.x
paths-ignore:
- README.md
- .github/**
- '!.github/workflows/nx.yml'
- '!.github/actions/build-rust/action.yml'
- '!.github/actions/setup-node/action.yml'
jobs:
main:
name: Nx Cloud - Main Job
uses: nrwl/ci/.github/workflows/nx-cloud-main.yml@v0.13.0
with:
runs-on: macos-latest
main-branch-name: master
number-of-agents: 5
init-commands: |
yarn exec nx-cloud start-ci-run --stop-agents-after="build" --agent-count=5
environment-variables: |
BUILD_TYPE=canary
# parallel-commands: |
# yarn exec nx-cloud record -- yarn exec nx format:check
parallel-commands-on-agents: |
yarn exec nx affected --target=build --parallel=5
timeout: 60
agents:
name: Nx Cloud - Agents
uses: nrwl/ci/.github/workflows/nx-cloud-agents.yml@v0.13.0
with:
runs-on: macos-latest
number-of-agents: 5
environment-variables: |
BUILD_TYPE=canary
timeout: 60

View File

@@ -9,4 +9,4 @@ jobs:
add-reviews:
runs-on: ubuntu-latest
steps:
- uses: kentaro-m/auto-assign-action@v1.2.4
- uses: kentaro-m/auto-assign-action@v1.2.5

View File

@@ -7,7 +7,7 @@ on:
- edited
- synchronize
branches:
- master
- canary
permissions:
contents: read
@@ -17,7 +17,9 @@ jobs:
name: Check pull request title
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- run: echo "${{ github.event.pull_request.title }}" | npx commitlint -g ./.commitlintrc.json
with:
electron-install: false
- run: echo "${{ github.event.pull_request.title }}" | yarn dlx commitlint -g ./.commitlintrc.json

View File

@@ -7,25 +7,23 @@ on:
workflow_dispatch:
push:
branches:
- master
- canary
pull_request:
branches:
- master
- canary
paths-ignore:
- README.md
- .github/**
- apps/server
- apps/docs
- apps/electron
- packages/backend/server
- packages/frontend/electron
- '!.github/workflows/publish-storybook.yml'
jobs:
publish-storybook:
name: Publish Storybook
runs-on: ubuntu-latest
environment: development
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
# This is required to fetch all commits for chromatic
@@ -38,7 +36,7 @@ jobs:
run: yarn run build:plugins
- uses: chromaui/action-next@v1
with:
workingDir: apps/storybook
workingDir: tests/storybook
buildScriptName: build
exitOnceUploaded: true
onlyChanged: false
@@ -46,7 +44,7 @@ jobs:
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
NODE_OPTIONS: ${{ env.NODE_OPTIONS }}
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
if: always()
with:
name: chromatic-build-artifacts-${{ github.run_id }}

View File

@@ -40,11 +40,10 @@ env:
jobs:
before-make:
runs-on: ubuntu-latest
environment: production
outputs:
RELEASE_VERSION: ${{ steps.get-canary-version.outputs.RELEASE_VERSION }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Setup @sentry/cli
@@ -54,12 +53,12 @@ jobs:
if: ${{ github.ref_type == 'tag' }}
run: |
TAG_VERSION=${GITHUB_REF#refs/tags/v}
PACKAGE_VERSION=$(node -p "require('./apps/electron/package.json').version")
PACKAGE_VERSION=$(node -p "require('./packages/frontend/electron/package.json').version")
if [ "$TAG_VERSION" != "$PACKAGE_VERSION" ]; then
echo "Tag version ($TAG_VERSION) does not match package.json version ($PACKAGE_VERSION)"
exit 1
fi
echo "RELEASE_VERSION=$(node -p "require('./apps/electron/package.json').version")" >> $GITHUB_OUTPUT
echo "RELEASE_VERSION=$(node -p "require('./packages/frontend/electron/package.json').version")" >> $GITHUB_OUTPUT
- name: generate-assets
run: yarn workspace @affine/electron generate-assets
env:
@@ -67,15 +66,15 @@ jobs:
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
RELEASE_VERSION: ${{ github.event.inputs.version || steps.get-canary-version.outputs.RELEASE_VERSION }}
SKIP_PLUGIN_BUILD: 'true'
- name: Upload core artifact
uses: actions/upload-artifact@v3
with:
name: core
path: apps/electron/resources/web-static
path: packages/frontend/electron/resources/web-static
make-distribution:
environment: production
strategy:
# all combinations: macos-latest x64, macos-latest arm64, ubuntu-latest x64
# For windows, we need a separate approach
@@ -101,13 +100,16 @@ jobs:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
SKIP_GENERATE_ASSETS: 1
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
timeout-minutes: 10
uses: ./.github/actions/setup-node
- name: Setup Maker
timeout-minutes: 10
uses: ./.github/actions/setup-maker
with:
extra-flags: workspaces focus @affine/electron @affine/monorepo
hard-link-nm: false
build-plugins: false
nmHoistingLimits: workspaces
enableScripts: false
- name: Build AFFiNE native
uses: ./.github/actions/build-rust
with:
@@ -117,10 +119,7 @@ jobs:
- uses: actions/download-artifact@v3
with:
name: core
path: apps/electron/resources/web-static
- name: Build Plugins
run: yarn run build:plugins
path: packages/frontend/electron/resources/web-static
- name: Build Desktop Layers
run: yarn workspace @affine/electron build
@@ -134,19 +133,23 @@ jobs:
- name: make
run: yarn workspace @affine/electron make --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
env:
SKIP_PLUGIN_BUILD: 1
SKIP_WEB_BUILD: 1
HOIST_NODE_MODULES: 1
- name: Save artifacts (mac)
if: ${{ matrix.spec.platform == 'darwin' }}
run: |
mkdir -p builds
mv apps/electron/out/*/make/*.dmg ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.dmg
mv apps/electron/out/*/make/zip/darwin/${{ matrix.spec.arch }}/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.zip
mv packages/frontend/electron/out/*/make/*.dmg ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.dmg
mv packages/frontend/electron/out/*/make/zip/darwin/${{ matrix.spec.arch }}/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.zip
- name: Save artifacts (linux)
if: ${{ matrix.spec.platform == 'linux' }}
run: |
mkdir -p builds
mv apps/electron/out/*/make/zip/linux/x64/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.zip
mv apps/electron/out/*/make/AppImage/x64/*.AppImage ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.AppImage
mv packages/frontend/electron/out/*/make/zip/linux/x64/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.zip
mv packages/frontend/electron/out/*/make/AppImage/x64/*.AppImage ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.AppImage
- name: Upload Artifact
uses: actions/upload-artifact@v3
@@ -155,7 +158,6 @@ jobs:
path: builds
package-distribution-windows:
environment: production
strategy:
# all combinations: macos-latest x64, macos-latest arm64, ubuntu-latest x64
# For windows, we need a separate approach
@@ -172,13 +174,15 @@ jobs:
env:
SKIP_GENERATE_ASSETS: 1
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
timeout-minutes: 10
uses: ./.github/actions/setup-node
- name: Setup Maker
timeout-minutes: 10
uses: ./.github/actions/setup-maker
with:
extra-flags: workspaces focus @affine/electron @affine/monorepo
hard-link-nm: false
build-plugins: false
nmHoistingLimits: workspaces
- name: Build AFFiNE native
uses: ./.github/actions/build-rust
with:
@@ -188,7 +192,7 @@ jobs:
- uses: actions/download-artifact@v3
with:
name: core
path: apps/electron/resources/web-static
path: packages/frontend/electron/resources/web-static
- name: Build Plugins
run: yarn run build:plugins
@@ -198,16 +202,20 @@ jobs:
- name: package
run: yarn workspace @affine/electron package --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
env:
SKIP_PLUGIN_BUILD: 1
SKIP_WEB_BUILD: 1
HOIST_NODE_MODULES: 1
- name: get all files to be signed
id: get_files_to_be_signed
run: |
Set-Variable -Name FILES_TO_BE_SIGNED -Value ((Get-ChildItem -Path apps/electron/out -Recurse -File | Where-Object { $_.Extension -in @(".exe", ".node", ".dll", ".msi") } | ForEach-Object { '"' + $_.FullName.Replace((Get-Location).Path + '\apps\electron\out\', '') + '"' }) -join ' ')
Set-Variable -Name FILES_TO_BE_SIGNED -Value ((Get-ChildItem -Path packages/frontend/electron/out -Recurse -File | Where-Object { $_.Extension -in @(".exe", ".node", ".dll", ".msi") } | ForEach-Object { '"' + $_.FullName.Replace((Get-Location).Path + '\packages\frontend\electron\out\', '') + '"' }) -join ' ')
"FILES_TO_BE_SIGNED=$FILES_TO_BE_SIGNED" >> $env:GITHUB_OUTPUT
echo $FILES_TO_BE_SIGNED
- name: Zip artifacts for faster upload
run: Compress-Archive -CompressionLevel Fastest -Path apps/electron/out/* -DestinationPath archive.zip
run: Compress-Archive -CompressionLevel Fastest -Path packages/frontend/electron/out/* -DestinationPath archive.zip
- name: Save packaged artifacts for signing
uses: actions/upload-artifact@v3
@@ -225,7 +233,6 @@ jobs:
artifact-name: packaged-win32-x64
make-windows-installer:
environment: production
needs: sign-packaged-artifacts-windows
strategy:
# all combinations: macos-latest x64, macos-latest arm64, ubuntu-latest x64
@@ -240,7 +247,7 @@ jobs:
outputs:
FILES_TO_BE_SIGNED: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
timeout-minutes: 10
uses: ./.github/actions/setup-node
@@ -250,18 +257,18 @@ jobs:
name: signed-packaged-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}
path: .
- name: unzip file
run: Expand-Archive -Path signed.zip -DestinationPath apps/electron/out
run: Expand-Archive -Path signed.zip -DestinationPath packages/frontend/electron/out
- name: Make squirrel.windows installer
run: yarn workspace @affine/electron make-squirrel --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
- name: Zip artifacts for faster upload
run: Compress-Archive -CompressionLevel Fastest -Path apps/electron/out/${{ env.BUILD_TYPE }}/make/* -DestinationPath archive.zip
run: Compress-Archive -CompressionLevel Fastest -Path packages/frontend/electron/out/${{ env.BUILD_TYPE }}/make/* -DestinationPath archive.zip
- name: get all files to be signed
id: get_files_to_be_signed
run: |
Set-Variable -Name FILES_TO_BE_SIGNED -Value ((Get-ChildItem -Path apps/electron/out/${{ env.BUILD_TYPE }}/make -Recurse -File | Where-Object { $_.Extension -in @(".exe", ".node", ".dll", ".msi") } | ForEach-Object { '"' + $_.FullName.Replace((Get-Location).Path + '\apps\electron\out\${{ env.BUILD_TYPE }}\make\', '') + '"' }) -join ' ')
Set-Variable -Name FILES_TO_BE_SIGNED -Value ((Get-ChildItem -Path packages/frontend/electron/out/${{ env.BUILD_TYPE }}/make -Recurse -File | Where-Object { $_.Extension -in @(".exe", ".node", ".dll", ".msi") } | ForEach-Object { '"' + $_.FullName.Replace((Get-Location).Path + '\packages\frontend\electron\out\${{ env.BUILD_TYPE }}\make\', '') + '"' }) -join ' ')
"FILES_TO_BE_SIGNED=$FILES_TO_BE_SIGNED" >> $env:GITHUB_OUTPUT
echo $FILES_TO_BE_SIGNED
@@ -279,7 +286,6 @@ jobs:
artifact-name: installer-win32-x64
finalize-installer-windows:
environment: production
needs: sign-installer-artifacts-windows
strategy:
# all combinations: macos-latest x64, macos-latest arm64, ubuntu-latest x64
@@ -298,14 +304,14 @@ jobs:
name: signed-installer-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}
path: .
- name: unzip file
run: Expand-Archive -Path signed.zip -DestinationPath apps/electron/out/${{ env.BUILD_TYPE }}/make
run: Expand-Archive -Path signed.zip -DestinationPath packages/frontend/electron/out/${{ env.BUILD_TYPE }}/make
- name: Save artifacts
run: |
mkdir -p builds
mv apps/electron/out/*/make/zip/win32/x64/AFFiNE*-win32-x64-*.zip ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.zip
mv apps/electron/out/*/make/squirrel.windows/x64/*.exe ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.exe
mv apps/electron/out/*/make/squirrel.windows/x64/*.msi ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.msi
mv packages/frontend/electron/out/*/make/zip/win32/x64/AFFiNE*-win32-x64-*.zip ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.zip
mv packages/frontend/electron/out/*/make/squirrel.windows/x64/*.exe ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.exe
mv packages/frontend/electron/out/*/make/squirrel.windows/x64/*.msi ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.msi
- name: Upload Artifact
uses: actions/upload-artifact@v3
@@ -318,7 +324,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/download-artifact@v3
with:
name: core
path: web-static
- name: Zip web-static
run: zip -r web-static.zip web-static
- name: Download Artifacts (macos-x64)
uses: actions/download-artifact@v3
with:
@@ -339,19 +351,16 @@ jobs:
with:
name: affine-linux-x64-builds
path: ./
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 18
- name: Generate Release yml
run: |
cp ./apps/electron/scripts/generate-yml.js .
node generate-yml.js
node ./packages/frontend/electron/scripts/generate-yml.js
env:
RELEASE_VERSION: ${{ github.event.inputs.version || needs.before-make.outputs.RELEASE_VERSION }}
- name: Create Release Draft
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
with:
name: ${{ github.event.inputs.version || needs.before-make.outputs.RELEASE_VERSION }}
body: ''

View File

@@ -3,7 +3,7 @@ name: Release
on:
push:
branches:
- master
- canary
env:
BUILD_TYPE: stable
@@ -17,7 +17,7 @@ jobs:
name: Try publishing npm@latest release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Try publishing to NPM
@@ -31,7 +31,7 @@ jobs:
environment: development
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build Plugins
@@ -42,7 +42,7 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: core
path: ./apps/core/dist
path: ./packages/frontend/core/dist
if-no-files-found: error
build-server:
@@ -50,7 +50,7 @@ jobs:
runs-on: ubuntu-latest
environment: development
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
@@ -61,7 +61,7 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: server-dist
path: ./apps/server/dist
path: ./packages/backend/server/dist
if-no-files-found: error
build-storage:
@@ -72,7 +72,7 @@ jobs:
environment: development
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Setup Rust
@@ -85,11 +85,11 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: storage.node
path: ./packages/storage/storage.node
path: ./packages/backend/storage/storage.node
if-no-files-found: error
build-docker:
if: github.ref == 'refs/heads/master'
if: github.ref == 'refs/heads/canary'
name: Build Docker
runs-on: ubuntu-latest
needs:
@@ -97,38 +97,38 @@ jobs:
- build-core
- build-storage
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Download core artifact
uses: actions/download-artifact@v3
with:
name: core
path: ./apps/core/dist
path: ./packages/frontend/core/dist
- name: Download server dist
uses: actions/download-artifact@v3
with:
name: server-dist
path: ./apps/server/dist
path: ./packages/backend/server/dist
- name: Download storage.node
uses: actions/download-artifact@v3
with:
name: storage.node
path: ./apps/server
path: ./packages/backend/server
- name: Setup Git short hash
run: |
echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: ghcr.io
logout: false
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Build front Dockerfile
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
context: .
push: true
@@ -141,7 +141,7 @@ jobs:
# setup node without cache configuration
# Prisma cache is not compatible with docker build cache
- name: Setup Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
registry-url: https://npm.pkg.github.com
@@ -154,7 +154,7 @@ jobs:
run: yarn workspace @affine/server prisma generate
- name: Build graphql Dockerfile
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
context: .
push: true

View File

@@ -3,9 +3,9 @@ name: Deploy Cloudflare Worker
on:
push:
branches:
- master
- canary
paths:
- packages/workers/**
- tools/workers/**
jobs:
deploy:
@@ -13,10 +13,11 @@ jobs:
name: Deploy
environment: production
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Publish
uses: cloudflare/wrangler-action@2.0.0
uses: cloudflare/wrangler-action@v3.3.2
with:
apiToken: ${{ secrets.CF_API_TOKEN }}
accountId: ${{ secrets.CF_ACCOUNT_ID }}
workingDirectory: 'packages/workers'
workingDirectory: 'tools/workers'
packageManager: 'yarn'

1
.gitignore vendored
View File

@@ -78,3 +78,4 @@ tsconfig.node.tsbuildinfo
lib
affine.db
apps/web/next-routes.conf
.nx

View File

@@ -3,8 +3,8 @@
"version": 1,
"list": [
{
"input": "./packages/i18n/src/resources/en.json",
"output": "./packages/i18n/src/i18n-generated",
"input": "./packages/frontend/i18n/src/resources/en.json",
"output": "./packages/frontend/i18n/src/i18n-generated",
"parser": {
"type": "i18next",
"contextSeparator": "$",

View File

@@ -2,16 +2,22 @@ yarn.lock
target
lib
test-results
packages/i18n/src/i18n-generated.ts
packages/graphql/src/graphql/index.ts
.next
out
dist
.yarn
tests/affine-legacy/**/static
.github/helm
_next
storybook-static
web-static
public
apps/server/src/schema.gql
packages/backend/server/src/schema.gql
packages/frontend/i18n/src/i18n-generated.ts
packages/frontend/graphql/src/graphql/index.ts
tests/affine-legacy/**/static
# auto-generated by NAPI-RS
# fixme(@joooye34): need script to check and generate ignore list here
packages/backend/storage/index.d.ts
packages/frontend/native/index.d.ts
packages/frontend/native/index.js

View File

@@ -29,15 +29,7 @@
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"vitest.include": [
"packages/**/*.spec.ts",
"packages/**/*.spec.tsx",
"apps/web/**/*.spec.ts",
"apps/web/**/*.spec.tsx",
"apps/electron/src/**/*.spec.ts",
"tests/unit/**/*.spec.ts",
"tests/unit/**/*.spec.tsx"
],
"vitest.include": ["packages/**/*.spec.ts", "packages/**/*.spec.tsx"],
"rust-analyzer.check.extraEnv": {
"DATABASE_URL": "sqlite:affine.db"
}

View File

@@ -0,0 +1,15 @@
diff --git a/package.json b/package.json
index ca30bca63196b923fa5a27eb85ce2ee890222d36..39e9d08dea40f25568a39bfbc0154458d32c8a66 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,10 @@
"types": "./index.d.ts",
"default": "./index.js"
},
+ "./core": {
+ "types": "./core/index.d.ts",
+ "default": "./core/index.js"
+ },
"./adapters": {
"types": "./adapters.d.ts"
},

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

893
.yarn/releases/yarn-4.0.1.cjs vendored Executable file

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,7 @@
compressionLevel: mixed
enableGlobalCache: true
nmMode: hardlinks-local
nodeLinker: node-modules
@@ -8,12 +12,4 @@ npmPublishAccess: public
npmPublishRegistry: 'https://registry.npmjs.org'
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: '@yarnpkg/plugin-interactive-tools'
- path: .yarn/plugins/@yarnpkg/plugin-version.cjs
spec: '@yarnpkg/plugin-version'
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: '@yarnpkg/plugin-workspace-tools'
yarnPath: .yarn/releases/yarn-3.6.3.cjs
yarnPath: .yarn/releases/yarn-4.0.1.cjs

660
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
[workspace]
resolver = "2"
members = [
"./packages/native",
"./packages/native/schema",
"./packages/storage",
"./packages/frontend/native",
"./packages/frontend/native/schema",
"./packages/backend/storage",
]
[profile.dev.package.sqlx-macros]

View File

@@ -2,7 +2,7 @@ Copyright (c) 2022-present TOEVERYTHING PTE. LTD. and its affiliates.
Portions of this software are licensed as follows:
- All content that resides under the "apps/server" directory of this repository, if that directory exists, is licensed under the license defined in "apps/server/LICENSE".
- All content that resides under the "packages/backend/server" directory of this repository, if that directory exists, is licensed under the license defined in "packages/backend/server/LICENSE".
- All third party components incorporated into the AFFiNE Software are licensed under the original license provided by the owner of the applicable component.
- Content outside of the above mentioned directories or restrictions above is available under the "MIT" license as defined in "LICENSE-MIT".

View File

@@ -110,9 +110,9 @@ If you have questions, you are welcome to contact us. One of the best places to
| Name | | |
| ----------------------------------------------------------------------------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| [@toeverything/component](https://github.com/toeverything/design/tree/main/packages/components) | Toeverything Shared Component Resources | |
| [@affine/component](packages/component) | AFFiNE Component Resources | [![](https://img.shields.io/codecov/c/github/toeverything/affine?style=flat-square)](https://affine-storybook.vercel.app/) |
| [@toeverything/y-indexeddb](packages/y-indexeddb) | IndexedDB database adapter for Yjs | [![](https://img.shields.io/npm/dm/@toeverything/y-indexeddb?style=flat-square&color=eee)](https://www.npmjs.com/package/@toeverything/y-indexeddb) |
| [@toeverything/theme](packages/theme) | AFFiNE theme | [![](https://img.shields.io/npm/dm/@toeverything/theme?style=flat-square&color=eee)](https://www.npmjs.com/package/@toeverything/theme) |
| [@affine/component](packages/frontend/component) | AFFiNE Component Resources | [![](https://img.shields.io/codecov/c/github/toeverything/affine?style=flat-square)](https://affine-storybook.vercel.app/) |
| [@toeverything/y-indexeddb](packages/common/y-indexeddb) | IndexedDB database adapter for Yjs | [![](https://img.shields.io/npm/dm/@toeverything/y-indexeddb?style=flat-square&color=eee)](https://www.npmjs.com/package/@toeverything/y-indexeddb) |
| [@toeverything/theme](packages/common/theme) | AFFiNE theme | [![](https://img.shields.io/npm/dm/@toeverything/theme?style=flat-square&color=eee)](https://www.npmjs.com/package/@toeverything/theme) |
## Plugins
@@ -120,15 +120,14 @@ If you have questions, you are welcome to contact us. One of the best places to
>
> (Currently, the plugin system is under heavy development. You will see the plugin system in the canary release.)
- [@affine/sdk](./packages/sdk) - SDK for developing plugins
- [@affine/plugin-cli](./packages/plugin-cli) - CLI for developing plugins
- [@affine/sdk](./packages/common/sdk) - SDK for developing plugins
- [@affine/plugin-cli](./tools/plugin-cli) - CLI for developing plugins
| Official Plugin | Description | Status |
| ----------------------------------------------------- | ----------------------------------------- | ------ |
| [@affine/bookmark-plugin](plugins/bookmark) | A block for bookmarking a website | |
| [@affine/copilot-plugin](plugins/copilot) | AI Copilot that help you document writing | 🚧 |
| [@affine/image-preview-plugin](plugins/image-preview) | Component for previewing an image | ✅ |
| [@affine/outline](plugins/outline) | Outline for your document | ✅ |
| Official Plugin | Description | Status |
| ---------------------------------------------------------------- | ----------------------------------------- | ------ |
| [@affine/copilot-plugin](./packages/plugins/copilot) | AI Copilot that help you document writing | 🚧 |
| [@affine/image-preview-plugin](./packages/plugins/image-preview) | Component for previewing an image | |
| [@affine/outline](./packages/plugins/outline) | Outline for your document | ✅ |
## Upstreams
@@ -147,18 +146,7 @@ We would also like to give thanks to open-source projects that make AFFiNE possi
Thanks a lot to the community for providing such powerful and simple libraries, so that we can focus more on the implementation of the product logic, and we hope that in the future our projects will also provide a more easy-to-use knowledge base for everyone.
# Contributors
## Current Core members
Team members who are currently maintaining the project:
- [JimmFly](https://github.com/JimmFly) - Jinfei Yang <yangjinfei001@gmail.com> (he/him)
- [pengx17](https://github.com/pengx17) - Peng Xiao <pengxiao@outlook.com> (he/him)
- [QiShaoXuan](https://github.com/QiSHaoXuan) - Shaoxuan Qi <qishaoxuan777@gmail.com> (he/him)
- [himself65](https://github.com/himself65) - Zeyu "Alex" Yang <himself65@outlook.com> (he/him)
## All Contributors
## Contributors
We would like to express our gratitude to all the individuals who have already contributed to AFFiNE! If you have any AFFiNE-related project, documentation, tool or template, please feel free to contribute it by submitting a pull request to our curated list on GitHub: [awesome-affine](https://github.com/toeverything/awesome-affine).
@@ -207,6 +195,13 @@ For feature request, please see [community.affine.pro](https://community.affine.
## Building
### Codespaces
From the GitHub repo main page, click the green "Code" button and select "Create codespace on master". This will open a new Codespace with the (supposedly auto-forked
AFFiNE repo cloned, built, and ready to go.
### Local
See [BUILDING.md] for instructions on how to build AFFiNE from source code.
## Contributing
@@ -232,11 +227,11 @@ See [LICENSE] for details.
[update page]: https://affine.pro/blog?tag=Release%20Note
[jobs available]: ./docs/jobs.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
[contributor license agreement]: https://github.com/toeverything/affine/edit/canary/.github/CLA.md
[rust-version-icon]: https://img.shields.io/badge/Rust-1.71.0-dea584
[stars-icon]: https://img.shields.io/github/stars/toeverything/AFFiNE.svg?style=flat&logo=github&colorB=red&label=stars
[codecov]: https://codecov.io/gh/toeverything/affine/branch/master/graphs/badge.svg?branch=master
[codecov]: https://codecov.io/gh/toeverything/affine/branch/canary/graphs/badge.svg?branch=canary
[node-version-icon]: https://img.shields.io/badge/node-%3E=18.16.1-success
[typescript-version-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/affine/dev/typescript
[react-version-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/AFFiNE/react?filename=apps%2Fcore%2Fpackage.json&color=rgb(97%2C228%2C251)
[blocksuite-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/AFFiNE/@blocksuite/store?color=6880ff&filename=apps%2Fcore%2Fpackage.json&label=blocksuite
[react-version-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/AFFiNE/react?filename=packages%2Ffrontend%2Fcore%2Fpackage.json&color=rgb(97%2C228%2C251)
[blocksuite-icon]: https://img.shields.io/github/package-json/dependency-version/toeverything/AFFiNE/@blocksuite/store?color=6880ff&filename=packages%2Ffrontend%2Fcore%2Fpackage.json&label=blocksuite

View File

@@ -1,29 +0,0 @@
# Apps structure
> This is the structure of the `apps` directory.
## docs
AFFiNE Developer Documentation using [waku](https://github.com/dai-shi/waku).
## electron
> `core` needs to be built before electron.
AFFiNE Desktop (macOS, Linux and Windows Distribution) using [Electron](https://www.electronjs.org/).
## server
Server using [Nest.js](https://nestjs.com/).
## storybook
Storybook using [Storybook](https://storybook.js.org/).
## prototype
AFFiNE Prototype using [React.js](https://reactjs.org/) + [Vite](https://vitejs.dev/).
## core
AFFiNE Core Application using [React.js](https://reactjs.org/) + [Webpack](https://webpack.js.org/).

View File

@@ -1,12 +0,0 @@
import type { BuildFlags } from '@affine/cli/config';
export function computeCacheKey(buildFlags: BuildFlags) {
return [
'1',
'node' + process.version,
buildFlags.mode,
buildFlags.distribution,
buildFlags.channel,
...(buildFlags.localBlockSuite ? [buildFlags.localBlockSuite] : []),
].join('-');
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -1,166 +0,0 @@
import { setupGlobal } from '@affine/env/global';
import type { WorkspaceAdapter } from '@affine/env/workspace';
import { WorkspaceFlavour } from '@affine/env/workspace';
import type { RootWorkspaceMetadata } from '@affine/workspace/atom';
import {
type RootWorkspaceMetadataV2,
rootWorkspacesMetadataAtom,
workspaceAdaptersAtom,
} from '@affine/workspace/atom';
import {
getOrCreateWorkspace,
globalBlockSuiteSchema,
} from '@affine/workspace/manager';
import { assertExists } from '@blocksuite/global/utils';
import { nanoid } from '@blocksuite/store';
import {
migrateLocalBlobStorage,
migrateWorkspace,
WorkspaceVersion,
} from '@toeverything/infra/blocksuite';
import { downloadBinary, overwriteBinary } from '@toeverything/y-indexeddb';
import type { createStore } from 'jotai/vanilla';
import { applyUpdate, Doc as YDoc, encodeStateAsUpdate } from 'yjs';
import { WorkspaceAdapters } from '../adapters/workspace';
async function tryMigration() {
const value = localStorage.getItem('jotai-workspaces');
if (value) {
try {
const metadata = JSON.parse(value) as RootWorkspaceMetadata[];
const promises: Promise<void>[] = [];
const newMetadata = [...metadata];
metadata.forEach(oldMeta => {
if (oldMeta.flavour === WorkspaceFlavour.LOCAL) {
let doc: YDoc;
const options = {
getCurrentRootDoc: async () => {
doc = new YDoc({
guid: oldMeta.id,
});
const downloadWorkspace = async (doc: YDoc): Promise<void> => {
const binary = await downloadBinary(doc.guid);
if (binary) {
applyUpdate(doc, binary);
}
await Promise.all(
[...doc.subdocs.values()].map(subdoc =>
downloadWorkspace(subdoc)
)
);
};
await downloadWorkspace(doc);
return doc;
},
createWorkspace: async () =>
getOrCreateWorkspace(nanoid(), WorkspaceFlavour.LOCAL),
getSchema: () => globalBlockSuiteSchema,
};
promises.push(
migrateWorkspace(
'version' in oldMeta ? oldMeta.version : undefined,
options
).then(async status => {
if (typeof status !== 'boolean') {
const adapter = WorkspaceAdapters[oldMeta.flavour];
const oldWorkspace = await adapter.CRUD.get(oldMeta.id);
const newId = await adapter.CRUD.create(status);
assertExists(
oldWorkspace,
'workspace should exist after migrate'
);
await adapter.CRUD.delete(oldWorkspace.blockSuiteWorkspace);
const index = newMetadata.findIndex(
meta => meta.id === oldMeta.id
);
newMetadata[index] = {
...oldMeta,
id: newId,
version: WorkspaceVersion.Surface,
};
await migrateLocalBlobStorage(status.id, newId);
console.log('workspace migrated', oldMeta.id, newId);
} else if (status) {
const index = newMetadata.findIndex(
meta => meta.id === oldMeta.id
);
newMetadata[index] = {
...oldMeta,
version: WorkspaceVersion.Surface,
};
const overWrite = async (doc: YDoc): Promise<void> => {
await overwriteBinary(doc.guid, encodeStateAsUpdate(doc));
return Promise.all(
[...doc.subdocs.values()].map(subdoc => overWrite(subdoc))
).then();
};
await overWrite(doc);
console.log('workspace migrated', oldMeta.id);
}
})
);
}
});
await Promise.all(promises)
.then(() => {
console.log('migration done');
})
.catch(e => {
console.error('migration failed', e);
})
.finally(() => {
localStorage.setItem('jotai-workspaces', JSON.stringify(newMetadata));
window.dispatchEvent(new CustomEvent('migration-done'));
window.$migrationDone = true;
});
} catch (e) {
console.error('error when migrating data', e);
}
}
}
export function createFirstAppData(store: ReturnType<typeof createStore>) {
const createFirst = (): RootWorkspaceMetadataV2[] => {
const Plugins = Object.values(WorkspaceAdapters).sort(
(a, b) => a.loadPriority - b.loadPriority
);
return Plugins.flatMap(Plugin => {
return Plugin.Events['app:init']?.().map(
id =>
<RootWorkspaceMetadataV2>{
id,
flavour: Plugin.flavour,
version: WorkspaceVersion.DatabaseV3,
}
);
}).filter((ids): ids is RootWorkspaceMetadataV2 => !!ids);
};
if (localStorage.getItem('is-first-open') !== null) {
return;
}
const result = createFirst();
console.info('create first workspace', result);
localStorage.setItem('is-first-open', 'false');
store.set(rootWorkspacesMetadataAtom, result);
}
export async function setup(store: ReturnType<typeof createStore>) {
store.set(
workspaceAdaptersAtom,
WorkspaceAdapters as Record<
WorkspaceFlavour,
WorkspaceAdapter<WorkspaceFlavour>
>
);
console.log('setup global');
setupGlobal();
await tryMigration();
// do not read `rootWorkspacesMetadataAtom` before migration
await store.get(rootWorkspacesMetadataAtom);
console.log('setup done');
}

View File

@@ -1,114 +0,0 @@
import type {
QueryParamError,
Unreachable,
WorkspaceNotFoundError,
} from '@affine/env/constant';
import { PageNotFoundError } from '@affine/env/constant';
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
import {
currentPageIdAtom,
currentWorkspaceIdAtom,
getCurrentStore,
} from '@toeverything/infra/atom';
import { useAtomValue } from 'jotai/react';
import { Provider } from 'jotai/react';
import type { ErrorInfo, ReactElement, ReactNode } from 'react';
import type React from 'react';
import { Component } from 'react';
import { useLocation, useParams } from 'react-router-dom';
export type AffineErrorBoundaryProps = React.PropsWithChildren;
type AffineError =
| QueryParamError
| Unreachable
| WorkspaceNotFoundError
| PageNotFoundError
| Error;
interface AffineErrorBoundaryState {
error: AffineError | null;
}
export const DumpInfo = () => {
const location = useLocation();
const metadata = useAtomValue(rootWorkspacesMetadataAtom);
const currentWorkspaceId = useAtomValue(currentWorkspaceIdAtom);
const currentPageId = useAtomValue(currentPageIdAtom);
const path = location.pathname;
const query = useParams();
return (
<>
<div>
Please copy the following information and send it to the developer.
</div>
<div
style={{
border: '1px solid red',
}}
>
<div>path: {path}</div>
<div>query: {JSON.stringify(query)}</div>
<div>currentWorkspaceId: {currentWorkspaceId}</div>
<div>currentPageId: {currentPageId}</div>
<div>metadata: {JSON.stringify(metadata)}</div>
</div>
</>
);
};
export class AffineErrorBoundary extends Component<
AffineErrorBoundaryProps,
AffineErrorBoundaryState
> {
public override state: AffineErrorBoundaryState = {
error: null,
};
public static getDerivedStateFromError(
error: AffineError
): AffineErrorBoundaryState {
return { error };
}
public override componentDidCatch(error: AffineError, errorInfo: ErrorInfo) {
console.error('Uncaught error:', error, errorInfo);
}
public override render(): ReactNode {
if (this.state.error) {
let errorDetail: ReactElement | null = null;
const error = this.state.error;
if (error instanceof PageNotFoundError) {
errorDetail = (
<>
<h1>Sorry.. there was an error</h1>
<>
<span> Page error </span>
<span>
Cannot find page {error.pageId} in workspace{' '}
{error.workspace.id}
</span>
</>
</>
);
} else {
errorDetail = (
<>
<h1>Sorry.. there was an error</h1>
{error.message ?? error.toString()}
</>
);
}
return (
<>
{errorDetail}
<Provider key="JotaiProvider" store={getCurrentStore()}>
<DumpInfo />
</Provider>
</>
);
}
return this.props.children;
}
}

View File

@@ -1,87 +0,0 @@
import {
AuthContent,
BackButton,
CountDownRender,
ModalHeader,
} from '@affine/component/auth-components';
import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { Button } from '@toeverything/components/button';
import { useCallback } from 'react';
import { useCurrentLoginStatus } from '../../../hooks/affine/use-current-login-status';
import type { AuthPanelProps } from './index';
import * as style from './style.css';
import { useAuth } from './use-auth';
export const AfterSignInSendEmail = ({
setAuthState,
email,
onSignedIn,
}: AuthPanelProps) => {
const t = useAFFiNEI18N();
const loginStatus = useCurrentLoginStatus();
const { resendCountDown, allowSendEmail, signIn } = useAuth();
if (loginStatus === 'authenticated') {
onSignedIn?.();
}
const onResendClick = useCallback(async () => {
await signIn(email);
}, [email, signIn]);
return (
<>
<ModalHeader
title={t['com.affine.auth.sign.in']()}
subTitle={t['com.affine.auth.sign.in.sent.email.subtitle']()}
/>
<AuthContent style={{ height: 162 }}>
{t['com.affine.auth.sign.sent.email.message.start']()}
<a href={`mailto:${email}`}>{email}</a>
{t['com.affine.auth.sign.sent.email.message.end']()}
</AuthContent>
<div className={style.resendWrapper}>
{allowSendEmail ? (
<Button type="plain" size="large" onClick={onResendClick}>
{t['com.affine.auth.sign.auth.code.resend.hint']()}
</Button>
) : (
<>
<span className="resend-code-hint">
{t['com.affine.auth.sign.auth.code.on.resend.hint']()}
</span>
<CountDownRender
className={style.resendCountdown}
timeLeft={resendCountDown}
/>
</>
)}
</div>
<div className={style.authMessage} style={{ marginTop: 20 }}>
{/*prettier-ignore*/}
<Trans i18nKey="com.affine.auth.sign.auth.code.message.password">
If you haven&apos;t received the email, please check your spam folder.
Or <span
className="link"
data-testid='sign-in-with-password'
onClick={useCallback(() => {
setAuthState('signInWithPassword');
}, [setAuthState])}
>
sign in with password
</span> instead.
</Trans>
</div>
<BackButton
onClick={useCallback(() => {
setAuthState('signIn');
}, [setAuthState])}
/>
</>
);
};

View File

@@ -1,71 +0,0 @@
import { LOCALES } from '@affine/i18n';
import { useI18N } from '@affine/i18n';
import { Menu, MenuItem, MenuTrigger } from '@toeverything/components/menu';
import type { ReactElement } from 'react';
import { useCallback, useMemo } from 'react';
// Fixme: keyboard focus should be supported by Menu component
const LanguageMenuContent = ({
currentLanguage,
onSelect,
}: {
currentLanguage?: string;
onSelect: (value: string) => void;
}) => {
return (
<>
{LOCALES.map(option => {
return (
<MenuItem
key={option.name}
selected={currentLanguage === option.originalName}
title={option.name}
onSelect={() => onSelect(option.tag)}
>
{option.originalName}
</MenuItem>
);
})}
</>
);
};
export const LanguageMenu = () => {
const i18n = useI18N();
const currentLanguage = useMemo(
() => LOCALES.find(item => item.tag === i18n.language),
[i18n.language]
);
const onSelect = useCallback(
(event: string) => {
return i18n.changeLanguage(event);
},
[i18n]
);
return (
<Menu
items={
(
<LanguageMenuContent
currentLanguage={currentLanguage?.originalName}
onSelect={onSelect}
/>
) as ReactElement
}
contentOptions={{
style: {
background: 'var(--affine-white)',
},
align: 'end',
}}
>
<MenuTrigger
data-testid="language-menu-button"
style={{ textTransform: 'capitalize', fontWeight: 600 }}
block={true}
>
{currentLanguage?.originalName || ''}
</MenuTrigger>
</Menu>
);
};

View File

@@ -1,38 +0,0 @@
import { styled } from '@affine/component';
export const StyledModalWrapper = styled('div')(() => {
return {
position: 'relative',
padding: '0px',
width: '560px',
background: 'var(--affine-background-overlay-panel-color)',
borderRadius: '12px',
// height: '312px',
};
});
export const StyledModalHeader = styled('div')(() => {
return {
margin: '44px 0px 12px 0px',
width: '560px',
fontWeight: '600',
fontSize: '20px;',
textAlign: 'center',
};
});
export const StyledInputContent = styled('div')(() => {
return {
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
margin: '24px 0',
fontSize: 'var(--affine-font-base)',
};
});
export const StyledWorkspaceName = styled('span')(() => {
return {
fontWeight: '600',
};
});

View File

@@ -1,45 +0,0 @@
import { globalStyle, style } from '@vanilla-extract/css';
export const settingContent = style({
flexGrow: '1',
height: '100%',
padding: '40px 15px',
overflow: 'hidden',
});
globalStyle(`${settingContent} .wrapper`, {
padding: '0 15px',
height: '100%',
maxWidth: '560px',
margin: '0 auto',
overflowY: 'auto',
});
globalStyle(`${settingContent} .wrapper::-webkit-scrollbar`, {
display: 'none',
});
globalStyle(`${settingContent} .content`, {
minHeight: '100%',
paddingBottom: '80px',
});
globalStyle(`${settingContent} .footer`, {
cursor: 'pointer',
paddingTop: '40px',
marginTop: '-80px',
fontSize: 'var(--affine-font-sm)',
display: 'flex',
minHeight: '100px',
});
globalStyle(`${settingContent} .footer a`, {
color: 'var(--affine-text-primary-color)',
lineHeight: 'normal',
});
export const footerIconWrapper = style({
fontSize: 'var(--affine-font-base)',
color: 'var(--affine-icon-color)',
marginRight: '12px',
height: '19px',
display: 'flex',
alignItems: 'center',
});

View File

@@ -1,110 +0,0 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { assertExists } from '@blocksuite/global/utils';
import { Tooltip } from '@toeverything/components/tooltip';
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
import { useAtom } from 'jotai';
import type { CSSProperties } from 'react';
import { useEffect } from 'react';
import { pageSettingFamily } from '../../../atoms';
import type { BlockSuiteWorkspace } from '../../../shared';
import { toast } from '../../../utils';
import { StyledEditorModeSwitch, StyledKeyboardItem } from './style';
import { EdgelessSwitchItem, PageSwitchItem } from './switch-items';
export type EditorModeSwitchProps = {
// todo(himself65): combine these two properties
blockSuiteWorkspace: BlockSuiteWorkspace;
pageId: string;
style?: CSSProperties;
};
const TooltipContent = () => {
const t = useAFFiNEI18N();
return (
<>
{t['Switch']()}
<StyledKeyboardItem>
{!environment.isServer && environment.isMacOs ? '⌥ + S' : 'Alt + S'}
</StyledKeyboardItem>
</>
);
};
export const EditorModeSwitch = ({
style,
blockSuiteWorkspace,
pageId,
}: EditorModeSwitchProps) => {
const [setting, setSetting] = useAtom(pageSettingFamily(pageId));
const currentMode = setting?.mode ?? 'page';
const pageMeta = useBlockSuitePageMeta(blockSuiteWorkspace).find(
meta => meta.id === pageId
);
const t = useAFFiNEI18N();
assertExists(pageMeta);
const { trash } = pageMeta;
useEffect(() => {
if (trash) {
return;
}
const keydown = (e: KeyboardEvent) => {
if (
!environment.isServer && environment.isMacOs
? e.key === 'ß'
: e.key === 's' && e.altKey
) {
e.preventDefault();
setSetting(setting => {
if (setting?.mode !== 'page') {
toast(t['com.affine.toastMessage.pageMode']());
return { ...setting, mode: 'page' };
} else {
toast(t['com.affine.toastMessage.edgelessMode']());
return { ...setting, mode: 'edgeless' };
}
});
}
};
document.addEventListener('keydown', keydown, { capture: true });
return () =>
document.removeEventListener('keydown', keydown, { capture: true });
}, [setSetting, t, trash]);
return (
<Tooltip content={<TooltipContent />}>
<StyledEditorModeSwitch
style={style}
switchLeft={currentMode === 'page'}
showAlone={trash}
>
<PageSwitchItem
data-testid="switch-page-mode-button"
active={currentMode === 'page'}
hide={trash && currentMode !== 'page'}
trash={trash}
onClick={() => {
setSetting(setting => {
if (setting?.mode !== 'page') {
toast(t['com.affine.toastMessage.pageMode']());
}
return { ...setting, mode: 'page' };
});
}}
/>
<EdgelessSwitchItem
data-testid="switch-edgeless-mode-button"
active={currentMode === 'edgeless'}
hide={trash && currentMode !== 'edgeless'}
trash={trash}
onClick={() => {
setSetting(setting => {
if (setting?.mode !== 'edgeless') {
toast(t['com.affine.toastMessage.edgelessMode']());
}
return { ...setting, mode: 'edgeless' };
});
}}
/>
</StyledEditorModeSwitch>
</Tooltip>
);
};

View File

@@ -1,292 +0,0 @@
import { Empty } from '@affine/component';
import type { ListData, TrashListData } from '@affine/component/page-list';
import { PageList, PageListTrashView } from '@affine/component/page-list';
import type { Collection } from '@affine/env/filter';
import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { assertExists } from '@blocksuite/global/utils';
import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
import { type PageMeta, type Workspace } from '@blocksuite/store';
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
import { useBlockSuitePagePreview } from '@toeverything/hooks/use-block-suite-page-preview';
import { useBlockSuiteWorkspacePage } from '@toeverything/hooks/use-block-suite-workspace-page';
import { useAtom, useAtomValue } from 'jotai';
import { Suspense, useCallback, useMemo } from 'react';
import { allPageModeSelectAtom } from '../../../atoms';
import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper';
import { useGetPageInfoById } from '../../../hooks/use-get-page-info';
import type { BlockSuiteWorkspace } from '../../../shared';
import { toast } from '../../../utils';
import { filterPage } from '../../../utils/filter';
import { currentCollectionsAtom } from '../../../utils/user-setting';
import { emptyDescButton, emptyDescKbd, pageListEmptyStyle } from './index.css';
import { usePageHelper } from './utils';
export interface BlockSuitePageListProps {
blockSuiteWorkspace: BlockSuiteWorkspace;
listType: 'all' | 'trash' | 'shared' | 'public';
isPublic?: boolean;
onOpenPage: (pageId: string, newTab?: boolean) => void;
collection?: Collection;
}
const filter = {
all: (pageMeta: PageMeta) => !pageMeta.trash,
public: (pageMeta: PageMeta) => !pageMeta.trash,
trash: (pageMeta: PageMeta, allMetas: PageMeta[]) => {
const parentMeta = allMetas.find(m => m.subpageIds?.includes(pageMeta.id));
return !parentMeta?.trash && pageMeta.trash;
},
shared: (pageMeta: PageMeta) => pageMeta.isPublic && !pageMeta.trash,
};
interface PagePreviewInnerProps {
workspace: Workspace;
pageId: string;
}
const PagePreviewInner = ({ workspace, pageId }: PagePreviewInnerProps) => {
const page = useBlockSuiteWorkspacePage(workspace, pageId);
assertExists(page);
const previewAtom = useBlockSuitePagePreview(page);
const preview = useAtomValue(previewAtom);
return preview;
};
interface PagePreviewProps {
workspace: Workspace;
pageId: string;
}
const PagePreview = ({ workspace, pageId }: PagePreviewProps) => {
return (
<Suspense>
<PagePreviewInner workspace={workspace} pageId={pageId} />
</Suspense>
);
};
interface PageListEmptyProps {
createPage?: ReturnType<typeof usePageHelper>['createPage'];
listType: BlockSuitePageListProps['listType'];
}
const PageListEmpty = (props: PageListEmptyProps) => {
const { listType, createPage } = props;
const t = useAFFiNEI18N();
const onCreatePage = useCallback(() => {
createPage?.();
}, [createPage]);
const getEmptyDescription = () => {
if (listType === 'all') {
const createNewPageButton = (
<button className={emptyDescButton} onClick={onCreatePage}>
New Page
</button>
);
if (environment.isDesktop) {
const shortcut = environment.isMacOs ? '⌘ + N' : 'Ctrl + N';
return (
<Trans i18nKey="emptyAllPagesClient">
Click on the {createNewPageButton} button Or press
<kbd className={emptyDescKbd}>{{ shortcut } as any}</kbd> to create
your first page.
</Trans>
);
}
return (
<Trans i18nKey="emptyAllPages">
Click on the
{createNewPageButton}
button to create your first page.
</Trans>
);
}
if (listType === 'trash') {
return t['emptyTrash']();
}
if (listType === 'shared') {
return t['emptySharedPages']();
}
return;
};
return (
<div className={pageListEmptyStyle}>
<Empty
title={t['com.affine.emptyDesc']()}
description={getEmptyDescription()}
/>
</div>
);
};
export const BlockSuitePageList = ({
blockSuiteWorkspace,
onOpenPage,
listType,
isPublic = false,
collection,
}: BlockSuitePageListProps) => {
const pageMetas = useBlockSuitePageMeta(blockSuiteWorkspace);
const {
toggleFavorite,
removeToTrash,
restoreFromTrash,
permanentlyDeletePage,
cancelPublicPage,
} = useBlockSuiteMetaHelper(blockSuiteWorkspace);
const [filterMode] = useAtom(allPageModeSelectAtom);
const { createPage, createEdgeless, importFile, isPreferredEdgeless } =
usePageHelper(blockSuiteWorkspace);
const t = useAFFiNEI18N();
const getPageInfo = useGetPageInfoById(blockSuiteWorkspace);
const tagOptionMap = useMemo(
() =>
Object.fromEntries(
(blockSuiteWorkspace.meta.properties.tags?.options ?? []).map(v => [
v.id,
v,
])
),
[blockSuiteWorkspace.meta.properties.tags?.options]
);
const list = useMemo(
() =>
pageMetas
.filter(pageMeta => {
if (filterMode === 'all') {
return true;
}
if (filterMode === 'edgeless') {
return isPreferredEdgeless(pageMeta.id);
}
if (filterMode === 'page') {
return !isPreferredEdgeless(pageMeta.id);
}
console.error('unknown filter mode', pageMeta, filterMode);
return true;
})
.filter(pageMeta => {
if (!filter[listType](pageMeta, pageMetas)) {
return false;
}
if (!collection) {
return true;
}
return filterPage(collection, pageMeta);
}),
[pageMetas, filterMode, isPreferredEdgeless, listType, collection]
);
if (listType === 'trash') {
const pageList: TrashListData[] = list.map(pageMeta => {
return {
icon: isPreferredEdgeless(pageMeta.id) ? (
<EdgelessIcon />
) : (
<PageIcon />
),
pageId: pageMeta.id,
title: pageMeta.title,
preview: (
<PagePreview workspace={blockSuiteWorkspace} pageId={pageMeta.id} />
),
createDate: new Date(pageMeta.createDate),
trashDate: pageMeta.trashDate
? new Date(pageMeta.trashDate)
: undefined,
onClickPage: () => onOpenPage(pageMeta.id),
onClickRestore: () => {
restoreFromTrash(pageMeta.id);
},
onRestorePage: () => {
restoreFromTrash(pageMeta.id);
toast(
t['com.affine.toastMessage.restored']({
title: pageMeta.title || 'Untitled',
})
);
},
onPermanentlyDeletePage: () => {
permanentlyDeletePage(pageMeta.id);
toast(t['com.affine.toastMessage.permanentlyDeleted']());
},
};
});
return (
<PageListTrashView
list={pageList}
fallback={<PageListEmpty listType={listType} />}
/>
);
}
const pageList: ListData[] = list.map(pageMeta => {
const page = blockSuiteWorkspace.getPage(pageMeta.id);
return {
icon: isPreferredEdgeless(pageMeta.id) ? <EdgelessIcon /> : <PageIcon />,
pageId: pageMeta.id,
title: pageMeta.title,
preview: (
<PagePreview workspace={blockSuiteWorkspace} pageId={pageMeta.id} />
),
tags:
page?.meta.tags?.map(id => tagOptionMap[id]).filter(v => v != null) ??
[],
favorite: !!pageMeta.favorite,
isPublicPage: !!pageMeta.isPublic,
createDate: new Date(pageMeta.createDate),
updatedDate: new Date(pageMeta.updatedDate ?? pageMeta.createDate),
onClickPage: () => onOpenPage(pageMeta.id),
onOpenPageInNewTab: () => onOpenPage(pageMeta.id, true),
onClickRestore: () => {
restoreFromTrash(pageMeta.id);
},
removeToTrash: () => {
removeToTrash(pageMeta.id);
toast(t['com.affine.toastMessage.successfullyDeleted']());
},
onRestorePage: () => {
restoreFromTrash(pageMeta.id);
toast(
t['com.affine.toastMessage.restored']({
title: pageMeta.title || 'Untitled',
})
);
},
bookmarkPage: () => {
const status = pageMeta.favorite;
toggleFavorite(pageMeta.id);
toast(
status
? t['com.affine.toastMessage.removedFavorites']()
: t['com.affine.toastMessage.addedFavorites']()
);
},
onDisablePublicSharing: () => {
cancelPublicPage(pageMeta.id);
toast('Successfully disabled', {
portal: document.body,
});
},
};
});
return (
<PageList
collectionsAtom={currentCollectionsAtom}
propertiesMeta={blockSuiteWorkspace.meta.properties}
getPageInfo={getPageInfo}
onCreateNewPage={createPage}
onCreateNewEdgeless={createEdgeless}
onImportFile={importFile}
isPublicWorkspace={isPublic}
list={pageList}
fallback={<PageListEmpty createPage={createPage} listType={listType} />}
/>
);
};

View File

@@ -1,18 +0,0 @@
import { style } from '@vanilla-extract/css';
export const filterContainerStyle = style({
padding: '12px',
display: 'flex',
position: 'relative',
'::after': {
content: '""',
display: 'block',
width: '100%',
height: '1px',
background: 'var(--affine-border-color)',
position: 'absolute',
bottom: 0,
left: 0,
margin: '0 1px',
},
});

View File

@@ -1,81 +0,0 @@
import type {
AffineSocketIOProvider,
LocalIndexedDBBackgroundProvider,
SQLiteProvider,
} from '@affine/env/workspace';
import {
syncDataSourceFromDoc,
syncDocFromDataSource,
} from '@affine/y-provider';
import { assertExists } from '@blocksuite/global/utils';
import { Button } from '@toeverything/components/button';
import { forceUpgradePages } from '@toeverything/infra/blocksuite';
import { useCallback, useMemo, useState } from 'react';
import { useCurrentWorkspace } from '../hooks/current/use-current-workspace';
export const MigrationFallback = function MigrationFallback() {
const [done, setDone] = useState(false);
const [workspace] = useCurrentWorkspace();
const providers = workspace.blockSuiteWorkspace.providers;
const remoteProvider: AffineSocketIOProvider | undefined = useMemo(() => {
return providers.find(
(provider): provider is AffineSocketIOProvider =>
provider.flavour === 'affine-socket-io'
);
}, [providers]);
const localProvider = useMemo(() => {
const sqliteProvider = providers.find(
(provider): provider is SQLiteProvider => provider.flavour === 'sqlite'
);
const indexedDbProvider = providers.find(
(provider): provider is LocalIndexedDBBackgroundProvider =>
provider.flavour === 'local-indexeddb-background'
);
const provider = sqliteProvider || indexedDbProvider;
assertExists(provider, 'no local provider');
return provider;
}, [providers]);
const handleClick = useCallback(async () => {
setDone(false);
await syncDocFromDataSource(
workspace.blockSuiteWorkspace.doc,
localProvider.datasource
);
if (remoteProvider) {
await syncDocFromDataSource(
workspace.blockSuiteWorkspace.doc,
remoteProvider.datasource
);
}
await forceUpgradePages({
getCurrentRootDoc: async () => workspace.blockSuiteWorkspace.doc,
getSchema: () => workspace.blockSuiteWorkspace.schema,
});
await syncDataSourceFromDoc(
workspace.blockSuiteWorkspace.doc,
localProvider.datasource
);
if (remoteProvider) {
await syncDataSourceFromDoc(
workspace.blockSuiteWorkspace.doc,
remoteProvider.datasource
);
}
setDone(true);
}, [
localProvider.datasource,
remoteProvider,
workspace.blockSuiteWorkspace.doc,
workspace.blockSuiteWorkspace.schema,
]);
if (done) {
return <div>Done, please refresh the page.</div>;
}
return (
<Button data-testid="upgrade-workspace" onClick={handleClick}>
Upgrade Workspace
</Button>
);
};

View File

@@ -1,86 +0,0 @@
import { BrowserWarning } from '@affine/component/affine-banner';
import { DownloadTips } from '@affine/component/affine-banner';
import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useAtom } from 'jotai';
import { useEffect, useState } from 'react';
import { guideDownloadClientTipAtom } from '../../../atoms/guide';
const minimumChromeVersion = 102;
const shouldShowWarning = () => {
if (environment.isDesktop) {
// even though desktop has compatibility issues,
// we don't want to show the warning
return false;
}
if (!environment.isBrowser) {
// disable in SSR
return false;
}
if (environment.isChrome) {
return environment.chromeVersion < minimumChromeVersion;
} else {
return !environment.isMobile;
}
};
const OSWarningMessage = () => {
const t = useAFFiNEI18N();
const [notChrome, setNotChrome] = useState(false);
const [notGoodVersion, setNotGoodVersion] = useState(false);
useEffect(() => {
setNotChrome(environment.isBrowser && !environment.isChrome);
setNotGoodVersion(
environment.isBrowser &&
environment.isChrome &&
environment.chromeVersion < minimumChromeVersion
);
}, []);
if (notChrome) {
return (
<span>
<Trans i18nKey="recommendBrowser">
We recommend the <strong>Chrome</strong> browser for an optimal
experience.
</Trans>
</span>
);
} else if (notGoodVersion) {
return <span>{t['upgradeBrowser']()}</span>;
}
return null;
};
export const TopTip = () => {
const [showWarning, setShowWarning] = useState(false);
const [showDownloadTip, setShowDownloadTip] = useAtom(
guideDownloadClientTipAtom
);
useEffect(() => {
setShowWarning(shouldShowWarning());
}, []);
if (showDownloadTip && environment.isDesktop) {
return (
<DownloadTips
onClose={() => {
setShowDownloadTip(false);
localStorage.setItem('affine-is-dt-hide', '1');
}}
/>
);
}
return (
<BrowserWarning
show={showWarning}
message={<OSWarningMessage />}
onClose={() => {
setShowWarning(false);
}}
/>
);
};

View File

@@ -1,148 +0,0 @@
import { MuiFade } from '@affine/component';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CloseIcon, NewIcon, UserGuideIcon } from '@blocksuite/icons';
import { Tooltip } from '@toeverything/components/tooltip';
import { useSetAtom } from 'jotai/react';
import { useAtomValue } from 'jotai/react';
import { useCallback, useState } from 'react';
import { openOnboardingModalAtom, openSettingModalAtom } from '../../../atoms';
import { currentModeAtom } from '../../../atoms/mode';
import { ShortcutsModal } from '../shortcuts-modal';
import { ContactIcon, HelpIcon, KeyboardIcon } from './icons';
import {
StyledAnimateWrapper,
StyledIconWrapper,
StyledIsland,
StyledTriggerWrapper,
} from './style';
const DEFAULT_SHOW_LIST: IslandItemNames[] = [
'whatNew',
'contact',
'shortcuts',
];
const DESKTOP_SHOW_LIST: IslandItemNames[] = [...DEFAULT_SHOW_LIST, 'guide'];
export type IslandItemNames = 'whatNew' | 'contact' | 'shortcuts' | 'guide';
export const HelpIsland = ({
showList = environment.isDesktop ? DESKTOP_SHOW_LIST : DEFAULT_SHOW_LIST,
}: {
showList?: IslandItemNames[];
}) => {
const mode = useAtomValue(currentModeAtom);
const setOpenOnboarding = useSetAtom(openOnboardingModalAtom);
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
const [spread, setShowSpread] = useState(false);
const t = useAFFiNEI18N();
const [openShortCut, setOpenShortCut] = useState(false);
const openAbout = useCallback(() => {
setShowSpread(false);
setOpenSettingModalAtom({
open: true,
activeTab: 'about',
workspaceId: null,
});
}, [setOpenSettingModalAtom]);
return (
<>
<StyledIsland
spread={spread}
data-testid="help-island"
onClick={() => {
setShowSpread(!spread);
}}
inEdgelessPage={mode === 'edgeless'}
>
<StyledAnimateWrapper
style={{ height: spread ? `${showList.length * 40 + 4}px` : 0 }}
>
{showList.includes('whatNew') && (
<Tooltip
content={t['com.affine.appUpdater.whatsNew']()}
side="left"
>
<StyledIconWrapper
data-testid="right-bottom-change-log-icon"
onClick={() => {
window.open(runtimeConfig.changelogUrl, '_blank');
}}
>
<NewIcon />
</StyledIconWrapper>
</Tooltip>
)}
{showList.includes('contact') && (
<Tooltip
content={t['com.affine.helpIsland.contactUs']()}
side="left"
>
<StyledIconWrapper
data-testid="right-bottom-contact-us-icon"
onClick={openAbout}
>
<ContactIcon />
</StyledIconWrapper>
</Tooltip>
)}
{showList.includes('shortcuts') && (
<Tooltip
content={t['com.affine.keyboardShortcuts.title']()}
side="left"
>
<StyledIconWrapper
data-testid="shortcuts-icon"
onClick={() => {
setShowSpread(false);
setOpenShortCut(true);
}}
>
<KeyboardIcon />
</StyledIconWrapper>
</Tooltip>
)}
{showList.includes('guide') && (
<Tooltip
content={t['com.affine.helpIsland.gettingStarted']()}
side="left"
>
<StyledIconWrapper
data-testid="easy-guide"
onClick={() => {
setShowSpread(false);
setOpenOnboarding(true);
}}
>
<UserGuideIcon />
</StyledIconWrapper>
</Tooltip>
)}
</StyledAnimateWrapper>
<Tooltip
content={t['com.affine.helpIsland.helpAndFeedback']()}
side="left"
>
<MuiFade in={!spread} data-testid="faq-icon">
<StyledTriggerWrapper>
<HelpIcon />
</StyledTriggerWrapper>
</MuiFade>
</Tooltip>
<MuiFade in={spread}>
<StyledTriggerWrapper spread>
<CloseIcon />
</StyledTriggerWrapper>
</MuiFade>
</StyledIsland>
<ShortcutsModal
open={openShortCut}
onClose={() => setOpenShortCut(false)}
/>
</>
);
};

View File

@@ -1,60 +0,0 @@
import { WorkspaceSubPath } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import {
DeleteTemporarilyIcon,
FolderIcon,
SettingsIcon,
} from '@blocksuite/icons';
import { useAtom } from 'jotai';
import type { ReactElement, SVGProps } from 'react';
import { useMemo } from 'react';
import { openSettingModalAtom } from '../../../atoms';
type IconComponent = (props: SVGProps<SVGSVGElement>) => ReactElement;
interface ConfigItem {
title: string;
icon: IconComponent;
onClick: () => void;
}
interface ConfigPathItem {
title: string;
icon: IconComponent;
subPath: WorkspaceSubPath;
}
export type Config = ConfigItem | ConfigPathItem;
export const useSwitchToConfig = (workspaceId: string): Config[] => {
const t = useAFFiNEI18N();
const [, setOpenSettingModalAtom] = useAtom(openSettingModalAtom);
return useMemo(
() => [
{
title: t['com.affine.workspaceSubPath.all'](),
subPath: WorkspaceSubPath.ALL,
icon: FolderIcon,
},
{
title: t['Workspace Settings'](),
onClick: () => {
setOpenSettingModalAtom({
open: true,
activeTab: 'workspace',
workspaceId,
});
},
icon: SettingsIcon,
},
{
title: t['com.affine.workspaceSubPath.trash'](),
subPath: WorkspaceSubPath.TRASH,
icon: DeleteTemporarilyIcon,
},
],
[t, workspaceId, setOpenSettingModalAtom]
);
};

View File

@@ -1,59 +0,0 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { assertEquals } from '@blocksuite/global/utils';
import { PlusIcon } from '@blocksuite/icons';
import { nanoid } from '@blocksuite/store';
import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper';
import { initEmptyPage } from '@toeverything/infra/blocksuite';
import { Command } from 'cmdk';
import { useCallback } from 'react';
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
import type { BlockSuiteWorkspace } from '../../../shared';
import { StyledModalFooterContent } from './style';
export interface FooterProps {
query: string;
onClose: () => void;
blockSuiteWorkspace: BlockSuiteWorkspace;
}
export const Footer = ({
query,
onClose,
blockSuiteWorkspace,
}: FooterProps) => {
const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
const t = useAFFiNEI18N();
const { jumpToPage } = useNavigateHelper();
const MAX_QUERY_SHOW_LENGTH = 20;
const normalizedQuery =
query.length > MAX_QUERY_SHOW_LENGTH
? query.slice(0, MAX_QUERY_SHOW_LENGTH) + '...'
: query;
return (
<Command.Item
data-testid="quick-search-add-new-page"
onSelect={useCallback(async () => {
const id = nanoid();
const page = createPage(id);
assertEquals(page.id, id);
await initEmptyPage(page, query);
blockSuiteWorkspace.setPageMeta(page.id, {
title: query,
});
onClose();
jumpToPage(blockSuiteWorkspace.id, page.id);
}, [blockSuiteWorkspace, createPage, jumpToPage, onClose, query])}
>
<StyledModalFooterContent>
<PlusIcon />
{query ? (
<span>{t['New Keyword Page']({ query: normalizedQuery })}</span>
) : (
<span>{t['New Page']()}</span>
)}
</StyledModalFooterContent>
</Command.Item>
);
};

View File

@@ -1,164 +0,0 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { Modal } from '@toeverything/components/modal';
import { Command } from 'cmdk';
import { startTransition, Suspense } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import type { AllWorkspace } from '../../../shared';
import { Footer } from './footer';
import { Results } from './results';
import { SearchInput } from './search-input';
import {
StyledContent,
StyledModalDivider,
StyledModalFooter,
StyledModalHeader,
StyledNotFound,
StyledShortcut,
} from './style';
export interface QuickSearchModalProps {
workspace: AllWorkspace;
open: boolean;
setOpen: (value: boolean) => void;
}
export const QuickSearchModal = ({
open,
setOpen,
workspace,
}: QuickSearchModalProps) => {
const blockSuiteWorkspace = workspace?.blockSuiteWorkspace;
const t = useAFFiNEI18N();
const inputRef = useRef<HTMLInputElement>(null);
const [query, _setQuery] = useState('');
const setQuery = useCallback((query: string) => {
startTransition(() => {
_setQuery(query);
});
}, []);
const [showCreatePage, setShowCreatePage] = useState(true);
const handleClose = useCallback(() => {
setOpen(false);
}, [setOpen]);
// Add ‘⌘+K shortcut keys as switches
useEffect(() => {
const keydown = (e: KeyboardEvent) => {
if ((e.key === 'k' && e.metaKey) || (e.key === 'k' && e.ctrlKey)) {
const selection = window.getSelection();
// prevent search bar focus in firefox
e.preventDefault();
setQuery('');
if (selection?.toString()) {
setOpen(false);
return;
}
setOpen(!open);
}
};
document.addEventListener('keydown', keydown, { capture: true });
return () =>
document.removeEventListener('keydown', keydown, { capture: true });
}, [open, setOpen, setQuery]);
useEffect(() => {
if (open) {
// Waiting for DOM rendering
requestAnimationFrame(() => {
const inputElement = inputRef.current;
inputElement?.focus();
});
}
}, [open]);
return (
<Modal
open={open}
onOpenChange={setOpen}
width={608}
withoutCloseButton
contentOptions={{
['data-testid' as string]: 'quickSearch',
style: {
maxHeight: '80vh',
minHeight: '412px',
top: '80px',
overflow: 'hidden',
transform: 'translateX(-50%)',
padding: 0,
},
}}
>
<Command
shouldFilter={false}
//Handle KeyboardEvent conflicts with blocksuite
onKeyDown={(e: React.KeyboardEvent) => {
if (
e.key === 'ArrowDown' ||
e.key === 'ArrowUp' ||
e.key === 'ArrowLeft' ||
e.key === 'ArrowRight'
) {
e.stopPropagation();
}
}}
>
<StyledModalHeader>
<SearchInput
ref={inputRef}
onValueChange={value => {
setQuery(value);
}}
onKeyDown={e => {
// Avoid triggering the cmdk onSelect event when the input method is in use
if (e.nativeEvent.isComposing) {
e.stopPropagation();
return;
}
}}
placeholder={t['Quick search placeholder']()}
/>
<StyledShortcut>
{environment.isBrowser && environment.isMacOs
? '⌘ + K'
: 'Ctrl + K'}
</StyledShortcut>
</StyledModalHeader>
<StyledModalDivider />
<Command.List>
<StyledContent>
<Suspense
fallback={
<StyledNotFound>
<span>{t['com.affine.loading']()}</span>
</StyledNotFound>
}
>
<Results
query={query}
onClose={handleClose}
workspace={workspace}
setShowCreatePage={setShowCreatePage}
/>
</Suspense>
</StyledContent>
{showCreatePage ? (
<>
<StyledModalDivider />
<StyledModalFooter>
<Footer
query={query}
onClose={handleClose}
blockSuiteWorkspace={blockSuiteWorkspace}
/>
</StyledModalFooter>
</>
) : null}
</Command.List>
</Command>
</Modal>
);
};
export default QuickSearchModal;

View File

@@ -1,188 +0,0 @@
import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { assertExists } from '@blocksuite/global/utils';
import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
import type { Workspace } from '@blocksuite/store';
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper';
import { Command } from 'cmdk';
import { type Atom, atom, useAtomValue } from 'jotai';
import type { Dispatch, SetStateAction } from 'react';
import { startTransition, useEffect } from 'react';
import { recentPageSettingsAtom } from '../../../atoms';
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
import type { AllWorkspace } from '../../../shared';
import { useSwitchToConfig } from './config';
import { StyledListItem, StyledNotFound } from './style';
export interface ResultsProps {
workspace: AllWorkspace;
query: string;
onClose: () => void;
setShowCreatePage: Dispatch<SetStateAction<boolean>>;
}
const loadAllPageWeakMap = new WeakMap<Workspace, Atom<Promise<void>>>();
function getLoadAllPage(workspace: Workspace) {
if (loadAllPageWeakMap.has(workspace)) {
return loadAllPageWeakMap.get(workspace) as Atom<Promise<void>>;
} else {
const aAtom = atom(async () => {
// fixme: we have to load all pages here and re-index them
// there might have performance issue
await Promise.all(
[...workspace.pages.values()].map(page =>
page.waitForLoaded().then(() => {
workspace.indexer.search.refreshPageIndex(page.id, page.spaceDoc);
})
)
);
});
loadAllPageWeakMap.set(workspace, aAtom);
return aAtom;
}
}
export const Results = ({
query,
workspace,
setShowCreatePage,
onClose,
}: ResultsProps) => {
const blockSuiteWorkspace = workspace.blockSuiteWorkspace;
useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
const pageList = useBlockSuitePageMeta(blockSuiteWorkspace);
assertExists(blockSuiteWorkspace.id);
const list = useSwitchToConfig(workspace.id);
useAtomValue(getLoadAllPage(blockSuiteWorkspace));
const recentPageSetting = useAtomValue(recentPageSettingsAtom);
const t = useAFFiNEI18N();
const { jumpToPage, jumpToSubPath } = useNavigateHelper();
const pageIds = [...blockSuiteWorkspace.search({ query }).values()].map(
id => {
if (id.startsWith('space:')) {
return id.slice(6);
} else {
return id;
}
}
);
const resultsPageMeta = pageList.filter(
page => pageIds.indexOf(page.id) > -1 && !page.trash
);
const recentlyViewedItem = recentPageSetting.filter(recent => {
const page = pageList.find(page => recent.id === page.id);
if (!page) {
return false;
} else {
return page.trash !== true;
}
});
useEffect(() => {
startTransition(() => {
setShowCreatePage(resultsPageMeta.length === 0);
});
}, [resultsPageMeta.length, setShowCreatePage]);
if (!query) {
return (
<>
{recentlyViewedItem.length > 0 && (
<Command.Group heading={t['Recent']()}>
{recentlyViewedItem.map(recent => {
const page = pageList.find(page => recent.id === page.id);
assertExists(page);
return (
<Command.Item
key={page.id}
value={page.id}
onSelect={() => {
onClose();
jumpToPage(blockSuiteWorkspace.id, page.id);
}}
>
<StyledListItem>
{recent.mode === 'edgeless' ? (
<EdgelessIcon />
) : (
<PageIcon />
)}
<span>{page.title || UNTITLED_WORKSPACE_NAME}</span>
</StyledListItem>
</Command.Item>
);
})}
</Command.Group>
)}
<Command.Group heading={t['Jump to']()}>
{list.map(link => {
return (
<Command.Item
key={link.title}
value={link.title}
onSelect={() => {
onClose();
if ('subPath' in link) {
jumpToSubPath(blockSuiteWorkspace.id, link.subPath);
} else if ('onClick' in link) {
link.onClick();
} else {
throw new Error('Invalid link');
}
}}
>
<StyledListItem>
<link.icon />
<span>{link.title}</span>
</StyledListItem>
</Command.Item>
);
})}
</Command.Group>
</>
);
}
if (!resultsPageMeta.length) {
return (
<StyledNotFound>
<span>{t['Find 0 result']()}</span>
<img
alt="no result"
src="/imgs/no-result.svg"
width={200}
height={200}
/>
</StyledNotFound>
);
}
return (
<Command.Group
heading={t['Find results']({ number: `${resultsPageMeta.length}` })}
>
{resultsPageMeta.map(result => {
return (
<Command.Item
key={result.id}
onSelect={() => {
onClose();
assertExists(blockSuiteWorkspace.id);
jumpToPage(blockSuiteWorkspace.id, result.id);
}}
value={result.id}
>
<StyledListItem>
{result.mode === 'edgeless' ? <EdgelessIcon /> : <PageIcon />}
<span>{result.title || UNTITLED_WORKSPACE_NAME}</span>
</StyledListItem>
</Command.Item>
);
})}
</Command.Group>
);
};

View File

@@ -1,33 +0,0 @@
import { SearchIcon } from '@blocksuite/icons';
import { Command } from 'cmdk';
import { forwardRef } from 'react';
import { StyledInputContent, StyledLabel } from './style';
export const SearchInput = forwardRef<
HTMLInputElement,
Omit<
React.InputHTMLAttributes<HTMLInputElement>,
'value' | 'onChange' | 'type'
> & {
/**
* Optional controlled state for the value of the search input.
*/
value?: string;
/**
* Event handler called when the search value changes.
*/
onValueChange?: (search: string) => void;
} & React.RefAttributes<HTMLInputElement>
>((props, ref) => {
return (
<StyledInputContent>
<StyledLabel htmlFor=":r5:">
<SearchIcon />
</StyledLabel>
<Command.Input ref={ref} {...props} />
</StyledInputContent>
);
});
SearchInput.displayName = 'SearchInput';

View File

@@ -1,180 +0,0 @@
import { displayFlex, styled, textEllipsis } from '@affine/component';
export const StyledContent = styled('div')(() => {
return {
minHeight: '290px',
maxHeight: '70vh',
width: '100%',
overflow: 'auto',
marginBottom: '10px',
...displayFlex('flex-start', 'flex-start'),
flexDirection: 'column',
color: 'var(--affine-text-primary-color)',
transition: 'all 0.15s',
letterSpacing: '0.06em',
'[cmdk-group]': {
width: '100%',
},
'[cmdk-group-heading]': {
...displayFlex('start', 'center'),
margin: '0 16px',
height: '36px',
lineHeight: '22px',
fontSize: 'var(--affine-font-sm)',
color: 'var(--affine-text-secondary-color)',
},
'[cmdk-item]': {
margin: '0 4px',
},
'[aria-selected="true"]': {
transition: 'all 0.15s',
borderRadius: '4px',
color: 'var(--affine-primary-color)',
backgroundColor: 'var(--affine-hover-color)',
padding: '0 2px',
},
};
});
export const StyledJumpTo = styled('div')(() => {
return {
...displayFlex('center', 'start'),
flexDirection: 'column',
padding: '10px 10px 10px 0',
fontSize: 'var(--affine-font-base)',
strong: {
fontWeight: '500',
marginBottom: '10px',
},
};
});
export const StyledNotFound = styled('div')(() => {
return {
width: '612px',
...displayFlex('center', 'center'),
flexDirection: 'column',
padding: '0 16px',
fontSize: 'var(--affine-font-sm)',
lineHeight: '22px',
color: 'var(--affine-text-secondary-color)',
span: {
...displayFlex('flex-start', 'center'),
width: '100%',
fontWeight: '400',
height: '36px',
},
img: {
marginTop: '10px',
},
};
});
export const StyledInputContent = styled('div')(() => {
return {
...displayFlex('space-between', 'center'),
input: {
width: '492px',
height: '22px',
padding: '0 12px',
fontSize: 'var(--affine-font-base)',
...displayFlex('space-between', 'center'),
letterSpacing: '0.06em',
color: 'var(--affine-text-primary-color)',
'::placeholder': {
color: 'var(--affine-placeholder-color)',
},
},
};
});
export const StyledShortcut = styled('div')(() => {
return {
color: 'var(--affine-placeholder-color)',
fontSize: 'var(--affine-font-sm)',
whiteSpace: 'nowrap',
};
});
export const StyledLabel = styled('label')(() => {
return {
width: '20px',
height: '20px',
color: 'var(--affine-icon-color)',
fontSize: '20px',
};
});
export const StyledModalHeader = styled('div')(() => {
return {
height: '36px',
margin: '12px 16px 0px 16px',
...displayFlex('space-between', 'center'),
};
});
export const StyledModalDivider = styled('div')(() => {
return {
width: 'auto',
height: '0',
margin: '6px 16px',
borderTop: '0.5px solid var(--affine-border-color)',
};
});
export const StyledModalFooter = styled('div')(() => {
return {
fontSize: 'inherit',
lineHeight: '22px',
marginBottom: '8px',
textAlign: 'center',
color: 'var(--affine-text-primary-color)',
...displayFlex('center', 'center'),
transition: 'all .15s',
'[cmdk-item]': {
margin: '0 4px',
},
'[aria-selected="true"]': {
transition: 'all 0.15s',
borderRadius: '4px',
color: 'var(--affine-primary-color)',
backgroundColor: 'var(--affine-hover-color)',
'span,svg': {
transition: 'all 0.15s',
transform: 'scale(1.02)',
},
},
};
});
export const StyledModalFooterContent = styled('button')(() => {
return {
width: '600px',
height: '32px',
fontSize: 'var(--affine-font-base)',
lineHeight: '22px',
textAlign: 'center',
...displayFlex('center', 'center'),
color: 'inherit',
borderRadius: '4px',
transition: 'background .15s, color .15s',
'>svg': {
fontSize: '20px',
marginRight: '12px',
},
};
});
export const StyledListItem = styled('button')(() => {
return {
width: '100%',
height: '32px',
fontSize: 'inherit',
color: 'inherit',
padding: '0 12px',
borderRadius: '4px',
transition: 'all .15s',
...displayFlex('flex-start', 'center'),
span: {
...textEllipsis(1),
},
'> svg': {
fontSize: '20px',
marginRight: '12px',
},
};
});

View File

@@ -1,27 +0,0 @@
export const CloseIcon = () => {
return (
<svg
width="14"
height="14"
viewBox="0 0 14 14"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M7.94 7.00014L13.4667 1.47348C13.5759 1.34594 13.633 1.18189 13.6265 1.01411C13.62 0.846324 13.5504 0.687165 13.4317 0.568435C13.313 0.449706 13.1538 0.38015 12.986 0.37367C12.8183 0.367189 12.6542 0.42426 12.5267 0.533477L7 6.06014L1.47334 0.526811C1.3478 0.401275 1.17754 0.33075 1 0.33075C0.822468 0.33075 0.652205 0.401275 0.526669 0.526811C0.401133 0.652346 0.330608 0.82261 0.330608 1.00014C0.330608 1.17768 0.401133 1.34794 0.526669 1.47348L6.06 7.00014L0.526669 12.5268C0.456881 12.5866 0.400201 12.6601 0.360186 12.7428C0.32017 12.8255 0.297683 12.9156 0.294137 13.0074C0.290591 13.0993 0.306061 13.1908 0.339577 13.2764C0.373094 13.3619 0.423932 13.4396 0.488902 13.5046C0.553872 13.5695 0.63157 13.6204 0.71712 13.6539C0.80267 13.6874 0.894225 13.7029 0.986038 13.6993C1.07785 13.6958 1.16794 13.6733 1.25065 13.6333C1.33336 13.5933 1.4069 13.5366 1.46667 13.4668L7 7.94014L12.5267 13.4668C12.6542 13.576 12.8183 13.6331 12.986 13.6266C13.1538 13.6201 13.313 13.5506 13.4317 13.4319C13.5504 13.3131 13.62 13.154 13.6265 12.9862C13.633 12.8184 13.5759 12.6543 13.4667 12.5268L7.94 7.00014Z" />
</svg>
);
};
export const KeyboardIcon = () => {
return (
<svg
width="20"
height="15"
viewBox="0 0 20 15"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M17.745 0C18.3417 0 18.914 0.237053 19.336 0.65901C19.7579 1.08097 19.995 1.65326 19.995 2.25V11.755C19.995 12.3517 19.7579 12.924 19.336 13.346C18.914 13.7679 18.3417 14.005 17.745 14.005H2.25C1.95453 14.005 1.66194 13.9468 1.38896 13.8337C1.11598 13.7207 0.867941 13.5549 0.65901 13.346C0.450078 13.1371 0.284344 12.889 0.171271 12.616C0.058198 12.3431 0 12.0505 0 11.755V2.25C0 1.65326 0.237053 1.08097 0.65901 0.65901C1.08097 0.237053 1.65326 0 2.25 0H17.745ZM17.745 1.5H2.25C2.05109 1.5 1.86032 1.57902 1.71967 1.71967C1.57902 1.86032 1.5 2.05109 1.5 2.25V11.755C1.5 12.169 1.836 12.505 2.25 12.505H17.745C17.9439 12.505 18.1347 12.426 18.2753 12.2853C18.416 12.1447 18.495 11.9539 18.495 11.755V2.25C18.495 2.05109 18.416 1.86032 18.2753 1.71967C18.1347 1.57902 17.9439 1.5 17.745 1.5ZM4.75 9.5H15.25C15.44 9.50006 15.6229 9.57224 15.7618 9.70197C15.9006 9.8317 15.9851 10.0093 15.998 10.1989C16.011 10.3885 15.9515 10.5759 15.8316 10.7233C15.7117 10.8707 15.5402 10.9671 15.352 10.993L15.25 11H4.75C4.55998 10.9999 4.37706 10.9278 4.23821 10.798C4.09936 10.6683 4.01493 10.4907 4.00197 10.3011C3.98902 10.1115 4.04852 9.92411 4.16843 9.7767C4.28835 9.62929 4.45975 9.5329 4.648 9.507L4.75 9.5H15.25H4.75ZM14.5 6C14.7652 6 15.0196 6.10536 15.2071 6.29289C15.3946 6.48043 15.5 6.73478 15.5 7C15.5 7.26522 15.3946 7.51957 15.2071 7.70711C15.0196 7.89464 14.7652 8 14.5 8C14.2348 8 13.9804 7.89464 13.7929 7.70711C13.6054 7.51957 13.5 7.26522 13.5 7C13.5 6.73478 13.6054 6.48043 13.7929 6.29289C13.9804 6.10536 14.2348 6 14.5 6ZM8.505 6C8.77022 6 9.02457 6.10536 9.21211 6.29289C9.39964 6.48043 9.505 6.73478 9.505 7C9.505 7.26522 9.39964 7.51957 9.21211 7.70711C9.02457 7.89464 8.77022 8 8.505 8C8.23978 8 7.98543 7.89464 7.79789 7.70711C7.61036 7.51957 7.505 7.26522 7.505 7C7.505 6.73478 7.61036 6.48043 7.79789 6.29289C7.98543 6.10536 8.23978 6 8.505 6ZM5.505 6C5.77022 6 6.02457 6.10536 6.21211 6.29289C6.39964 6.48043 6.505 6.73478 6.505 7C6.505 7.26522 6.39964 7.51957 6.21211 7.70711C6.02457 7.89464 5.77022 8 5.505 8C5.23978 8 4.98543 7.89464 4.79789 7.70711C4.61036 7.51957 4.505 7.26522 4.505 7C4.505 6.73478 4.61036 6.48043 4.79789 6.29289C4.98543 6.10536 5.23978 6 5.505 6ZM11.505 6C11.7702 6 12.0246 6.10536 12.2121 6.29289C12.3996 6.48043 12.505 6.73478 12.505 7C12.505 7.26522 12.3996 7.51957 12.2121 7.70711C12.0246 7.89464 11.7702 8 11.505 8C11.2398 8 10.9854 7.89464 10.7979 7.70711C10.6104 7.51957 10.505 7.26522 10.505 7C10.505 6.73478 10.6104 6.48043 10.7979 6.29289C10.9854 6.10536 11.2398 6 11.505 6ZM4 3C4.26522 3 4.51957 3.10536 4.70711 3.29289C4.89464 3.48043 5 3.73478 5 4C5 4.26522 4.89464 4.51957 4.70711 4.70711C4.51957 4.89464 4.26522 5 4 5C3.73478 5 3.48043 4.89464 3.29289 4.70711C3.10536 4.51957 3 4.26522 3 4C3 3.73478 3.10536 3.48043 3.29289 3.29289C3.48043 3.10536 3.73478 3 4 3ZM6.995 3C7.26022 3 7.51457 3.10536 7.70211 3.29289C7.88964 3.48043 7.995 3.73478 7.995 4C7.995 4.26522 7.88964 4.51957 7.70211 4.70711C7.51457 4.89464 7.26022 5 6.995 5C6.72978 5 6.47543 4.89464 6.28789 4.70711C6.10036 4.51957 5.995 4.26522 5.995 4C5.995 3.73478 6.10036 3.48043 6.28789 3.29289C6.47543 3.10536 6.72978 3 6.995 3ZM9.995 3C10.2602 3 10.5146 3.10536 10.7021 3.29289C10.8896 3.48043 10.995 3.73478 10.995 4C10.995 4.26522 10.8896 4.51957 10.7021 4.70711C10.5146 4.89464 10.2602 5 9.995 5C9.72978 5 9.47543 4.89464 9.28789 4.70711C9.10036 4.51957 8.995 4.26522 8.995 4C8.995 3.73478 9.10036 3.48043 9.28789 3.29289C9.47543 3.10536 9.72978 3 9.995 3ZM12.995 3C13.2602 3 13.5146 3.10536 13.7021 3.29289C13.8896 3.48043 13.995 3.73478 13.995 4C13.995 4.26522 13.8896 4.51957 13.7021 4.70711C13.5146 4.89464 13.2602 5 12.995 5C12.7298 5 12.4754 4.89464 12.2879 4.70711C12.1004 4.51957 11.995 4.26522 11.995 4C11.995 3.73478 12.1004 3.48043 12.2879 3.29289C12.4754 3.10536 12.7298 3 12.995 3ZM15.995 3C16.2602 3 16.5146 3.10536 16.7021 3.29289C16.8896 3.48043 16.995 3.73478 16.995 4C16.995 4.26522 16.8896 4.51957 16.7021 4.70711C16.5146 4.89464 16.2602 5 15.995 5C15.7298 5 15.4754 4.89464 15.2879 4.70711C15.1004 4.51957 14.995 4.26522 14.995 4C14.995 3.73478 15.1004 3.48043 15.2879 3.29289C15.4754 3.10536 15.7298 3 15.995 3Z" />
</svg>
);
};

View File

@@ -1,97 +0,0 @@
import { MuiClickAwayListener, MuiSlide } from '@affine/component';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CloseIcon } from '@blocksuite/icons';
import { IconButton } from '@toeverything/components/button';
import {
type ShortcutsInfo,
useEdgelessShortcuts,
useGeneralShortcuts,
useMarkdownShortcuts,
usePageShortcuts,
} from '../../../hooks/affine/use-shortcuts';
import { KeyboardIcon } from './icons';
import * as styles from './style.css';
type ModalProps = {
open: boolean;
onClose: () => void;
};
const ShortcutsPanel = ({
shortcutsInfo,
}: {
shortcutsInfo: ShortcutsInfo;
}) => {
return (
<>
<div className={styles.subtitle}>{shortcutsInfo.title}</div>
{Object.entries(shortcutsInfo.shortcuts).map(([title, shortcuts]) => {
return (
<div className={styles.listItem} key={title}>
<span>{title}</span>
<div className={styles.keyContainer}>
{shortcuts.map(key => {
return (
<span className={styles.key} key={key}>
{key}
</span>
);
})}
</div>
</div>
);
})}
</>
);
};
export const ShortcutsModal = ({ open, onClose }: ModalProps) => {
const t = useAFFiNEI18N();
const markdownShortcutsInfo = useMarkdownShortcuts();
const pageShortcutsInfo = usePageShortcuts();
const edgelessShortcutsInfo = useEdgelessShortcuts();
const generalShortcutsInfo = useGeneralShortcuts();
return (
<MuiSlide direction="left" in={open} mountOnEnter unmountOnExit>
<div className={styles.shortcutsModal} data-testid="shortcuts-modal">
<MuiClickAwayListener
onClickAway={() => {
onClose();
}}
>
<div>
<div
className={styles.modalHeader}
style={{ marginBottom: '-28px' }}
>
<div className={styles.title}>
<KeyboardIcon />
{t['Shortcuts']()}
</div>
<IconButton
style={{
position: 'absolute',
right: 6,
top: 6,
}}
onClick={() => {
onClose();
}}
icon={<CloseIcon />}
/>
</div>
<ShortcutsPanel shortcutsInfo={generalShortcutsInfo} />
<ShortcutsPanel shortcutsInfo={pageShortcutsInfo} />
<ShortcutsPanel shortcutsInfo={edgelessShortcutsInfo} />
<ShortcutsPanel shortcutsInfo={markdownShortcutsInfo} />
</div>
</MuiClickAwayListener>
</div>
</MuiSlide>
);
};

View File

@@ -1,89 +0,0 @@
import { globalStyle, style } from '@vanilla-extract/css';
export const shortcutsModal = style({
width: '288px',
height: '74vh',
paddingBottom: '28px',
backgroundColor: 'var(--affine-white)',
boxShadow: 'var(--affine-popover-shadow)',
borderRadius: `var(--affine-popover-radius)`,
overflow: 'auto',
position: 'fixed',
right: '12px',
top: '0',
bottom: '0',
margin: 'auto',
zIndex: 'var(--affine-z-index-modal)',
});
// export const shortcutsModal = style({
// color: 'var(--affine-text-primary-color)',
// fontWeight: '500',
// fontSize: 'var(--affine-font-sm)',
// height: '44px',
// display: 'flex',
// justifyContent: 'center',
// alignItems: 'center',
// svg: {
// width: '20px',
// marginRight: '14px',
// color: 'var(--affine-primary-color)',
// },
// });
export const title = style({
color: 'var(--affine-text-primary-color)',
fontWeight: '500',
fontSize: 'var(--affine-font-sm)',
height: '44px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
});
globalStyle(`${title} svg`, {
width: '20px',
marginRight: '14px',
color: 'var(--affine-primary-color)',
});
export const subtitle = style({
fontWeight: '500',
fontSize: 'var(--affine-font-sm)',
height: '34px',
lineHeight: '36px',
marginTop: '28px',
padding: '0 16px',
});
export const modalHeader = style({
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
paddingTop: '8px 4px 0 4px',
width: '100%',
padding: '8px 16px 0 16px',
position: 'sticky',
left: '0',
top: '0',
background: 'var(--affine-white)',
transition: 'background-color 0.5s',
});
export const listItem = style({
height: '34px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
fontSize: 'var(--affine-font-sm)',
padding: '0 16px',
});
export const keyContainer = style({
display: 'flex',
});
export const key = style({
selectors: {
'&:not(:last-child)::after': {
content: '+',
margin: '0 4px',
},
},
});

View File

@@ -1,60 +0,0 @@
import {
EditCollectionModal,
useCollectionManager,
} from '@affine/component/page-list';
import type { Collection } from '@affine/env/filter';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { PlusIcon } from '@blocksuite/icons';
import type { Workspace } from '@blocksuite/store';
import { uuidv4 } from '@blocksuite/store';
import { IconButton } from '@toeverything/components/button';
import { useCallback, useState } from 'react';
import { useGetPageInfoById } from '../../../../hooks/use-get-page-info';
import { currentCollectionsAtom } from '../../../../utils/user-setting';
type AddCollectionButtonProps = {
workspace: Workspace;
};
export const AddCollectionButton = ({
workspace,
}: AddCollectionButtonProps) => {
const getPageInfo = useGetPageInfoById(workspace);
const setting = useCollectionManager(currentCollectionsAtom);
const t = useAFFiNEI18N();
const [show, showUpdateCollection] = useState(false);
const [defaultCollection, setDefaultCollection] = useState<Collection>();
const handleClick = useCallback(() => {
showUpdateCollection(true);
setDefaultCollection({
id: uuidv4(),
name: '',
pinned: true,
filterList: [],
workspaceId: workspace.id,
});
}, [showUpdateCollection, workspace.id]);
return (
<>
<IconButton
data-testid="slider-bar-add-collection-button"
onClick={handleClick}
size="small"
>
<PlusIcon />
</IconButton>
<EditCollectionModal
propertiesMeta={workspace.meta.properties}
getPageInfo={getPageInfo}
onConfirm={setting.saveCollection}
open={show}
onOpenChange={showUpdateCollection}
title={t['com.affine.editCollection.saveCollection']()}
init={defaultCollection}
/>
</>
);
};

View File

@@ -1,309 +0,0 @@
import { MenuItem as CollectionItem } from '@affine/component/app-sidebar';
import {
EditCollectionModal,
useCollectionManager,
useSavedCollections,
} from '@affine/component/page-list';
import type { Collection } from '@affine/env/filter';
import type { GetPageInfoById } from '@affine/env/page-info';
import { WorkspaceSubPath } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import {
DeleteIcon,
FilterIcon,
InformationIcon,
MoreHorizontalIcon,
UnpinIcon,
ViewLayersIcon,
} from '@blocksuite/icons';
import type { PageMeta, Workspace } from '@blocksuite/store';
import type { DragEndEvent } from '@dnd-kit/core';
import { useDroppable } from '@dnd-kit/core';
import * as Collapsible from '@radix-ui/react-collapsible';
import { IconButton } from '@toeverything/components/button';
import {
Menu,
MenuIcon,
MenuItem,
type MenuItemProps,
} from '@toeverything/components/menu';
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
import type { ReactElement } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { useGetPageInfoById } from '../../../../hooks/use-get-page-info';
import { useNavigateHelper } from '../../../../hooks/use-navigate-helper';
import { filterPage } from '../../../../utils/filter';
import { currentCollectionsAtom } from '../../../../utils/user-setting';
import type { CollectionsListProps } from '../index';
import { Page } from './page';
import * as styles from './styles.css';
const Collections_DROP_AREA_PREFIX = 'collections-';
const isCollectionsDropArea = (id?: string | number) => {
return typeof id === 'string' && id.startsWith(Collections_DROP_AREA_PREFIX);
};
export const processCollectionsDrag = (e: DragEndEvent) => {
if (
isCollectionsDropArea(e.over?.id) &&
String(e.active.id).startsWith('page-list-item-')
) {
e.over?.data.current?.addToCollection?.(e.active.data.current?.pageId);
}
};
const CollectionOperations = ({
view,
showUpdateCollection,
setting,
}: {
view: Collection;
showUpdateCollection: () => void;
setting: ReturnType<typeof useCollectionManager>;
}) => {
const t = useAFFiNEI18N();
const actions = useMemo<
Array<
| {
icon: ReactElement;
name: string;
click: () => void;
type?: MenuItemProps['type'];
element?: undefined;
}
| {
element: ReactElement;
}
>
>(
() => [
{
icon: (
<MenuIcon>
<FilterIcon />
</MenuIcon>
),
name: t['Edit Filter'](),
click: showUpdateCollection,
},
{
icon: (
<MenuIcon>
<UnpinIcon />
</MenuIcon>
),
name: t['Unpin'](),
click: () => {
return setting.updateCollection({
...view,
pinned: false,
});
},
},
{
element: <div key="divider" className={styles.menuDividerStyle}></div>,
},
{
icon: (
<MenuIcon>
<DeleteIcon />
</MenuIcon>
),
name: t['Delete'](),
click: () => {
return setting.deleteCollection(view.id);
},
type: 'danger',
},
],
[setting, showUpdateCollection, t, view]
);
return (
<div style={{ minWidth: 150 }}>
{actions.map(action => {
if (action.element) {
return action.element;
}
return (
<MenuItem
data-testid="collection-option"
key={action.name}
type={action.type}
preFix={action.icon}
onClick={action.click}
>
{action.name}
</MenuItem>
);
})}
</div>
);
};
const CollectionRenderer = ({
collection,
pages,
workspace,
getPageInfo,
}: {
collection: Collection;
pages: PageMeta[];
workspace: Workspace;
getPageInfo: GetPageInfoById;
}) => {
const [collapsed, setCollapsed] = useState(true);
const setting = useCollectionManager(currentCollectionsAtom);
const { jumpToSubPath } = useNavigateHelper();
const clickCollection = useCallback(() => {
jumpToSubPath(workspace.id, WorkspaceSubPath.ALL);
setting.selectCollection(collection.id);
}, [jumpToSubPath, workspace.id, setting, collection.id]);
const { setNodeRef, isOver } = useDroppable({
id: `${Collections_DROP_AREA_PREFIX}${collection.id}`,
data: {
addToCollection: (id: string) => {
setting.addPage(collection.id, id).catch(err => {
console.error(err);
});
},
},
});
const allPagesMeta = useMemo(
() => Object.fromEntries(pages.map(v => [v.id, v])),
[pages]
);
const [show, showUpdateCollection] = useState(false);
const allowList = useMemo(
() => new Set(collection.allowList),
[collection.allowList]
);
const excludeList = useMemo(
() => new Set(collection.excludeList),
[collection.excludeList]
);
const removeFromAllowList = useCallback(
(id: string) => {
return setting.updateCollection({
...collection,
allowList: collection.allowList?.filter(v => v != id),
});
},
[collection, setting]
);
const addToExcludeList = useCallback(
(id: string) => {
return setting.updateCollection({
...collection,
excludeList: [id, ...(collection.excludeList ?? [])],
});
},
[collection, setting]
);
const pagesToRender = pages.filter(
page => filterPage(collection, page) && !page.trash
);
return (
<Collapsible.Root open={!collapsed}>
<EditCollectionModal
propertiesMeta={workspace.meta.properties}
getPageInfo={getPageInfo}
init={collection}
onConfirm={setting.saveCollection}
open={show}
onOpenChange={showUpdateCollection}
/>
<CollectionItem
data-testid="collection-item"
data-type="collection-list-item"
ref={setNodeRef}
onCollapsedChange={setCollapsed}
active={isOver}
icon={<ViewLayersIcon />}
postfix={
<Menu
items={
<CollectionOperations
view={collection}
showUpdateCollection={() => showUpdateCollection(true)}
setting={setting}
/>
}
>
<IconButton
data-testid="collection-options"
type="plain"
withoutHoverStyle
>
<MoreHorizontalIcon />
</IconButton>
</Menu>
}
collapsed={pagesToRender.length > 0 ? collapsed : undefined}
onClick={clickCollection}
>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}}
>
<div>{collection.name}</div>
</div>
</CollectionItem>
<Collapsible.Content className={styles.collapsibleContent}>
<div style={{ marginLeft: 20, marginTop: -4 }}>
{pagesToRender.map(page => {
return (
<Page
inAllowList={allowList.has(page.id)}
removeFromAllowList={removeFromAllowList}
inExcludeList={excludeList.has(page.id)}
addToExcludeList={addToExcludeList}
allPageMeta={allPagesMeta}
page={page}
key={page.id}
workspace={workspace}
/>
);
})}
</div>
</Collapsible.Content>
</Collapsible.Root>
);
};
export const CollectionsList = ({ workspace }: CollectionsListProps) => {
const metas = useBlockSuitePageMeta(workspace);
const { savedCollections } = useSavedCollections(currentCollectionsAtom);
const getPageInfo = useGetPageInfoById(workspace);
const pinedCollections = useMemo(
() => savedCollections.filter(v => v.pinned),
[savedCollections]
);
const t = useAFFiNEI18N();
if (pinedCollections.length === 0) {
return (
<CollectionItem
data-testid="slider-bar-collection-null-description"
icon={<InformationIcon />}
disabled
>
<span>{t['Create a collection']()}</span>
</CollectionItem>
);
}
return (
<div data-testid="collections" className={styles.wrapper}>
{pinedCollections.map(view => {
return (
<CollectionRenderer
getPageInfo={getPageInfo}
key={view.id}
collection={view}
pages={metas}
workspace={workspace}
/>
);
})}
</div>
);
};

View File

@@ -1,130 +0,0 @@
import { alpha, displayFlex, styled, textEllipsis } from '@affine/component';
export const StyledListItem = styled('div')<{
active?: boolean;
disabled?: boolean;
}>(({ active, disabled }) => {
return {
height: '32px',
color: active
? 'var(--affine-primary-color)'
: 'var(--affine-text-primary-color)',
paddingLeft: '2px',
paddingRight: '2px',
borderRadius: '8px',
cursor: 'pointer',
marginBottom: '4px',
position: 'relative',
flexShrink: 0,
userSelect: 'none',
...displayFlex('flex-start', 'stretch'),
...(disabled
? {
cursor: 'not-allowed',
color: 'var(--affine-border-color)',
}
: {}),
'a > svg, div > svg': {
fontSize: '20px',
marginLeft: '14px',
marginRight: '12px',
color: active
? 'var(--affine-primary-color)'
: 'var(--affine-icon-color)',
},
':hover:not([disabled])': {
backgroundColor: 'var(--affine-hover-color)',
},
};
});
export const StyledCollapseButton = styled('button')<{
collapse: boolean;
show?: boolean;
}>(({ collapse, show = true }) => {
return {
width: '16px',
height: '100%',
...displayFlex('center', 'center'),
fontSize: '16px',
position: 'absolute',
left: '0',
top: '0',
bottom: '0',
margin: 'auto',
color: 'var(--affine-icon-color)',
opacity: '.6',
transition: 'opacity .15s ease-in-out',
display: show ? 'flex' : 'none',
svg: {
transform: `rotate(${collapse ? '0' : '-90'}deg)`,
},
':hover': {
opacity: '1',
},
':focus-visible': {
outline: '-webkit-focus-ring-color auto 1px',
},
};
});
export const StyledCollapseItem = styled('div')<{
disable?: boolean;
active?: boolean;
isOver?: boolean;
textWrap?: boolean;
}>(({ disable = false, active = false, isOver, textWrap = false }) => {
return {
width: '100%',
lineHeight: '1.5',
minHeight: '32px',
borderRadius: '8px',
...displayFlex('flex-start', 'center'),
paddingRight: '2px',
position: 'relative',
color: disable
? 'var(--affine-text-disable-color)'
: active
? 'var(--affine-primary-color)'
: 'var(--affine-text-primary-color)',
cursor: disable ? 'not-allowed' : 'pointer',
background: isOver ? alpha('var(--affine-primary-color)', 0.06) : '',
userSelect: 'none',
...(textWrap
? {
wordBreak: 'break-word',
whiteSpace: 'pre-wrap',
}
: {}),
span: {
flexGrow: '1',
textAlign: 'left',
...textEllipsis(1),
},
'> svg': {
fontSize: '20px',
marginRight: '8px',
flexShrink: '0',
color: active
? 'var(--affine-primary-color)'
: 'var(--affine-icon-color)',
},
':hover': disable
? {}
: {
backgroundColor: 'var(--affine-hover-color)',
'.operation-button': {
visibility: 'visible',
},
},
};
});
export const StyledRouteNavigationWrapper = styled('div')({
height: '32px',
width: '80px',
marginRight: '16px',
...displayFlex('space-between', 'center'),
});

View File

@@ -1,85 +0,0 @@
import { displayFlex, styled, textEllipsis } from '@affine/component';
import { Link } from '@mui/material';
import { baseTheme } from '@toeverything/theme';
export const StyledSliderBarInnerWrapper = styled('div')(() => {
return {
flexGrow: 1,
margin: '0 2px',
position: 'relative',
height: 'calc(100% - 52px * 2)',
display: 'flex',
flexDirection: 'column',
};
});
export const StyledLink = styled(Link)(() => {
return {
flexGrow: 1,
textAlign: 'left',
color: 'inherit',
...displayFlex('flex-start', 'center'),
':visited': {
color: 'inherit',
},
overflow: 'hidden',
div: {
wordBreak: 'break-all',
wordWrap: 'break-word',
whiteSpace: 'nowrap',
...textEllipsis(1),
},
userDrag: 'none',
userSelect: 'none',
appRegion: 'no-drag',
WebkitUserSelect: 'none',
WebkitUserDrag: 'none',
WebkitAppRegion: 'no-drag',
};
});
export const StyledNewPageButton = styled('button')(() => {
return {
width: '100%',
height: '52px',
...displayFlex('flex-start', 'center'),
padding: '0 16px',
svg: {
fontSize: '20px',
color: 'var(--affine-icon-color)',
marginRight: '12px',
},
':hover': {
color: 'var(--affine-primary-color)',
svg: {
color: 'var(--affine-primary-color)',
},
},
};
});
export const StyledSliderModalBackground = styled('div')<{ active: boolean }>(({
active,
}) => {
return {
transition: 'opacity .15s',
pointerEvents: active ? 'auto' : 'none',
opacity: active ? 1 : 0,
position: 'fixed',
top: 0,
left: 0,
right: active ? 0 : '100%',
bottom: 0,
zIndex: parseInt(baseTheme.zIndexModal) - 1,
background: 'var(--affine-background-modal-color)',
};
});
export const StyledScrollWrapper = styled('div')<{
showTopBorder: boolean;
}>(({ showTopBorder }) => {
return {
maxHeight: '50%',
overflowY: 'auto',
borderTop: '1px solid',
borderColor: showTopBorder ? 'var(--affine-border-color)' : 'transparent',
};
});

View File

@@ -1,397 +0,0 @@
import { WorkspaceList } from '@affine/component/workspace-list';
import type {
AffineCloudWorkspace,
LocalWorkspace,
} from '@affine/env/workspace';
import { WorkspaceFlavour, WorkspaceSubPath } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { RootWorkspaceMetadata } from '@affine/workspace/atom';
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
import {
AccountIcon,
ImportIcon,
Logo1Icon,
MoreHorizontalIcon,
PlusIcon,
SignOutIcon,
} from '@blocksuite/icons';
import type { DragEndEvent } from '@dnd-kit/core';
import { arrayMove } from '@dnd-kit/sortable';
import { IconButton } from '@toeverything/components/button';
import { Divider } from '@toeverything/components/divider';
import { Menu, MenuIcon, MenuItem } from '@toeverything/components/menu';
import {
currentPageIdAtom,
currentWorkspaceIdAtom,
} from '@toeverything/infra/atom';
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { useSession } from 'next-auth/react';
import { startTransition, useCallback, useMemo, useTransition } from 'react';
import {
authAtom,
openCreateWorkspaceModalAtom,
openDisableCloudAlertModalAtom,
openSettingModalAtom,
} from '../../../../atoms';
import type { AllWorkspace } from '../../../../shared';
import { signOutCloud } from '../../../../utils/cloud-utils';
import { useNavigateHelper } from '../.././../../hooks/use-navigate-helper';
import {
StyledCreateWorkspaceCardPill,
StyledCreateWorkspaceCardPillContent,
StyledCreateWorkspaceCardPillIcon,
StyledImportWorkspaceCardPill,
StyledItem,
StyledModalBody,
StyledModalContent,
StyledModalFooterContent,
StyledModalHeader,
StyledModalHeaderContent,
StyledModalHeaderLeft,
StyledModalTitle,
StyledOperationWrapper,
StyledSignInCardPill,
StyledSignInCardPillTextCotainer,
StyledSignInCardPillTextPrimary,
StyledSignInCardPillTextSecondary,
StyledWorkspaceFlavourTitle,
} from './styles';
interface WorkspaceModalProps {
disabled?: boolean;
workspaces: RootWorkspaceMetadata[];
currentWorkspaceId: AllWorkspace['id'] | null;
onClickWorkspace: (workspace: RootWorkspaceMetadata['id']) => void;
onClickWorkspaceSetting: (workspace: RootWorkspaceMetadata['id']) => void;
onNewWorkspace: () => void;
onAddWorkspace: () => void;
onMoveWorkspace: (activeId: string, overId: string) => void;
}
const AccountMenu = ({
onOpenAccountSetting,
onSignOut,
}: {
onOpenAccountSetting: () => void;
onSignOut: () => void;
}) => {
const t = useAFFiNEI18N();
return (
<div>
<MenuItem
preFix={
<MenuIcon>
<AccountIcon />
</MenuIcon>
}
data-testid="editor-option-menu-import"
onClick={onOpenAccountSetting}
>
{t['com.affine.workspace.cloud.account.settings']()}
</MenuItem>
<Divider />
<MenuItem
preFix={
<MenuIcon>
<SignOutIcon />
</MenuIcon>
}
data-testid="editor-option-menu-import"
onClick={onSignOut}
>
{t['com.affine.workspace.cloud.account.logout']()}
</MenuItem>
</div>
);
};
const CloudWorkSpaceList = ({
disabled,
workspaces,
onClickWorkspace,
onClickWorkspaceSetting,
currentWorkspaceId,
onMoveWorkspace,
}: WorkspaceModalProps) => {
const t = useAFFiNEI18N();
return (
<>
<StyledModalHeader>
<StyledModalHeaderLeft>
<StyledWorkspaceFlavourTitle>
{t['com.affine.workspace.cloud']()}
</StyledWorkspaceFlavourTitle>
</StyledModalHeaderLeft>
</StyledModalHeader>
<StyledModalContent>
<WorkspaceList
disabled={disabled}
items={
workspaces.filter(
({ flavour }) => flavour === WorkspaceFlavour.AFFINE_CLOUD
) as (AffineCloudWorkspace | LocalWorkspace)[]
}
currentWorkspaceId={currentWorkspaceId}
onClick={onClickWorkspace}
onSettingClick={onClickWorkspaceSetting}
onDragEnd={useCallback(
(event: DragEndEvent) => {
const { active, over } = event;
if (active.id !== over?.id) {
onMoveWorkspace(active.id as string, over?.id as string);
}
},
[onMoveWorkspace]
)}
/>
</StyledModalContent>
</>
);
};
export const UserWithWorkspaceList = ({
onEventEnd,
}: {
onEventEnd?: () => void;
}) => {
const setOpenCreateWorkspaceModal = useSetAtom(openCreateWorkspaceModalAtom);
const { jumpToSubPath, jumpToIndex } = useNavigateHelper();
const workspaces = useAtomValue(rootWorkspacesMetadataAtom, {
delay: 0,
});
const setWorkspaces = useSetAtom(rootWorkspacesMetadataAtom);
const [currentWorkspaceId, setCurrentWorkspaceId] = useAtom(
currentWorkspaceIdAtom
);
const setCurrentPageId = useSetAtom(currentPageIdAtom);
const [, startCloseTransition] = useTransition();
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
const setSettingModalAtom = useSetAtom(openSettingModalAtom);
const t = useAFFiNEI18N();
const setOpen = useSetAtom(authAtom);
const setDisableCloudOpen = useSetAtom(openDisableCloudAlertModalAtom);
// TODO: AFFiNE Cloud support
const { data: session, status } = useSession();
const isLoggedIn = useMemo(() => status === 'authenticated', [status]);
const cloudWorkspaces = useMemo(
() =>
workspaces.filter(
({ flavour }) => flavour === WorkspaceFlavour.AFFINE_CLOUD
) as (AffineCloudWorkspace | LocalWorkspace)[],
[workspaces]
);
const localWorkspaces = useMemo(
() =>
workspaces.filter(
({ flavour }) => flavour === WorkspaceFlavour.LOCAL
) as (AffineCloudWorkspace | LocalWorkspace)[],
[workspaces]
);
const onClickWorkspaceSetting = useCallback(
(workspaceId: string) => {
setOpenSettingModalAtom({
open: true,
activeTab: 'workspace',
workspaceId,
});
onEventEnd?.();
},
[onEventEnd, setOpenSettingModalAtom]
);
const onMoveWorkspace = useCallback(
(activeId: string, overId: string) => {
const oldIndex = workspaces.findIndex(w => w.id === activeId);
const newIndex = workspaces.findIndex(w => w.id === overId);
startTransition(() => {
setWorkspaces(workspaces => arrayMove(workspaces, oldIndex, newIndex));
});
},
[setWorkspaces, workspaces]
);
const onClickWorkspace = useCallback(
(workspaceId: string) => {
startCloseTransition(() => {
setCurrentWorkspaceId(workspaceId);
setCurrentPageId(null);
jumpToSubPath(workspaceId, WorkspaceSubPath.ALL);
});
onEventEnd?.();
},
[jumpToSubPath, onEventEnd, setCurrentPageId, setCurrentWorkspaceId]
);
const onNewWorkspace = useCallback(() => {
setOpenCreateWorkspaceModal('new');
onEventEnd?.();
}, [onEventEnd, setOpenCreateWorkspaceModal]);
const onAddWorkspace = useCallback(async () => {
setOpenCreateWorkspaceModal('add');
onEventEnd?.();
}, [onEventEnd, setOpenCreateWorkspaceModal]);
const onOpenAccountSetting = useCallback(() => {
setSettingModalAtom(prev => ({
...prev,
open: true,
activeTab: 'account',
}));
onEventEnd?.();
}, [onEventEnd, setSettingModalAtom]);
const onSignOut = useCallback(async () => {
signOutCloud()
.then(() => {
jumpToIndex();
})
.catch(console.error);
onEventEnd?.();
}, [onEventEnd, jumpToIndex]);
return (
<>
{!isLoggedIn ? (
<StyledModalHeaderContent>
<StyledSignInCardPill>
<StyledItem
onClick={async () => {
if (!runtimeConfig.enableCloud) {
setDisableCloudOpen(true);
} else {
setOpen(state => ({
...state,
openModal: true,
}));
}
}}
data-testid="cloud-signin-button"
>
<StyledCreateWorkspaceCardPillContent>
<StyledCreateWorkspaceCardPillIcon>
<Logo1Icon />
</StyledCreateWorkspaceCardPillIcon>
<StyledSignInCardPillTextCotainer>
<StyledSignInCardPillTextPrimary>
{t['com.affine.workspace.cloud.auth']()}
</StyledSignInCardPillTextPrimary>
<StyledSignInCardPillTextSecondary>
{t['com.affine.workspace.cloud.description']()}
</StyledSignInCardPillTextSecondary>
</StyledSignInCardPillTextCotainer>
</StyledCreateWorkspaceCardPillContent>
</StyledItem>
</StyledSignInCardPill>
<Divider
style={{
margin: '12px 0px',
}}
/>
</StyledModalHeaderContent>
) : (
<StyledModalHeaderContent>
<StyledModalHeader>
<StyledModalTitle>{session?.user.email}</StyledModalTitle>
<StyledOperationWrapper>
<Menu
items={
<AccountMenu
onOpenAccountSetting={onOpenAccountSetting}
onSignOut={onSignOut}
/>
}
contentOptions={{
side: 'right',
sideOffset: 30,
}}
>
<IconButton
data-testid="more-button"
icon={<MoreHorizontalIcon />}
type="plain"
/>
</Menu>
</StyledOperationWrapper>
</StyledModalHeader>
<Divider style={{ margin: '12px 0px' }} />
</StyledModalHeaderContent>
)}
<StyledModalBody>
{isLoggedIn && cloudWorkspaces.length !== 0 ? (
<>
<CloudWorkSpaceList
workspaces={workspaces}
onClickWorkspace={onClickWorkspace}
onClickWorkspaceSetting={onClickWorkspaceSetting}
onNewWorkspace={onNewWorkspace}
onAddWorkspace={onAddWorkspace}
currentWorkspaceId={currentWorkspaceId}
onMoveWorkspace={onMoveWorkspace}
/>
<Divider
style={{
margin: '12px 0px',
}}
/>
</>
) : null}
<StyledModalHeader>
<StyledWorkspaceFlavourTitle>
{t['com.affine.workspace.local']()}
</StyledWorkspaceFlavourTitle>
</StyledModalHeader>
<StyledModalContent>
<WorkspaceList
items={localWorkspaces}
currentWorkspaceId={currentWorkspaceId}
onClick={onClickWorkspace}
onSettingClick={onClickWorkspaceSetting}
onDragEnd={useCallback(
(event: DragEndEvent) => {
const { active, over } = event;
if (active.id !== over?.id) {
onMoveWorkspace(active.id as string, over?.id as string);
}
},
[onMoveWorkspace]
)}
/>
</StyledModalContent>
{runtimeConfig.enableSQLiteProvider && environment.isDesktop ? (
<StyledImportWorkspaceCardPill>
<StyledItem onClick={onAddWorkspace} data-testid="add-workspace">
<StyledCreateWorkspaceCardPillContent
style={{ gap: '14px', paddingLeft: '2px' }}
>
<StyledCreateWorkspaceCardPillIcon style={{ fontSize: '24px' }}>
<ImportIcon />
</StyledCreateWorkspaceCardPillIcon>
<div>
<p>{t['com.affine.workspace.local.import']()}</p>
</div>
</StyledCreateWorkspaceCardPillContent>
</StyledItem>
</StyledImportWorkspaceCardPill>
) : null}
</StyledModalBody>
<StyledModalFooterContent>
<StyledCreateWorkspaceCardPill>
<StyledItem onClick={onNewWorkspace} data-testid="new-workspace">
<StyledCreateWorkspaceCardPillContent>
<StyledCreateWorkspaceCardPillIcon>
<PlusIcon />
</StyledCreateWorkspaceCardPillIcon>
<div>
<p>{t['New Workspace']()}</p>
</div>
</StyledCreateWorkspaceCardPillContent>
</StyledItem>
</StyledCreateWorkspaceCardPill>
</StyledModalFooterContent>
</>
);
};

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