Compare commits

...

518 Commits

Author SHA1 Message Date
Guido D'Orsi
0088aa8b25 Merge pull request #2292 from garden-co/changeset-release/main
Version Packages
2025-05-20 15:48:40 +02:00
github-actions[bot]
35a66df1e4 Version Packages 2025-05-20 12:52:45 +00:00
Guido D'Orsi
3b2fa64a82 Merge pull request #2291 from garden-co/fix/test
fix: restore custom AccountSchema support in testing utils
2025-05-20 14:46:22 +02:00
Guido D'Orsi
3d1027f278 chore: changeset 2025-05-20 14:46:04 +02:00
Guido D'Orsi
cc78386163 Merge pull request #2290 from garden-co/feat/extand-parent
feat: make possible to extend a group without having access to it
2025-05-20 14:17:56 +02:00
Guido D'Orsi
c240eed6a4 fix: Fix custom AccountSchema support in testing utils 2025-05-20 13:19:59 +02:00
Guido D'Orsi
2a9b7f5d52 test: more tests on group extend 2025-05-20 12:44:31 +02:00
Guido D'Orsi
f2fbd29de5 feat: make possible to extend a group without having access to it 2025-05-20 11:54:11 +02:00
Benjamin S. Leveritt
c960176a2a Merge pull request #2285 from garden-co/update-subscriptions-and-loading-for-zod
Update subscriptions and loading docs for 0.14
2025-05-20 06:43:46 +01:00
Benjamin S. Leveritt
fd4bae4cc1 Replace Resolved with Loaded 2025-05-19 22:16:41 +01:00
Benjamin S. Leveritt
ae32b7c19b Updates code expamples 2025-05-19 22:07:55 +01:00
Guido D'Orsi
83986c6699 Merge pull request #2270 from garden-co/changeset-release/main
Version Packages
2025-05-19 21:39:10 +02:00
github-actions[bot]
59a4e2cee3 Version Packages 2025-05-19 19:32:13 +00:00
Trisha Lim
d2a971c86c fix: update plaintext init to zod (#2283) 2025-05-19 20:29:15 +01:00
Trisha Lim
aae9ef49da improvement: org design pattern docs (#2253) 2025-05-19 19:38:05 +01:00
Trisha Lim
58c5ee5c73 form design pattern docs: emphasize not having a save button (#2241)
* form design pattern docs: emphasize not having a save button

* rewording to follow diataxis

* fix: instructions field shows undefined
2025-05-19 19:37:40 +01:00
Trisha Lim
f1d6097ee6 fix: twoslash breaks page scroll (#2267) 2025-05-19 17:37:35 +01:00
Anselm
3172a61543 Explain Loaded 2025-05-19 16:47:40 +01:00
Anselm
25324c28d9 Mention RN issue 2025-05-19 16:26:28 +01:00
Anselm
f74cb4885a Upgrade guide progress 2025-05-19 15:59:53 +01:00
Guido D'Orsi
4130213d82 Merge pull request #2271 from garden-co/fix/account-profile
fix: force sync of the group after acceptInvite
2025-05-19 15:35:04 +02:00
Guido D'Orsi
c36a26e669 test: update sync snapshots 2025-05-19 15:22:45 +02:00
Guido D'Orsi
c8b33ad7f1 fix: force sync of the group after acceptInvite 2025-05-19 15:14:41 +02:00
Benjamin S. Leveritt
a79489683e Merge pull request #2246 from joeinnes/2245-svelte-provider
add Svelte provider documentation and metadata
2025-05-19 14:09:08 +01:00
Guido D'Orsi
1251fb89b1 Merge pull request #2269 from garden-co/fix/account-profile
fix: make the profile access on Group members trigger updates correctly
2025-05-19 15:07:13 +02:00
Guido D'Orsi
cdfc10557a fix: make the profile access on Group members trigger updates correctly 2025-05-19 15:01:30 +02:00
Anselm
6e0481114c Fix homepage type errors 2025-05-19 13:50:40 +01:00
Anselm
f7e157d2f5 Make docNavigationItems js again 2025-05-19 13:35:10 +01:00
Anselm
ce83f101c7 More skeleton for the upgrade guide 2025-05-19 13:01:24 +01:00
Anselm Eickhoff
c3c723776e Merge pull request #2268 from garden-co/changeset-release/main
Version Packages
2025-05-19 12:57:06 +01:00
github-actions[bot]
33d7789530 Version Packages 2025-05-19 11:53:38 +00:00
Anselm Eickhoff
dcd7398b06 Merge pull request #2150 from garden-co/feature/zod-horribly
Zod support (using a schema translation layer)
2025-05-19 12:51:00 +01:00
Anselm
c3dd8d0271 Upgrade guide skeleton 2025-05-19 12:50:15 +01:00
Anselm
afe0accad5 Merge branch 'main' into feature/zod-horribly 2025-05-19 12:37:47 +01:00
Anselm
5835ed1274 Add changeset 2025-05-19 12:37:15 +01:00
Anselm
c5a6b87885 Fix betterauth example 2025-05-19 12:35:11 +01:00
Trisha Lim
294678e18e update thumbnail and desc for hend app (#2259) 2025-05-19 12:34:46 +01:00
Trisha Lim
9bb90489f8 feat(docs): link to Previous and Next pages (#2258) 2025-05-19 12:34:34 +01:00
Anselm
67efefde3f Merge branch 'main' into feature/zod-horribly 2025-05-19 12:32:37 +01:00
Anselm
5b28545714 More docs and allow broken homepage samples 2025-05-19 12:26:57 +01:00
Guido D'Orsi
b4352e38aa test: add await on group.removeMember 2025-05-19 13:16:21 +02:00
Guido D'Orsi
8015b8099b test: disable RetryUnavailable flaky test 2025-05-19 13:12:55 +02:00
Anselm
ee451ebd67 Port FileStreams docs (some schema methods missing) 2025-05-19 12:02:00 +01:00
Anselm
9dcd70c896 Port CoFeed docs 2025-05-19 11:56:07 +01:00
Anselm
bcacd04773 Post CoList docs 2025-05-19 11:53:32 +01:00
Anselm
65f20184e5 Port CoMap docs 2025-05-19 11:50:51 +01:00
Anselm
0c1817e5a4 Port accounts-and-migrations 2025-05-19 11:38:23 +01:00
Guido D'Orsi
addd31d43d test: disable RetryUnavailable flaky test 2025-05-19 12:25:54 +02:00
Giordano Ricci
53a02511e1 Merge pull request #2254 from garden-co/gio/betterauth-shadcn
Rework betterauth example
2025-05-19 11:24:18 +01:00
Anselm
c66aa631b3 Remove references to guide for now 2025-05-19 11:14:03 +01:00
Anselm
49a84bd8c5 Make .by and .members props again 2025-05-19 10:56:39 +01:00
Giordano Ricci
218ec7eb8d Merge branch 'main' into gio/betterauth-shadcn 2025-05-19 10:53:33 +01:00
Giordano Ricci
54f313e41e chore: readme & .env 2025-05-19 09:46:03 +01:00
Anselm
f14d971a75 Fix useAcceptInvite in jazz-react-native-core 2025-05-19 09:15:31 +01:00
Anselm
5290452e52 Fix CoList schema bug 2025-05-19 08:48:47 +01:00
Anselm
f49c24807a Upgrade to latest zod 2025-05-18 20:50:06 +01:00
Anselm Eickhoff
938f6767e4 add zod sponsor message 2025-05-18 20:30:23 +01:00
Anselm
b053369589 Make primitive objects & arrays actually work 2025-05-18 20:11:32 +01:00
Anselm
42c7f48c52 Fix CoFeed Proxy stuff and typecheck org example 2025-05-18 18:07:02 +01:00
Anselm
cd3c2cbe81 Move CoFeed entries into perAccount subobject 2025-05-18 17:39:21 +01:00
Anselm
e55bcafedb Make remaining tests pass 2025-05-18 17:03:41 +01:00
Anselm
dd3f7b5685 Make remaining examples build 2025-05-18 16:57:42 +01:00
Anselm
bde9d7637d Fix more examples 2025-05-18 16:36:49 +01:00
Anselm
b3f1bba17f Port all example apps except richtext and vue 2025-05-18 16:22:36 +01:00
Anselm
eb33fe6cf0 Handle non-collaborative objects & arrays 2025-05-18 15:25:04 +01:00
Anselm
54cf1b5906 Fix file name typo 2025-05-18 15:15:10 +01:00
Anselm
c7f48a0750 Split zod schema stuff into files 2025-05-18 15:13:01 +01:00
Anselm
f0419c5cde Port a bunch more apps, fix more stuff 2025-05-18 14:19:00 +01:00
Anselm
500ceb593c Add FileStreamSchema.createFromBlob, start working through examples 2025-05-18 11:09:04 +01:00
Anselm
51251fd87c Fix react package types 2025-05-18 10:28:26 +01:00
Anselm
ce27b1d6f9 Properly implement co.profile and inbox (all jazz-tools tests pass) 2025-05-18 10:25:53 +01:00
Anselm
dc46e36281 Merge branch 'emil/comap-doc-typecheck' into feature/zod-horribly 2025-05-17 22:58:10 +01:00
Anselm
b2bef6ffc2 Update intro & schema docs 2025-05-17 22:54:23 +01:00
Anselm
2766009ce6 Get rid of registered accounts 2025-05-17 22:04:22 +01:00
Anselm
8f88b212c1 Attempt to make form example work 2025-05-17 21:25:20 +01:00
Anselm
32e0133468 Extra changes after merge 2025-05-17 19:32:24 +01:00
Anselm
bf88604943 Merge branch 'main' into feature/zod-horribly 2025-05-17 19:32:11 +01:00
Anselm
9b66fde52d Add co.richText() 2025-05-17 17:43:00 +01:00
Anselm
1caa2761a4 Port a bunch of higher level packages 2025-05-17 17:28:47 +01:00
Anselm
4d54f1ec50 Port jazz-react-core 2025-05-17 16:28:25 +01:00
Anselm
135ca17438 Fix overly strict type assertions 2025-05-17 16:21:19 +01:00
Anselm
a5f9223215 Port requestToJoin (types only) 2025-05-17 16:12:06 +01:00
Anselm
b14a045cdb Port inbox tests (only types) 2025-05-17 15:44:52 +01:00
Anselm
0dfbda0487 Mostly make deep loading work 2025-05-17 15:26:20 +01:00
Anselm
19cb9c3ad3 port ContextManager test 2025-05-17 14:38:54 +01:00
Anselm
646c12f093 Make arbitrarily nested schema unions work 2025-05-17 14:22:57 +01:00
Anselm
fbbaad3e80 Port testing test 2025-05-17 11:59:50 +01:00
Anselm
425aa476c8 Port subscribe tests 2025-05-17 11:54:24 +01:00
Anselm
6922431197 Prefer accepting/returning undefined for optional refs in CoLists 2025-05-17 11:43:54 +01:00
Guido D'Orsi
31614c0a4f Merge pull request #2263 from garden-co/changeset-release/main
Version Packages
2025-05-17 08:58:51 +02:00
Emil Sayahi
07222cd6b0 fix: remove extra line 2025-05-16 15:58:18 -04:00
Emil Sayahi
4a88732f89 feat(docs): typechecking for CoMap docs 2025-05-16 15:57:55 -04:00
github-actions[bot]
57b69eb8da Version Packages 2025-05-16 19:49:36 +00:00
Guido D'Orsi
066676c243 Merge pull request #2264 from garden-co/feat/storage-streaming
feat(storage): implement content streaming
2025-05-16 21:46:46 +02:00
Giordano Ricci
2aac54b91d remove callback param from sso button 2025-05-16 20:29:55 +01:00
Giordano Ricci
4e4b53c420 remove leftover routes handlers 2025-05-16 20:24:33 +01:00
Guido D'Orsi
e141024656 feat(storage): implement content streaming 2025-05-16 20:36:09 +02:00
Guido D'Orsi
2c48ae0434 Merge pull request #2262 from garden-co/feat/storage-streaming
feat(storage): implement chunking for large content files
2025-05-16 19:46:36 +02:00
Guido D'Orsi
2bf974390d feat(storage): implement chunking for large content files 2025-05-16 18:41:14 +02:00
Anselm
a5116b9d63 Most CoList tests passing 2025-05-16 17:24:36 +01:00
Anselm
91ffdcc82f Fix _refs types 2025-05-16 17:19:15 +01:00
Anselm
810a7e6db2 Add getMe() 2025-05-16 16:58:23 +01:00
Joe Innes
e123715819 Fix missing $ in template literal (#2261)
Svelte interpolates curly braces in normal strings, but not template literals.
2025-05-16 16:47:11 +01:00
Anselm
1b1c0a98af Add co.account().createAs 2025-05-16 16:42:53 +01:00
Anselm
9fdf5b4831 Make first custom account test pass 2025-05-16 16:40:02 +01:00
Trisha Lim
0d087f3d4c docs: all roles can remove themselves from a Group (#2255) 2025-05-16 13:44:27 +01:00
Giordano Ricci
e2c222c8a6 password recovery flow 2025-05-16 12:08:12 +01:00
Guido D'Orsi
e410823087 Merge pull request #2251 from garden-co/changeset-release/main
Version Packages
2025-05-16 12:26:58 +02:00
Giordano Ricci
5262580222 cleanup layout 2025-05-16 11:26:25 +01:00
github-actions[bot]
dcc11e5b60 Version Packages 2025-05-16 10:22:41 +00:00
Guido D'Orsi
1b05fe5b55 Merge pull request #2252 from garden-co/fix/remove-members-no-parent-access
fix(group): removing members when the admin doesn't have access to the parent group read keys
2025-05-16 12:18:14 +02:00
Guido D'Orsi
de8f896bf9 Merge pull request #2247 from garden-co/feat/skipInvalid
feat: add $onError to catch errors on resolve
2025-05-16 12:17:10 +02:00
Guido D'Orsi
d63716a827 fix(group): removing members when the admin doesn't have access to the parent group read keys 2025-05-16 12:11:03 +02:00
Guido D'Orsi
a1e7fce3b9 Merge pull request #2250 from garden-co/fix/invites-upgrades
fix(invite): restore role upgrades an inviting revoked members
2025-05-16 12:02:50 +02:00
Guido D'Orsi
d5edad7ba5 fix(invite): restore role upgrades an inviting revoked members 2025-05-16 12:02:32 +02:00
Guido D'Orsi
559a4a223b test: cover more logic for $onError 2025-05-16 11:18:27 +02:00
Guido D'Orsi
16d5553ccd docs: replace expect in the example with comments 2025-05-16 09:52:01 +02:00
Guido D'Orsi
ef76c586cc docs: improve the Requesting Invites section 2025-05-15 23:25:54 +02:00
Guido D'Orsi
8ea4b9761c docs: improve the Requesting Invites section 2025-05-15 23:24:38 +02:00
Guido D'Orsi
a81f281079 feat: move to 2025-05-15 23:11:18 +02:00
Guido D'Orsi
3ecb602459 fix: remove Jazz paper scissors from the examples list because the worker isn't deployed 2025-05-15 23:09:16 +02:00
Meg Culotta
ea69ea1f67 Merge pull request #2004 from garden-co/feat/1936-add-request-invites-to-docs
Add request invites to docs
2025-05-15 15:25:28 -05:00
Margaret Culotta
e430bd061e remove unnecessary break 2025-05-15 15:18:35 -05:00
Guido D'Orsi
a957485172 chore: docs improvements and more tests 2025-05-15 19:52:00 +02:00
Margaret Culotta
9060975dfb Merge main and handle conflicts 2025-05-15 12:41:32 -05:00
Margaret Culotta
753ec83fdd remove unnecessary comments, clean up 2025-05-15 12:34:05 -05:00
Margaret Culotta
44a9785e93 clean up, clarify and simplfy examples 2025-05-15 12:28:55 -05:00
Trisha Lim
d9b390e538 remove support for demos in examples page (#2230) 2025-05-15 17:57:12 +01:00
Guido D'Orsi
b194f7831b fix: fix return type of $skipInvalid when used on nested lists 2025-05-15 18:17:53 +02:00
Giordano Ricci
9892dd708f improvements, restore SSO button 2025-05-15 17:07:14 +01:00
Guido D'Orsi
b60be9405d docs: add $skipInvalid usage 2025-05-15 17:48:32 +02:00
Guido D'Orsi
84588e0798 feat(organization): add remove member flow and add tests 2025-05-15 17:11:56 +02:00
Guido D'Orsi
e5b170f25e feat: add $skipInvalid resolve keyword 2025-05-15 17:11:09 +02:00
Joe Innes
6efdfef386 Merge remote-tracking branch 'upstream/main' into 2245-svelte-provider 2025-05-15 17:03:55 +02:00
Joe Innes
6528d350ec add Svelte provider documentation and metadata 2025-05-15 17:02:40 +02:00
Giordano Ricci
b6244582c7 wrap up 2025-05-15 15:34:19 +01:00
Guido D'Orsi
cfbf3aa51e Merge pull request #2243 from garden-co/changeset-release/main
Version Packages
2025-05-15 14:38:46 +02:00
github-actions[bot]
b1e00d2b18 Version Packages 2025-05-15 12:35:33 +00:00
Guido D'Orsi
ec330eaa14 chore: include betterauth packages in the linked changeset packages 2025-05-15 14:32:37 +02:00
Guido D'Orsi
07dd2c5e69 fix: restore the longer timeout for the storage loading 2025-05-15 14:26:18 +02:00
Giordano Ricci
37ab25ff62 wip: replace home components 2025-05-15 11:39:53 +01:00
Giordano Ricci
4750e23800 wip: login & signup forms 2025-05-15 11:18:00 +01:00
Trisha Lim
862386c19b add og:description to docs pages (#2153) 2025-05-15 09:29:55 +01:00
Benjamin S. Leveritt
30df1098e1 Merge pull request #2238 from garden-co/update-flake-lock
Updates nix flake lock
2025-05-15 09:12:46 +01:00
Benjamin S. Leveritt
dad4394eed Updates nix flake lock 2025-05-15 09:12:19 +01:00
Benjamin S. Leveritt
9b70336a38 Merge pull request #2187 from garden-co/2186-split-graphemes-with-unicode-segmenter
2186-split-graphemes-with-unicode-segmenter
2025-05-15 09:06:53 +01:00
Guido D'Orsi
75328ac49d Merge pull request #2227 from sectore/fix/pnpm-flake
fix(flake): stick with `pnpm@9.x`
2025-05-15 10:04:03 +02:00
Guido D'Orsi
0acdcf5f5f Merge pull request #2235 from garden-co/changeset-release/main
Version Packages
2025-05-15 09:39:49 +02:00
github-actions[bot]
dafcce5ecd Version Packages 2025-05-15 07:35:31 +00:00
Guido D'Orsi
c93536f795 Merge pull request #2198 from garden-co/fix/storage-sync-manager
fix(storage): create specialized sync/async storage managers
2025-05-15 09:33:06 +02:00
Guido D'Orsi
7bad34f2f5 chore: disable flaky test 2025-05-15 09:32:34 +02:00
Guido D'Orsi
91e0c09f33 feat(sqlite): smarter work scheduling 2025-05-15 09:24:17 +02:00
Guido D'Orsi
e2d6ba3922 fix(storage): create specialized sync/async storage managers 2025-05-15 09:21:50 +02:00
Guido D'Orsi
ba460c7b70 Merge pull request #2233 from garden-co/feat/smart-retry
feat(load): increase retry delay and immediately resolve on available
2025-05-15 09:20:25 +02:00
Guido D'Orsi
fc9f7c9199 Merge pull request #2232 from garden-co/fix/downgrade-writeonly
fix(group): correctly rotate the readKey when downgrating a member to writeOnly
2025-05-15 09:20:02 +02:00
Guido D'Orsi
2fcea1e91b Merge pull request #2201 from garden-co/fix/inspector-colist-items
fix(inspector): colist shows too many items after navigating from a longer colist
2025-05-14 18:55:30 +02:00
Emil Sayahi
191ae380c8 feat: jazz cloud auth & better auth integration (#1580)
* feat(auth): cloud authentication client

* new client function

* key splitting

TODO:
- POST signed key shard to keyserver
- GET key shard from keyserver & reassemble (sending JWT and signer ID)

* key shard merging

* start cloudauth example

TODO: CloudAuth with React Native

* separate packages

* fix sign-up

Bug: sqlite3 can't store updated user details (`secretSeed` is a byte array)

* fix: login

* trying to fix signup then login

* fix: XOR was skipping some bytes!!

* fix: yay! logins work for real this time

* feat: sealer secret sharding

TODO:
- Tests
- Investigate login proceeding despite Better Auth failing to authenticate (eg, with incorrect password)

* feat: provide `CryptoProvider`

* feat: specify `baseUrl` and `keyserver`

also, fixed logging in with `CloudAuthBasicUI` still happening when the user authenticates with Better Auth successfully, logs out in Jazz, then tries to log back in to the Better Auth server with incorrect credentials.
note: with this change, if the key shards are lost in the keyserver (eg, it restarts), then you cannot log back into the Better Auth account.

* feat: e2e test

* Update pnpm-lock.yaml

* fix imports

* disable cloudauth tests for now

* feat(auth): cloud authentication client

* new client function

* key splitting

TODO:
- POST signed key shard to keyserver
- GET key shard from keyserver & reassemble (sending JWT and signer ID)

* key shard merging

* start cloudauth example

TODO: CloudAuth with React Native

* separate packages

* fix sign-up

Bug: sqlite3 can't store updated user details (`secretSeed` is a byte array)

* fix: login

* trying to fix signup then login

* fix: XOR was skipping some bytes!!

* fix: yay! logins work for real this time

* feat: sealer secret sharding

TODO:
- Tests
- Investigate login proceeding despite Better Auth failing to authenticate (eg, with incorrect password)

* feat: provide `CryptoProvider`

* feat: specify `baseUrl` and `keyserver`

also, fixed logging in with `CloudAuthBasicUI` still happening when the user authenticates with Better Auth successfully, logs out in Jazz, then tries to log back in to the Better Auth server with incorrect credentials.
note: with this change, if the key shards are lost in the keyserver (eg, it restarts), then you cannot log back into the Better Auth account.

* feat: e2e test

* Update pnpm-lock.yaml

* fix imports

* disable cloudauth tests for now

* feat: clientside of new cloudauth spec

todo: run test server, test example app locally

* fix: tests

* feat(cloudauth): more developed example

* fix(cloudauth): use new `resolve` API

* fix(deps): remove unused `better-auth`

* docs(auth): cloud/better auth

* feat: additional plugins

* docs(auth): expo better auth

* feat: better auth options

* fix: docs

* rename packages

* merge

TODO: update docs

* docs: auth docs

* feat(examples): improve betterauth example

TODO:
- Self-hosted Better Auth

* feat(examples): self-hosted betterauth

* refactor

* feat(betterauth): api refactor

* fix(docs): build error

* feat(examples): email otp

* feat(examples): account deletion, verification, linking

* feat(tests): enable `betterauth` tests

* feat: begin Better Auth components

* fix: provider button text

* fix: tailwind class detection

* feat: see description

- feat: `providers` parameter for sign-in, sign-up, and settings components
- feat: `AccountProviders` component
- fix: auth state not updated on Next.js redirects
- feat: pass Better Auth client options to provider, not client object
  - feat: improved auth client type-checking in jazz packages
- docs: show usage of auth provider component

* fix: memory management

* fix: `lucide-react` import errors

* Update pnpm-lock.yaml

* Update `pnpm-lock.yaml`

* design system is driving me crazy

* Update `homepage/pnpm-lock.yaml`

`git clean -dfX; rm ./pnpm-lock.yaml; rm ./homepage/pnpm-lock.yaml; pnpm install; cd homepage && pnpm install; cd ../; pnpm install; pnpm format-and-lint:fix; pnpm build`

* fix: just move the design system package

* please just work

* widen react range

* fix: re-rendering too often

* Update `pnpm-lock.yaml`

* todo: replace alert, heading, input

* replace alert, heading, input

* Update `pnpm-lock.yaml`

* fix: minimise number of auth fetches

* Update pnpm-lock.yaml

* fix(deps): missing doc dep

* misc: optional `options` param

* feat(docs): hide documentation page

* feat(ci): add tests

* fix: `useSession` effect

* feat: remove key rotation

* fix: minimal number of fetches

* Delete JazzBetterAuth.test.ts.disabled

* feat: upgrade @noble deps

---------

Co-authored-by: Guido D'Orsi <gu.dorsi@gmail.com>
Co-authored-by: Guido D'Orsi <guido@garden.co>
2025-05-14 12:53:57 -04:00
Margaret Culotta
63d0b0673e run linter 2025-05-14 11:49:21 -05:00
Margaret Culotta
2b456b5e07 add approval code snippet 2025-05-14 11:45:25 -05:00
Margaret Culotta
e20809d314 fix errors in code snippets 2025-05-14 11:12:26 -05:00
Guido D'Orsi
eef1a5d994 feat(load): increase retry delay and immediately resolve on available 2025-05-14 17:38:21 +02:00
Guido D'Orsi
daee7b900d fix(group): correctly rotate the readKey when downgrading a member to writeOnly 2025-05-14 17:24:02 +02:00
Trisha Lim
9afcd38e3b link to discord and github from every docs page (#2224)
* link to discord and github from every docs page

* change to hr

* fix: incorrect url

* lint fix

* move links to right nav

* mobile

* use issue template to set labels and assignee

* update github link text

* increase focus area of links

* remove ComingSoon component
2025-05-14 15:50:03 +01:00
Trisha Lim
477ff0284a Merge pull request #2229 from garden-co/fix/form-docs
Add twoslash type check to design pattern docs
2025-05-14 15:45:53 +01:00
Guido D'Orsi
85604ec4c5 Merge pull request #2228 from garden-co/changeset-release/main
Version Packages
2025-05-14 16:32:12 +02:00
Trisha Lim
1c641ad03d fix import 2025-05-14 15:14:37 +01:00
Trisha Lim
87cfca41f0 Add twoslash type check to organization design pattern docs 2025-05-14 15:11:32 +01:00
Trisha Lim
2783f8c308 Add twoslash type check to form design pattern docs 2025-05-14 14:49:42 +01:00
github-actions[bot]
de783063e2 Version Packages 2025-05-14 12:46:57 +00:00
Guido D'Orsi
9681691701 Merge pull request #2226 from garden-co/feat/ws-connect-events
feat(worker): add waitForConnection and subscribeToConnectionChange APIs to handle connection drops
2025-05-14 14:42:50 +02:00
Guido D'Orsi
6c7ae1faee Merge pull request #2225 from garden-co/feat/dependencies-load
fix: recovery from missing dependencies when getting new content
2025-05-14 14:42:23 +02:00
jk
9472347b57 nix flake update 2025-05-14 14:37:57 +02:00
jk
b43395d8ed fix(flake) stick to pnpm@9.x
to be in sync with pnpm definition in `package.json`. In other case
`pnpm@10` is used and `pnpm i` or other commands won't work properly
2025-05-14 14:36:04 +02:00
Anselm Eickhoff
12e9837858 Update issue templates 2025-05-14 13:27:49 +01:00
Guido D'Orsi
422dbc4222 feat(worker): add waitForConnection and subscribeToConnectionChange APIs to handle connection drops 2025-05-14 13:09:10 +02:00
Guido D'Orsi
e7ccb2c054 fix: recovery from missing dependencies when getting new content 2025-05-14 12:58:39 +02:00
Guido D'Orsi
2f7046002d Merge pull request #2214 from garden-co/feat/sync-polish
feat: make the SyncManager async-free, support parallel load on server peers
2025-05-14 12:55:36 +02:00
Benjamin S. Leveritt
20c1588249 Merge pull request #2218 from garden-co/2217-type-check-accounts-and-migrations
Adds typechecking to Accounts and Migrations
2025-05-14 10:36:29 +01:00
Benjamin S. Leveritt
f3d3d4dc5d Adds typechecking to Accounts and Migrations 2025-05-14 09:50:38 +01:00
Anselm Eickhoff
3135d711d4 Merge pull request #2216 from garden-co/fix-account-resolve-docs
Fix account resolution in accounts-and-migrations.mdx
2025-05-14 09:02:58 +01:00
Anselm Eickhoff
14ad9622ea Update accounts-and-migrations.mdx 2025-05-14 09:02:24 +01:00
Guido D'Orsi
0fee2aa21b chore: make the SyncManager async-free, support parallel load on server peers 2025-05-13 21:44:10 +02:00
Margaret Culotta
b6de11e125 Merge branch 'main' into feat/1936-add-request-invites-to-docs 2025-05-13 12:56:38 -05:00
Guido D'Orsi
1e6581cd68 Merge pull request #2206 from garden-co/changeset-release/main
Version Packages
2025-05-13 17:48:05 +02:00
github-actions[bot]
aaacaf0130 Version Packages 2025-05-13 15:35:24 +00:00
Guido D'Orsi
7dcca057e7 Merge pull request #2205 from garden-co/feat/self-revoke
feat: allow accounts to self-remove from groups
2025-05-13 17:32:57 +02:00
Anselm
09b815b87e WIP Account schemas 2025-05-13 16:27:52 +01:00
Guido D'Orsi
63570520a3 feat: allow accounts to self-remove from groups 2025-05-13 17:27:51 +02:00
Trisha Lim
aeed9595ae Merge pull request #2203 from garden-co/docs/server-workers-example 2025-05-13 13:40:23 +01:00
Trisha Lim
6755e28d0f docs: link to server workers example 2025-05-13 12:28:18 +01:00
Trisha Lim
8e5ff13115 fix(inspector): colist shows too many items after navigating from a longer colist 2025-05-13 12:00:38 +01:00
Guido D'Orsi
23521e6468 Merge pull request #2197 from garden-co/changeset-release/main
Version Packages
2025-05-12 20:06:14 +02:00
github-actions[bot]
1627eb7652 Version Packages 2025-05-12 18:05:04 +00:00
Guido D'Orsi
ff846d9095 fix: loading RawAccount with the new subscription engine 2025-05-12 20:02:57 +02:00
Guido D'Orsi
8d7d62d64f Merge pull request #2194 from garden-co/changeset-release/main
Version Packages
2025-05-12 19:09:18 +02:00
github-actions[bot]
65a8227b2b Version Packages 2025-05-12 14:58:57 +00:00
Guido D'Orsi
a846e0730e fix: block load of invalid ids on a lower level and do not break sync when getting invalid ids 2025-05-12 16:56:32 +02:00
Trisha Lim
e9c0e65170 Merge pull request #2190 from garden-co/improvement/homepage-cta
homepage: improve CTA, move problem section down
2025-05-12 15:53:55 +01:00
Trisha Lim
14636606bd homepage: improve CTA, move problem section down 2025-05-12 14:59:31 +01:00
Guido D'Orsi
5118ff6585 chore: remove unused typecheck clause 2025-05-12 14:34:45 +02:00
Guido D'Orsi
51a0f86b99 test: add tests on the load API for unavailable retry 2025-05-12 14:34:45 +02:00
Guido D'Orsi
fa61ab4eb5 chore: make the retries use CO_VALUE_LOADING_CONFIG 2025-05-12 14:34:45 +02:00
Benjamin S. Leveritt
1ab839bd59 Switch to mapping by grapheme, rather than code points 2025-05-12 10:49:02 +01:00
Benjamin S. Leveritt
af3b13428c Replace Intl.Segmenter with unicode-segmenter 2025-05-12 10:48:23 +01:00
Benjamin S. Leveritt
fe241e7e7c Add extended tests for complex grapheme splitting 2025-05-12 10:12:56 +01:00
Trisha Lim
d4cdd43cf7 Merge pull request #2173 from garden-co/docs/change-role
add docs for changing roles and removing members in a Group
2025-05-12 10:05:48 +01:00
Anselm Eickhoff
c5d5fb25f7 Merge pull request #2183 from garden-co/jmsv/jazz-tiptap
jazz-richtext-tiptap
2025-05-10 12:27:18 +01:00
James Vickery
38f2276fdd version example app 2025-05-10 12:19:48 +01:00
James Vickery
b92bfce041 jazz-richtext-tiptap 2025-05-10 12:18:27 +01:00
Benjamin S. Leveritt
e3fd7bf6e3 Merge pull request #2154 from garden-co/1816-update-examples-to-coplaintext
Update examples to CoPlainText
2025-05-09 20:18:30 +01:00
Benjamin S. Leveritt
7526e444d6 Merge origin/main into 1816-update-examples-to-coplaintext 2025-05-09 20:00:59 +01:00
Benjamin S. Leveritt
28522bd062 Merge origin/main into 1816-update-examples-to-coplaintext 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
824be82f94 Adds a note for React use of CoText 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
8a9ef8ac78 Update chat-rn-expo-clerk with shorthands 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
5fb2f959f1 Fix comment 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
7a900d43f3 Fix formatting 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
c8624d56d2 Updates richtext with shorthands 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
8ed7c3947d Updates version-history with shorthands 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
ec0e61a442 Updates todo with shorthands 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
824d417d4e Updates form with shorthands 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
9f1ce81e09 Updates chat-vue with shorthands 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
48b32b6a72 Updates chat-rn with shorthands 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
03fec5f1b3 Updates chat with new CoPlainText shorthands 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
cdd7ac98cc Update examples/form/src/OrderForm.tsx
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
8218a272ee Update chat-rn-expo-clerk to CoPlainText 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
25d146ead8 Update chat-vue to CoPlainText 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
3f4d493286 Update chat-rn to CoPlainText 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
d476b06d0e Update version-history to CoPlainText 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
22baf1f547 Update todo to CoPlainText
Replace text with CoPlainText in todo
2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
d6ec873b1e Update form to CoPlainText 2025-05-09 20:00:38 +01:00
Benjamin S. Leveritt
160b30406b Update chat to CoPlainText 2025-05-09 20:00:38 +01:00
Guido D'Orsi
3e473eff50 chore: skip flaky test 2025-05-09 18:44:36 +02:00
Guido D'Orsi
47ad35085e Merge pull request #2179 from garden-co/changeset-release/main
Version Packages
2025-05-09 18:42:53 +02:00
github-actions[bot]
0b0d06ea44 Version Packages 2025-05-09 16:40:20 +00:00
Guido D'Orsi
ec546b4cd6 fix(svelte): fix return type of the id callback to accept undefined values 2025-05-09 18:38:24 +02:00
Guido D'Orsi
cadc5ef913 Merge pull request #2177 from garden-co/changeset-release/main
Version Packages
2025-05-09 18:14:32 +02:00
github-actions[bot]
f961dedbd0 Version Packages 2025-05-09 16:11:48 +00:00
Guido D'Orsi
c7b27e902b Merge pull request #2110 from garden-co/filestream-metadata
feat(FileStream): add getMetadata to easily get file metadata without having to process all the file chunks
2025-05-09 18:07:15 +02:00
Guido D'Orsi
b1cbc9283a Merge pull request #2174 from garden-co/feat/svelte-classes
feat(svelte):  new CoState and AccountCoState APIs
2025-05-09 18:05:02 +02:00
Guido D'Orsi
f2c7c20a75 Merge pull request #2176 from garden-co/changeset-release/main
Version Packages
2025-05-09 17:58:47 +02:00
Guido D'Orsi
53705d0ac1 test: fix file-share-svelte e2e test 2025-05-09 17:47:35 +02:00
github-actions[bot]
4031b7532f Version Packages 2025-05-09 15:41:11 +00:00
Guido D'Orsi
5ac43c300d Merge pull request #2123 from garden-co/repro-rnquickcrypto-chat-signature-issue
Fix the InvalidSignature issues on RNQuickCrypto
2025-05-09 17:39:16 +02:00
Anselm
db3c737e9f Make co.feed() work 2025-05-09 16:31:24 +01:00
Guido D'Orsi
064174501e Merge remote-tracking branch 'origin/main' into repro-rnquickcrypto-chat-signature-issue 2025-05-09 17:21:32 +02:00
Guido D'Orsi
b0c2a5a53f changeset 2025-05-09 17:20:09 +02:00
Guido D'Orsi
24d9a6b7e1 chore: cleanup 2025-05-09 17:16:24 +02:00
Guido D'Orsi
e270295387 chore: add comments to ignore binding_property_non_reactive 2025-05-09 17:13:13 +02:00
Guido D'Orsi
67fa7be0d4 feat(svelte): react to id changes b accepting an id callback, remove the version state 2025-05-09 16:40:17 +02:00
Anselm
28d7fe9b90 Start updating the homepage 2025-05-09 15:29:55 +01:00
Guido D'Orsi
3431076350 feat: add CoState and AccountCoState class APIs to subscribe CoValues for better integration with Svelte runes 2025-05-09 15:51:35 +02:00
Anselm
4c70ca198b Port coList tests to zod 2025-05-09 14:40:54 +01:00
Guido D'Orsi
e8092141e8 Merge pull request #2175 from garden-co/improvement/inspector-in-starter
embed inspector component in react starter app
2025-05-09 15:29:25 +02:00
Guido D'Orsi
a2aac6791d Merge pull request #2171 from garden-co/docs/vanilla-inspector
update inspector docs to include vanilla, vue, svelte
2025-05-09 15:29:14 +02:00
Anselm
742283263f Get rid of coField<...>, stop using instance getters on CoMaps 2025-05-09 14:26:55 +01:00
Trisha Lim
aebd1519c3 embed inspector component in react starter app 2025-05-09 14:00:29 +01:00
Trisha Lim
a9cf6da27a add docs for changing roles and removing members in a Group 2025-05-09 13:47:00 +01:00
Guido D'Orsi
2e0378639c fix env access 2025-05-09 14:42:19 +02:00
Trisha Lim
5ba76eeab5 update inspector docs to include vanilla, vue, svelte 2025-05-09 13:31:04 +01:00
Anselm
5f4e9a9936 Trying to make records work 2025-05-09 10:35:18 +01:00
Guido D'Orsi
279fc1c390 test: skip flaky test on CI 2025-05-08 21:32:44 +02:00
Brad Anderson
865b0e81a7 chore: remove debug stuffs 2025-05-08 15:32:07 -04:00
Guido D'Orsi
829ab08873 Merge pull request #2165 from garden-co/changeset-release/main
Version Packages
2025-05-08 21:28:50 +02:00
github-actions[bot]
2c9b08a080 Version Packages 2025-05-08 19:27:22 +00:00
Guido D'Orsi
48bda8854f Merge pull request #2163 from garden-co/vanilla-inspector
feat: package jazz-inspector as custom element
2025-05-08 21:24:59 +02:00
Brad Anderson
2858db7419 fix: bump RNQC to version with verify fix 2025-05-08 15:18:40 -04:00
Guido D'Orsi
96ed9adf59 Merge pull request #2142 from garden-co/feat/simplify-accept-invite
feat: simplified the acceptInvite method and cleaned the coValue transfer methods
2025-05-08 18:21:03 +02:00
Guido D'Orsi
5e4905ca99 test: add sync invite tests 2025-05-08 18:18:03 +02:00
Guido D'Orsi
1d4949b70c Merge remote-tracking branch 'origin/main' into feat/simplify-accept-invite 2025-05-08 18:08:02 +02:00
Guido D'Orsi
7dacfd03f9 Merge remote-tracking branch 'origin/main' into vanilla-inspector 2025-05-08 18:05:55 +02:00
Guido D'Orsi
bd4191520e Merge pull request #2164 from garden-co/fix/sync-browser-flaky-test
test: fix the flakiness on a browser integration test
2025-05-08 18:05:19 +02:00
Guido D'Orsi
e3dfb1b06e test: fix the flakiness on a browser integration test 2025-05-08 18:05:06 +02:00
Guido D'Orsi
7de210f225 feat: package jazz-inspector as custom element 2025-05-08 17:48:49 +02:00
Brad Anderson
d456a8c124 fix: regex to stop dupes (for realz) 2025-05-08 10:58:51 -04:00
Guido D'Orsi
1676ff852a fix: remove a broken JSON.stringify from expectCoValueLoaded error 2025-05-08 16:14:24 +02:00
Guido D'Orsi
2217e12ba6 clearUserCredentials 2025-05-08 10:08:27 -04:00
Guido D'Orsi
a8af6efe1a Merge pull request #2162 from garden-co/changeset-release/main
Version Packages
2025-05-08 16:06:25 +02:00
Anselm
2b40b3052c Implement the rest of CoMap loading & subscribing 2025-05-08 15:05:55 +01:00
Guido D'Orsi
519eda0ac2 fix: skip profileID check 2025-05-08 09:59:30 -04:00
Brad Anderson
a8725abfb4 fix: XCode 16.3 bug fix, RNQuickCrypto remove dead code 2025-05-08 09:40:19 -04:00
Guido D'Orsi
2229e5a64f Create a repro for the InvalidSignature issues on RNQuickCrypto 2025-05-08 09:40:18 -04:00
github-actions[bot]
ee11b30d3a Version Packages 2025-05-08 13:38:09 +00:00
Guido D'Orsi
ef78d58729 Merge pull request #2155 from garden-co/feat/missing-profile
fix(auth): removed throw error when the profile is unavailable after a login
2025-05-08 15:36:09 +02:00
Guido D'Orsi
40e2dd0ece Merge pull request #2159 from garden-co/feat/storage-sync
fix: wait for storage sync before resolving the new account creation
2025-05-08 15:35:41 +02:00
Benjamin S. Leveritt
b60c7c1ce0 Merge pull request #2156 from garden-co/play-selection-co-literal-jazz-paper-scissors
Changes to co.literal for playSelection
2025-05-08 14:22:01 +01:00
Benjamin S. Leveritt
69cd362114 Merge pull request #2160 from garden-co/coplaintext-shorthands
CoPlainText shorthands
2025-05-08 14:19:31 +01:00
Benjamin S. Leveritt
526bf0a3cf Adds better output for Number(CoPlainText) 2025-05-08 13:57:59 +01:00
Benjamin S. Leveritt
439f0fe57e Add changeset 2025-05-08 13:30:07 +01:00
Benjamin S. Leveritt
686433f42e Adds reading as string shorthand
Dropping need for `.toString()` in situations where a string is expected
2025-05-08 13:29:54 +01:00
Benjamin S. Leveritt
3ba258e181 Adds owner shorthand to create and constructor 2025-05-08 13:27:55 +01:00
Guido D'Orsi
d6e143e4d5 fix: wait for storage sync before resolving the new account creation 2025-05-08 14:24:11 +02:00
Guido D'Orsi
3e6229da4d feat(waitForSync): skip closed peers and not subscribed client peers 2025-05-08 14:22:55 +02:00
Guido D'Orsi
adfc9a6032 feat: make waitForSync work on storage peers by handling optimistic/known state 2025-05-08 14:19:07 +02:00
Anselm
9dd926a59a Add todo for discriminated union narrowing 2025-05-08 12:08:35 +01:00
Anselm
9aa6ab6fea Make discriminated unions of CoMaps work 2025-05-08 12:03:31 +01:00
Anselm
4207fb5914 Use zod schemas for simple CoMap tests 2025-05-08 10:58:02 +01:00
Benjamin S. Leveritt
1a6879b1c2 Fix selection in worker 2025-05-08 10:10:45 +01:00
Benjamin S. Leveritt
172fec56f6 Changes to co.literal for playSelection 2025-05-08 10:06:32 +01:00
Guido D'Orsi
13892071f5 fix(auth): removed throw error when the profile is unavailable after a login 2025-05-08 10:45:23 +02:00
Anselm
cb3e5cbe81 Fix build, update RN (to support zod4's wildcard exports) and make tests pass 2025-05-07 20:07:55 +01:00
Guido D'Orsi
b62d75b847 Merge pull request #2149 from garden-co/changeset-release/main
Version Packages
2025-05-07 18:31:36 +02:00
Anselm
d199fb06b3 Make ImageDefs work, fix circular imports 2025-05-07 17:29:52 +01:00
github-actions[bot]
beafbd3088 Version Packages 2025-05-07 16:29:39 +00:00
Guido D'Orsi
653d8ba69f Merge pull request #2140 from garden-co/guido/rn-e2e-test
test: cover join chat in the rn e2e tests
2025-05-07 18:27:56 +02:00
Guido D'Orsi
6c23dab790 Merge pull request #2148 from garden-co/fix/autoload
fix(subscribe): fix error management and autoload on $each
2025-05-07 18:27:42 +02:00
Guido D'Orsi
80530a4065 fix(subscribe): fix error management and autoload on 2025-05-07 18:24:01 +02:00
Guido D'Orsi
e14e61f7d9 feat: simplified the acceptInvite method and cleaned the coValue transfer methods 2025-05-07 16:23:57 +02:00
Guido D'Orsi
2ba5ea684e Merge pull request #2089 from garden-co/changeset-release/main
Version Packages
2025-05-07 14:37:04 +02:00
Guido D'Orsi
a8466946d3 test: cover join chat in the rn e2e tests 2025-05-07 14:25:56 +02:00
github-actions[bot]
105b240076 Version Packages 2025-05-07 11:36:04 +00:00
Guido D'Orsi
097dd8a646 Update large-rings-help.md 2025-05-07 13:34:23 +02:00
Guido D'Orsi
138dd7ee8b Merge pull request #2139 from garden-co/feat/simplify-localnode-auth
feat: simplify LocalNode account creation flow
2025-05-07 13:30:25 +02:00
Guido D'Orsi
5b17085b30 test(e2e-rn): add an initial cleanup step 2025-05-07 13:19:32 +02:00
Guido D'Orsi
cb7bdffabe test(cloudflare): wait for the account to be uploaded before starting the worker 2025-05-07 12:54:48 +02:00
Anselm
0eaa500aef Make basic chat app work 2025-05-07 11:17:39 +01:00
Guido D'Orsi
90892523da feat: simplify LocalNode account creation flow 2025-05-07 11:04:52 +02:00
Anselm
774c05cef0 Merge branch 'main' into feature/zod-horribly 2025-05-07 09:28:45 +01:00
Guido D'Orsi
693a058890 chore: fix type error in sqlite test 2025-05-06 23:19:48 +02:00
Guido D'Orsi
4d20f1bcfc Merge pull request #2114 from garden-co/perf/storage
Perf: optimize dependency load from storage and skip self sync on storage peers
2025-05-06 22:57:51 +02:00
Guido D'Orsi
36bf3924ff Merge pull request #2015 from garden-co/feat/new-subscription
A new subscription engine for jazz-tools
2025-05-06 22:57:37 +02:00
Guido D'Orsi
6b6fc8b31a chore: remove a console.log 2025-05-06 22:56:22 +02:00
Anselm
1649f06667 Allow immediately unsubscribing from CoValueCore changes (and use that) 2025-05-06 17:37:38 +01:00
Anselm
e9bd290dec Merge branch 'main' into feat/new-subscription 2025-05-06 17:35:17 +01:00
Guido D'Orsi
9d76292db5 Merge pull request #2125 from garden-co/fix/expect-covalue-loaded
fix(sync): avoid triggering fatal errors when syncing without all the deps loaded
2025-05-06 17:47:21 +02:00
Guido D'Orsi
66373bade3 fix(sync): avoid triggering fatal errors when syncing without all the deps loaded 2025-05-06 17:42:34 +02:00
Guido D'Orsi
40118a0b2c chore: remove console.log 2025-05-06 17:32:28 +02:00
Anselm Eickhoff
86b57415b8 Merge pull request #2074 from garden-co/refactor/combine-covalue-core-and-state
Combine CoValueCore and CoValueState Part 1 & 2
2025-05-06 15:55:02 +01:00
Guido D'Orsi
cdbc37a144 fix: add dist/crypto to the exports 2025-05-06 16:16:44 +02:00
Anselm
96a1f303fe Merge branch 'refactor/combine-covalue-core-and-state' into feature/zod-horribly 2025-05-06 15:14:47 +01:00
Benjamin S. Leveritt
e996c71e83 Merge pull request #2121 from garden-co/2120-fix-covalues-crud-links
2120-fix-covalues-crud-links
2025-05-06 15:01:16 +01:00
Anselm Eickhoff
0f704a2330 Merge pull request #2108 from garden-co/refactor/simplify-localnode-bootstrap-and-more
Simplify LocalNode bootstrap and more
2025-05-06 14:38:52 +01:00
Guido D'Orsi
2f206e7613 chore: clean up changes from cojson 2025-05-06 15:18:37 +02:00
Guido D'Orsi
761759c893 chore: changeset 2025-05-06 15:02:40 +02:00
Guido D'Orsi
15c57e24ad test: cover subscribe updates on permissions change 2025-05-06 14:59:39 +02:00
Benjamin S. Leveritt
580e2577c9 Fix SchemaUnions 2025-05-06 13:45:55 +01:00
Guido D'Orsi
3804198c02 test: cover CoMap.Record and unsubscribe on refs change 2025-05-06 14:42:23 +02:00
Guido D'Orsi
537d56a30e chore: code cleanup 2025-05-06 14:41:33 +02:00
Benjamin S. Leveritt
5524d3e0cb Unneeded error squash 2025-05-06 13:18:03 +01:00
Anselm Eickhoff
3d2682e3a7 Merge pull request #2109 from garden-co/refactor/combine-covalue-core-and-state-2
Combine CoValueCore and CoValueState Part Part 2
2025-05-06 13:15:38 +01:00
Benjamin S. Leveritt
0c131f3af4 Last bits 2025-05-06 13:07:11 +01:00
Benjamin S. Leveritt
fada6b0eb5 Fix FileStreams section 2025-05-06 13:00:22 +01:00
Benjamin S. Leveritt
c8b94475cd Fix CoFeeds section 2025-05-06 12:58:13 +01:00
Benjamin S. Leveritt
162a024345 Fix CoLists section 2025-05-06 12:54:53 +01:00
Benjamin S. Leveritt
7cd195b3a4 Fix CoMaps section 2025-05-06 12:52:38 +01:00
Benjamin S. Leveritt
ccad5a3c70 Merge pull request #2042 from garden-co/2041-react-installation-docs
Adds React installation doc
2025-05-06 12:28:10 +01:00
Benjamin S. Leveritt
4268d189cb Adds link to useCoState 2025-05-06 12:27:37 +01:00
Anselm
d48712986f Merge branch 'refactor/simplify-localnode-bootstrap-and-more' into refactor/combine-covalue-core-and-state-2 2025-05-06 12:08:42 +01:00
Anselm
ba2fda5de1 Use ephemeral nodes again instead of switching control 2025-05-06 12:00:45 +01:00
Guido D'Orsi
b1d26f23d6 test: cover push on deeply loaded lists 2025-05-06 12:56:57 +02:00
Sammii
e7766746d9 Merge pull request #2118 from garden-co/fix/og-image-2-font-api-route
switching out local font for google fonts font
2025-05-06 11:40:48 +01:00
Anselm
7c64d104a6 Merge branch 'refactor/combine-covalue-core-and-state' into refactor/simplify-localnode-bootstrap-and-more 2025-05-06 11:39:42 +01:00
Anselm
1870a1268a Explain CoValueCore.verified 2025-05-06 11:31:50 +01:00
Anselm
a8222368d0 Faster implementation of VerifiedState.clone() 2025-05-06 11:20:07 +01:00
Sammii
72d11ce003 switching out local font for google fonts font 2025-05-06 11:18:35 +01:00
Anselm
6a55a548a0 Replacing ID<CoValue> in favor of string, initial "soft" step 2025-05-06 11:17:22 +01:00
Guido D'Orsi
5fbf9770d2 test: cover more logic on coList and coMap 2025-05-06 11:42:10 +02:00
Trisha Lim
707dedb33e Merge pull request #2115 from garden-co/fix/toc-headings
fix: hidden headings are showing in TOC
2025-05-06 10:33:29 +01:00
Guido D'Orsi
86d42d9e49 chore: add comments 2025-05-06 10:31:56 +02:00
Guido D'Orsi
64f01915f4 Revert "fix(useCoState): defer updates to avoid getting the invalid mutation error when doing autoload"
This reverts commit c75a3042ce.
2025-05-06 10:26:00 +02:00
Guido D'Orsi
9a8fd2ce47 perf: do not trigger updates when autoloading available refs 2025-05-06 10:25:24 +02:00
Guido D'Orsi
4dc7cdb4e6 fix: coFeed account autoload 2025-05-06 10:11:15 +02:00
Guido D'Orsi
84b993944d chore: clanup 2025-05-05 18:46:11 +02:00
Guido D'Orsi
fa064443a0 chore: clanup 2025-05-05 18:45:39 +02:00
Trisha Lim
32e9678394 fix: hidden headings are showing in TOC 2025-05-05 17:10:08 +01:00
Guido D'Orsi
b1850efd7f chore: rename CoValueResolutionNode into SubscriptionScope 2025-05-05 18:05:10 +02:00
Guido D'Orsi
922e3c8244 chore: revert processedChangesId 2025-05-05 18:00:54 +02:00
Guido D'Orsi
1c063455d1 chore: revert timeout on click 2025-05-05 17:58:41 +02:00
Guido D'Orsi
35abeba323 Merge branch 'main' into feat/new-subscription 2025-05-05 17:58:12 +02:00
Guido D'Orsi
84f623097f chore: add hidden project generation page 2025-05-05 17:56:43 +02:00
Guido D'Orsi
73015a3438 fix: remove singleTabOPFS from the storage types 2025-05-05 17:55:20 +02:00
Guido D'Orsi
7c3bf78fef chore: split subscribe module 2025-05-05 17:33:53 +02:00
Guido D'Orsi
68cb6064a5 chore: simplify autoload code 2025-05-05 17:15:44 +02:00
Guido D'Orsi
992d5e572e feat: polish code and fix the typescript errors 2025-05-05 16:41:55 +02:00
Guido D'Orsi
c75a3042ce fix(useCoState): defer updates to avoid getting the invalid mutation error when doing autoload 2025-05-05 16:41:11 +02:00
Anselm
0e87c1c971 Fix more occurences of coField 2025-05-05 15:28:48 +01:00
Guido D'Orsi
fa8b20899d test(upload): increase the timeout on the share file button 2025-05-05 16:15:58 +02:00
Guido D'Orsi
f24cad1909 perf(sync): skip self-sync when getting content from storage peers 2025-05-05 16:06:34 +02:00
Guido D'Orsi
8b2df0e5e2 perf(storage): optimized the dependency push from storage to send a given dependency only once 2025-05-05 16:05:05 +02:00
Guido D'Orsi
f93222079f fix(coList): correctly copy state when calling rebuildFromCore 2025-05-05 15:44:50 +02:00
Guido D'Orsi
514f4c9a72 test(permissions): skip tests on invite roles group key revelation 2025-05-05 15:26:42 +02:00
Anselm
89fac342bf Rename co -> coField in preparation of giving co a new meaning 2025-05-05 11:38:35 +01:00
Guido D'Orsi
02a240ce75 feat(FileStream): add getMetadata to easily get file metadata 2025-05-05 11:20:10 +02:00
Guido D'Orsi
6b781cf4a6 feat(RawBinaryCoStreamView): add getBinaryStreamInfo to retrieve file info without processing all the chunks 2025-05-05 11:18:42 +02:00
Anselm
d5daf060c9 Merge CoValueStore into LocalNode and more cleanup 2025-05-04 15:13:01 +01:00
Anselm
604cd4e3a9 Merge CoValueState into CoValueCore 2025-05-04 14:33:27 +01:00
Anselm
3446b38f69 Provide core instead of content to CoValueCore listeners 2025-05-04 11:54:31 +01:00
Anselm
de12b03d3f use nodeWithRandomAgentAndSessionID in more tests 2025-05-04 11:49:41 +01:00
Anselm
c3db5cf0b5 Get rid of permission/key tracing and debug logs 2025-05-04 11:44:30 +01:00
Anselm
e7f2521b41 Fix recursion issues and all tests but one 2025-05-04 11:37:57 +01:00
Anselm
0c6cd571c9 Fix types in packages depending on cojson 2025-05-04 09:44:15 +01:00
Anselm
e1d56a45f7 Simplify LocalNode bootstrap and more 2025-05-03 16:41:50 +01:00
Anselm
e40e01fce0 Make core.verified nullable instead of core itself 2025-05-03 13:50:30 +01:00
Guido D'Orsi
d70343d864 chore(subscribe): code cleanup 2025-05-02 23:32:25 +02:00
Guido D'Orsi
160ab768e9 feat(storage): deduplicate dependency load 2025-05-02 23:31:48 +02:00
Guido D'Orsi
4ff03f67d9 feat: skip self-sync on new content for storage peers 2025-05-02 23:31:14 +02:00
Guido D'Orsi
5a81adffec feat: make the unavailable state transitive 2025-05-02 22:39:39 +02:00
Guido D'Orsi
f4fdb3c14e Merge pull request #2073 from garden-co/fix/inspector-docs
fix(docs): hide inspector widget docs for non-react
2025-05-02 18:59:37 +02:00
Guido D'Orsi
d001144a87 fix: update value when raw changes 2025-05-02 15:58:52 +02:00
Trisha Lim
ded473b75e Merge pull request #2087 from garden-co/fix/code-snippet-overflow
prevent horizontal scroll on code sample in landing page
2025-05-02 14:58:51 +01:00
Trisha Lim
602c34b0f2 Merge pull request #2080 from garden-co/fix/docs-content-overflow
fix(docs): body wider than page container
2025-05-02 14:58:19 +01:00
Guido D'Orsi
77cf06945a Merge remote-tracking branch 'origin/main' into feat/new-subscription 2025-05-02 14:25:56 +02:00
Guido D'Orsi
61f39bb56f Merge pull request #2061 from garden-co/fix/redundant-messages
fix(sync): remove redundant content messages when loading a coValue from multiple peers
2025-05-02 14:25:01 +02:00
Trisha Lim
7cc51b77f3 prevent horizontal scroll on code sample in landing page 2025-05-02 12:18:30 +01:00
Trisha Lim
bf303d58e3 fix(docs): body wider than page container 2025-05-02 11:57:22 +01:00
Sammii
def5c474e6 Merge pull request #2054 from garden-co/add-docs-og-image 2025-05-02 11:37:59 +01:00
Sammii
c81dca23ad catching empty toc 2025-05-02 10:55:35 +01:00
Guido D'Orsi
b470f63f86 chore: changeset 2025-05-02 11:54:47 +02:00
Anselm
5c7072bf6e Merge remote-tracking branch 'origin/fix/redundant-messages' into refactor/combine-covalue-core-and-state 2025-05-02 10:39:47 +01:00
Guido D'Orsi
bd62b1342a feat: add loadRequestSent to more surgically avoid to send a load request twice on the same peer in the sync protocol 2025-05-02 11:38:57 +02:00
Trisha Lim
af314e8584 hide inspector widget docs for non-react 2025-05-02 10:28:16 +01:00
Anselm
8ad1878f86 Fix biomes trailing comma 2025-05-02 10:26:47 +01:00
Anselm
2fe5cd1326 Split verified state logic out of CoValueCore 2025-05-02 10:23:29 +01:00
pax
2ae0b8df0d Merge pull request #2055 from garden-co/changeset-release/main
Version Packages
2025-05-01 16:23:16 +03:00
github-actions[bot]
77dc51d466 Version Packages 2025-05-01 13:19:08 +00:00
pax
bd645db4cc Merge pull request #2071 from garden-co/create-jazz-app-git-flag
create-jazz-app --git flag
2025-05-01 16:17:01 +03:00
pax-k
af46c68a4a chore: changest 2025-05-01 16:15:06 +03:00
pax-k
fb58cb9299 feat(create-jazz-app): option to pass git init boolean flag 2025-05-01 16:11:32 +03:00
Anselm
b3605c0c22 Create folder for coValueCore before breaking it into pieces 2025-05-01 12:41:45 +01:00
James Vickery
63fb80e50d Merge pull request #2069 from garden-co/jmsv/jazz-richtext-prosemirror-custom-schema
createJazzPlugin support custom ProseMirror schema
2025-05-01 11:56:35 +01:00
James Vickery
133b8abcbe createJazzPlugin support custom ProseMirror schema 2025-05-01 10:35:16 +01:00
Guido D'Orsi
e272849026 chore: add comment 2025-04-30 23:37:27 +02:00
Guido D'Orsi
c4fdfeaa48 fix(peer): disable crashOnClose while doing the gracefulShutdown 2025-04-30 23:33:21 +02:00
Guido D'Orsi
3f0859c3f2 fix(sync): remove redundant content messages when loading a coValue from multiple peers 2025-04-30 23:16:30 +02:00
Sammii
6e286bac7e creating styled open graph images for docs topics and subtopics 2025-04-30 18:00:02 +01:00
Benjamin S. Leveritt
235aab15b6 Adds a bit more context 2025-04-30 15:41:12 +01:00
Benjamin S. Leveritt
a440121ac9 Tweaks copy 2025-04-30 15:31:42 +01:00
Guido D'Orsi
6209bd2285 Merge pull request #2057 from garden-co/feat/resolve-load-earlier
feat: resolve load earlier and move the retry in LocalNode
2025-04-30 16:25:53 +02:00
Benjamin S. Leveritt
8ad9fc57cc Simplifies sections 2025-04-30 15:14:50 +01:00
Benjamin S. Leveritt
a763b947b8 Remove the Introduction header 2025-04-30 15:14:50 +01:00
Benjamin S. Leveritt
026a26da3c Adds React installation doc 2025-04-30 15:14:50 +01:00
Benjamin S. Leveritt
d8d1addf2b Merge pull request #2056 from garden-co/2049-show-how-to-supply-owner-in-filestream-docs
2049-show-how-to-supply-owner-in-filestream-docs
2025-04-30 15:13:49 +01:00
Guido D'Orsi
937a34c76e test(prosemirror): skip failing test 2025-04-30 16:13:42 +02:00
Guido D'Orsi
c6142a1f64 chore: cleanup 2025-04-30 16:06:36 +02:00
Guido D'Orsi
f3fb2dee52 Merge branch 'feat/resolve-load-earlier' into feat/new-subscription 2025-04-30 16:00:02 +02:00
Guido D'Orsi
15996ced64 Merge remote-tracking branch 'origin/main' into feat/resolve-load-earlier 2025-04-30 15:57:38 +02:00
Guido D'Orsi
9fb98e2114 feat: resolve load earlier and move the retry in LocalNode 2025-04-30 15:45:52 +02:00
Benjamin S. Leveritt
f55f779ea1 Adds owner to FileStream examples
Adds twoslash too
2025-04-30 14:36:26 +01:00
Margaret Culotta
d23c71d511 working through twoslash errors 2025-04-30 08:11:32 -05:00
Benjamin S. Leveritt
18c98fc3f5 Merge pull request #2053 from garden-co/2052-match-filestreamsubscribe-signatures-with-other-covalues
Update FileStream.subscribe signature to allow omitting options
2025-04-30 14:05:57 +01:00
Guido D'Orsi
41b286b672 Merge pull request #2051 from garden-co/feat/incremental-processing
feat(colist): re-introduce incremental processing
2025-04-30 14:51:25 +02:00
Sammii
3c97e8e7f2 Merge branch 'main' into add-docs-og-image 2025-04-30 13:33:07 +01:00
Sammii
7996a2aa9c og image docs 2025-04-30 13:30:30 +01:00
Benjamin S. Leveritt
ba944c20ed Update subscribe signature + tests 2025-04-30 13:28:07 +01:00
Guido D'Orsi
0b89fadfdd feat(colist): re-introduce incremental processing 2025-04-30 13:12:43 +02:00
Guido D'Orsi
1e50cebf55 test(coList): add failing tests on the append operation 2025-04-30 11:33:52 +02:00
Trisha Lim
ca8c5c0b02 Merge pull request #2010 from garden-co/docs/update-examples-page
update examples page
2025-04-30 09:33:15 +01:00
Benjamin S. Leveritt
a0aa261cab Merge pull request #1998 from garden-co/1962-add-cotext-docs
Adds CoText doc
2025-04-30 08:16:11 +01:00
Benjamin S. Leveritt
5d3d11e87c Merge pull request #2016 from garden-co/1862-react-provider-doc
1862-react-provider-doc
2025-04-30 07:01:09 +01:00
Benjamin S. Leveritt
4a9ed21ea2 Fix links 2025-04-29 20:11:22 +01:00
Guido D'Orsi
2ddfc9d92b Merge pull request #2017 from garden-co/changeset-release/main
Version Packages
2025-04-29 18:43:06 +02:00
github-actions[bot]
a032fda936 Version Packages 2025-04-29 16:34:56 +00:00
Guido D'Orsi
c6fb8dc845 fix: handle null values on msg.id 2025-04-29 18:32:08 +02:00
Guido D'Orsi
bf399d72c1 feat(subscribe): improve error logging, spec compliance 2025-04-29 18:31:14 +02:00
Guido D'Orsi
60fefe8158 Merge remote-tracking branch 'origin/main' into feat/new-subscription 2025-04-29 16:24:53 +02:00
Guido D'Orsi
9be66e196c feat(subscribe): improve error logging, spec compliance 2025-04-29 16:24:43 +02:00
Guido D'Orsi
04d96e52e0 fix: handle null values on msg.id 2025-04-29 16:24:43 +02:00
Guido D'Orsi
5b483dac6f feat: optimize the resolution of the loading states 2025-04-29 16:24:43 +02:00
Guido D'Orsi
fadb4bf76e feat(RawCoList): incremental processing 2025-04-29 16:17:37 +02:00
Guido D'Orsi
98a25b1fd6 feat(todo-example): add a page to generate random projects with many tasks 2025-04-29 16:04:10 +02:00
Trisha Lim
69499e3965 lint 2025-04-29 11:56:01 +01:00
Trisha Lim
d67ced14c4 Merge pull request #1988 from garden-co/tobiaslins-patch-1
[expo] Fix CSS import when using `create-jazz-app`
2025-04-29 11:01:16 +01:00
Trisha Lim
95ae69ead2 add coplaintext to rich text example card 2025-04-29 10:07:44 +01:00
Trisha Lim
4170f13858 jazz-paper-scissors: move api key, add lint, change demo url 2025-04-29 10:07:44 +01:00
Trisha Lim
45e4a77afb lint fix 2025-04-29 10:07:44 +01:00
Trisha Lim
603538e255 add jazz-paper-scissors to examples page 2025-04-29 10:07:44 +01:00
Trisha Lim
afb49f3666 update richtext readme 2025-04-29 10:07:44 +01:00
Trisha Lim
c6de2ce8b8 add richtext example to examples page 2025-04-29 10:07:44 +01:00
Benjamin S. Leveritt
cdc4229df7 Add example app 2025-04-28 22:36:09 +01:00
Benjamin S. Leveritt
fa19f7471f Fix component name 2025-04-28 21:54:18 +01:00
Benjamin S. Leveritt
75f3af2cc1 Adds more config options 2025-04-28 21:48:32 +01:00
Benjamin S. Leveritt
5ae77ee57e Adds React Provider doc 2025-04-28 17:39:35 +01:00
Guido D'Orsi
6df5d72dfd fix: Account deep loading 2025-04-28 18:23:49 +02:00
Guido D'Orsi
b91b33e9be feat: pass the subscribe.test.ts 2025-04-28 18:06:00 +02:00
Benjamin S. Leveritt
b24071cf33 Fixes example for TwoSlash 2025-04-28 16:36:25 +01:00
Guido D'Orsi
fd7226585b Merge remote-tracking branch 'origin/main' into feat/new-subscription 2025-04-28 17:17:08 +02:00
Benjamin S. Leveritt
a5c88c08de Merge main into feat/1936-add-request-invites-to-docs 2025-04-28 16:14:17 +01:00
Guido D'Orsi
712b67b782 fix: improve permission checks 2025-04-28 14:29:25 +02:00
Guido D'Orsi
88ea30a6f8 Merge pull request #2013 from garden-co/changeset-release/main
Version Packages
2025-04-28 14:12:53 +02:00
github-actions[bot]
f4cbe395d5 Version Packages 2025-04-28 12:09:21 +00:00
Guido D'Orsi
c59fb5dc1f fix: complete the incremental view revert 2025-04-28 14:07:15 +02:00
Guido D'Orsi
c712ef28e8 fix(coList): revert incremental processing 2025-04-28 13:20:09 +02:00
Guido D'Orsi
c62a4a1c69 Revert "perf(colist): process the content incrementally"
This reverts commit e05dff9c32.
2025-04-28 13:15:56 +02:00
Benjamin S. Leveritt
87a7cf202f Tweak copy 2025-04-28 11:39:27 +01:00
Benjamin S. Leveritt
9a9b424ff2 Add note about co.strings 2025-04-28 11:39:27 +01:00
Benjamin S. Leveritt
dfe6146aa3 Add Vue example 2025-04-28 11:39:27 +01:00
Benjamin S. Leveritt
8b26728914 Refinements 2025-04-28 11:39:27 +01:00
Benjamin S. Leveritt
f5003ac8ec Add more examples 2025-04-28 11:39:26 +01:00
Benjamin S. Leveritt
ee71ba99e2 Adds CoText doc
Closes #1962
2025-04-28 11:39:26 +01:00
Guido D'Orsi
56c7a2dda2 feat: initial commit of the new subscribe 2025-04-26 11:10:04 +02:00
Guido D'Orsi
d28ce598e2 Merge pull request #2003 from garden-co/changeset-release/main
Version Packages
2025-04-26 10:24:45 +02:00
Guido D'Orsi
e050f17945 Merge remote-tracking branch 'origin/main' into feat/new-subscription 2025-04-25 16:09:19 +02:00
github-actions[bot]
14475991c8 Version Packages 2025-04-25 12:39:39 +00:00
Guido D'Orsi
15d9ec4b38 chore: small cleanup on startPeerReconciliation 2025-04-25 14:37:34 +02:00
Guido D'Orsi
f911545ae3 Merge pull request #2006 from garden-co/chore/simplify-coValue-sync
chore: clean up syncCoValue code and remove the peer role
2025-04-25 13:55:00 +02:00
Guido D'Orsi
ad71530cc0 Merge pull request #2005 from garden-co/feat/no-sync-promises
feat(sync): make the incoming messages handling in the sync manager syncronous
2025-04-25 13:54:35 +02:00
Guido D'Orsi
c33c02691f chore: clean up syncCoValue code and remove the peer role 2025-04-25 13:22:02 +02:00
Guido D'Orsi
51c19770a8 feat: remove unused promises in PeerState outgoing queue 2025-04-25 13:08:20 +02:00
Guido D'Orsi
5c2c7d4188 feat(sync): make the incoming messages handling in the sync manager syncronous 2025-04-25 12:36:31 +02:00
Benjamin S. Leveritt
334d27d53d Merge pull request #2002 from garden-co/2001-fix-unused-vars-in-jazz-paper-scissors
Removes unused vars
2025-04-25 08:19:17 +01:00
Margaret Culotta
906932db1e update imports and mocks for twoslash 2025-04-24 12:22:17 -05:00
Margaret Culotta
2033e35a41 Merge branch 'main' into feat/1936-add-request-invites-to-docs 2025-04-24 11:26:09 -05:00
Benjamin S. Leveritt
a5396a42ce Merge pull request #1989 from garden-co/fix-rn-metro-docs
docs(metro): fix RN docs for metro config
2025-04-24 15:31:12 +01:00
Guido D'Orsi
8a60897086 chore: remove ACCESS_FROM debug code 2025-04-24 16:09:49 +02:00
Benjamin S. Leveritt
5cfe38d547 Removes unused vars 2025-04-24 13:43:25 +01:00
Benjamin S. Leveritt
3f7aa34726 Merge pull request #1996 from garden-co/1995-add-ownership-sections-to-all-the-covalue-docs
Add Ownership sections to CoValue docs
2025-04-24 13:11:52 +01:00
Trisha Lim
008750d401 Merge pull request #1997 from garden-co/docs/framework-component
add Framework component to show framework name
2025-04-24 12:27:54 +01:00
Trisha Lim
72708f82ea add Framework component to show framework name 2025-04-24 12:18:24 +01:00
Benjamin S. Leveritt
30f65f1c91 Add Ownership sections to CoValue docs 2025-04-24 12:06:52 +01:00
Meg Culotta
67d55ce0ee update styles to handle mobile devices (#1944)
* update styles to handle mobile devices

* handle twoslash overflow

---------

Co-authored-by: Margaret Culotta <mculotta@Margarets-MacBook-Air.local>
Co-authored-by: Trisha Lim <hello@trishalim.com>
2025-04-24 09:11:36 +01:00
Trisha Lim
e887f37713 Merge pull request #1994 from garden-co/fix/code-copy
fix(docs): exclude twoslash popovers from getting copied
2025-04-24 09:08:55 +01:00
Trisha Lim
82a515d493 fix(docs): exclude twoslash popovers from getting copied 2025-04-24 08:57:00 +01:00
pax-k
bd94012507 chore: changeset 2025-04-23 23:00:22 +03:00
pax-k
e1dbab1517 chore: changeset 2025-04-23 22:58:11 +03:00
pax-k
dafea6039b docs(metro): fix RN docs for metro config 2025-04-23 22:31:50 +03:00
Tobias Lins
ccc5f89ed7 Fix css path 2025-04-23 21:18:22 +02:00
Margaret Culotta
5a2a12ccbb update twoslash imports 2025-04-18 08:10:50 -05:00
Margaret Culotta
d288cb6954 update docs for request invite 2025-04-17 10:18:17 -05:00
Anselm
06db1dd423 Add opengraph images for docs and move where H1s get filtered out of toc 2025-04-11 10:30:59 +01:00
731 changed files with 45439 additions and 17605 deletions

View File

@@ -12,9 +12,14 @@
"cojson-transport-ws",
"jazz-browser",
"jazz-auth-clerk",
"jazz-auth-betterauth",
"jazz-betterauth-client-plugin",
"jazz-betterauth-server-plugin",
"jazz-react-auth-betterauth",
"jazz-browser-media-images",
"jazz-expo",
"jazz-inspector",
"jazz-inspector-element",
"jazz-nodejs",
"jazz-react",
"jazz-react-core",

10
.github/ISSUE_TEMPLATE/docs-request.md vendored Normal file
View File

@@ -0,0 +1,10 @@
---
name: Docs request
about: Allow people to quickly report issues & improvements for the docs
title: 'Docs: '
labels: docs, requested
assignees: bensleveritt
---

View File

@@ -13,7 +13,7 @@ jobs:
continue-on-error: true
strategy:
matrix:
project: ["tests/e2e", "examples/chat", "examples/clerk", "examples/file-share-svelte", "examples/form", "examples/music-player", "examples/pets", "starters/react-passkey-auth"]
project: ["tests/e2e", "examples/chat", "examples/clerk", "examples/betterauth", "examples/file-share-svelte", "examples/form", "examples/music-player", "examples/organization", "examples/pets", "starters/react-passkey-auth"]
steps:
- uses: actions/checkout@v4

2
.gitignore vendored
View File

@@ -29,3 +29,5 @@ test-results
.cursorrules
.windsurfrules
playwright-report

1
examples/betterauth/.env Normal file
View File

@@ -0,0 +1 @@
BETTER_AUTH_SECRET="TEST_SECRET"

47
examples/betterauth/.gitignore vendored Normal file
View File

@@ -0,0 +1,47 @@
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
sqlite.db
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
!.env
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@@ -0,0 +1,82 @@
# betterauth
## 0.1.7
### Patch Changes
- Updated dependencies [3d1027f]
- Updated dependencies [c240eed]
- jazz-tools@0.14.2
- jazz-betterauth-server-plugin@0.14.2
- jazz-inspector@0.14.2
- jazz-react@0.14.2
- jazz-react-auth-betterauth@0.14.2
- jazz-betterauth-client-plugin@0.14.2
## 0.1.6
### Patch Changes
- Updated dependencies [cdfc105]
- jazz-tools@0.14.1
- jazz-betterauth-server-plugin@0.14.1
- jazz-inspector@0.14.1
- jazz-react@0.14.1
- jazz-react-auth-betterauth@0.14.1
- jazz-betterauth-client-plugin@0.14.1
## 0.1.5
### Patch Changes
- Updated dependencies [5835ed1]
- jazz-tools@0.14.0
- jazz-betterauth-server-plugin@0.14.0
- jazz-inspector@0.14.0
- jazz-react@0.14.0
- jazz-react-auth-betterauth@0.14.0
- jazz-betterauth-client-plugin@0.14.0
## 0.1.4
### Patch Changes
- jazz-betterauth-server-plugin@0.13.32
- jazz-react@0.13.32
- jazz-react-auth-betterauth@0.13.32
- jazz-betterauth-client-plugin@0.13.32
## 0.1.3
### Patch Changes
- Updated dependencies [e5b170f]
- jazz-tools@0.13.31
- jazz-betterauth-server-plugin@0.13.31
- jazz-inspector@0.13.31
- jazz-react@0.13.31
- jazz-react-auth-betterauth@0.13.31
- jazz-betterauth-client-plugin@0.13.31
## 0.1.2
### Patch Changes
- jazz-betterauth-server-plugin@0.13.30
- jazz-inspector@0.13.30
- jazz-react@0.13.30
- jazz-react-auth-betterauth@0.13.30
- jazz-tools@0.13.30
- jazz-betterauth-client-plugin@0.13.30
## 0.1.1
### Patch Changes
- Updated dependencies [8e5ff13]
- jazz-inspector@0.13.29
- jazz-betterauth-server-plugin@0.0.1
- jazz-react@0.13.29
- jazz-react-auth-betterauth@0.0.1
- jazz-tools@0.13.29
- jazz-betterauth-client-plugin@0.0.1

View File

@@ -0,0 +1,54 @@
# Better Auth Integration Example
This example demonstrates how to integrate [Better Auth](https://www.better-auth.com/) with Jazz.
## Getting started
To run this example, you may either:
* Clone the Jazz monorepo and run this example from within.
* Create a new Jazz project using this example as a template, and run that new project.
### Setting environment variables
- `NEXT_PUBLIC_AUTH_BASE_URL`: A URL to a Better Auth server. If undefined, the example will self-host a Better Auth server.
- `BETTER_AUTH_SECRET`: The encryption secret used by the self-hosted Better Auth server (required only if `NEXT_PUBLIC_AUTH_BASE_URL` is undefined)
- `GITHUB_CLIENT_ID`: The client ID for the GitHub OAuth provider used by the self-hosted Better Auth server (required only if `NEXT_PUBLIC_AUTH_BASE_URL` is undefined)
- `GITHUB_CLIENT_SECRET`: The client secret for the GitHub OAuth provider used by the self-hosted Better Auth server (required only if `NEXT_PUBLIC_AUTH_BASE_URL` is undefined)
### Using this example as a template
1. Create a new Jazz project, and use this example as a template.
```sh
npx create-jazz-app@latest betterauth-app --example betterauth
```
2. Navigate to the new project and start the development server.
```sh
cd betterauth-app
pnpm install
pnpm dev
```
### Using the monorepo
This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation).
Clone the jazz repository.
```bash
git clone https://github.com/garden-co/jazz.git
```
Install and build dependencies.
```bash
pnpm i && npx turbo build
```
Go to the example directory.
```bash
cd jazz/examples/betterauth/
```
Start the dev server.
```bash
pnpm dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

View File

@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/styles/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}

View File

@@ -0,0 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;

View File

@@ -0,0 +1,51 @@
{
"name": "betterauth",
"version": "0.1.7",
"private": true,
"type": "module",
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint",
"format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"email": "email dev --dir src/components/emails"
},
"dependencies": {
"@icons-pack/react-simple-icons": "^12.8.0",
"@radix-ui/react-label": "^2.1.6",
"@radix-ui/react-slot": "^1.2.2",
"better-auth": "^1.2.4",
"better-sqlite3": "^11.9.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"jazz-betterauth-client-plugin": "workspace:*",
"jazz-betterauth-server-plugin": "workspace:*",
"jazz-inspector": "workspace:*",
"jazz-react": "workspace:*",
"jazz-react-auth-betterauth": "workspace:*",
"jazz-tools": "workspace:*",
"lucide-react": "^0.510.0",
"next": "15.3.2",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"sonner": "^2.0.3",
"tailwind-merge": "^3.3.0",
"tw-animate-css": "^1.2.5"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@playwright/test": "^1.50.1",
"@tailwindcss/postcss": "^4",
"@types/better-sqlite3": "^7.6.12",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"react-email": "^4.0.11",
"tailwindcss": "^4",
"typescript": "^5"
}
}

View File

@@ -0,0 +1,53 @@
import { defineConfig, devices } from "@playwright/test";
import isCI from "is-ci";
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// import dotenv from 'dotenv';
// dotenv.config({ path: path.resolve(__dirname, '.env') });
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: "./tests",
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: isCI,
/* Retry on CI only */
retries: isCI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: isCI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: "html",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: "http://localhost:3000/",
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
permissions: ["clipboard-read", "clipboard-write"],
},
/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
],
/* Run your local dev server before starting the tests */
webServer: [
{
command: "pnpm dev",
url: "http://localhost:3000/",
reuseExistingServer: !isCI,
},
],
});

View File

@@ -0,0 +1,5 @@
const config = {
plugins: ["@tailwindcss/postcss"],
};
export default config;

View File

@@ -0,0 +1,15 @@
<svg
viewBox="0 0 386 146"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<path id="text"
d="M176.725 33.865H188.275V22.7H176.725V33.865ZM164.9 129.4H172.875C182.72 129.4 188.275 123.9 188.275 114.22V43.6H176.725V109.545C176.725 115.65 173.975 118.51 167.925 118.51H164.9V129.4ZM245.298 53.28C241.613 45.47 233.363 41.95 222.748 41.95C208.998 41.95 200.748 48.44 197.888 58.615L208.613 61.915C210.648 55.315 216.368 52.565 222.638 52.565C231.933 52.565 235.673 56.415 236.058 64.61C226.433 65.93 216.643 67.195 209.768 69.23C200.583 72.145 195.743 77.865 195.743 86.83C195.743 96.51 202.673 104.65 215.818 104.65C225.443 104.65 232.318 101.35 237.213 94.365V103H247.388V66.425C247.388 61.475 247.168 57.185 245.298 53.28ZM217.853 95.245C210.483 95.245 207.128 91.34 207.128 86.72C207.128 82.045 210.593 79.515 215.323 77.92C220.328 76.435 226.983 75.5 235.948 74.18C235.893 76.93 235.673 80.725 234.738 83.475C233.418 89.25 227.643 95.245 217.853 95.245ZM251.22 103H301.545V92.715H269.535L303.195 45.47V43.6H254.3V53.885H284.935L251.22 101.185V103ZM304.815 103H355.14V92.715H323.13L356.79 45.47V43.6H307.895V53.885H338.53L304.815 101.185V103Z"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M136.179 44.8277C136.179 44.8277 136.179 44.8277 136.179 44.8276V21.168C117.931 28.5527 97.9854 32.6192 77.0897 32.6192C65.1466 32.6192 53.5138 31.2908 42.331 28.7737V51.4076C42.331 51.4076 42.331 51.4076 42.331 51.4076V81.1508C41.2955 80.4385 40.1568 79.8458 38.9405 79.3915C36.1732 78.358 33.128 78.0876 30.1902 78.6145C27.2524 79.1414 24.5539 80.4419 22.4358 82.3516C20.3178 84.2613 18.8754 86.6944 18.291 89.3433C17.7066 91.9921 18.0066 94.7377 19.1528 97.2329C20.2991 99.728 22.2403 101.861 24.7308 103.361C27.2214 104.862 30.1495 105.662 33.1448 105.662H33.1455C33.6061 105.662 33.8365 105.662 34.0314 105.659C44.5583 105.449 53.042 96.9656 53.2513 86.4386C53.2534 86.3306 53.2544 86.2116 53.2548 86.0486H53.2552V85.7149L53.2552 85.5521V82.0762L53.2552 53.1993C61.0533 54.2324 69.0092 54.7656 77.0897 54.7656C77.6696 54.7656 78.2489 54.7629 78.8276 54.7574V110.696C77.792 109.983 76.6533 109.391 75.437 108.936C72.6697 107.903 69.6246 107.632 66.6867 108.159C63.7489 108.686 61.0504 109.987 58.9323 111.896C56.8143 113.806 55.3719 116.239 54.7875 118.888C54.2032 121.537 54.5031 124.283 55.6494 126.778C56.7956 129.273 58.7368 131.405 61.2273 132.906C63.7179 134.406 66.646 135.207 69.6414 135.207C70.1024 135.207 70.3329 135.207 70.5279 135.203C81.0548 134.994 89.5385 126.51 89.7478 115.983C89.7517 115.788 89.7517 115.558 89.7517 115.097V111.621L89.7517 54.3266C101.962 53.4768 113.837 51.4075 125.255 48.2397V80.9017C124.219 80.1894 123.081 79.5966 121.864 79.1424C119.097 78.1089 116.052 77.8384 113.114 78.3653C110.176 78.8922 107.478 80.1927 105.36 82.1025C103.242 84.0122 101.799 86.4453 101.215 89.0941C100.631 91.743 100.931 94.4886 102.077 96.9837C103.223 99.4789 105.164 101.612 107.655 103.112C110.145 104.612 113.073 105.413 116.069 105.413C116.53 105.413 116.76 105.413 116.955 105.409C127.482 105.2 135.966 96.7164 136.175 86.1895C136.179 85.9945 136.179 85.764 136.179 85.3029V81.8271L136.179 44.8277Z"
fill="#146AFF"
/>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1,12 @@
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 125 125"
fill="none"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M118.179 29.6597V6C99.931 13.3847 79.9854 17.4512 59.0897 17.4512C47.1466 17.4512 35.5138 16.1228 24.331 13.6057V36.2396V65.9828C23.2955 65.2705 22.1568 64.6778 20.9405 64.2235C18.1732 63.19 15.128 62.9196 12.1902 63.4465C9.25242 63.9734 6.55392 65.2739 4.43582 67.1836C2.31782 69.0933 0.875416 71.5264 0.291016 74.1753C-0.293384 76.8241 0.00661504 79.5697 1.15281 82.0649C2.29911 84.56 4.24032 86.693 6.73082 88.193C9.22142 89.694 12.1495 90.494 15.1448 90.494C15.6054 90.494 15.8365 90.494 16.0314 90.491C26.5583 90.281 35.042 81.7976 35.2513 71.2706C35.2534 71.1626 35.2544 71.0436 35.2548 70.8806L35.2552 70.5469V70.3841V66.9082V38.0313C43.0533 39.0644 51.0092 39.5976 59.0897 39.5976C59.6696 39.5976 60.2489 39.5949 60.8276 39.5894V95.528C59.792 94.815 58.6533 94.223 57.437 93.768C54.6697 92.735 51.6246 92.464 48.6867 92.991C45.7489 93.518 43.0504 94.819 40.9323 96.728C38.8143 98.638 37.3719 101.071 36.7875 103.72C36.2032 106.369 36.5031 109.115 37.6494 111.61C38.7956 114.105 40.7368 116.237 43.2273 117.738C45.7179 119.238 48.646 120.039 51.6414 120.039C52.1024 120.039 52.3329 120.039 52.5279 120.035C63.0548 119.826 71.5385 111.342 71.7478 100.815C71.7517 100.62 71.7517 100.39 71.7517 99.929V96.453V39.1586C83.962 38.3088 95.837 36.2395 107.255 33.0717V65.7337C106.219 65.0214 105.081 64.4286 103.864 63.9744C101.097 62.9409 98.052 62.6704 95.114 63.1973C92.176 63.7242 89.478 65.0247 87.36 66.9345C85.242 68.8442 83.799 71.2773 83.215 73.9261C82.631 76.575 82.931 79.3206 84.077 81.8157C85.223 84.3109 87.164 86.444 89.655 87.944C92.145 89.444 95.073 90.245 98.069 90.245C98.53 90.245 98.76 90.245 98.955 90.241C109.482 90.032 117.966 81.5484 118.175 71.0215C118.179 70.8265 118.179 70.596 118.179 70.1349V66.6591V29.6597Z"
fill="#146AFF"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,22 @@
import { Navbar } from "@/components/navbar";
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Jazz Example: Better Auth",
description: "Jazz example application demonstrating Better Auth integration",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<>
<Navbar />
<div className="container mx-auto pt-16 min-h-screen flex flex-col items-center justify-center">
{children}
</div>
</>
);
}

View File

@@ -0,0 +1,113 @@
"use client";
import { Button } from "@/components/ui/button";
import { useAccount } from "jazz-react";
import { Account } from "jazz-tools";
import {
AppWindowMacIcon,
FileTextIcon,
GlobeIcon,
WrenchIcon,
} from "lucide-react";
import Image from "next/image";
export default function Home() {
const { me } = useAccount(Account, { resolve: { profile: {} } });
if (!me) {
return null;
}
return (
<div className="grow flex flex-col items-center justify-center">
<main className="flex flex-col gap-8 row-start-2 grow justify-center">
<Image
src="/jazz-logo.svg"
alt="Jazz logo"
width={180}
height={38}
priority
/>
<p className="text-sm/6 text-center sm:text-left">
Signed in as{" "}
<span className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-mono font-semibold">
{me.profile.name}
</span>{" "}
with id{" "}
<span className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-mono font-semibold">
{me.id}
</span>
</p>
<div className="flex gap-4 items-center flex-row">
<Button asChild size="lg">
<a
href="https://jazz.tools/docs"
target="_blank"
rel="noopener noreferrer"
>
<Image src="/jazz.svg" alt="" width={20} height={20} />
Start building
</a>
</Button>
<Button asChild variant="secondary" size="lg">
<a
href="https://jazz.tools/docs"
target="_blank"
rel="noopener noreferrer"
>
<FileTextIcon className="size-4" />
Read the docs
</a>
</Button>
</div>
</main>
<footer className="flex gap-4 py-8">
<Button asChild variant="ghost">
<a
href="https://jazz.tools/api-reference"
target="_blank"
rel="noopener noreferrer"
>
<FileTextIcon className="size-4" />
API reference
</a>
</Button>
<Button asChild variant="ghost">
<a
href="https://jazz.tools/examples"
target="_blank"
rel="noopener noreferrer"
>
<AppWindowMacIcon className="size-4" />
Examples
</a>
</Button>
<Button asChild variant="ghost">
<a
href="https://jazz.tools/status"
target="_blank"
rel="noopener noreferrer"
>
<GlobeIcon className="size-4" />
Status
</a>
</Button>
<Button asChild variant="ghost">
<a
href="https://jazz.tools/showcase"
target="_blank"
rel="noopener noreferrer"
>
<WrenchIcon className="size-4" />
Built with Jazz
</a>
</Button>
</footer>
</div>
);
}

View File

@@ -0,0 +1,14 @@
import { UserSettings } from "@/components/user-settings";
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Settings | Jazz Example: Better Auth",
};
export default function SettingsPage() {
return (
<div className="max-w-screen-md w-full mx-auto px-4">
<UserSettings />
</div>
);
}

View File

@@ -0,0 +1,10 @@
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";
export const { GET, POST } = (() => {
if (!process.env.NEXT_PUBLIC_AUTH_BASE_URL) {
return toNextJsHandler(auth.handler);
} else {
return { GET: undefined, POST: undefined };
}
})();

View File

@@ -0,0 +1,10 @@
import { ForgotPasswordForm } from "@/components/forgot-password-form";
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Forgot password | Jazz Example: Better Auth",
};
export default function ForgotPasswordPage() {
return <ForgotPasswordForm />;
}

View File

@@ -0,0 +1,23 @@
import Image from "next/image";
import Link from "next/link";
interface Props {
children: React.ReactNode;
}
export default function AuthLayout({ children }: Props) {
return (
<div className="bg-muted flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
<div className="flex w-full max-w-sm flex-col gap-6">
<Link
href="/"
className="flex items-center gap-2 self-center font-medium"
>
<Image src="/jazz.svg" alt="Jazz Logo" width={24} height={24} />
Jazz BetterAuth Demo
</Link>
{children}
</div>
</div>
);
}

View File

@@ -0,0 +1,10 @@
import { ResetPasswordForm } from "@/components/reset-password-form";
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Reset password | Jazz Example: Better Auth",
};
export default function ResetPasswordPage() {
return <ResetPasswordForm />;
}

View File

@@ -0,0 +1,11 @@
import { SigninForm } from "@/components/signin-form";
import type { Metadata } from "next";
import { ssoProviders } from "../sso-providers";
export const metadata: Metadata = {
title: "Sign in | Jazz Example: Better Auth",
};
export default function LoginPage() {
return <SigninForm providers={ssoProviders} />;
}

View File

@@ -0,0 +1,11 @@
import { SignupForm } from "@/components/signup-form";
import type { Metadata } from "next";
import { ssoProviders } from "../sso-providers";
export const metadata: Metadata = {
title: "Sign up | Jazz Example: Better Auth",
};
export default function LoginPage() {
return <SignupForm providers={ssoProviders} />;
}

View File

@@ -0,0 +1,4 @@
import type { SSOProviderType } from "jazz-react-auth-betterauth";
// Fill in the providers you want to use
export const ssoProviders: SSOProviderType[] = ["github"];

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1,123 @@
@import "tailwindcss";
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.145 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.145 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.985 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.396 0.141 25.723);
--destructive-foreground: oklch(0.637 0.237 25.331);
--border: oklch(0.269 0 0);
--input: oklch(0.269 0 0);
--ring: oklch(0.439 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(0.269 0 0);
--sidebar-ring: oklch(0.439 0 0);
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}

View File

@@ -0,0 +1,26 @@
import { JazzAndAuth } from "@/components/JazzAndAuth";
import { Toaster } from "@/components/ui/sonner";
import type { Metadata } from "next";
import "./globals.css";
export const metadata: Metadata = {
title: "Jazz Example: Better Auth",
description: "Jazz example application demonstrating Better Auth integration",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className="antialiased">
<JazzAndAuth>
{children}
<Toaster richColors />
</JazzAndAuth>
</body>
</html>
);
}

View File

@@ -0,0 +1,33 @@
"use client";
import { JazzProvider } from "jazz-react";
import { AuthProvider } from "jazz-react-auth-betterauth";
import { type ReactNode, lazy } from "react";
const JazzDevTools =
process.env.NODE_ENV === "production"
? () => null
: lazy(() =>
import("jazz-inspector").then((res) => ({
default: res.JazzInspector,
})),
);
export function JazzAndAuth({ children }: { children: ReactNode }) {
return (
<JazzProvider
sync={{
peer: "wss://cloud.jazz.tools/?key=betterauth-example@garden.co",
}}
>
<AuthProvider
options={{
baseURL: process.env.NEXT_PUBLIC_AUTH_BASE_URL,
}}
>
{children}
</AuthProvider>
<JazzDevTools />
</JazzProvider>
);
}

View File

@@ -0,0 +1,137 @@
import { Button } from "@/components/ui/button";
import {
SiApple,
SiDiscord,
SiDropbox,
SiFacebook,
SiGithub,
SiGitlab,
SiGoogle,
SiKick,
SiReddit,
SiRoblox,
SiSpotify,
SiTiktok,
SiTwitch,
SiVk,
SiX,
SiZoom,
} from "@icons-pack/react-simple-icons";
import { type SSOProviderType, useAuth } from "jazz-react-auth-betterauth";
import { useRouter } from "next/navigation";
import type { ReactNode } from "react";
import { toast } from "sonner";
interface SocialProvider {
name: string;
icon?: ReactNode;
}
const socialProviderMap: Record<SSOProviderType, SocialProvider> = {
github: {
name: "GitHub",
icon: <SiGithub />,
},
google: {
name: "Google",
icon: <SiGoogle />,
},
apple: {
name: "Apple",
icon: <SiApple />,
},
discord: {
name: "Discord",
icon: <SiDiscord />,
},
facebook: {
name: "Facebook",
icon: <SiFacebook />,
},
microsoft: {
name: "Microsoft",
},
twitter: {
name: "X",
icon: <SiX />,
},
dropbox: {
name: "Dropbox",
icon: <SiDropbox />,
},
linkedin: {
name: "LinkedIn",
},
gitlab: {
name: "GitLab",
icon: <SiGitlab />,
},
kick: {
name: "Kick",
icon: <SiKick />,
},
tiktok: {
name: "TikTok",
icon: <SiTiktok />,
},
twitch: {
name: "Twitch",
icon: <SiTwitch />,
},
vk: {
name: "VK",
icon: <SiVk />,
},
zoom: {
name: "Zoom",
icon: <SiZoom />,
},
roblox: {
name: "Roblox",
icon: <SiRoblox />,
},
reddit: {
name: "Reddit",
icon: <SiReddit />,
},
spotify: {
name: "Spotify",
icon: <SiSpotify />,
},
};
interface Props {
provider: SSOProviderType;
}
export function SSOButton({ provider }: Props) {
const auth = useAuth();
const router = useRouter();
return (
<Button
type="button"
variant="outline"
onClick={() => {
auth.authClient.signIn.social(
{
provider,
},
{
onSuccess: () => {
router.push("/");
},
onError: (error) => {
toast.error("Error", {
description: error.error.message,
});
},
},
);
}}
>
{socialProviderMap[provider].icon}
Continue with {socialProviderMap[provider].name}
</Button>
);
}

View File

@@ -0,0 +1,85 @@
"use client";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useAuth } from "jazz-react-auth-betterauth";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { toast } from "sonner";
export function ForgotPasswordForm() {
const router = useRouter();
const [email, setEmail] = useState("");
const auth = useAuth();
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
auth.authClient.forgetPassword(
{ email, redirectTo: `${window.location.origin}/auth/reset-password` },
{
onSuccess: () => {
toast.success("Email sent");
},
onError: (error) => {
toast.error("Error", {
description: error.error.message,
});
},
},
);
};
return (
<div className="flex flex-col gap-6">
<Card>
<CardHeader className="text-center">
<CardTitle className="text-xl">Recover password</CardTitle>
<CardDescription>
Enter your email to receive a link to reset your password
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit}>
<div className="grid gap-6">
<div className="grid gap-6">
<div className="grid gap-3">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="you@example.com"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<Button type="submit" className="w-full">
Recover password
</Button>
</div>
<div className="text-center text-sm">
Back to{" "}
<Link
href="/auth/sign-in"
className="underline underline-offset-4"
>
Sign in
</Link>
</div>
</div>
</form>
</CardContent>
</Card>
</div>
);
}

View File

@@ -0,0 +1,55 @@
"use client";
import { Button } from "@/components/ui/button";
import { useAccount, useIsAuthenticated } from "jazz-react";
import { useAuth } from "jazz-react-auth-betterauth";
import { Account } from "jazz-tools";
import Image from "next/image";
import Link from "next/link";
import { useCallback } from "react";
export function Navbar() {
const { authClient } = useAuth();
const { logOut } = useAccount(Account, { resolve: { profile: {} } });
const isAuthenticated = useIsAuthenticated();
const signOut = useCallback(() => {
authClient.signOut().catch(console.error).finally(logOut);
}, [logOut, authClient]);
return (
<header className="absolute p-4 top-0 left-0 w-full z-10 flex justify-between">
<nav className="flex gap-4">
<Link href="/">
<Image
src="/jazz-logo.svg"
alt="Jazz logo"
width={96}
height={96}
priority
/>
</Link>
{isAuthenticated && (
<Button asChild variant="link">
<Link href="/settings">Settings</Link>
</Button>
)}
</nav>
<nav className="flex gap-4">
{isAuthenticated ? (
<Button onClick={signOut}>Sign out</Button>
) : (
<>
<Button asChild variant="secondary">
<Link href="/auth/sign-in">Sign in</Link>
</Button>
<Button asChild>
<Link href="/auth/sign-up">Sign up</Link>
</Button>
</>
)}
</nav>
</header>
);
}

View File

@@ -0,0 +1,108 @@
"use client";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useAuth } from "jazz-react-auth-betterauth";
import Link from "next/link";
import { useRouter, useSearchParams } from "next/navigation";
import { useState } from "react";
import { toast } from "sonner";
export function ResetPasswordForm() {
const router = useRouter();
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const auth = useAuth();
const searchParams = useSearchParams();
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const token = searchParams.get("token");
if (password !== confirmPassword) {
toast.error("Passwords do not match");
return;
}
if (!token) {
toast.error("Invalid token");
return;
}
auth.authClient.resetPassword(
{ newPassword: password, token },
{
onSuccess: () => {
toast.success("Password successfully updated");
router.push("/auth/sign-in");
},
onError: (error) => {
toast.error("Error", {
description: error.error.message,
});
},
},
);
};
return (
<div className="flex flex-col gap-6">
<Card>
<CardHeader className="text-center">
<CardTitle className="text-xl">Reset password</CardTitle>
<CardDescription>Enter your new password</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit}>
<div className="grid gap-6">
<div className="grid gap-6">
<div className="grid gap-3">
<Label htmlFor="password">New password</Label>
<Input
id="password"
type="password"
required
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<div className="grid gap-3">
<Label htmlFor="confirmPassword">Confirm password</Label>
<Input
id="confirmPassword"
type="password"
required
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
</div>
<Button type="submit" className="w-full">
Set password
</Button>
</div>
<div className="text-center text-sm">
Back to{" "}
<Link
href="/auth/sign-in"
className="underline underline-offset-4"
>
Sign in
</Link>
</div>
</div>
</form>
</CardContent>
</Card>
</div>
);
}

View File

@@ -0,0 +1,124 @@
"use client";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { type SSOProviderType, useAuth } from "jazz-react-auth-betterauth";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { toast } from "sonner";
import { SSOButton } from "./SSOButton";
interface Props {
providers: SSOProviderType[];
}
export function SigninForm({ providers }: Props) {
const router = useRouter();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const auth = useAuth();
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
await auth.authClient.signIn.email(
{ email, password },
{
onSuccess: async () => {
await auth.logIn();
router.push("/");
},
onError: (error) => {
toast.error("Error", {
description: error.error.message,
});
},
},
);
};
return (
<div className="flex flex-col gap-6">
<Card>
<CardHeader className="text-center">
<CardTitle className="text-xl">Welcome back</CardTitle>
<CardDescription>
Sign in with one of the following providers
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit}>
<div className="grid gap-6">
{providers.length > 0 && (
<>
<div className="flex flex-col gap-4">
{providers?.map((provider) => (
<SSOButton key={provider} provider={provider} />
))}
</div>
<div className="after:border-border relative text-center text-sm after:absolute after:inset-0 after:top-1/2 after:z-0 after:flex after:items-center after:border-t">
<span className="bg-card text-muted-foreground relative z-10 px-2">
Or continue with
</span>
</div>
</>
)}
<div className="grid gap-6">
<div className="grid gap-3">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="you@example.com"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div className="grid gap-3">
<div className="flex items-center justify-between">
<Label htmlFor="password">Password</Label>
<Link
href="/auth/forgot-password"
className="ml-auto inline-block text-sm underline-offset-4 hover:underline"
>
Forgot your password?
</Link>
</div>
<Input
id="password"
type="password"
required
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<Button type="submit" className="w-full">
Sign in
</Button>
</div>
<div className="text-center text-sm">
Don&apos;t have an account?{" "}
<Link
href="/auth/sign-up"
className="underline underline-offset-4"
>
Sign up
</Link>
</div>
</div>
</form>
</CardContent>
</Card>
</div>
);
}

View File

@@ -0,0 +1,151 @@
"use client";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { type SSOProviderType, useAuth } from "jazz-react-auth-betterauth";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { toast } from "sonner";
import { SSOButton } from "./SSOButton";
interface Props {
providers: SSOProviderType[];
}
export function SignupForm({ providers }: Props) {
const router = useRouter();
const auth = useAuth();
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (password !== confirmPassword) {
toast.error("Error", {
description: "Passwords do not match",
});
return;
}
await auth.authClient.signUp.email(
{
email,
password,
name,
},
{
onSuccess: async () => {
await auth.signIn();
router.push("/");
},
onError: (error) => {
toast.error("Sign up error", {
description: error.error.message,
});
},
},
);
};
return (
<div className="flex flex-col gap-6">
<Card>
<CardHeader className="text-center">
<CardTitle className="text-xl">Welcome</CardTitle>
<CardDescription>
Sign up with one of the following providers
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit}>
<div className="grid gap-6">
{providers.length > 0 && (
<>
<div className="flex flex-col gap-4">
{providers?.map((provider) => (
<SSOButton key={provider} provider={provider} />
))}
</div>
<div className="after:border-border relative text-center text-sm after:absolute after:inset-0 after:top-1/2 after:z-0 after:flex after:items-center after:border-t">
<span className="bg-card text-muted-foreground relative z-10 px-2">
Or continue with
</span>
</div>
</>
)}
<div className="grid gap-6">
<div className="grid gap-3">
<Label htmlFor="name">Name</Label>
<Input
id="name"
type="text"
placeholder="John Doe"
required
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
<div className="grid gap-3">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="you@example.com"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div className="grid gap-3">
<Label htmlFor="password">Password</Label>
<Input
id="password"
type="password"
placeholder="********"
required
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<div className="grid gap-3">
<Label htmlFor="confirmPassword">Confirm password</Label>
<Input
id="confirmPassword"
type="password"
required
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
</div>
<Button type="submit" className="w-full">
Sign up
</Button>
</div>
<div className="text-center text-sm">
Already have an account?{" "}
<Link
href="/auth/sign-in"
className="underline underline-offset-4"
>
Sign in
</Link>
</div>
</div>
</form>
</CardContent>
</Card>
</div>
);
}

View File

@@ -0,0 +1,59 @@
import { Slot } from "@radix-ui/react-slot";
import { type VariantProps, cva } from "class-variance-authority";
import * as React from "react";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
icon: "size-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
function Button({
className,
variant,
size,
asChild = false,
...props
}: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean;
}) {
const Comp = asChild ? Slot : "button";
return (
<Comp
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
);
}
export { Button, buttonVariants };

View File

@@ -0,0 +1,92 @@
import * as React from "react";
import { cn } from "@/lib/utils";
function Card({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card"
className={cn(
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
className,
)}
{...props}
/>
);
}
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-header"
className={cn(
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
className,
)}
{...props}
/>
);
}
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-title"
className={cn("leading-none font-semibold", className)}
{...props}
/>
);
}
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
);
}
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-action"
className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
className,
)}
{...props}
/>
);
}
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-content"
className={cn("px-6", className)}
{...props}
/>
);
}
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-footer"
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
{...props}
/>
);
}
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardAction,
CardDescription,
CardContent,
};

View File

@@ -0,0 +1,21 @@
import * as React from "react";
import { cn } from "@/lib/utils";
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
return (
<input
type={type}
data-slot="input"
className={cn(
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
className,
)}
{...props}
/>
);
}
export { Input };

View File

@@ -0,0 +1,22 @@
import * as LabelPrimitive from "@radix-ui/react-label";
import * as React from "react";
import { cn } from "@/lib/utils";
function Label({
className,
...props
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
return (
<LabelPrimitive.Root
data-slot="label"
className={cn(
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
className,
)}
{...props}
/>
);
}
export { Label };

View File

@@ -0,0 +1,22 @@
"use client";
import { Toaster as Sonner, type ToasterProps } from "sonner";
const Toaster = ({ ...props }: ToasterProps) => {
return (
<Sonner
theme="light"
className="toaster group"
style={
{
"--normal-bg": "var(--popover)",
"--normal-text": "var(--popover-foreground)",
"--normal-border": "var(--border)",
} as React.CSSProperties
}
{...props}
/>
);
};
export { Toaster };

View File

@@ -0,0 +1,60 @@
"use client";
import { Button } from "@/components/ui/button";
import { useAccount, useIsAuthenticated } from "jazz-react";
import { useAuth } from "jazz-react-auth-betterauth";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { toast } from "sonner";
export function UserSettings() {
const router = useRouter();
const { authClient } = useAuth();
const { logOut } = useAccount();
const isAuthenticated = useIsAuthenticated();
if (!isAuthenticated) {
return (
<div className="flex flex-col gap-8">
<h1 className="text-center text-2xl">Forbidden</h1>
<p className="text-center text-sm text-muted-foreground">
Please{" "}
<Link href="/auth/sign-in" className="underline text-primary">
sign in
</Link>{" "}
to access this page.
</p>
</div>
);
}
return (
<>
<div className="flex flex-col gap-8">
<h1 className="text-4xl font-semibold">Settings</h1>
<Button
variant="destructive"
className="relative"
type="button"
onClick={async (e) => {
e.preventDefault();
authClient.deleteUser(undefined, {
onSuccess: () => {
logOut();
router.push("/");
},
onError: (error) => {
toast.error("Error", {
description: error.error.message,
});
},
});
}}
>
Delete account
</Button>
</div>
</>
);
}

View File

@@ -0,0 +1,50 @@
import { betterAuth } from "better-auth";
import { getMigrations } from "better-auth/db";
import Database from "better-sqlite3";
import { jazzPlugin } from "jazz-betterauth-server-plugin";
import { socialProviders } from "./socialProviders";
export const auth = await (async () => {
// Configure Better Auth server
const auth = betterAuth({
appName: "Jazz Example: Better Auth",
database: new Database("sqlite.db"),
emailAndPassword: {
enabled: true,
async sendResetPassword({ url }) {
// Here we can send an email to the user with the reset password link
console.log("****** RESET PASSWORD ******");
console.log("navigate to", url, "to reset your password");
console.log("******");
},
},
emailVerification: {
async sendVerificationEmail(data) {
console.error("Not implemented");
},
},
socialProviders,
user: {
deleteUser: {
enabled: true,
},
},
plugins: [jazzPlugin()],
databaseHooks: {
user: {
create: {
async after(user) {
// Here we can send a welcome email to the user
console.error("Not implemented");
},
},
},
},
});
// Run database migrations
const migrations = await getMigrations(auth.options);
await migrations.runMigrations();
return auth;
})();

View File

@@ -0,0 +1,186 @@
const apple =
process.env.APPLE_CLIENT_ID &&
process.env.APPLE_CLIENT_SECRET &&
process.env.APPLE_APP_BUNDLE_IDENTIFIER
? {
apple: {
clientId: process.env.APPLE_CLIENT_ID as string,
clientSecret: process.env.APPLE_CLIENT_SECRET as string,
// For native iOS
appBundleIdentifier: process.env
.APPLE_APP_BUNDLE_IDENTIFIER as string,
},
}
: undefined;
const discord =
process.env.DISCORD_CLIENT_ID && process.env.DISCORD_CLIENT_SECRET
? {
discord: {
clientId: process.env.DISCORD_CLIENT_ID as string,
clientSecret: process.env.DISCORD_CLIENT_SECRET as string,
},
}
: undefined;
const facebook =
process.env.FACEBOOK_CLIENT_ID && process.env.FACEBOOK_CLIENT_SECRET
? {
facebook: {
clientId: process.env.FACEBOOK_CLIENT_ID as string,
clientSecret: process.env.FACEBOOK_CLIENT_SECRET as string,
},
}
: undefined;
const github =
process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET
? {
github: {
clientId: process.env.GITHUB_CLIENT_ID as string,
clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
},
}
: undefined;
const google =
process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET
? {
google: {
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
},
}
: undefined;
const kick =
process.env.KICK_CLIENT_ID && process.env.KICK_CLIENT_SECRET
? {
kick: {
clientId: process.env.KICK_CLIENT_ID as string,
clientSecret: process.env.KICK_CLIENT_SECRET as string,
},
}
: undefined;
const microsoft =
process.env.MICROSOFT_CLIENT_ID && process.env.MICROSOFT_CLIENT_SECRET
? {
microsoft: {
clientId: process.env.MICROSOFT_CLIENT_ID as string,
clientSecret: process.env.MICROSOFT_CLIENT_SECRET as string,
tenantId: "common",
requireSelectAccount: true,
},
}
: undefined;
const tiktok =
process.env.TIKTOK_CLIENT_ID &&
process.env.TIKTOK_CLIENT_SECRET &&
process.env.TIKTOK_CLIENT_KEY
? {
tiktok: {
clientId: process.env.TIKTOK_CLIENT_ID,
clientSecret: process.env.TIKTOK_CLIENT_SECRET,
clientKey: process.env.TIKTOK_CLIENT_KEY,
},
}
: undefined;
const twitch =
process.env.TWITCH_CLIENT_ID && process.env.TWITCH_CLIENT_SECRET
? {
twitch: {
clientId: process.env.TWITCH_CLIENT_ID as string,
clientSecret: process.env.TWITCH_CLIENT_SECRET as string,
},
}
: undefined;
const twitter =
process.env.TWITTER_CLIENT_ID && process.env.TWITTER_CLIENT_SECRET
? {
twitter: {
clientId: process.env.TWITTER_CLIENT_ID,
clientSecret: process.env.TWITTER_CLIENT_SECRET,
},
}
: undefined;
const dropbox =
process.env.DROPBOX_CLIENT_ID && process.env.DROPBOX_CLIENT_SECRET
? {
dropbox: {
clientId: process.env.DROPBOX_CLIENT_ID as string,
clientSecret: process.env.DROPBOX_CLIENT_SECRET as string,
},
}
: undefined;
const linkedin =
process.env.LINKEDIN_CLIENT_ID && process.env.LINKEDIN_CLIENT_SECRET
? {
linkedin: {
clientId: process.env.LINKEDIN_CLIENT_ID as string,
clientSecret: process.env.LINKEDIN_CLIENT_SECRET as string,
},
}
: undefined;
const gitlab =
process.env.GITLAB_CLIENT_ID &&
process.env.GITLAB_CLIENT_SECRET &&
process.env.GITLAB_ISSUER
? {
gitlab: {
clientId: process.env.GITLAB_CLIENT_ID as string,
clientSecret: process.env.GITLAB_CLIENT_SECRET as string,
issuer: process.env.GITLAB_ISSUER as string,
},
}
: undefined;
const reddit =
process.env.REDDIT_CLIENT_ID && process.env.REDDIT_CLIENT_SECRET
? {
reddit: {
clientId: process.env.REDDIT_CLIENT_ID as string,
clientSecret: process.env.REDDIT_CLIENT_SECRET as string,
},
}
: undefined;
const roblox =
process.env.ROBLOX_CLIENT_ID && process.env.ROBLOX_CLIENT_SECRET
? {
roblox: {
clientId: process.env.ROBLOX_CLIENT_ID,
clientSecret: process.env.ROBLOX_CLIENT_SECRET,
},
}
: undefined;
const spotify =
process.env.SPOTIFY_CLIENT_ID && process.env.SPOTIFY_CLIENT_SECRET
? {
spotify: {
clientId: process.env.SPOTIFY_CLIENT_ID as string,
clientSecret: process.env.SPOTIFY_CLIENT_SECRET as string,
},
}
: undefined;
const vk =
process.env.VK_CLIENT_ID && process.env.VK_CLIENT_SECRET
? {
vk: {
clientId: process.env.VK_CLIENT_ID as string,
clientSecret: process.env.VK_CLIENT_SECRET as string,
},
}
: undefined;
export const socialProviders = {
...(apple && { ...apple }),
...(discord && { ...discord }),
...(facebook && { ...facebook }),
...(github && { ...github }),
...(google && { ...google }),
...(kick && { ...kick }),
...(microsoft && { ...microsoft }),
...(tiktok && { ...tiktok }),
...(twitch && { ...twitch }),
...(twitter && { ...twitter }),
...(dropbox && { ...dropbox }),
...(linkedin && { ...linkedin }),
...(gitlab && { ...gitlab }),
...(reddit && { ...reddit }),
...(roblox && { ...roblox }),
...(spotify && { ...spotify }),
...(vk && { ...vk }),
};

View File

@@ -0,0 +1,6 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

View File

@@ -0,0 +1,28 @@
import { randomBytes } from "node:crypto";
import { test } from "@playwright/test";
import { HomePage } from "./pages/HomePage";
test("should sign up, sign in, and logout", async ({ page }) => {
const username = randomBytes(4).toString("hex");
const email = `${username}@example.com`;
const password = randomBytes(8).toString("hex");
// Sign up
await page.goto("/");
const homePage = new HomePage(page);
await homePage.expectLoggedOut();
await homePage.signUpLink.click();
await homePage.signUpEmail(username, email, password);
await homePage.expectLoggedIn(username);
// Log out & sign in
await homePage.logout();
await homePage.expectLoggedOut();
await homePage.signInLink.click();
await homePage.signInEmail(email, password);
await homePage.expectLoggedIn(username);
// Logout
await homePage.logout();
await homePage.expectLoggedOut();
});

View File

@@ -0,0 +1,76 @@
import { type Page, expect } from "@playwright/test";
export class HomePage {
constructor(public page: Page) {}
usernameInput = this.page.getByRole("textbox", {
name: "Name",
exact: true,
});
emailInput = this.page.getByRole("textbox", {
name: "Email",
exact: true,
});
passwordInput = this.page.getByRole("textbox", {
name: "Password",
exact: true,
});
confirmPasswordInput = this.page.getByRole("textbox", {
name: "Confirm password",
exact: true,
});
signUpButton = this.page.getByRole("button", {
name: "Sign up",
exact: true,
});
signInButton = this.page.getByRole("button", {
name: "Sign in",
exact: true,
});
signUpLink = this.page.getByRole("link", {
name: "Sign up",
exact: true,
});
signInLink = this.page.getByRole("link", {
name: "Sign in",
exact: true,
});
logoutButton = this.page.getByRole("button", {
name: "Sign out",
exact: true,
});
async signUpEmail(name: string, email: string, password: string) {
await this.usernameInput.fill(name);
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.confirmPasswordInput.fill(password);
await this.signUpButton.click();
}
async signInEmail(email: string, password: string) {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.signInButton.click();
}
async logout() {
await this.logoutButton.click();
}
async expectLoggedIn(name?: string) {
await expect(this.logoutButton).toBeVisible();
await expect(this.signInLink).not.toBeVisible();
await expect(this.signUpLink).not.toBeVisible();
if (name) {
await expect(this.page.getByText(`Signed in as ${name}`)).toBeVisible();
}
}
async expectLoggedOut() {
await expect(this.logoutButton).not.toBeVisible();
await expect(this.signInLink).toBeVisible();
await expect(this.signUpLink).toBeVisible();
await expect(this.page.getByText(`Anonymous user`)).toBeVisible();
}
}

View File

@@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

View File

@@ -1,5 +1,180 @@
# chat-rn-expo-clerk
## 1.0.126
### Patch Changes
- Updated dependencies [3d1027f]
- Updated dependencies [c240eed]
- jazz-tools@0.14.2
- jazz-expo@0.14.2
- jazz-react-native-media-images@0.14.2
## 1.0.125
### Patch Changes
- Updated dependencies [cdfc105]
- jazz-tools@0.14.1
- jazz-expo@0.14.1
- jazz-react-native-media-images@0.14.1
## 1.0.124
### Patch Changes
- Updated dependencies [5835ed1]
- jazz-tools@0.14.0
- jazz-expo@0.14.0
- jazz-react-native-media-images@0.14.0
## 1.0.123
### Patch Changes
- jazz-expo@0.13.32
## 1.0.122
### Patch Changes
- Updated dependencies [e5b170f]
- jazz-tools@0.13.31
- jazz-expo@0.13.31
- jazz-react-native-media-images@0.13.31
## 1.0.121
### Patch Changes
- jazz-expo@0.13.30
- jazz-tools@0.13.30
- jazz-react-native-media-images@0.13.30
## 1.0.120
### Patch Changes
- jazz-expo@0.13.29
- jazz-tools@0.13.29
- jazz-react-native-media-images@0.13.29
## 1.0.119
### Patch Changes
- jazz-expo@0.13.28
- jazz-tools@0.13.28
- jazz-react-native-media-images@0.13.28
## 1.0.118
### Patch Changes
- jazz-expo@0.13.27
- jazz-tools@0.13.27
- jazz-react-native-media-images@0.13.27
## 1.0.117
### Patch Changes
- Updated dependencies [ff846d9]
- jazz-tools@0.13.26
- jazz-expo@0.13.26
- jazz-react-native-media-images@0.13.26
## 1.0.116
### Patch Changes
- jazz-expo@0.13.25
- jazz-tools@0.13.25
- jazz-react-native-media-images@0.13.25
## 1.0.115
### Patch Changes
- Updated dependencies [02a240c]
- jazz-tools@0.13.23
- jazz-expo@0.13.23
- jazz-react-native-media-images@0.13.23
## 1.0.114
### Patch Changes
- jazz-expo@0.13.22
## 1.0.113
### Patch Changes
- jazz-expo@0.13.21
- jazz-tools@0.13.21
- jazz-react-native-media-images@0.13.21
## 1.0.112
### Patch Changes
- Updated dependencies [439f0fe]
- jazz-tools@0.13.20
- jazz-expo@0.13.20
- jazz-react-native-media-images@0.13.20
## 1.0.111
### Patch Changes
- Updated dependencies [80530a4]
- jazz-tools@0.13.19
- jazz-expo@0.13.19
- jazz-react-native-media-images@0.13.19
## 1.0.110
### Patch Changes
- Updated dependencies [761759c]
- jazz-tools@0.13.18
- jazz-expo@0.13.18
- jazz-react-native-media-images@0.13.18
## 1.0.109
### Patch Changes
- jazz-expo@0.13.17
- jazz-tools@0.13.17
- jazz-react-native-media-images@0.13.17
## 1.0.108
### Patch Changes
- jazz-expo@0.13.16
- jazz-tools@0.13.16
- jazz-react-native-media-images@0.13.16
## 1.0.107
### Patch Changes
- jazz-expo@0.13.15
- jazz-tools@0.13.15
- jazz-react-native-media-images@0.13.15
## 1.0.106
### Patch Changes
- Updated dependencies [bd94012]
- jazz-expo@0.13.14
- jazz-tools@0.13.14
- jazz-react-native-media-images@0.13.14
## 1.0.105
### Patch Changes

View File

@@ -7,7 +7,7 @@ import { useLocalSearchParams } from "expo-router";
import { useAccount, useCoState } from "jazz-expo";
import { ProgressiveImg } from "jazz-expo";
import { createImage } from "jazz-react-native-media-images";
import { Group, ID } from "jazz-tools";
import { CoPlainText, Group, Loaded } from "jazz-tools";
import { useEffect, useLayoutEffect, useState } from "react";
import React, {
SafeAreaView,
@@ -26,7 +26,7 @@ import React, {
export default function Conversation() {
const { chatId } = useLocalSearchParams();
const { me } = useAccount();
const [chat, setChat] = useState<Chat>();
const [chat, setChat] = useState<Loaded<typeof Chat>>();
const [message, setMessage] = useState("");
const loadedChat = useCoState(Chat, chat?.id, { resolve: { $each: true } });
const navigation = useNavigation();
@@ -37,7 +37,7 @@ export default function Conversation() {
if (chatId === "new") {
createChat();
} else {
loadChat(chatId as ID<Chat>);
loadChat(chatId as string);
}
}, [chat]);
@@ -69,10 +69,10 @@ export default function Conversation() {
setChat(chat);
};
const loadChat = async (chatId: ID<Chat>) => {
const loadChat = async (chatId: string) => {
try {
const chat = await Chat.load(chatId, me);
setChat(chat);
const chat = await Chat.load(chatId);
if (chat) setChat(chat);
} catch (error) {
console.log("Error loading chat", error);
Alert.alert("Error", `Error loading chat: ${error}`);
@@ -82,7 +82,12 @@ export default function Conversation() {
const sendMessage = () => {
if (!chat) return;
if (message.trim()) {
chat.push(Message.create({ text: message }, { owner: chat._owner }));
chat.push(
Message.create(
{ text: CoPlainText.create(message, chat._owner) },
chat._owner,
),
);
setMessage("");
}
};
@@ -104,7 +109,12 @@ export default function Conversation() {
maxSize: 2048,
});
chat.push(Message.create({ text: "", image }, { owner: chat._owner }));
chat.push(
Message.create(
{ text: CoPlainText.create("", chat._owner), image },
chat._owner,
),
);
}
} catch (error) {
Alert.alert("Error", "Failed to upload image");
@@ -113,8 +123,8 @@ export default function Conversation() {
}
};
const renderMessageItem = ({ item }: { item: Message }) => {
const isMe = item._edits.text.by?.isMe;
const renderMessageItem = ({ item }: { item: Loaded<typeof Message> }) => {
const isMe = item._edits.text?.by?.isMe;
return (
<View
className={clsx(
@@ -129,7 +139,7 @@ export default function Conversation() {
isMe ? "text-right" : "text-left",
)}
>
{item._edits.text.by?.profile?.name}
{item._edits.text?.by?.profile?.name}
</Text>
) : null}
<View
@@ -165,8 +175,8 @@ export default function Conversation() {
!isMe ? "mt-2 text-gray-500" : "mt-1 text-gray-200",
)}
>
{item._edits.text.madeAt?.getHours().toString().padStart(2, "0")}:
{item._edits.text.madeAt?.getMinutes().toString().padStart(2, "0")}
{item._edits.text?.madeAt?.getHours().toString().padStart(2, "0")}:
{item._edits.text?.madeAt?.getMinutes().toString().padStart(2, "0")}
</Text>
</View>
</View>

View File

@@ -32,7 +32,7 @@ export default function ChatScreen() {
});
}, [navigation]);
const loadChat = async (chatId: ID<Chat> | "new") => {
const loadChat = async (chatId: string | "new") => {
router.navigate(`/chat/${chatId}`);
};
@@ -49,7 +49,7 @@ export default function ChatScreen() {
text: "Join",
onPress: (chatId) => {
if (chatId) {
loadChat(chatId as ID<Chat>);
loadChat(chatId);
} else {
Alert.alert("Error", "Chat ID cannot be empty.");
}

View File

@@ -1,7 +1,7 @@
{
"name": "chat-rn-expo-clerk",
"main": "index.js",
"version": "1.0.105",
"version": "1.0.126",
"scripts": {
"build": "expo export -p ios",
"start": "expo start",
@@ -18,50 +18,50 @@
"@bam.tech/react-native-image-resizer": "^3.0.11",
"@clerk/clerk-expo": "^2.2.21",
"@craftzdog/react-native-buffer": "6.0.5",
"@expo/vector-icons": "^14.0.2",
"@expo/vector-icons": "^14.1.0",
"@react-native-community/netinfo": "11.4.1",
"@react-navigation/native": "7.0.19",
"@react-navigation/native-stack": "7.2.1",
"clsx": "^2.0.0",
"expo": "^52.0.42",
"expo-build-properties": "~0.13.1",
"expo-clipboard": "~7.0.0",
"expo-constants": "~17.0.8",
"expo-crypto": "~14.0.2",
"expo-dev-client": "~5.0.16",
"expo-file-system": "^18.0.4",
"expo-font": "~13.0.1",
"expo-image-picker": "~16.0.6",
"expo-linking": "~7.0.5",
"expo-router": "~4.0.19",
"expo-secure-store": "~14.0.0",
"expo-splash-screen": "~0.29.22",
"expo-sqlite": "15.1.3",
"expo-status-bar": "~2.0.1",
"expo-web-browser": "~14.0.1",
"expo": "^53.0.8",
"expo-build-properties": "~0.14.6",
"expo-clipboard": "~7.1.4",
"expo-constants": "~17.1.6",
"expo-crypto": "~14.1.4",
"expo-dev-client": "~5.1.8",
"expo-file-system": "^18.1.9",
"expo-font": "~13.3.1",
"expo-image-picker": "~16.1.4",
"expo-linking": "~7.1.4",
"expo-router": "~5.0.6",
"expo-secure-store": "~14.2.3",
"expo-splash-screen": "~0.30.8",
"expo-sqlite": "15.2.9",
"expo-status-bar": "~2.2.3",
"expo-web-browser": "~14.1.6",
"jazz-expo": "workspace:*",
"jazz-react-native-media-images": "workspace:*",
"jazz-tools": "workspace:*",
"nativewind": "^4.1.21",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-native": "0.76.7",
"react-native-gesture-handler": "~2.20.2",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-native": "0.79.2",
"react-native-gesture-handler": "~2.24.0",
"react-native-get-random-values": "^1.11.0",
"react-native-reanimated": "~3.16.3",
"react-native-safe-area-context": "4.12.0",
"react-native-screens": "4.4.0",
"react-native-reanimated": "~3.17.5",
"react-native-safe-area-context": "5.4.0",
"react-native-screens": "4.10.0",
"react-native-url-polyfill": "^2.0.0",
"react-native-web": "~0.19.13",
"react-native-web": "~0.20.0",
"readable-stream": "4.7.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@types/react": "~18.3.12",
"@types/react": "~19.0.14",
"@types/react-test-renderer": "^19.0.0",
"react-test-renderer": "18.3.1",
"tailwindcss": "^3.4.17",
"typescript": "5.6.2"
"typescript": "5.8.3"
},
"private": true
}

View File

@@ -1,8 +1,8 @@
import { CoList, CoMap, ImageDefinition, co } from "jazz-tools";
import { CoList, co, coField, z } from "jazz-tools";
export class Message extends CoMap {
text = co.string;
image = co.optional.ref(ImageDefinition);
}
export const Message = co.map({
text: co.plainText(),
image: z.optional(co.image()),
});
export class Chat extends CoList.Of(co.ref(Message)) {}
export const Chat = co.list(Message);

View File

@@ -1,5 +1,161 @@
# chat-rn-expo
## 1.0.113
### Patch Changes
- Updated dependencies [3d1027f]
- Updated dependencies [c240eed]
- jazz-tools@0.14.2
- jazz-expo@0.14.2
## 1.0.112
### Patch Changes
- Updated dependencies [cdfc105]
- jazz-tools@0.14.1
- jazz-expo@0.14.1
## 1.0.111
### Patch Changes
- Updated dependencies [5835ed1]
- jazz-tools@0.14.0
- jazz-expo@0.14.0
## 1.0.110
### Patch Changes
- jazz-expo@0.13.32
## 1.0.109
### Patch Changes
- Updated dependencies [e5b170f]
- jazz-tools@0.13.31
- jazz-expo@0.13.31
## 1.0.108
### Patch Changes
- jazz-expo@0.13.30
- jazz-tools@0.13.30
## 1.0.107
### Patch Changes
- jazz-expo@0.13.29
- jazz-tools@0.13.29
## 1.0.106
### Patch Changes
- jazz-expo@0.13.28
- jazz-tools@0.13.28
## 1.0.105
### Patch Changes
- jazz-expo@0.13.27
- jazz-tools@0.13.27
## 1.0.104
### Patch Changes
- Updated dependencies [ff846d9]
- jazz-tools@0.13.26
- jazz-expo@0.13.26
## 1.0.103
### Patch Changes
- jazz-expo@0.13.25
- jazz-tools@0.13.25
## 1.0.102
### Patch Changes
- Updated dependencies [02a240c]
- jazz-tools@0.13.23
- jazz-expo@0.13.23
## 1.0.101
### Patch Changes
- jazz-expo@0.13.22
## 1.0.100
### Patch Changes
- jazz-expo@0.13.21
- jazz-tools@0.13.21
## 1.0.99
### Patch Changes
- Updated dependencies [439f0fe]
- jazz-tools@0.13.20
- jazz-expo@0.13.20
## 1.0.98
### Patch Changes
- Updated dependencies [80530a4]
- jazz-tools@0.13.19
- jazz-expo@0.13.19
## 1.0.97
### Patch Changes
- Updated dependencies [761759c]
- jazz-tools@0.13.18
- jazz-expo@0.13.18
## 1.0.96
### Patch Changes
- jazz-expo@0.13.17
- jazz-tools@0.13.17
## 1.0.95
### Patch Changes
- jazz-expo@0.13.16
- jazz-tools@0.13.16
## 1.0.94
### Patch Changes
- jazz-expo@0.13.15
- jazz-tools@0.13.15
## 1.0.93
### Patch Changes
- Updated dependencies [bd94012]
- jazz-expo@0.13.14
- jazz-tools@0.13.14
## 1.0.92
### Patch Changes

View File

@@ -0,0 +1,55 @@
const { withBuildProperties } = require("expo-build-properties");
const { withDangerousMod } = require("@expo/config-plugins");
const fs = require("fs/promises");
const path = require("path");
/**
* https://github.com/mrousavy/nitro/issues/422#issuecomment-2545988256
*/
function withCustomIosMod(config) {
// Use expo-build-properties to bump iOS deployment target
config = withBuildProperties(config, { ios: { deploymentTarget: "16.0" } });
// Patch the generated Podfile fallback to ensure platform is always 16.0
config = withDangerousMod(config, [
"ios",
async (modConfig) => {
const podfilePath = path.join(
modConfig.modRequest.platformProjectRoot,
"Podfile",
);
let contents = await fs.readFile(podfilePath, "utf-8");
// Check if the IPHONEOS_DEPLOYMENT_TARGET setting is already present
// We search for the key being assigned, e.g., config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] =
const deploymentTargetSettingExists =
/\.build_settings\s*\[\s*['"]IPHONEOS_DEPLOYMENT_TARGET['"]\s*\]\s*=/.test(
contents,
);
if (!deploymentTargetSettingExists) {
// IPHONEOS_DEPLOYMENT_TARGET setting not found, proceed to add it.
contents = contents.replace(
/(post_install\s+do\s+\|installer\|[\s\S]*?)(\r?\n\s end\s*)$/m,
`$1
# Expo Build Properties: force deployment target
# https://github.com/mrousavy/nitro/issues/422#issuecomment-2545988256
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '16.0'
end
end
$2`,
);
}
await fs.writeFile(podfilePath, contents);
return modConfig;
},
]);
return config;
}
module.exports = ({ config }) => {
return withCustomIosMod(config);
};

View File

@@ -23,7 +23,12 @@
},
"package": "com.jazz.chatrn"
},
"plugins": ["expo-secure-store", "expo-sqlite", "expo-build-properties"],
"plugins": [
"expo-secure-store",
"expo-sqlite",
"expo-build-properties",
"expo-web-browser"
],
"extra": {
"eas": {
"projectId": "e0e61872-1906-4c84-b9d8-9be77355cad0"

View File

@@ -1,9 +1,9 @@
{
"name": "chat-rn-expo",
"version": "1.0.92",
"version": "1.0.113",
"main": "index.js",
"scripts": {
"build": "expo export -p ios",
"build": "tsc --noEmit && expo export -p ios",
"start": "expo start",
"format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write",
@@ -19,33 +19,35 @@
"@react-navigation/native": "7.0.19",
"@react-navigation/native-stack": "7.2.1",
"clsx": "^2.0.0",
"expo": "52.0.42",
"expo-build-properties": "~0.13.1",
"expo-clipboard": "~7.0.0",
"expo-constants": "~17.0.8",
"expo-dev-client": "~5.0.16",
"expo-linking": "~7.0.5",
"expo-secure-store": "~14.0.0",
"expo-sqlite": "15.1.3",
"expo-status-bar": "~2.0.1",
"expo-web-browser": "~14.0.1",
"expo": "53.0.8",
"expo-build-properties": "~0.14.6",
"expo-clipboard": "~7.1.4",
"expo-constants": "~17.1.6",
"expo-dev-client": "~5.1.8",
"expo-linking": "~7.1.4",
"expo-secure-store": "~14.2.3",
"expo-sqlite": "15.2.9",
"expo-status-bar": "~2.2.3",
"expo-web-browser": "~14.1.6",
"jazz-expo": "workspace:*",
"jazz-tools": "workspace:*",
"nativewind": "^4.1.21",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-native": "0.76.7",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-native": "0.79.2",
"react-native-get-random-values": "^1.11.0",
"react-native-safe-area-context": "4.12.0",
"react-native-screens": "4.4.0",
"react-native-safe-area-context": "5.4.0",
"react-native-screens": "4.10.0",
"react-native-nitro-modules": "0.25.2",
"react-native-quick-crypto": "1.0.0-beta.15",
"react-native-url-polyfill": "^2.0.0",
"readable-stream": "4.7.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@types/react": "~18.3.12",
"@types/react": "~19.0.14",
"tailwindcss": "^3.4.17",
"typescript": "5.6.2"
"typescript": "5.8.3"
},
"private": true
}

View File

@@ -6,6 +6,7 @@ import {
} from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import * as Linking from "expo-linking";
import { RNQuickCrypto } from "jazz-expo/crypto";
import React, { StrictMode, useEffect, useState } from "react";
import HandleInviteScreen from "./invite";
@@ -46,6 +47,7 @@ function App() {
return (
<StrictMode>
<JazzProvider
CryptoProvider={RNQuickCrypto}
sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}}

View File

@@ -1,6 +1,6 @@
import clsx from "clsx";
import * as Clipboard from "expo-clipboard";
import { Group, ID, Profile } from "jazz-tools";
import { Group, ID, Loaded, Profile } from "jazz-tools";
import { useEffect, useState } from "react";
import React, {
Button,
@@ -19,7 +19,8 @@ import { Chat, Message } from "./schema";
export default function ChatScreen({ navigation }: { navigation: any }) {
const { me, logOut } = useAccount();
const [chatId, setChatId] = useState<ID<Chat>>();
const [chatId, setChatId] = useState<string>();
const [chatIdInput, setChatIdInput] = useState<string>();
const loadedChat = useCoState(Chat, chatId, { resolve: { $each: true } });
const [message, setMessage] = useState("");
const profile = useCoState(Profile, me._refs.profile?.id, {});
@@ -57,27 +58,11 @@ export default function ChatScreen({ navigation }: { navigation: any }) {
};
const joinChat = () => {
Alert.prompt(
"Join Chat",
"Enter the Chat ID (example: co_zBGEHYvRfGuT2YSBraY3njGjnde)",
[
{
text: "Cancel",
style: "cancel",
},
{
text: "Join",
onPress: (chatId) => {
if (chatId) {
setChatId(chatId as ID<Chat>);
} else {
Alert.alert("Error", "Chat ID cannot be empty.");
}
},
},
],
"plain-text",
);
if (chatIdInput) {
setChatId(chatIdInput);
} else {
Alert.alert("Error", "Chat ID cannot be empty.");
}
};
const sendMessage = () => {
@@ -90,7 +75,7 @@ export default function ChatScreen({ navigation }: { navigation: any }) {
}
};
const renderMessageItem = ({ item }: { item: Message }) => {
const renderMessageItem = ({ item }: { item: Loaded<typeof Message> }) => {
const isMe = item._edits?.text?.by?.isMe;
return (
<View
@@ -160,9 +145,25 @@ export default function ChatScreen({ navigation }: { navigation: any }) {
>
<Text className="text-white font-semibold">Start new chat</Text>
</TouchableOpacity>
<Text className="text-m font-bold mt-6">Join existing chat</Text>
<TextInput
className="rounded h-12 p-2 m-2 mt-4 w-80 border border-gray-200 block"
placeholder="Chat ID"
value={chatIdInput ?? ""}
onChangeText={(value) => {
setChatIdInput(value);
}}
textAlignVertical="center"
onSubmitEditing={() => {
if (chatIdInput) {
setChatId(chatIdInput);
}
}}
testID="chat-id-input"
/>
<TouchableOpacity
onPress={joinChat}
className="bg-green-500 p-4 rounded-md mt-4"
className="bg-green-500 p-4 rounded-md"
>
<Text className="text-white font-semibold">Join chat</Text>
</TouchableOpacity>
@@ -172,7 +173,6 @@ export default function ChatScreen({ navigation }: { navigation: any }) {
<FlatList
contentContainerStyle={{
flexGrow: 1,
flex: 1,
gap: 6,
padding: 8,
}}

View File

@@ -1,7 +1,7 @@
import { CoList, CoMap, co } from "jazz-tools";
import { co, z } from "jazz-tools";
export class Message extends CoMap {
text = co.string;
}
export const Message = co.map({
text: z.string(),
});
export class Chat extends CoList.Of(co.ref(Message)) {}
export const Chat = co.list(Message);

View File

@@ -9,6 +9,8 @@ appId: com.jazz.chatrn
# - tapOn: "Reload"
# login
- assertVisible: "Logout"
- tapOn: "Logout"
- assertVisible: "Anonymous user"
- runFlow:
label: "Erase existing username"
@@ -42,9 +44,11 @@ appId: com.jazz.chatrn
# logout
- tapOn: "Logout"
- assertVisible: "Anonymous user"
# This doesn't work on CI, maybe because Android has a different alert dialog
# - tapOn: "Join chat"
# - inputText: "co_zFs6KFyhxPw4xtw83tcEMzeHUNv" # Use a static id because maestro doesn't have access to the system clipboard
# - pressKey: "enter"
# - assertVisible: "boorad"
# - assertVisible: "bro, low key, it do be like that tho"
# join chat
- tapOn:
id: "chat-id-input"
- inputText: "co_zFs6KFyhxPw4xtw83tcEMzeHUNv" # Use a static id because maestro doesn't have access to the system clipboard
- tapOn: "Join chat"
- assertVisible: "boorad"
- assertVisible: "bro, low key, it do be like that tho"

View File

@@ -1,5 +1,219 @@
# chat-rn
## 1.0.121
### Patch Changes
- Updated dependencies [3d1027f]
- Updated dependencies [c240eed]
- jazz-tools@0.14.2
- jazz-react-native@0.14.2
## 1.0.120
### Patch Changes
- Updated dependencies [c8b33ad]
- Updated dependencies [cdfc105]
- cojson@0.14.1
- jazz-tools@0.14.1
- cojson-transport-ws@0.14.1
- jazz-react-native@0.14.1
## 1.0.119
### Patch Changes
- Updated dependencies [5835ed1]
- Updated dependencies [5835ed1]
- cojson@0.14.0
- jazz-tools@0.14.0
- cojson-transport-ws@0.14.0
- jazz-react-native@0.14.0
## 1.0.118
### Patch Changes
- jazz-react-native@0.13.32
## 1.0.117
### Patch Changes
- Updated dependencies [e5b170f]
- Updated dependencies [d63716a]
- Updated dependencies [d5edad7]
- jazz-tools@0.13.31
- cojson@0.13.31
- jazz-react-native@0.13.31
- cojson-transport-ws@0.13.31
## 1.0.116
### Patch Changes
- Updated dependencies [07dd2c5]
- cojson@0.13.30
- cojson-transport-ws@0.13.30
- jazz-react-native@0.13.30
- jazz-tools@0.13.30
## 1.0.115
### Patch Changes
- Updated dependencies [eef1a5d]
- Updated dependencies [191ae38]
- Updated dependencies [daee7b9]
- cojson@0.13.29
- jazz-react-native@0.13.29
- cojson-transport-ws@0.13.29
- jazz-tools@0.13.29
## 1.0.114
### Patch Changes
- Updated dependencies [e7ccb2c]
- Updated dependencies [422dbc4]
- cojson@0.13.28
- cojson-transport-ws@0.13.28
- jazz-react-native@0.13.28
- jazz-tools@0.13.28
## 1.0.113
### Patch Changes
- Updated dependencies [6357052]
- cojson@0.13.27
- cojson-transport-ws@0.13.27
- jazz-react-native@0.13.27
- jazz-tools@0.13.27
## 1.0.112
### Patch Changes
- Updated dependencies [ff846d9]
- jazz-tools@0.13.26
- jazz-react-native@0.13.26
## 1.0.111
### Patch Changes
- Updated dependencies [a846e07]
- cojson@0.13.25
- cojson-transport-ws@0.13.25
- jazz-react-native@0.13.25
- jazz-tools@0.13.25
## 1.0.110
### Patch Changes
- Updated dependencies [6b781cf]
- Updated dependencies [02a240c]
- cojson@0.13.23
- jazz-tools@0.13.23
- cojson-transport-ws@0.13.23
- jazz-react-native@0.13.23
## 1.0.109
### Patch Changes
- jazz-react-native@0.13.22
## 1.0.108
### Patch Changes
- Updated dependencies [e14e61f]
- cojson@0.13.21
- cojson-transport-ws@0.13.21
- jazz-react-native@0.13.21
- jazz-tools@0.13.21
## 1.0.107
### Patch Changes
- Updated dependencies [adfc9a6]
- Updated dependencies [1389207]
- Updated dependencies [d6e143e]
- Updated dependencies [439f0fe]
- Updated dependencies [3e6229d]
- cojson@0.13.20
- jazz-tools@0.13.20
- jazz-react-native@0.13.20
- cojson-transport-ws@0.13.20
## 1.0.106
### Patch Changes
- Updated dependencies [80530a4]
- jazz-tools@0.13.19
- jazz-react-native@0.13.19
## 1.0.105
### Patch Changes
- Updated dependencies [9089252]
- Updated dependencies [b470f63]
- Updated dependencies [761759c]
- Updated dependencies [66373ba]
- Updated dependencies [f24cad1]
- cojson@0.13.18
- jazz-tools@0.13.18
- cojson-transport-ws@0.13.18
- jazz-react-native@0.13.18
## 1.0.104
### Patch Changes
- Updated dependencies [9fb98e2]
- Updated dependencies [0b89fad]
- cojson@0.13.17
- cojson-transport-ws@0.13.17
- jazz-react-native@0.13.17
- jazz-tools@0.13.17
## 1.0.103
### Patch Changes
- Updated dependencies [c6fb8dc]
- cojson@0.13.16
- cojson-transport-ws@0.13.16
- jazz-react-native@0.13.16
- jazz-tools@0.13.16
## 1.0.102
### Patch Changes
- Updated dependencies [c712ef2]
- cojson@0.13.15
- cojson-transport-ws@0.13.15
- jazz-react-native@0.13.15
- jazz-tools@0.13.15
## 1.0.101
### Patch Changes
- Updated dependencies [5c2c7d4]
- cojson@0.13.14
- cojson-transport-ws@0.13.14
- jazz-react-native@0.13.14
- jazz-tools@0.13.14
## 1.0.100
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "chat-rn",
"version": "1.0.100",
"version": "1.0.121",
"main": "index.js",
"scripts": {
"android": "react-native run-android",
@@ -24,7 +24,7 @@
"jazz-react-native": "workspace:*",
"jazz-tools": "workspace:*",
"react": "18.3.1",
"react-native": "0.76.7",
"react-native": "0.79.2",
"react-native-get-random-values": "^1.11.0",
"react-native-mmkv": "^3.2.0",
"react-native-polyfill-globals": "^3.1.0",

View File

@@ -1,6 +1,6 @@
import Clipboard from "@react-native-clipboard/clipboard";
import { useAccount, useCoState } from "jazz-react-native";
import { Group, ID, Profile } from "jazz-tools";
import { CoPlainText, Group, ID, Loaded, Profile } from "jazz-tools";
import { useEffect, useState } from "react";
import {
Alert,
@@ -18,7 +18,7 @@ import { Chat, Message } from "./schema";
export function ChatScreen({ navigation }: { navigation: any }) {
const { me, logOut } = useAccount();
const [chatId, setChatId] = useState<ID<Chat>>();
const [chatId, setChatId] = useState<string>();
const loadedChat = useCoState(Chat, chatId, { resolve: { $each: true } });
const [message, setMessage] = useState("");
const profile = useCoState(Profile, me._refs.profile?.id, {});
@@ -68,7 +68,7 @@ export function ChatScreen({ navigation }: { navigation: any }) {
text: "Join",
onPress: (chatId) => {
if (chatId) {
setChatId(chatId as ID<Chat>);
setChatId(chatId);
} else {
Alert.alert("Error", "Chat ID cannot be empty.");
}
@@ -83,13 +83,18 @@ export function ChatScreen({ navigation }: { navigation: any }) {
if (!loadedChat) return;
if (message.trim()) {
loadedChat.push(
Message.create({ text: message }, { owner: loadedChat?._owner }),
Message.create(
{ text: CoPlainText.create(message, loadedChat?._owner) },
loadedChat?._owner,
),
);
setMessage("");
}
};
const renderMessageItem = ({ item }: { item: Message }) => {
const renderMessageItem = ({
item,
}: { item: Loaded<typeof Message, { text: true }> }) => {
const isMe = item._edits?.text?.by?.isMe;
return (
<View

View File

@@ -1,7 +1,7 @@
import { CoList, CoMap, co } from "jazz-tools";
import { co } from "jazz-tools";
export class Message extends CoMap {
text = co.string;
}
export const Message = co.map({
text: co.plainText(),
});
export class Chat extends CoList.Of(co.ref(Message)) {}
export const Chat = co.list(Message);

View File

@@ -1,5 +1,174 @@
# chat-vue
## 0.0.104
### Patch Changes
- Updated dependencies [3d1027f]
- Updated dependencies [c240eed]
- jazz-tools@0.14.2
- jazz-browser@0.14.2
- jazz-vue@0.14.2
## 0.0.103
### Patch Changes
- Updated dependencies [cdfc105]
- jazz-tools@0.14.1
- jazz-browser@0.14.1
- jazz-vue@0.14.1
## 0.0.102
### Patch Changes
- Updated dependencies [5835ed1]
- jazz-tools@0.14.0
- jazz-browser@0.14.0
- jazz-vue@0.14.0
## 0.0.101
### Patch Changes
- jazz-browser@0.13.32
- jazz-vue@0.13.32
## 0.0.100
### Patch Changes
- Updated dependencies [e5b170f]
- jazz-tools@0.13.31
- jazz-browser@0.13.31
- jazz-vue@0.13.31
## 0.0.99
### Patch Changes
- jazz-browser@0.13.30
- jazz-tools@0.13.30
- jazz-vue@0.13.30
## 0.0.98
### Patch Changes
- jazz-browser@0.13.29
- jazz-tools@0.13.29
- jazz-vue@0.13.29
## 0.0.97
### Patch Changes
- jazz-browser@0.13.28
- jazz-tools@0.13.28
- jazz-vue@0.13.28
## 0.0.96
### Patch Changes
- jazz-browser@0.13.27
- jazz-tools@0.13.27
- jazz-vue@0.13.27
## 0.0.95
### Patch Changes
- Updated dependencies [ff846d9]
- jazz-tools@0.13.26
- jazz-browser@0.13.26
- jazz-vue@0.13.26
## 0.0.94
### Patch Changes
- jazz-browser@0.13.25
- jazz-tools@0.13.25
- jazz-vue@0.13.25
## 0.0.93
### Patch Changes
- Updated dependencies [02a240c]
- jazz-tools@0.13.23
- jazz-browser@0.13.23
- jazz-vue@0.13.23
## 0.0.92
### Patch Changes
- jazz-browser@0.13.21
- jazz-tools@0.13.21
- jazz-vue@0.13.21
## 0.0.91
### Patch Changes
- Updated dependencies [439f0fe]
- jazz-tools@0.13.20
- jazz-browser@0.13.20
- jazz-vue@0.13.20
## 0.0.90
### Patch Changes
- Updated dependencies [80530a4]
- jazz-tools@0.13.19
- jazz-browser@0.13.19
- jazz-vue@0.13.19
## 0.0.89
### Patch Changes
- Updated dependencies [761759c]
- jazz-tools@0.13.18
- jazz-browser@0.13.18
- jazz-vue@0.13.18
## 0.0.88
### Patch Changes
- jazz-browser@0.13.17
- jazz-tools@0.13.17
- jazz-vue@0.13.17
## 0.0.87
### Patch Changes
- jazz-browser@0.13.16
- jazz-tools@0.13.16
- jazz-vue@0.13.16
## 0.0.86
### Patch Changes
- jazz-browser@0.13.15
- jazz-tools@0.13.15
- jazz-vue@0.13.15
## 0.0.85
### Patch Changes
- jazz-browser@0.13.14
- jazz-tools@0.13.14
- jazz-vue@0.13.14
## 0.0.84
### Patch Changes

View File

@@ -1,11 +1,11 @@
{
"name": "chat-vue",
"version": "0.0.84",
"version": "0.0.104",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build-type-check": "run-p type-check \"build-only {@}\" --",
"build-type-check": "run-p type-check \"build {@}\" --",
"preview": "vite preview",
"build": "vite build",
"type-check": "vue-tsc --build --force",

View File

@@ -1,9 +1,9 @@
<template>
<BubbleContainer :fromMe="lastEdit.by?.isMe">
<BubbleBody>{{ msg.text }}</BubbleBody>
<BubbleInfo :by="lastEdit.by?.profile?.name" :madeAt="lastEdit.madeAt" />
</BubbleContainer>
</template>
<BubbleContainer :fromMe="lastEdit.by?.isMe">
<BubbleBody>{{ msg.text }}</BubbleBody>
<BubbleInfo :by="lastEdit.by?.profile?.name" :madeAt="lastEdit.madeAt" />
</BubbleContainer>
</template>
<script lang="ts">
import { computed, defineComponent } from "vue";

View File

@@ -1,7 +1,7 @@
import { CoList, CoMap, co } from "jazz-tools";
import { CoList, CoMap, CoPlainText, coField } from "jazz-tools";
export class Message extends CoMap {
text = co.string;
text = coField.ref(CoPlainText);
}
export class Chat extends CoList.Of(co.ref(Message)) {}
export class Chat extends CoList.Of(coField.ref(Message)) {}

View File

@@ -25,7 +25,7 @@
</template>
<script lang="ts">
import type { ID } from "jazz-tools";
import { CoPlainText, type ID } from "jazz-tools";
import { useCoState } from "jazz-vue";
import { type PropType, computed, defineComponent, ref } from "vue";
import ChatBody from "../components/ChatBody.vue";
@@ -61,7 +61,12 @@ export default defineComponent({
}
function handleSubmit(text: string) {
chat?.value?.push(Message.create({ text }, { owner: chat.value._owner }));
chat?.value?.push(
Message.create(
{ text: CoPlainText.create(text, chat.value._owner) },
chat.value._owner,
),
);
}
return {

View File

@@ -1,5 +1,175 @@
# jazz-example-chat
## 0.0.202
### Patch Changes
- Updated dependencies [3d1027f]
- Updated dependencies [c240eed]
- jazz-tools@0.14.2
- jazz-inspector@0.14.2
- jazz-react@0.14.2
## 0.0.201
### Patch Changes
- Updated dependencies [cdfc105]
- jazz-tools@0.14.1
- jazz-inspector@0.14.1
- jazz-react@0.14.1
## 0.0.200
### Patch Changes
- Updated dependencies [5835ed1]
- jazz-tools@0.14.0
- jazz-inspector@0.14.0
- jazz-react@0.14.0
## 0.0.199
### Patch Changes
- jazz-react@0.13.32
## 0.0.198
### Patch Changes
- Updated dependencies [e5b170f]
- jazz-tools@0.13.31
- jazz-inspector@0.13.31
- jazz-react@0.13.31
## 0.0.197
### Patch Changes
- jazz-inspector@0.13.30
- jazz-react@0.13.30
- jazz-tools@0.13.30
## 0.0.196
### Patch Changes
- Updated dependencies [8e5ff13]
- jazz-inspector@0.13.29
- jazz-react@0.13.29
- jazz-tools@0.13.29
## 0.0.195
### Patch Changes
- jazz-inspector@0.13.28
- jazz-react@0.13.28
- jazz-tools@0.13.28
## 0.0.194
### Patch Changes
- jazz-inspector@0.13.27
- jazz-react@0.13.27
- jazz-tools@0.13.27
## 0.0.193
### Patch Changes
- Updated dependencies [ff846d9]
- jazz-tools@0.13.26
- jazz-inspector@0.13.26
- jazz-react@0.13.26
## 0.0.192
### Patch Changes
- jazz-inspector@0.13.25
- jazz-react@0.13.25
- jazz-tools@0.13.25
## 0.0.191
### Patch Changes
- Updated dependencies [02a240c]
- jazz-tools@0.13.23
- jazz-inspector@0.13.23
- jazz-react@0.13.23
## 0.0.190
### Patch Changes
- Updated dependencies [7de210f]
- jazz-inspector@0.13.21
- jazz-react@0.13.21
- jazz-tools@0.13.21
## 0.0.189
### Patch Changes
- Updated dependencies [439f0fe]
- jazz-tools@0.13.20
- jazz-inspector@0.13.20
- jazz-react@0.13.20
## 0.0.188
### Patch Changes
- Updated dependencies [80530a4]
- jazz-tools@0.13.19
- jazz-inspector@0.13.19
- jazz-react@0.13.19
## 0.0.187
### Patch Changes
- Updated dependencies [761759c]
- jazz-tools@0.13.18
- jazz-inspector@0.13.18
- jazz-react@0.13.18
## 0.0.186
### Patch Changes
- jazz-inspector@0.13.17
- jazz-react@0.13.17
- jazz-tools@0.13.17
## 0.0.185
### Patch Changes
- jazz-inspector@0.13.16
- jazz-react@0.13.16
- jazz-tools@0.13.16
## 0.0.184
### Patch Changes
- jazz-inspector@0.13.15
- jazz-react@0.13.15
- jazz-tools@0.13.15
## 0.0.183
### Patch Changes
- jazz-inspector@0.13.14
- jazz-react@0.13.14
- jazz-tools@0.13.14
## 0.0.182
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-chat",
"private": true,
"version": "0.0.182",
"version": "0.0.202",
"type": "module",
"scripts": {
"dev": "vite",
@@ -20,7 +20,8 @@
"jazz-tools": "workspace:*",
"lucide-react": "^0.274.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"zod": "4.0.0-beta.20250505T012514"
},
"devDependencies": {
"@playwright/test": "^1.50.1",

View File

@@ -3,7 +3,7 @@ import { getRandomUsername, inIframe, onChatLoad } from "@/util.ts";
import { useIframeHashRouter } from "hash-slash";
import { JazzInspector } from "jazz-inspector";
import { JazzProvider, useAccount } from "jazz-react";
import { Group, ID } from "jazz-tools";
import { Group } from "jazz-tools";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { ChatScreen } from "./chatScreen.tsx";
@@ -43,7 +43,7 @@ export function App() {
</TopBar>
{router.route({
"/": () => createChat() as never,
"/chat/:id": (id) => <ChatScreen chatID={id as ID<Chat>} />,
"/chat/:id": (id) => <ChatScreen chatID={id} />,
})}
</AppContainer>
);

View File

@@ -1,5 +1,5 @@
import { createImage, useAccount, useCoState } from "jazz-react";
import { Account, ID } from "jazz-tools";
import { Account, Loaded, co } from "jazz-tools";
import { useState } from "react";
import { Chat, Message } from "./schema.ts";
import {
@@ -15,8 +15,10 @@ import {
TextInput,
} from "./ui.tsx";
export function ChatScreen(props: { chatID: ID<Chat> }) {
const chat = useCoState(Chat, props.chatID, { resolve: { $each: true } });
export function ChatScreen(props: { chatID: string }) {
const chat = useCoState(Chat, props.chatID, {
resolve: { $each: { text: true } },
});
const account = useAccount();
const [showNLastMessages, setShowNLastMessages] = useState(30);
@@ -36,7 +38,15 @@ export function ChatScreen(props: { chatID: ID<Chat> }) {
}
createImage(file, { owner: chat._owner }).then((image) => {
chat.push(Message.create({ text: file.name, image: image }, chat._owner));
chat.push(
Message.create(
{
text: co.plainText().create(file.name, chat._owner),
image: image,
},
chat._owner,
),
);
});
};
@@ -66,7 +76,12 @@ export function ChatScreen(props: { chatID: ID<Chat> }) {
<TextInput
onSubmit={(text) => {
chat.push(Message.create({ text }, { owner: chat._owner }));
chat.push(
Message.create(
{ text: co.plainText().create(text, chat._owner) },
chat._owner,
),
);
}}
/>
</InputBar>
@@ -74,8 +89,11 @@ export function ChatScreen(props: { chatID: ID<Chat> }) {
);
}
function ChatBubble(props: { me: Account; msg: Message }) {
if (!props.me.canRead(props.msg)) {
function ChatBubble(props: {
me: Account;
msg: Loaded<typeof Message, { text: true }>;
}) {
if (!props.me.canRead(props.msg) || !props.msg.text?.toString()) {
return (
<BubbleContainer fromMe={false}>
<BubbleBody fromMe={false}>
@@ -89,7 +107,7 @@ function ChatBubble(props: { me: Account; msg: Message }) {
}
const lastEdit = props.msg._edits.text;
const fromMe = lastEdit.by?.isMe;
const fromMe = lastEdit?.by?.isMe;
const { text, image } = props.msg;
return (
@@ -98,7 +116,9 @@ function ChatBubble(props: { me: Account; msg: Message }) {
{image && <BubbleImage image={image} />}
<BubbleText text={text} />
</BubbleBody>
<BubbleInfo by={lastEdit.by?.profile?.name} madeAt={lastEdit.madeAt} />
{lastEdit && (
<BubbleInfo by={lastEdit.by?.profile?.name} madeAt={lastEdit.madeAt} />
)}
</BubbleContainer>
);
}

View File

@@ -1,8 +1,8 @@
import { CoList, CoMap, ImageDefinition, co } from "jazz-tools";
import { co, z } from "jazz-tools";
export class Message extends CoMap {
text = co.string;
image = co.optional.ref(ImageDefinition);
}
export const Message = co.map({
text: co.plainText(),
image: z.optional(co.image()),
});
export class Chat extends CoList.Of(co.ref(Message)) {}
export const Chat = co.list(Message);

View File

@@ -1,6 +1,6 @@
import clsx from "clsx";
import { ProgressiveImg } from "jazz-react";
import { ImageDefinition } from "jazz-tools";
import { CoPlainText, ImageDefinition, Loaded } from "jazz-tools";
import { ImageIcon } from "lucide-react";
import { useId, useRef } from "react";
@@ -70,7 +70,10 @@ export function BubbleBody(props: {
);
}
export function BubbleText(props: { text: string; className?: string }) {
export function BubbleText(props: {
text: CoPlainText | string;
className?: string;
}) {
return (
<p className={clsx("px-2 leading-relaxed", props.className)}>
{props.text}
@@ -78,7 +81,7 @@ export function BubbleText(props: { text: string; className?: string }) {
);
}
export function BubbleImage(props: { image: ImageDefinition }) {
export function BubbleImage(props: { image: Loaded<typeof ImageDefinition> }) {
return (
<ProgressiveImg image={props.image}>
{({ src }) => (

View File

@@ -2,8 +2,8 @@
// This is NOT needed to make the chat work
import { Chat } from "@/schema.ts";
export function onChatLoad(chat: Chat) {
import { Loaded } from "jazz-tools";
export function onChatLoad(chat: Loaded<typeof Chat>) {
if (window.parent) {
chat.waitForSync().then(() => {
window.parent.postMessage(

View File

@@ -1,5 +1,174 @@
# minimal-auth-clerk
## 0.0.101
### Patch Changes
- Updated dependencies [3d1027f]
- Updated dependencies [c240eed]
- jazz-tools@0.14.2
- jazz-react@0.14.2
- jazz-react-auth-clerk@0.14.2
## 0.0.100
### Patch Changes
- Updated dependencies [cdfc105]
- jazz-tools@0.14.1
- jazz-react@0.14.1
- jazz-react-auth-clerk@0.14.1
## 0.0.99
### Patch Changes
- Updated dependencies [5835ed1]
- jazz-tools@0.14.0
- jazz-react@0.14.0
- jazz-react-auth-clerk@0.14.0
## 0.0.98
### Patch Changes
- jazz-react@0.13.32
- jazz-react-auth-clerk@0.13.32
## 0.0.97
### Patch Changes
- Updated dependencies [e5b170f]
- jazz-tools@0.13.31
- jazz-react@0.13.31
- jazz-react-auth-clerk@0.13.31
## 0.0.96
### Patch Changes
- jazz-react@0.13.30
- jazz-react-auth-clerk@0.13.30
- jazz-tools@0.13.30
## 0.0.95
### Patch Changes
- jazz-react@0.13.29
- jazz-react-auth-clerk@0.13.29
- jazz-tools@0.13.29
## 0.0.94
### Patch Changes
- jazz-react@0.13.28
- jazz-react-auth-clerk@0.13.28
- jazz-tools@0.13.28
## 0.0.93
### Patch Changes
- jazz-react@0.13.27
- jazz-react-auth-clerk@0.13.27
- jazz-tools@0.13.27
## 0.0.92
### Patch Changes
- Updated dependencies [ff846d9]
- jazz-tools@0.13.26
- jazz-react@0.13.26
- jazz-react-auth-clerk@0.13.26
## 0.0.91
### Patch Changes
- jazz-react@0.13.25
- jazz-react-auth-clerk@0.13.25
- jazz-tools@0.13.25
## 0.0.90
### Patch Changes
- Updated dependencies [02a240c]
- jazz-tools@0.13.23
- jazz-react@0.13.23
- jazz-react-auth-clerk@0.13.23
## 0.0.89
### Patch Changes
- jazz-react@0.13.21
- jazz-react-auth-clerk@0.13.21
- jazz-tools@0.13.21
## 0.0.88
### Patch Changes
- Updated dependencies [439f0fe]
- jazz-tools@0.13.20
- jazz-react@0.13.20
- jazz-react-auth-clerk@0.13.20
## 0.0.87
### Patch Changes
- Updated dependencies [80530a4]
- jazz-tools@0.13.19
- jazz-react@0.13.19
- jazz-react-auth-clerk@0.13.19
## 0.0.86
### Patch Changes
- Updated dependencies [761759c]
- jazz-tools@0.13.18
- jazz-react@0.13.18
- jazz-react-auth-clerk@0.13.18
## 0.0.85
### Patch Changes
- jazz-react@0.13.17
- jazz-react-auth-clerk@0.13.17
- jazz-tools@0.13.17
## 0.0.84
### Patch Changes
- jazz-react@0.13.16
- jazz-react-auth-clerk@0.13.16
- jazz-tools@0.13.16
## 0.0.83
### Patch Changes
- jazz-react@0.13.15
- jazz-react-auth-clerk@0.13.15
- jazz-tools@0.13.15
## 0.0.82
### Patch Changes
- jazz-react@0.13.14
- jazz-react-auth-clerk@0.13.14
- jazz-tools@0.13.14
## 0.0.81
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "clerk",
"private": true,
"version": "0.0.81",
"version": "0.0.101",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,174 @@
# file-share-svelte
## 0.0.85
### Patch Changes
- Updated dependencies [3d1027f]
- Updated dependencies [c240eed]
- jazz-tools@0.14.2
- jazz-inspector-element@0.14.2
- jazz-svelte@0.14.2
## 0.0.84
### Patch Changes
- Updated dependencies [cdfc105]
- jazz-tools@0.14.1
- jazz-svelte@0.14.1
- jazz-inspector-element@0.14.1
## 0.0.83
### Patch Changes
- Updated dependencies [5835ed1]
- jazz-tools@0.14.0
- jazz-svelte@0.14.0
- jazz-inspector-element@0.14.0
## 0.0.82
### Patch Changes
- jazz-svelte@0.13.32
## 0.0.81
### Patch Changes
- Updated dependencies [e5b170f]
- jazz-tools@0.13.31
- jazz-inspector-element@0.13.31
- jazz-svelte@0.13.31
## 0.0.80
### Patch Changes
- jazz-svelte@0.13.30
- jazz-tools@0.13.30
- jazz-inspector-element@0.13.30
## 0.0.79
### Patch Changes
- jazz-svelte@0.13.29
- jazz-tools@0.13.29
- jazz-inspector-element@0.13.29
## 0.0.78
### Patch Changes
- jazz-svelte@0.13.28
- jazz-tools@0.13.28
- jazz-inspector-element@0.13.28
## 0.0.77
### Patch Changes
- jazz-svelte@0.13.27
- jazz-tools@0.13.27
- jazz-inspector-element@0.13.27
## 0.0.76
### Patch Changes
- Updated dependencies [ff846d9]
- jazz-tools@0.13.26
- jazz-inspector-element@0.13.26
- jazz-svelte@0.13.26
## 0.0.75
### Patch Changes
- jazz-svelte@0.13.25
- jazz-tools@0.13.25
- jazz-inspector-element@0.13.25
## 0.0.74
### Patch Changes
- Updated dependencies [ec546b4]
- jazz-svelte@0.13.24
## 0.0.73
### Patch Changes
- Updated dependencies [3431076]
- Updated dependencies [02a240c]
- jazz-svelte@0.13.23
- jazz-tools@0.13.23
- jazz-inspector-element@0.13.23
## 0.0.72
### Patch Changes
- jazz-inspector-element@0.13.21
- jazz-svelte@0.13.21
- jazz-tools@0.13.21
## 0.0.71
### Patch Changes
- Updated dependencies [439f0fe]
- jazz-tools@0.13.20
- jazz-svelte@0.13.20
## 0.0.70
### Patch Changes
- Updated dependencies [80530a4]
- jazz-tools@0.13.19
- jazz-svelte@0.13.19
## 0.0.69
### Patch Changes
- Updated dependencies [761759c]
- jazz-tools@0.13.18
- jazz-svelte@0.13.18
## 0.0.68
### Patch Changes
- jazz-svelte@0.13.17
- jazz-tools@0.13.17
## 0.0.67
### Patch Changes
- jazz-svelte@0.13.16
- jazz-tools@0.13.16
## 0.0.66
### Patch Changes
- jazz-svelte@0.13.15
- jazz-tools@0.13.15
## 0.0.65
### Patch Changes
- jazz-svelte@0.13.14
- jazz-tools@0.13.14
## 0.0.64
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "file-share-svelte",
"version": "0.0.64",
"version": "0.0.85",
"private": true,
"type": "module",
"scripts": {
@@ -39,6 +39,7 @@
},
"dependencies": {
"@tailwindcss/typography": "^0.5.15",
"jazz-inspector-element": "workspace:*",
"jazz-svelte": "workspace:*",
"jazz-tools": "workspace:*",
"lucide-svelte": "^0.463.0",

View File

@@ -3,9 +3,8 @@
import { SharedFile } from '$lib/schema';
import { FileStream } from 'jazz-tools';
import { File, FileDown, Trash2, Link2 } from 'lucide-svelte';
import { useAccount } from 'jazz-svelte';
import { toast } from 'svelte-sonner';
import { formatFileSize } from '$lib/utils';
import { downloadFileBlob, formatFileSize } from '$lib/utils';
const {
file,
@@ -17,32 +16,22 @@
onDelete: (file: SharedFile) => void;
} = $props();
const { me } = useAccount();
const isAdmin = $derived(me && file._owner?.myRole() === 'admin');
const isAdmin = $derived(file._owner?.myRole() === 'admin');
const fileStreamId = $derived(file._refs.file?.id);
async function downloadFile() {
if (!file._refs.file?.id || !me) {
if (!fileStreamId) {
toast.error('Failed to download file');
return;
}
try {
const fileId = file._refs.file.id;
// Load the file as a blob, can take a while
const blob = await FileStream.loadAsBlob(fileId, me, {});
const blob = await FileStream.loadAsBlob(fileStreamId);
if (!blob) {
toast.error('Failed to download file');
return;
}
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = file.name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
downloadFileBlob(blob, file.name);
toast.success('File downloaded successfully');
} catch (error) {
console.error('Error downloading file:', error);
@@ -66,14 +55,19 @@
class="flex items-center justify-between rounded-lg border border-gray-200 bg-white p-4"
transition:slide={{ duration: 200 }}
>
<div class="flex items-center space-x-4">
<div class="flex items-center space-x-4 flex-grow">
<div class="flex h-10 w-10 items-center justify-center rounded-lg bg-blue-100 text-blue-600">
<File class="h-6 w-6" />
</div>
<div>
<a href="/file/{file.id}" class="hover:text-blue-600 hover:underline">
<div class="flex-grow">
{#if isAdmin}
<label class="sr-only" for={`file-name-${file.id}`}>File name</label>
<!-- Jazz values are reactive, but they are not recognized as reactive by Svelte -->
<!-- svelte-ignore binding_property_non_reactive -->
<input class="font-medium text-gray-900 w-full py-1" type="text" bind:value={file.name} id={`file-name-${file.id}`} />
{:else}
<h3 class="font-medium text-gray-900">{file.name}</h3>
</a>
{/if}
<p class="text-sm text-gray-500">
{isAdmin ? 'Owned by you' : ''} • Uploaded {new Date(
file.createdAt || 0

View File

@@ -1,28 +1,27 @@
import { Account, CoList, CoMap, FileStream, Profile, co, Group } from 'jazz-tools';
import { Account, CoList, CoMap, FileStream, Profile, coField, Group } from 'jazz-tools';
export class SharedFile extends CoMap {
name = co.string;
file = co.ref(FileStream);
createdAt = co.Date;
uploadedAt = co.Date;
size = co.number;
name = coField.string;
file = coField.ref(FileStream);
createdAt = coField.Date;
uploadedAt = coField.Date;
size = coField.number;
}
export class FileShareProfile extends Profile {
name = co.string;
name = coField.string;
}
export class ListOfSharedFiles extends CoList.Of(co.ref(SharedFile)) {}
export class ListOfSharedFiles extends CoList.Of(coField.ref(SharedFile)) {}
export class FileShareAccountRoot extends CoMap {
type = co.string;
sharedFiles = co.ref(ListOfSharedFiles);
publicGroup = co.ref(Group);
type = coField.literal('file-share-account');
sharedFiles = coField.ref(ListOfSharedFiles);
}
export class FileShareAccount extends Account {
profile = co.ref(FileShareProfile);
root = co.ref(FileShareAccountRoot);
profile = coField.ref(FileShareProfile);
root = coField.ref(FileShareAccountRoot);
/** The account migration is run on account creation and on every log-in.
* You can use it to set up the account root and any other initial CoValues you need.
@@ -31,7 +30,7 @@ export class FileShareAccount extends Account {
await this._refs.root?.load();
// Initialize root if it doesn't exist
if (!this.root || this.root.type !== 'file-share-account') {
if (this.root === undefined || this.root?.type !== 'file-share-account') {
// Create a group that will own all shared files
const publicGroup = Group.create({ owner: this });
publicGroup.addMember('everyone', 'reader');
@@ -40,9 +39,7 @@ export class FileShareAccount extends Account {
{
type: 'file-share-account',
sharedFiles: ListOfSharedFiles.create([], { owner: publicGroup }),
publicGroup
},
{ owner: this }
);
}
}

View File

@@ -20,3 +20,13 @@ export function formatFileSize(bytes: number): string {
export function generateTempFileId(fileName: string | undefined, createdAt: Date | undefined): string {
return `file-${fileName ?? 'unknown'}-${createdAt?.getTime() ?? 0}`;
}
export function downloadFileBlob(blob: Blob, fileName: string) {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}

View File

@@ -8,7 +8,8 @@
<script lang="ts">
import { JazzProvider } from 'jazz-svelte';
import { PasskeyAuthBasicUI, usePasskeyAuth } from 'jazz-svelte';
import "jazz-inspector-element"
import { PasskeyAuthBasicUI } from 'jazz-svelte';
import { Toaster } from 'svelte-sonner';
import '../app.css';
import { FileShareAccount } from '$lib/schema';
@@ -29,6 +30,7 @@
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
}}
>
<jazz-inspector></jazz-inspector>
<PasskeyAuthBasicUI appName="File Share">
<div class="min-h-screen bg-gray-100">
{@render children()}

View File

@@ -1,54 +1,38 @@
<script lang="ts">
import { useAccount, useCoState } from 'jazz-svelte';
import { SharedFile, ListOfSharedFiles } from '$lib/schema';
import { createInviteLink } from 'jazz-svelte';
import { AccountCoState } from 'jazz-svelte';
import { SharedFile } from '$lib/schema';
import { FileStream } from 'jazz-tools';
import FileItem from '$lib/components/FileItem.svelte';
import { SvelteMap } from 'svelte/reactivity';
import { generateTempFileId } from '$lib/utils';
import { CloudUpload } from 'lucide-svelte';
const { me, logOut } = useAccount();
const me = new AccountCoState({
resolve: {
profile: true,
root: {
sharedFiles: {
$each: true
},
}
}
});
const mySharedFilesId = me?.root?._refs.sharedFiles.id;
const sharedFiles = $derived(useCoState(ListOfSharedFiles, mySharedFilesId, [{}]));
const sharedFiles = $derived(me.current?.root.sharedFiles);
let fileInput: HTMLInputElement;
type PendingSharedFile = {
name: string;
id: string;
createdAt: Date;
};
// Track files that are currently uploading
const uploadingFiles = new SvelteMap<string, PendingSharedFile>();
async function handleFileUpload(event: Event) {
const input = event.target as HTMLInputElement;
const files = input.files;
if (!files || !files.length || !me.root?.sharedFiles || !me.root.publicGroup) return;
if (!files?.length || !sharedFiles) return;
const file = files[0];
const fileName = file.name;
const createdAt = new Date();
const fileId = generateTempFileId(fileName, createdAt);
const tempFile: PendingSharedFile = {
name: fileName,
id: fileId,
createdAt
};
// Add to uploading files
uploadingFiles.set(fileId, tempFile);
try {
const ownership = { owner: me.root.publicGroup };
// Create a FileStream from the uploaded file
const fileStream = await FileStream.createFromBlob(file, ownership);
const fileStream = await FileStream.createFromBlob(file, sharedFiles._owner);
// Create the shared file entry
const sharedFile = SharedFile.create(
@@ -59,29 +43,22 @@
uploadedAt: new Date(),
size: file.size
},
ownership
sharedFiles._owner
);
// Add the file to the user's files list
me.root.sharedFiles.push(sharedFile);
sharedFiles.push(sharedFile);
} finally {
uploadingFiles.delete(fileId);
fileInput.value = ''; // reset input
}
}
async function shareFile(file: SharedFile) {
const inviteLink = createInviteLink(file, 'reader');
await navigator.clipboard.writeText(inviteLink);
alert('Share link copied to clipboard!');
}
async function deleteFile(file: SharedFile) {
if (!me?.root?.sharedFiles || !sharedFiles.current) return;
if (!sharedFiles) return;
const index = sharedFiles.current.indexOf(file);
const index = sharedFiles.indexOf(file);
if (index > -1) {
me.root.sharedFiles.splice(index, 1);
sharedFiles.splice(index, 1);
}
}
</script>
@@ -91,11 +68,11 @@
<div class="mb-12 flex items-center justify-between">
<div>
<h1 class="mb-2 text-4xl font-bold text-gray-900">File Share</h1>
<h2 class="text-xl text-gray-600">Welcome back, {me?.profile?.name}</h2>
<h2 class="text-xl text-gray-600">Welcome back, {me.current?.profile.name}</h2>
</div>
<button
onclick={logOut}
onclick={me.logOut}
class="rounded-lg bg-red-500 px-6 py-2.5 text-sm font-medium text-white transition-colors hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2"
>
Log Out
@@ -126,14 +103,12 @@
<!-- Files List -->
<div class="space-y-4">
{#if sharedFiles.current}
{#if !(sharedFiles.current.length === 0 && uploadingFiles.size === 0)}
{#each [...sharedFiles.current, ...uploadingFiles.values()] as file (generateTempFileId(file?.name, file?.createdAt))}
{#if sharedFiles}
{#if sharedFiles.length}
{#each sharedFiles as file}
{#if file}
<FileItem
{file}
loading={uploadingFiles.has(generateTempFileId(file?.name, file?.createdAt))}
onShare={shareFile}
onDelete={deleteFile}
/>
{/if}

View File

@@ -1,39 +1,33 @@
<script lang="ts">
import { page } from '$app/stores';
import { useAccount, useCoState } from 'jazz-svelte';
import { CoState } from 'jazz-svelte';
import { SharedFile } from '$lib/schema';
import { File, FileDown, Link2 } from 'lucide-svelte';
import type { ID } from 'jazz-tools';
import { FileStream } from 'jazz-tools';
import { toast } from 'svelte-sonner';
import { downloadFileBlob } from '$lib/utils';
const { me } = useAccount();
const fileId = $page.params.fileId;
const file = $state(useCoState(SharedFile, fileId as ID<SharedFile>, {}));
const isAdmin = $derived(me && file.current?._owner?.myRole() === 'admin');
const file = $derived(new CoState(SharedFile, fileId as ID<SharedFile>));
const isAdmin = $derived(file.current?._owner?.myRole() === 'admin');
const fileStreamId = $derived(file.current?._refs.file?.id);
async function downloadFile() {
if (!file.current?._refs.file?.id || !me) {
if (!fileStreamId || !file.current) {
toast.error('Failed to download file');
return;
}
try {
const fileId = file.current._refs.file.id;
const blob = await FileStream.loadAsBlob(fileId, me, {});
const blob = await FileStream.loadAsBlob(fileStreamId);
if (!blob) {
toast.error('Failed to download file');
return;
}
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = file.current.name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
downloadFileBlob(blob, file.current.name);
toast.success('File downloaded successfully');
} catch (error) {
console.error('Error downloading file:', error);

View File

@@ -59,7 +59,7 @@ test('can login with passkey and upload file', async ({ page, browser }) => {
await fileChooser.setFiles(filePath);
// Verify the uploaded file appears in the list
await expect(page.getByText('test-file.txt')).toBeVisible();
await expect(page.getByRole("textbox", { name: "File name" })).toHaveValue("test-file.txt");
await page.getByRole('button', { name: 'Share file' }).click();
const inviteLink = await page.evaluate(() => navigator.clipboard.readText());

View File

@@ -1,5 +1,175 @@
# jazz-tailwind-demo-auth-starter
## 0.0.41
### Patch Changes
- Updated dependencies [3d1027f]
- Updated dependencies [c240eed]
- jazz-tools@0.14.2
- jazz-inspector@0.14.2
- jazz-react@0.14.2
## 0.0.40
### Patch Changes
- Updated dependencies [cdfc105]
- jazz-tools@0.14.1
- jazz-inspector@0.14.1
- jazz-react@0.14.1
## 0.0.39
### Patch Changes
- Updated dependencies [5835ed1]
- jazz-tools@0.14.0
- jazz-inspector@0.14.0
- jazz-react@0.14.0
## 0.0.38
### Patch Changes
- jazz-react@0.13.32
## 0.0.37
### Patch Changes
- Updated dependencies [e5b170f]
- jazz-tools@0.13.31
- jazz-inspector@0.13.31
- jazz-react@0.13.31
## 0.0.36
### Patch Changes
- jazz-inspector@0.13.30
- jazz-react@0.13.30
- jazz-tools@0.13.30
## 0.0.35
### Patch Changes
- Updated dependencies [8e5ff13]
- jazz-inspector@0.13.29
- jazz-react@0.13.29
- jazz-tools@0.13.29
## 0.0.34
### Patch Changes
- jazz-inspector@0.13.28
- jazz-react@0.13.28
- jazz-tools@0.13.28
## 0.0.33
### Patch Changes
- jazz-inspector@0.13.27
- jazz-react@0.13.27
- jazz-tools@0.13.27
## 0.0.32
### Patch Changes
- Updated dependencies [ff846d9]
- jazz-tools@0.13.26
- jazz-inspector@0.13.26
- jazz-react@0.13.26
## 0.0.31
### Patch Changes
- jazz-inspector@0.13.25
- jazz-react@0.13.25
- jazz-tools@0.13.25
## 0.0.30
### Patch Changes
- Updated dependencies [02a240c]
- jazz-tools@0.13.23
- jazz-inspector@0.13.23
- jazz-react@0.13.23
## 0.0.29
### Patch Changes
- Updated dependencies [7de210f]
- jazz-inspector@0.13.21
- jazz-react@0.13.21
- jazz-tools@0.13.21
## 0.0.28
### Patch Changes
- Updated dependencies [439f0fe]
- jazz-tools@0.13.20
- jazz-inspector@0.13.20
- jazz-react@0.13.20
## 0.0.27
### Patch Changes
- Updated dependencies [80530a4]
- jazz-tools@0.13.19
- jazz-inspector@0.13.19
- jazz-react@0.13.19
## 0.0.26
### Patch Changes
- Updated dependencies [761759c]
- jazz-tools@0.13.18
- jazz-inspector@0.13.18
- jazz-react@0.13.18
## 0.0.25
### Patch Changes
- jazz-inspector@0.13.17
- jazz-react@0.13.17
- jazz-tools@0.13.17
## 0.0.24
### Patch Changes
- jazz-inspector@0.13.16
- jazz-react@0.13.16
- jazz-tools@0.13.16
## 0.0.23
### Patch Changes
- jazz-inspector@0.13.15
- jazz-react@0.13.15
- jazz-tools@0.13.15
## 0.0.22
### Patch Changes
- jazz-inspector@0.13.14
- jazz-react@0.13.14
- jazz-tools@0.13.14
## 0.0.21
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "filestream",
"private": true,
"version": "0.0.21",
"version": "0.0.41",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -7,28 +7,6 @@ function App() {
<main className="container mt-16 flex flex-col gap-8">
<Logo />
<FileWidget />
<p className="text-center">
Edit the form above,{" "}
<button
type="button"
onClick={() => window.location.reload()}
className="font-semibold underline"
>
refresh
</button>{" "}
this page, and see your changes persist.
</p>
<p className="text-center my-16">
Go to{" "}
<a
className="font-semibold underline"
href="https://jazz.tools/docs/react/guide"
>
jazz.tools/docs/react/guide
</a>{" "}
for a full tutorial.
</p>
</main>
</>
);

View File

@@ -1,7 +1,8 @@
"use client";
import { useAccount } from "jazz-react";
import { FileStream } from "jazz-tools";
import { FileStream, co } from "jazz-tools";
import { useRef, useState } from "react";
import { JazzAccount } from "./schema";
export function FileWidget() {
const inputRef = useRef<HTMLInputElement>(null);
@@ -9,7 +10,7 @@ export function FileWidget() {
const [isUploading, setIsUploading] = useState(false);
const [progress, setProgress] = useState(0);
const [error, setError] = useState<string | null>(null);
const { me } = useAccount();
const { me } = useAccount(JazzAccount, { resolve: { profile: true } });
async function handleUpload(event: React.FormEvent) {
event.preventDefault();
@@ -29,7 +30,7 @@ export function FileWidget() {
try {
setIsUploading(true);
me.profile.file = await FileStream.createFromBlob(file, {
me.profile.file = await co.fileStream().createFromBlob(file, {
onProgress: (p) => setProgress(Math.round(p * 100)),
});
} catch (error) {
@@ -189,7 +190,7 @@ export function FileWidget() {
);
}
const fileData = me?.profile?.file?.getChunks();
const fileData = me?.profile?.file?.getMetadata();
const mimeType = fileData?.mimeType || "unknown";
return (

View File

@@ -10,12 +10,6 @@ import { JazzAccount } from "./schema.ts";
// We use this to identify the app in the passkey auth
export const APPLICATION_NAME = "Jazz File Stream Example";
declare module "jazz-react" {
export interface Register {
Account: JazzAccount;
}
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<JazzProvider

View File

@@ -1,14 +1,10 @@
import { Account, FileStream, Profile, co } from "jazz-tools";
import { co, z } from "jazz-tools";
export class JazzProfile extends Profile {
file = co.ref(FileStream, { optional: true });
}
export const JazzProfile = co.profile({
file: z.optional(co.fileStream()),
});
export class JazzAccount extends Account {
profile = co.ref(JazzProfile);
/** The account migration is run on account creation and on every log-in.
* You can use it to set up the account root and any other initial CoValues you need.
*/
migrate(this: JazzAccount) {}
}
export const JazzAccount = co.account({
profile: JazzProfile,
root: co.map({}),
});

View File

@@ -1,5 +1,154 @@
# form
## 0.1.42
### Patch Changes
- Updated dependencies [3d1027f]
- Updated dependencies [c240eed]
- jazz-tools@0.14.2
- jazz-react@0.14.2
## 0.1.41
### Patch Changes
- Updated dependencies [cdfc105]
- jazz-tools@0.14.1
- jazz-react@0.14.1
## 0.1.40
### Patch Changes
- Updated dependencies [5835ed1]
- jazz-tools@0.14.0
- jazz-react@0.14.0
## 0.1.39
### Patch Changes
- jazz-react@0.13.32
## 0.1.38
### Patch Changes
- Updated dependencies [e5b170f]
- jazz-tools@0.13.31
- jazz-react@0.13.31
## 0.1.37
### Patch Changes
- jazz-react@0.13.30
- jazz-tools@0.13.30
## 0.1.36
### Patch Changes
- jazz-react@0.13.29
- jazz-tools@0.13.29
## 0.1.35
### Patch Changes
- jazz-react@0.13.28
- jazz-tools@0.13.28
## 0.1.34
### Patch Changes
- jazz-react@0.13.27
- jazz-tools@0.13.27
## 0.1.33
### Patch Changes
- Updated dependencies [ff846d9]
- jazz-tools@0.13.26
- jazz-react@0.13.26
## 0.1.32
### Patch Changes
- jazz-react@0.13.25
- jazz-tools@0.13.25
## 0.1.31
### Patch Changes
- Updated dependencies [02a240c]
- jazz-tools@0.13.23
- jazz-react@0.13.23
## 0.1.30
### Patch Changes
- jazz-react@0.13.21
- jazz-tools@0.13.21
## 0.1.29
### Patch Changes
- Updated dependencies [439f0fe]
- jazz-tools@0.13.20
- jazz-react@0.13.20
## 0.1.28
### Patch Changes
- Updated dependencies [80530a4]
- jazz-tools@0.13.19
- jazz-react@0.13.19
## 0.1.27
### Patch Changes
- Updated dependencies [761759c]
- jazz-tools@0.13.18
- jazz-react@0.13.18
## 0.1.26
### Patch Changes
- jazz-react@0.13.17
- jazz-tools@0.13.17
## 0.1.25
### Patch Changes
- jazz-react@0.13.16
- jazz-tools@0.13.16
## 0.1.24
### Patch Changes
- jazz-react@0.13.15
- jazz-tools@0.13.15
## 0.1.23
### Patch Changes
- jazz-react@0.13.14
- jazz-tools@0.13.14
## 0.1.22
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "form",
"private": true,
"version": "0.1.22",
"version": "0.1.42",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,9 +1,7 @@
import { useIframeHashRouter } from "hash-slash";
import { ID } from "jazz-tools";
import { CreateOrder } from "./CreateOrder.tsx";
import { EditOrder } from "./EditOrder.tsx";
import { Orders } from "./Orders.tsx";
import { BubbleTeaOrder } from "./schema.ts";
function App() {
const router = useIframeHashRouter();
@@ -14,7 +12,7 @@ function App() {
{router.route({
"/": () => <Orders />,
"/order": () => <CreateOrder />,
"/order/:id": (id) => <EditOrder id={id as ID<BubbleTeaOrder>} />,
"/order/:id": (id) => <EditOrder id={id} />,
})}
</main>
</>

View File

@@ -1,6 +1,6 @@
import { useIframeHashRouter } from "hash-slash";
import { useAccount, useCoState } from "jazz-react";
import { ID } from "jazz-tools";
import { Loaded } from "jazz-tools";
import { useState } from "react";
import { Errors } from "./Errors.tsx";
import { LinkToHome } from "./LinkToHome.tsx";
@@ -8,11 +8,12 @@ import { OrderForm } from "./OrderForm.tsx";
import {
BubbleTeaOrder,
DraftBubbleTeaOrder,
JazzAccount,
ListOfBubbleTeaAddOns,
} from "./schema.ts";
export function CreateOrder() {
const { me } = useAccount({
const { me } = useAccount(JazzAccount, {
resolve: { root: { draft: true, orders: true } },
});
const router = useIframeHashRouter();
@@ -20,16 +21,16 @@ export function CreateOrder() {
if (!me?.root) return;
const onSave = (draft: DraftBubbleTeaOrder) => {
const onSave = (draft: Loaded<typeof DraftBubbleTeaOrder>) => {
// validate if the draft is a valid order
const validation = draft.validate();
const validation = DraftBubbleTeaOrder.validate(draft);
setErrors(validation.errors);
if (validation.errors.length > 0) {
return;
}
// turn the draft into a real order
me.root.orders.push(draft as BubbleTeaOrder);
me.root.orders.push(draft as Loaded<typeof BubbleTeaOrder>);
// reset the draft
me.root.draft = DraftBubbleTeaOrder.create({
@@ -58,11 +59,11 @@ function CreateOrderForm({
id,
onSave,
}: {
id: ID<DraftBubbleTeaOrder>;
onSave: (draft: DraftBubbleTeaOrder) => void;
id: string;
onSave: (draft: Loaded<typeof DraftBubbleTeaOrder>) => void;
}) {
const draft = useCoState(DraftBubbleTeaOrder, id, {
resolve: { addOns: true },
resolve: { addOns: true, instructions: true },
});
if (!draft) return;

View File

@@ -1,11 +1,11 @@
import { useAccount } from "jazz-react";
import { DraftBubbleTeaOrder, JazzAccount } from "./schema";
export function DraftIndicator() {
const { me } = useAccount({
const { me } = useAccount(JazzAccount, {
resolve: { root: { draft: true } },
});
if (me?.root.draft?.hasChanges) {
if (DraftBubbleTeaOrder.hasChanges(me?.root.draft)) {
return (
<div className="absolute -top-1 -right-1 bg-blue-500 border-2 border-white w-3 h-3 rounded-full dark:border-stone-925">
<span className="sr-only">You have a draft</span>

View File

@@ -1,11 +1,10 @@
import { useCoState } from "jazz-react";
import { ID } from "jazz-tools";
import { LinkToHome } from "./LinkToHome.tsx";
import { OrderForm } from "./OrderForm.tsx";
import { OrderThumbnail } from "./OrderThumbnail.tsx";
import { BubbleTeaOrder } from "./schema.ts";
export function EditOrder(props: { id: ID<BubbleTeaOrder> }) {
export function EditOrder(props: { id: string }) {
const order = useCoState(BubbleTeaOrder, props.id);
if (!order) return;

View File

@@ -1,3 +1,4 @@
import { CoPlainText, Loaded } from "jazz-tools";
import {
BubbleTeaAddOnTypes,
BubbleTeaBaseTeaTypes,
@@ -9,9 +10,21 @@ export function OrderForm({
order,
onSave,
}: {
order: BubbleTeaOrder | DraftBubbleTeaOrder;
order: Loaded<typeof BubbleTeaOrder> | Loaded<typeof DraftBubbleTeaOrder>;
onSave?: (e: React.FormEvent<HTMLFormElement>) => void;
}) {
// Handles updates to the instructions field of the order.
// If instructions already exist, applyDiff updates them incrementally.
// Otherwise, creates a new CoPlainText instance for the instructions.
const handleInstructionsChange = (
e: React.ChangeEvent<HTMLTextAreaElement>,
) => {
if (order.instructions) {
return order.instructions.applyDiff(e.target.value);
}
order.instructions = CoPlainText.create(e.target.value, order._owner);
};
return (
<form onSubmit={onSave} className="grid gap-5">
<div className="flex flex-col gap-2">
@@ -88,9 +101,9 @@ export function OrderForm({
<textarea
name="instructions"
id="instructions"
value={order.instructions}
value={`${order.instructions}`}
className="dark:bg-transparent"
onChange={(e) => (order.instructions = e.target.value)}
onChange={handleInstructionsChange}
></textarea>
</div>

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