Compare commits
453 Commits
jazz-react
...
cojson-sto
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a383c7e984 | ||
|
|
8ca283147d | ||
|
|
c3d87796ed | ||
|
|
cb88caced9 | ||
|
|
6313ead62d | ||
|
|
f674910aa9 | ||
|
|
37fc25f5a6 | ||
|
|
5e9c338c27 | ||
|
|
4b61f7c7a5 | ||
|
|
1c8472ffbd | ||
|
|
9dba68ac36 | ||
|
|
13b57aa576 | ||
|
|
aedf0f3ac5 | ||
|
|
7fa61644b0 | ||
|
|
12389c82f9 | ||
|
|
4c03a17d3a | ||
|
|
5d1ea45a9c | ||
|
|
cc0479afe0 | ||
|
|
cfb6786468 | ||
|
|
5662faa9c0 | ||
|
|
3e7d9cb585 | ||
|
|
2116a598ae | ||
|
|
d14a069a12 | ||
|
|
aacef80994 | ||
|
|
2a237e5d32 | ||
|
|
0fe30eca0b | ||
|
|
dea7a8dfd9 | ||
|
|
ab7191bed0 | ||
|
|
901b7c0a51 | ||
|
|
17bea5975c | ||
|
|
e005ecd0a1 | ||
|
|
e7e505e5f3 | ||
|
|
d6ffe03d3c | ||
|
|
1120747a24 | ||
|
|
1e2d7d1c4e | ||
|
|
e26f110acd | ||
|
|
28f84a4ee6 | ||
|
|
cffe4abb84 | ||
|
|
2e0b7cee8c | ||
|
|
d3b47f59e8 | ||
|
|
aea3287965 | ||
|
|
6525f8a12e | ||
|
|
fe4c934a4b | ||
|
|
60261c8dba | ||
|
|
7411049faa | ||
|
|
356b06559b | ||
|
|
193738cfe2 | ||
|
|
ddb74fa167 | ||
|
|
aca5642671 | ||
|
|
a5bed20c4a | ||
|
|
09bf53a8d3 | ||
|
|
061ec996b1 | ||
|
|
a7ee0465b5 | ||
|
|
e995d8a1fe | ||
|
|
5ccb48446e | ||
|
|
04b20c2e42 | ||
|
|
7aae2441a1 | ||
|
|
650e95a528 | ||
|
|
3d80ab11d9 | ||
|
|
85e6489a3e | ||
|
|
45b6c87ade | ||
|
|
edadc4b986 | ||
|
|
455b722357 | ||
|
|
ad2e1d1e98 | ||
|
|
7c94b70c31 | ||
|
|
74bcf1752e | ||
|
|
97a54b3911 | ||
|
|
c393c8880f | ||
|
|
359157fa70 | ||
|
|
62d9680449 | ||
|
|
db6067439d | ||
|
|
5051d6a410 | ||
|
|
25efaf30f5 | ||
|
|
98aadd9842 | ||
|
|
d555b18c11 | ||
|
|
550111f8a1 | ||
|
|
3d4a00a19d | ||
|
|
d06c6677dc | ||
|
|
ce30f3c4ff | ||
|
|
1072b9c2fe | ||
|
|
5ea41c69ef | ||
|
|
2d8eed3b69 | ||
|
|
45c9c8558f | ||
|
|
dccb80edf0 | ||
|
|
6f72419b6e | ||
|
|
fc55e5c8e8 | ||
|
|
4a74ccf399 | ||
|
|
a442315995 | ||
|
|
0d5ee3ed07 | ||
|
|
4e32cae8a7 | ||
|
|
49d721a29b | ||
|
|
2c6d27598d | ||
|
|
e637cb0700 | ||
|
|
c42848ece9 | ||
|
|
841a818dff | ||
|
|
f74881a3fe | ||
|
|
c559054378 | ||
|
|
d6d9c0adcb | ||
|
|
cdf9b5a71c | ||
|
|
4b950bce14 | ||
|
|
8343d8f0fe | ||
|
|
eeb280f17b | ||
|
|
a927334b40 | ||
|
|
be0c1bcda5 | ||
|
|
b861daca60 | ||
|
|
b4cd307eba | ||
|
|
1d0e83d3fa | ||
|
|
be7c4c29a1 | ||
|
|
ffa4db0b61 | ||
|
|
7daf992c51 | ||
|
|
333c583265 | ||
|
|
fa2227716f | ||
|
|
bfa5e14112 | ||
|
|
38dabd4602 | ||
|
|
dcdf89bba7 | ||
|
|
a6e6939015 | ||
|
|
71aa24054d | ||
|
|
4c88421440 | ||
|
|
64bad01d9a | ||
|
|
4dfa809a77 | ||
|
|
726df167f8 | ||
|
|
2f62cbde13 | ||
|
|
93ef74219d | ||
|
|
495aa81335 | ||
|
|
99caaba328 | ||
|
|
4bfcc787a1 | ||
|
|
7e96eb12dd | ||
|
|
a71eba2eb5 | ||
|
|
b129cf9328 | ||
|
|
a6e31c41b7 | ||
|
|
47746ff9ba | ||
|
|
4e4319a546 | ||
|
|
d1de8baf1d | ||
|
|
d427a2a13c | ||
|
|
1fa6c3987a | ||
|
|
ddf49e532f | ||
|
|
a0c4ef72ef | ||
|
|
99009a9054 | ||
|
|
e512df4eff | ||
|
|
da3ede8b54 | ||
|
|
56ed6d3271 | ||
|
|
78112bb19a | ||
|
|
ac6b0c6561 | ||
|
|
632365a4fb | ||
|
|
14f52dbc4e | ||
|
|
82e4256e26 | ||
|
|
5e253cc717 | ||
|
|
0c1663505c | ||
|
|
ad85703373 | ||
|
|
3a6ec3791c | ||
|
|
f9590f9120 | ||
|
|
706ebf87da | ||
|
|
00dd317a5d | ||
|
|
5ced45fb90 | ||
|
|
23daa7cdde | ||
|
|
c018752c0c | ||
|
|
ba3bdcc268 | ||
|
|
4177f6f92c | ||
|
|
e32a1f7392 | ||
|
|
7c43fd37ae | ||
|
|
48168d619b | ||
|
|
1b9b9273e5 | ||
|
|
2f4eada56c | ||
|
|
709b3432d7 | ||
|
|
78a7672401 | ||
|
|
bb214f0e8b | ||
|
|
8706398f74 | ||
|
|
1502a6b421 | ||
|
|
f167112d99 | ||
|
|
d4d6cb3307 | ||
|
|
9565cdf323 | ||
|
|
dfb21ff0f2 | ||
|
|
98d697fafb | ||
|
|
9150d53f90 | ||
|
|
ac7ecc278c | ||
|
|
99595bc7e3 | ||
|
|
f5bae09679 | ||
|
|
a7b9ae0b76 | ||
|
|
196efcf48e | ||
|
|
153aa972b3 | ||
|
|
4508524f77 | ||
|
|
e7e9062dff | ||
|
|
65e8d135c1 | ||
|
|
0b8e84e22d | ||
|
|
7b51e38cf6 | ||
|
|
fcc2cbdc93 | ||
|
|
5aa13b5afc | ||
|
|
dc746a2fc9 | ||
|
|
ca39bae001 | ||
|
|
020b12fa32 | ||
|
|
bfea02c229 | ||
|
|
74e6495beb | ||
|
|
3fe683206a | ||
|
|
f869d9a4dd | ||
|
|
0724cfb95e | ||
|
|
3b7fd14976 | ||
|
|
55fbe8a0a8 | ||
|
|
22c2600ef1 | ||
|
|
156167e6d7 | ||
|
|
d41feafebe | ||
|
|
05e3e30451 | ||
|
|
637ae13131 | ||
|
|
4a601a6441 | ||
|
|
aef84cf2ef | ||
|
|
275a26e2c1 | ||
|
|
15b422f711 | ||
|
|
946ae63070 | ||
|
|
2e42825971 | ||
|
|
089cdb2031 | ||
|
|
5cfe47ebbe | ||
|
|
6ee0ec755e | ||
|
|
8ffca202bd | ||
|
|
6d2bcc7490 | ||
|
|
fa3cf6d0c1 | ||
|
|
d823700a7c | ||
|
|
ed472e834a | ||
|
|
365b0ea3a9 | ||
|
|
43c831167f | ||
|
|
cac0f1ad2b | ||
|
|
f66b7c1a5a | ||
|
|
1b57cfc3f6 | ||
|
|
a805e27b0a | ||
|
|
9d6d9fe7a5 | ||
|
|
69709c2cf2 | ||
|
|
409758afd0 | ||
|
|
e0bc532345 | ||
|
|
87e02c0516 | ||
|
|
405f9be7b9 | ||
|
|
c82bf737bf | ||
|
|
91cbb2f9d4 | ||
|
|
cfbba59c6d | ||
|
|
b1209e2e09 | ||
|
|
20b3d88135 | ||
|
|
d9ad1f4de5 | ||
|
|
c90153e0c9 | ||
|
|
a4241c0f4b | ||
|
|
e1ff7a65a8 | ||
|
|
011af55446 | ||
|
|
e9dbcf53c8 | ||
|
|
3da21b95ec | ||
|
|
a6d0dd07a1 | ||
|
|
e7733a5428 | ||
|
|
ac474c4afb | ||
|
|
afe8e1f3b2 | ||
|
|
b12b3808fa | ||
|
|
08ec8fe709 | ||
|
|
f70cae6bf6 | ||
|
|
59fe373863 | ||
|
|
be58b4c1d8 | ||
|
|
96c520ae4d | ||
|
|
a9553b4945 | ||
|
|
fa516522e3 | ||
|
|
59e4b5da54 | ||
|
|
2fa3f94b4a | ||
|
|
a2f8461e26 | ||
|
|
cabaf079be | ||
|
|
d8de4a7ada | ||
|
|
e510a544b7 | ||
|
|
b2ee30630d | ||
|
|
ab0d4f364a | ||
|
|
6d9c6ae698 | ||
|
|
914af3deae | ||
|
|
4ad8e9ae78 | ||
|
|
1e0b496555 | ||
|
|
0088aa8b25 | ||
|
|
35a66df1e4 | ||
|
|
3b2fa64a82 | ||
|
|
3d1027f278 | ||
|
|
cc78386163 | ||
|
|
c240eed6a4 | ||
|
|
2a9b7f5d52 | ||
|
|
f2fbd29de5 | ||
|
|
c960176a2a | ||
|
|
fd4bae4cc1 | ||
|
|
ae32b7c19b | ||
|
|
83986c6699 | ||
|
|
59a4e2cee3 | ||
|
|
d2a971c86c | ||
|
|
aae9ef49da | ||
|
|
58c5ee5c73 | ||
|
|
e88252bee4 | ||
|
|
a6b7857f6f | ||
|
|
265c30158e | ||
|
|
505e132f1e | ||
|
|
c6d5195cb5 | ||
|
|
8af543e7d6 | ||
|
|
016b2098a7 | ||
|
|
f1d6097ee6 | ||
|
|
3172a61543 | ||
|
|
4683cc7ada | ||
|
|
25324c28d9 | ||
|
|
f74cb4885a | ||
|
|
4130213d82 | ||
|
|
c36a26e669 | ||
|
|
c8b33ad7f1 | ||
|
|
a79489683e | ||
|
|
1251fb89b1 | ||
|
|
cdfc10557a | ||
|
|
6e0481114c | ||
|
|
f7e157d2f5 | ||
|
|
ce83f101c7 | ||
|
|
c3c723776e | ||
|
|
33d7789530 | ||
|
|
dcd7398b06 | ||
|
|
c3dd8d0271 | ||
|
|
afe0accad5 | ||
|
|
5835ed1274 | ||
|
|
c5a6b87885 | ||
|
|
294678e18e | ||
|
|
9bb90489f8 | ||
|
|
67efefde3f | ||
|
|
5b28545714 | ||
|
|
b4352e38aa | ||
|
|
8015b8099b | ||
|
|
ee451ebd67 | ||
|
|
9dcd70c896 | ||
|
|
bcacd04773 | ||
|
|
65f20184e5 | ||
|
|
0c1817e5a4 | ||
|
|
addd31d43d | ||
|
|
53a02511e1 | ||
|
|
c66aa631b3 | ||
|
|
49a84bd8c5 | ||
|
|
218ec7eb8d | ||
|
|
54f313e41e | ||
|
|
f14d971a75 | ||
|
|
5290452e52 | ||
|
|
f49c24807a | ||
|
|
938f6767e4 | ||
|
|
b053369589 | ||
|
|
42c7f48c52 | ||
|
|
cd3c2cbe81 | ||
|
|
e55bcafedb | ||
|
|
dd3f7b5685 | ||
|
|
bde9d7637d | ||
|
|
b3f1bba17f | ||
|
|
eb33fe6cf0 | ||
|
|
54cf1b5906 | ||
|
|
c7f48a0750 | ||
|
|
f0419c5cde | ||
|
|
500ceb593c | ||
|
|
51251fd87c | ||
|
|
ce27b1d6f9 | ||
|
|
dc46e36281 | ||
|
|
b2bef6ffc2 | ||
|
|
2766009ce6 | ||
|
|
8f88b212c1 | ||
|
|
32e0133468 | ||
|
|
bf88604943 | ||
|
|
9b66fde52d | ||
|
|
1caa2761a4 | ||
|
|
4d54f1ec50 | ||
|
|
135ca17438 | ||
|
|
a5f9223215 | ||
|
|
b14a045cdb | ||
|
|
0dfbda0487 | ||
|
|
19cb9c3ad3 | ||
|
|
646c12f093 | ||
|
|
fbbaad3e80 | ||
|
|
425aa476c8 | ||
|
|
6922431197 | ||
|
|
31614c0a4f | ||
|
|
07222cd6b0 | ||
|
|
4a88732f89 | ||
|
|
57b69eb8da | ||
|
|
066676c243 | ||
|
|
2aac54b91d | ||
|
|
4e4b53c420 | ||
|
|
e141024656 | ||
|
|
2c48ae0434 | ||
|
|
2bf974390d | ||
|
|
a5116b9d63 | ||
|
|
91ffdcc82f | ||
|
|
810a7e6db2 | ||
|
|
e123715819 | ||
|
|
1b1c0a98af | ||
|
|
9fdf5b4831 | ||
|
|
0d087f3d4c | ||
|
|
e2c222c8a6 | ||
|
|
e410823087 | ||
|
|
5262580222 | ||
|
|
dcc11e5b60 | ||
|
|
1b05fe5b55 | ||
|
|
de8f896bf9 | ||
|
|
d63716a827 | ||
|
|
a1e7fce3b9 | ||
|
|
d5edad7ba5 | ||
|
|
559a4a223b | ||
|
|
16d5553ccd | ||
|
|
ef76c586cc | ||
|
|
8ea4b9761c | ||
|
|
a81f281079 | ||
|
|
3ecb602459 | ||
|
|
ea69ea1f67 | ||
|
|
e430bd061e | ||
|
|
a957485172 | ||
|
|
9060975dfb | ||
|
|
753ec83fdd | ||
|
|
44a9785e93 | ||
|
|
d9b390e538 | ||
|
|
b194f7831b | ||
|
|
9892dd708f | ||
|
|
b60be9405d | ||
|
|
84588e0798 | ||
|
|
e5b170f25e | ||
|
|
6efdfef386 | ||
|
|
6528d350ec | ||
|
|
b6244582c7 | ||
|
|
cfbf3aa51e | ||
|
|
b1e00d2b18 | ||
|
|
ec330eaa14 | ||
|
|
07dd2c5e69 | ||
|
|
37ab25ff62 | ||
|
|
4750e23800 | ||
|
|
862386c19b | ||
|
|
30df1098e1 | ||
|
|
dad4394eed | ||
|
|
9b70336a38 | ||
|
|
75328ac49d | ||
|
|
63d0b0673e | ||
|
|
2b456b5e07 | ||
|
|
e20809d314 | ||
|
|
9472347b57 | ||
|
|
b43395d8ed | ||
|
|
b6de11e125 | ||
|
|
09b815b87e | ||
|
|
1ab839bd59 | ||
|
|
af3b13428c | ||
|
|
fe241e7e7c | ||
|
|
db3c737e9f | ||
|
|
28d7fe9b90 | ||
|
|
4c70ca198b | ||
|
|
742283263f | ||
|
|
5f4e9a9936 | ||
|
|
2b40b3052c | ||
|
|
9dd926a59a | ||
|
|
9aa6ab6fea | ||
|
|
4207fb5914 | ||
|
|
cb3e5cbe81 | ||
|
|
d199fb06b3 | ||
|
|
0eaa500aef | ||
|
|
774c05cef0 | ||
|
|
96a1f303fe | ||
|
|
6a55a548a0 | ||
|
|
0e87c1c971 | ||
|
|
89fac342bf | ||
|
|
d23c71d511 | ||
|
|
b24071cf33 | ||
|
|
a5c88c08de | ||
|
|
906932db1e | ||
|
|
2033e35a41 | ||
|
|
5a2a12ccbb | ||
|
|
d288cb6954 |
@@ -2,8 +2,8 @@
|
||||
"$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"fixed": [],
|
||||
"linked": [
|
||||
"linked": [],
|
||||
"fixed": [
|
||||
[
|
||||
"cojson",
|
||||
"cojson-storage",
|
||||
@@ -12,6 +12,10 @@
|
||||
"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",
|
||||
@@ -23,6 +27,8 @@
|
||||
"jazz-react-native-core",
|
||||
"jazz-react-native",
|
||||
"jazz-react-native-media-images",
|
||||
"jazz-richtext-prosemirror",
|
||||
"jazz-richtext-tiptap",
|
||||
"jazz-run",
|
||||
"jazz-svelte",
|
||||
"jazz-tools",
|
||||
|
||||
1
.github/workflows/e2e-rn-test.yml
vendored
@@ -38,7 +38,6 @@ jobs:
|
||||
- name: chat-rn-expo App Pre Build
|
||||
working-directory: ./examples/chat-rn-expo
|
||||
run: |
|
||||
pnpm build
|
||||
pnpm expo prebuild --clean
|
||||
|
||||
- name: Install Maestro
|
||||
|
||||
46
.github/workflows/playwright-homepage.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Playwright Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
timeout-minutes: 60
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2204
|
||||
strategy:
|
||||
matrix:
|
||||
project: ["homepage/homepage"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup Source Code
|
||||
uses: ./.github/actions/source-code/
|
||||
|
||||
- name: Install project dependencies
|
||||
run: pnpm install
|
||||
working-directory: ./${{ matrix.project }}
|
||||
|
||||
- name: Pnpm Build
|
||||
run: pnpm turbo build
|
||||
working-directory: ./${{ matrix.project }}
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
run: pnpm exec playwright install
|
||||
working-directory: ./${{ matrix.project }}
|
||||
|
||||
- name: Run Playwright tests
|
||||
run: pnpm exec playwright test
|
||||
working-directory: ./${{ matrix.project }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: ${{ hashFiles(format('{0}/package.json', matrix.project)) }}-playwright-report
|
||||
path: ./${{ matrix.project }}/playwright-report/
|
||||
retention-days: 30
|
||||
2
.github/workflows/playwright.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
project: ["tests/e2e", "examples/chat", "examples/clerk", "examples/betterauth", "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
|
||||
|
||||
5
.gitignore
vendored
@@ -20,6 +20,9 @@ __screenshots__
|
||||
# Playwright
|
||||
test-results
|
||||
|
||||
# Java
|
||||
.java-version
|
||||
|
||||
.husky
|
||||
|
||||
.vscode/*
|
||||
@@ -29,3 +32,5 @@ test-results
|
||||
|
||||
.cursorrules
|
||||
.windsurfrules
|
||||
|
||||
playwright-report
|
||||
@@ -64,7 +64,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"include": ["packages/**/src/tests/**"],
|
||||
"include": ["**/tests/**"],
|
||||
"linter": {
|
||||
"rules": {
|
||||
"correctness": {
|
||||
|
||||
@@ -1 +1 @@
|
||||
BETTER_AUTH_SECRET="TEST_SECRET"
|
||||
BETTER_AUTH_SECRET="TEST_SECRET"
|
||||
@@ -1,5 +1,278 @@
|
||||
# betterauth
|
||||
|
||||
## 0.1.24
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e7e505e]
|
||||
- Updated dependencies [13b57aa]
|
||||
- Updated dependencies [5662faa]
|
||||
- Updated dependencies [2116a59]
|
||||
- jazz-tools@0.14.21
|
||||
- jazz-betterauth-server-plugin@0.14.21
|
||||
- jazz-inspector@0.14.21
|
||||
- jazz-react@0.14.21
|
||||
- jazz-react-auth-betterauth@0.14.21
|
||||
- jazz-betterauth-client-plugin@0.14.21
|
||||
|
||||
## 0.1.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6f72419]
|
||||
- Updated dependencies [04b20c2]
|
||||
- jazz-tools@0.14.20
|
||||
- jazz-betterauth-server-plugin@0.14.20
|
||||
- jazz-inspector@0.14.20
|
||||
- jazz-react@0.14.20
|
||||
- jazz-react-auth-betterauth@0.14.20
|
||||
- jazz-betterauth-client-plugin@0.14.20
|
||||
|
||||
## 0.1.22
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-betterauth-client-plugin@0.14.19
|
||||
- jazz-betterauth-server-plugin@0.14.19
|
||||
- jazz-react-auth-betterauth@0.14.19
|
||||
- jazz-inspector@0.14.19
|
||||
- jazz-react@0.14.19
|
||||
- jazz-tools@0.14.19
|
||||
|
||||
## 0.1.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4b950bc]
|
||||
- Updated dependencies [d6d9c0a]
|
||||
- Updated dependencies [c559054]
|
||||
- jazz-tools@0.14.18
|
||||
- jazz-betterauth-server-plugin@0.14.18
|
||||
- jazz-inspector@0.14.18
|
||||
- jazz-react@0.14.18
|
||||
- jazz-react-auth-betterauth@0.14.18
|
||||
- jazz-betterauth-client-plugin@0.14.18
|
||||
|
||||
## 0.1.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e512df4]
|
||||
- jazz-betterauth-server-plugin@0.14.17
|
||||
- jazz-tools@0.14.17
|
||||
- jazz-betterauth-client-plugin@0.14.17
|
||||
- jazz-inspector@0.14.17
|
||||
- jazz-react@0.14.17
|
||||
- jazz-react-auth-betterauth@0.14.17
|
||||
|
||||
## 0.1.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-betterauth-server-plugin@0.14.16
|
||||
- jazz-inspector@0.14.16
|
||||
- jazz-react@0.14.16
|
||||
- jazz-react-auth-betterauth@0.14.16
|
||||
- jazz-tools@0.14.16
|
||||
- jazz-betterauth-client-plugin@0.14.16
|
||||
|
||||
## 0.1.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [f9590f9]
|
||||
- jazz-react@0.14.15
|
||||
- jazz-betterauth-server-plugin@0.14.15
|
||||
- jazz-inspector@0.14.15
|
||||
- jazz-react-auth-betterauth@0.14.15
|
||||
- jazz-tools@0.14.15
|
||||
- jazz-betterauth-client-plugin@0.14.15
|
||||
|
||||
## 0.1.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e32a1f7]
|
||||
- jazz-tools@0.14.14
|
||||
- jazz-betterauth-server-plugin@0.14.14
|
||||
- jazz-inspector@0.14.14
|
||||
- jazz-react@0.14.14
|
||||
- jazz-react-auth-betterauth@0.14.14
|
||||
- jazz-betterauth-client-plugin@0.14.14
|
||||
|
||||
## 0.1.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-inspector@0.14.13
|
||||
- jazz-react@0.14.13
|
||||
- jazz-react-auth-betterauth@0.14.13
|
||||
|
||||
## 0.1.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-inspector@0.14.12
|
||||
- jazz-react@0.14.12
|
||||
- jazz-react-auth-betterauth@0.14.12
|
||||
|
||||
## 0.1.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [dc746a2]
|
||||
- Updated dependencies [f869d9a]
|
||||
- Updated dependencies [3fe6832]
|
||||
- jazz-react-auth-betterauth@0.14.10
|
||||
- jazz-inspector@0.14.10
|
||||
- jazz-react@0.14.10
|
||||
- jazz-tools@0.14.10
|
||||
- jazz-betterauth-server-plugin@0.14.10
|
||||
- jazz-betterauth-client-plugin@0.14.10
|
||||
|
||||
## 0.1.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [22c2600]
|
||||
- jazz-tools@0.14.9
|
||||
- jazz-betterauth-server-plugin@0.14.9
|
||||
- jazz-inspector@0.14.9
|
||||
- jazz-react@0.14.9
|
||||
- jazz-react-auth-betterauth@0.14.9
|
||||
- jazz-betterauth-client-plugin@0.14.9
|
||||
|
||||
## 0.1.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [637ae13]
|
||||
- jazz-tools@0.14.8
|
||||
- jazz-betterauth-server-plugin@0.14.8
|
||||
- jazz-inspector@0.14.8
|
||||
- jazz-react@0.14.8
|
||||
- jazz-react-auth-betterauth@0.14.8
|
||||
- jazz-betterauth-client-plugin@0.14.8
|
||||
|
||||
## 0.1.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [365b0ea]
|
||||
- jazz-tools@0.14.7
|
||||
- jazz-betterauth-server-plugin@0.14.7
|
||||
- jazz-inspector@0.14.7
|
||||
- jazz-react@0.14.7
|
||||
- jazz-react-auth-betterauth@0.14.7
|
||||
- jazz-betterauth-client-plugin@0.14.7
|
||||
|
||||
## 0.1.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9d6d9fe]
|
||||
- Updated dependencies [9d6d9fe]
|
||||
- jazz-tools@0.14.6
|
||||
- jazz-betterauth-server-plugin@0.14.6
|
||||
- jazz-inspector@0.14.6
|
||||
- jazz-react@0.14.6
|
||||
- jazz-react-auth-betterauth@0.14.6
|
||||
- jazz-betterauth-client-plugin@0.14.6
|
||||
|
||||
## 0.1.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [91cbb2f]
|
||||
- Updated dependencies [20b3d88]
|
||||
- jazz-tools@0.14.5
|
||||
- jazz-betterauth-server-plugin@0.14.5
|
||||
- jazz-inspector@0.14.5
|
||||
- jazz-react@0.14.5
|
||||
- jazz-react-auth-betterauth@0.14.5
|
||||
- jazz-betterauth-client-plugin@0.14.5
|
||||
|
||||
## 0.1.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [011af55]
|
||||
- jazz-tools@0.14.4
|
||||
- jazz-betterauth-server-plugin@0.14.4
|
||||
- jazz-inspector@0.14.4
|
||||
- jazz-react@0.14.4
|
||||
- jazz-react-auth-betterauth@0.14.4
|
||||
- jazz-betterauth-client-plugin@0.14.4
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Better Auth Integration Example
|
||||
|
||||
This example demonstrates using Jazz with Better Auth and Next.js.
|
||||
This example demonstrates how to integrate [Better Auth](https://www.better-auth.com/) with Jazz.
|
||||
|
||||
## Getting started
|
||||
|
||||
@@ -29,21 +29,26 @@ pnpm dev
|
||||
|
||||
### Using the monorepo
|
||||
|
||||
This requires `pnpm` to be installed; see [https://pnpm.io/installation](https://pnpm.io/installation).
|
||||
This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation).
|
||||
|
||||
1. Clone the `jazz` repository.
|
||||
```sh
|
||||
Clone the jazz repository.
|
||||
```bash
|
||||
git clone https://github.com/garden-co/jazz.git
|
||||
```
|
||||
2. Install dependencies.
|
||||
```sh
|
||||
cd jazz
|
||||
pnpm install
|
||||
|
||||
Install and build dependencies.
|
||||
```bash
|
||||
pnpm i && npx turbo build
|
||||
```
|
||||
3. Navigate to the example and start the development server.
|
||||
```sh
|
||||
cd examples/betterauth
|
||||
|
||||
Go to the example directory.
|
||||
```bash
|
||||
cd jazz/examples/betterauth/
|
||||
```
|
||||
|
||||
Start the dev server.
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
The example should be running at [http://localhost:3000](http://localhost:3000) by default.
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
21
examples/betterauth/components.json
Normal 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"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "betterauth",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.24",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -15,18 +15,26 @@
|
||||
"email": "email dev --dir src/components/emails"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-email/components": "^0.0.38",
|
||||
"@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",
|
||||
"jazz-betterauth-server-plugin": "workspace:*",
|
||||
"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:*",
|
||||
"next": "15.3.1",
|
||||
"lucide-react": "^0.510.0",
|
||||
"next": "15.3.2",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^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",
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
||||
|
Before Width: | Height: | Size: 391 B |
@@ -1 +0,0 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1,17 +1,11 @@
|
||||
<svg
|
||||
viewBox="0 0 386 146"
|
||||
fill="none"
|
||||
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"
|
||||
/>
|
||||
<style>
|
||||
path#text { fill: #000 }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path#text { fill: #fff }
|
||||
}
|
||||
</style>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.2 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#666" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link-icon lucide-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
|
||||
|
Before Width: | Height: | Size: 344 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#666" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-mail-icon lucide-mail"><path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7"/><rect x="2" y="4" width="20" height="16" rx="2"/></svg>
|
||||
|
Before Width: | Height: | Size: 301 B |
@@ -1 +0,0 @@
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
|
||||
|
Before Width: | Height: | Size: 823 B |
@@ -1 +0,0 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
||||
|
Before Width: | Height: | Size: 385 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#666" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-wrench-icon lucide-wrench"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>
|
||||
|
Before Width: | Height: | Size: 369 B |
22
examples/betterauth/src/app/(app)/layout.tsx
Normal 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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
113
examples/betterauth/src/app/(app)/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
14
examples/betterauth/src/app/(app)/settings/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
10
examples/betterauth/src/app/auth/forgot-password/page.tsx
Normal 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 />;
|
||||
}
|
||||
23
examples/betterauth/src/app/auth/layout.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
10
examples/betterauth/src/app/auth/reset-password/page.tsx
Normal 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 />;
|
||||
}
|
||||
11
examples/betterauth/src/app/auth/sign-in/page.tsx
Normal 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} />;
|
||||
}
|
||||
11
examples/betterauth/src/app/auth/sign-up/page.tsx
Normal 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} />;
|
||||
}
|
||||
4
examples/betterauth/src/app/auth/sso-providers.ts
Normal 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"];
|
||||
@@ -1,10 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useAccount } from "jazz-react";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default function Page() {
|
||||
const { logOut } = useAccount({ resolve: { profile: true } });
|
||||
logOut();
|
||||
redirect("/");
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import ForgotForm from "@/components/forms/Forgot";
|
||||
|
||||
export default function ForgotPage() {
|
||||
return <ForgotForm />;
|
||||
}
|
||||
@@ -1,27 +1,123 @@
|
||||
@import "tailwindcss";
|
||||
@import "tailwindcss/utilities";
|
||||
@import "tw-animate-css";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
--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);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
--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);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { JazzAndAuth } from "@/components/JazzAndAuth";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import type { Metadata } from "next";
|
||||
import "./globals.css";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Jazz Example: Better Auth",
|
||||
@@ -20,15 +10,16 @@ export const metadata: Metadata = {
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
<JazzAndAuth>{children}</JazzAndAuth>
|
||||
<body className="antialiased">
|
||||
<JazzAndAuth>
|
||||
{children}
|
||||
<Toaster richColors />
|
||||
</JazzAndAuth>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import MagicLinkSignIn from "@/components/routes/magic-link/signIn/page";
|
||||
|
||||
export default MagicLinkSignIn;
|
||||
@@ -1,5 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import MagicLinkSignUp from "@/components/routes/magic-link/logIn/page";
|
||||
|
||||
export default MagicLinkSignUp;
|
||||
@@ -1,11 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Verify } from "@/components/emails";
|
||||
|
||||
export default function Page() {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const name = searchParams.get("name") ?? undefined;
|
||||
const url = searchParams.get("url") ?? undefined;
|
||||
const otp = searchParams.get("otp") ?? undefined;
|
||||
return <Verify name={name} url={url} otp={otp}></Verify>;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Reset } from "@/components/emails";
|
||||
|
||||
export default function Page() {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const name = searchParams.get("name") ?? undefined;
|
||||
const url = searchParams.get("url") ?? undefined;
|
||||
const otp = searchParams.get("otp") ?? undefined;
|
||||
return <Reset name={name} url={url} otp={otp}></Reset>;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Login } from "@/components/emails";
|
||||
|
||||
export default function Page() {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const name = searchParams.get("name") ?? undefined;
|
||||
const url = searchParams.get("url") ?? undefined;
|
||||
const otp = searchParams.get("otp") ?? undefined;
|
||||
return <Login name={name} url={url} otp={otp}></Login>;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Welcome } from "@/components/emails";
|
||||
|
||||
export default function Page() {
|
||||
return <Welcome></Welcome>;
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/components/Button";
|
||||
import { useAccount, useIsAuthenticated } from "jazz-react";
|
||||
import { useAuth } from "jazz-react-auth-betterauth";
|
||||
import Image from "next/image";
|
||||
import { useCallback } from "react";
|
||||
|
||||
export default function Home() {
|
||||
const { authClient, account, state } = useAuth();
|
||||
const hasCredentials = state !== "anonymous";
|
||||
const { me, logOut } = useAccount({ resolve: { profile: {} } });
|
||||
const isAuthenticated = useIsAuthenticated();
|
||||
const signOut = useCallback(() => {
|
||||
authClient.signOut().catch(console.error).finally(logOut);
|
||||
}, [logOut, authClient]);
|
||||
console.log("me", me);
|
||||
console.log("account", account);
|
||||
console.log("state", state);
|
||||
console.log("hasCredentials", hasCredentials);
|
||||
console.log("isAuthenticated", isAuthenticated);
|
||||
|
||||
return (
|
||||
<>
|
||||
<header className="absolute p-4 top-0 left-0 w-full z-10 flex items-center justify-between gap-4">
|
||||
<div className="float-start flex gap-4">
|
||||
{me && hasCredentials && isAuthenticated && (
|
||||
<>
|
||||
<Button onClick={signOut}>Sign out</Button>
|
||||
<Button href="/settings">Settings</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="float-end flex gap-4">
|
||||
{!hasCredentials && !isAuthenticated && (
|
||||
<>
|
||||
<Button href="/sign-in" variant="secondary">
|
||||
Sign in
|
||||
</Button>
|
||||
<Button href="/sign-up">Sign up</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
||||
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
|
||||
<Image
|
||||
src="/jazz-logo.svg"
|
||||
alt="Jazz logo"
|
||||
width={180}
|
||||
height={38}
|
||||
priority
|
||||
/>
|
||||
<p className="text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
||||
{me && hasCredentials && isAuthenticated && (
|
||||
<>
|
||||
{"Signed in as "}
|
||||
<span className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
|
||||
{me.profile.name}
|
||||
</span>
|
||||
.
|
||||
</>
|
||||
)}
|
||||
{!hasCredentials && !isAuthenticated && <>Not signed in.</>}
|
||||
{!hasCredentials && isAuthenticated && (
|
||||
<>Not connected to the authentication server.</>
|
||||
)}
|
||||
{hasCredentials && !isAuthenticated && (
|
||||
<>Authenticated, but not logged in. Try refreshing.</>
|
||||
)}
|
||||
</p>
|
||||
|
||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
||||
<a
|
||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
|
||||
href="https://jazz.tools/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image src="/jazz.svg" alt="Jazz logo" width={20} height={20} />
|
||||
Start building
|
||||
</a>
|
||||
<a
|
||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center gap-2 hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto"
|
||||
href="https://jazz.tools/docs"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/file.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Read the docs
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://jazz.tools/api-reference"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/file.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
API reference
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://jazz.tools/examples"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/window.svg"
|
||||
alt="Window icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Examples
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://jazz.tools/status"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/globe.svg"
|
||||
alt="Globe icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Status
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://jazz.tools/showcase"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/wrench.svg"
|
||||
alt="Wrench icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Built with Jazz
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { auth } from "@/lib/auth";
|
||||
import { toNodeHandler } from "better-auth/node";
|
||||
|
||||
// Disallow body parsing, we will parse it manually
|
||||
export const config = { api: { bodyParser: false } };
|
||||
|
||||
export default toNodeHandler(auth.handler);
|
||||
@@ -1,7 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import ResetForm from "@/components/forms/Reset";
|
||||
|
||||
export default function ResetPage() {
|
||||
return <ResetForm />;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import SettingsForm from "@/components/forms/Settings";
|
||||
|
||||
export default function SettingsPage() {
|
||||
return <SettingsForm providers={["github"]} />;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import SignInForm from "@/components/forms/SignIn";
|
||||
|
||||
export default function SignInPage() {
|
||||
return <SignInForm providers={["github"]} />;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import SignUpForm from "@/components/forms/SignUp";
|
||||
|
||||
export default function SignUpPage() {
|
||||
return <SignUpForm providers={["github"]} />;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useAuth } from "jazz-react-auth-betterauth";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default function Page() {
|
||||
const { logIn } = useAuth();
|
||||
logIn().then(redirect("/"));
|
||||
return null;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useAuth } from "jazz-react-auth-betterauth";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default function Page() {
|
||||
const { signIn } = useAuth();
|
||||
signIn().then(redirect("/"));
|
||||
return null;
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
import { Button } from "@/components/Button";
|
||||
import { AccountsType, useAuth } from "jazz-react-auth-betterauth";
|
||||
|
||||
export const AccountProviders = ({
|
||||
setLoading,
|
||||
setError,
|
||||
accounts,
|
||||
}: {
|
||||
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setError: React.Dispatch<React.SetStateAction<Error | undefined>>;
|
||||
accounts: AccountsType | undefined;
|
||||
}) => {
|
||||
const auth = useAuth();
|
||||
return (
|
||||
<table className="w-full text-sm border-full border-collapse">
|
||||
<thead className="text-xs">
|
||||
<tr>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
Provider
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
Created
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
Updated
|
||||
</th>
|
||||
<th scope="col" className="px-6 py-3">
|
||||
Scopes
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{!accounts?.data?.length && "No authentication providers found"}
|
||||
{accounts?.data &&
|
||||
accounts.data.map((account) => (
|
||||
<tr key={account.id} className="border-b">
|
||||
<th
|
||||
scope="row"
|
||||
className="px-6 py-4 font-medium whitespace-nowrap"
|
||||
>
|
||||
{account.provider}
|
||||
</th>
|
||||
<td className="px-6 py-4">
|
||||
{account.createdAt.toLocaleString()}
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
{account.updatedAt.toLocaleString()}
|
||||
</td>
|
||||
<td className="px-6 py-4">{account.scopes.join(", ")}</td>
|
||||
<td className="px-6 py-4">
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="relative"
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const { error } = await auth.authClient.unlinkAccount({
|
||||
providerId: account.provider,
|
||||
accountId: account.id,
|
||||
});
|
||||
const errorMessage = error?.message ?? error?.statusText;
|
||||
setError(
|
||||
error
|
||||
? {
|
||||
...error,
|
||||
name: error.statusText,
|
||||
message:
|
||||
errorMessage && errorMessage.length > 0
|
||||
? errorMessage
|
||||
: "An error occurred",
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
Unlink
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
};
|
||||
@@ -1,92 +0,0 @@
|
||||
import { clsx } from "clsx";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { forwardRef } from "react";
|
||||
|
||||
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
variant?: "primary" | "secondary" | "danger";
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
src?: string;
|
||||
alt?: string;
|
||||
imageClassName?: string;
|
||||
href?: string;
|
||||
newTab?: boolean;
|
||||
}
|
||||
|
||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
(
|
||||
{
|
||||
className,
|
||||
children,
|
||||
variant = "primary",
|
||||
src,
|
||||
alt,
|
||||
imageClassName,
|
||||
href,
|
||||
newTab = false,
|
||||
...buttonProps
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const primary =
|
||||
"rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto";
|
||||
const secondary =
|
||||
"rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center gap-2 hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto";
|
||||
const danger =
|
||||
"rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center bg-red-400 dark:bg-red-600 gap-2 hover:bg-red-300 dark:hover:bg-red-700 hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto";
|
||||
const classes = clsx(
|
||||
variant === "primary"
|
||||
? primary
|
||||
: variant === "secondary"
|
||||
? secondary
|
||||
: danger,
|
||||
className,
|
||||
);
|
||||
const [width, height] = [
|
||||
variant === "primary" ? 20 : 16,
|
||||
variant === "primary" ? 20 : 16,
|
||||
];
|
||||
if (href) {
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
target={newTab ? "_blank" : undefined}
|
||||
className={classes}
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{src && alt && (
|
||||
<Image
|
||||
className={imageClassName}
|
||||
src={src}
|
||||
alt={alt}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
)}
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
{...buttonProps}
|
||||
className={classes}
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{src && alt && (
|
||||
<Image
|
||||
className={imageClassName}
|
||||
src={src}
|
||||
alt={alt}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
)}
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -1,56 +0,0 @@
|
||||
import { Button } from "@/components/Button";
|
||||
import { useAuth } from "jazz-react-auth-betterauth";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { forwardRef } from "react";
|
||||
|
||||
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
children?: React.ReactNode;
|
||||
src?: InstanceType<typeof Image>["src"];
|
||||
alt?: InstanceType<typeof Image>["alt"];
|
||||
callbackURL: string;
|
||||
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setError: React.Dispatch<React.SetStateAction<Error | undefined>>;
|
||||
}
|
||||
|
||||
export const DeleteAccountButton = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ callbackURL, setLoading, setError }) => {
|
||||
const router = useRouter();
|
||||
const auth = useAuth();
|
||||
return (
|
||||
<Button
|
||||
variant="danger"
|
||||
className="relative"
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const { error } = await auth.authClient.deleteUser(
|
||||
{
|
||||
callbackURL: callbackURL,
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
router.replace(callbackURL);
|
||||
},
|
||||
},
|
||||
);
|
||||
const errorMessage = error?.message ?? error?.statusText;
|
||||
setError(
|
||||
error
|
||||
? {
|
||||
...error,
|
||||
name: error.statusText,
|
||||
message:
|
||||
errorMessage && errorMessage.length > 0
|
||||
? errorMessage
|
||||
: "An error occurred",
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
Delete account
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { emailOTPClient, magicLinkClient } from "better-auth/client/plugins";
|
||||
import { JazzProvider } from "jazz-react";
|
||||
import { AuthProvider } from "jazz-react-auth-betterauth";
|
||||
import { type ReactNode, lazy } from "react";
|
||||
@@ -21,17 +20,14 @@ export function JazzAndAuth({ children }: { children: ReactNode }) {
|
||||
peer: "wss://cloud.jazz.tools/?key=betterauth-example@garden.co",
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<AuthProvider
|
||||
options={{
|
||||
baseURL: process.env.NEXT_PUBLIC_AUTH_BASE_URL,
|
||||
plugins: [magicLinkClient(), emailOTPClient()],
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AuthProvider>
|
||||
<JazzDevTools />
|
||||
</>
|
||||
<AuthProvider
|
||||
options={{
|
||||
baseURL: process.env.NEXT_PUBLIC_AUTH_BASE_URL,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AuthProvider>
|
||||
<JazzDevTools />
|
||||
</JazzProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export function Loading() {
|
||||
return (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,73 +1,137 @@
|
||||
import { Button } from "@/components/Button";
|
||||
import { SSOProviderType, useAuth } from "jazz-react-auth-betterauth";
|
||||
import { socialProviderNames } from "jazz-react-auth-betterauth";
|
||||
import { forwardRef } from "react";
|
||||
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 ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
children?: React.ReactNode;
|
||||
src?: InstanceType<typeof Image>["src"];
|
||||
alt?: InstanceType<typeof Image>["alt"];
|
||||
provider: SSOProviderType;
|
||||
link?: boolean;
|
||||
callbackURL?: string;
|
||||
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setError: React.Dispatch<React.SetStateAction<Error | undefined>>;
|
||||
interface SocialProvider {
|
||||
name: string;
|
||||
icon?: ReactNode;
|
||||
}
|
||||
|
||||
export const SSOButton = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
(
|
||||
{
|
||||
children,
|
||||
provider,
|
||||
link = false,
|
||||
callbackURL,
|
||||
setLoading,
|
||||
setError,
|
||||
...buttonProps
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const auth = useAuth();
|
||||
const providerName = socialProviderNames[provider];
|
||||
return (
|
||||
<Button
|
||||
src={`/social/${provider}.svg`}
|
||||
alt={`${providerName} logo`}
|
||||
imageClassName="absolute left-3 dark:invert"
|
||||
variant="secondary"
|
||||
className="relative"
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const { error } = await (async () => {
|
||||
if (link) {
|
||||
return await auth.authClient.linkSocial({
|
||||
provider: provider,
|
||||
});
|
||||
} else {
|
||||
return await auth.authClient.signIn.social({
|
||||
provider: provider,
|
||||
callbackURL: callbackURL,
|
||||
});
|
||||
}
|
||||
})();
|
||||
if (error) {
|
||||
setError({
|
||||
...error,
|
||||
name: error.message ?? error.statusText,
|
||||
message: error.message ?? error.statusText,
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
}}
|
||||
{...buttonProps}
|
||||
ref={ref}
|
||||
>
|
||||
{link
|
||||
? `Link ${providerName} account`
|
||||
: `Continue with ${providerName}`}
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
export const appName = "Jazz Example App";
|
||||
|
||||
export const main = {
|
||||
backgroundColor: "#f6f9fc",
|
||||
padding: "10px 0",
|
||||
};
|
||||
|
||||
export const container = {
|
||||
backgroundColor: "#ffffff",
|
||||
border: "1px solid #f0f0f0",
|
||||
padding: "45px",
|
||||
};
|
||||
|
||||
export const text = {
|
||||
fontSize: "16px",
|
||||
fontFamily:
|
||||
"'Open Sans', 'HelveticaNeue-Light', 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif",
|
||||
fontWeight: "300",
|
||||
color: "#404040",
|
||||
lineHeight: "26px",
|
||||
};
|
||||
|
||||
export const h1 = {
|
||||
...text,
|
||||
fontSize: "24px",
|
||||
fontWeight: "bold",
|
||||
};
|
||||
|
||||
export const button = {
|
||||
backgroundColor: "#146AFF",
|
||||
borderRadius: "4px",
|
||||
color: "#fff",
|
||||
fontFamily: "'Open Sans', 'Helvetica Neue', Arial",
|
||||
fontSize: "15px",
|
||||
textDecoration: "none",
|
||||
textAlign: "center" as const,
|
||||
display: "block",
|
||||
width: "210px",
|
||||
padding: "14px 7px",
|
||||
};
|
||||
|
||||
export const anchor = {
|
||||
textDecoration: "underline",
|
||||
};
|
||||
|
||||
export const codeContainer = {
|
||||
background: "rgba(0,0,0,.05)",
|
||||
borderRadius: "4px",
|
||||
margin: "16px auto 14px",
|
||||
verticalAlign: "middle",
|
||||
width: "280px",
|
||||
};
|
||||
|
||||
export const code = {
|
||||
color: "#000",
|
||||
fontFamily: "HelveticaNeue-Bold",
|
||||
fontSize: "32px",
|
||||
fontWeight: 700,
|
||||
letterSpacing: "6px",
|
||||
lineHeight: "40px",
|
||||
paddingBottom: "8px",
|
||||
paddingTop: "8px",
|
||||
margin: "0 auto",
|
||||
display: "block",
|
||||
textAlign: "center" as const,
|
||||
};
|
||||
@@ -1,91 +0,0 @@
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Container,
|
||||
Head,
|
||||
Heading,
|
||||
Html,
|
||||
Preview,
|
||||
Section,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import Image from "next/image";
|
||||
import * as React from "react";
|
||||
import {
|
||||
appName,
|
||||
button,
|
||||
code,
|
||||
codeContainer,
|
||||
container,
|
||||
h1,
|
||||
main,
|
||||
text,
|
||||
} from "./Common";
|
||||
|
||||
export const Login = ({
|
||||
name = undefined,
|
||||
url = undefined,
|
||||
otp = undefined,
|
||||
}: {
|
||||
name?: string;
|
||||
url?: string;
|
||||
otp?: string;
|
||||
}) => {
|
||||
return (
|
||||
<Html>
|
||||
<Head>
|
||||
<title>
|
||||
{url && !otp
|
||||
? `Your login link for ${appName}`
|
||||
: `Your login code for ${appName}`}
|
||||
</title>
|
||||
</Head>
|
||||
<Preview>
|
||||
{url && !otp
|
||||
? `Your login link for ${appName}`
|
||||
: `Your login code for ${appName}`}
|
||||
</Preview>
|
||||
<Body style={main}>
|
||||
<Container style={container}>
|
||||
<Image
|
||||
src="/jazz.svg"
|
||||
alt="Jazz logo"
|
||||
width={100}
|
||||
height={100}
|
||||
priority
|
||||
/>
|
||||
<Heading style={h1}>
|
||||
{url && !otp
|
||||
? `Your login link for ${appName}`
|
||||
: `Your login code for ${appName}`}
|
||||
</Heading>
|
||||
<Section>
|
||||
<Text style={text}>Hello{name ? ` ${name}` : ""},</Text>
|
||||
<Text style={text}>
|
||||
Someone recently attempted to login to {appName} using your email
|
||||
address.
|
||||
{url && !otp ? " If this was you, you can login here:" : ""}
|
||||
</Text>
|
||||
{url && !otp && (
|
||||
<Button style={button} href={url}>
|
||||
Login
|
||||
</Button>
|
||||
)}
|
||||
{otp && !url && (
|
||||
<Section style={codeContainer}>
|
||||
<Text style={code}>{otp}</Text>
|
||||
</Section>
|
||||
)}
|
||||
<Text style={text}>
|
||||
To keep your account secure, please do not forward this email to
|
||||
anyone. If you did not try to login, just ignore and delete this
|
||||
message.
|
||||
</Text>
|
||||
</Section>
|
||||
</Container>
|
||||
</Body>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export default Login;
|
||||
@@ -1,84 +0,0 @@
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Container,
|
||||
Head,
|
||||
Heading,
|
||||
Html,
|
||||
Preview,
|
||||
Section,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import Image from "next/image";
|
||||
import * as React from "react";
|
||||
import {
|
||||
appName,
|
||||
button,
|
||||
code,
|
||||
codeContainer,
|
||||
container,
|
||||
h1,
|
||||
main,
|
||||
text,
|
||||
} from "./Common";
|
||||
|
||||
export const Reset = ({
|
||||
name = undefined,
|
||||
url = undefined,
|
||||
otp = undefined,
|
||||
}: {
|
||||
name?: string;
|
||||
url?: string;
|
||||
otp?: string;
|
||||
}) => {
|
||||
return (
|
||||
<Html>
|
||||
<Head>
|
||||
<title>{`Reset your ${appName} password`}</title>
|
||||
</Head>
|
||||
<Preview>Reset your {appName} password</Preview>
|
||||
<Body style={main}>
|
||||
<Container style={container}>
|
||||
<Image
|
||||
src="/jazz.svg"
|
||||
alt="Jazz logo"
|
||||
width={100}
|
||||
height={100}
|
||||
priority
|
||||
/>
|
||||
<Heading style={h1}>Reset your {appName} password</Heading>
|
||||
<Section>
|
||||
<Text style={text}>Hello{name ? ` ${name}` : ""},</Text>
|
||||
<Text style={text}>
|
||||
Someone recently requested a password change for your {appName}{" "}
|
||||
account.
|
||||
{url && !otp
|
||||
? " If this was you, you can set a new password here:"
|
||||
: ""}
|
||||
</Text>
|
||||
{url && !otp && (
|
||||
<Button style={button} href={url}>
|
||||
Reset password
|
||||
</Button>
|
||||
)}
|
||||
{otp && !url && (
|
||||
<Section style={codeContainer}>
|
||||
<Text style={code}>{otp}</Text>
|
||||
</Section>
|
||||
)}
|
||||
<Text style={text}>
|
||||
If you do not want to change your password or did not request
|
||||
this, just ignore and delete this message.
|
||||
</Text>
|
||||
<Text style={text}>
|
||||
To keep your account secure, please do not forward this email to
|
||||
anyone.
|
||||
</Text>
|
||||
</Section>
|
||||
</Container>
|
||||
</Body>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export default Reset;
|
||||
@@ -1,80 +0,0 @@
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Container,
|
||||
Head,
|
||||
Heading,
|
||||
Html,
|
||||
Preview,
|
||||
Section,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import Image from "next/image";
|
||||
import * as React from "react";
|
||||
import {
|
||||
appName,
|
||||
button,
|
||||
code,
|
||||
codeContainer,
|
||||
container,
|
||||
h1,
|
||||
main,
|
||||
text,
|
||||
} from "./Common";
|
||||
|
||||
export const Verify = ({
|
||||
name = undefined,
|
||||
url = undefined,
|
||||
otp = undefined,
|
||||
}: {
|
||||
name?: string;
|
||||
url?: string;
|
||||
otp?: string;
|
||||
}) => {
|
||||
return (
|
||||
<Html>
|
||||
<Head>
|
||||
<title>{`Verify your ${appName} account`}</title>
|
||||
</Head>
|
||||
<Preview>Verify your {appName} account</Preview>
|
||||
<Body style={main}>
|
||||
<Container style={container}>
|
||||
<Image
|
||||
src="/jazz.svg"
|
||||
alt="Jazz logo"
|
||||
width={100}
|
||||
height={100}
|
||||
priority
|
||||
/>
|
||||
<Heading style={h1}>Verify your {appName} account</Heading>
|
||||
<Section>
|
||||
<Text style={text}>Hello{name ? ` ${name}` : ""},</Text>
|
||||
<Text style={text}>
|
||||
Someone recently signed up for a {appName} account using your
|
||||
email address.
|
||||
{url && !otp
|
||||
? " If this was you, you can verify your account here:"
|
||||
: ""}
|
||||
</Text>
|
||||
{url && !otp && (
|
||||
<Button style={button} href={url}>
|
||||
Verify account
|
||||
</Button>
|
||||
)}
|
||||
{otp && !url && (
|
||||
<Section style={codeContainer}>
|
||||
<Text style={code}>{otp}</Text>
|
||||
</Section>
|
||||
)}
|
||||
<Text style={text}>
|
||||
To keep your account secure, please do not forward this email to
|
||||
anyone.
|
||||
</Text>
|
||||
</Section>
|
||||
</Container>
|
||||
</Body>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export default Verify;
|
||||
@@ -1,53 +0,0 @@
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Container,
|
||||
Head,
|
||||
Heading,
|
||||
Html,
|
||||
Preview,
|
||||
Row,
|
||||
Section,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import Image from "next/image";
|
||||
import * as React from "react";
|
||||
import { appName, button, container, h1, main, text } from "./Common";
|
||||
|
||||
export const Welcome = () => {
|
||||
return (
|
||||
<Html>
|
||||
<Head>
|
||||
<title>{`Welcome to ${appName}`}</title>
|
||||
</Head>
|
||||
<Preview>Welcome to {appName}</Preview>
|
||||
<Body style={main}>
|
||||
<Container style={container}>
|
||||
<Image
|
||||
src="/jazz.svg"
|
||||
alt="Jazz logo"
|
||||
width={100}
|
||||
height={100}
|
||||
priority
|
||||
/>
|
||||
<Heading style={h1}>Welcome to {appName}</Heading>
|
||||
<Section>
|
||||
<Row>
|
||||
<Text style={text}>
|
||||
Congratulations! You have successfully registered for {appName}.
|
||||
We are excited to have you on board!
|
||||
</Text>
|
||||
</Row>
|
||||
</Section>
|
||||
<Section>
|
||||
<Button style={button} href="/">
|
||||
Go home
|
||||
</Button>
|
||||
</Section>
|
||||
</Container>
|
||||
</Body>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export default Welcome;
|
||||
@@ -1,5 +0,0 @@
|
||||
export * from "./Common";
|
||||
export * from "./Welcome";
|
||||
export * from "./Verify";
|
||||
export * from "./Reset";
|
||||
export * from "./Login";
|
||||
85
examples/betterauth/src/components/forgot-password-form.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
import { Button } from "@/components/Button";
|
||||
import { Loading } from "@/components/Loading";
|
||||
import { useAuth } from "jazz-react-auth-betterauth";
|
||||
import type { FullAuthClient } from "jazz-react-auth-betterauth";
|
||||
import { useState } from "react";
|
||||
|
||||
const title = "Forgot Password";
|
||||
|
||||
export default function ForgotForm() {
|
||||
const auth = useAuth();
|
||||
const [email, setEmail] = useState<string>("");
|
||||
const [otp, setOtp] = useState<string>("");
|
||||
const [otpSentStatus, setOtpSentStatus] = useState<boolean>(false);
|
||||
const [otpStatus, setOtpStatus] = useState<boolean>(false);
|
||||
const [password, setPassword] = useState<string>("");
|
||||
const [confirmPassword, setConfirmPassword] = useState<string>("");
|
||||
const [status, setStatus] = useState<boolean>(false);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [error, setError] = useState<Error | undefined>(undefined);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col justify-center">
|
||||
<div className="max-w-md flex flex-col gap-8 w-full px-6 py-12 mx-auto">
|
||||
<div>
|
||||
<h1 className="text-stone-950 dark:text-white font-display text-5xl lg:text-6xl font-medium tracking-tighter mb-2">
|
||||
{title}
|
||||
</h1>
|
||||
<p>
|
||||
Enter your email address, and we'll send you a link to reset your
|
||||
password.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{status && !otpStatus && (
|
||||
<div>
|
||||
Instructions to reset your password have been sent to {email}, if an
|
||||
account with that email address exists.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{otpStatus && (
|
||||
<div>
|
||||
Your password has been successfully reset. You may now log in.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && <div>{error.message}</div>}
|
||||
|
||||
{loading && <Loading />}
|
||||
|
||||
<form className="flex flex-col gap-6">
|
||||
<div>
|
||||
<label htmlFor="email-address">Email address</label>
|
||||
<input
|
||||
id="email-address"
|
||||
value={email}
|
||||
disabled={loading}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const { data, error } = await auth.authClient.forgetPassword({
|
||||
email: email,
|
||||
redirectTo: `${window.location.origin}/reset`,
|
||||
});
|
||||
setStatus(data?.status ?? false);
|
||||
const errorMessage = error?.message ?? error?.statusText;
|
||||
setError(
|
||||
error
|
||||
? {
|
||||
...error,
|
||||
name: error.statusText,
|
||||
message:
|
||||
errorMessage && errorMessage.length > 0
|
||||
? errorMessage
|
||||
: "An error occurred",
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
setLoading(false);
|
||||
}}
|
||||
disabled={loading}
|
||||
>
|
||||
Send recovery link
|
||||
</Button>
|
||||
<Button
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const { data, error } = await (
|
||||
auth.authClient as FullAuthClient
|
||||
).emailOtp.sendVerificationOtp({
|
||||
email: email,
|
||||
type: "forget-password",
|
||||
});
|
||||
setStatus(data?.success ?? false);
|
||||
setOtpSentStatus(data?.success ?? false);
|
||||
const errorMessage = error?.message ?? error?.statusText;
|
||||
setError(
|
||||
error
|
||||
? {
|
||||
...error,
|
||||
name: error.statusText,
|
||||
message:
|
||||
errorMessage && errorMessage.length > 0
|
||||
? errorMessage
|
||||
: "An error occurred",
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
setLoading(false);
|
||||
}}
|
||||
disabled={loading}
|
||||
>
|
||||
Send recovery one-time password
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
{otpSentStatus && (
|
||||
<form
|
||||
className="flex flex-col gap-6"
|
||||
onSubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
if (password !== confirmPassword) {
|
||||
setError(new Error("Passwords do not match"));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
const { data, error } = await (
|
||||
auth.authClient as FullAuthClient
|
||||
).emailOtp.resetPassword({
|
||||
email: email,
|
||||
otp: otp,
|
||||
password: password,
|
||||
});
|
||||
setStatus(data?.success ?? false);
|
||||
setOtpStatus(data?.success ?? false);
|
||||
const errorMessage = error?.message ?? error?.statusText;
|
||||
setError(
|
||||
error
|
||||
? {
|
||||
...error,
|
||||
name: error.statusText,
|
||||
message:
|
||||
errorMessage && errorMessage.length > 0
|
||||
? errorMessage
|
||||
: "An error occurred",
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<label htmlFor="otp">One-time password</label>
|
||||
<input
|
||||
id="otp"
|
||||
value={otp}
|
||||
disabled={loading}
|
||||
onChange={(e) => setOtp(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="password">Password</label>
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
value={password}
|
||||
disabled={loading}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="confirm-password">Confirm password</label>
|
||||
<input
|
||||
id="confirm-password"
|
||||
type="password"
|
||||
value={confirmPassword}
|
||||
disabled={loading}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button type={"submit"} disabled={loading}>
|
||||
Submit
|
||||
</Button>
|
||||
</form>
|
||||
)}
|
||||
|
||||
<Button href="/sign-in" disabled={loading}>
|
||||
Sign in
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
import { Button } from "@/components/Button";
|
||||
import { Loading } from "@/components/Loading";
|
||||
import { useAuth } from "jazz-react-auth-betterauth";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
|
||||
const title = "Reset Password";
|
||||
|
||||
export default function ResetForm() {
|
||||
const auth = useAuth();
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const token = searchParams.get("token");
|
||||
const initialError = searchParams.get("error");
|
||||
const [error, setError] = useState<Error | undefined>(
|
||||
initialError
|
||||
? {
|
||||
name: initialError,
|
||||
message: initialError,
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
const [password, setPassword] = useState<string>("");
|
||||
const [confirmPassword, setConfirmPassword] = useState<string>("");
|
||||
const [status, setStatus] = useState<boolean>(false);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col justify-center">
|
||||
<div className="max-w-md flex flex-col gap-8 w-full px-6 py-12 mx-auto">
|
||||
<div>
|
||||
<h1 className="text-stone-950 dark:text-white font-display text-5xl lg:text-6xl font-medium tracking-tighter mb-2">
|
||||
{title}
|
||||
</h1>
|
||||
<p>Enter your new password.</p>
|
||||
</div>
|
||||
|
||||
{status && (
|
||||
<div>
|
||||
Your password has been reset. You may now{" "}
|
||||
<Link href="/sign-in">sign in</Link>.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{error && <div>{error.message}</div>}
|
||||
|
||||
{loading && <Loading />}
|
||||
|
||||
<form
|
||||
className="flex flex-col gap-6"
|
||||
onSubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
if (password !== confirmPassword) {
|
||||
setError(new Error("Passwords do not match"));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
if (!token) {
|
||||
setError(new Error("No password reset token provided"));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
const { data, error } = await auth.authClient.resetPassword({
|
||||
newPassword: password,
|
||||
token,
|
||||
});
|
||||
setStatus(data?.status ?? false);
|
||||
const errorMessage = error?.message ?? error?.statusText;
|
||||
setError(
|
||||
error
|
||||
? {
|
||||
...error,
|
||||
name: error.statusText,
|
||||
message:
|
||||
errorMessage && errorMessage.length > 0
|
||||
? errorMessage
|
||||
: "An error occurred",
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<label htmlFor="password">Password</label>
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
value={password}
|
||||
disabled={loading}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="confirm-password">Confirm password</label>
|
||||
<input
|
||||
id="confirm-password"
|
||||
type="password"
|
||||
value={confirmPassword}
|
||||
disabled={loading}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" disabled={loading}>
|
||||
Reset password
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<Button href="/sign-in" disabled={loading}>
|
||||
Sign in
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
import { AccountProviders } from "@/components/AccountProviders";
|
||||
import { Button } from "@/components/Button";
|
||||
import { DeleteAccountButton } from "@/components/DeleteAccountButton";
|
||||
import { Loading } from "@/components/Loading";
|
||||
import { SSOButton } from "@/components/SSOButton";
|
||||
import { useAccount, useIsAuthenticated } from "jazz-react";
|
||||
import { useAuth } from "jazz-react-auth-betterauth";
|
||||
import type {
|
||||
AccountsType,
|
||||
FullAuthClient,
|
||||
SSOProviderType,
|
||||
} from "jazz-react-auth-betterauth";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
const title = "Settings";
|
||||
|
||||
export default function SettingsForm({
|
||||
providers,
|
||||
}: {
|
||||
providers?: SSOProviderType[];
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const { authClient, account, state } = useAuth();
|
||||
const hasCredentials = state !== "anonymous";
|
||||
|
||||
const [accounts, setAccounts] = useState<AccountsType | undefined>(undefined);
|
||||
useEffect(() => {
|
||||
return authClient.useSession.subscribe(() => {
|
||||
authClient.listAccounts().then((x) => setAccounts(x));
|
||||
});
|
||||
}, [authClient]);
|
||||
|
||||
const [status, setStatus] = useState<boolean>(false);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [error, setError] = useState<Error | undefined>(undefined);
|
||||
const [otpSentStatus, setOtpSentStatus] = useState<boolean>(false);
|
||||
const [otpStatus, setOtpStatus] = useState<boolean>(false);
|
||||
const [otp, setOtp] = useState<string>("");
|
||||
|
||||
const { me, logOut } = useAccount({ resolve: { profile: true } });
|
||||
const isAuthenticated = useIsAuthenticated();
|
||||
const signOut = useCallback(() => {
|
||||
authClient
|
||||
.signOut()
|
||||
.catch(console.error)
|
||||
.finally(() => {
|
||||
logOut();
|
||||
router.push("/");
|
||||
});
|
||||
}, [logOut, authClient]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<header className="absolute p-4 top-0 left-0 w-full z-10 flex items-center justify-between gap-4">
|
||||
<div className="float-start">
|
||||
{me && hasCredentials && account && isAuthenticated && (
|
||||
<Button className="float-start" onClick={signOut}>
|
||||
Sign out
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
<div className="min-h-screen flex flex-col justify-center font-[family-name:var(--font-geist-sans)]">
|
||||
<div className="max-w-md flex flex-col gap-8 w-full px-6 py-12 mx-auto">
|
||||
<h1 className="text-stone-950 dark:text-white font-display text-5xl lg:text-6xl font-medium tracking-tighter mb-2">
|
||||
{title}
|
||||
</h1>
|
||||
|
||||
{status && account && !account?.emailVerified && (
|
||||
<div>
|
||||
Instructions to verify your account have been sent to{" "}
|
||||
{account.email}, if an account with that email address exists.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(status || otpStatus) && account && account.emailVerified && (
|
||||
<div>Your account has been successfully verified.</div>
|
||||
)}
|
||||
|
||||
{error && <div>{error.message}</div>}
|
||||
|
||||
{loading && <Loading />}
|
||||
|
||||
<AccountProviders
|
||||
accounts={accounts}
|
||||
setLoading={setLoading}
|
||||
setError={setError}
|
||||
/>
|
||||
{accounts?.data &&
|
||||
providers?.map((x) => {
|
||||
return (
|
||||
accounts.data.find((y) => y.provider === x) === undefined && (
|
||||
<SSOButton
|
||||
link={true}
|
||||
provider={x}
|
||||
setLoading={setLoading}
|
||||
setError={setError}
|
||||
/>
|
||||
)
|
||||
);
|
||||
})}
|
||||
{account && account.emailVerified && <p>Account verified.</p>}
|
||||
{account && !account.emailVerified && (
|
||||
<>
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="relative"
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const { data, error } =
|
||||
await authClient.sendVerificationEmail({
|
||||
email: account.email,
|
||||
callbackURL: `${window.location.origin}`,
|
||||
});
|
||||
setStatus(data?.status ?? false);
|
||||
const errorMessage = error?.message ?? error?.statusText;
|
||||
setError(
|
||||
error
|
||||
? {
|
||||
...error,
|
||||
name: error.statusText,
|
||||
message:
|
||||
errorMessage && errorMessage.length > 0
|
||||
? errorMessage
|
||||
: "An error occurred",
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
Send verification link
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="relative"
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const { data, error } = await (
|
||||
authClient as FullAuthClient
|
||||
).emailOtp.sendVerificationOtp({
|
||||
email: account.email,
|
||||
type: "email-verification",
|
||||
});
|
||||
setStatus(data?.success ?? false);
|
||||
setOtpSentStatus(data?.success ?? false);
|
||||
const errorMessage = error?.message ?? error?.statusText;
|
||||
setError(
|
||||
error
|
||||
? {
|
||||
...error,
|
||||
name: error.statusText,
|
||||
message:
|
||||
errorMessage && errorMessage.length > 0
|
||||
? errorMessage
|
||||
: "An error occurred",
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
Send verification one-time password
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{otpSentStatus && account && !account.emailVerified && (
|
||||
<form
|
||||
className="flex flex-col gap-6"
|
||||
onSubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const { data, error } = await (
|
||||
authClient as FullAuthClient
|
||||
).emailOtp.verifyEmail({
|
||||
email: account.email,
|
||||
otp: otp,
|
||||
});
|
||||
setOtpStatus(data?.status ?? false);
|
||||
const errorMessage = error?.message ?? error?.statusText;
|
||||
setError(
|
||||
error
|
||||
? {
|
||||
...error,
|
||||
name: error.statusText,
|
||||
message:
|
||||
errorMessage && errorMessage.length > 0
|
||||
? errorMessage
|
||||
: "An error occurred",
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<label htmlFor="otp">One-time password</label>
|
||||
<input
|
||||
id="otp"
|
||||
value={otp}
|
||||
disabled={loading}
|
||||
onChange={(e) => setOtp(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Button type={"submit"} disabled={loading}>
|
||||
Submit
|
||||
</Button>
|
||||
</form>
|
||||
)}
|
||||
<DeleteAccountButton
|
||||
setLoading={setLoading}
|
||||
setError={setError}
|
||||
callbackURL={`${window.location.origin}/delete-account`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
import { Button } from "@/components/Button";
|
||||
import { Loading } from "@/components/Loading";
|
||||
import { SSOButton } from "@/components/SSOButton";
|
||||
import { useAuth } from "jazz-react-auth-betterauth";
|
||||
import type {
|
||||
FullAuthClient,
|
||||
SSOProviderType,
|
||||
} from "jazz-react-auth-betterauth";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
|
||||
const title = "Sign In";
|
||||
|
||||
export default function SignInForm({
|
||||
providers,
|
||||
}: {
|
||||
providers?: SSOProviderType[];
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const auth = useAuth();
|
||||
const [email, setEmail] = useState<string>("");
|
||||
const [password, setPassword] = useState<string>("");
|
||||
const [rememberMe, setRememberMe] = useState(true);
|
||||
const [otp, setOtp] = useState<string>("");
|
||||
const [otpStatus, setOtpStatus] = useState<boolean>(false);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [error, setError] = useState<Error | undefined>(undefined);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col justify-center">
|
||||
<h1 className="sr-only">{title}</h1>
|
||||
<div className="max-w-md flex flex-col gap-8 w-full px-6 py-12 mx-auto">
|
||||
{otpStatus && (
|
||||
<div>A one-time password has been sent to your email.</div>
|
||||
)}
|
||||
|
||||
{error && <div>{error.message}</div>}
|
||||
|
||||
{loading && <Loading />}
|
||||
|
||||
<form
|
||||
className="flex flex-col gap-6"
|
||||
onSubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
if (!otpStatus) {
|
||||
await auth.authClient.signIn.email(
|
||||
{
|
||||
email,
|
||||
password,
|
||||
rememberMe,
|
||||
},
|
||||
{
|
||||
onSuccess: async () => {
|
||||
await auth.logIn();
|
||||
router.push("/");
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error.error);
|
||||
},
|
||||
},
|
||||
);
|
||||
} else {
|
||||
const { data, error } = await (
|
||||
auth.authClient as FullAuthClient
|
||||
).signIn.emailOtp({
|
||||
email: email,
|
||||
otp: otp,
|
||||
});
|
||||
const errorMessage = error?.message ?? error?.statusText;
|
||||
setError(
|
||||
error
|
||||
? {
|
||||
...error,
|
||||
name: error.statusText,
|
||||
message:
|
||||
errorMessage && errorMessage.length > 0
|
||||
? errorMessage
|
||||
: "An error occurred",
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
if (data) {
|
||||
await auth.logIn();
|
||||
router.push("/");
|
||||
}
|
||||
}
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<label htmlFor="email-address">Email address</label>
|
||||
<input
|
||||
id="email-address"
|
||||
value={email}
|
||||
disabled={loading}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
{!otpStatus && (
|
||||
<div>
|
||||
<label htmlFor="password">Password</label>
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
value={password}
|
||||
disabled={loading}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{otpStatus && (
|
||||
<div>
|
||||
<label htmlFor="otp">One-time password</label>
|
||||
<input
|
||||
id="otp"
|
||||
value={otp}
|
||||
disabled={loading}
|
||||
onChange={(e) => setOtp(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="items-center">
|
||||
<div>
|
||||
<label htmlFor="remember-me">Remember me</label>
|
||||
<input
|
||||
id="remember-me"
|
||||
type="checkbox"
|
||||
checked={rememberMe}
|
||||
disabled={loading}
|
||||
onChange={(e) => setRememberMe(e.target.checked)}
|
||||
/>
|
||||
</div>
|
||||
<Link href="/forgot" className="text-sm float-right">
|
||||
Forgot password?
|
||||
</Link>
|
||||
</div>
|
||||
<Button type="submit" disabled={loading}>
|
||||
Sign in
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<hr className="flex-1" />
|
||||
<p className="text-center">or</p>
|
||||
<hr className="flex-1" />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
{providers?.map((x) => {
|
||||
return (
|
||||
<SSOButton
|
||||
callbackURL={`${window.location.origin}/social/logIn`}
|
||||
provider={x}
|
||||
setLoading={setLoading}
|
||||
setError={setError}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="relative"
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const { error } = await (
|
||||
auth.authClient as FullAuthClient
|
||||
).signIn.magicLink({
|
||||
email: email,
|
||||
callbackURL: `${window.location.origin}/magic-link/logIn`,
|
||||
});
|
||||
const errorMessage = error?.message ?? error?.statusText;
|
||||
setError(
|
||||
error
|
||||
? {
|
||||
...error,
|
||||
name: error.statusText,
|
||||
message:
|
||||
errorMessage && errorMessage.length > 0
|
||||
? errorMessage
|
||||
: "An error occurred",
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src="/link.svg"
|
||||
alt="Link icon"
|
||||
className="absolute left-3"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Sign in with magic link
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="relative"
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const { data, error } = await (
|
||||
auth.authClient as FullAuthClient
|
||||
).emailOtp.sendVerificationOtp({
|
||||
email: email,
|
||||
type: "sign-in",
|
||||
});
|
||||
setOtpStatus(data?.success ?? false);
|
||||
const errorMessage = error?.message ?? error?.statusText;
|
||||
setError(
|
||||
error
|
||||
? {
|
||||
...error,
|
||||
name: error.statusText,
|
||||
message:
|
||||
errorMessage && errorMessage.length > 0
|
||||
? errorMessage
|
||||
: "An error occurred",
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src="/mail.svg"
|
||||
alt="Mail icon"
|
||||
className="absolute left-3"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Sign in with one-time password
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<p className="text-sm">
|
||||
Don't have an account? <Link href="/sign-up">Sign up</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
import { Button } from "@/components/Button";
|
||||
import { Loading } from "@/components/Loading";
|
||||
import { SSOButton } from "@/components/SSOButton";
|
||||
import { useAuth } from "jazz-react-auth-betterauth";
|
||||
import type {
|
||||
FullAuthClient,
|
||||
SSOProviderType,
|
||||
} from "jazz-react-auth-betterauth";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
|
||||
const title = "Sign Up";
|
||||
|
||||
export default function SignUpForm({
|
||||
providers,
|
||||
}: {
|
||||
providers?: SSOProviderType[];
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const auth = useAuth();
|
||||
const [name, setName] = useState<string>("");
|
||||
const [email, setEmail] = useState<string>("");
|
||||
const [password, setPassword] = useState<string>("");
|
||||
const [confirmPassword, setConfirmPassword] = useState<string>("");
|
||||
const [otp, setOtp] = useState<string>("");
|
||||
const [otpStatus, setOtpStatus] = useState<boolean>(false);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [error, setError] = useState<Error | undefined>(undefined);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col justify-center">
|
||||
<h1 className="sr-only">{title}</h1>
|
||||
<div className="max-w-md flex flex-col gap-8 w-full px-6 py-12 mx-auto">
|
||||
{otpStatus && (
|
||||
<div>A one-time password has been sent to your email.</div>
|
||||
)}
|
||||
|
||||
{error && <div>{error.message}</div>}
|
||||
|
||||
{loading && <Loading />}
|
||||
|
||||
<form
|
||||
className="flex flex-col gap-6"
|
||||
onSubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
if (password !== confirmPassword) {
|
||||
setError(new Error("Passwords do not match"));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
if (!otpStatus) {
|
||||
await auth.authClient.signUp.email(
|
||||
{
|
||||
email,
|
||||
password,
|
||||
name,
|
||||
},
|
||||
{
|
||||
onSuccess: async () => {
|
||||
await auth.signIn();
|
||||
router.push("/");
|
||||
},
|
||||
onError: (error) => {
|
||||
setError(error.error);
|
||||
},
|
||||
},
|
||||
);
|
||||
} else {
|
||||
const { data, error } = await (
|
||||
auth.authClient as FullAuthClient
|
||||
).signIn.emailOtp({
|
||||
email: email,
|
||||
otp: otp,
|
||||
});
|
||||
const errorMessage = error?.message ?? error?.statusText;
|
||||
setError(
|
||||
error
|
||||
? {
|
||||
...error,
|
||||
name: error.statusText,
|
||||
message:
|
||||
errorMessage && errorMessage.length > 0
|
||||
? errorMessage
|
||||
: "An error occurred",
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
if (data) {
|
||||
await auth.signIn();
|
||||
router.push("/");
|
||||
}
|
||||
}
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<label htmlFor="full-name">Full name</label>
|
||||
<input
|
||||
id="full-name"
|
||||
value={name}
|
||||
disabled={loading}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="email-address">Email address</label>
|
||||
<input
|
||||
id="email-address"
|
||||
value={email}
|
||||
disabled={loading}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
{!otpStatus && (
|
||||
<>
|
||||
<div>
|
||||
<label htmlFor="password">Password</label>
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
value={password}
|
||||
disabled={loading}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="confirm-password">Confirm password</label>
|
||||
<input
|
||||
id="confirm-password"
|
||||
type="password"
|
||||
value={confirmPassword}
|
||||
disabled={loading}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{otpStatus && (
|
||||
<div>
|
||||
<label htmlFor="otp">One-time password</label>
|
||||
<input
|
||||
id="otp"
|
||||
value={otp}
|
||||
disabled={loading}
|
||||
onChange={(e) => setOtp(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<Button type="submit" disabled={loading}>
|
||||
Sign up
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<hr className="flex-1" />
|
||||
<p className="text-center">or</p>
|
||||
<hr className="flex-1" />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
{providers?.map((x) => {
|
||||
return (
|
||||
<SSOButton
|
||||
callbackURL={`${window.location.origin}/social/signIn`}
|
||||
provider={x}
|
||||
setLoading={setLoading}
|
||||
setError={setError}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="relative"
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const { error } = await (
|
||||
auth.authClient as FullAuthClient
|
||||
).signIn.magicLink({
|
||||
email: email,
|
||||
callbackURL: `${window.location.origin}/magic-link/signIn`,
|
||||
});
|
||||
const errorMessage = error?.message ?? error?.statusText;
|
||||
setError(
|
||||
error
|
||||
? {
|
||||
...error,
|
||||
name: error.statusText,
|
||||
message:
|
||||
errorMessage && errorMessage.length > 0
|
||||
? errorMessage
|
||||
: "An error occurred",
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src="/link.svg"
|
||||
alt="Link icon"
|
||||
className="absolute left-3"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Sign up with magic link
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="relative"
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
const { data, error } = await (
|
||||
auth.authClient as FullAuthClient
|
||||
).emailOtp.sendVerificationOtp({
|
||||
email: email,
|
||||
type: "sign-in",
|
||||
});
|
||||
setOtpStatus(data?.success ?? false);
|
||||
const errorMessage = error?.message ?? error?.statusText;
|
||||
setError(
|
||||
error
|
||||
? {
|
||||
...error,
|
||||
name: error.statusText,
|
||||
message:
|
||||
errorMessage && errorMessage.length > 0
|
||||
? errorMessage
|
||||
: "An error occurred",
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src="/mail.svg"
|
||||
alt="Mail icon"
|
||||
className="absolute left-3"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Sign up with one-time password
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<p className="text-sm">
|
||||
Already have an account? <Link href="/sign-in">Sign in</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
55
examples/betterauth/src/components/navbar.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
108
examples/betterauth/src/components/reset-password-form.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { useAuth } from "jazz-react-auth-betterauth";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
export default function Page() {
|
||||
const router = useRouter();
|
||||
const auth = useAuth();
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const error = searchParams.get("error");
|
||||
if (!error) {
|
||||
auth.logIn().then(() => router.push("/"));
|
||||
return null;
|
||||
} else {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col justify-center">
|
||||
<div className="max-w-md flex flex-col gap-8 w-full px-6 py-12 mx-auto">
|
||||
<div>{error}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { useAuth } from "jazz-react-auth-betterauth";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
export default function Page() {
|
||||
const router = useRouter();
|
||||
const auth = useAuth();
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const error = searchParams.get("error");
|
||||
if (!error) {
|
||||
auth.signIn().then(() => router.push("/"));
|
||||
return null;
|
||||
} else {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col justify-center">
|
||||
<div className="max-w-md flex flex-col gap-8 w-full px-6 py-12 mx-auto">
|
||||
<div>{error}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
124
examples/betterauth/src/components/signin-form.tsx
Normal 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't have an account?{" "}
|
||||
<Link
|
||||
href="/auth/sign-up"
|
||||
className="underline underline-offset-4"
|
||||
>
|
||||
Sign up
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
151
examples/betterauth/src/components/signup-form.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
59
examples/betterauth/src/components/ui/button.tsx
Normal 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 };
|
||||
92
examples/betterauth/src/components/ui/card.tsx
Normal 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,
|
||||
};
|
||||
21
examples/betterauth/src/components/ui/input.tsx
Normal 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 };
|
||||
22
examples/betterauth/src/components/ui/label.tsx
Normal 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 };
|
||||
22
examples/betterauth/src/components/ui/sonner.tsx
Normal 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 };
|
||||
60
examples/betterauth/src/components/user-settings.tsx
Normal 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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import type { betterAuth } from "better-auth";
|
||||
import type { emailOTP } from "better-auth/plugins";
|
||||
import type { magicLink } from "better-auth/plugins";
|
||||
|
||||
export const sendEmailOtpCb: Parameters<
|
||||
typeof emailOTP
|
||||
>[0]["sendVerificationOTP"] = async ({ email, otp, type }) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
searchParams.set("email", email);
|
||||
searchParams.set("otp", otp);
|
||||
|
||||
let emailLink = "";
|
||||
if (type === "sign-in") {
|
||||
// Send the OTP for sign-in
|
||||
emailLink = `/mockEmail/sign-in?${searchParams.toString()}`;
|
||||
} else if (type === "email-verification") {
|
||||
// Send the OTP for email verification
|
||||
emailLink = `/mockEmail/email-verification?${searchParams.toString()}`;
|
||||
} else {
|
||||
// Send the OTP for password reset
|
||||
emailLink = `/mockEmail/forget-password?${searchParams.toString()}`;
|
||||
}
|
||||
console.log("Mock email sent: " + emailLink);
|
||||
};
|
||||
|
||||
export const sendMagicLinkCb: Parameters<typeof magicLink>[0]["sendMagicLink"] =
|
||||
async ({ email, url }) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
searchParams.set("url", url);
|
||||
const emailLink = `/mockEmail/sign-in?${searchParams.toString()}`;
|
||||
console.log("Mock email sent: " + emailLink);
|
||||
};
|
||||
|
||||
export const sendWelcomeEmailCb: NonNullable<
|
||||
NonNullable<
|
||||
NonNullable<Parameters<typeof betterAuth>[0]["databaseHooks"]>["user"]
|
||||
>["create"]
|
||||
>["after"] = async (user) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
searchParams.set("name", user.name);
|
||||
const emailLink = `/mockEmail/welcome?${searchParams.toString()}`;
|
||||
console.log("Mock email sent: " + emailLink);
|
||||
};
|
||||
|
||||
export const sendVerificationEmailCb: NonNullable<
|
||||
Parameters<typeof betterAuth>[0]["emailVerification"]
|
||||
>["sendVerificationEmail"] = async ({ user, url }) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
searchParams.set("name", user.name);
|
||||
searchParams.set("url", url);
|
||||
const emailLink = `/mockEmail/email-verification?${searchParams.toString()}`;
|
||||
console.log("Mock email sent: " + emailLink);
|
||||
};
|
||||
|
||||
export const sendResetPasswordCb: NonNullable<
|
||||
Parameters<typeof betterAuth>[0]["emailAndPassword"]
|
||||
>["sendResetPassword"] = async ({ user, url }) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
searchParams.set("name", user.name);
|
||||
searchParams.set("url", url);
|
||||
const emailLink = `/mockEmail/forgot-password?${searchParams.toString()}`;
|
||||
console.log("Mock email sent: " + emailLink);
|
||||
};
|
||||
@@ -1,54 +1,42 @@
|
||||
import { appName } from "@/components/emails";
|
||||
import { betterAuth } from "better-auth";
|
||||
import { getMigrations } from "better-auth/db";
|
||||
import { emailOTP, haveIBeenPwned, magicLink } from "better-auth/plugins";
|
||||
import Database from "better-sqlite3";
|
||||
import { jazzPlugin } from "jazz-betterauth-server-plugin";
|
||||
import {
|
||||
sendEmailOtpCb,
|
||||
sendMagicLinkCb,
|
||||
sendResetPasswordCb,
|
||||
sendVerificationEmailCb,
|
||||
sendWelcomeEmailCb,
|
||||
} from "./auth-email";
|
||||
import { socialProviders } from "./socialProviders";
|
||||
|
||||
export const auth = await (async () => {
|
||||
// Configure Better Auth server
|
||||
const auth = betterAuth({
|
||||
appName: appName,
|
||||
appName: "Jazz Example: Better Auth",
|
||||
database: new Database("sqlite.db"),
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
sendResetPassword: sendResetPasswordCb,
|
||||
},
|
||||
emailVerification: {
|
||||
sendVerificationEmail: sendVerificationEmailCb,
|
||||
},
|
||||
socialProviders: socialProviders,
|
||||
account: {
|
||||
accountLinking: {
|
||||
allowDifferentEmails: true,
|
||||
allowUnlinkingAll: 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: [
|
||||
haveIBeenPwned(),
|
||||
jazzPlugin(),
|
||||
magicLink({
|
||||
sendMagicLink: sendMagicLinkCb,
|
||||
}),
|
||||
emailOTP({ sendVerificationOTP: sendEmailOtpCb }),
|
||||
],
|
||||
plugins: [jazzPlugin()],
|
||||
databaseHooks: {
|
||||
user: {
|
||||
create: {
|
||||
after: sendWelcomeEmailCb,
|
||||
async after(user) {
|
||||
// Here we can send a welcome email to the user
|
||||
console.error("Not implemented");
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
6
examples/betterauth/src/lib/utils.ts
Normal 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));
|
||||
}
|
||||
@@ -11,14 +11,14 @@ test("should sign up, sign in, and logout", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
const homePage = new HomePage(page);
|
||||
await homePage.expectLoggedOut();
|
||||
await homePage.signUpLinkButton.click();
|
||||
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.signInLinkButton.click();
|
||||
await homePage.signInLink.click();
|
||||
await homePage.signInEmail(email, password);
|
||||
await homePage.expectLoggedIn(username);
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ export class HomePage {
|
||||
constructor(public page: Page) {}
|
||||
|
||||
usernameInput = this.page.getByRole("textbox", {
|
||||
name: "Full name",
|
||||
name: "Name",
|
||||
exact: true,
|
||||
});
|
||||
emailInput = this.page.getByRole("textbox", {
|
||||
name: "Email address",
|
||||
name: "Email",
|
||||
exact: true,
|
||||
});
|
||||
passwordInput = this.page.getByRole("textbox", {
|
||||
@@ -27,11 +27,11 @@ export class HomePage {
|
||||
name: "Sign in",
|
||||
exact: true,
|
||||
});
|
||||
signUpLinkButton = this.page.getByRole("link", {
|
||||
signUpLink = this.page.getByRole("link", {
|
||||
name: "Sign up",
|
||||
exact: true,
|
||||
});
|
||||
signInLinkButton = this.page.getByRole("link", {
|
||||
signInLink = this.page.getByRole("link", {
|
||||
name: "Sign in",
|
||||
exact: true,
|
||||
});
|
||||
@@ -60,17 +60,17 @@ export class HomePage {
|
||||
|
||||
async expectLoggedIn(name?: string) {
|
||||
await expect(this.logoutButton).toBeVisible();
|
||||
await expect(this.signInLinkButton).not.toBeVisible();
|
||||
await expect(this.signUpLinkButton).not.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();
|
||||
await expect(this.page.getByText(`Signed in as ${name}`)).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
async expectLoggedOut() {
|
||||
await expect(this.logoutButton).not.toBeVisible();
|
||||
await expect(this.signInLinkButton).toBeVisible();
|
||||
await expect(this.signUpLinkButton).toBeVisible();
|
||||
await expect(this.page.getByText(`Not signed in.`)).toBeVisible();
|
||||
await expect(this.signInLink).toBeVisible();
|
||||
await expect(this.signUpLink).toBeVisible();
|
||||
await expect(this.page.getByText(`Anonymous user`)).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,219 @@
|
||||
# chat-rn-expo-clerk
|
||||
|
||||
## 1.0.144
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e7e505e]
|
||||
- Updated dependencies [cfb6786]
|
||||
- Updated dependencies [13b57aa]
|
||||
- Updated dependencies [5662faa]
|
||||
- Updated dependencies [2116a59]
|
||||
- jazz-tools@0.14.21
|
||||
- jazz-expo@0.14.21
|
||||
- jazz-react-native-media-images@0.14.21
|
||||
|
||||
## 1.0.143
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6f72419]
|
||||
- Updated dependencies [04b20c2]
|
||||
- jazz-tools@0.14.20
|
||||
- jazz-expo@0.14.20
|
||||
- jazz-react-native-media-images@0.14.20
|
||||
|
||||
## 1.0.142
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [061ec99]
|
||||
- jazz-expo@0.14.19
|
||||
- jazz-react-native-media-images@0.14.19
|
||||
- jazz-tools@0.14.19
|
||||
|
||||
## 1.0.141
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4b950bc]
|
||||
- Updated dependencies [d6d9c0a]
|
||||
- Updated dependencies [c559054]
|
||||
- jazz-tools@0.14.18
|
||||
- jazz-expo@0.14.18
|
||||
- jazz-react-native-media-images@0.14.18
|
||||
|
||||
## 1.0.140
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e512df4]
|
||||
- jazz-tools@0.14.17
|
||||
- jazz-expo@0.14.17
|
||||
- jazz-react-native-media-images@0.14.17
|
||||
|
||||
## 1.0.139
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-expo@0.14.16
|
||||
- jazz-tools@0.14.16
|
||||
- jazz-react-native-media-images@0.14.16
|
||||
|
||||
## 1.0.138
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-expo@0.14.15
|
||||
- jazz-tools@0.14.15
|
||||
- jazz-react-native-media-images@0.14.15
|
||||
|
||||
## 1.0.137
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e32a1f7]
|
||||
- jazz-tools@0.14.14
|
||||
- jazz-expo@0.14.14
|
||||
- jazz-react-native-media-images@0.14.14
|
||||
|
||||
## 1.0.136
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-expo@0.14.13
|
||||
|
||||
## 1.0.135
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-expo@0.14.12
|
||||
|
||||
## 1.0.134
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [98d697f]
|
||||
- jazz-expo@0.14.11
|
||||
|
||||
## 1.0.133
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [dc746a2]
|
||||
- Updated dependencies [f869d9a]
|
||||
- Updated dependencies [3fe6832]
|
||||
- jazz-react-native-media-images@0.14.10
|
||||
- jazz-tools@0.14.10
|
||||
- jazz-expo@0.14.10
|
||||
|
||||
## 1.0.132
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [22c2600]
|
||||
- jazz-tools@0.14.9
|
||||
- jazz-expo@0.14.9
|
||||
- jazz-react-native-media-images@0.14.9
|
||||
|
||||
## 1.0.131
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [637ae13]
|
||||
- jazz-tools@0.14.8
|
||||
- jazz-expo@0.14.8
|
||||
- jazz-react-native-media-images@0.14.8
|
||||
|
||||
## 1.0.130
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [365b0ea]
|
||||
- jazz-tools@0.14.7
|
||||
- jazz-expo@0.14.7
|
||||
- jazz-react-native-media-images@0.14.7
|
||||
|
||||
## 1.0.129
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9d6d9fe]
|
||||
- Updated dependencies [9d6d9fe]
|
||||
- jazz-tools@0.14.6
|
||||
- jazz-expo@0.14.6
|
||||
- jazz-react-native-media-images@0.14.6
|
||||
|
||||
## 1.0.128
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [91cbb2f]
|
||||
- Updated dependencies [20b3d88]
|
||||
- jazz-tools@0.14.5
|
||||
- jazz-expo@0.14.5
|
||||
- jazz-react-native-media-images@0.14.5
|
||||
|
||||
## 1.0.127
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [011af55]
|
||||
- jazz-tools@0.14.4
|
||||
- jazz-expo@0.14.4
|
||||
- jazz-react-native-media-images@0.14.4
|
||||
|
||||
## 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
|
||||
|
||||
@@ -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 { CoPlainText, 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,7 +69,7 @@ export default function Conversation() {
|
||||
setChat(chat);
|
||||
};
|
||||
|
||||
const loadChat = async (chatId: ID<Chat>) => {
|
||||
const loadChat = async (chatId: string) => {
|
||||
try {
|
||||
const chat = await Chat.load(chatId);
|
||||
if (chat) setChat(chat);
|
||||
@@ -123,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(
|
||||
@@ -139,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
|
||||
@@ -175,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>
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "chat-rn-expo-clerk",
|
||||
"main": "index.js",
|
||||
"version": "1.0.120",
|
||||
"version": "1.0.144",
|
||||
"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
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { CoList, CoMap, CoPlainText, ImageDefinition, co } from "jazz-tools";
|
||||
import { CoList, co, coField, z } from "jazz-tools";
|
||||
|
||||
export class Message extends CoMap {
|
||||
text = co.ref(CoPlainText);
|
||||
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);
|
||||
|
||||
7
examples/chat-rn-expo/.gitignore
vendored
@@ -7,8 +7,10 @@ node_modules/
|
||||
.expo/
|
||||
dist/
|
||||
web-build/
|
||||
expo-env.d.ts
|
||||
|
||||
# Native
|
||||
.kotlin/
|
||||
*.orig.*
|
||||
*.jks
|
||||
*.p8
|
||||
@@ -33,6 +35,5 @@ yarn-error.*
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
|
||||
ios
|
||||
android
|
||||
android/
|
||||
ios/
|
||||
@@ -1,897 +1,99 @@
|
||||
# chat-rn-expo
|
||||
|
||||
## 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
|
||||
|
||||
- jazz-expo@0.13.13
|
||||
- jazz-tools@0.13.13
|
||||
|
||||
## 1.0.91
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4547525]
|
||||
- jazz-tools@0.13.12
|
||||
- jazz-expo@0.13.12
|
||||
|
||||
## 1.0.90
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [17273a6]
|
||||
- jazz-tools@0.13.11
|
||||
- jazz-expo@0.13.11
|
||||
|
||||
## 1.0.89
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-expo@0.13.10
|
||||
- jazz-tools@0.13.10
|
||||
|
||||
## 1.0.88
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [a6cf01f]
|
||||
- jazz-tools@0.13.9
|
||||
- jazz-expo@0.13.9
|
||||
|
||||
## 1.0.87
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-expo@0.13.8
|
||||
|
||||
## 1.0.86
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [bc3d7bb]
|
||||
- jazz-tools@0.13.7
|
||||
- jazz-expo@0.13.7
|
||||
|
||||
## 1.0.85
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [fe6f561]
|
||||
- jazz-tools@0.13.5
|
||||
- jazz-expo@0.13.5
|
||||
|
||||
## 1.0.84
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3129982]
|
||||
- Updated dependencies [3129982]
|
||||
- jazz-expo@0.13.4
|
||||
- jazz-tools@0.13.4
|
||||
|
||||
## 1.0.83
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [12f8bfa]
|
||||
- Updated dependencies [bd57177]
|
||||
- jazz-tools@0.13.3
|
||||
- jazz-expo@0.13.3
|
||||
|
||||
## 1.0.82
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-expo@0.13.2
|
||||
- jazz-tools@0.13.2
|
||||
|
||||
## 1.0.81
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [63a7aa0]
|
||||
- jazz-expo@0.13.1
|
||||
|
||||
## 1.0.80
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [bce3bcc]
|
||||
- Updated dependencies [afd1374]
|
||||
- jazz-expo@0.13.0
|
||||
- jazz-tools@0.13.0
|
||||
|
||||
## 1.0.88
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 1.0.87
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.12.1
|
||||
- jazz-tools@0.12.1
|
||||
|
||||
## 1.0.86
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [01523dc]
|
||||
- Updated dependencies [4ea87dc]
|
||||
- Updated dependencies [1e6da19]
|
||||
- Updated dependencies [b6c6a0a]
|
||||
- jazz-tools@0.12.0
|
||||
- jazz-react-native@0.12.0
|
||||
|
||||
## 1.0.85
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.11.8
|
||||
- jazz-tools@0.11.8
|
||||
|
||||
## 1.0.84
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [a140f55]
|
||||
- Updated dependencies [2b0d1b0]
|
||||
- jazz-tools@0.11.7
|
||||
- jazz-react-native@0.11.7
|
||||
|
||||
## 1.0.83
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e7c85b7]
|
||||
- jazz-react-native@0.11.6
|
||||
- jazz-tools@0.11.6
|
||||
|
||||
## 1.0.82
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.11.5
|
||||
- jazz-tools@0.11.5
|
||||
|
||||
## 1.0.81
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [57a3dbe]
|
||||
- Updated dependencies [a717754]
|
||||
- Updated dependencies [a91f343]
|
||||
- jazz-tools@0.11.4
|
||||
- jazz-react-native@0.11.4
|
||||
|
||||
## 1.0.80
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.11.3
|
||||
- jazz-tools@0.11.3
|
||||
|
||||
## 1.0.79
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6892dc6]
|
||||
- jazz-tools@0.11.2
|
||||
- jazz-react-native@0.11.2
|
||||
|
||||
## 1.0.78
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.11.1
|
||||
|
||||
## 1.0.77
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6a96d8b]
|
||||
- Updated dependencies [a35249a]
|
||||
- Updated dependencies [b9d194a]
|
||||
- Updated dependencies [a4713df]
|
||||
- Updated dependencies [34cbdc3]
|
||||
- Updated dependencies [f039e8f]
|
||||
- Updated dependencies [e22de9f]
|
||||
- jazz-tools@0.11.0
|
||||
- jazz-react-native@0.11.0
|
||||
|
||||
## 1.0.76
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2f99de0]
|
||||
- jazz-tools@0.10.15
|
||||
- jazz-react-native@0.10.15
|
||||
|
||||
## 1.0.75
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [75211e3]
|
||||
- jazz-tools@0.10.14
|
||||
- jazz-react-native@0.10.14
|
||||
|
||||
## 1.0.74
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [07feedd]
|
||||
- jazz-tools@0.10.13
|
||||
- jazz-react-native@0.10.13
|
||||
|
||||
## 1.0.73
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-tools@0.10.12
|
||||
- jazz-react-native@0.10.12
|
||||
|
||||
## 1.0.72
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5a54e4a]
|
||||
- jazz-react-native@0.10.11
|
||||
|
||||
## 1.0.71
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3405d8f]
|
||||
- jazz-react-native@0.10.10
|
||||
|
||||
## 1.0.70
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [2fb6428]
|
||||
- jazz-tools@0.10.8
|
||||
- jazz-react-native@0.10.8
|
||||
|
||||
## 1.0.69
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [1136d9b]
|
||||
- Updated dependencies [0eed228]
|
||||
- jazz-react-native@0.10.7
|
||||
- jazz-tools@0.10.7
|
||||
|
||||
## 1.0.68
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ada802b]
|
||||
- jazz-tools@0.10.6
|
||||
- jazz-react-native@0.10.6
|
||||
|
||||
## 1.0.67
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [59ff77e]
|
||||
- jazz-tools@0.10.5
|
||||
- jazz-react-native@0.10.5
|
||||
|
||||
## 1.0.66
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.10.4
|
||||
- jazz-tools@0.10.4
|
||||
|
||||
## 1.0.65
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d8582fc]
|
||||
- jazz-tools@0.10.3
|
||||
- jazz-react-native@0.10.3
|
||||
|
||||
## 1.0.64
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.10.2
|
||||
- jazz-tools@0.10.2
|
||||
|
||||
## 1.0.63
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5a63cba]
|
||||
- jazz-tools@0.10.1
|
||||
- jazz-react-native@0.10.1
|
||||
|
||||
## 1.0.62
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [498954f]
|
||||
- Updated dependencies [d42c2aa]
|
||||
- Updated dependencies [dd03464]
|
||||
- Updated dependencies [b426342]
|
||||
- jazz-react-native@0.10.0
|
||||
- jazz-tools@0.10.0
|
||||
|
||||
## 1.0.61
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.9.23
|
||||
- jazz-tools@0.9.23
|
||||
|
||||
## 1.0.60
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.9.22
|
||||
|
||||
## 1.0.59
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [1be017d]
|
||||
- jazz-tools@0.9.21
|
||||
- jazz-react-native@0.9.21
|
||||
|
||||
## 1.0.58
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b01cc1f]
|
||||
- jazz-tools@0.9.20
|
||||
- jazz-react-native@0.9.20
|
||||
|
||||
## 1.0.57
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.9.19
|
||||
- jazz-tools@0.9.19
|
||||
|
||||
## 1.0.56
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.9.18
|
||||
- jazz-tools@0.9.18
|
||||
|
||||
## 1.0.55
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c2ca1fe]
|
||||
- Updated dependencies [1227047]
|
||||
- jazz-tools@0.9.17
|
||||
- jazz-react-native@0.9.17
|
||||
|
||||
## 1.0.54
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [24b3b6a]
|
||||
- jazz-tools@0.9.16
|
||||
- jazz-react-native@0.9.16
|
||||
|
||||
## 1.0.53
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [7491711]
|
||||
- jazz-tools@0.9.15
|
||||
- jazz-react-native@0.9.15
|
||||
|
||||
## 1.0.52
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3df93cc]
|
||||
- jazz-tools@0.9.14
|
||||
- jazz-react-native@0.9.14
|
||||
|
||||
## 1.0.51
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.9.13
|
||||
- jazz-tools@0.9.13
|
||||
|
||||
## 1.0.50
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.9.12
|
||||
- jazz-tools@0.9.12
|
||||
|
||||
## 1.0.49
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.9.11
|
||||
- jazz-tools@0.9.11
|
||||
|
||||
## 1.0.48
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [f76274c]
|
||||
- Updated dependencies [5e83864]
|
||||
- jazz-react-native@0.9.10
|
||||
- jazz-tools@0.9.10
|
||||
|
||||
## 1.0.47
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [8eb9247]
|
||||
- jazz-tools@0.9.9
|
||||
- jazz-react-native@0.9.9
|
||||
|
||||
## 1.0.46
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d1d773b]
|
||||
- jazz-tools@0.9.8
|
||||
- jazz-react-native@0.9.8
|
||||
|
||||
## 1.0.45
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [8a390d2]
|
||||
- jazz-react-native@0.9.6
|
||||
|
||||
## 1.0.44
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c871912]
|
||||
- jazz-react-native@0.9.5
|
||||
|
||||
## 1.0.43
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.9.4
|
||||
|
||||
## 1.0.42
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [7cd691f]
|
||||
- jazz-react-native@0.9.3
|
||||
|
||||
## 1.0.41
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80fd3e9]
|
||||
- jazz-react-native@0.9.2
|
||||
|
||||
## 1.0.40
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [1b71969]
|
||||
- jazz-tools@0.9.1
|
||||
- jazz-react-native@0.9.1
|
||||
|
||||
## 1.0.39
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [1da4d55]
|
||||
- Updated dependencies [8eda792]
|
||||
- Updated dependencies [1e5e3a1]
|
||||
- jazz-react-native@0.9.0
|
||||
- jazz-tools@0.9.0
|
||||
|
||||
## 1.0.38
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [dc62b95]
|
||||
- Updated dependencies [1de26f8]
|
||||
- jazz-tools@0.8.51
|
||||
- jazz-react-native@0.8.51
|
||||
|
||||
## 1.0.37
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.50
|
||||
- jazz-tools@0.8.50
|
||||
|
||||
## 1.0.36
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.49
|
||||
- jazz-tools@0.8.49
|
||||
|
||||
## 1.0.35
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [635e824]
|
||||
- Updated dependencies [0a85982]
|
||||
- jazz-tools@0.8.48
|
||||
- jazz-react-native@0.8.48
|
||||
|
||||
## 1.0.34
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [33ef9c4]
|
||||
- jazz-react-native@0.8.47
|
||||
|
||||
## 1.0.33
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ab4ffbd]
|
||||
- jazz-react-native@0.8.46
|
||||
|
||||
## 1.0.32
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [7701307]
|
||||
- Updated dependencies [fa41f8e]
|
||||
- Updated dependencies [88d7d9a]
|
||||
- Updated dependencies [60e35ea]
|
||||
- jazz-react-native@0.8.45
|
||||
- jazz-tools@0.8.45
|
||||
|
||||
## 1.0.31
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.44
|
||||
- jazz-tools@0.8.44
|
||||
|
||||
## 1.0.30
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.41
|
||||
- jazz-tools@0.8.41
|
||||
|
||||
## 1.0.29
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0c6b0f3]
|
||||
- Updated dependencies [249eecb]
|
||||
- jazz-react-native@0.8.39
|
||||
- jazz-tools@0.8.39
|
||||
|
||||
## 1.0.28
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.38
|
||||
- jazz-tools@0.8.38
|
||||
|
||||
## 1.0.27
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.37
|
||||
- jazz-tools@0.8.37
|
||||
|
||||
## 1.0.26
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c84764a]
|
||||
- Updated dependencies [441fe27]
|
||||
- jazz-react-native@0.8.36
|
||||
- jazz-tools@0.8.36
|
||||
|
||||
## 1.0.25
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [8b87117]
|
||||
- jazz-tools@0.8.35
|
||||
- jazz-react-native@0.8.35
|
||||
|
||||
## 1.0.24
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.34
|
||||
- jazz-tools@0.8.34
|
||||
|
||||
## 1.0.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [df42b2b]
|
||||
- jazz-tools@0.8.32
|
||||
- jazz-react-native@0.8.32
|
||||
|
||||
## 1.0.22
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.31
|
||||
- jazz-tools@0.8.31
|
||||
|
||||
## 1.0.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.30
|
||||
- jazz-tools@0.8.30
|
||||
|
||||
## 1.0.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.29
|
||||
- jazz-tools@0.8.29
|
||||
|
||||
## 1.0.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.28
|
||||
- jazz-tools@0.8.28
|
||||
|
||||
## 1.0.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.27
|
||||
- jazz-tools@0.8.27
|
||||
|
||||
## 1.0.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [d348c2d]
|
||||
- Updated dependencies [6902b5b]
|
||||
- Updated dependencies [1a0cd3d]
|
||||
- jazz-tools@0.8.23
|
||||
- jazz-react-native@0.8.23
|
||||
|
||||
## 1.0.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [149ca97]
|
||||
- jazz-tools@0.8.21
|
||||
- jazz-react-native@0.8.21
|
||||
|
||||
## 1.0.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3ef3ff3]
|
||||
- jazz-react-native@0.8.20
|
||||
|
||||
## 1.0.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.19
|
||||
- jazz-tools@0.8.19
|
||||
|
||||
## 1.0.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.18
|
||||
- jazz-tools@0.8.18
|
||||
|
||||
## 1.0.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.17
|
||||
- jazz-tools@0.8.17
|
||||
- Updated dependencies [e7e505e]
|
||||
- Updated dependencies [cfb6786]
|
||||
- Updated dependencies [13b57aa]
|
||||
- Updated dependencies [5662faa]
|
||||
- Updated dependencies [2116a59]
|
||||
- jazz-tools@0.14.21
|
||||
- jazz-expo@0.14.21
|
||||
|
||||
## 1.0.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.16
|
||||
- jazz-tools@0.8.16
|
||||
- Updated dependencies [6f72419]
|
||||
- Updated dependencies [04b20c2]
|
||||
- jazz-tools@0.14.20
|
||||
- jazz-expo@0.14.20
|
||||
|
||||
## 1.0.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cce679b]
|
||||
- jazz-tools@0.8.15
|
||||
- jazz-react-native@0.8.15
|
||||
- Updated dependencies [061ec99]
|
||||
- jazz-expo@0.14.19
|
||||
- jazz-tools@0.14.19
|
||||
|
||||
## 1.0.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [36273b3]
|
||||
- jazz-tools@0.8.14
|
||||
- jazz-react-native@0.8.14
|
||||
- Updated dependencies [4b950bc]
|
||||
- Updated dependencies [d6d9c0a]
|
||||
- Updated dependencies [c559054]
|
||||
- jazz-tools@0.14.18
|
||||
- jazz-expo@0.14.18
|
||||
|
||||
## 1.0.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [fd011d7]
|
||||
- jazz-tools@0.8.13
|
||||
- jazz-react-native@0.8.13
|
||||
- Updated dependencies [e512df4]
|
||||
- jazz-tools@0.14.17
|
||||
- jazz-expo@0.14.17
|
||||
|
||||
## 1.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.12
|
||||
- jazz-tools@0.8.12
|
||||
- jazz-expo@0.14.16
|
||||
- jazz-tools@0.14.16
|
||||
|
||||
## 1.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.11
|
||||
- jazz-tools@0.8.11
|
||||
- jazz-expo@0.14.15
|
||||
- jazz-tools@0.14.15
|
||||
|
||||
## 1.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b7639cf: feat(react-native): replaced react-native-mmkv with expo-secure-store and initialize it by default as kvStore in createJazzRNApp() (BREAKING)
|
||||
- Updated dependencies [b7639cf]
|
||||
- jazz-react-native@0.8.8
|
||||
- Updated dependencies [e32a1f7]
|
||||
- jazz-tools@0.14.14
|
||||
- jazz-expo@0.14.14
|
||||
|
||||
## 1.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [32b05b6]
|
||||
- jazz-react-native@0.8.7
|
||||
- jazz-expo@0.14.13
|
||||
|
||||
## 1.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.6
|
||||
- jazz-expo@0.14.12
|
||||
|
||||
## 1.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c3f4e6b]
|
||||
- Updated dependencies [d9152ed]
|
||||
- jazz-react-native@0.8.5
|
||||
- jazz-tools@0.8.5
|
||||
- Updated dependencies [98d697f]
|
||||
- jazz-expo@0.14.11
|
||||
|
||||
## 1.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- jazz-react-native@0.8.3
|
||||
- jazz-tools@0.8.3
|
||||
- Updated dependencies [dc746a2]
|
||||
- Updated dependencies [f869d9a]
|
||||
- Updated dependencies [3fe6832]
|
||||
- jazz-tools@0.14.10
|
||||
- jazz-expo@0.14.10
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
# 🎷 Jazz + Expo + `react-navigation` + Demo Auth
|
||||
|
||||
## 🚀 How to Run
|
||||
|
||||
### 1. Inside the Workspace Root
|
||||
|
||||
First, install dependencies and build the project:
|
||||
|
||||
```bash
|
||||
pnpm i
|
||||
pnpm run build
|
||||
```
|
||||
|
||||
### 2. Inside the `examples/chat-rn-expo` Directory
|
||||
|
||||
Next, navigate to the specific example project and run the following commands:
|
||||
|
||||
```bash
|
||||
pnpm expo prebuild
|
||||
pnpx pod-install
|
||||
pnpm expo run:ios
|
||||
```
|
||||
|
||||
This will set up and launch the app on iOS. For Android, you can replace the last command with `pnpm expo run:android`.
|
||||
@@ -1,55 +0,0 @@
|
||||
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);
|
||||
};
|
||||
@@ -1,34 +1,32 @@
|
||||
{
|
||||
"expo": {
|
||||
"name": "jazz-chat-rn-expo",
|
||||
"scheme": "jazz-chat-rn-expo",
|
||||
"slug": "jazz-chat-rn-expo",
|
||||
"name": "chat-rn-expo",
|
||||
"slug": "chat-rn-expo",
|
||||
"version": "1.0.0",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/icon.png",
|
||||
"userInterfaceStyle": "light",
|
||||
"newArchEnabled": true,
|
||||
"splash": {
|
||||
"image": "./assets/splash.png",
|
||||
"image": "./assets/splash-icon.png",
|
||||
"resizeMode": "contain",
|
||||
"backgroundColor": "#ffffff"
|
||||
},
|
||||
"ios": {
|
||||
"supportsTablet": true,
|
||||
"bundleIdentifier": "com.jazz.chatrn"
|
||||
"bundleIdentifier": "tools.jazz.chatrnexpo"
|
||||
},
|
||||
"android": {
|
||||
"adaptiveIcon": {
|
||||
"foregroundImage": "./assets/adaptive-icon.png",
|
||||
"backgroundColor": "#ffffff"
|
||||
},
|
||||
"package": "com.jazz.chatrn"
|
||||
"edgeToEdgeEnabled": true,
|
||||
"package": "tools.jazz.chatrnexpo"
|
||||
},
|
||||
"plugins": ["expo-secure-store", "expo-sqlite", "expo-build-properties"],
|
||||
"extra": {
|
||||
"eas": {
|
||||
"projectId": "e0e61872-1906-4c84-b9d8-9be77355cad0"
|
||||
}
|
||||
"web": {
|
||||
"favicon": "./assets/favicon.png"
|
||||
},
|
||||
"owner": "paxx"
|
||||
"plugins": ["expo-secure-store", "expo-sqlite"]
|
||||
}
|
||||
}
|
||||
|
||||
BIN
examples/chat-rn-expo/assets/splash-icon.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 46 KiB |
@@ -1,9 +0,0 @@
|
||||
module.exports = function (api) {
|
||||
api.cache(true);
|
||||
return {
|
||||
presets: [
|
||||
["babel-preset-expo", { jsxImportSource: "nativewind" }],
|
||||
"nativewind/babel",
|
||||
],
|
||||
};
|
||||
};
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"cli": {
|
||||
"version": ">= 12.5.1",
|
||||
"appVersionSource": "remote"
|
||||
},
|
||||
"build": {
|
||||
"development": {
|
||||
"developmentClient": true,
|
||||
"distribution": "internal"
|
||||
},
|
||||
"ios-simulator": {
|
||||
"extends": "development",
|
||||
"ios": {
|
||||
"simulator": true
|
||||
}
|
||||
},
|
||||
"preview": {
|
||||
"distribution": "internal"
|
||||
},
|
||||
"production": {
|
||||
"autoIncrement": true
|
||||
}
|
||||
},
|
||||
"submit": {
|
||||
"production": {}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -1,4 +0,0 @@
|
||||
import "./polyfills";
|
||||
import { registerRootComponent } from "expo";
|
||||
import App from "./src/App";
|
||||
registerRootComponent(App);
|
||||
9
examples/chat-rn-expo/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { registerRootComponent } from "expo";
|
||||
import "./polyfills";
|
||||
|
||||
import App from "./src/App";
|
||||
|
||||
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
|
||||
// It also ensures that whether you load the app in Expo Go or in a native build,
|
||||
// the environment is set up appropriately
|
||||
registerRootComponent(App);
|
||||
@@ -1,35 +0,0 @@
|
||||
// Learn more https://docs.expo.dev/guides/monorepos
|
||||
const { getDefaultConfig } = require("expo/metro-config");
|
||||
const { withNativeWind } = require("nativewind/metro");
|
||||
const { FileStore } = require("metro-cache");
|
||||
const path = require("path");
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const projectRoot = __dirname;
|
||||
const workspaceRoot = path.resolve(projectRoot, "../..");
|
||||
|
||||
const config = getDefaultConfig(projectRoot, { isCSSEnabled: true });
|
||||
|
||||
// Since we are using pnpm, we have to setup the monorepo manually for Metro
|
||||
// #1 - Watch all files in the monorepo
|
||||
config.watchFolders = [workspaceRoot];
|
||||
// #2 - Try resolving with project modules first, then workspace modules
|
||||
config.resolver.nodeModulesPaths = [
|
||||
path.resolve(projectRoot, "node_modules"),
|
||||
path.resolve(workspaceRoot, "node_modules"),
|
||||
];
|
||||
config.resolver.sourceExts = ["mjs", "js", "json", "ts", "tsx"];
|
||||
config.resolver.requireCycleIgnorePatterns = [
|
||||
/(^|\/|\\)node_modules($|\/|\\)/,
|
||||
/(^|\/|\\)packages($|\/|\\)/,
|
||||
];
|
||||
|
||||
// Use turborepo to restore the cache when possible
|
||||
config.cacheStores = [
|
||||
new FileStore({
|
||||
root: path.join(projectRoot, "node_modules", ".cache", "metro"),
|
||||
}),
|
||||
];
|
||||
|
||||
// module.exports = config;
|
||||
module.exports = withNativeWind(config, { input: "./global.css" });
|
||||
1
examples/chat-rn-expo/nativewind-env.d.ts
vendored
@@ -1 +0,0 @@
|
||||
/// <reference types="nativewind/types" />
|
||||