Compare commits

...

61 Commits

Author SHA1 Message Date
Pilou
024f026241 Merge pull request #839 from nhost/changeset-release/main
chore: update versions
2022-07-19 10:43:17 +02:00
Pierre-Louis Mercereau
a422a4850d chore: correct peer deps bumps 2022-07-19 09:54:46 +02:00
github-actions[bot]
a7e67979fe chore: update versions 2022-07-19 07:48:08 +00:00
Pilou
1dcbf268db Merge pull request #820 from nhost/feat/sms-hook-and-composable
feat: useSignInSmsPasswordless
2022-07-19 09:47:05 +02:00
Pilou
5c5d489740 Merge pull request #841 from nhost/contributors-readme-action-mLUJqJcCpL
contributors readme action update
2022-07-19 09:46:51 +02:00
github-actions[bot]
a2559e3482 contrib-readme-action has updated readme 2022-07-19 07:46:19 +00:00
Pilou
bbef104a85 Merge pull request #840 from nhost/contributors-readme-action-MWLllvW8wS
contributors readme action update
2022-07-19 09:46:05 +02:00
github-actions[bot]
7843b1aec1 contrib-readme-action has updated readme 2022-07-19 07:41:45 +00:00
Pilou
4711bfa8ec Merge pull request #679 from Svarto/feat/changePassword-with-ticket
included an optional ticket in changePassword function to allow for c…
2022-07-19 09:41:27 +02:00
Pilou
6f3f8a5020 Merge pull request #838 from nhost/fix-providers
added twitch as provider
2022-07-19 09:38:32 +02:00
Pilou
a120bcc8fc Merge pull request #828 from dminkovsky/dminkovsky/fix-react-native-build
Fix React Native build
2022-07-19 09:36:02 +02:00
Johan Eliasson
53e20e87f3 Revert "Create stale-bags-design.md"
This reverts commit 9479aeb596.
2022-07-19 09:18:24 +02:00
Johan Eliasson
9479aeb596 Create stale-bags-design.md 2022-07-19 09:10:25 +02:00
Pilou
c4f11af072 Merge pull request #837 from nhost/contributors-readme-action-ZK2-b_IYkd
contributors readme action update
2022-07-19 09:07:47 +02:00
Johan Eliasson
747aa96914 Create dry-radios-allow.md 2022-07-19 09:07:43 +02:00
Johan Eliasson
5682d92592 added twitch as provider 2022-07-19 09:06:51 +02:00
github-actions[bot]
2cf6556499 contrib-readme-action has updated readme 2022-07-19 07:06:07 +00:00
Johan Eliasson
89553fcaf6 Merge pull request #830 from QuestGiverOrg/main
Add discord to provider types
2022-07-19 09:05:48 +02:00
Johan Eliasson
10beea7246 Create nine-students-design.md 2022-07-19 09:03:06 +02:00
Johan Eliasson
1334ddb693 added package.json export for all npm packages 2022-07-19 09:02:23 +02:00
Pilou
d212128815 Merge pull request #829 from nhost/changeset-release/main
chore: update versions
2022-07-16 15:22:46 +02:00
Gavan Wilhite
302c28b202 Merge pull request #1 from QuestGiverOrg/Adding-discord-to-provider-type
Added discord to provider type
2022-07-15 13:28:45 -07:00
Gavan Wilhite
f3f760b987 Added discord to provider type 2022-07-15 13:24:32 -07:00
github-actions[bot]
9d9caf9834 chore: update versions 2022-07-15 20:20:38 +00:00
Pilou
96cbf023ca Merge pull request #824 from nhost/docs/react-apollo-example-improvements
React Apollo example improvements
2022-07-15 22:19:01 +02:00
Pierre-Louis Mercereau
1ed534cb4a Merge branch 'main' into docs/react-apollo-example-improvements 2022-07-15 22:06:07 +02:00
Pilou
dcdee0b426 Merge pull request #819 from nhost/docs/vue-example-improvements
Vue example improvements
2022-07-15 22:02:58 +02:00
Pierre-Louis Mercereau
e31f4756b4 fix(vue-example): disabled & loading buttons 2022-07-15 20:13:44 +02:00
Pierre-Louis Mercereau
259e198d80 fix(react-apollo-example): fixed logo width 2022-07-15 19:45:22 +02:00
Pierre-Louis Mercereau
1d10a47414 fix(react-apollo-example): fit image logo 2022-07-15 19:42:37 +02:00
Dmitry Minkovsky
34470ff6e0 Fix React Native build fail 2022-07-15 12:11:18 -04:00
Pierre-Louis Mercereau
f30d6779bb chore: bump @apollo/client to v3.6.9 2022-07-15 15:55:29 +02:00
Pierre-Louis Mercereau
2a3b2c3af5 Merge branch 'main' into docs/vue-example-improvements 2022-07-15 15:39:38 +02:00
Pierre-Louis Mercereau
a0db6b58de chore: add hasura-auth 0.10 metadata 2022-07-15 12:06:08 +02:00
Pierre-Louis Mercereau
523d52aa7f Merge branch 'main' into docs/react-apollo-example-improvements 2022-07-15 11:48:41 +02:00
Pierre-Louis Mercereau
6e1ee1802d docs: explicit pnpm run 2022-07-15 11:45:45 +02:00
Pierre-Louis Mercereau
51ad1eb355 fix(react-apollo-example): qrcode img by alttext 2022-07-15 11:39:22 +02:00
Pierre-Louis Mercereau
e084643032 docs: correct readme 2022-07-15 11:11:35 +02:00
Pierre-Louis Mercereau
5514e81186 docs: update readme 2022-07-15 11:10:44 +02:00
Pierre-Louis Mercereau
16f38aa893 docs(example): update readme instructions 2022-07-15 11:09:08 +02:00
Pierre-Louis Mercereau
76c6e8d0d6 feat(react-apollo-example): improve navbar & index 2022-07-15 10:33:27 +02:00
Pierre-Louis Mercereau
a7d5c85f60 fix(example): hide nav drawer on small screens 2022-07-15 09:35:28 +02:00
Pierre-Louis Mercereau
bc657251d6 fix: remove refresh token from the url 2022-07-14 22:07:59 +02:00
Pierre-Louis Mercereau
5abc362a4d fix(example): close magic link sent dialog 2022-07-14 15:10:11 +02:00
Pierre-Louis Mercereau
7a4c9fa806 refactor(example): use form submit 2022-07-14 15:06:57 +02:00
Pierre-Louis Mercereau
348318d709 feat(example): add dialog after magic link is sent 2022-07-14 14:48:30 +02:00
Pierre-Louis Mercereau
4e4600d769 refactor: improve readability 2022-07-14 09:13:55 +02:00
Pierre-Louis Mercereau
44d57d4b89 refactor: from comments 2022-07-14 09:04:16 +02:00
Pierre-Louis Mercereau
84ba29dd7f feat: useSignInSmsPasswordless 2022-07-13 20:39:17 +02:00
Pierre-Louis Mercereau
b5f7f7fe5f refactor(example): toast errors on top of window 2022-07-13 19:45:38 +02:00
Pierre-Louis Mercereau
7116a4df8a refactor(example): misc improvements 2022-07-13 19:39:11 +02:00
Pierre-Louis Mercereau
7e4c52dbd1 feat(example): add title and GitHub link 2022-07-13 17:20:55 +02:00
Pierre-Louis Mercereau
446dc01bde feat(example): toast anonymous user errors 2022-07-13 17:07:25 +02:00
Pierre-Louis Mercereau
a1989c51f8 feat(example): enable anonymous users in the backend 2022-07-13 17:00:59 +02:00
Pierre-Louis Mercereau
1383de558a feat(example): add email verification dialog on sign-in 2022-07-13 17:00:39 +02:00
Pierre-Louis Mercereau
d828107f74 fix(example): remove "return" from previous "setup()" syntax 2022-07-13 16:43:10 +02:00
Pierre-Louis Mercereau
4a1650cb35 refactor(example): use the template "setup" syntax 2022-07-13 16:37:23 +02:00
Pierre-Louis Mercereau
913651d440 feat(example): add "verification email sent" dialog 2022-07-13 16:29:40 +02:00
Pierre-Louis Mercereau
6af2d52666 chore(vue): bump dependencies 2022-07-13 15:52:12 +02:00
Svarto
18ac56d097 added changeset for hasura-auth-js 2022-06-06 21:38:53 +02:00
Svarto
366fc2403d included an optional ticket in changePassword function to allow for changeing password of logged out users, matches hasura-auth #186 2022-06-06 21:27:44 +02:00
93 changed files with 1753 additions and 1073 deletions

