From 24123ad01cbe2c68179a16253253ecd14ca2a416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=8D=8E=E6=A1=A5?= Date: Fri, 1 Dec 2023 13:29:43 +0800 Subject: [PATCH] Revert "Revert "Merge remote-tracking branch 'origin/canary' into stable"" This reverts commit 89197bacef1500a6a333e12097f45c5303aa7384. --- Cargo.lock | 144 ++++-- docs/issue-triaging.md | 28 ++ .../migration.sql | 2 + packages/backend/server/schema.prisma | 14 +- packages/backend/server/src/modules/config.ts | 30 ++ .../backend/server/src/modules/doc/history.ts | 2 +- packages/backend/server/src/modules/index.ts | 23 +- .../server/src/modules/payment/resolver.ts | 2 +- .../server/src/modules/payment/service.ts | 1 + .../backend/server/src/modules/self-hosted.ts | 38 ++ packages/backend/server/src/schema.gql | 12 + packages/backend/storage/Cargo.toml | 6 +- packages/common/env/package.json | 4 +- packages/common/env/src/global.ts | 1 + packages/common/infra/package.json | 10 +- packages/common/sdk/package.json | 10 +- packages/common/y-indexeddb/package.json | 4 +- packages/common/y-provider/package.json | 2 +- packages/frontend/component/package.json | 10 +- .../src/components/page-list/page-tags.css.ts | 23 +- .../src/components/page-list/page-tags.tsx | 72 +-- .../setting-components/storage-progess.tsx | 36 +- .../frontend/core/.webpack/runtime-config.ts | 8 + packages/frontend/core/package.json | 14 +- .../frontend/core/src/atoms/page-history.ts | 7 + .../affine/auth/user-plan-button.tsx | 5 + .../new-workspace-setting-detail/index.tsx | 8 +- .../new-workspace-setting-detail/members.tsx | 22 +- .../affine/page-history-modal/data.ts | 251 ++++++++++ .../empty-history-shape.tsx | 119 +++++ .../page-history-modal/history-modal.tsx | 446 ++++++++++++++++++ .../affine/page-history-modal/index.tsx | 1 + .../affine/page-history-modal/styles.css.ts | 185 ++++++++ .../setting-modal/account-setting/index.tsx | 4 + .../setting-modal/general-setting/index.tsx | 27 +- .../operation-menu.tsx | 36 +- .../src/components/root-app-sidebar/index.tsx | 4 +- .../core/src/hooks/affine/use-mutate-cloud.ts | 1 + ...se-register-blocksuite-editor-commands.tsx | 40 +- .../src/hooks/affine/use-server-flavor.ts | 34 ++ ...nds.ts => use-browser-history-commands.ts} | 2 +- .../core/src/hooks/use-subscription.ts | 7 + .../core/src/pages/workspace/detail-page.tsx | 5 +- packages/frontend/electron/package.json | 8 +- packages/frontend/electron/scripts/dev.ts | 8 +- .../graphql/src/graphql/histories.gql | 13 + .../frontend/graphql/src/graphql/index.ts | 41 ++ .../graphql/src/graphql/recover-doc.gql | 7 + .../graphql/src/graphql/server-config.gql | 6 + packages/frontend/graphql/src/schema.ts | 57 +++ packages/frontend/hooks/package.json | 12 +- packages/frontend/i18n/src/resources/en.json | 11 +- packages/frontend/native/Cargo.toml | 4 +- packages/frontend/workspace/package.json | 1 + packages/frontend/workspace/src/affine/gql.ts | 91 +++- tests/affine-cloud/e2e/page-history.spec.ts | 146 ++++++ tests/affine-legacy/0.6.1-beta.1/package.json | 8 +- .../0.7.0-canary.18/package.json | 8 +- .../affine-legacy/0.8.0-canary.7/package.json | 8 +- tests/affine-legacy/0.8.4/package.json | 8 +- tests/affine-migration/package.json | 8 +- tests/storybook/package.json | 14 +- yarn.lock | 360 +++++++------- 63 files changed, 2109 insertions(+), 410 deletions(-) create mode 100644 docs/issue-triaging.md create mode 100644 packages/backend/server/migrations/20231124091123_soft_delete_opt_blobs/migration.sql create mode 100644 packages/backend/server/src/modules/config.ts create mode 100644 packages/backend/server/src/modules/self-hosted.ts create mode 100644 packages/frontend/core/src/atoms/page-history.ts create mode 100644 packages/frontend/core/src/components/affine/page-history-modal/data.ts create mode 100644 packages/frontend/core/src/components/affine/page-history-modal/empty-history-shape.tsx create mode 100644 packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx create mode 100644 packages/frontend/core/src/components/affine/page-history-modal/index.tsx create mode 100644 packages/frontend/core/src/components/affine/page-history-modal/styles.css.ts create mode 100644 packages/frontend/core/src/hooks/affine/use-server-flavor.ts rename packages/frontend/core/src/hooks/{use-shortcut-commands.ts => use-browser-history-commands.ts} (95%) create mode 100644 packages/frontend/graphql/src/graphql/histories.gql create mode 100644 packages/frontend/graphql/src/graphql/recover-doc.gql create mode 100644 packages/frontend/graphql/src/graphql/server-config.gql create mode 100644 tests/affine-cloud/e2e/page-history.spec.ts diff --git a/Cargo.lock b/Cargo.lock index 4f03beba90..6e263c1c4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,14 +83,15 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", "getrandom", "once_cell", "version_check", + "zerocopy", ] [[package]] @@ -240,6 +241,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "atomic-write-file" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c232177ba50b16fe7a4588495bd474a62a9e45a8e4ca6fd7d0b7ac29d164631e" +dependencies = [ + "nix", + "rand", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -941,7 +952,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.6", ] [[package]] @@ -950,7 +961,7 @@ version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.6", "allocator-api2", ] @@ -1112,7 +1123,7 @@ dependencies = [ [[package]] name = "jwst-codec" version = "0.1.0" -source = "git+https://github.com/toeverything/OctoBase.git?rev=aad9e5b#aad9e5b7e9d6f479e6cf7555f5845bbbaaadbc66" +source = "git+https://github.com/toeverything/OctoBase.git?rev=49a6b7a#49a6b7af25ce1fe54e8383e10980e9536821d286" dependencies = [ "arbitrary", "bitvec", @@ -1133,7 +1144,7 @@ dependencies = [ [[package]] name = "jwst-core" version = "0.1.0" -source = "git+https://github.com/toeverything/OctoBase.git?rev=aad9e5b#aad9e5b7e9d6f479e6cf7555f5845bbbaaadbc66" +source = "git+https://github.com/toeverything/OctoBase.git?rev=49a6b7a#49a6b7af25ce1fe54e8383e10980e9536821d286" dependencies = [ "async-trait", "base64", @@ -1151,7 +1162,7 @@ dependencies = [ [[package]] name = "jwst-logger" version = "0.1.0" -source = "git+https://github.com/toeverything/OctoBase.git?rev=aad9e5b#aad9e5b7e9d6f479e6cf7555f5845bbbaaadbc66" +source = "git+https://github.com/toeverything/OctoBase.git?rev=49a6b7a#49a6b7af25ce1fe54e8383e10980e9536821d286" dependencies = [ "chrono", "nu-ansi-term 0.49.0", @@ -1164,7 +1175,7 @@ dependencies = [ [[package]] name = "jwst-storage" version = "0.1.0" -source = "git+https://github.com/toeverything/OctoBase.git?rev=aad9e5b#aad9e5b7e9d6f479e6cf7555f5845bbbaaadbc66" +source = "git+https://github.com/toeverything/OctoBase.git?rev=49a6b7a#49a6b7af25ce1fe54e8383e10980e9536821d286" dependencies = [ "anyhow", "async-trait", @@ -1189,7 +1200,7 @@ dependencies = [ [[package]] name = "jwst-storage-migration" version = "0.1.0" -source = "git+https://github.com/toeverything/OctoBase.git?rev=aad9e5b#aad9e5b7e9d6f479e6cf7555f5845bbbaaadbc66" +source = "git+https://github.com/toeverything/OctoBase.git?rev=49a6b7a#49a6b7af25ce1fe54e8383e10980e9536821d286" dependencies = [ "sea-orm-migration", "tokio", @@ -1257,9 +1268,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libsqlite3-sys" -version = "0.26.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" dependencies = [ "cc", "pkg-config", @@ -1337,6 +1348,15 @@ version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1375,9 +1395,9 @@ dependencies = [ [[package]] name = "napi" -version = "2.14.0" +version = "2.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d90182620f32fe34b6ac9b52cba898af26e94c7f5abc01eb4094c417ae2e6c" +checksum = "1133249c46e92da921bafc8aba4912bf84d6c475f7625183772ed2d0844dc3a7" dependencies = [ "anyhow", "bitflags 2.4.1", @@ -1399,9 +1419,9 @@ checksum = "d4b4532cf86bfef556348ac65e561e3123879f0e7566cca6d43a6ff5326f13df" [[package]] name = "napi-derive" -version = "2.14.1" +version = "2.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3619fa472d23cd5af94d63a2bae454a77a8863251f40230fbf59ce20eafa8a86" +checksum = "a0cca5738c6e81eb5ffd2c8ff2b4f05ece9c4c60c7e2b36cec6524492cf7f330" dependencies = [ "cfg-if", "convert_case", @@ -1413,9 +1433,9 @@ dependencies = [ [[package]] name = "napi-derive-backend" -version = "1.0.54" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd3ea4b54020c73d591a49cd192f6334c5f37f71a63ead54dbc851fa991ef00" +checksum = "35960e5f33228192a9b661447d0dfe8f5a3790ff5b4058c4d67680ded4f65b91" dependencies = [ "convert_case", "once_cell", @@ -1435,6 +1455,19 @@ dependencies = [ "libloading", ] +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", + "pin-utils", +] + [[package]] name = "no-std-compat" version = "0.4.1" @@ -2292,18 +2325,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", @@ -2450,9 +2483,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e50c216e3624ec8e7ecd14c6a6a6370aad6ee5d8cfc3ab30b5162eeeef2ed33" +checksum = "dba03c279da73694ef99763320dea58b51095dfe87d001b1d4b5fe78ba8763cf" dependencies = [ "sqlx-core", "sqlx-macros", @@ -2463,11 +2496,11 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d6753e460c998bbd4cd8c6f0ed9a64346fcca0723d6e75e52fdc351c5d2169d" +checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd" dependencies = [ - "ahash 0.8.3", + "ahash 0.8.6", "atoi", "bigdecimal", "byteorder", @@ -2511,9 +2544,9 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a793bb3ba331ec8359c1853bd39eed32cdd7baaf22c35ccf5c92a7e8d1189ec" +checksum = "89961c00dc4d7dffb7aee214964b065072bff69e36ddb9e2c107541f75e4f2a5" dependencies = [ "proc-macro2", "quote", @@ -2524,10 +2557,11 @@ dependencies = [ [[package]] name = "sqlx-macros-core" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4ee1e104e00dedb6aa5ffdd1343107b0a4702e862a84320ee7cc74782d96fc" +checksum = "d0bd4519486723648186a08785143599760f7cc81c52334a55d6a83ea1e20841" dependencies = [ + "atomic-write-file", "dotenvy", "either", "heck", @@ -2550,9 +2584,9 @@ dependencies = [ [[package]] name = "sqlx-mysql" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db" +checksum = "e37195395df71fd068f6e2082247891bc11e3289624bbc776a0cdfa1ca7f1ea4" dependencies = [ "atoi", "base64", @@ -2597,9 +2631,9 @@ dependencies = [ [[package]] name = "sqlx-postgres" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624" +checksum = "d6ac0ac3b7ccd10cc96c7ab29791a7dd236bd94021f31eec7ba3d46a74aa1c24" dependencies = [ "atoi", "base64", @@ -2642,9 +2676,9 @@ dependencies = [ [[package]] name = "sqlx-sqlite" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59dc83cf45d89c555a577694534fcd1b55c545a816c816ce51f20bbe56a4f3f" +checksum = "210976b7d948c7ba9fced8ca835b11cbb2d677c59c79de41ac0d397e14547490" dependencies = [ "atoi", "chrono", @@ -2662,6 +2696,7 @@ dependencies = [ "time", "tracing", "url", + "urlencoding", "uuid", ] @@ -3024,6 +3059,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf8parse" version = "0.2.1" @@ -3032,9 +3073,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58fe91d841bc04822c9801002db4ea904b9e4b8e6bbad25127b46eff8dc516b" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" dependencies = [ "getrandom", "rand", @@ -3141,12 +3182,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.24.0" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888" -dependencies = [ - "rustls-webpki", -] +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" [[package]] name = "whoami" @@ -3278,6 +3316,26 @@ dependencies = [ "tap", ] +[[package]] +name = "zerocopy" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "zeroize" version = "1.6.0" diff --git a/docs/issue-triaging.md b/docs/issue-triaging.md new file mode 100644 index 0000000000..04283de3a5 --- /dev/null +++ b/docs/issue-triaging.md @@ -0,0 +1,28 @@ +# Issues Triaging + +When we receive your issue, we will first triaging it. Triaging an issue usually takes around one business day but may take longer. Goal of triaging is to provide you with a clear understanding of what will happen to your issue. For example, after your feature request was triaged you know whether we plan to tackle the issue or whether we'll wait to hear what the broader community thinks about this request. + +Here are issue states and their descriptions: + +| State | Description | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Untriaged | The team has not yet reviewed the issue. We usually do it within one business day. | +| As designed | The behavior described in the issue is intentional. If you find it seriously disruptive or if we’ve misunderstood you, please let us know in the issue’s comments section. | +| Blocked | We can’t work on this issue until another one (linked) is resolved. | +| Can’t Reproduce | We have been unable to reproduce the issue on our side. It could be flaky or fixed already, or we may not have had all the details we needed. If you’re still experiencing the issue and have any further details, please share them. | +| Duplicate | The issue is the same (or has the same cause) as another one (linked). | +| Fixed | If the issue was a bug, it’s been fixed; if it was a missing feature, it’s been implemented. | +| Fixed In Branch | If the issue was a bug, it’s been fixed; if it was a missing feature, it’s been implemented; the changes are now in a separate branch and haven’t been merged into the default branch yet. | +| In Progress | We’re currently working on the issue. | +| Incomplete | Unfortunately we don’t have enough information to proceed. If you’re willing to share any further details about the issue, please do so in the comments. | +| Obsolete | The part of the product that was causing this issue has been removed or significantly reworked since it was created. | +| Upvoting | We are currently evaluating demand for the issue and checking whether it requires complicated or risky changes. Please leave a vote or comment if you think it should be prioritized. | +| Open | We want to implement the fix or feature in the near future. We can’t promise it will appear in the next public release, but it’s on our short list. | +| Shelved | We have reviewed the issue and decided that, even though it has merit, we cannot currently include it in our near-term plan. | +| Third Party Problem | The issue is caused by a third party. We've done our best to inform them about it. | +| To be Discussed | We need some time to discuss the issue. | +| To Reproduce | We will try to find the steps needed to reproduce the issue on our side. | +| Under Investigation | We’ve triaged the issue, but now we need to investigate it more thoroughly. This may require processing additional information like logs or dumps. | +| Waiting for Info | We’ve requested additional information from the person who created the issue and are waiting for them to get back to us. | +| Declined | We’ve reviewed the suggestion and, while we appreciate its value, we unfortunately do not have the resources to implement it. | +| Answered | The issue actually turned out to be a question or a misunderstanding, and it has been answered or resolved. | diff --git a/packages/backend/server/migrations/20231124091123_soft_delete_opt_blobs/migration.sql b/packages/backend/server/migrations/20231124091123_soft_delete_opt_blobs/migration.sql new file mode 100644 index 0000000000..6e107ce072 --- /dev/null +++ b/packages/backend/server/migrations/20231124091123_soft_delete_opt_blobs/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "optimized_blobs" ADD COLUMN "deleted_at" TIMESTAMPTZ(6); diff --git a/packages/backend/server/schema.prisma b/packages/backend/server/schema.prisma index a35a4187d2..640ad71c4a 100644 --- a/packages/backend/server/schema.prisma +++ b/packages/backend/server/schema.prisma @@ -178,13 +178,15 @@ model Blob { } model OptimizedBlob { - id Int @id @default(autoincrement()) @db.Integer - hash String @db.VarChar - workspaceId String @map("workspace_id") @db.VarChar - params String @db.VarChar - blob Bytes @db.ByteA + id Int @id @default(autoincrement()) @db.Integer + hash String @db.VarChar + workspaceId String @map("workspace_id") @db.VarChar + params String @db.VarChar + blob Bytes @db.ByteA length BigInt - createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + // not for keeping, but for snapshot history + deletedAt DateTime? @map("deleted_at") @db.Timestamptz(6) @@unique([workspaceId, hash, params]) @@map("optimized_blobs") diff --git a/packages/backend/server/src/modules/config.ts b/packages/backend/server/src/modules/config.ts new file mode 100644 index 0000000000..d0fb9e69d4 --- /dev/null +++ b/packages/backend/server/src/modules/config.ts @@ -0,0 +1,30 @@ +import { Module } from '@nestjs/common'; +import { Field, ObjectType, Query } from '@nestjs/graphql'; + +import { SERVER_FLAVOR } from '../modules'; + +@ObjectType() +export class ServerConfigType { + @Field({ description: 'server version' }) + version!: string; + + @Field({ description: 'server flavor' }) + flavor!: string; +} + +export class ServerConfigResolver { + @Query(() => ServerConfigType, { + description: 'server config', + }) + serverConfig(): ServerConfigType { + return { + version: AFFiNE.version, + flavor: SERVER_FLAVOR || 'allinone', + }; + } +} + +@Module({ + providers: [ServerConfigResolver], +}) +export class ServerConfigModule {} diff --git a/packages/backend/server/src/modules/doc/history.ts b/packages/backend/server/src/modules/doc/history.ts index ddb869d18e..2a6dc47554 100644 --- a/packages/backend/server/src/modules/doc/history.ts +++ b/packages/backend/server/src/modules/doc/history.ts @@ -89,7 +89,7 @@ export class DocHistoryManager { workspaceId, id, timestamp: { - lte: before, + lt: before, }, // only include the ones has not expired expiredAt: { diff --git a/packages/backend/server/src/modules/index.ts b/packages/backend/server/src/modules/index.ts index 34458f4a2d..a16215c68e 100644 --- a/packages/backend/server/src/modules/index.ts +++ b/packages/backend/server/src/modules/index.ts @@ -3,9 +3,10 @@ import { EventEmitterModule } from '@nestjs/event-emitter'; import { ScheduleModule } from '@nestjs/schedule'; import { GqlModule } from '../graphql.module'; -import { AuthModule } from './auth'; +import { ServerConfigModule } from './config'; import { DocModule } from './doc'; import { PaymentModule } from './payment'; +import { SelfHostedModule } from './self-hosted'; import { SyncModule } from './sync'; import { UsersModule } from './users'; import { WorkspaceModule } from './workspaces'; @@ -22,13 +23,25 @@ switch (SERVER_FLAVOR) { case 'sync': BusinessModules.push(SyncModule, DocModule.forSync()); break; - case 'graphql': + case 'selfhosted': BusinessModules.push( + ServerConfigModule, + SelfHostedModule, + ScheduleModule.forRoot(), + GqlModule, + WorkspaceModule, + UsersModule, + SyncModule, + DocModule.forRoot() + ); + break; + case 'graphql': + BusinessModules.push( + ServerConfigModule, ScheduleModule.forRoot(), GqlModule, WorkspaceModule, UsersModule, - AuthModule, DocModule.forRoot(), PaymentModule ); @@ -36,11 +49,11 @@ switch (SERVER_FLAVOR) { case 'allinone': default: BusinessModules.push( + ServerConfigModule, ScheduleModule.forRoot(), GqlModule, WorkspaceModule, UsersModule, - AuthModule, SyncModule, DocModule.forRoot(), PaymentModule @@ -48,4 +61,4 @@ switch (SERVER_FLAVOR) { break; } -export { BusinessModules }; +export { BusinessModules, SERVER_FLAVOR }; diff --git a/packages/backend/server/src/modules/payment/resolver.ts b/packages/backend/server/src/modules/payment/resolver.ts index 913609a76d..7374f8e1d5 100644 --- a/packages/backend/server/src/modules/payment/resolver.ts +++ b/packages/backend/server/src/modules/payment/resolver.ts @@ -52,7 +52,7 @@ class SubscriptionPrice { } @ObjectType('UserSubscription') -class UserSubscriptionType implements Partial { +export class UserSubscriptionType implements Partial { @Field({ name: 'id' }) stripeSubscriptionId!: string; diff --git a/packages/backend/server/src/modules/payment/service.ts b/packages/backend/server/src/modules/payment/service.ts index 6dfbf19218..c30ac03279 100644 --- a/packages/backend/server/src/modules/payment/service.ts +++ b/packages/backend/server/src/modules/payment/service.ts @@ -30,6 +30,7 @@ export enum SubscriptionPlan { Pro = 'pro', Team = 'team', Enterprise = 'enterprise', + SelfHosted = 'selfhosted', } export function encodeLookupKey( diff --git a/packages/backend/server/src/modules/self-hosted.ts b/packages/backend/server/src/modules/self-hosted.ts new file mode 100644 index 0000000000..5bd684b981 --- /dev/null +++ b/packages/backend/server/src/modules/self-hosted.ts @@ -0,0 +1,38 @@ +import { Module } from '@nestjs/common'; +import { ResolveField, Resolver } from '@nestjs/graphql'; + +import { UserSubscriptionType } from './payment/resolver'; +import { + SubscriptionPlan, + SubscriptionRecurring, + SubscriptionStatus, +} from './payment/service'; +import { UserType } from './users'; + +const YEAR = 1000 * 60 * 60 * 24 * 30 * 12; + +@Resolver(() => UserType) +export class SelfHostedDummyResolver { + private readonly start = new Date(); + private readonly end = new Date(Number(this.start) + YEAR); + constructor() {} + + @ResolveField(() => UserSubscriptionType) + async subscription() { + return { + stripeSubscriptionId: 'dummy', + plan: SubscriptionPlan.SelfHosted, + recurring: SubscriptionRecurring.Yearly, + status: SubscriptionStatus.Active, + start: this.start, + end: this.end, + createdAt: this.start, + updatedAt: this.start, + }; + } +} + +@Module({ + providers: [SelfHostedDummyResolver], +}) +export class SelfHostedModule {} diff --git a/packages/backend/server/src/schema.gql b/packages/backend/server/src/schema.gql index 9bbce9fb1c..68dc28c522 100644 --- a/packages/backend/server/src/schema.gql +++ b/packages/backend/server/src/schema.gql @@ -2,6 +2,14 @@ # THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY) # ------------------------------------------------------ +type ServerConfigType { + """server version""" + version: String! + + """server flavor""" + flavor: String! +} + type UserType { id: ID! @@ -73,6 +81,7 @@ enum SubscriptionPlan { Pro Team Enterprise + SelfHosted } type UserSubscription { @@ -240,6 +249,9 @@ type DocHistoryType { } type Query { + """server config""" + serverConfig: ServerConfigType! + """Get is owner of workspace""" isOwner(workspaceId: String!): Boolean! diff --git a/packages/backend/storage/Cargo.toml b/packages/backend/storage/Cargo.toml index 7ccff97bdd..b1374cac9f 100644 --- a/packages/backend/storage/Cargo.toml +++ b/packages/backend/storage/Cargo.toml @@ -8,9 +8,9 @@ crate-type = ["cdylib"] [dependencies] chrono = "0.4" -jwst-codec = { git = "https://github.com/toeverything/OctoBase.git", rev = "aad9e5b" } -jwst-core = { git = "https://github.com/toeverything/OctoBase.git", rev = "aad9e5b" } -jwst-storage = { git = "https://github.com/toeverything/OctoBase.git", rev = "aad9e5b" } +jwst-codec = { git = "https://github.com/toeverything/OctoBase.git", rev = "49a6b7a" } +jwst-core = { git = "https://github.com/toeverything/OctoBase.git", rev = "49a6b7a" } +jwst-storage = { git = "https://github.com/toeverything/OctoBase.git", rev = "49a6b7a" } napi = { version = "2", default-features = false, features = [ "napi5", "async", diff --git a/packages/common/env/package.json b/packages/common/env/package.json index 49af4f5c91..ae3ed57436 100644 --- a/packages/common/env/package.json +++ b/packages/common/env/package.json @@ -3,8 +3,8 @@ "private": true, "type": "module", "devDependencies": { - "@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", "react": "18.2.0", "react-dom": "18.2.0", "vitest": "0.34.6", diff --git a/packages/common/env/src/global.ts b/packages/common/env/src/global.ts index 5699cc6e19..7e2125a38e 100644 --- a/packages/common/env/src/global.ts +++ b/packages/common/env/src/global.ts @@ -32,6 +32,7 @@ export const runtimeFlagsSchema = z.object({ enableCaptcha: z.boolean(), enableEnhanceShareMode: z.boolean(), enablePayment: z.boolean(), + enablePageHistory: z.boolean(), // this is for the electron app serverUrlPrefix: z.string(), enableMoveDatabase: z.boolean(), diff --git a/packages/common/infra/package.json b/packages/common/infra/package.json index e9faa19a5f..4ad26cf3a2 100644 --- a/packages/common/infra/package.json +++ b/packages/common/infra/package.json @@ -55,9 +55,9 @@ }, "dependencies": { "@affine/sdk": "workspace:*", - "@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", "jotai": "^2.5.1", "jotai-effect": "^0.2.3", "tinykeys": "^2.1.0", @@ -66,8 +66,8 @@ "devDependencies": { "@affine-test/fixtures": "workspace:*", "@affine/templates": "workspace:*", - "@blocksuite/editor": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/lit": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly", "@testing-library/react": "^14.0.0", "async-call-rpc": "^6.3.1", "electron": "link:../../frontend/electron/node_modules/electron", diff --git a/packages/common/sdk/package.json b/packages/common/sdk/package.json index 5bdbc3842d..1323343945 100644 --- a/packages/common/sdk/package.json +++ b/packages/common/sdk/package.json @@ -22,11 +22,11 @@ "dist" ], "dependencies": { - "@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/editor": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", "jotai": "^2.5.1", "zod": "^3.22.4" }, diff --git a/packages/common/y-indexeddb/package.json b/packages/common/y-indexeddb/package.json index 3a4febebbf..886f038512 100644 --- a/packages/common/y-indexeddb/package.json +++ b/packages/common/y-indexeddb/package.json @@ -37,8 +37,8 @@ "y-provider": "workspace:*" }, "devDependencies": { - "@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", "fake-indexeddb": "^5.0.0", "vite": "^4.4.11", "vite-plugin-dts": "3.6.0", diff --git a/packages/common/y-provider/package.json b/packages/common/y-provider/package.json index 0f2f91962b..915ea6358e 100644 --- a/packages/common/y-provider/package.json +++ b/packages/common/y-provider/package.json @@ -24,7 +24,7 @@ "build": "vite build" }, "devDependencies": { - "@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", "vite": "^4.4.11", "vite-plugin-dts": "3.6.0", "vitest": "0.34.6", diff --git a/packages/frontend/component/package.json b/packages/frontend/component/package.json index 4f7d32c529..f691a46287 100644 --- a/packages/frontend/component/package.json +++ b/packages/frontend/component/package.json @@ -64,12 +64,12 @@ "uuid": "^9.0.1" }, "devDependencies": { - "@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/editor": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", "@blocksuite/icons": "2.1.36", - "@blocksuite/lit": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", "@storybook/jest": "^0.2.3", "@storybook/testing-library": "^0.2.2", "@testing-library/react": "^14.0.0", diff --git a/packages/frontend/component/src/components/page-list/page-tags.css.ts b/packages/frontend/component/src/components/page-list/page-tags.css.ts index 543c655216..fbf535da09 100644 --- a/packages/frontend/component/src/components/page-list/page-tags.css.ts +++ b/packages/frontend/component/src/components/page-list/page-tags.css.ts @@ -1,4 +1,6 @@ -import { style } from '@vanilla-extract/css'; +import { createVar, style } from '@vanilla-extract/css'; + +export const hoverMaxWidth = createVar(); export const root = style({ position: 'relative', @@ -15,7 +17,8 @@ export const tagsContainer = style({ export const tagsScrollContainer = style([ tagsContainer, { - overflow: 'auto', + overflowX: 'hidden', + position: 'relative', height: '100%', gap: '8px', }, @@ -41,7 +44,7 @@ export const innerContainer = style({ transition: 'all 0.2s 0.3s ease-in-out', selectors: { [`${root}:hover &`]: { - maxWidth: 'var(--hover-max-width)', + maxWidth: hoverMaxWidth, }, }, }); @@ -66,6 +69,16 @@ export const innerBackdrop = style({ export const tag = style({ height: '20px', + display: 'flex', + minWidth: 0, + alignItems: 'center', + justifyContent: 'space-between', + ':last-child': { + minWidth: 'max-content', + }, +}); + +export const tagInnerWrapper = style({ display: 'flex', alignItems: 'center', justifyContent: 'space-between', @@ -74,7 +87,7 @@ export const tag = style({ }); export const tagSticky = style([ - tag, + tagInnerWrapper, { fontSize: 'var(--affine-font-xs)', borderRadius: '10px', @@ -82,10 +95,8 @@ export const tagSticky = style([ border: '1px solid var(--affine-border-color)', background: 'var(--affine-background-primary-color)', maxWidth: '128px', - position: 'sticky', textOverflow: 'ellipsis', whiteSpace: 'nowrap', - left: 0, }, ]); diff --git a/packages/frontend/component/src/components/page-list/page-tags.tsx b/packages/frontend/component/src/components/page-list/page-tags.tsx index 8650979983..3fd43ea9df 100644 --- a/packages/frontend/component/src/components/page-list/page-tags.tsx +++ b/packages/frontend/component/src/components/page-list/page-tags.tsx @@ -1,8 +1,9 @@ import type { Tag } from '@affine/env/filter'; import { MoreHorizontalIcon } from '@blocksuite/icons'; import { Menu } from '@toeverything/components/menu'; +import { assignInlineVars } from '@vanilla-extract/dynamic'; import clsx from 'clsx'; -import { useEffect, useMemo, useRef } from 'react'; +import { useMemo } from 'react'; import * as styles from './page-tags.css'; import { stopPropagation } from './utils'; @@ -42,18 +43,22 @@ const TagItem = ({ tag, idx, mode, style }: TagItemProps) => { return (
-
{tag.value}
+ className={mode === 'sticky' ? styles.tagSticky : styles.tagListItem} + > +
+
{tag.value}
+
); }; @@ -69,26 +74,6 @@ export const PageTags = ({ ? widthOnHover : `${widthOnHover}px` : 'auto'; - const tagsContainerRef = useRef(null); - - useEffect(() => { - if (tagsContainerRef.current) { - const tagsContainer = tagsContainerRef.current; - const listener = () => { - // on mouseleave, reset scroll position to the hoverExpandDirection - tagsContainer.scrollTo({ - left: hoverExpandDirection === 'left' ? Number.MAX_SAFE_INTEGER : 0, - behavior: 'smooth', - }); - }; - listener(); - tagsContainerRef.current.addEventListener('mouseleave', listener); - return () => { - tagsContainer.removeEventListener('mouseleave', listener); - }; - } - return; - }, [hoverExpandDirection]); const tagsInPopover = useMemo(() => { const lastTags = tags.slice(maxItems); @@ -107,36 +92,17 @@ export const PageTags = ({ // sort tags by length nTags.sort((a, b) => a.value.length - b.value.length); - const tagRightCharLength = nTags.reduceRight( - (acc, tag) => { - const curr = acc[0] + Math.min(tag.value.length, 10); - return [curr, ...acc]; - }, - [0] - ); - - tagRightCharLength.shift(); - return nTags.map((tag, idx) => ( - + )); }, [maxItems, tags]); return (
-
- {tagsNormal} -
+
{tagsNormal}
{maxItems && tags.length > maxItems ? ( void; plan: SubscriptionPlan; } @@ -23,6 +24,7 @@ enum ButtonType { export const StorageProgress = ({ max: upperLimit, value, + upgradable = true, onUpgrade, plan, }: StorageProgressProgress) => { @@ -63,22 +65,24 @@ export const StorageProgress = ({
- - - - - + {upgradable ? ( + + + + + + ) : null}
); }; diff --git a/packages/frontend/core/.webpack/runtime-config.ts b/packages/frontend/core/.webpack/runtime-config.ts index 52eead3d66..e54ac2410d 100644 --- a/packages/frontend/core/.webpack/runtime-config.ts +++ b/packages/frontend/core/.webpack/runtime-config.ts @@ -33,6 +33,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig { enableCaptcha: true, enableEnhanceShareMode: false, enablePayment: true, + enablePageHistory: false, serverUrlPrefix: 'https://insider.affine.pro', // Let insider be stable environment temporarily. editorFlags, appVersion: packageJson.version, @@ -41,6 +42,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig { get beta() { return { ...this.stable, + enablePageHistory: false, serverUrlPrefix: 'https://insider.affine.pro', }; }, @@ -75,6 +77,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig { enableCaptcha: true, enableEnhanceShareMode: false, enablePayment: true, + enablePageHistory: true, serverUrlPrefix: 'https://affine.fail', editorFlags, appVersion: packageJson.version, @@ -142,6 +145,11 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig { : buildFlags.mode === 'development' ? true : currentBuildPreset.enablePayment, + enablePageHistory: process.env.ENABLE_PAGE_HISTORY + ? process.env.ENABLE_PAGE_HISTORY === 'true' + : buildFlags.mode === 'development' + ? true + : currentBuildPreset.enablePageHistory, }; if (buildFlags.mode === 'development') { diff --git a/packages/frontend/core/package.json b/packages/frontend/core/package.json index 5d69463998..a8e7fe2559 100644 --- a/packages/frontend/core/package.json +++ b/packages/frontend/core/package.json @@ -25,14 +25,14 @@ "@affine/i18n": "workspace:*", "@affine/templates": "workspace:*", "@affine/workspace": "workspace:*", - "@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/editor": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", "@blocksuite/icons": "2.1.36", - "@blocksuite/lit": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/virgo": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/virgo": "0.0.0-20231124123613-7c06e95d-nightly", "@dnd-kit/core": "^6.0.8", "@dnd-kit/sortable": "^8.0.0", "@emotion/cache": "^11.11.0", diff --git a/packages/frontend/core/src/atoms/page-history.ts b/packages/frontend/core/src/atoms/page-history.ts new file mode 100644 index 0000000000..aef79debb7 --- /dev/null +++ b/packages/frontend/core/src/atoms/page-history.ts @@ -0,0 +1,7 @@ +import { atom } from 'jotai'; + +// make page history controllable by atom to make it easier to use in CMDK +export const pageHistoryModalAtom = atom({ + open: false, + pageId: '', +}); diff --git a/packages/frontend/core/src/components/affine/auth/user-plan-button.tsx b/packages/frontend/core/src/components/affine/auth/user-plan-button.tsx index aa0b417c6f..dc1ecfdb29 100644 --- a/packages/frontend/core/src/components/affine/auth/user-plan-button.tsx +++ b/packages/frontend/core/src/components/affine/auth/user-plan-button.tsx @@ -28,6 +28,11 @@ const UserPlanButtonWithData = () => { const t = useAFFiNEI18N(); + if (plan === SubscriptionPlan.SelfHosted) { + // Self hosted version doesn't have a payment apis. + return
{plan}
; + } + return (
diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/index.tsx b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/index.tsx index 6732ce7b0e..56af1e62ef 100644 --- a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/index.tsx +++ b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/index.tsx @@ -7,6 +7,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name'; import { useMemo } from 'react'; +import { useSelfHosted } from '../../../hooks/affine/use-server-flavor'; import { useWorkspace } from '../../../hooks/use-workspace'; import { DeleteLeaveWorkspace } from './delete-leave-workspace'; import { ExportPanel } from './export'; @@ -20,6 +21,7 @@ import type { WorkspaceSettingDetailProps } from './types'; export const WorkspaceSettingDetail = (props: WorkspaceSettingDetailProps) => { const { workspaceId } = props; const t = useAFFiNEI18N(); + const isSelfHosted = useSelfHosted(); const workspace = useWorkspace(workspaceId); const [name] = useBlockSuiteWorkspaceName(workspace.blockSuiteWorkspace); @@ -56,7 +58,11 @@ export const WorkspaceSettingDetail = (props: WorkspaceSettingDetailProps) => { - + {storageAndExportSetting} diff --git a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/members.tsx b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/members.tsx index 83e5057f04..5901dce957 100644 --- a/packages/frontend/core/src/components/affine/new-workspace-setting-detail/members.tsx +++ b/packages/frontend/core/src/components/affine/new-workspace-setting-detail/members.tsx @@ -50,6 +50,7 @@ enum MemberLimitCount { const COUNT_PER_PAGE = 8; export interface MembersPanelProps extends WorkspaceSettingDetailProps { + upgradable: boolean; workspace: AffineOfficialWorkspace; } type OnRevoke = (memberId: string) => void; @@ -69,6 +70,7 @@ const MembersPanelLocal = () => { export const CloudWorkspaceMembersPanel = ({ workspace, isOwner, + upgradable, }: MembersPanelProps) => { const workspaceId = workspace.id; const memberCount = useMemberCount(workspaceId); @@ -164,16 +166,20 @@ export const CloudWorkspaceMembersPanel = ({ planName: plan, memberLimit, })} - , -
- - {t['com.affine.payment.member.description.go-upgrade']()} - - -
+ {upgradable ? ( + <> + , +
+ + {t['com.affine.payment.member.description.go-upgrade']()} + + +
+ + ) : null} ); - }, [handleUpgrade, memberLimit, plan, t]); + }, [handleUpgrade, memberLimit, plan, t, upgradable]); return ( <> diff --git a/packages/frontend/core/src/components/affine/page-history-modal/data.ts b/packages/frontend/core/src/components/affine/page-history-modal/data.ts new file mode 100644 index 0000000000..c3844ebd7c --- /dev/null +++ b/packages/frontend/core/src/components/affine/page-history-modal/data.ts @@ -0,0 +1,251 @@ +import { DebugLogger } from '@affine/debug'; +import { + fetchWithTraceReport, + type ListHistoryQuery, + listHistoryQuery, + recoverDocMutation, +} from '@affine/graphql'; +import { + useMutateQueryResource, + useMutation, + useQueryInfinite, +} from '@affine/workspace/affine/gql'; +import { createAffineCloudBlobEngine } from '@affine/workspace/blob'; +import { globalBlockSuiteSchema } from '@affine/workspace/manager'; +import { assertEquals } from '@blocksuite/global/utils'; +import { Workspace } from '@blocksuite/store'; +import { usePageMetaHelper } from '@toeverything/hooks/use-block-suite-page-meta'; +import { useBlockSuiteWorkspacePage } from '@toeverything/hooks/use-block-suite-workspace-page'; +import { revertUpdate } from '@toeverything/y-indexeddb'; +import { useMemo } from 'react'; +import useSWRImmutable from 'swr/immutable'; +import { applyUpdate } from 'yjs'; + +const logger = new DebugLogger('page-history'); + +type DocHistory = ListHistoryQuery['workspace']['histories'][number]; + +export const usePageSnapshotList = (workspaceId: string, pageDocId: string) => { + const pageSize = 10; + const { data, loadingMore, loadMore } = useQueryInfinite({ + query: listHistoryQuery, + getVariables: (_, previousPageData) => { + // use the timestamp of the last history as the cursor + const before = previousPageData?.workspace.histories.at(-1)?.timestamp; + const vars = { + pageDocId: pageDocId, + workspaceId: workspaceId, + before: before, + take: pageSize, + }; + + return vars; + }, + }); + + const shouldLoadMore = useMemo(() => { + if (!data) { + return false; + } + const lastPage = data.at(-1); + if (!lastPage) { + return false; + } + return lastPage.workspace.histories.length === pageSize; + }, [data]); + + const histories = useMemo(() => { + if (!data) { + return []; + } + return data.flatMap(page => page.workspace.histories); + }, [data]); + + return [ + histories, + shouldLoadMore ? loadMore : undefined, + loadingMore, + ] as const; +}; + +const snapshotFetcher = async ( + [workspaceId, pageDocId, ts]: [ + workspaceId: string, + pageDocId: string, + ts: string, + ] // timestamp is the key to the history/snapshot +) => { + if (!ts) { + return null; + } + const res = await fetchWithTraceReport( + runtimeConfig.serverUrlPrefix + + `/api/workspaces/${workspaceId}/docs/${pageDocId}/histories/${ts}`, + { + priority: 'high', + } + ); + + if (!res.ok) { + throw new Error('Failed to fetch snapshot'); + } + + const snapshot = await res.arrayBuffer(); + if (!snapshot) { + throw new Error('Invalid snapshot'); + } + return snapshot; +}; + +// attach the Page shown in the modal to a temporary workspace +// so that we do not need to worry about providers etc +// todo: fix references to the page (the referenced page will shown as deleted) +// if we simply clone the current workspace, it maybe time consuming right? +const workspaceMap = new Map(); + +// assume the workspace is a cloud workspace since the history feature is only enabled for cloud workspace +const getOrCreateWorkspace = (workspaceId: string) => { + let workspace = workspaceMap.get(workspaceId); + if (!workspace) { + const blobEngine = createAffineCloudBlobEngine(workspaceId); + workspace = new Workspace({ + id: workspaceId, + providerCreators: [], + blobStorages: [ + () => ({ + crud: { + async get(key) { + return (await blobEngine.get(key)) ?? null; + }, + async set(key, value) { + await blobEngine.set(key, value); + return key; + }, + async delete(key) { + return blobEngine.delete(key); + }, + async list() { + return blobEngine.list(); + }, + }, + }), + ], + schema: globalBlockSuiteSchema, + }); + workspaceMap.set(workspaceId, workspace); + } + return workspace; +}; + +// workspace id + page id + timestamp -> snapshot (update binary) +export const usePageHistory = ( + workspaceId: string, + pageDocId: string, + ts?: string +) => { + // snapshot should be immutable. so we use swr immutable to disable revalidation + const { data } = useSWRImmutable( + [workspaceId, pageDocId, ts], + { + fetcher: snapshotFetcher, + suspense: false, + } + ); + return data ?? undefined; +}; + +// workspace id + page id + timestamp + snapshot -> Page (to be used for rendering in blocksuite editor) +export const useSnapshotPage = ( + workspaceId: string, + pageDocId: string, + ts?: string, + snapshot?: ArrayBuffer +) => { + const page = useMemo(() => { + if (!ts) { + return null; + } + const pageId = pageDocId + '-' + ts; + const historyShellWorkspace = getOrCreateWorkspace(workspaceId); + let page = historyShellWorkspace.getPage(pageId); + if (!page && snapshot) { + page = historyShellWorkspace.createPage({ + id: pageId, + }); + page.awarenessStore.setReadonly(page, true); + const spaceDoc = page.spaceDoc; + page + .load(() => applyUpdate(spaceDoc, new Uint8Array(snapshot))) + .catch(console.error); // must load before applyUpdate + } + return page; + }, [pageDocId, snapshot, ts, workspaceId]); + + return page; +}; + +export const historyListGroupByDay = (histories: DocHistory[]) => { + const map = new Map(); + for (const history of histories) { + const day = new Date(history.timestamp).toLocaleDateString(); + const list = map.get(day) ?? []; + list.push(history); + map.set(day, list); + } + return [...map.entries()]; +}; + +export const useRestorePage = (workspace: Workspace, pageId: string) => { + const page = useBlockSuiteWorkspacePage(workspace, pageId); + const mutateQueryResource = useMutateQueryResource(); + const { trigger: recover, isMutating } = useMutation({ + mutation: recoverDocMutation, + }); + const { getPageMeta, setPageTitle } = usePageMetaHelper(workspace); + + const onRestore = useMemo(() => { + return async (version: string, update: Uint8Array) => { + if (!page) { + return; + } + const pageDocId = page.spaceDoc.guid; + revertUpdate(page.spaceDoc, update, key => { + assertEquals(key, 'blocks'); // only expect this value is 'blocks' + return 'Map'; + }); + + // should also update the page title, since it may be changed in the history + const title = page.meta.title; + + if (getPageMeta(pageDocId)?.title !== title) { + setPageTitle(pageDocId, title); + } + + await recover({ + docId: pageDocId, + timestamp: version, + workspaceId: workspace.id, + }); + + await mutateQueryResource(listHistoryQuery, vars => { + return ( + vars.pageDocId === pageDocId && vars.workspaceId === workspace.id + ); + }); + + logger.info('Page restored', pageDocId, version); + }; + }, [ + getPageMeta, + mutateQueryResource, + page, + recover, + setPageTitle, + workspace.id, + ]); + + return { + onRestore, + isMutating, + }; +}; diff --git a/packages/frontend/core/src/components/affine/page-history-modal/empty-history-shape.tsx b/packages/frontend/core/src/components/affine/page-history-modal/empty-history-shape.tsx new file mode 100644 index 0000000000..d36d99de89 --- /dev/null +++ b/packages/frontend/core/src/components/affine/page-history-modal/empty-history-shape.tsx @@ -0,0 +1,119 @@ +export const EmptyHistoryShape = () => ( + + + + + + + + + + + + + + + + + + + + + +); diff --git a/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx b/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx new file mode 100644 index 0000000000..220fe3ef2f --- /dev/null +++ b/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx @@ -0,0 +1,446 @@ +import { Scrollable } from '@affine/component'; +import { + BlockSuiteEditor, + BlockSuiteFallback, +} from '@affine/component/block-suite-editor'; +import type { PageMode } from '@affine/core/atoms'; +import { useAFFiNEI18N } from '@affine/i18n/hooks'; +import type { Workspace } from '@blocksuite/store'; +import type { DialogContentProps } from '@radix-ui/react-dialog'; +import { Button } from '@toeverything/components/button'; +import { ConfirmModal, Modal } from '@toeverything/components/modal'; +import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; +import { useAtom, useAtomValue } from 'jotai'; +import { + type PropsWithChildren, + Suspense, + useCallback, + useLayoutEffect, + useMemo, + useState, +} from 'react'; + +import { currentModeAtom } from '../../../atoms/mode'; +import { pageHistoryModalAtom } from '../../../atoms/page-history'; +import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace'; +import { StyledEditorModeSwitch } from '../../blocksuite/block-suite-mode-switch/style'; +import { + EdgelessSwitchItem, + PageSwitchItem, +} from '../../blocksuite/block-suite-mode-switch/switch-items'; +import { AffineErrorBoundary } from '../affine-error-boundary'; +import { + historyListGroupByDay, + usePageHistory, + usePageSnapshotList, + useRestorePage, + useSnapshotPage, +} from './data'; +import { EmptyHistoryShape } from './empty-history-shape'; +import * as styles from './styles.css'; + +export interface PageHistoryModalProps { + open: boolean; + onOpenChange: (open: boolean) => void; + workspace: Workspace; + pageId: string; +} + +const contentOptions: DialogContentProps = { + ['data-testid' as string]: 'page-history-modal', + onPointerDownOutside: e => { + e.preventDefault(); + }, + style: { + padding: 0, + maxWidth: 944, + backgroundColor: 'var(--affine-background-primary-color)', + overflow: 'hidden', + }, +}; + +const ModalContainer = ({ + onOpenChange, + open, + children, +}: PropsWithChildren<{ + open: boolean; + onOpenChange: (open: boolean) => void; +}>) => { + return ( + + {children} + + ); +}; + +const localTimeFormatter = new Intl.DateTimeFormat('en', { + timeStyle: 'short', +}); + +const timestampToLocalTime = (ts: string) => { + return localTimeFormatter.format(new Date(ts)); +}; + +interface HistoryEditorPreviewProps { + workspaceId: string; + pageDocId: string; + ts?: string; + snapshot?: ArrayBuffer; + mode: PageMode; + onModeChange: (mode: PageMode) => void; + title: string; +} + +const HistoryEditorPreview = ({ + ts, + snapshot, + onModeChange, + mode, + workspaceId, + pageDocId, + title, +}: HistoryEditorPreviewProps) => { + const onSwitchToPageMode = useCallback(() => { + onModeChange('page'); + }, [onModeChange]); + const onSwitchToEdgelessMode = useCallback(() => { + onModeChange('edgeless'); + }, [onModeChange]); + const page = useSnapshotPage(workspaceId, pageDocId, ts, snapshot); + + return ( +
+
+ + + + + +
{title}
+ +
+ {ts ? timestampToLocalTime(ts) : null} +
+
+ + {page ? ( + + ) : ( + + )} +
+ ); +}; + +const PageHistoryList = ({ + pageDocId, + workspaceId, + activeVersion, + onVersionChange, +}: { + workspaceId: string; + pageDocId: string; + activeVersion?: string; + onVersionChange: (version: string) => void; +}) => { + const [historyList, loadMore, loadingMore] = usePageSnapshotList( + workspaceId, + pageDocId + ); + const historyListByDay = useMemo(() => { + return historyListGroupByDay(historyList); + }, [historyList]); + + const t = useAFFiNEI18N(); + + useLayoutEffect(() => { + if (historyList.length > 0 && !activeVersion) { + onVersionChange(historyList[0].timestamp); + } + }, [activeVersion, historyList, onVersionChange]); + + return ( +
+
+ {t['com.affine.history.version-history']()} +
+ + + {historyListByDay.map(([day, list]) => { + return ( +
+
{day}
+ {list.map(history => ( +
{ + e.stopPropagation(); + onVersionChange(history.timestamp); + }} + data-active={activeVersion === history.timestamp} + > + +
+ ))} +
+ ); + })} + {loadMore ? ( + + ) : null} +
+ +
+
+ ); +}; + +interface ConfirmRestoreModalProps { + open: boolean; + onConfirm: (res: boolean) => void; + isMutating: boolean; +} + +const ConfirmRestoreModal = ({ + isMutating, + open, + onConfirm, +}: ConfirmRestoreModalProps) => { + const t = useAFFiNEI18N(); + + const handleConfirm = useCallback(() => { + onConfirm(true); + }, [onConfirm]); + + const handleCancel = useCallback(() => { + onConfirm(false); + }, [onConfirm]); + + return ( + + ); +}; + +const EmptyHistoryPrompt = () => { + const t = useAFFiNEI18N(); + + return ( +
+ +
+ {t['com.affine.history.empty-prompt.title']()} +
+
+ {t['com.affine.history.empty-prompt.description']()} +
+
+ ); +}; + +const PageHistoryManager = ({ + workspace, + pageId, + onClose, +}: { + workspace: Workspace; + pageId: string; + onClose: () => void; +}) => { + const workspaceId = workspace.id; + const [activeVersion, setActiveVersion] = useState(); + + const pageDocId = useMemo(() => { + return workspace.getPage(pageId)?.spaceDoc.guid ?? pageId; + }, [pageId, workspace]); + + const snapshot = usePageHistory(workspaceId, pageDocId, activeVersion); + + const t = useAFFiNEI18N(); + + const { onRestore, isMutating } = useRestorePage(workspace, pageId); + + const handleRestore = useMemo( + () => async () => { + if (!activeVersion || !snapshot) { + return; + } + await onRestore(activeVersion, new Uint8Array(snapshot)); + // close the modal after restore + onClose(); + }, + [activeVersion, onClose, onRestore, snapshot] + ); + + const defaultPreviewPageMode = useAtomValue(currentModeAtom); + const [mode, setMode] = useState(defaultPreviewPageMode); + + const title = useMemo( + () => workspace.getPage(pageId)?.meta.title || t['Untitled'](), + [pageId, t, workspace] + ); + + const [showRestoreConfirmModal, setShowRestoreConfirmModal] = useState(false); + + const showRestoreConfirm = useCallback(() => { + setShowRestoreConfirmModal(true); + }, []); + + const onConfirmRestore = useAsyncCallback( + async res => { + if (res) { + await handleRestore(); + } + setShowRestoreConfirmModal(false); + }, + [handleRestore] + ); + + return ( +
+
+ + + +
+ + {!activeVersion ? ( +
+ +
+ ) : null} + +
+ +
+ +
+ + +
+ ); +}; + +export const PageHistoryModal = ({ + onOpenChange, + open, + pageId, + workspace, +}: PageHistoryModalProps) => { + const onClose = useCallback(() => { + onOpenChange(false); + }, [onOpenChange]); + + return ( + + }> + + + + ); +}; + +export const GlobalPageHistoryModal = () => { + const [{ open, pageId }, setState] = useAtom(pageHistoryModalAtom); + const [workspace] = useCurrentWorkspace(); + + const handleOpenChange = useCallback( + (open: boolean) => { + setState(prev => ({ + ...prev, + open, + })); + }, + [setState] + ); + + return ( + + ); +}; diff --git a/packages/frontend/core/src/components/affine/page-history-modal/index.tsx b/packages/frontend/core/src/components/affine/page-history-modal/index.tsx new file mode 100644 index 0000000000..3df0bbb51e --- /dev/null +++ b/packages/frontend/core/src/components/affine/page-history-modal/index.tsx @@ -0,0 +1 @@ +export * from './history-modal'; diff --git a/packages/frontend/core/src/components/affine/page-history-modal/styles.css.ts b/packages/frontend/core/src/components/affine/page-history-modal/styles.css.ts new file mode 100644 index 0000000000..8694b73337 --- /dev/null +++ b/packages/frontend/core/src/components/affine/page-history-modal/styles.css.ts @@ -0,0 +1,185 @@ +import { createVar, globalStyle, style } from '@vanilla-extract/css'; + +const headerHeight = createVar('header-height'); +const footerHeight = createVar('footer-height'); +const historyListWidth = createVar('history-list-width'); + +export const root = style({ + height: '100%', + width: '100%', + vars: { + [headerHeight]: '52px', + [footerHeight]: '68px', + [historyListWidth]: '160px', + }, +}); + +export const modalContent = style({ + display: 'flex', + height: `calc(100% - ${footerHeight})`, + width: '100%', + position: 'absolute', + selectors: { + '&[data-empty="true"]': { + opacity: 0, + pointerEvents: 'none', + }, + }, +}); + +export const previewWrapper = style({ + display: 'flex', + flexDirection: 'column', + flexGrow: 1, + height: '100%', + width: `calc(100% - ${historyListWidth})`, + backgroundColor: 'var(--affine-background-secondary-color)', +}); + +export const previewHeader = style({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + height: headerHeight, + borderBottom: '1px solid var(--affine-border-color)', + backgroundColor: 'var(--affine-background-primary-color)', + padding: '0 12px', + flexShrink: 0, + gap: 12, +}); + +export const previewHeaderTitle = style({ + fontSize: 'var(--affine-font-xs)', + fontWeight: 600, + maxWidth: 400, // better responsiveness +}); + +export const previewHeaderTimestamp = style({ + color: 'var(--affine-text-secondary-color)', + backgroundColor: 'var(--affine-background-secondary-color)', + padding: '0 10px', + borderRadius: 4, + fontSize: 'var(--affine-font-xs)', +}); + +export const editor = style({ + height: '100%', + flexGrow: 1, + overflow: 'hidden', +}); + +export const historyList = style({ + overflow: 'hidden', + height: '100%', + width: historyListWidth, + flexShrink: 0, + borderLeft: '1px solid var(--affine-border-color)', +}); + +export const historyListScrollable = style({ + height: `calc(100% - ${headerHeight})`, +}); + +export const historyListScrollableInner = style({ + display: 'flex', + gap: 16, + flexDirection: 'column', +}); + +export const historyListHeader = style({ + display: 'flex', + alignItems: 'center', + height: 52, + borderBottom: '1px solid var(--affine-border-color)', + fontWeight: 'bold', + flexShrink: 0, + padding: '0 12px', +}); + +export const historyItemGroup = style({ + display: 'flex', + flexDirection: 'column', + rowGap: 6, +}); + +export const historyItemGroupTitle = style({ + display: 'flex', + alignItems: 'center', + padding: '12px', + fontWeight: 'bold', + backgroundColor: 'var(--affine-background-primary-color)', + position: 'sticky', + top: 0, +}); + +export const historyItem = style({ + display: 'flex', + alignItems: 'center', + padding: '0 12px', + height: 32, + cursor: 'pointer', + selectors: { + '&:hover, &[data-active=true]': { + backgroundColor: 'var(--affine-hover-color)', + }, + }, +}); + +export const historyItemLoadMore = style([ + historyItem, + { + cursor: 'pointer', + color: 'var(--affine-text-secondary-color)', + flexShrink: 0, + borderRadius: 0, + selectors: { + '&:hover': { + backgroundColor: 'var(--affine-hover-color)', + }, + }, + }, +]); + +globalStyle(`${historyItem} button`, { + color: 'inherit', +}); + +export const historyFooter = style({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + height: 68, + borderTop: '1px solid var(--affine-border-color)', + padding: '0 24px', + position: 'absolute', + bottom: 0, + left: 0, + right: 0, +}); + +export const spacer = style({ + flexGrow: 1, +}); + +export const emptyHistoryPrompt = style({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + height: '100%', + width: '100%', + zIndex: 1, + gap: 20, +}); + +export const emptyHistoryPromptTitle = style({ + fontWeight: 600, + fontSize: 'var(--affine-font-h-5)', +}); + +export const emptyHistoryPromptDescription = style({ + width: 320, + textAlign: 'center', + fontSize: 'var(--affine-font-xs)', + color: 'var(--affine-text-secondary-color)', +}); diff --git a/packages/frontend/core/src/components/affine/setting-modal/account-setting/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/account-setting/index.tsx index 2c16543363..4d82b3c5d8 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/account-setting/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/account-setting/index.tsx @@ -35,6 +35,7 @@ import { openSignOutModalAtom, } from '../../../../atoms'; import { useCurrentUser } from '../../../../hooks/affine/use-current-user'; +import { useSelfHosted } from '../../../../hooks/affine/use-server-flavor'; import { useUserSubscription } from '../../../../hooks/use-subscription'; import { Upload } from '../../../pure/file-upload'; import * as style from './style.css'; @@ -167,6 +168,7 @@ export const AvatarAndName = () => { const StoragePanel = () => { const t = useAFFiNEI18N(); + const isSelfHosted = useSelfHosted(); const { data } = useQuery({ query: allBlobSizesQuery, @@ -175,6 +177,7 @@ const StoragePanel = () => { const [subscription] = useUserSubscription(); const plan = subscription?.plan ?? SubscriptionPlan.Free; + // TODO(@JimmFly): get limit from user usage query directly after #4720 is merged const maxLimit = useMemo(() => { return bytes.parse(plan === SubscriptionPlan.Free ? '10GB' : '100GB'); }, [plan]); @@ -199,6 +202,7 @@ const StoragePanel = () => { plan={plan} value={data.collectAllBlobSizes.size} onUpgrade={onUpgrade} + upgradable={!isSelfHosted} /> ); diff --git a/packages/frontend/core/src/components/affine/setting-modal/general-setting/index.tsx b/packages/frontend/core/src/components/affine/setting-modal/general-setting/index.tsx index 956d99bd5b..2f63a3b1a4 100644 --- a/packages/frontend/core/src/components/affine/setting-modal/general-setting/index.tsx +++ b/packages/frontend/core/src/components/affine/setting-modal/general-setting/index.tsx @@ -8,6 +8,7 @@ import { import type { ReactElement, SVGProps } from 'react'; import { useCurrentLoginStatus } from '../../../../hooks/affine/use-current-login-status'; +import { useSelfHosted } from '../../../../hooks/affine/use-server-flavor'; import { AboutAffine } from './about'; import { AppearanceSettings } from './appearance'; import { BillingSettings } from './billing'; @@ -36,6 +37,7 @@ export type GeneralSettingList = GeneralSettingListItem[]; export const useGeneralSettingList = (): GeneralSettingList => { const t = useAFFiNEI18N(); const status = useCurrentLoginStatus(); + const isSelfHosted = useSelfHosted(); const settings: GeneralSettingListItem[] = [ { @@ -50,13 +52,6 @@ export const useGeneralSettingList = (): GeneralSettingList => { icon: KeyboardIcon, testId: 'shortcuts-panel-trigger', }, - { - key: 'plans', - title: t['com.affine.payment.title'](), - icon: UpgradeIcon, - testId: 'plans-panel-trigger', - }, - { key: 'plugins', title: 'Plugins', @@ -71,13 +66,21 @@ export const useGeneralSettingList = (): GeneralSettingList => { }, ]; - if (status === 'authenticated') { + if (!isSelfHosted) { settings.splice(3, 0, { - key: 'billing', - title: t['com.affine.payment.billing-setting.title'](), - icon: PaymentIcon, - testId: 'billing-panel-trigger', + key: 'plans', + title: t['com.affine.payment.title'](), + icon: UpgradeIcon, + testId: 'plans-panel-trigger', }); + if (status === 'authenticated') { + settings.splice(3, 0, { + key: 'billing', + title: t['com.affine.payment.billing-setting.title'](), + icon: PaymentIcon, + testId: 'billing-panel-trigger', + }); + } } return settings; diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-header-title/operation-menu.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-header-title/operation-menu.tsx index 42c702a40c..5d19c820da 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-header-title/operation-menu.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-header-title/operation-menu.tsx @@ -1,5 +1,6 @@ import { FlexWrapper } from '@affine/component'; import { Export, MoveToTrash } from '@affine/component/page-list'; +import { WorkspaceFlavour } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { assertExists } from '@blocksuite/global/utils'; import { @@ -8,6 +9,7 @@ import { EditIcon, FavoritedIcon, FavoriteIcon, + HistoryIcon, ImportIcon, PageIcon, } from '@blocksuite/icons'; @@ -25,7 +27,7 @@ import { } from '@toeverything/hooks/use-block-suite-page-meta'; import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper'; import { useAtomValue, useSetAtom } from 'jotai'; -import { useCallback, useRef } from 'react'; +import { useCallback, useRef, useState } from 'react'; import { applyUpdate, encodeStateAsUpdate } from 'yjs'; import { setPageModeAtom } from '../../../atoms'; @@ -36,6 +38,7 @@ import { useTrashModalHelper } from '../../../hooks/affine/use-trash-modal-helpe import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace'; import { useNavigateHelper } from '../../../hooks/use-navigate-helper'; import { toast } from '../../../utils'; +import { PageHistoryModal } from '../../affine/page-history-modal/history-modal'; import { HeaderDropDownButton } from '../../pure/header-drop-down-button'; import { usePageHelper } from '../block-suite-page-list/utils'; @@ -68,6 +71,12 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => { const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace); const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace); + const [historyModalOpen, setHistoryModalOpen] = useState(false); + + const openHistoryModal = useCallback(() => { + setHistoryModalOpen(true); + }, []); + const handleOpenTrashModal = useCallback(() => { setTrashModal({ open: true, @@ -207,6 +216,23 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => { > {t['Import']()} + + {workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD && + runtimeConfig.enablePageHistory ? ( + + + + } + data-testid="editor-option-menu-history" + onSelect={openHistoryModal} + style={menuItemStyle} + > + {t['com.affine.history.view-history-version']()} + + ) : null} + { > + {workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD ? ( + + ) : null} ); }; diff --git a/packages/frontend/core/src/components/root-app-sidebar/index.tsx b/packages/frontend/core/src/components/root-app-sidebar/index.tsx index c5367a1530..3b82e54e26 100644 --- a/packages/frontend/core/src/components/root-app-sidebar/index.tsx +++ b/packages/frontend/core/src/components/root-app-sidebar/index.tsx @@ -30,8 +30,8 @@ import { useAppSettingHelper } from '../../hooks/affine/use-app-setting-helper'; import { useDeleteCollectionInfo } from '../../hooks/affine/use-delete-collection-info'; import { useGeneralShortcuts } from '../../hooks/affine/use-shortcuts'; import { useTrashModalHelper } from '../../hooks/affine/use-trash-modal-helper'; +import { useRegisterBrowserHistoryCommands } from '../../hooks/use-browser-history-commands'; import { useNavigateHelper } from '../../hooks/use-navigate-helper'; -import { useRegisterBlocksuiteEditorCommands } from '../../hooks/use-shortcut-commands'; import type { AllWorkspace } from '../../shared'; import { CollectionsList } from '../pure/workspace-slider-bar/collections'; import { AddCollectionButton } from '../pure/workspace-slider-bar/collections/add-collection-button'; @@ -169,7 +169,7 @@ export const RootAppSidebar = ({ const closeUserWorkspaceList = useCallback(() => { setOpenUserWorkspaceList(false); }, [setOpenUserWorkspaceList]); - useRegisterBlocksuiteEditorCommands(router.back, router.forward); + useRegisterBrowserHistoryCommands(router.back, router.forward); const userInfo = useDeleteCollectionInfo(); return ( { + // todo: should not mutate all graphql cache return mutate(key => { if (Array.isArray(key)) { return key[0] === 'cloud'; diff --git a/packages/frontend/core/src/hooks/affine/use-register-blocksuite-editor-commands.tsx b/packages/frontend/core/src/hooks/affine/use-register-blocksuite-editor-commands.tsx index d7f6cd5134..62ddb8b48b 100644 --- a/packages/frontend/core/src/hooks/affine/use-register-blocksuite-editor-commands.tsx +++ b/packages/frontend/core/src/hooks/affine/use-register-blocksuite-editor-commands.tsx @@ -1,25 +1,29 @@ import { toast } from '@affine/component'; +import { WorkspaceFlavour } from '@affine/env/workspace'; 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 { EdgelessIcon, HistoryIcon, PageIcon } from '@blocksuite/icons'; import { usePageMetaHelper } from '@toeverything/hooks/use-block-suite-page-meta'; import { PreconditionStrategy, registerAffineCommand, } from '@toeverything/infra/command'; +import { useSetAtom } from 'jotai'; import { useCallback, useEffect } from 'react'; +import { pageHistoryModalAtom } from '../../atoms/page-history'; +import { useCurrentWorkspace } from '../current/use-current-workspace'; import { useBlockSuiteMetaHelper } from './use-block-suite-meta-helper'; import { useExportPage } from './use-export-page'; import { useTrashModalHelper } from './use-trash-modal-helper'; export function useRegisterBlocksuiteEditorCommands( - blockSuiteWorkspace: Workspace, pageId: string, mode: 'page' | 'edgeless' ) { const t = useAFFiNEI18N(); + const [workspace] = useCurrentWorkspace(); + const blockSuiteWorkspace = workspace.blockSuiteWorkspace; const { getPageMeta } = usePageMetaHelper(blockSuiteWorkspace); const currentPage = blockSuiteWorkspace.getPage(pageId); assertExists(currentPage); @@ -28,6 +32,15 @@ export function useRegisterBlocksuiteEditorCommands( const favorite = pageMeta.favorite ?? false; const trash = pageMeta.trash ?? false; + const setPageHistoryModalState = useSetAtom(pageHistoryModalAtom); + + const openHistoryModal = useCallback(() => { + setPageHistoryModalState(() => ({ + pageId, + open: true, + })); + }, [pageId, setPageHistoryModalState]); + const { togglePageMode, toggleFavorite, restoreFromTrash } = useBlockSuiteMetaHelper(blockSuiteWorkspace); const exportHandler = useExportPage(currentPage); @@ -40,12 +53,14 @@ export function useRegisterBlocksuiteEditorCommands( }); }, [pageId, pageMeta.title, setTrashModal]); + const isCloudWorkspace = workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD; + useEffect(() => { const unsubs: Array<() => void> = []; const preconditionStrategy = () => PreconditionStrategy.InPaperOrEdgeless && !trash; - //TODO: add back when edgeless presentation is ready + // TODO: add back when edgeless presentation is ready // this is pretty hack and easy to break. need a better way to communicate with blocksuite editor // unsubs.push( @@ -189,6 +204,20 @@ export function useRegisterBlocksuiteEditorCommands( }) ); + if (runtimeConfig.enablePageHistory && isCloudWorkspace) { + unsubs.push( + registerAffineCommand({ + id: `editor:${mode}-page-history`, + category: `editor:${mode}`, + icon: , + label: t['com.affine.cmdk.affine.editor.reveal-page-history-modal'](), + run() { + openHistoryModal(); + }, + }) + ); + } + return () => { unsubs.forEach(unsub => unsub()); }; @@ -198,11 +227,12 @@ export function useRegisterBlocksuiteEditorCommands( onClickDelete, exportHandler, pageId, - pageMeta.title, restoreFromTrash, t, toggleFavorite, togglePageMode, trash, + isCloudWorkspace, + openHistoryModal, ]); } diff --git a/packages/frontend/core/src/hooks/affine/use-server-flavor.ts b/packages/frontend/core/src/hooks/affine/use-server-flavor.ts new file mode 100644 index 0000000000..b7335d2318 --- /dev/null +++ b/packages/frontend/core/src/hooks/affine/use-server-flavor.ts @@ -0,0 +1,34 @@ +import { serverConfigQuery } from '@affine/graphql'; +import { useQuery } from '@affine/workspace/affine/gql'; +import type { BareFetcher, Middleware } from 'swr'; + +const wrappedFetcher = (fetcher: BareFetcher | null, ...args: any[]) => + fetcher?.(...args).catch(() => null); + +const errorHandler: Middleware = useSWRNext => (key, fetcher, config) => { + return useSWRNext(key, wrappedFetcher.bind(null, fetcher), config); +}; + +export const useServerFlavor = () => { + const { data: config, error } = useQuery( + { query: serverConfigQuery }, + { + use: [errorHandler], + revalidateOnFocus: false, + revalidateOnMount: false, + revalidateIfStale: false, + } + ); + + if (error || !config) { + return 'local'; + } + + return config.serverConfig.flavor; +}; + +export const useSelfHosted = () => { + const serverFlavor = useServerFlavor(); + + return ['local', 'selfhosted'].includes(serverFlavor); +}; diff --git a/packages/frontend/core/src/hooks/use-shortcut-commands.ts b/packages/frontend/core/src/hooks/use-browser-history-commands.ts similarity index 95% rename from packages/frontend/core/src/hooks/use-shortcut-commands.ts rename to packages/frontend/core/src/hooks/use-browser-history-commands.ts index e8003ddac8..4c3a6e810d 100644 --- a/packages/frontend/core/src/hooks/use-shortcut-commands.ts +++ b/packages/frontend/core/src/hooks/use-browser-history-commands.ts @@ -4,7 +4,7 @@ import { } from '@toeverything/infra/command'; import { useEffect } from 'react'; -export function useRegisterBlocksuiteEditorCommands( +export function useRegisterBrowserHistoryCommands( back: () => unknown, forward: () => unknown ) { diff --git a/packages/frontend/core/src/hooks/use-subscription.ts b/packages/frontend/core/src/hooks/use-subscription.ts index bdc254c71d..9ec556cb79 100644 --- a/packages/frontend/core/src/hooks/use-subscription.ts +++ b/packages/frontend/core/src/hooks/use-subscription.ts @@ -2,6 +2,8 @@ import { type SubscriptionQuery, subscriptionQuery } from '@affine/graphql'; import { useQuery } from '@affine/workspace/affine/gql'; import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; +import { useSelfHosted } from './affine/use-server-flavor'; + export type Subscription = NonNullable< NonNullable['subscription'] >; @@ -12,6 +14,7 @@ const selector = (data: SubscriptionQuery) => data.currentUser?.subscription ?? null; export const useUserSubscription = () => { + const isSelfHosted = useSelfHosted(); const { data, mutate } = useQuery({ query: subscriptionQuery, }); @@ -36,5 +39,9 @@ export const useUserSubscription = () => { [mutate] ); + if (isSelfHosted) { + return [selector(data), () => {}] as const; + } + return [selector(data), set] as const; }; diff --git a/packages/frontend/core/src/pages/workspace/detail-page.tsx b/packages/frontend/core/src/pages/workspace/detail-page.tsx index 36003f61d8..bc8cd4996c 100644 --- a/packages/frontend/core/src/pages/workspace/detail-page.tsx +++ b/packages/frontend/core/src/pages/workspace/detail-page.tsx @@ -24,6 +24,7 @@ import { setPageModeAtom } from '../../atoms'; import { collectionsCRUDAtom } from '../../atoms/collections'; import { currentModeAtom } from '../../atoms/mode'; import { AffineErrorBoundary } from '../../components/affine/affine-error-boundary'; +import { GlobalPageHistoryModal } from '../../components/affine/page-history-modal'; import { WorkspaceHeader } from '../../components/workspace-header'; import { useRegisterBlocksuiteEditorCommands } from '../../hooks/affine/use-register-blocksuite-editor-commands'; import { useCurrentSyncEngineStatus } from '../../hooks/current/use-current-sync-engine'; @@ -41,7 +42,7 @@ const DetailPageImpl = (): ReactElement => { const { setTemporaryFilter } = useCollectionManager(collectionsCRUDAtom); const mode = useAtomValue(currentModeAtom); const setPageMode = useSetAtom(setPageModeAtom); - useRegisterBlocksuiteEditorCommands(blockSuiteWorkspace, currentPageId, mode); + useRegisterBlocksuiteEditorCommands(currentPageId, mode); const onLoad = useCallback( (page: Page, editor: EditorContainer) => { try { @@ -101,6 +102,8 @@ const DetailPageImpl = (): ReactElement => { currentPageId={currentPageId} onLoadEditor={onLoad} /> + + ); }; diff --git a/packages/frontend/electron/package.json b/packages/frontend/electron/package.json index 99e8200b26..47a3a543ff 100644 --- a/packages/frontend/electron/package.json +++ b/packages/frontend/electron/package.json @@ -32,10 +32,10 @@ "@affine/sdk": "workspace:*", "@affine/templates": "workspace:*", "@affine/vue-hello-world-plugin": "workspace:*", - "@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/editor": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/lit": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", "@electron-forge/cli": "^7.1.0", "@electron-forge/core": "^7.1.0", "@electron-forge/core-utils": "^7.1.0", diff --git a/packages/frontend/electron/scripts/dev.ts b/packages/frontend/electron/scripts/dev.ts index 03796db9e7..fa22326a49 100644 --- a/packages/frontend/electron/scripts/dev.ts +++ b/packages/frontend/electron/scripts/dev.ts @@ -1,10 +1,11 @@ import { spawn } from 'node:child_process'; +import { resolve } from 'node:path'; import type { ChildProcessWithoutNullStreams } from 'child_process'; import type { BuildContext } from 'esbuild'; import * as esbuild from 'esbuild'; -import { config, electronDir } from './common'; +import { config, electronDir, rootDir } from './common'; // this means we don't spawn electron windows, mainly for testing const watchMode = process.argv.includes('--watch'); @@ -29,7 +30,10 @@ function spawnOrReloadElectron() { spawnProcess = null; } - spawnProcess = spawn('electron', ['.'], { + const ext = process.platform === 'win32' ? '.cmd' : ''; + const exe = resolve(rootDir, 'node_modules', '.bin', `electron${ext}`); + + spawnProcess = spawn(exe, ['.'], { cwd: electronDir, env: process.env, }); diff --git a/packages/frontend/graphql/src/graphql/histories.gql b/packages/frontend/graphql/src/graphql/histories.gql new file mode 100644 index 0000000000..b6aade3e0c --- /dev/null +++ b/packages/frontend/graphql/src/graphql/histories.gql @@ -0,0 +1,13 @@ +query listHistory( + $workspaceId: String! + $pageDocId: String! + $take: Int + $before: DateTime +) { + workspace(id: $workspaceId) { + histories(guid: $pageDocId, take: $take, before: $before) { + id + timestamp + } + } +} diff --git a/packages/frontend/graphql/src/graphql/index.ts b/packages/frontend/graphql/src/graphql/index.ts index 4fd2c04148..9f543e0569 100644 --- a/packages/frontend/graphql/src/graphql/index.ts +++ b/packages/frontend/graphql/src/graphql/index.ts @@ -362,6 +362,22 @@ query getWorkspaces { }`, }; +export const listHistoryQuery = { + id: 'listHistoryQuery' as const, + operationName: 'listHistory', + definitionName: 'workspace', + containsFile: false, + query: ` +query listHistory($workspaceId: String!, $pageDocId: String!, $take: Int, $before: DateTime) { + workspace(id: $workspaceId) { + histories(guid: $pageDocId, take: $take, before: $before) { + id + timestamp + } + } +}`, +}; + export const getInvoicesCountQuery = { id: 'getInvoicesCountQuery' as const, operationName: 'getInvoicesCount', @@ -445,6 +461,17 @@ mutation publishPage($workspaceId: String!, $pageId: String!, $mode: PublicPageM }`, }; +export const recoverDocMutation = { + id: 'recoverDocMutation' as const, + operationName: 'recoverDoc', + definitionName: 'recoverDoc', + containsFile: false, + query: ` +mutation recoverDoc($workspaceId: String!, $docId: String!, $timestamp: DateTime!) { + recoverDoc(workspaceId: $workspaceId, guid: $docId, timestamp: $timestamp) +}`, +}; + export const removeAvatarMutation = { id: 'removeAvatarMutation' as const, operationName: 'removeAvatar', @@ -545,6 +572,20 @@ mutation sendVerifyChangeEmail($token: String!, $email: String!, $callbackUrl: S }`, }; +export const serverConfigQuery = { + id: 'serverConfigQuery' as const, + operationName: 'serverConfig', + definitionName: 'serverConfig', + containsFile: false, + query: ` +query serverConfig { + serverConfig { + version + flavor + } +}`, +}; + export const setWorkspacePublicByIdMutation = { id: 'setWorkspacePublicByIdMutation' as const, operationName: 'setWorkspacePublicById', diff --git a/packages/frontend/graphql/src/graphql/recover-doc.gql b/packages/frontend/graphql/src/graphql/recover-doc.gql new file mode 100644 index 0000000000..d7a1dfcada --- /dev/null +++ b/packages/frontend/graphql/src/graphql/recover-doc.gql @@ -0,0 +1,7 @@ +mutation recoverDoc( + $workspaceId: String! + $docId: String! + $timestamp: DateTime! +) { + recoverDoc(workspaceId: $workspaceId, guid: $docId, timestamp: $timestamp) +} diff --git a/packages/frontend/graphql/src/graphql/server-config.gql b/packages/frontend/graphql/src/graphql/server-config.gql new file mode 100644 index 0000000000..6f7a99a477 --- /dev/null +++ b/packages/frontend/graphql/src/graphql/server-config.gql @@ -0,0 +1,6 @@ +query serverConfig { + serverConfig { + version + flavor + } +} diff --git a/packages/frontend/graphql/src/schema.ts b/packages/frontend/graphql/src/schema.ts index 99b6282ca3..b0243ca909 100644 --- a/packages/frontend/graphql/src/schema.ts +++ b/packages/frontend/graphql/src/schema.ts @@ -62,6 +62,7 @@ export enum SubscriptionPlan { Enterprise = 'Enterprise', Free = 'Free', Pro = 'Pro', + SelfHosted = 'SelfHosted', Team = 'Team', } @@ -372,6 +373,25 @@ export type GetWorkspacesQuery = { workspaces: Array<{ __typename?: 'WorkspaceType'; id: string }>; }; +export type ListHistoryQueryVariables = Exact<{ + workspaceId: Scalars['String']['input']; + pageDocId: Scalars['String']['input']; + take: InputMaybe; + before: InputMaybe; +}>; + +export type ListHistoryQuery = { + __typename?: 'Query'; + workspace: { + __typename?: 'WorkspaceType'; + histories: Array<{ + __typename?: 'DocHistoryType'; + id: string; + timestamp: string; + }>; + }; +}; + export type GetInvoicesCountQueryVariables = Exact<{ [key: string]: never }>; export type GetInvoicesCountQuery = { @@ -444,6 +464,17 @@ export type PublishPageMutation = { }; }; +export type RecoverDocMutationVariables = Exact<{ + workspaceId: Scalars['String']['input']; + docId: Scalars['String']['input']; + timestamp: Scalars['DateTime']['input']; +}>; + +export type RecoverDocMutation = { + __typename?: 'Mutation'; + recoverDoc: string; +}; + export type RemoveAvatarMutationVariables = Exact<{ [key: string]: never }>; export type RemoveAvatarMutation = { @@ -533,6 +564,17 @@ export type SendVerifyChangeEmailMutation = { sendVerifyChangeEmail: boolean; }; +export type ServerConfigQueryVariables = Exact<{ [key: string]: never }>; + +export type ServerConfigQuery = { + __typename?: 'Query'; + serverConfig: { + __typename?: 'ServerConfigType'; + version: string; + flavor: string; + }; +}; + export type SetWorkspacePublicByIdMutationVariables = Exact<{ id: Scalars['ID']['input']; public: Scalars['Boolean']['input']; @@ -717,6 +759,11 @@ export type Queries = variables: GetWorkspacesQueryVariables; response: GetWorkspacesQuery; } + | { + name: 'listHistoryQuery'; + variables: ListHistoryQueryVariables; + response: ListHistoryQuery; + } | { name: 'getInvoicesCountQuery'; variables: GetInvoicesCountQueryVariables; @@ -732,6 +779,11 @@ export type Queries = variables: PricesQueryVariables; response: PricesQuery; } + | { + name: 'serverConfigQuery'; + variables: ServerConfigQueryVariables; + response: ServerConfigQuery; + } | { name: 'subscriptionQuery'; variables: SubscriptionQueryVariables; @@ -799,6 +851,11 @@ export type Mutations = variables: PublishPageMutationVariables; response: PublishPageMutation; } + | { + name: 'recoverDocMutation'; + variables: RecoverDocMutationVariables; + response: RecoverDocMutation; + } | { name: 'removeAvatarMutation'; variables: RemoveAvatarMutationVariables; diff --git a/packages/frontend/hooks/package.json b/packages/frontend/hooks/package.json index b4840f3c48..84efd99933 100644 --- a/packages/frontend/hooks/package.json +++ b/packages/frontend/hooks/package.json @@ -18,12 +18,12 @@ "devDependencies": { "@affine/debug": "workspace:*", "@affine/env": "workspace:*", - "@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/editor": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/lit": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", "@testing-library/react": "^14.0.0", "@types/image-blob-reduce": "^4.1.3", "@types/lodash.debounce": "^4.0.7", diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json index b7373597a6..fa30483378 100644 --- a/packages/frontend/i18n/src/resources/en.json +++ b/packages/frontend/i18n/src/resources/en.json @@ -518,6 +518,7 @@ "com.affine.cmdk.affine.switch-state.on": "ON", "com.affine.cmdk.affine.translucent-ui-on-the-sidebar.to": "Change Translucent UI On The Sidebar to", "com.affine.cmdk.affine.whats-new": "What's New", + "com.affine.cmdk.affine.editor.reveal-page-history-modal": "Reveal Page History Modal", "com.affine.cmdk.placeholder": "Type a command or search anything...", "com.affine.collection-bar.action.tooltip.delete": "Delete", "com.affine.collection-bar.action.tooltip.edit": "Edit", @@ -979,5 +980,13 @@ "system": "System", "upgradeBrowser": "Please upgrade to the latest version of Chrome for the best experience.", "will be moved to Trash": "{{title}} will be moved to Trash", - "will delete member": "will delete member" + "will delete member": "will delete member", + "com.affine.history.version-history": "Version History", + "com.affine.history.view-history-version": "View History Version", + "com.affine.history.restore-current-version": "Restore current version", + "com.affine.history.back-to-page": "Back to page", + "com.affine.history.empty-prompt.title": "Empty", + "com.affine.history.empty-prompt.description": "This document is such a spring chicken, it hasn't sprouted a single historical sprig yet!", + "com.affine.history.confirm-restore-modal.restore": "Restore", + "com.affine.history.confirm-restore-modal.hint": "You are about to restore the current version of the page to the latest version available. This action will overwrite any changes made prior to the latest version." } diff --git a/packages/frontend/native/Cargo.toml b/packages/frontend/native/Cargo.toml index 443fdd3f70..59973bf6da 100644 --- a/packages/frontend/native/Cargo.toml +++ b/packages/frontend/native/Cargo.toml @@ -25,7 +25,7 @@ rand = "0.8" serde = "1" serde_json = "1" sha3 = "0.10" -sqlx = { version = "0.7.2", default-features = false, features = [ +sqlx = { version = "0.7.3", default-features = false, features = [ "sqlite", "migrate", "runtime-tokio", @@ -44,7 +44,7 @@ uuid = { version = "1", default-features = false, features = [ affine_schema = { path = "./schema" } dotenv = "0.15" napi-build = "2" -sqlx = { version = "0.7.2", default-features = false, features = [ +sqlx = { version = "0.7.3", default-features = false, features = [ "sqlite", "runtime-tokio", "tls-rustls", diff --git a/packages/frontend/workspace/package.json b/packages/frontend/workspace/package.json index 4774b5e8c3..92db92e83f 100644 --- a/packages/frontend/workspace/package.json +++ b/packages/frontend/workspace/package.json @@ -4,6 +4,7 @@ "exports": { "./atom": "./src/atom.ts", "./manager": "./src/manager/index.ts", + "./blob": "./src/blob/index.ts", "./local/crud": "./src/local/crud.ts", "./affine/*": "./src/affine/*.ts", "./providers": "./src/providers/index.ts" diff --git a/packages/frontend/workspace/src/affine/gql.ts b/packages/frontend/workspace/src/affine/gql.ts index 102e5c7d31..b7988042a0 100644 --- a/packages/frontend/workspace/src/affine/gql.ts +++ b/packages/frontend/workspace/src/affine/gql.ts @@ -5,12 +5,15 @@ import type { QueryOptions, QueryResponse, QueryVariables, + RecursiveMaybeFields, } from '@affine/graphql'; import { gqlFetcherFactory } from '@affine/graphql'; +import { useAsyncCallback } from '@toeverything/hooks/affine-async-hooks'; import type { GraphQLError } from 'graphql'; import { useMemo } from 'react'; import type { Key, SWRConfiguration, SWRResponse } from 'swr'; -import useSWR from 'swr'; +import useSWR, { useSWRConfig } from 'swr'; +import useSWRInfinite from 'swr/infinite'; import type { SWRMutationConfiguration, SWRMutationResponse, @@ -86,6 +89,63 @@ export function useQuery( ); } +export function useQueryInfinite( + options: Omit, 'variables'> & { + getVariables: ( + pageIndex: number, + previousPageData: QueryResponse + ) => QueryOptions['variables']; + }, + config?: Omit< + SWRConfiguration< + QueryResponse, + GraphQLError | GraphQLError[], + typeof fetcher + >, + 'fetcher' + > +) { + const configWithSuspense: SWRConfiguration = useMemo( + () => ({ + suspense: true, + ...config, + }), + [config] + ); + + const { data, setSize, size, error } = useSWRInfinite< + QueryResponse, + GraphQLError | GraphQLError[] + >( + (pageIndex: number, previousPageData: QueryResponse) => [ + 'cloud', + options.query.id, + options.getVariables(pageIndex, previousPageData), + ], + async ([_, __, variables]) => { + const params = { ...options, variables } as QueryOptions; + return fetcher(params); + }, + configWithSuspense + ); + + const loadingMore = size > 0 && data && !data[size - 1]; + + // todo: find a generic way to know whether or not there are more items to load + const loadMore = useAsyncCallback(async () => { + if (loadingMore) { + return; + } + await setSize(size => size + 1); + }, [loadingMore, setSize]); + return { + data, + error, + loadingMore, + loadMore, + }; +} + /** * A useSWRMutation wrapper for sending graphql mutations * @@ -138,3 +198,32 @@ export function useMutation( } export const gql = fetcher; + +// use this to revalidate all queries that match the filter +export const useMutateQueryResource = () => { + const { mutate } = useSWRConfig(); + const revalidateResource = useMemo( + () => + ( + query: Q, + varsFilter: ( + vars: RecursiveMaybeFields> + ) => boolean = _vars => true + ) => { + return mutate(key => { + const res = + Array.isArray(key) && + key[0] === 'cloud' && + key[1] === query.id && + varsFilter(key[2]); + if (res) { + console.debug('revalidate resource', key); + } + return res; + }); + }, + [mutate] + ); + + return revalidateResource; +}; diff --git a/tests/affine-cloud/e2e/page-history.spec.ts b/tests/affine-cloud/e2e/page-history.spec.ts new file mode 100644 index 0000000000..14ed1a7734 --- /dev/null +++ b/tests/affine-cloud/e2e/page-history.spec.ts @@ -0,0 +1,146 @@ +import { test } from '@affine-test/kit/playwright'; +import { + createRandomUser, + deleteUser, + enableCloudWorkspace, + loginUser, + runPrisma, +} from '@affine-test/kit/utils/cloud'; +import { + getBlockSuiteEditorTitle, + waitForEditorLoad, +} from '@affine-test/kit/utils/page-logic'; +import { createLocalWorkspace } from '@affine-test/kit/utils/workspace'; +import { expect, type Page } from '@playwright/test'; + +let user: { + id: string; + name: string; + email: string; + password: string; +}; + +test.beforeEach(async ({ page }) => { + user = await createRandomUser(); + await loginUser(page, user.email); +}); + +test.afterEach(async () => { + // if you want to keep the user in the database for debugging, + // comment this line + await deleteUser(user.email); +}); + +test('newly created page shows empty history', async ({ page }) => { + await page.reload(); + await waitForEditorLoad(page); + await createLocalWorkspace( + { + name: 'test', + }, + page + ); + await enableCloudWorkspace(page); + + // click the history button + await page.getByTestId('header-dropDownButton').click(); + await page.getByTestId('editor-option-menu-history').click(); + + const modal = page.getByTestId('page-history-modal'); + + // expect history modal shown up + await expect(modal).toBeVisible(); + + await expect(page.getByTestId('empty-history-prompt')).toBeVisible(); +}); + +const pushCurrentPageUpdates = async (page: Page) => { + const [workspaceId, guid, updates, state] = await page.evaluate(() => { + // @ts-expect-error + const Y = window.currentWorkspace.blockSuiteWorkspace.constructor.Y; + // @ts-expect-error + const spaceDoc = window.currentEditor.page.spaceDoc; + // @ts-expect-error + const workspaceId: string = window.currentWorkspace.id; + const updates: Uint8Array = Y.encodeStateAsUpdate(spaceDoc); + const state: Uint8Array = Y.encodeStateVector(spaceDoc); + + return [workspaceId, spaceDoc.guid, [...updates], [...state]] as const; + }); + + const toBuffer = (arr: readonly number[]) => Buffer.from(arr); + + await runPrisma(async client => { + await client.snapshotHistory.create({ + data: { + workspaceId: workspaceId, + id: guid, + blob: toBuffer(updates), + state: toBuffer(state), + timestamp: new Date(Date.now() - 10000), + expiredAt: new Date(Date.now() + 100000), + }, + }); + }); +}; + +test('can restore page to a history version', async ({ page }) => { + await page.reload(); + await waitForEditorLoad(page); + await createLocalWorkspace( + { + name: 'test', + }, + page + ); + await enableCloudWorkspace(page); + + await pushCurrentPageUpdates(page); + + const title = getBlockSuiteEditorTitle(page); + await title.pressSequentially('TEST TITLE', { + delay: 50, + }); + + // write something and push to history + await pushCurrentPageUpdates(page); + + await title.fill('New Title'); + + // click the history button + await page.getByTestId('header-dropDownButton').click(); + await page.getByTestId('editor-option-menu-history').click(); + + const modal = page.getByTestId('page-history-modal'); + + // expect history modal shown up + await expect(modal).toBeVisible(); + + // expect history list to have 2 items + await expect(modal.getByTestId('version-history-item')).toHaveCount(2); + + // check the first item in the preview should have title 'TEST TITLE' + await expect(modal.locator('[data-block-is-title]')).toHaveText('TEST TITLE'); + + // click restore + await modal + .getByRole('button', { + name: 'Restore current version', + }) + .click(); + + const confirm = page.getByTestId('confirm-restore-history-modal'); + + // expect confirm dialog to show up + await expect(confirm).toBeVisible(); + + // click restore + await confirm + .getByRole('button', { + name: 'Restore', + }) + .click(); + + // title should be restored to 'TEST TITLE' + await expect(title).toContainText('TEST TITLE'); +}); diff --git a/tests/affine-legacy/0.6.1-beta.1/package.json b/tests/affine-legacy/0.6.1-beta.1/package.json index 0053193f50..e74e1b2c46 100644 --- a/tests/affine-legacy/0.6.1-beta.1/package.json +++ b/tests/affine-legacy/0.6.1-beta.1/package.json @@ -9,10 +9,10 @@ "devDependencies": { "@affine-test/fixtures": "workspace:*", "@affine-test/kit": "workspace:*", - "@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", "@playwright/test": "^1.39.0", "express": "^4.18.2", "http-proxy-middleware": "^3.0.0-beta.1", diff --git a/tests/affine-legacy/0.7.0-canary.18/package.json b/tests/affine-legacy/0.7.0-canary.18/package.json index 8a0d386ad6..181931c4ee 100644 --- a/tests/affine-legacy/0.7.0-canary.18/package.json +++ b/tests/affine-legacy/0.7.0-canary.18/package.json @@ -9,10 +9,10 @@ "devDependencies": { "@affine-test/fixtures": "workspace:*", "@affine-test/kit": "workspace:*", - "@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", "@playwright/test": "^1.39.0", "express": "^4.18.2", "http-proxy-middleware": "^3.0.0-beta.1", diff --git a/tests/affine-legacy/0.8.0-canary.7/package.json b/tests/affine-legacy/0.8.0-canary.7/package.json index fe8959bafd..8228a31148 100644 --- a/tests/affine-legacy/0.8.0-canary.7/package.json +++ b/tests/affine-legacy/0.8.0-canary.7/package.json @@ -9,10 +9,10 @@ "devDependencies": { "@affine-test/fixtures": "workspace:*", "@affine-test/kit": "workspace:*", - "@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", "@playwright/test": "^1.39.0", "express": "^4.18.2", "http-proxy-middleware": "^3.0.0-beta.1", diff --git a/tests/affine-legacy/0.8.4/package.json b/tests/affine-legacy/0.8.4/package.json index d2f5d7a24d..5603164f29 100644 --- a/tests/affine-legacy/0.8.4/package.json +++ b/tests/affine-legacy/0.8.4/package.json @@ -9,10 +9,10 @@ "devDependencies": { "@affine-test/fixtures": "workspace:*", "@affine-test/kit": "workspace:*", - "@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", "@playwright/test": "^1.39.0", "express": "^4.18.2", "http-proxy-middleware": "^3.0.0-beta.1", diff --git a/tests/affine-migration/package.json b/tests/affine-migration/package.json index 0bd9e9f69c..5964e964dc 100644 --- a/tests/affine-migration/package.json +++ b/tests/affine-migration/package.json @@ -7,10 +7,10 @@ "devDependencies": { "@affine-test/fixtures": "workspace:*", "@affine-test/kit": "workspace:*", - "@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", "@playwright/test": "^1.39.0" }, "version": "0.10.3" diff --git a/tests/storybook/package.json b/tests/storybook/package.json index f5020aa64b..f0e80c4e14 100644 --- a/tests/storybook/package.json +++ b/tests/storybook/package.json @@ -31,14 +31,14 @@ "wait-on": "^7.2.0" }, "devDependencies": { - "@blocksuite/block-std": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/blocks": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/editor": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/global": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/block-std": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/blocks": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/editor": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/global": "0.0.0-20231124123613-7c06e95d-nightly", "@blocksuite/icons": "2.1.36", - "@blocksuite/lit": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/store": "0.0.0-20231122113751-6bf81eb3-nightly", - "@blocksuite/virgo": "0.0.0-20231122113751-6bf81eb3-nightly", + "@blocksuite/lit": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/store": "0.0.0-20231124123613-7c06e95d-nightly", + "@blocksuite/virgo": "0.0.0-20231124123613-7c06e95d-nightly", "@dnd-kit/sortable": "^8.0.0", "@tomfreudenberg/next-auth-mock": "^0.5.6", "chromatic": "^9.1.0", diff --git a/yarn.lock b/yarn.lock index c6c04f82e5..95fac082af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25,10 +25,10 @@ __metadata: dependencies: "@affine-test/fixtures": "workspace:*" "@affine-test/kit": "workspace:*" - "@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" "@playwright/test": "npm:^1.39.0" express: "npm:^4.18.2" http-proxy-middleware: "npm:^3.0.0-beta.1" @@ -42,10 +42,10 @@ __metadata: dependencies: "@affine-test/fixtures": "workspace:*" "@affine-test/kit": "workspace:*" - "@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" "@playwright/test": "npm:^1.39.0" express: "npm:^4.18.2" http-proxy-middleware: "npm:^3.0.0-beta.1" @@ -59,10 +59,10 @@ __metadata: dependencies: "@affine-test/fixtures": "workspace:*" "@affine-test/kit": "workspace:*" - "@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" "@playwright/test": "npm:^1.39.0" express: "npm:^4.18.2" http-proxy-middleware: "npm:^3.0.0-beta.1" @@ -76,10 +76,10 @@ __metadata: dependencies: "@affine-test/fixtures": "workspace:*" "@affine-test/kit": "workspace:*" - "@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" "@playwright/test": "npm:^1.39.0" express: "npm:^4.18.2" http-proxy-middleware: "npm:^3.0.0-beta.1" @@ -138,10 +138,10 @@ __metadata: dependencies: "@affine-test/fixtures": "workspace:*" "@affine-test/kit": "workspace:*" - "@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" "@playwright/test": "npm:^1.39.0" languageName: unknown linkType: soft @@ -212,12 +212,12 @@ __metadata: "@affine/graphql": "workspace:*" "@affine/i18n": "workspace:*" "@affine/workspace": "workspace:*" - "@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/editor": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" "@blocksuite/icons": "npm:2.1.36" - "@blocksuite/lit": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" "@dnd-kit/core": "npm:^6.0.8" "@dnd-kit/modifiers": "npm:^6.0.1" "@dnd-kit/sortable": "npm:^8.0.0" @@ -325,14 +325,14 @@ __metadata: "@affine/templates": "workspace:*" "@affine/workspace": "workspace:*" "@aws-sdk/client-s3": "npm:3.433.0" - "@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/editor": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" "@blocksuite/icons": "npm:2.1.36" - "@blocksuite/lit": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/virgo": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/virgo": "npm:0.0.0-20231124123613-7c06e95d-nightly" "@dnd-kit/core": "npm:^6.0.8" "@dnd-kit/sortable": "npm:^8.0.0" "@emotion/cache": "npm:^11.11.0" @@ -439,10 +439,10 @@ __metadata: "@affine/sdk": "workspace:*" "@affine/templates": "workspace:*" "@affine/vue-hello-world-plugin": "workspace:*" - "@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/editor": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/lit": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" "@electron-forge/cli": "npm:^7.1.0" "@electron-forge/core": "npm:^7.1.0" "@electron-forge/core-utils": "npm:^7.1.0" @@ -490,8 +490,8 @@ __metadata: version: 0.0.0-use.local resolution: "@affine/env@workspace:packages/common/env" dependencies: - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" lit: "npm:^3.0.2" react: "npm:18.2.0" react-dom: "npm:18.2.0" @@ -687,11 +687,11 @@ __metadata: version: 0.0.0-use.local resolution: "@affine/sdk@workspace:packages/common/sdk" dependencies: - "@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/editor": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" jotai: "npm:^2.5.1" vite: "npm:^4.4.11" vite-plugin-dts: "npm:3.6.0" @@ -814,14 +814,14 @@ __metadata: dependencies: "@affine/component": "workspace:*" "@affine/i18n": "workspace:*" - "@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/editor": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" "@blocksuite/icons": "npm:2.1.36" - "@blocksuite/lit": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/virgo": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/virgo": "npm:0.0.0-20231124123613-7c06e95d-nightly" "@dnd-kit/sortable": "npm:^8.0.0" "@storybook/addon-actions": "npm:^7.5.3" "@storybook/addon-essentials": "npm:^7.5.3" @@ -4062,31 +4062,31 @@ __metadata: languageName: node linkType: hard -"@blocksuite/block-std@npm:0.0.0-20231122113751-6bf81eb3-nightly": - version: 0.0.0-20231122113751-6bf81eb3-nightly - resolution: "@blocksuite/block-std@npm:0.0.0-20231122113751-6bf81eb3-nightly" +"@blocksuite/block-std@npm:0.0.0-20231124123613-7c06e95d-nightly": + version: 0.0.0-20231124123613-7c06e95d-nightly + resolution: "@blocksuite/block-std@npm:0.0.0-20231124123613-7c06e95d-nightly" dependencies: - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" lz-string: "npm:^1.5.0" w3c-keyname: "npm:^2.2.8" zod: "npm:^3.22.4" peerDependencies: - "@blocksuite/store": 0.0.0-20231122113751-6bf81eb3-nightly - checksum: 0b75b090cd676cc8ca5972fad73d1afa75f2624e9bf1c21f2b770bf9272b592f3b90e6d6e26695d4512c0c0c38f139199420525b68258d954281c89e7785fb45 + "@blocksuite/store": 0.0.0-20231124123613-7c06e95d-nightly + checksum: c2fbfdc60c8a6e2c38b8b572522d70543ae89b66283d8e246b8058eb6c8de83ede739dcfd5df204434147eefadbf0aeabc0bd26eb064f2fb771b8bfc32fc5443 languageName: node linkType: hard -"@blocksuite/blocks@npm:0.0.0-20231122113751-6bf81eb3-nightly": - version: 0.0.0-20231122113751-6bf81eb3-nightly - resolution: "@blocksuite/blocks@npm:0.0.0-20231122113751-6bf81eb3-nightly" +"@blocksuite/blocks@npm:0.0.0-20231124123613-7c06e95d-nightly": + version: 0.0.0-20231124123613-7c06e95d-nightly + resolution: "@blocksuite/blocks@npm:0.0.0-20231124123613-7c06e95d-nightly" dependencies: - "@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/lit": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/virgo": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/virgo": "npm:0.0.0-20231124123613-7c06e95d-nightly" "@floating-ui/dom": "npm:^1.5.3" - "@toeverything/theme": "npm:^0.7.24" + "@toeverything/theme": "npm:^0.7.26" "@types/webfontloader": "npm:^1.6.36" buffer: "npm:^6.0.3" date-fns: "npm:^2.30.0" @@ -4102,30 +4102,30 @@ __metadata: sortablejs: "npm:^1.15.0" webfontloader: "npm:^1.6.28" zod: "npm:^3.22.4" - checksum: eab05e86fbe9a0e5b10d82ff6867af3a2987f56e5e5656874149d656ec3cecda0b5870ab59340e0f50c22843bc5f3fb7af3cb0399b8c65f58d76e02a1a08d2af + checksum: d08b8fae9fd7d5b64d1c0a7f2278ae71608848c9df16aa52be69818868e71ff0d1abf9bd082395e6ec3ccb97ea32e8e3f9483bfec46941105366125a4c2751f3 languageName: node linkType: hard -"@blocksuite/editor@npm:0.0.0-20231122113751-6bf81eb3-nightly": - version: 0.0.0-20231122113751-6bf81eb3-nightly - resolution: "@blocksuite/editor@npm:0.0.0-20231122113751-6bf81eb3-nightly" +"@blocksuite/editor@npm:0.0.0-20231124123613-7c06e95d-nightly": + version: 0.0.0-20231124123613-7c06e95d-nightly + resolution: "@blocksuite/editor@npm:0.0.0-20231124123613-7c06e95d-nightly" dependencies: - "@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/lit": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@toeverything/theme": "npm:^0.7.24" + "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@toeverything/theme": "npm:^0.7.26" lit: "npm:^3.0.2" - checksum: 2792e80cc253926f5399d8e737ec6553a69ce74b1a881f4f7f5a8aeeb9ef54fe676a8bb522171f064b2f0638a551e1bbfd49668c63f810b23144313171809e47 + checksum: 9da41777250311c926c65a4d8812dc9b43709b3822859617542c6ea6afc5092df1a28d37c228e16c8d0089ff2565aadc3ff234f51429f9366e93b8c21bb4aaf3 languageName: node linkType: hard -"@blocksuite/global@npm:0.0.0-20231122113751-6bf81eb3-nightly": - version: 0.0.0-20231122113751-6bf81eb3-nightly - resolution: "@blocksuite/global@npm:0.0.0-20231122113751-6bf81eb3-nightly" +"@blocksuite/global@npm:0.0.0-20231124123613-7c06e95d-nightly": + version: 0.0.0-20231124123613-7c06e95d-nightly + resolution: "@blocksuite/global@npm:0.0.0-20231124123613-7c06e95d-nightly" dependencies: zod: "npm:^3.22.4" - checksum: ef0e6649c29290e4eb510cf019e83f0dcc0febc3b1cae24c982f20889534333ef44886f53a97540eb1c364c4eb4591ddf45cca4bcf6fbbe4fe15175bcc19eba2 + checksum: 739e00a63011212a9b74c6af913b6d6d73b3cf2b75fd8abd2680635bb295515f1634ae535334f10d10a8c670e641e5098040c8b5f9d77abdc900bcc058232d03 languageName: node linkType: hard @@ -4139,26 +4139,26 @@ __metadata: languageName: node linkType: hard -"@blocksuite/lit@npm:0.0.0-20231122113751-6bf81eb3-nightly": - version: 0.0.0-20231122113751-6bf81eb3-nightly - resolution: "@blocksuite/lit@npm:0.0.0-20231122113751-6bf81eb3-nightly" +"@blocksuite/lit@npm:0.0.0-20231124123613-7c06e95d-nightly": + version: 0.0.0-20231124123613-7c06e95d-nightly + resolution: "@blocksuite/lit@npm:0.0.0-20231124123613-7c06e95d-nightly" dependencies: - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/virgo": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/virgo": "npm:0.0.0-20231124123613-7c06e95d-nightly" lit: "npm:^3.0.2" peerDependencies: - "@blocksuite/block-std": 0.0.0-20231122113751-6bf81eb3-nightly - "@blocksuite/store": 0.0.0-20231122113751-6bf81eb3-nightly - checksum: 1a3d2d27823ecde0ca7453fe0d4216fa39b8623f0306486972e618b2ed9dd22f6b53a58e699f82ccc63cc001af113969eb3fa23ce7016a79d77c55d54fa8705c + "@blocksuite/block-std": 0.0.0-20231124123613-7c06e95d-nightly + "@blocksuite/store": 0.0.0-20231124123613-7c06e95d-nightly + checksum: 8ce5fa1918cf1a39cb8922ba39a4c4248690350a258bf6fdae9f098de7ca9fd45762ee994781626bdea76a02697c2bb972044a02b33ab45f1aecec6ce94260e0 languageName: node linkType: hard -"@blocksuite/store@npm:0.0.0-20231122113751-6bf81eb3-nightly": - version: 0.0.0-20231122113751-6bf81eb3-nightly - resolution: "@blocksuite/store@npm:0.0.0-20231122113751-6bf81eb3-nightly" +"@blocksuite/store@npm:0.0.0-20231124123613-7c06e95d-nightly": + version: 0.0.0-20231124123613-7c06e95d-nightly + resolution: "@blocksuite/store@npm:0.0.0-20231124123613-7c06e95d-nightly" dependencies: - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/virgo": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/virgo": "npm:0.0.0-20231124123613-7c06e95d-nightly" "@types/flexsearch": "npm:^0.7.3" "@types/mdast": "npm:^4.0.2" flexsearch: "npm:0.7.21" @@ -4176,20 +4176,20 @@ __metadata: peerDependencies: async-call-rpc: ^6 yjs: ^13 - checksum: 97132a465bb37383b851a278826e7ffd6e6b9dfdc64d3d34887c6837b432f0ed77816701d6f97f719896b48c6f259882cda429f1c6dff9de5ba0065d4a3b468e + checksum: fb993b2db0f3720c972a341923a6215fb86d2cb48812cc8fc7aecdb04ba2b904236271d18d796e9a7a5eb1a303dc516f91bc78e76d8fa94af1cee3016fb68f73 languageName: node linkType: hard -"@blocksuite/virgo@npm:0.0.0-20231122113751-6bf81eb3-nightly": - version: 0.0.0-20231122113751-6bf81eb3-nightly - resolution: "@blocksuite/virgo@npm:0.0.0-20231122113751-6bf81eb3-nightly" +"@blocksuite/virgo@npm:0.0.0-20231124123613-7c06e95d-nightly": + version: 0.0.0-20231124123613-7c06e95d-nightly + resolution: "@blocksuite/virgo@npm:0.0.0-20231124123613-7c06e95d-nightly" dependencies: - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" zod: "npm:^3.22.4" peerDependencies: lit: ^3.0.2 yjs: ^13 - checksum: a18e632590306cf14c02e6d308b8604532dc7cb1085d53a99857076768eabc6999b677d0e1ba225051d75f7915cc5a4954d4a303fd459436a7b5a902dbd7b7f1 + checksum: 02732e5ff5c4206e23f473c467c5b786f646af6b67c44bb38f2faf70525513e9571c9b640ff8a20fba221968c0dfd5d05391761d57b297970b9d77eb3aa21b14 languageName: node linkType: hard @@ -6422,20 +6422,20 @@ __metadata: linkType: hard "@graphql-tools/load@npm:^8.0.0": - version: 8.0.0 - resolution: "@graphql-tools/load@npm:8.0.0" + version: 8.0.1 + resolution: "@graphql-tools/load@npm:8.0.1" dependencies: "@graphql-tools/schema": "npm:^10.0.0" - "@graphql-tools/utils": "npm:^10.0.0" + "@graphql-tools/utils": "npm:^10.0.11" p-limit: "npm:3.1.0" tslib: "npm:^2.4.0" peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 64bbcaae28bf895f0d1f0636325a5b567cca1524ffd02bcad58a063087e74c65b9c1a5743adc2cc18a4f3c0379f7426090f8784abcddfd60997f187e6f100eb4 + checksum: 008acefaaa067b4de26321803a9e95749ac39a009fe2614a2b39ce331efc858e0c3fe2de098b0b486fff857d061a164cbf0da479e03671fc9aaab1b882cf003e languageName: node linkType: hard -"@graphql-tools/merge@npm:9.0.0, @graphql-tools/merge@npm:^9.0.0": +"@graphql-tools/merge@npm:9.0.0": version: 9.0.0 resolution: "@graphql-tools/merge@npm:9.0.0" dependencies: @@ -6459,6 +6459,18 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/merge@npm:^9.0.0, @graphql-tools/merge@npm:^9.0.1": + version: 9.0.1 + resolution: "@graphql-tools/merge@npm:9.0.1" + dependencies: + "@graphql-tools/utils": "npm:^10.0.10" + tslib: "npm:^2.4.0" + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 9c2e57a0eee07b568c4c781303947e4c633f1dff54ba23a449965ecdf3301d97d14ba071479e0d46e3c142ee552daf834a798a1cf57a6b846f9d055c703a4e16 + languageName: node + linkType: hard + "@graphql-tools/optimize@npm:^2.0.0": version: 2.0.0 resolution: "@graphql-tools/optimize@npm:2.0.0" @@ -6526,16 +6538,16 @@ __metadata: linkType: hard "@graphql-tools/schema@npm:^10.0.0": - version: 10.0.1 - resolution: "@graphql-tools/schema@npm:10.0.1" + version: 10.0.2 + resolution: "@graphql-tools/schema@npm:10.0.2" dependencies: - "@graphql-tools/merge": "npm:^9.0.0" - "@graphql-tools/utils": "npm:^10.0.9" + "@graphql-tools/merge": "npm:^9.0.1" + "@graphql-tools/utils": "npm:^10.0.10" tslib: "npm:^2.4.0" value-or-promise: "npm:^1.0.12" peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: 314283995b9e83a398dbd8b46be00056d1737d60868b88732fa651902d914f8458901efda64a976d53ff6498e4f023dfa47ed80eb451d03e5d2dda8aea42ed8b + checksum: ce9ddf9ed446c9d421d0601b226315a10339b629f2a47ae39690ee3684ec759c4784e4d45a02c84de18a39478c217399d7852b086e5c18046b45b40ea18c4ccd languageName: node linkType: hard @@ -6590,9 +6602,9 @@ __metadata: languageName: node linkType: hard -"@graphql-tools/utils@npm:^10.0.0, @graphql-tools/utils@npm:^10.0.2, @graphql-tools/utils@npm:^10.0.5, @graphql-tools/utils@npm:^10.0.8, @graphql-tools/utils@npm:^10.0.9": - version: 10.0.9 - resolution: "@graphql-tools/utils@npm:10.0.9" +"@graphql-tools/utils@npm:^10.0.0, @graphql-tools/utils@npm:^10.0.10, @graphql-tools/utils@npm:^10.0.11, @graphql-tools/utils@npm:^10.0.2, @graphql-tools/utils@npm:^10.0.5, @graphql-tools/utils@npm:^10.0.8": + version: 10.0.11 + resolution: "@graphql-tools/utils@npm:10.0.11" dependencies: "@graphql-typed-document-node/core": "npm:^3.1.1" cross-inspect: "npm:1.0.0" @@ -6600,7 +6612,7 @@ __metadata: tslib: "npm:^2.4.0" peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: f11e4b3289bf4c48c25e2dfbb53bb59395a4599c75234e4bb9a9bbe6d28469a3ae2248bf3c9705ed354e47817cdff7abdb6e66adcabcba6cb4a58eafe96d6fc1 + checksum: 37515aa8fa6cd85a7c3ca7e174017abffb558b8110f18f261bd8c2074b1496d764ac19b3015f9030d6dbe3235e259665bc36652a4aab8064ff75d7120887c7cc languageName: node linkType: hard @@ -10729,10 +10741,10 @@ __metadata: languageName: node linkType: hard -"@remix-run/router@npm:1.12.0": - version: 1.12.0 - resolution: "@remix-run/router@npm:1.12.0" - checksum: f984e42cfe855991e1d3067f686f857614f12e8c1c45168a2d98e3fc3a427e232fd0b6cf145173b7cd132faf070702b532c34230a825d933908c54c85077fc69 +"@remix-run/router@npm:1.13.0": + version: 1.13.0 + resolution: "@remix-run/router@npm:1.13.0" + checksum: bb173a012d2036c5ee69babfe30c73975b970c2e5a0edaba138c302ae80d255e238e462e77365ab4efe819b6397e1a7f3a416d6200d17f9655f0ca1c51c4f45e languageName: node linkType: hard @@ -12352,11 +12364,11 @@ __metadata: linkType: hard "@storybook/csf@npm:^0.1.0, @storybook/csf@npm:^0.1.1": - version: 0.1.1 - resolution: "@storybook/csf@npm:0.1.1" + version: 0.1.2 + resolution: "@storybook/csf@npm:0.1.2" dependencies: type-fest: "npm:^2.19.0" - checksum: d79f9eca1184a3e3e6cf6b19b47ea2378db3fb2ec574686d311597b0de83cd8148a038311a891a348089edac2dc4dd7ceb65a87845a882546d9fc00146e3ce29 + checksum: 11168df65e7b6bd0e5d31e7e805c8ba80397fc190cb33424e043b72bbd85d8f826dba082503992d7f606b72484337ab9d091eca947550613e241fbef57780d4c languageName: node linkType: hard @@ -13099,12 +13111,12 @@ __metadata: dependencies: "@affine/debug": "workspace:*" "@affine/env": "workspace:*" - "@blocksuite/block-std": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/editor": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/lit": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/block-std": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" "@testing-library/react": "npm:^14.0.0" "@types/image-blob-reduce": "npm:^4.1.3" "@types/lodash.debounce": "npm:^4.0.7" @@ -13151,11 +13163,11 @@ __metadata: "@affine-test/fixtures": "workspace:*" "@affine/sdk": "workspace:*" "@affine/templates": "workspace:*" - "@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/editor": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/global": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/lit": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/editor": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/global": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/lit": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" "@testing-library/react": "npm:^14.0.0" async-call-rpc: "npm:^6.3.1" electron: "link:../../frontend/electron/node_modules/electron" @@ -13193,10 +13205,10 @@ __metadata: languageName: unknown linkType: soft -"@toeverything/theme@npm:^0.7.20, @toeverything/theme@npm:^0.7.24": - version: 0.7.26 - resolution: "@toeverything/theme@npm:0.7.26" - checksum: be895af99318af5ea2d624a78b4c8b7d0ce3cf00e825a593e75d2e3c3c394fd0501213f99968424021462d10d89bdb3a66a7f50e2e6a6e1c8981513b4b15ad72 +"@toeverything/theme@npm:^0.7.20, @toeverything/theme@npm:^0.7.24, @toeverything/theme@npm:^0.7.26": + version: 0.7.27 + resolution: "@toeverything/theme@npm:0.7.27" + checksum: 9349d30256d33b5528441dbca09387819225449f97549585b92b33e354d7fbb3d0e5e35001c89b8a7e3033d8f51d3aee75ef9edaaf14fb7dd717dd66bfe96009 languageName: node linkType: hard @@ -13204,8 +13216,8 @@ __metadata: version: 0.0.0-use.local resolution: "@toeverything/y-indexeddb@workspace:packages/common/y-indexeddb" dependencies: - "@blocksuite/blocks": "npm:0.0.0-20231122113751-6bf81eb3-nightly" - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/blocks": "npm:0.0.0-20231124123613-7c06e95d-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" fake-indexeddb: "npm:^5.0.0" idb: "npm:^7.1.1" nanoid: "npm:^5.0.3" @@ -14003,20 +14015,20 @@ __metadata: linkType: hard "@types/node@npm:*, @types/node@npm:>=10.0.0, @types/node@npm:>=12.12.47, @types/node@npm:>=13.7.0, @types/node@npm:>=8.1.0, @types/node@npm:^20.9.3": - version: 20.9.4 - resolution: "@types/node@npm:20.9.4" + version: 20.10.0 + resolution: "@types/node@npm:20.10.0" dependencies: undici-types: "npm:~5.26.4" - checksum: d567855b48e453b443499c17fc6c939d154732b54319a05b9b31db6e475e6458f053838635b201b1bb493d349d9b1af0aecc58b28fd6062e564e9fbf593199eb + checksum: c7d5ddbdbf3491e2363135c9611eb6bfae90eda2957279237fa232bcb29cd0df1cc3ee149d6de9915b754262a531ee2d57d33c9ecd58d763e8ad4856113822f3 languageName: node linkType: hard "@types/node@npm:^18.0.0, @types/node@npm:^18.11.18, @types/node@npm:^18.11.9": - version: 18.18.12 - resolution: "@types/node@npm:18.18.12" + version: 18.18.13 + resolution: "@types/node@npm:18.18.13" dependencies: undici-types: "npm:~5.26.4" - checksum: bd32120a52e7bdbf38dafa0f9cdb0c322115fc7fe38890b6c151507199d307652905077b878f0173c375389c87400d78f25a12fc8517b8f829547c95df37ef4e + checksum: 5dcab799e39570a858741a13373a584529d0e6b81120c8a2118e158749d9ace291748644d760af554fe73ab3cebdd91500314bf1ecd17a746fabcae06ebf9eea languageName: node linkType: hard @@ -15249,15 +15261,15 @@ __metadata: linkType: hard "@whatwg-node/node-fetch@npm:^0.5.0": - version: 0.5.0 - resolution: "@whatwg-node/node-fetch@npm:0.5.0" + version: 0.5.1 + resolution: "@whatwg-node/node-fetch@npm:0.5.1" dependencies: "@whatwg-node/events": "npm:^0.1.0" busboy: "npm:^1.6.0" fast-querystring: "npm:^1.1.1" fast-url-parser: "npm:^1.1.3" tslib: "npm:^2.3.1" - checksum: b779288b07296e0be60e90e338de46b9d0c892d2e38b99bd04d062bbd4acb429607afc92b9bfdbf10841cef6f4b531e63415197583677de69a07e7b2e39350b9 + checksum: 7e630fe252dea7fb8c844e8643510e819d7b803649b709ee9ffd663870cc6715302f7a32b934c9cc6a9568f657d322173dfa66dd28ba2bd026014bf1f8bfcd33 languageName: node linkType: hard @@ -19682,9 +19694,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.4.535": - version: 1.4.590 - resolution: "electron-to-chromium@npm:1.4.590" - checksum: 04f54be67d5465432363e6a7cf0813dbd95a23d115f55679662677508d9e068ce241b4a8e1224a53fccc4037094928d38946529c5118c1184090e2609c65cd94 + version: 1.4.593 + resolution: "electron-to-chromium@npm:1.4.593" + checksum: 836463303815c1e599183e4cd84549b76c1451602c68119bfd428f706202c7d2e799cdbf1408e2d154155f84bc85020491b3244b2f5a29614d96fa7d6967b114 languageName: node linkType: hard @@ -19734,15 +19746,15 @@ __metadata: linkType: soft "electron@npm:^27.1.0": - version: 27.1.0 - resolution: "electron@npm:27.1.0" + version: 27.1.2 + resolution: "electron@npm:27.1.2" dependencies: "@electron/get": "npm:^2.0.0" "@types/node": "npm:^18.11.18" extract-zip: "npm:^2.0.1" bin: electron: cli.js - checksum: 8eed880bbda6efd55041cc855b93f632897139557761a913dd56c68a9e24bcb1c222a0b335f70e3df97e9692997f659d62600276bbf3714b374d841fed75bab4 + checksum: c24cbdee2ea7e219d2224e3b3f9760a248723490442dcdf28a5c5419c693127bc0ab169945bd6e7ff7db84c69b3ed4db65e2691dd2f7e15b2b0eea905a1e8863 languageName: node linkType: hard @@ -26332,9 +26344,9 @@ __metadata: linkType: hard "lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0": - version: 10.0.3 - resolution: "lru-cache@npm:10.0.3" - checksum: 2fd14d8f828760eb4f1e696d20383bf52bb58a02aebfd08cb8035a66876c6fc1ec244ffdcf93d5a201fdad6d9f8c59de62c4fc5ff5604d67a4f833441f5b5945 + version: 10.1.0 + resolution: "lru-cache@npm:10.1.0" + checksum: 207278d6fa711fb1f94a0835d4d4737441d2475302482a14785b10515e4c906a57ebf9f35bf060740c9560e91c7c1ad5a04fd7ed030972a9ba18bce2a228e95b languageName: node linkType: hard @@ -31074,26 +31086,26 @@ __metadata: linkType: hard "react-router-dom@npm:^6.16.0, react-router-dom@npm:^6.19.0": - version: 6.19.0 - resolution: "react-router-dom@npm:6.19.0" + version: 6.20.0 + resolution: "react-router-dom@npm:6.20.0" dependencies: - "@remix-run/router": "npm:1.12.0" - react-router: "npm:6.19.0" + "@remix-run/router": "npm:1.13.0" + react-router: "npm:6.20.0" peerDependencies: react: ">=16.8" react-dom: ">=16.8" - checksum: 38312efc11d3ef688062301479a8257a1495a81cd8dd7039c1f81aba6774963df7d21aaee2ba1a3c152857b70f4fb9966a3ccff47aca12212e854dcd6fc4deab + checksum: 4b6741c545cedf5a5c4f996deb953679dcc985425e0664e27b97fdb9ab1387cbe1a6a12bfc7f7c38ec40b15759b4bf6396930ec26540a4a81ae16d154fd35049 languageName: node linkType: hard -"react-router@npm:6.19.0": - version: 6.19.0 - resolution: "react-router@npm:6.19.0" +"react-router@npm:6.20.0": + version: 6.20.0 + resolution: "react-router@npm:6.20.0" dependencies: - "@remix-run/router": "npm:1.12.0" + "@remix-run/router": "npm:1.13.0" peerDependencies: react: ">=16.8" - checksum: 5454f4a4d65401430ded8f1033cebe4ccca771c3c827e8329c77dcfd73618ca9a32488fb58722bf6a07afef7d8e7ef22a710aae0f3337e5c20962bf6473d81a3 + checksum: 2cdac5ad8b7a7bc230173b26768bcf3f6a9abc0a19983fa7b76b9ffdbeb44bfbd88fcc2033e9062defafef144db207859eb3162a9c9742d70cfce4e7166ff1e5 languageName: node linkType: hard @@ -34010,9 +34022,9 @@ __metadata: linkType: hard "tocbot@npm:^4.20.1": - version: 4.22.0 - resolution: "tocbot@npm:4.22.0" - checksum: 41ff72948464c3d1ce6f63845c9ba1ddad11f94f25f3c22cfbbac6339ab912efc874144046fd85501c1f2e7194da3750151d7846e07b68e7663fbaed703a2163 + version: 4.23.0 + resolution: "tocbot@npm:4.23.0" + checksum: 883c7ef6baa9cd04ebb0b3ce9109f952212d695eb2ed5a44a1013a62922325f7a9b8ca8a91ad744cd717cebcb75fd94c7969d841707ea123caba4447497557f2 languageName: node linkType: hard @@ -34509,11 +34521,11 @@ __metadata: linkType: hard "undici@npm:^5.22.1, undici@npm:^5.27.2": - version: 5.27.2 - resolution: "undici@npm:5.27.2" + version: 5.28.0 + resolution: "undici@npm:5.28.0" dependencies: "@fastify/busboy": "npm:^2.0.0" - checksum: 2bf96b102fb84568fb235bdf6e1e352e5d2bf99566b243cd1b13b41578bf9dd5c7c3d3d82192b20a3fec61fe7a528f9d80cd5b4555ce65405c06c69b023013de + checksum: 70afe17996fbb84a4febbda78b30ce627fc6db585afe0c5191426d277ff4162f62ae36ddbc252b8aaba5cc1fd4e09bc6db8a919ff59adb10008fb3882492a171 languageName: node linkType: hard @@ -35920,8 +35932,8 @@ __metadata: linkType: hard "wrangler@npm:^3.15.0": - version: 3.17.0 - resolution: "wrangler@npm:3.17.0" + version: 3.17.1 + resolution: "wrangler@npm:3.17.1" dependencies: "@cloudflare/kv-asset-handler": "npm:^0.2.0" "@esbuild-plugins/node-globals-polyfill": "npm:^0.2.3" @@ -35944,7 +35956,7 @@ __metadata: bin: wrangler: bin/wrangler.js wrangler2: bin/wrangler.js - checksum: 62139687f1b7887158336a6c3caa37aac05305665252c5dd87e01fda7cfac92f8530986f0859dc4215411827f1961d8673d15bc6d6d182aa4258fdd070fbc0eb + checksum: 44ccfbaabe18e882d692791587966b372a0d38b7742c93a7d99242cd25287a675f452c8a3a21bbb6bea8294d9eaf13e9991a7cc2533608bf95dca744cacfa156 languageName: node linkType: hard @@ -36174,7 +36186,7 @@ __metadata: version: 0.0.0-use.local resolution: "y-provider@workspace:packages/common/y-provider" dependencies: - "@blocksuite/store": "npm:0.0.0-20231122113751-6bf81eb3-nightly" + "@blocksuite/store": "npm:0.0.0-20231124123613-7c06e95d-nightly" vite: "npm:^4.4.11" vite-plugin-dts: "npm:3.6.0" vitest: "npm:0.34.6"