Compare commits
61 Commits
@nhost/rea
...
@nhost/rea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
024f026241 | ||
|
|
a422a4850d | ||
|
|
a7e67979fe | ||
|
|
1dcbf268db | ||
|
|
5c5d489740 | ||
|
|
a2559e3482 | ||
|
|
bbef104a85 | ||
|
|
7843b1aec1 | ||
|
|
4711bfa8ec | ||
|
|
6f3f8a5020 | ||
|
|
a120bcc8fc | ||
|
|
53e20e87f3 | ||
|
|
9479aeb596 | ||
|
|
c4f11af072 | ||
|
|
747aa96914 | ||
|
|
5682d92592 | ||
|
|
2cf6556499 | ||
|
|
89553fcaf6 | ||
|
|
10beea7246 | ||
|
|
1334ddb693 | ||
|
|
d212128815 | ||
|
|
302c28b202 | ||
|
|
f3f760b987 | ||
|
|
9d9caf9834 | ||
|
|
96cbf023ca | ||
|
|
1ed534cb4a | ||
|
|
dcdee0b426 | ||
|
|
e31f4756b4 | ||
|
|
259e198d80 | ||
|
|
1d10a47414 | ||
|
|
34470ff6e0 | ||
|
|
f30d6779bb | ||
|
|
2a3b2c3af5 | ||
|
|
a0db6b58de | ||
|
|
523d52aa7f | ||
|
|
6e1ee1802d | ||
|
|
51ad1eb355 | ||
|
|
e084643032 | ||
|
|
5514e81186 | ||
|
|
16f38aa893 | ||
|
|
76c6e8d0d6 | ||
|
|
a7d5c85f60 | ||
|
|
bc657251d6 | ||
|
|
5abc362a4d | ||
|
|
7a4c9fa806 | ||
|
|
348318d709 | ||
|
|
4e4600d769 | ||
|
|
44d57d4b89 | ||
|
|
84ba29dd7f | ||
|
|
b5f7f7fe5f | ||
|
|
7116a4df8a | ||
|
|
7e4c52dbd1 | ||
|
|
446dc01bde | ||
|
|
a1989c51f8 | ||
|
|
1383de558a | ||
|
|
d828107f74 | ||
|
|
4a1650cb35 | ||
|
|
913651d440 | ||
|
|
6af2d52666 | ||
|
|
18ac56d097 | ||
|
|
366fc2403d |
20
README.md
20
README.md
@@ -204,21 +204,28 @@ Here are some ways of contributing to making Nhost better:
|
||||
<sub><b>Szilárd Dóró</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/GavanWilhite">
|
||||
<img src="https://avatars.githubusercontent.com/u/2085119?v=4" width="100;" alt="GavanWilhite"/>
|
||||
<br />
|
||||
<sub><b>Gavan Wilhite</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/FuzzyReason">
|
||||
<img src="https://avatars.githubusercontent.com/u/62517920?v=4" width="100;" alt="FuzzyReason"/>
|
||||
<br />
|
||||
<sub><b>Vadim Smirnov</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/macmac49">
|
||||
<img src="https://avatars.githubusercontent.com/u/831190?v=4" width="100;" alt="macmac49"/>
|
||||
<br />
|
||||
<sub><b>Macmac49</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/subhendukundu">
|
||||
<img src="https://avatars.githubusercontent.com/u/20059141?v=4" width="100;" alt="subhendukundu"/>
|
||||
@@ -253,13 +260,6 @@ Here are some ways of contributing to making Nhost better:
|
||||
<br />
|
||||
<sub><b>Filip Hájek</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/GavanWilhite">
|
||||
<img src="https://avatars.githubusercontent.com/u/2085119?v=4" width="100;" alt="GavanWilhite"/>
|
||||
<br />
|
||||
<sub><b>Gavan Wilhite</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/docs
|
||||
|
||||
## 0.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 747aa969: fix: added twitch and discord as provider
|
||||
|
||||
## 0.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -22,4 +22,6 @@ type Provider =
|
||||
| 'strava'
|
||||
| 'gitlab'
|
||||
| 'bitbucket'
|
||||
| 'discord'
|
||||
| 'twitch'
|
||||
```
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: useSignInSmsPasswordless()
|
||||
sidebar_label: useSignInSmsPasswordless()
|
||||
slug: /reference/nextjs/use-sign-in-sms-passwordless
|
||||
description: Use the hook `useSignInSmsPasswordless` to sign in a user with a one-time password sent via SMS to a phone.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/react/src/useSignInSmsPasswordless.ts#L57
|
||||
---
|
||||
|
||||
# `useSignInSmsPasswordless()`
|
||||
|
||||
Use the hook `useSignInSmsPasswordless` to sign in a user with a one-time password sent via SMS to a phone.
|
||||
|
||||
1. The `signInSmsPasswordless` action sends a one-time password to the given phone number.
|
||||
2. The client is then awaiting the OTP. `needsOtp` equals true.
|
||||
3. After the code is received by SMS, the client sends the code with `sendOtp`. On success, the client is authenticated, and `isSuccess` equals `true`.
|
||||
|
||||
Any error is monitored through `isError` and `error`. While the `signInSmsPasswordless` and `sendOtp` actions are running, `isLoading` equals `true`.
|
||||
|
||||
```tsx
|
||||
const {
|
||||
signInSmsPasswordless,
|
||||
sendOtp,
|
||||
needsOtp,
|
||||
isLoading,
|
||||
isSuccess,
|
||||
isError,
|
||||
error
|
||||
} = useSignInSmsPasswordless()
|
||||
|
||||
console.log({ isLoading, isSuccess, isError, error })
|
||||
|
||||
const askCode = async (e) => {
|
||||
e.preventDefault()
|
||||
await signInSmsPasswordless('+32455555555')
|
||||
}
|
||||
|
||||
const sendCode = async (e) => {
|
||||
e.preventDefault()
|
||||
await sendOtp('123456')
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">stateOptions</span>** <span className="optional-status">optional</span> `PasswordlessOptions`
|
||||
|
||||
---
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: SignInSmsPasswordlessHandler
|
||||
sidebar_label: SignInSmsPasswordlessHandler
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/react/src/useSignInSmsPasswordless.ts#L14
|
||||
---
|
||||
|
||||
# `SignInSmsPasswordlessHandler`
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: SignInSmsPasswordlessHookResult
|
||||
sidebar_label: SignInSmsPasswordlessHookResult
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/react/src/useSignInSmsPasswordless.ts#L23
|
||||
---
|
||||
|
||||
# `SignInSmsPasswordlessHookResult`
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">needsOtp</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
Returns true when the one-time password has been sent over by SMS, and the user needs to send it back to complete sign-in.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isError</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` if an error occurred
|
||||
|
||||
**`@depreacted`**
|
||||
|
||||
use `!isSuccess` or `!!error` instead
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">error</span>** <span className="optional-status">required</span> `null` | `ErrorPayload`
|
||||
|
||||
Provides details about the error
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isLoading</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` when the action is executing, `false` when it finished its execution.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isSuccess</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
Returns `true` if the action is successful.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">signInSmsPasswordless</span>** <span className="optional-status">required</span> [`SignInSmsPasswordlessHandler`](/reference/docgen/nextjs/types/sign-in-sms-passwordless-handler)
|
||||
|
||||
Sends a one-time code to the given phoneNumber
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">sendOtp</span>** <span className="optional-status">required</span> [`SignInSmsPasswordlessOtpHandler`](/reference/docgen/nextjs/types/sign-in-sms-passwordless-otp-handler)
|
||||
|
||||
---
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: SignInSmsPasswordlessOtpHandler
|
||||
sidebar_label: SignInSmsPasswordlessOtpHandler
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/react/src/useSignInSmsPasswordless.ts#L18
|
||||
---
|
||||
|
||||
# `SignInSmsPasswordlessOtpHandler`
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: useSignInSmsPasswordless()
|
||||
sidebar_label: useSignInSmsPasswordless()
|
||||
slug: /reference/react/use-sign-in-sms-passwordless
|
||||
description: Use the hook `useSignInSmsPasswordless` to sign in a user with a one-time password sent via SMS to a phone.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/react/src/useSignInSmsPasswordless.ts#L57
|
||||
---
|
||||
|
||||
# `useSignInSmsPasswordless()`
|
||||
|
||||
Use the hook `useSignInSmsPasswordless` to sign in a user with a one-time password sent via SMS to a phone.
|
||||
|
||||
1. The `signInSmsPasswordless` action sends a one-time password to the given phone number.
|
||||
2. The client is then awaiting the OTP. `needsOtp` equals true.
|
||||
3. After the code is received by SMS, the client sends the code with `sendOtp`. On success, the client is authenticated, and `isSuccess` equals `true`.
|
||||
|
||||
Any error is monitored through `isError` and `error`. While the `signInSmsPasswordless` and `sendOtp` actions are running, `isLoading` equals `true`.
|
||||
|
||||
```tsx
|
||||
const {
|
||||
signInSmsPasswordless,
|
||||
sendOtp,
|
||||
needsOtp,
|
||||
isLoading,
|
||||
isSuccess,
|
||||
isError,
|
||||
error
|
||||
} = useSignInSmsPasswordless()
|
||||
|
||||
console.log({ isLoading, isSuccess, isError, error })
|
||||
|
||||
const askCode = async (e) => {
|
||||
e.preventDefault()
|
||||
await signInSmsPasswordless('+32455555555')
|
||||
}
|
||||
|
||||
const sendCode = async (e) => {
|
||||
e.preventDefault()
|
||||
await sendOtp('123456')
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">stateOptions</span>** <span className="optional-status">optional</span> `PasswordlessOptions`
|
||||
|
||||
---
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: SignInSmsPasswordlessHandler
|
||||
sidebar_label: SignInSmsPasswordlessHandler
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/react/src/useSignInSmsPasswordless.ts#L14
|
||||
---
|
||||
|
||||
# `SignInSmsPasswordlessHandler`
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: SignInSmsPasswordlessHookResult
|
||||
sidebar_label: SignInSmsPasswordlessHookResult
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/react/src/useSignInSmsPasswordless.ts#L23
|
||||
---
|
||||
|
||||
# `SignInSmsPasswordlessHookResult`
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">needsOtp</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
Returns true when the one-time password has been sent over by SMS, and the user needs to send it back to complete sign-in.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isError</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` if an error occurred
|
||||
|
||||
**`@depreacted`**
|
||||
|
||||
use `!isSuccess` or `!!error` instead
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">error</span>** <span className="optional-status">required</span> `null` | `ErrorPayload`
|
||||
|
||||
Provides details about the error
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isLoading</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` when the action is executing, `false` when it finished its execution.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isSuccess</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
Returns `true` if the action is successful.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">signInSmsPasswordless</span>** <span className="optional-status">required</span> [`SignInSmsPasswordlessHandler`](/reference/docgen/react/types/sign-in-sms-passwordless-handler)
|
||||
|
||||
Sends a one-time code to the given phoneNumber
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">sendOtp</span>** <span className="optional-status">required</span> [`SignInSmsPasswordlessOtpHandler`](/reference/docgen/react/types/sign-in-sms-passwordless-otp-handler)
|
||||
|
||||
---
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: SignInSmsPasswordlessOtpHandler
|
||||
sidebar_label: SignInSmsPasswordlessOtpHandler
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/react/src/useSignInSmsPasswordless.ts#L18
|
||||
---
|
||||
|
||||
# `SignInSmsPasswordlessOtpHandler`
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: useSignInSmsPasswordless()
|
||||
sidebar_label: useSignInSmsPasswordless()
|
||||
slug: /reference/vue/use-sign-in-sms-passwordless
|
||||
description: Use the composable `useSignInSmsPasswordless` to sign in a user with a one-time password sent via SMS to a phone.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/vue/src/useSignInSmsPasswordless.ts#L65
|
||||
---
|
||||
|
||||
# `useSignInSmsPasswordless()`
|
||||
|
||||
Use the composable `useSignInSmsPasswordless` to sign in a user with a one-time password sent via SMS to a phone.
|
||||
|
||||
1. The `signInSmsPasswordless` action sends a one-time password to the given phone number.
|
||||
2. The client is then awaiting the OTP. `needsOtp` equals true.
|
||||
3. After the code is received by SMS, the client sends the code with `sendOtp`. On success, the client is authenticated, and `isSuccess` equals `true`.
|
||||
|
||||
Any error is monitored through `isError` and `error`. While the `signInSmsPasswordless` and `sendOtp` actions are running, `isLoading` equals `true`.
|
||||
|
||||
```tsx
|
||||
const {
|
||||
signInSmsPasswordless,
|
||||
sendOtp,
|
||||
needsOtp,
|
||||
isLoading,
|
||||
isSuccess,
|
||||
isError,
|
||||
error
|
||||
} = useSignInSmsPasswordless()
|
||||
|
||||
console.log({ isLoading, isSuccess, isError, error })
|
||||
|
||||
const askCode = async (e) => {
|
||||
e.preventDefault()
|
||||
await signInSmsPasswordless('+32455555555')
|
||||
}
|
||||
|
||||
const sendCode = async (e) => {
|
||||
e.preventDefault()
|
||||
await sendOtp('123456')
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">stateOptions</span>** <span className="optional-status">optional</span> `NestedRefOfValue<undefined | PasswordlessOptions>`
|
||||
|
||||
---
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: SignInSmsPasswordlessComposableResult
|
||||
sidebar_label: SignInSmsPasswordlessComposableResult
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/vue/src/useSignInSmsPasswordless.ts#L31
|
||||
---
|
||||
|
||||
# `SignInSmsPasswordlessComposableResult`
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">needsOtp</span>** <span className="optional-status">required</span> `Ref<boolean>`
|
||||
|
||||
Returns true when the one-time password has been sent over by SMS, and the user needs to send it back to complete sign-in.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isError</span>** <span className="optional-status">required</span> `Ref<boolean>`
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` if an error occurred
|
||||
|
||||
**`@depreacted`**
|
||||
|
||||
use `!isSuccess` or `!!error` instead
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">error</span>** <span className="optional-status">required</span> `Ref<null | ErrorPayload>`
|
||||
|
||||
Provides details about the error
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isLoading</span>** <span className="optional-status">required</span> `Ref<boolean>`
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` when the action is executing, `false` when it finished its execution.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isSuccess</span>** <span className="optional-status">required</span> `Ref<boolean>`
|
||||
|
||||
Returns `true` if the action is successful.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">signInSmsPasswordless</span>** <span className="optional-status">required</span> [`SignInSmsPasswordlessHandler`](/reference/docgen/vue/types/sign-in-sms-passwordless-handler)
|
||||
|
||||
Sends a one-time code to the given phoneNumber
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">sendOtp</span>** <span className="optional-status">required</span> [`SignInSmsPasswordlessOtpHandler`](/reference/docgen/vue/types/sign-in-sms-passwordless-otp-handler)
|
||||
|
||||
---
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: SignInSmsPasswordlessHandler
|
||||
sidebar_label: SignInSmsPasswordlessHandler
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/vue/src/useSignInSmsPasswordless.ts#L16
|
||||
---
|
||||
|
||||
# `SignInSmsPasswordlessHandler`
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: SignInSmsPasswordlessOtpHandler
|
||||
sidebar_label: SignInSmsPasswordlessOtpHandler
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/vue/src/useSignInSmsPasswordless.ts#L23
|
||||
---
|
||||
|
||||
# `SignInSmsPasswordlessOtpHandler`
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/docs",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"verify:fix": "run-p prettier:fix lint:fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.6.2",
|
||||
"@apollo/client": "^3.6.9",
|
||||
"@mantine/core": "^4.2.2",
|
||||
"@mantine/hooks": "^4.2.2",
|
||||
"@mantine/next": "^4.2.2",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.5.10",
|
||||
"@apollo/client": "^3.6.9",
|
||||
"@headlessui/react": "^1.5.0",
|
||||
"@heroicons/react": "^1.0.6",
|
||||
"@nhost/react": "*",
|
||||
@@ -55,11 +55,11 @@
|
||||
"@graphql-codegen/typescript": "^2.4.8",
|
||||
"@graphql-codegen/typescript-operations": "^2.3.5",
|
||||
"@graphql-codegen/typescript-react-apollo": "^3.2.11",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "^26.0.24",
|
||||
"@types/node": "^12.20.48",
|
||||
"@types/react": "^17.0.44",
|
||||
"@types/react-dom": "^17.0.15",
|
||||
"@types/express": "^4.17.13",
|
||||
"@vitejs/plugin-react": "^1.3.2",
|
||||
"autoprefixer": "^10.4.4",
|
||||
"express": "^4.17.3",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# React-Apollo example
|
||||
# Nhost React + Apollo example
|
||||
|
||||
## See this example live
|
||||
|
||||
@@ -13,20 +13,26 @@ git clone https://github.com/nhost/nhost
|
||||
cd nhost
|
||||
```
|
||||
|
||||
2. Install dependencies
|
||||
2. Install and build dependencies
|
||||
|
||||
```sh
|
||||
pnpm install
|
||||
pnpm run build
|
||||
```
|
||||
|
||||
3. Go to the example folder
|
||||
|
||||
```sh
|
||||
cd examples/react-apollo
|
||||
pnpm install
|
||||
```
|
||||
|
||||
3. Terminal 1: Start Nhost
|
||||
4. Terminal 1: Start Nhost
|
||||
|
||||
```sh
|
||||
nhost dev
|
||||
```
|
||||
|
||||
4. Terminal 2: Start the React application
|
||||
5. Terminal 2: Start the React application
|
||||
|
||||
```sh
|
||||
pnpm run dev
|
||||
|
||||
@@ -43,36 +43,32 @@ context('Sign in with email+password', () => {
|
||||
.findByRole('button')
|
||||
.click()
|
||||
|
||||
cy.findByText(/Activate 2-step verification/i)
|
||||
.get('img')
|
||||
.then(async (img) => {
|
||||
// * Activate MFA
|
||||
const result = await new Decoder().scan(img.prop('src'))
|
||||
const [, params] = result.data.split('?')
|
||||
const { secret, algorithm, digits, period } = Object.fromEntries(
|
||||
new URLSearchParams(params)
|
||||
)
|
||||
const code = totp(secret, {
|
||||
algorithm: algorithm.replace('SHA1', 'SHA-1'),
|
||||
digits: parseInt(digits),
|
||||
period: parseInt(period)
|
||||
})
|
||||
cy.findByPlaceholderText('Enter activation code').type(code)
|
||||
cy.findByRole('button', { name: /Activate/i }).click()
|
||||
cy.contains('MFA has been activated!!!')
|
||||
cy.signOut()
|
||||
|
||||
// * Sign-in with MFA
|
||||
cy.visit('/sign-in')
|
||||
cy.findByRole('button', { name: /Continue with email \+ password/i }).click()
|
||||
cy.findByPlaceholderText('Email Address').type(email)
|
||||
cy.findByPlaceholderText('Password').type(password)
|
||||
cy.findByRole('button', { name: /Sign in/i }).click()
|
||||
cy.contains('Send 2-step verification code')
|
||||
const newCode = totp(secret, { timestamp: Date.now() })
|
||||
cy.findByPlaceholderText('One-time password').type(newCode)
|
||||
cy.findByRole('button', { name: /Send 2-step verification code/i }).click()
|
||||
cy.contains('You are authenticated')
|
||||
cy.findAllByAltText(/qrcode/i).then(async (img) => {
|
||||
// * Activate MFA
|
||||
const result = await new Decoder().scan(img.prop('src'))
|
||||
const [, params] = result.data.split('?')
|
||||
const { secret, algorithm, digits, period } = Object.fromEntries(new URLSearchParams(params))
|
||||
const code = totp(secret, {
|
||||
algorithm: algorithm.replace('SHA1', 'SHA-1'),
|
||||
digits: parseInt(digits),
|
||||
period: parseInt(period)
|
||||
})
|
||||
cy.findByPlaceholderText('Enter activation code').type(code)
|
||||
cy.findByRole('button', { name: /Activate/i }).click()
|
||||
cy.contains('MFA has been activated!!!')
|
||||
cy.signOut()
|
||||
|
||||
// * Sign-in with MFA
|
||||
cy.visit('/sign-in')
|
||||
cy.findByRole('button', { name: /Continue with email \+ password/i }).click()
|
||||
cy.findByPlaceholderText('Email Address').type(email)
|
||||
cy.findByPlaceholderText('Password').type(password)
|
||||
cy.findByRole('button', { name: /Sign in/i }).click()
|
||||
cy.contains('Send 2-step verification code')
|
||||
const newCode = totp(secret, { timestamp: Date.now() })
|
||||
cy.findByPlaceholderText('One-time password').type(newCode)
|
||||
cy.findByRole('button', { name: /Send 2-step verification code/i }).click()
|
||||
cy.contains('You are authenticated')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Nhost with React and Apollo</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,30 @@
|
||||
table:
|
||||
name: user_authenticators
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
credential_id:
|
||||
custom_name: credentialId
|
||||
credential_public_key:
|
||||
custom_name: credentialPublicKey
|
||||
user_id:
|
||||
custom_name: userId
|
||||
custom_column_names:
|
||||
credential_id: credentialId
|
||||
credential_public_key: credentialPublicKey
|
||||
user_id: userId
|
||||
custom_name: authUserAuthenticators
|
||||
custom_root_fields:
|
||||
delete: deleteAuthUserAuthenticators
|
||||
delete_by_pk: deleteAuthUserAuthenticator
|
||||
insert: insertAuthUserAuthenticators
|
||||
insert_one: insertAuthUserAuthenticator
|
||||
select: authUserAuthenticators
|
||||
select_aggregate: authUserAuthenticatorsAggregate
|
||||
select_by_pk: authUserAuthenticator
|
||||
update: updateAuthUserAuthenticators
|
||||
update_by_pk: updateAuthUserAuthenticator
|
||||
object_relationships:
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
@@ -39,6 +39,8 @@ configuration:
|
||||
custom_name: totpSecret
|
||||
updated_at:
|
||||
custom_name: updatedAt
|
||||
webauthn_current_challenge:
|
||||
custom_name: currentChallenge
|
||||
custom_column_names:
|
||||
active_mfa_type: activeMfaType
|
||||
avatar_url: avatarUrl
|
||||
@@ -58,6 +60,7 @@ configuration:
|
||||
ticket_expires_at: ticketExpiresAt
|
||||
totp_secret: totpSecret
|
||||
updated_at: updatedAt
|
||||
webauthn_current_challenge: currentChallenge
|
||||
custom_name: users
|
||||
custom_root_fields:
|
||||
delete: deleteUsers
|
||||
@@ -74,6 +77,13 @@ object_relationships:
|
||||
using:
|
||||
foreign_key_constraint_on: default_role
|
||||
array_relationships:
|
||||
- name: authenticators
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_authenticators
|
||||
schema: auth
|
||||
- name: refreshTokens
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
- "!include auth_providers.yaml"
|
||||
- "!include auth_refresh_tokens.yaml"
|
||||
- "!include auth_roles.yaml"
|
||||
- "!include auth_user_authenticators.yaml"
|
||||
- "!include auth_user_providers.yaml"
|
||||
- "!include auth_user_roles.yaml"
|
||||
- "!include auth_users.yaml"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.6.2",
|
||||
"@apollo/client": "^3.6.9",
|
||||
"@mantine/core": "^4.2.2",
|
||||
"@mantine/dropzone": "^4.2.6",
|
||||
"@mantine/hooks": "^4.2.2",
|
||||
@@ -16,7 +16,8 @@
|
||||
"react-dom": "^18.1.0",
|
||||
"react-icons": "^4.3.1",
|
||||
"react-router": "^6.3.0",
|
||||
"react-router-dom": "^6.3.0"
|
||||
"react-router-dom": "^6.3.0",
|
||||
"tabler-icons-react": "^1.52.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -66,4 +67,4 @@
|
||||
"ws": "^8.7.0",
|
||||
"xstate": "^4.32.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
examples/react-apollo/public/favicon.svg
Normal file
10
examples/react-apollo/public/favicon.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M88.0355 21.4793L53.1776 1.35118C50.0502 -0.450393 46.168 -0.450393 43.0343 1.35118C39.9069 3.1591 37.9657 6.52119 37.9657 10.1307V12.7569L35.6948 11.4438C32.5674 9.64222 28.6851 9.64222 25.5514 11.4438C22.424 13.2517 20.4829 16.6138 20.4829 20.2296V22.8559L18.2119 21.5428C15.0845 19.7412 11.2022 19.7412 8.06851 21.5428C4.94113 23.3507 3 26.7128 3 30.3286V93.3963C3 95.2106 4.05303 96.898 5.68967 97.6846C7.31996 98.4775 9.29916 98.2619 10.7201 97.1391L28.0063 83.5067L54.662 98.8962C55.3979 99.3212 56.2225 99.5306 57.0472 99.5306C57.8719 99.5306 58.6965 99.3149 59.4324 98.8962C60.9041 98.0462 61.8176 96.4666 61.8176 94.7666V56.813C61.8176 50.5836 58.4682 44.7856 53.0761 41.6709L44.3347 36.6214V10.137C44.3347 8.79218 45.0579 7.53616 46.2251 6.86374C47.3923 6.19132 48.8386 6.19132 50.0058 6.86374L84.8638 26.9855C88.2956 28.9647 90.4271 32.663 90.4271 36.6214V83.881C90.4271 85.2258 89.7039 86.4819 88.5367 87.1543L79.3004 92.4892V46.714C79.3004 40.4846 75.951 34.6866 70.559 31.5719L49.0987 19.1829V26.5225L67.3809 37.0782C70.8127 39.0573 72.9442 42.7493 72.9442 46.714V95.236C72.9442 96.9297 73.8577 98.5156 75.3294 99.3656C76.0652 99.7907 76.8899 100 77.7145 100C78.5392 100 79.3639 99.7843 80.0997 99.3656L91.7212 92.6541C94.8485 90.8462 96.7897 87.4841 96.7897 83.8683V36.6087C96.777 30.3984 93.4276 24.594 88.0355 21.4793ZM49.8853 47.1771C53.3172 49.1563 55.4486 52.8483 55.4486 56.813V92.0198L33.373 79.2756L40.4588 73.6932C42.9137 71.7584 44.322 68.8594 44.322 65.732V43.9736L49.8853 47.1771ZM37.9657 40.2943V65.7194C37.9657 66.8866 37.4392 67.9713 36.5258 68.6881L9.35626 90.1104V30.3223C9.35626 28.9774 10.0794 27.7214 11.2466 27.049C12.4139 26.3766 13.8602 26.3766 15.0274 27.049L20.4829 30.1954V75.2664L26.8391 70.255V20.2296C26.8391 18.8848 27.5623 17.6288 28.7295 16.9564C29.8967 16.2839 31.3431 16.2839 32.5103 16.9564L37.9657 20.1028V32.9485L31.6095 29.2756V36.6214L37.9657 40.2943Z" fill="#0052CD"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="93.7897" height="100" fill="white" transform="translate(3)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -1,6 +1,7 @@
|
||||
import { Route, Routes } from 'react-router-dom'
|
||||
import { BrandGithub } from 'tabler-icons-react'
|
||||
|
||||
import { AppShell, Header, MantineProvider } from '@mantine/core'
|
||||
import { AppShell, Button, Group, Header, Image, MantineProvider, Title } from '@mantine/core'
|
||||
import { NotificationsProvider } from '@mantine/notifications'
|
||||
|
||||
import { AuthGate, PublicGate } from './components/auth-gates'
|
||||
@@ -17,13 +18,13 @@ import './App.css'
|
||||
const title = 'Nhost with React and Apollo'
|
||||
|
||||
function App() {
|
||||
const colorScheme = 'light'
|
||||
return (
|
||||
<MantineProvider
|
||||
withGlobalStyles
|
||||
withNormalizeCSS
|
||||
theme={{
|
||||
/** Put your mantine theme override here */
|
||||
colorScheme: 'light'
|
||||
colorScheme
|
||||
}}
|
||||
>
|
||||
<NotificationsProvider>
|
||||
@@ -32,7 +33,24 @@ function App() {
|
||||
navbar={<NavBar />}
|
||||
header={
|
||||
<Header height={60} p="xs">
|
||||
{title}
|
||||
<Group position="apart" noWrap>
|
||||
<Group noWrap>
|
||||
<Image src="/logo.svg" height={35} fit="contain" width={120} />
|
||||
<Title order={3} style={{ whiteSpace: 'nowrap' }}>
|
||||
{title}
|
||||
</Title>
|
||||
</Group>
|
||||
<Button
|
||||
leftIcon={<BrandGithub />}
|
||||
variant="outline"
|
||||
color={colorScheme}
|
||||
component="a"
|
||||
href="https://github.com/nhost/nhost/tree/main/examples/react-apollo"
|
||||
target="_blank"
|
||||
>
|
||||
GitHub
|
||||
</Button>
|
||||
</Group>
|
||||
</Header>
|
||||
}
|
||||
styles={(theme) => ({
|
||||
|
||||
@@ -1 +1,39 @@
|
||||
# Nhost Vue3 Apollo example
|
||||
# Nhost Vue 3 + Apollo example
|
||||
|
||||
## See this example live
|
||||
|
||||
Visit our demo application on [vue-apollo.example.nhost.io](https://vue-apollo.example.nhost.io)
|
||||
|
||||
## Get started
|
||||
|
||||
1. Clone the repository
|
||||
|
||||
```sh
|
||||
git clone https://github.com/nhost/nhost
|
||||
cd nhost
|
||||
```
|
||||
|
||||
2. Install and build dependencies
|
||||
|
||||
```sh
|
||||
pnpm install
|
||||
pnpm build
|
||||
```
|
||||
|
||||
3. Go to the example folder
|
||||
|
||||
```sh
|
||||
cd examples/vue-apollo
|
||||
```
|
||||
|
||||
4. Terminal 1: Start Nhost
|
||||
|
||||
```sh
|
||||
nhost dev
|
||||
```
|
||||
|
||||
5. Terminal 2: Start the Vue application
|
||||
|
||||
```sh
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Nhost with Vue and Apollo</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -18,7 +18,7 @@ auth:
|
||||
blocked_email_domains: ''
|
||||
blocked_emails: ''
|
||||
allowed_redirect_urls: ''
|
||||
anonymous_users_enabled: false
|
||||
anonymous_users_enabled: true
|
||||
client_url: http://localhost:3000
|
||||
disable_new_users: false
|
||||
email:
|
||||
|
||||
@@ -2,6 +2,7 @@ table:
|
||||
name: provider_requests
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config: {}
|
||||
custom_column_names: {}
|
||||
custom_name: authProviderRequests
|
||||
custom_root_fields:
|
||||
|
||||
@@ -2,6 +2,7 @@ table:
|
||||
name: providers
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config: {}
|
||||
custom_column_names: {}
|
||||
custom_name: authProviders
|
||||
custom_root_fields:
|
||||
|
||||
@@ -2,6 +2,15 @@ table:
|
||||
name: refresh_tokens
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
created_at:
|
||||
custom_name: createdAt
|
||||
expires_at:
|
||||
custom_name: expiresAt
|
||||
refresh_token:
|
||||
custom_name: refreshToken
|
||||
user_id:
|
||||
custom_name: userId
|
||||
custom_column_names:
|
||||
created_at: createdAt
|
||||
expires_at: expiresAt
|
||||
|
||||
@@ -2,6 +2,7 @@ table:
|
||||
name: roles
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config: {}
|
||||
custom_column_names: {}
|
||||
custom_name: authRoles
|
||||
custom_root_fields:
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
table:
|
||||
name: user_authenticators
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
credential_id:
|
||||
custom_name: credentialId
|
||||
credential_public_key:
|
||||
custom_name: credentialPublicKey
|
||||
user_id:
|
||||
custom_name: userId
|
||||
custom_column_names:
|
||||
credential_id: credentialId
|
||||
credential_public_key: credentialPublicKey
|
||||
user_id: userId
|
||||
custom_name: authUserAuthenticators
|
||||
custom_root_fields:
|
||||
delete: deleteAuthUserAuthenticators
|
||||
delete_by_pk: deleteAuthUserAuthenticator
|
||||
insert: insertAuthUserAuthenticators
|
||||
insert_one: insertAuthUserAuthenticator
|
||||
select: authUserAuthenticators
|
||||
select_aggregate: authUserAuthenticatorsAggregate
|
||||
select_by_pk: authUserAuthenticator
|
||||
update: updateAuthUserAuthenticators
|
||||
update_by_pk: updateAuthUserAuthenticator
|
||||
object_relationships:
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
@@ -2,6 +2,21 @@ table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
access_token:
|
||||
custom_name: accessToken
|
||||
created_at:
|
||||
custom_name: createdAt
|
||||
provider_id:
|
||||
custom_name: providerId
|
||||
provider_user_id:
|
||||
custom_name: providerUserId
|
||||
refresh_token:
|
||||
custom_name: refreshToken
|
||||
updated_at:
|
||||
custom_name: updatedAt
|
||||
user_id:
|
||||
custom_name: userId
|
||||
custom_column_names:
|
||||
access_token: accessToken
|
||||
created_at: createdAt
|
||||
|
||||
@@ -2,6 +2,11 @@ table:
|
||||
name: user_roles
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
created_at:
|
||||
custom_name: createdAt
|
||||
user_id:
|
||||
custom_name: userId
|
||||
custom_column_names:
|
||||
created_at: createdAt
|
||||
user_id: userId
|
||||
|
||||
@@ -2,6 +2,45 @@ table:
|
||||
name: users
|
||||
schema: auth
|
||||
configuration:
|
||||
column_config:
|
||||
active_mfa_type:
|
||||
custom_name: activeMfaType
|
||||
avatar_url:
|
||||
custom_name: avatarUrl
|
||||
created_at:
|
||||
custom_name: createdAt
|
||||
default_role:
|
||||
custom_name: defaultRole
|
||||
display_name:
|
||||
custom_name: displayName
|
||||
email_verified:
|
||||
custom_name: emailVerified
|
||||
is_anonymous:
|
||||
custom_name: isAnonymous
|
||||
last_seen:
|
||||
custom_name: lastSeen
|
||||
new_email:
|
||||
custom_name: newEmail
|
||||
otp_hash:
|
||||
custom_name: otpHash
|
||||
otp_hash_expires_at:
|
||||
custom_name: otpHashExpiresAt
|
||||
otp_method_last_used:
|
||||
custom_name: otpMethodLastUsed
|
||||
password_hash:
|
||||
custom_name: passwordHash
|
||||
phone_number:
|
||||
custom_name: phoneNumber
|
||||
phone_number_verified:
|
||||
custom_name: phoneNumberVerified
|
||||
ticket_expires_at:
|
||||
custom_name: ticketExpiresAt
|
||||
totp_secret:
|
||||
custom_name: totpSecret
|
||||
updated_at:
|
||||
custom_name: updatedAt
|
||||
webauthn_current_challenge:
|
||||
custom_name: currentChallenge
|
||||
custom_column_names:
|
||||
active_mfa_type: activeMfaType
|
||||
avatar_url: avatarUrl
|
||||
@@ -21,6 +60,7 @@ configuration:
|
||||
ticket_expires_at: ticketExpiresAt
|
||||
totp_secret: totpSecret
|
||||
updated_at: updatedAt
|
||||
webauthn_current_challenge: currentChallenge
|
||||
custom_name: users
|
||||
custom_root_fields:
|
||||
delete: deleteUsers
|
||||
@@ -37,6 +77,13 @@ object_relationships:
|
||||
using:
|
||||
foreign_key_constraint_on: default_role
|
||||
array_relationships:
|
||||
- name: authenticators
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: user_id
|
||||
table:
|
||||
name: user_authenticators
|
||||
schema: auth
|
||||
- name: refreshTokens
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
|
||||
@@ -2,6 +2,23 @@ table:
|
||||
name: buckets
|
||||
schema: storage
|
||||
configuration:
|
||||
column_config:
|
||||
cache_control:
|
||||
custom_name: cacheControl
|
||||
created_at:
|
||||
custom_name: createdAt
|
||||
download_expiration:
|
||||
custom_name: downloadExpiration
|
||||
id:
|
||||
custom_name: id
|
||||
max_upload_file_size:
|
||||
custom_name: maxUploadFileSize
|
||||
min_upload_file_size:
|
||||
custom_name: minUploadFileSize
|
||||
presigned_urls_enabled:
|
||||
custom_name: presignedUrlsEnabled
|
||||
updated_at:
|
||||
custom_name: updatedAt
|
||||
custom_column_names:
|
||||
cache_control: cacheControl
|
||||
created_at: createdAt
|
||||
|
||||
@@ -2,6 +2,27 @@ table:
|
||||
name: files
|
||||
schema: storage
|
||||
configuration:
|
||||
column_config:
|
||||
bucket_id:
|
||||
custom_name: bucketId
|
||||
created_at:
|
||||
custom_name: createdAt
|
||||
etag:
|
||||
custom_name: etag
|
||||
id:
|
||||
custom_name: id
|
||||
is_uploaded:
|
||||
custom_name: isUploaded
|
||||
mime_type:
|
||||
custom_name: mimeType
|
||||
name:
|
||||
custom_name: name
|
||||
size:
|
||||
custom_name: size
|
||||
updated_at:
|
||||
custom_name: updatedAt
|
||||
uploaded_by_user_id:
|
||||
custom_name: uploadedByUserId
|
||||
custom_column_names:
|
||||
bucket_id: bucketId
|
||||
created_at: createdAt
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
- "!include auth_providers.yaml"
|
||||
- "!include auth_refresh_tokens.yaml"
|
||||
- "!include auth_roles.yaml"
|
||||
- "!include auth_user_authenticators.yaml"
|
||||
- "!include auth_user_providers.yaml"
|
||||
- "!include auth_user_roles.yaml"
|
||||
- "!include auth_users.yaml"
|
||||
|
||||
@@ -14,16 +14,16 @@
|
||||
"verify:fix": "run-p prettier:fix lint:fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.6.2",
|
||||
"@apollo/client": "^3.6.9",
|
||||
"@mdi/font": "5.9.55",
|
||||
"@nhost/apollo": "*",
|
||||
"@nhost/vue": "*",
|
||||
"@vue/apollo-composable": "^4.0.0-alpha.17",
|
||||
"@vue/apollo-composable": "4.0.0-alpha.18",
|
||||
"graphql": "15.7.2",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"roboto-fontface": "*",
|
||||
"vue": "^3.2.25",
|
||||
"vue-router": "^4.0.3",
|
||||
"vue": "^3.2.37",
|
||||
"vue-router": "^4.1.2",
|
||||
"vuetify": "^3.0.0-beta.0",
|
||||
"webfontloader": "^1.0.0"
|
||||
},
|
||||
@@ -31,12 +31,12 @@
|
||||
"@nhost/core": "*",
|
||||
"@types/webfontloader": "^1.0.0",
|
||||
"@vitejs/plugin-vue": "^2.3.1",
|
||||
"@vuetify/vite-plugin": "^1.0.0-alpha.0",
|
||||
"@vuetify/vite-plugin": "1.0.0-alpha.11",
|
||||
"@xstate/inspect": "^0.6.2",
|
||||
"sass": "1.32.0",
|
||||
"typescript": "^4.5.4",
|
||||
"typescript": "^4.7.4",
|
||||
"vite": "^2.9.0",
|
||||
"vue-tsc": "^0.29.8"
|
||||
"vue-tsc": "^0.38.5"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
<template #prepend>
|
||||
<v-app-bar-nav-icon @click.stop="drawer = !drawer" />
|
||||
</template>
|
||||
<v-app-bar-title>Nhost with Vue and Apollo</v-app-bar-title>
|
||||
<template #append>
|
||||
<v-btn icon="mdi-github" href="https://github.com/nhost/nhost/tree/main/examples/vue-apollo" target="_blank" />
|
||||
<v-btn v-if="isAuthenticated" icon="mdi-exit-to-app" @click="signOutHandler" />
|
||||
</template>
|
||||
</v-app-bar>
|
||||
<v-navigation-drawer v-model="drawer" permanent>
|
||||
<v-navigation-drawer v-model="drawer" :permanent="mdAndUp">
|
||||
<nav-bar />
|
||||
</v-navigation-drawer>
|
||||
<v-main class="my-4">
|
||||
@@ -17,30 +19,22 @@
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
<script lang="ts" setup>
|
||||
import { useDisplay } from 'vuetify'
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import { useAuthenticated, useSignOut } from '@nhost/vue'
|
||||
|
||||
import NavBar from './components/NavBar.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: { NavBar },
|
||||
setup() {
|
||||
const router = useRouter()
|
||||
const isAuthenticated = useAuthenticated()
|
||||
const { signOut } = useSignOut()
|
||||
const drawer = ref(true)
|
||||
const signOutHandler = async () => {
|
||||
await signOut()
|
||||
router.replace('/signout')
|
||||
}
|
||||
return {
|
||||
drawer,
|
||||
isAuthenticated,
|
||||
signOutHandler
|
||||
}
|
||||
}
|
||||
})
|
||||
const { mdAndUp } = useDisplay()
|
||||
const router = useRouter()
|
||||
const isAuthenticated = useAuthenticated()
|
||||
const { signOut } = useSignOut()
|
||||
const drawer = ref()
|
||||
const signOutHandler = async () => {
|
||||
await signOut()
|
||||
router.replace('/signout')
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,25 +1,45 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-text-field v-model="email" placeholder="Email Address" autofocus />
|
||||
<v-btn block color="primary" @click="signIn"> Continue with email </v-btn>
|
||||
<form @submit="submit">
|
||||
<v-text-field v-model="email" placeholder="Email Address" autofocus />
|
||||
<v-btn block color="primary" type="submit" :disabled="isLoading" :loading="isLoading"> Continue with email
|
||||
</v-btn>
|
||||
</form>
|
||||
<error-snack-bar :error="error" />
|
||||
<v-dialog v-model="emailSentDialog">
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<span class="text-h5">Verification email sent</span>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
A email has been sent to {{ email }}. Please follow the link to sign in to the application.
|
||||
</v-card-text>
|
||||
<v-card-actions class="d-flex justify-center">
|
||||
<v-btn text @click="emailSentDialog = false">
|
||||
Close
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { useSignInEmailPasswordless } from '@nhost/vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const email = ref('')
|
||||
const { signInEmailPasswordless, error } = useSignInEmailPasswordless({
|
||||
redirectTo: '/#/profile'
|
||||
})
|
||||
const email = ref('')
|
||||
const emailSentDialog = ref(false)
|
||||
|
||||
const signIn = () => signInEmailPasswordless(email)
|
||||
return { email, signIn, error }
|
||||
}
|
||||
const { signInEmailPasswordless, error, isLoading } = useSignInEmailPasswordless({
|
||||
redirectTo: '/profile'
|
||||
})
|
||||
|
||||
const submit = async (e: Event) => {
|
||||
e.preventDefault()
|
||||
const { isSuccess } = await signInEmailPasswordless(email)
|
||||
if (isSuccess) {
|
||||
emailSentDialog.value = true
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-snackbar v-model="snack" :timeout="2_000">
|
||||
<v-snackbar v-model="snack" :timeout="2_000" top>
|
||||
{{ error?.message }}
|
||||
<template #actions>
|
||||
<v-btn color="blue" variant="text" @click="snack = false"> Close </v-btn>
|
||||
@@ -7,24 +7,21 @@
|
||||
</v-snackbar>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, ref, watchEffect } from 'vue'
|
||||
<script lang="ts" setup>
|
||||
import { PropType, ref, watchEffect } from 'vue'
|
||||
|
||||
import { ErrorPayload } from '@nhost/core'
|
||||
export default defineComponent({
|
||||
props: {
|
||||
error: Object as PropType<ErrorPayload | null>
|
||||
},
|
||||
setup(props) {
|
||||
const snack = ref(false)
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.error) {
|
||||
snack.value = true
|
||||
}
|
||||
})
|
||||
const props = defineProps({
|
||||
error: Object as PropType<ErrorPayload | null>
|
||||
})
|
||||
const snack = ref(false)
|
||||
|
||||
return { snack }
|
||||
watchEffect(() => {
|
||||
if (props.error) {
|
||||
snack.value = true
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
@@ -4,33 +4,20 @@
|
||||
<v-list-item title="Profile" to="/profile" value="profile" prepend-icon="mdi-account" />
|
||||
<v-list-item title="Apollo" to="/apollo" value="apollo" prepend-icon="mdi-api" />
|
||||
<v-list-item title="About" to="/about" value="about" prepend-icon="mdi-information" />
|
||||
<v-list-item
|
||||
v-if="authenticated"
|
||||
title="Sign out"
|
||||
prepend-icon="mdi-exit-to-app"
|
||||
@click="signOutHandler"
|
||||
/>
|
||||
<v-list-item v-if="authenticated" title="Sign out" prepend-icon="mdi-exit-to-app" @click="signOutHandler" />
|
||||
</v-list>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import { useAuthenticated, useSignOut } from '@nhost/vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const router = useRouter()
|
||||
const { signOut } = useSignOut()
|
||||
const authenticated = useAuthenticated()
|
||||
const signOutHandler = async () => {
|
||||
await signOut()
|
||||
router.push('/')
|
||||
}
|
||||
return {
|
||||
authenticated,
|
||||
signOutHandler
|
||||
}
|
||||
}
|
||||
})
|
||||
const router = useRouter()
|
||||
const { signOut } = useSignOut()
|
||||
const authenticated = useAuthenticated()
|
||||
const signOutHandler = async () => {
|
||||
await signOut()
|
||||
router.push('/')
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,48 +1,21 @@
|
||||
<template>
|
||||
<v-btn
|
||||
class="my-1"
|
||||
block
|
||||
variant="contained-text"
|
||||
prepend-icon="mdi-github"
|
||||
color="white"
|
||||
style="background-color: #333"
|
||||
:href="github"
|
||||
>
|
||||
<v-btn class="my-1" block variant="contained-text" prepend-icon="mdi-github" color="white"
|
||||
style="background-color: #333" :href="github">
|
||||
Continue with GitHub
|
||||
</v-btn>
|
||||
<v-btn
|
||||
class="my-1"
|
||||
block
|
||||
variant="contained-text"
|
||||
prepend-icon="mdi-google"
|
||||
color="white"
|
||||
style="background-color: #de5246"
|
||||
:href="google"
|
||||
>
|
||||
<v-btn class="my-1" block variant="contained-text" prepend-icon="mdi-google" color="white"
|
||||
style="background-color: #de5246" :href="google">
|
||||
Continue with Google
|
||||
</v-btn>
|
||||
<v-btn
|
||||
class="my-1"
|
||||
block
|
||||
variant="contained-text"
|
||||
prepend-icon="mdi-facebook"
|
||||
color="white"
|
||||
style="background-color: #3b5998"
|
||||
:href="facebook"
|
||||
>
|
||||
<v-btn class="my-1" block variant="contained-text" prepend-icon="mdi-facebook" color="white"
|
||||
style="background-color: #3b5998" :href="facebook">
|
||||
Continue with Facebook
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
<script lang="ts" setup>
|
||||
|
||||
import { useProviderLink } from '@nhost/vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const { github, google, facebook } = useProviderLink({ redirectTo: window.location.origin })
|
||||
return { github, google, facebook }
|
||||
}
|
||||
})
|
||||
const { github, google, facebook } = useProviderLink({ redirectTo: '/' })
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<v-dialog v-model="modelValue">
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<span class="text-h5">Verification email sent</span>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
A email has been sent to {{ email }}. Please follow the link to verify your email address and to
|
||||
complete your registration.
|
||||
</v-card-text>
|
||||
<v-card-actions class="d-flex justify-center">
|
||||
<v-btn text @click="$emit('update:modelValue', false)">
|
||||
Close
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineProps(['modelValue', 'email'])
|
||||
</script>
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import { createVuetify, ThemeDefinition } from 'vuetify'
|
||||
|
||||
import { createApolloClient } from '@nhost/apollo'
|
||||
@@ -13,6 +13,7 @@ import 'vuetify/styles'
|
||||
import EmailPasswordless from './components/EmailPasswordlessForm.vue'
|
||||
import ErrorSnackBar from './components/ErrorSnackBar.vue'
|
||||
import OauthLinks from './components/OAuthLinks.vue'
|
||||
import VerificationEmailDialog from './components/VerificationEmailDialog.vue'
|
||||
import App from './App.vue'
|
||||
import { routes } from './routes'
|
||||
|
||||
@@ -68,7 +69,7 @@ nhost.auth.onAuthStateChanged((d) => {
|
||||
})
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
history: createWebHistory(),
|
||||
routes
|
||||
})
|
||||
|
||||
@@ -88,4 +89,5 @@ createApp(App)
|
||||
.component('ErrorSnackBar', ErrorSnackBar)
|
||||
.component('EmailPasswordless', EmailPasswordless)
|
||||
.component('OauthLinks', OauthLinks)
|
||||
.component('VerificationEmailDialog', VerificationEmailDialog)
|
||||
.mount('#app')
|
||||
|
||||
@@ -1,7 +1,29 @@
|
||||
<template>
|
||||
<div className="d-flex align-center flex-column">
|
||||
<v-card width="400">
|
||||
<v-card-text> About </v-card-text>
|
||||
<v-card-title>About this example</v-card-title>
|
||||
<v-card-text>This application demonstrates the available features of the Nhost stack.
|
||||
<p class="pt-2">Nhost cloud leverages the
|
||||
following services in the backend:
|
||||
<ul class="px-8">
|
||||
<li>Hasura</li>
|
||||
<li>Hasura Auth</li>
|
||||
<li>Hasura Storage</li>
|
||||
<li>Lambda Functions</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p class="py-2">
|
||||
This frontend is built with the following technologies:
|
||||
<ul class="px-8">
|
||||
<li>Vue 3</li>
|
||||
<li>Vue-router</li>
|
||||
<li>Vuetify 3</li>
|
||||
<li>and of course, the Nhost Vue client</li>
|
||||
</ul>
|
||||
|
||||
</p>
|
||||
Now let's go to the <router-link to="/">Home page</router-link>.
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
<template>
|
||||
<div className="d-flex align-center flex-column">
|
||||
<v-card width="400" tile>
|
||||
<v-card-text> Apollo </v-card-text>
|
||||
<v-list density="compact" v-if="result">
|
||||
<v-list-subheader>Books</v-list-subheader>
|
||||
<v-list-item v-for="(item, i) in result.books" :key="i" :value="item.id">
|
||||
<v-list-item-title v-text="item.title"></v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<v-card-title> Apollo </v-card-title>
|
||||
<v-card-text>
|
||||
<v-list density="compact" v-if="result">
|
||||
<v-list-subheader>Books</v-list-subheader>
|
||||
<v-list-item v-for="(item, i) in result.books" :key="i" :value="item.id">
|
||||
<v-list-item-title v-text="item.title"></v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { gql } from '@apollo/client/core'
|
||||
import { useAuthenticated } from '@nhost/vue'
|
||||
@@ -28,20 +30,15 @@ const GET_BOOKS = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const isAuthenticated = useAuthenticated()
|
||||
// TODO check if the query always runs with the headers
|
||||
const { result } = useQuery(
|
||||
GET_BOOKS,
|
||||
null,
|
||||
computed(() => ({
|
||||
pollInterval: 5000,
|
||||
fetchPolicy: 'cache-and-network',
|
||||
enabled: isAuthenticated.value
|
||||
}))
|
||||
)
|
||||
return { result }
|
||||
}
|
||||
})
|
||||
const isAuthenticated = useAuthenticated()
|
||||
// TODO check if the query always runs with the headers
|
||||
const { result } = useQuery(
|
||||
GET_BOOKS,
|
||||
null,
|
||||
computed(() => ({
|
||||
pollInterval: 5000,
|
||||
fetchPolicy: 'cache-and-network',
|
||||
enabled: isAuthenticated.value
|
||||
}))
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -6,14 +6,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
<script lang="ts" setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const router = useRouter()
|
||||
setTimeout(() => router.replace('/'), 2_500)
|
||||
return {}
|
||||
}
|
||||
})
|
||||
const router = useRouter()
|
||||
setTimeout(() => router.replace('/'), 2_500)
|
||||
</script>
|
||||
|
||||
@@ -1,39 +1,37 @@
|
||||
<template>
|
||||
<v-text-field v-model="email" label="Email" />
|
||||
<v-text-field v-model="password" label="Password" type="password" />
|
||||
<v-btn block color="primary" class="my-1" @click="signIn"> Sign in </v-btn>
|
||||
<form @submit="handleSignIn">
|
||||
<v-text-field v-model="email" label="Email" />
|
||||
<v-text-field v-model="password" label="Password" type="password" />
|
||||
<v-btn block color="primary" class="my-1" type="submit" :disabled="isLoading" :loading="isLoading"> Sign in </v-btn>
|
||||
</form>
|
||||
<v-btn class="my-1" block variant="text" color="primary" to="/signin">
|
||||
← Other Login Options
|
||||
← Other Sign-in Options
|
||||
</v-btn>
|
||||
<error-snack-bar :error="error" />
|
||||
<verification-email-dialog v-model="emailVerificationDialog" :email="email" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import { useSignInEmailPassword } from '@nhost/vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const email = ref('')
|
||||
const password = ref('')
|
||||
const email = ref('')
|
||||
const password = ref('')
|
||||
const emailVerificationDialog = ref(false)
|
||||
|
||||
const router = useRouter()
|
||||
const { signInEmailPassword, error } = useSignInEmailPassword()
|
||||
const signIn = async () => {
|
||||
const { isSuccess } = await signInEmailPassword(email, password)
|
||||
if (isSuccess) {
|
||||
router.replace('/')
|
||||
}
|
||||
}
|
||||
const router = useRouter()
|
||||
const { signInEmailPassword, error, isLoading } = useSignInEmailPassword()
|
||||
|
||||
return {
|
||||
email,
|
||||
error,
|
||||
password,
|
||||
signIn
|
||||
}
|
||||
const handleSignIn = async (e: Event) => {
|
||||
e.preventDefault()
|
||||
const { isSuccess, needsEmailVerification } = await signInEmailPassword(email, password)
|
||||
if (isSuccess) {
|
||||
router.replace('/')
|
||||
}
|
||||
})
|
||||
if (needsEmailVerification) {
|
||||
emailVerificationDialog.value = true
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<email-passwordless />
|
||||
<v-btn class="my-1" block variant="text" color="primary" to="/signin">
|
||||
← Other Login Options
|
||||
← Other Sign-in Options
|
||||
</v-btn>
|
||||
</template>
|
||||
|
||||
@@ -1,24 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import { useSignInAnonymous } from '@nhost/vue'
|
||||
const { signInAnonymous } = useSignInAnonymous()
|
||||
const router = useRouter()
|
||||
const handleSignInAnonymous = async (e: Event) => {
|
||||
e.preventDefault()
|
||||
const { isSuccess, error } = await signInAnonymous()
|
||||
if (isSuccess) {
|
||||
router.push('/profile')
|
||||
} else {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div className="d-flex align-center flex-column">
|
||||
<v-card width="400">
|
||||
<v-card-title>Log in to the Application</v-card-title>
|
||||
<v-card-title>Sign in to the Application</v-card-title>
|
||||
<v-card-text>
|
||||
<router-view />
|
||||
</v-card-text>
|
||||
@@ -26,7 +9,24 @@ const handleSignInAnonymous = async (e: Event) => {
|
||||
<v-divider class="my-4" style="min-width: 90%" />
|
||||
<div>
|
||||
Don‘t have an account? <router-link to="/signup"> Sign up </router-link> or
|
||||
<a href="#" @click="handleSignInAnonymous">sign in anonymously</a>
|
||||
<a v-if="!isLoading" href="#" @click="handleSignInAnonymous">sign in anonymously</a>
|
||||
<v-progress-circular v-else indeterminate />
|
||||
</div>
|
||||
</div>
|
||||
<error-snack-bar :error="error" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import { useSignInAnonymous } from '@nhost/vue'
|
||||
const { signInAnonymous, error, isLoading } = useSignInAnonymous()
|
||||
const router = useRouter()
|
||||
const handleSignInAnonymous = async (e: Event) => {
|
||||
e.preventDefault()
|
||||
const { isSuccess } = await signInAnonymous()
|
||||
if (isSuccess) {
|
||||
router.push('/profile')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,38 +1,33 @@
|
||||
<template>
|
||||
<v-text-field v-model="email" label="Email" />
|
||||
<v-text-field v-model="password" label="Password" type="password" />
|
||||
<v-btn block color="primary" class="my-1" @click="signUp"> Sign up </v-btn>
|
||||
<form @submit="handleSignUp">
|
||||
<v-text-field v-model="email" label="Email" />
|
||||
<v-text-field v-model="password" label="Password" type="password" />
|
||||
<v-btn block color="primary" class="my-1" type="submit" :disabled="isLoading" :loading="isLoading"> Sign up </v-btn>
|
||||
</form>
|
||||
<v-btn class="my-1" block variant="text" color="primary" to="/signup">
|
||||
← Other registration Options!
|
||||
</v-btn>
|
||||
<error-snack-bar :error="error" />
|
||||
<verification-email-dialog v-model="emailVerificationDialog" :email="email" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { useSignUpEmailPassword } from '@nhost/vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const email = ref('')
|
||||
const password = ref('')
|
||||
const { signUpEmailPassword, error } = useSignUpEmailPassword({
|
||||
redirectTo: window.location.origin
|
||||
})
|
||||
|
||||
const signUp = async () => {
|
||||
const result = await signUpEmailPassword(email, password)
|
||||
if (result.error) {
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
email,
|
||||
error,
|
||||
password,
|
||||
signUp
|
||||
}
|
||||
}
|
||||
const emailVerificationDialog = ref(false)
|
||||
const email = ref('')
|
||||
const password = ref('')
|
||||
const { signUpEmailPassword, error, isLoading } = useSignUpEmailPassword({
|
||||
redirectTo: '/'
|
||||
})
|
||||
|
||||
const handleSignUp = async (e: Event) => {
|
||||
e.preventDefault()
|
||||
const result = await signUpEmailPassword(email, password)
|
||||
if (result.needsEmailVerification) {
|
||||
emailVerificationDialog.value = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
@@ -50,5 +50,5 @@ export const routes: RouteRecordRaw[] = [
|
||||
}
|
||||
]
|
||||
},
|
||||
{ path: '/apollo', component: ApolloPage }
|
||||
{ path: '/apollo', component: ApolloPage, meta: { auth: true } }
|
||||
]
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"noEmit": true,
|
||||
"jsx": "preserve",
|
||||
"declaration": false,
|
||||
"declarationMap": false
|
||||
"declarationMap": false,
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
import path from 'path'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import vuetify from '@vuetify/vite-plugin'
|
||||
|
||||
|
||||
11
examples/vue-quickstart/auto-imports.d.ts
vendored
11
examples/vue-quickstart/auto-imports.d.ts
vendored
@@ -47,6 +47,8 @@ declare global {
|
||||
const refDefault: typeof import('@vueuse/core')['refDefault']
|
||||
const refThrottled: typeof import('@vueuse/core')['refThrottled']
|
||||
const refWithControl: typeof import('@vueuse/core')['refWithControl']
|
||||
const resolveRef: typeof import('@vueuse/core')['resolveRef']
|
||||
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
|
||||
const syncRef: typeof import('@vueuse/core')['syncRef']
|
||||
const syncRefs: typeof import('@vueuse/core')['syncRefs']
|
||||
const templateRef: typeof import('@vueuse/core')['templateRef']
|
||||
@@ -65,6 +67,7 @@ declare global {
|
||||
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
|
||||
const useBase64: typeof import('@vueuse/core')['useBase64']
|
||||
const useBattery: typeof import('@vueuse/core')['useBattery']
|
||||
const useBluetooth: typeof import('@vueuse/core')['useBluetooth']
|
||||
const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
|
||||
const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
|
||||
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
|
||||
@@ -89,6 +92,7 @@ declare global {
|
||||
const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia']
|
||||
const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility']
|
||||
const useDraggable: typeof import('@vueuse/core')['useDraggable']
|
||||
const useDropZone: typeof import('@vueuse/core')['useDropZone']
|
||||
const useElementBounding: typeof import('@vueuse/core')['useElementBounding']
|
||||
const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint']
|
||||
const useElementHover: typeof import('@vueuse/core')['useElementHover']
|
||||
@@ -100,6 +104,7 @@ declare global {
|
||||
const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
|
||||
const useFavicon: typeof import('@vueuse/core')['useFavicon']
|
||||
const useFetch: typeof import('@vueuse/core')['useFetch']
|
||||
const useFileDialog: typeof import('@vueuse/core')['useFileDialog']
|
||||
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
|
||||
const useFocus: typeof import('@vueuse/core')['useFocus']
|
||||
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
|
||||
@@ -108,6 +113,7 @@ declare global {
|
||||
const useGamepad: typeof import('@vueuse/core')['useGamepad']
|
||||
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
|
||||
const useIdle: typeof import('@vueuse/core')['useIdle']
|
||||
const useImage: typeof import('@vueuse/core')['useImage']
|
||||
const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
|
||||
const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
|
||||
const useInterval: typeof import('@vueuse/core')['useInterval']
|
||||
@@ -129,6 +135,7 @@ declare global {
|
||||
const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
|
||||
const useNetwork: typeof import('@vueuse/core')['useNetwork']
|
||||
const useNow: typeof import('@vueuse/core')['useNow']
|
||||
const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl']
|
||||
const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
|
||||
const useOnline: typeof import('@vueuse/core')['useOnline']
|
||||
const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
|
||||
@@ -153,11 +160,13 @@ declare global {
|
||||
const useShare: typeof import('@vueuse/core')['useShare']
|
||||
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
|
||||
const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
|
||||
const useStepper: typeof import('@vueuse/core')['useStepper']
|
||||
const useStorage: typeof import('@vueuse/core')['useStorage']
|
||||
const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
|
||||
const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
|
||||
const useSwipe: typeof import('@vueuse/core')['useSwipe']
|
||||
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
|
||||
const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize']
|
||||
const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
|
||||
const useThrottle: typeof import('@vueuse/core')['useThrottle']
|
||||
const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
|
||||
@@ -184,12 +193,14 @@ declare global {
|
||||
const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
|
||||
const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
|
||||
const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
|
||||
const watchArray: typeof import('@vueuse/core')['watchArray']
|
||||
const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
|
||||
const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
|
||||
const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
|
||||
const watchOnce: typeof import('@vueuse/core')['watchOnce']
|
||||
const watchPausable: typeof import('@vueuse/core')['watchPausable']
|
||||
const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
|
||||
const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
|
||||
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
|
||||
const whenever: typeof import('@vueuse/core')['whenever']
|
||||
}
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
"test": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.6.2",
|
||||
"@apollo/client": "^3.6.9",
|
||||
"@nhost/apollo": "*",
|
||||
"@nhost/vue": "*",
|
||||
"@vue/apollo-composable": "^4.0.0-alpha.17",
|
||||
"@vueuse/core": "^8.4.2",
|
||||
"@vue/apollo-composable": "4.0.0-alpha.18",
|
||||
"@vueuse/core": "^8.9.2",
|
||||
"graphql": "^15.7.2",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"vue": "^3.2.33",
|
||||
"vue-router": "^4.0.15"
|
||||
"vue": "^3.2.37",
|
||||
"vue-router": "^4.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^0.23.0",
|
||||
@@ -26,7 +26,7 @@
|
||||
"@types/node": "^17.0.32",
|
||||
"@unocss/reset": "^0.33.2",
|
||||
"@vitejs/plugin-vue": "^2.3.2",
|
||||
"@vue/test-utils": "^2.0.0-rc.21",
|
||||
"@vue/test-utils": "^2.0.2",
|
||||
"eslint": "^8.15.0",
|
||||
"jsdom": "^19.0.0",
|
||||
"pnpm": "^7.0.1",
|
||||
@@ -37,6 +37,6 @@
|
||||
"vite": "^2.9.8",
|
||||
"vite-plugin-pages": "^0.23.0",
|
||||
"vitest": "^0.12.4",
|
||||
"vue-tsc": "^0.34.12"
|
||||
"vue-tsc": "^0.38.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @nhost/apollo
|
||||
|
||||
## 0.5.24
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 10beea72: Fix React Native build: Export `package.json` for all npm packages.
|
||||
- Updated dependencies [10beea72]
|
||||
- @nhost/nhost-js@1.4.7
|
||||
|
||||
## 0.5.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- f30d6779: Bump @apollo/client to v3.6.9
|
||||
|
||||
## 0.5.22
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/apollo",
|
||||
"version": "0.5.22",
|
||||
"version": "0.5.24",
|
||||
"description": "Nhost Apollo Client library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -31,6 +31,7 @@
|
||||
"README.md"
|
||||
],
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": {
|
||||
"node": "./dist/index.cjs.js",
|
||||
@@ -66,6 +67,6 @@
|
||||
"graphql-ws": "^5.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@apollo/client": "^3.6.2"
|
||||
"@apollo/client": "^3.6.9"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost/core
|
||||
|
||||
## 0.7.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 747aa969: fix: added twitch and discord as provider
|
||||
- 10beea72: Fix React Native build: Export `package.json` for all npm packages.
|
||||
|
||||
## 0.7.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/core",
|
||||
"version": "0.7.5",
|
||||
"version": "0.7.6",
|
||||
"description": "Nhost core client library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -30,6 +30,7 @@
|
||||
"README.md"
|
||||
],
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": {
|
||||
"node": "./dist/index.cjs.js",
|
||||
|
||||
@@ -12,6 +12,7 @@ export type ChangePasswordEvents =
|
||||
| {
|
||||
type: 'REQUEST'
|
||||
password?: string
|
||||
ticket?: string
|
||||
}
|
||||
| { type: 'SUCCESS' }
|
||||
| { type: 'ERROR'; error: ErrorPayload | null }
|
||||
@@ -75,10 +76,10 @@ export const createChangePasswordMachine = ({ backendUrl, interpreter }: AuthCli
|
||||
invalidPassword: (_, { password }) => !isValidPassword(password)
|
||||
},
|
||||
services: {
|
||||
requestChange: (_, { password }) =>
|
||||
requestChange: (_, { password, ticket }) =>
|
||||
api.post<string, { data: { error?: ErrorPayload } }>(
|
||||
'/user/password',
|
||||
{ newPassword: password },
|
||||
{ newPassword: password, ticket: ticket },
|
||||
{
|
||||
headers: {
|
||||
authorization: `Bearer ${interpreter?.state.context.accessToken.value}`
|
||||
|
||||
@@ -10,11 +10,12 @@ export interface ChangePasswordHandlerResult extends ActionErrorState, ActionSuc
|
||||
|
||||
export const changePasswordPromise = async (
|
||||
interpreter: InterpreterFrom<ChangePasswordMachine>,
|
||||
password: string
|
||||
password: string,
|
||||
ticket?: string
|
||||
): Promise<ChangePasswordHandlerResult> =>
|
||||
new Promise<ChangePasswordHandlerResult>((resolve) => {
|
||||
interpreter.send('REQUEST', {
|
||||
password
|
||||
password, ticket
|
||||
})
|
||||
interpreter.onTransition((state) => {
|
||||
if (state.matches({ idle: 'error' })) {
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { USER_ALREADY_SIGNED_IN } from '../errors'
|
||||
import { AuthInterpreter, PasswordlessOptions } from '../types'
|
||||
|
||||
import { ActionErrorState, ActionSuccessState } from './types'
|
||||
import { ActionErrorState, ActionLoadingState, ActionSuccessState } from './types'
|
||||
|
||||
export interface SignInSmsPasswordlessState
|
||||
extends SignInSmsPasswordlessHandlerResult,
|
||||
ActionLoadingState {}
|
||||
|
||||
export interface SignInSmsPasswordlessHandlerResult extends ActionErrorState, ActionSuccessState {
|
||||
/**
|
||||
|
||||
@@ -149,6 +149,8 @@ export type Provider =
|
||||
| 'strava'
|
||||
| 'gitlab'
|
||||
| 'bitbucket'
|
||||
| 'discord'
|
||||
| 'twitch'
|
||||
|
||||
// TODO share with hasura-auth
|
||||
export interface JWTHasuraClaims {
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# @nhost/hasura-auth-js
|
||||
|
||||
## 1.4.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 18ac56d0: added option to include ticket in changePassword to allow for changing password without the user being signed in
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 10beea72: Fix React Native build: Export `package.json` for all npm packages.
|
||||
- Updated dependencies [747aa969]
|
||||
- Updated dependencies [10beea72]
|
||||
- @nhost/core@0.7.6
|
||||
|
||||
## 1.3.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/hasura-auth-js",
|
||||
"version": "1.3.4",
|
||||
"version": "1.4.0",
|
||||
"description": "Hasura-auth client",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -29,6 +29,7 @@
|
||||
"README.md"
|
||||
],
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": {
|
||||
"node": "./dist/index.cjs.js",
|
||||
|
||||
@@ -251,7 +251,7 @@ export class HasuraAuthClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Use `nhost.auth.changePassword` to change the password for the user. The old password is not needed.
|
||||
* Use `nhost.auth.changePassword` to change the password for the signed-in user. The old password is not needed. In case the user is not signed-in, a password reset ticket needs to be provided.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
@@ -260,9 +260,9 @@ export class HasuraAuthClient {
|
||||
*
|
||||
* @docs https://docs.nhost.io/reference/javascript/auth/change-password
|
||||
*/
|
||||
async changePassword({ newPassword }: ChangePasswordParams): Promise<ApiChangePasswordResponse> {
|
||||
async changePassword({ newPassword, ticket }: ChangePasswordParams): Promise<ApiChangePasswordResponse> {
|
||||
const service = interpret(createChangePasswordMachine(this._client)).start()
|
||||
const { error } = await changePasswordPromise(service, newPassword)
|
||||
const { error } = await changePasswordPromise(service, newPassword, ticket)
|
||||
return { error }
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ export interface ResetPasswordParams {
|
||||
|
||||
export interface ChangePasswordParams {
|
||||
newPassword: string
|
||||
ticket?: string
|
||||
}
|
||||
|
||||
export interface SendVerificationEmailParams {
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @nhost/hasura-storage-js
|
||||
|
||||
## 0.5.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 10beea72: Fix React Native build: Export `package.json` for all npm packages.
|
||||
- Updated dependencies [747aa969]
|
||||
- Updated dependencies [10beea72]
|
||||
- @nhost/core@0.7.6
|
||||
|
||||
## 0.5.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/hasura-storage-js",
|
||||
"version": "0.5.2",
|
||||
"version": "0.5.3",
|
||||
"description": "Hasura-storage client",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -27,6 +27,7 @@
|
||||
"README.md"
|
||||
],
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": {
|
||||
"node": "./dist/index.cjs.js",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @nhost/nextjs
|
||||
|
||||
## 1.7.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 10beea72: Fix React Native build: Export `package.json` for all npm packages.
|
||||
- Updated dependencies [747aa969]
|
||||
- Updated dependencies [10beea72]
|
||||
- Updated dependencies [84ba29dd]
|
||||
- @nhost/core@0.7.6
|
||||
- @nhost/nhost-js@1.4.7
|
||||
- @nhost/react@0.12.0
|
||||
|
||||
## 1.6.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/nextjs",
|
||||
"version": "1.6.2",
|
||||
"version": "1.7.0",
|
||||
"description": "Nhost NextJS library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -33,6 +33,7 @@
|
||||
"README.md"
|
||||
],
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": {
|
||||
"node": "./dist/index.cjs.js",
|
||||
@@ -82,4 +83,4 @@
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,15 @@
|
||||
# @nhost/nhost-js
|
||||
|
||||
## 1.4.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 10beea72: Fix React Native build: Export `package.json` for all npm packages.
|
||||
- Updated dependencies [18ac56d0]
|
||||
- Updated dependencies [10beea72]
|
||||
- @nhost/hasura-auth-js@1.4.0
|
||||
- @nhost/hasura-storage-js@0.5.3
|
||||
|
||||
## 1.4.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/nhost-js",
|
||||
"version": "1.4.6",
|
||||
"version": "1.4.7",
|
||||
"description": "Nhost JavaScript SDK",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -30,6 +30,7 @@
|
||||
"README.md"
|
||||
],
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": {
|
||||
"node": "./dist/index.cjs.js",
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# @nhost/react-apollo
|
||||
|
||||
## 4.7.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 10beea72: Fix React Native build: Export `package.json` for all npm packages.
|
||||
- Updated dependencies [10beea72]
|
||||
- Updated dependencies [84ba29dd]
|
||||
- @nhost/apollo@0.5.24
|
||||
- @nhost/react@0.12.0
|
||||
|
||||
## 4.6.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- f30d6779: Bump @apollo/client to v3.6.9
|
||||
- Updated dependencies [f30d6779]
|
||||
- @nhost/apollo@0.5.23
|
||||
|
||||
## 4.6.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/react-apollo",
|
||||
"version": "4.6.2",
|
||||
"version": "4.7.0",
|
||||
"description": "Nhost React Apollo client",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -32,6 +32,7 @@
|
||||
"README.md"
|
||||
],
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": {
|
||||
"node": "./dist/index.cjs.js",
|
||||
@@ -69,11 +70,11 @@
|
||||
"react-dom": "^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@apollo/client": "^3.6.2",
|
||||
"@apollo/client": "^3.6.9",
|
||||
"@nhost/react": "workspace:*",
|
||||
"@types/react": "^18.0.8",
|
||||
"graphql": "16",
|
||||
"react": "^18.1.0",
|
||||
"react-dom": "^18.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,16 @@
|
||||
# @nhost/react-auth
|
||||
|
||||
## 3.5.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 10beea72: Fix React Native build: Export `package.json` for all npm packages.
|
||||
- Updated dependencies [18ac56d0]
|
||||
- Updated dependencies [10beea72]
|
||||
- Updated dependencies [84ba29dd]
|
||||
- @nhost/hasura-auth-js@1.4.0
|
||||
- @nhost/react@0.12.0
|
||||
|
||||
## 3.4.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/react-auth",
|
||||
"version": "3.4.2",
|
||||
"version": "3.5.0",
|
||||
"description": "Nhost React client",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -31,6 +31,7 @@
|
||||
"README.md"
|
||||
],
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": {
|
||||
"node": "./dist/index.cjs.js",
|
||||
@@ -71,4 +72,4 @@
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,38 @@
|
||||
# @nhost/react
|
||||
|
||||
## 0.12.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 84ba29dd: Introduce `useSignInSmsPasswordless`
|
||||
|
||||
```ts
|
||||
const {
|
||||
signInSmsPasswordless,
|
||||
sendOtp,
|
||||
needsOtp,
|
||||
isLoading,
|
||||
isSuccess,
|
||||
isError,
|
||||
error
|
||||
} = useSignInSmsPasswordless()
|
||||
```
|
||||
|
||||
1. The `signInSmsPasswordless` action will send a one-time password to the given phone number.
|
||||
2. The client is then awaiting the OTP. `needsOtp` equals true
|
||||
3. After the code is received by SMS, the client sends the code with `sendOtp`. On success, the client is authenticated, and `isSuccess` equals `true`.
|
||||
|
||||
Any error is monitored through `isError` and `error`. While the `signInSmsPasswordless` and `sendOtp` actions are running, `isLoading` equals `true`
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 10beea72: Fix React Native build: Export `package.json` for all npm packages.
|
||||
- Updated dependencies [747aa969]
|
||||
- Updated dependencies [10beea72]
|
||||
- @nhost/core@0.7.6
|
||||
- @nhost/hasura-storage-js@0.5.3
|
||||
- @nhost/nhost-js@1.4.7
|
||||
|
||||
## 0.11.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/react",
|
||||
"version": "0.11.2",
|
||||
"version": "0.12.0",
|
||||
"description": "Nhost React library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -31,6 +31,7 @@
|
||||
"README.md"
|
||||
],
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": {
|
||||
"node": "./dist/index.cjs.js",
|
||||
|
||||
@@ -22,6 +22,7 @@ export * from './useSendVerificationEmail'
|
||||
export * from './useSignInAnonymous'
|
||||
export * from './useSignInEmailPassword'
|
||||
export * from './useSignInEmailPasswordless'
|
||||
export * from './useSignInSmsPasswordless'
|
||||
export * from './useSignOut'
|
||||
export * from './useSignUpEmailPassword'
|
||||
export * from './useUserAvatarUrl'
|
||||
|
||||
101
packages/react/src/useSignInSmsPasswordless.ts
Normal file
101
packages/react/src/useSignInSmsPasswordless.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { useState } from 'react'
|
||||
|
||||
import {
|
||||
PasswordlessOptions,
|
||||
SignInSmsPasswordlessHandlerResult,
|
||||
SignInSmsPasswordlessOtpHandlerResult,
|
||||
SignInSmsPasswordlessState
|
||||
} from '@nhost/core'
|
||||
import { signInSmsPasswordlessOtpPromise, signInSmsPasswordlessPromise } from '@nhost/core'
|
||||
import { useSelector } from '@xstate/react'
|
||||
|
||||
import { useAuthInterpreter } from './useAuthInterpreter'
|
||||
|
||||
export interface SignInSmsPasswordlessHandler {
|
||||
(phoneNumber: string, options?: PasswordlessOptions): Promise<SignInSmsPasswordlessHandlerResult>
|
||||
}
|
||||
|
||||
export interface SignInSmsPasswordlessOtpHandler {
|
||||
(code: string): Promise<SignInSmsPasswordlessOtpHandlerResult>
|
||||
(phoneNumber: string, code: string): Promise<SignInSmsPasswordlessOtpHandlerResult>
|
||||
}
|
||||
|
||||
export interface SignInSmsPasswordlessHookResult extends SignInSmsPasswordlessState {
|
||||
/** Sends a one-time code to the given phoneNumber */
|
||||
signInSmsPasswordless: SignInSmsPasswordlessHandler
|
||||
sendOtp: SignInSmsPasswordlessOtpHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the hook `useSignInSmsPasswordless` to sign in a user with a one-time password sent via SMS to a phone.
|
||||
*
|
||||
* 1. The `signInSmsPasswordless` action sends a one-time password to the given phone number.
|
||||
* 2. The client is then awaiting the OTP. `needsOtp` equals true.
|
||||
* 3. After the code is received by SMS, the client sends the code with `sendOtp`. On success, the client is authenticated, and `isSuccess` equals `true`.
|
||||
*
|
||||
* Any error is monitored through `isError` and `error`. While the `signInSmsPasswordless` and `sendOtp` actions are running, `isLoading` equals `true`.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const { signInSmsPasswordless, sendOtp, needsOtp, isLoading, isSuccess, isError, error } = useSignInSmsPasswordless()
|
||||
*
|
||||
* console.log({ isLoading, isSuccess, isError, error });
|
||||
*
|
||||
* const askCode = async (e) => {
|
||||
* e.preventDefault();
|
||||
* await signInSmsPasswordless('+32455555555');
|
||||
* }
|
||||
*
|
||||
* const sendCode = async (e) => {
|
||||
* e.preventDefault();
|
||||
* await sendOtp('123456');
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @docs https://docs.nhost.io/reference/react/use-sign-in-sms-passwordless
|
||||
*/
|
||||
export function useSignInSmsPasswordless(
|
||||
stateOptions?: PasswordlessOptions
|
||||
): SignInSmsPasswordlessHookResult {
|
||||
const service = useAuthInterpreter()
|
||||
const [_phoneNumber, setPhoneNumber] = useState('')
|
||||
|
||||
const signInSmsPasswordless: SignInSmsPasswordlessHandler = (
|
||||
phoneNumber: string,
|
||||
valueOptions = stateOptions
|
||||
) => {
|
||||
setPhoneNumber(phoneNumber)
|
||||
return signInSmsPasswordlessPromise(service, phoneNumber, valueOptions)
|
||||
}
|
||||
|
||||
const sendOtp: SignInSmsPasswordlessOtpHandler = async (...args: string[]) => {
|
||||
if (args.length === 2) {
|
||||
const [phoneNumber, code] = args
|
||||
return signInSmsPasswordlessOtpPromise(service, phoneNumber, code)
|
||||
}
|
||||
const [code] = args
|
||||
return signInSmsPasswordlessOtpPromise(service, _phoneNumber, code)
|
||||
}
|
||||
|
||||
const error = useSelector(
|
||||
service,
|
||||
(state) => state.context.errors.registration || null,
|
||||
(a, b) => a?.error === b?.error
|
||||
)
|
||||
const isLoading = useSelector(
|
||||
service,
|
||||
(state) =>
|
||||
state.matches('registration.passwordlessSms') ||
|
||||
state.matches('registration.passwordlessSmsOtp')
|
||||
)
|
||||
|
||||
const isSuccess = useSelector(service, (state) => state.matches('authentication.signedIn'))
|
||||
|
||||
const needsOtp = useSelector(service, (state) =>
|
||||
state.matches('registration.incomplete.needsOtp')
|
||||
)
|
||||
|
||||
const isError = useSelector(service, (state) => state.matches('registration.incomplete.failed'))
|
||||
|
||||
return { signInSmsPasswordless, sendOtp, isLoading, isSuccess, needsOtp, isError, error }
|
||||
}
|
||||
@@ -1,5 +1,44 @@
|
||||
# @nhost/vue
|
||||
|
||||
## 0.4.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 84ba29dd: Introduce `useSignInSmsPasswordless`
|
||||
|
||||
```ts
|
||||
const {
|
||||
signInSmsPasswordless,
|
||||
sendOtp,
|
||||
needsOtp,
|
||||
isLoading,
|
||||
isSuccess,
|
||||
isError,
|
||||
error
|
||||
} = useSignInSmsPasswordless()
|
||||
```
|
||||
|
||||
1. The `signInSmsPasswordless` action will send a one-time password to the given phone number.
|
||||
2. The client is then awaiting the OTP. `needsOtp` equals true
|
||||
3. After the code is received by SMS, the client sends the code with `sendOtp`. On success, the client is authenticated, and `isSuccess` equals `true`.
|
||||
|
||||
Any error is monitored through `isError` and `error`. While the `signInSmsPasswordless` and `sendOtp` actions are running, `isLoading` equals `true`
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 10beea72: Fix React Native build: Export `package.json` for all npm packages.
|
||||
- Updated dependencies [747aa969]
|
||||
- Updated dependencies [10beea72]
|
||||
- @nhost/core@0.7.6
|
||||
- @nhost/nhost-js@1.4.7
|
||||
|
||||
## 0.3.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- bc657251: Fix the deletion of refresh tokens in the URL when autoSignIn is enabled.
|
||||
This feature only work when using the HTML5 history mode. A warning will appear when using the Hash mode and when in development mode.
|
||||
|
||||
## 0.3.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/vue",
|
||||
"version": "0.3.6",
|
||||
"version": "0.4.0",
|
||||
"description": "Nhost Vue library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -31,6 +31,7 @@
|
||||
"README.md"
|
||||
],
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": {
|
||||
"node": "./dist/index.cjs.js",
|
||||
@@ -65,7 +66,7 @@
|
||||
"dependencies": {
|
||||
"@nhost/core": "workspace:*",
|
||||
"@nhost/nhost-js": "workspace:*",
|
||||
"@vueuse/core": "^8.3.1",
|
||||
"@vueuse/core": "^8.9.2",
|
||||
"@xstate/vue": "^1.0.0",
|
||||
"immer": "^9.0.12",
|
||||
"jwt-decode": "^3.1.2"
|
||||
@@ -75,8 +76,8 @@
|
||||
"@nhost/hasura-auth-js": "workspace:*",
|
||||
"@vitejs/plugin-vue": "^2.3.1",
|
||||
"@xstate/inspect": "^0.6.2",
|
||||
"vue": "^3.2.31",
|
||||
"vue-router": "^4.0.14",
|
||||
"vue": "^3.2.37",
|
||||
"vue-router": "^4.1.2",
|
||||
"ws": "^8.4.2",
|
||||
"xstate": "^4.32.1"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { App, getCurrentInstance } from 'vue'
|
||||
import { Router } from 'vue-router'
|
||||
import { App, warn } from 'vue'
|
||||
|
||||
import { removeParameterFromWindow } from '@nhost/core'
|
||||
import {
|
||||
BackendUrl,
|
||||
NhostAuthConstructorParams,
|
||||
@@ -16,8 +16,10 @@ export interface NhostVueClientConstructorParams
|
||||
Omit<NhostAuthConstructorParams, 'url' | 'start' | 'client'> {}
|
||||
|
||||
export class NhostClient extends VanillaClient {
|
||||
private autoSignIn: boolean
|
||||
constructor(params: NhostVueClientConstructorParams) {
|
||||
super({ ...params, start: true })
|
||||
this.autoSignIn = params.autoSignIn ?? true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -25,40 +27,25 @@ export class NhostClient extends VanillaClient {
|
||||
* This method transforms the NhostClient class into a Vue plugin
|
||||
*/
|
||||
install(app: App) {
|
||||
const autoSignIn = this.autoSignIn
|
||||
app.provide(DefaultNhostClient, this)
|
||||
// * Remove the refreshToken & type from the hash when using Vue Router
|
||||
app.mixin({
|
||||
created() {
|
||||
const instance = getCurrentInstance()
|
||||
// * On creation, check if we are in the root component.
|
||||
if (instance?.uid === instance?.root.uid) {
|
||||
const router: Router | undefined = this.$router
|
||||
// * If Vue router is used, remove the refeshToken & type from the hash and query after each routing event
|
||||
router?.afterEach((to) => {
|
||||
if (to.hash.includes('refreshToken') || to.query['refreshToken']) {
|
||||
delete to.query['refreshToken']
|
||||
delete to.query['type']
|
||||
const hash = new URLSearchParams(to.hash.slice(1))
|
||||
if (hash.has('refreshToken')) {
|
||||
hash.delete('refreshToken')
|
||||
hash.delete('type')
|
||||
}
|
||||
let path = '/#' + to.path
|
||||
if (Object.keys(to.params).length) {
|
||||
path +=
|
||||
'?' +
|
||||
Object.entries(to.params)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.join('&')
|
||||
}
|
||||
if (Array.from(hash).length) {
|
||||
path += '#' + hash.toString()
|
||||
}
|
||||
window.history.pushState({}, '', path)
|
||||
}
|
||||
})
|
||||
}
|
||||
if (autoSignIn) {
|
||||
const router = app.config.globalProperties.$router
|
||||
if (!router) {
|
||||
// * Vue-router is not set. Do nothing.
|
||||
return
|
||||
}
|
||||
})
|
||||
if (router.options.history.base) {
|
||||
warn(
|
||||
'[Nhost]: Vue-router is configured with a history Hash Mode. Refresh tokens will not be removed from the hash.'
|
||||
)
|
||||
return
|
||||
}
|
||||
router.afterEach(() => {
|
||||
removeParameterFromWindow('refreshToken')
|
||||
removeParameterFromWindow('type')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export * from './useSendVerificationEmail'
|
||||
export * from './useSignInAnonymous'
|
||||
export * from './useSignInEmailPassword'
|
||||
export * from './useSignInEmailPasswordless'
|
||||
export * from './useSignInSmsPasswordless'
|
||||
export * from './useSignOut'
|
||||
export * from './useSignUpEmailPassword'
|
||||
export * from './useUserAvatarUrl'
|
||||
|
||||
115
packages/vue/src/useSignInSmsPasswordless.ts
Normal file
115
packages/vue/src/useSignInSmsPasswordless.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { ref, ToRefs, unref } from 'vue'
|
||||
|
||||
import {
|
||||
PasswordlessOptions,
|
||||
SignInSmsPasswordlessHandlerResult,
|
||||
SignInSmsPasswordlessOtpHandlerResult,
|
||||
signInSmsPasswordlessOtpPromise,
|
||||
signInSmsPasswordlessPromise,
|
||||
SignInSmsPasswordlessState
|
||||
} from '@nhost/core'
|
||||
import { useSelector } from '@xstate/vue'
|
||||
|
||||
import { NestedRefOfValue, nestedUnref, RefOrValue } from './helpers'
|
||||
import { useAuthInterpreter } from './useAuthInterpreter'
|
||||
|
||||
export interface SignInSmsPasswordlessHandler {
|
||||
(
|
||||
phoneNumber: RefOrValue<string>,
|
||||
options?: NestedRefOfValue<PasswordlessOptions | undefined>
|
||||
): Promise<SignInSmsPasswordlessHandlerResult>
|
||||
}
|
||||
|
||||
export interface SignInSmsPasswordlessOtpHandler {
|
||||
(code: RefOrValue<string>): Promise<SignInSmsPasswordlessOtpHandlerResult>
|
||||
(
|
||||
phoneNumber: RefOrValue<string>,
|
||||
code: RefOrValue<string>
|
||||
): Promise<SignInSmsPasswordlessOtpHandlerResult>
|
||||
}
|
||||
|
||||
export interface SignInSmsPasswordlessComposableResult extends ToRefs<SignInSmsPasswordlessState> {
|
||||
/** Sends a one-time code to the given phoneNumber */
|
||||
signInSmsPasswordless: SignInSmsPasswordlessHandler
|
||||
sendOtp: SignInSmsPasswordlessOtpHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the composable `useSignInSmsPasswordless` to sign in a user with a one-time password sent via SMS to a phone.
|
||||
*
|
||||
* 1. The `signInSmsPasswordless` action sends a one-time password to the given phone number.
|
||||
* 2. The client is then awaiting the OTP. `needsOtp` equals true.
|
||||
* 3. After the code is received by SMS, the client sends the code with `sendOtp`. On success, the client is authenticated, and `isSuccess` equals `true`.
|
||||
*
|
||||
* Any error is monitored through `isError` and `error`. While the `signInSmsPasswordless` and `sendOtp` actions are running, `isLoading` equals `true`.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const { signInSmsPasswordless, sendOtp, needsOtp, isLoading, isSuccess, isError, error } = useSignInSmsPasswordless()
|
||||
*
|
||||
* console.log({ isLoading, isSuccess, isError, error });
|
||||
*
|
||||
* const askCode = async (e) => {
|
||||
* e.preventDefault();
|
||||
* await signInSmsPasswordless('+32455555555');
|
||||
* }
|
||||
*
|
||||
* const sendCode = async (e) => {
|
||||
* e.preventDefault();
|
||||
* await sendOtp('123456');
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @docs https://docs.nhost.io/reference/vue/use-sign-in-sms-passwordless
|
||||
*/
|
||||
export function useSignInSmsPasswordless(
|
||||
stateOptions?: NestedRefOfValue<PasswordlessOptions | undefined>
|
||||
): SignInSmsPasswordlessComposableResult {
|
||||
const service = useAuthInterpreter()
|
||||
const _phoneNumber = ref('')
|
||||
|
||||
const signInSmsPasswordless: SignInSmsPasswordlessHandler = (
|
||||
phoneNumber: RefOrValue<string>,
|
||||
valueOptions = stateOptions
|
||||
) => {
|
||||
_phoneNumber.value = unref(phoneNumber)
|
||||
return signInSmsPasswordlessPromise(
|
||||
service.value,
|
||||
unref(phoneNumber),
|
||||
nestedUnref(valueOptions)
|
||||
)
|
||||
}
|
||||
|
||||
const sendOtp: SignInSmsPasswordlessOtpHandler = async (...args: Array<RefOrValue<string>>) => {
|
||||
if (args.length === 2) {
|
||||
const [phoneNumber, code] = args
|
||||
return signInSmsPasswordlessOtpPromise(service.value, unref(phoneNumber), unref(code))
|
||||
}
|
||||
const [code] = args
|
||||
return signInSmsPasswordlessOtpPromise(service.value, unref(_phoneNumber), unref(code))
|
||||
}
|
||||
|
||||
const error = useSelector(
|
||||
service.value,
|
||||
(state) => state.context.errors.registration || null,
|
||||
(a, b) => a?.error === b?.error
|
||||
)
|
||||
const isLoading = useSelector(
|
||||
service.value,
|
||||
(state) =>
|
||||
state.matches('registration.passwordlessSms') ||
|
||||
state.matches('registration.passwordlessSmsOtp')
|
||||
)
|
||||
|
||||
const isSuccess = useSelector(service.value, (state) => state.matches('authentication.signedIn'))
|
||||
|
||||
const needsOtp = useSelector(service.value, (state) =>
|
||||
state.matches('registration.incomplete.needsOtp')
|
||||
)
|
||||
|
||||
const isError = useSelector(service.value, (state) =>
|
||||
state.matches('registration.incomplete.failed')
|
||||
)
|
||||
|
||||
return { signInSmsPasswordless, sendOtp, isLoading, isSuccess, needsOtp, isError, error }
|
||||
}
|
||||
1006
pnpm-lock.yaml
generated
1006
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user