View File

@@ -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">

View File

@@ -1,5 +1,11 @@
# @nhost/docs
## 0.0.2
### Patch Changes
- 747aa969: fix: added twitch and discord as provider
## 0.0.1
### Patch Changes

View File

@@ -22,4 +22,6 @@ type Provider =
| 'strava'
| 'gitlab'
| 'bitbucket'
| 'discord'
| 'twitch'
```

View File

@@ -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`
---

View File

@@ -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`

View File

@@ -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)
---

View File

@@ -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`

View File

@@ -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`
---

View File

@@ -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`

View File

@@ -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)
---

View File

@@ -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`

View File

@@ -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>`
---

View File

@@ -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)
---

View File

@@ -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`

View File

@@ -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`

View File

@@ -1,6 +1,6 @@
{
"name": "@nhost/docs",
"version": "0.0.1",
"version": "0.0.2",
"private": true,
"scripts": {
"docusaurus": "docusaurus",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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

View File

@@ -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')
})
})
})

View File

@@ -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>

View File

@@ -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

View File

@@ -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:

View File

@@ -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"

View File

@@ -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"
}
}
}

View 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

View File

@@ -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) => ({

View File

@@ -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
```

View File

@@ -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>

View File

@@ -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:

View File

@@ -2,6 +2,7 @@ table:
name: provider_requests
schema: auth
configuration:
column_config: {}
custom_column_names: {}
custom_name: authProviderRequests
custom_root_fields:

View File

@@ -2,6 +2,7 @@ table:
name: providers
schema: auth
configuration:
column_config: {}
custom_column_names: {}
custom_name: authProviders
custom_root_fields:

View File

@@ -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

View File

@@ -2,6 +2,7 @@ table:
name: roles
schema: auth
configuration:
column_config: {}
custom_column_names: {}
custom_name: authRoles
custom_root_fields:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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,

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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')

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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">
&#8592; Other Login Options
&#8592; 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>

View File

@@ -1,6 +1,6 @@
<template>
<email-passwordless />
<v-btn class="my-1" block variant="text" color="primary" to="/signin">
&#8592; Other Login Options
&#8592; Other Sign-in Options
</v-btn>
</template>

View File

@@ -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&lsquo;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>

View File

@@ -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">
&#8592; 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>

View File

@@ -50,5 +50,5 @@ export const routes: RouteRecordRaw[] = [
}
]
},
{ path: '/apollo', component: ApolloPage }
{ path: '/apollo', component: ApolloPage, meta: { auth: true } }
]

View File

@@ -17,7 +17,7 @@
"noEmit": true,
"jsx": "preserve",
"declaration": false,
"declarationMap": false
"declarationMap": false,
},
"include": ["src"]
}

View File

@@ -1,5 +1,5 @@
import { defineConfig } from 'vite'
import path from 'path'
import vue from '@vitejs/plugin-vue'
import vuetify from '@vuetify/vite-plugin'

View File

@@ -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']
}

View File

@@ -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"
}
}

View File

@@ -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

View File

@@ -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"
}
}

View File

@@ -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

View File

@@ -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",

View File

@@ -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}`

View File

@@ -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' })) {

View File

@@ -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 {
/**

View File

@@ -149,6 +149,8 @@ export type Provider =
| 'strava'
| 'gitlab'
| 'bitbucket'
| 'discord'
| 'twitch'
// TODO share with hasura-auth
export interface JWTHasuraClaims {

View File

@@ -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

View File

@@ -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",

View File

@@ -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 }
}

View File

@@ -95,6 +95,7 @@ export interface ResetPasswordParams {
export interface ChangePasswordParams {
newPassword: string
ticket?: string
}
export interface SendVerificationEmailParams {

View File

@@ -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

View File

@@ -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",

View File

@@ -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

View File

@@ -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"
}
}
}

View File

@@ -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

View File

@@ -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",

View File

@@ -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

View File

@@ -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"
}
}
}

View File

@@ -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

View File

@@ -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"
}
}
}

View File

@@ -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

View File

@@ -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",

View File

@@ -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'

View 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 }
}

View File

@@ -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

View File

@@ -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"
}

View File

@@ -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')
})
}
}
}

View File

@@ -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'

View 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

File diff suppressed because it is too large Load Diff