From 013e1fdf8fe2a74c5844af5b87221d224e51f3be Mon Sep 17 00:00:00 2001 From: sw-yx Date: Wed, 29 May 2019 15:49:49 -0400 Subject: [PATCH 1/3] update version --- README.md | 20 ++++---------- package.json | 2 +- src/index.tsx | 74 ++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 64 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 3afc658..cc988b8 100644 --- a/README.md +++ b/README.md @@ -81,25 +81,25 @@ function Login() { const email = formRef.current.email.value const password = formRef.current.password.value signupUser(email, password) - .then(user => { + .then((user) => { console.log("Success! Signed up", user) navigate("/dashboard") }) - .catch(err => console.error(err) || setMsg("Error: " + err.message)) + .catch((err) => console.error(err) || setMsg("Error: " + err.message)) } return (
{ + onSubmit={(e) => { e.preventDefault() const email = e.target.email.value const password = e.target.password.value load(loginUser(email, password, true)) - .then(user => { + .then((user) => { console.log("Success! Logged in", user) navigate("/dashboard") }) - .catch(err => console.error(err) || setMsg("Error: " + err.message)) + .catch((err) => console.error(err) || setMsg("Error: " + err.message)) }} >
@@ -174,16 +174,6 @@ function Dashboard() { -**As a render prop**: This is also exported as a render prop component, `NetlifyIdentity`, but we're not quite sure if its that useful if you can already use hooks in your app: - -```tsx - - {({ loginUser, signupUser }) => { - // use it - }} - -``` - ## License MIT © [sw-yx](https://github.com/sw-yx) diff --git a/package.json b/package.json index 85df366..9549d60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-netlify-identity", - "version": "0.0.16", + "version": "0.1.0-beta", "description": "use netlify identity with react", "author": "sw-yx", "license": "MIT", diff --git a/src/index.tsx b/src/index.tsx index c24067e..573a7e6 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -5,27 +5,68 @@ import { runRoutes } from "./runRoutes" type authChangeParam = (user?: User) => string | void -interface NIProps { - children: any - domain: string - onAuthChange?: authChangeParam -} - export type Settings = Settings export type User = User -export default function NetlifyIdentity({ children, domain, onAuthChange }: NIProps) { - return children(useNetlifyIdentity(domain, onAuthChange)) + +export type ReactNetlifyIdentityAPI = { + user: User | undefined + /** not meant for normal use! you should mostly use one of the other exported methods to update the user instance */ + setUser: React.Dispatch> + isConfirmedUser: boolean + isLoggedIn: boolean + signupUser: (email: string, password: string, data: Object) => Promise + loginUser: (email: string, password: string, remember?: boolean) => Promise + logoutUser: () => Promise + requestPasswordRecovery: (email: string) => Promise + recoverAccount: (token: string, remember?: boolean | undefined) => Promise + updateUser: (fields: Object) => Promise + getFreshJWT: () => Promise + authedFetch: { + get: (endpoint: string, obj?: {}) => Promise + post: (endpoint: string, obj?: {}) => Promise + put: (endpoint: string, obj?: {}) => Promise + delete: (endpoint: string, obj?: {}) => Promise + } + _goTrueInstance: GoTrue + _url: string + loginProvider: (provider: "bitbucket" | "github" | "gitlab" | "google") => void + acceptInviteExternalUrl: (provider: "bitbucket" | "github" | "gitlab" | "google", token: string) => string + settings: () => Promise } -export function useNetlifyIdentity(domain: string, onAuthChange: authChangeParam = () => {}) { + +export const IdentityContext = React.createContext(undefined) +export function IdentityContextProvider({ + url, + children, + onAuthChange = () => {} +}: { + url: string + children: React.ReactNode + onAuthChange: authChangeParam +}) { /******** SETUP */ - if (!domain || !validateUrl(domain)) { + if (!url || !validateUrl(url)) { // just a safety check in case a JS user tries to skip this throw new Error( - "invalid netlify instance URL: " + domain + ". Please check the docs for proper usage or file an issue." + "invalid netlify instance URL: " + url + ". Please check the docs for proper usage or file an issue." ) } + const identity = React.useMemo(() => useNetlifyIdentity(url, onAuthChange), [url, onAuthChange]) + return {children} +} + +// // Deprecated for now +// interface NIProps { +// children: any +// url: string +// onAuthChange?: authChangeParam +// } +// export default function NetlifyIdentity({ children, url, onAuthChange }: NIProps) { +// return children(useNetlifyIdentity(url, onAuthChange)) +// } +export function useNetlifyIdentity(url: string, onAuthChange: authChangeParam = () => {}): ReactNetlifyIdentityAPI { const goTrueInstance = new GoTrue({ - APIUrl: `${domain}/.netlify/identity`, + APIUrl: `${url}/.netlify/identity`, setCookie: true }) @@ -48,7 +89,7 @@ export function useNetlifyIdentity(domain: string, onAuthChange: authChangeParam const loginProvider = (provider: Provider) => { const url = goTrueInstance.loginExternalUrl(provider) - if (window) window.location.href = url + window.location.href = url } const acceptInviteExternalUrl = (provider: Provider, token: string) => goTrueInstance.acceptInviteExternalUrl(provider, token) @@ -89,7 +130,7 @@ export function useNetlifyIdentity(domain: string, onAuthChange: authChangeParam } } const finalObj = Object.assign(defaultObj, { method }, obj) - return fetch(endpoint, finalObj).then(res => + return fetch(endpoint, finalObj).then((res) => finalObj.headers["Content-Type"] === "application/json" ? res.json() : res ) } @@ -103,7 +144,8 @@ export function useNetlifyIdentity(domain: string, onAuthChange: authChangeParam /******* hook API */ return { user, - setUser, // use carefully!! mostly you should use the methods below + /** not meant for normal use! you should mostly use one of the other exported methods to update the user instance */ + setUser, isConfirmedUser: !!(user && user.confirmed_at), isLoggedIn: !!user, signupUser, @@ -115,7 +157,7 @@ export function useNetlifyIdentity(domain: string, onAuthChange: authChangeParam getFreshJWT, authedFetch, _goTrueInstance: goTrueInstance, - _domain: domain, + _url: url, loginProvider, acceptInviteExternalUrl, settings From 149fccf91317ffade6b5468c19cdfcc333cf245d Mon Sep 17 00:00:00 2001 From: sw-yx Date: Wed, 29 May 2019 22:56:58 -0400 Subject: [PATCH 2/3] working contextapi --- .gitignore | 3 + example/.gitignore | 2 +- example/index.html | 13 + example/package.json | 50 +- example/src/App.tsx | 181 +- example/src/react-app-env.d.ts | 1 - example/tsconfig.json | 34 +- example/yarn.lock | 7052 +++++--------------------------- package.json | 44 +- rollup.config.js | 42 - src/index.tsx | 256 +- src/runRoutes.tsx | 61 +- tsconfig.json | 38 +- tsconfig.test.json | 6 - yarn.lock | 6896 +++++++++++-------------------- 15 files changed, 3802 insertions(+), 10877 deletions(-) create mode 100644 example/index.html delete mode 100644 example/src/react-app-env.d.ts delete mode 100644 rollup.config.js delete mode 100644 tsconfig.test.json diff --git a/.gitignore b/.gitignore index 7005be7..2dbe075 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ node_modules build dist .rpt2_cache +.rts2_cache_cjs +.rts2_cache_es +.rts2_cache_umd # misc .DS_Store diff --git a/example/.gitignore b/example/.gitignore index 4d29575..8f9b412 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -4,7 +4,7 @@ /node_modules /.pnp .pnp.js - +.cache # testing /coverage diff --git a/example/index.html b/example/index.html new file mode 100644 index 0000000..b54da92 --- /dev/null +++ b/example/index.html @@ -0,0 +1,13 @@ + + + + + + Playground + + + +
+ + + diff --git a/example/package.json b/example/package.json index 3ec29ca..9194535 100644 --- a/example/package.json +++ b/example/package.json @@ -1,41 +1,29 @@ { "name": "example", - "version": "0.1.0", - "private": true, + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "scripts": { + "start": "parcel index.html", + "build": "parcel build index.html" + }, "dependencies": { "@reach/router": "^1.2.1", + "react-app-polyfill": "^1.0.1", "react-netlify-identity": "latest", - "react-scripts": "3.0.0", + "react": "latest", + "react-dom": "latest", "typescript": "3.4.5" }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "eslintConfig": { - "extends": "react-app" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, "devDependencies": { - "@types/jest": "24.0.11", - "@types/node": "11.13.8", "@types/reach__router": "^1.2.4", - "@types/react": "16.8.14", - "@types/react-dom": "16.8.4", - "react": "^16.8.6", - "react-dom": "^16.8.6" - } + "@types/react": "^16.8.15", + "@types/react-dom": "^16.8.4", + "parcel": "^1.12.3", + "typescript": "^3.4.5" + }, + "browserslist": [ + "last 1 chrome version", + "not dead" + ] } diff --git a/example/src/App.tsx b/example/src/App.tsx index 02b46c0..d25d295 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,14 +1,17 @@ -import React from "react" -import { Router, Link, navigate } from "@reach/router" -import "./App.css" -import { useNetlifyIdentity, Settings } from "react-netlify-identity" -import useLoading from "./useLoading" +import React from 'react'; +import { Router, Link, navigate } from '@reach/router'; +import './App.css'; +import { useIdentityCtx, IdentityContextProvider, Settings } from '../../src'; +import useLoading from './useLoading'; -let IdentityContext = React.createContext>(undefined!) -type MaybePathProps = { path?: string } -function PrivateRoute(props: React.PropsWithoutRef }>) { - const identity = React.useContext(IdentityContext) - let { as: Comp } = props +type MaybePathProps = { path?: string }; +function PrivateRoute( + props: React.PropsWithoutRef< + MaybePathProps & { as: React.ComponentType } + > +) { + const identity = useIdentityCtx(); + let { as: Comp } = props; return identity.user ? ( ) : ( @@ -16,41 +19,47 @@ function PrivateRoute(props: React.PropsWithoutRefYou are trying to view a protected page. Please log in
- ) + ); } function Login({ }: MaybePathProps) { - const { loginUser, signupUser, settings, loginProvider } = React.useContext(IdentityContext) - const formRef = React.useRef(null!) - const [msg, setMsg] = React.useState("") - const [isLoading, load] = useLoading() - const [setting, setSetting] = React.useState(null) + const { loginUser, signupUser, settings, loginProvider } = useIdentityCtx(); + const formRef = React.useRef(null!); + const [msg, setMsg] = React.useState(''); + const [isLoading, load] = useLoading(); + const [setting, setSetting] = React.useState(null); React.useEffect(() => { - settings().then(x => setSetting(x)) - }, [settings]) + settings().then(x => setSetting(x)); + }, [settings]); const signup = () => { - const email = formRef.current.email.value - const password = formRef.current.password.value - load(signupUser(email, password, { data: "signed up thru react-netlify-identity" })) + const email = formRef.current.email.value; + const password = formRef.current.password.value; + load( + signupUser(email, password, { + data: 'signed up thru react-netlify-identity', + }) + ) .then(user => { - console.log("Success! Signed up", user) - navigate("/dashboard") + console.log('Success! Signed up', user); + navigate('/dashboard'); }) - .catch(err => void console.error(err) || setMsg("Error: " + err.message)) - } + .catch(err => void console.error(err) || setMsg('Error: ' + err.message)); + }; return ( { - e.preventDefault() - const email = formRef.current.email.value - const password = formRef.current.password.value + e.preventDefault(); + const email = formRef.current.email.value; + const password = formRef.current.password.value; load(loginUser(email, password)) .then(user => { - console.log("Success! Logged in", user) - navigate("/dashboard") + console.log('Success! Logged in', user); + navigate('/dashboard'); }) - .catch(err => void console.error(err) || setMsg("Error: " + err.message)) + .catch( + err => void console.error(err) || setMsg('Error: ' + err.message) + ); }} >
@@ -76,27 +85,39 @@ function Login({ }: MaybePathProps) { )} {setting &&
{JSON.stringify(setting, null, 2)}
} {setting && setting.external.bitbucket && ( - )} {setting && setting.external.github && ( - )} {setting && setting.external.gitlab && ( - )} {setting && setting.external.google && ( - )} - ) + ); } function Home({ }: MaybePathProps) { @@ -106,50 +127,58 @@ function Home({ }: MaybePathProps) {

this is a Public Page, not behind an authentication wall

-
+
- This demo is{" "} - Open Source.{" "} + This demo is{' '} + + Open Source. + {' '}
- ) + ); } function About({ }: MaybePathProps) { - return
About
+ return
About
; } function Dashboard({ }: MaybePathProps) { - const props = React.useContext(IdentityContext) - const { isConfirmedUser, authedFetch } = props - const [isLoading, load] = useLoading() - const [msg, setMsg] = React.useState("Click to load something") + const props = useIdentityCtx(); + const { isConfirmedUser, authedFetch } = props; + const [isLoading, load] = useLoading(); + const [msg, setMsg] = React.useState('Click to load something'); const handler = () => { - load(authedFetch.get("/.netlify/functions/authEndPoint")).then(setMsg) - } + load(authedFetch.get('/.netlify/functions/authEndPoint')).then(setMsg); + }; return (

This is a Protected Dashboard!

{!isConfirmedUser && ( -
-          You have not confirmed your email. Please confirm it before you ping the API.
+        
+          You have not confirmed your email. Please confirm it before you ping
+          the API.
         
)}

You can try pinging our authenticated API here.

-

If you are logged in, you should be able to see a `user` info here.

+

+ If you are logged in, you should be able to see a `user` info here. +

{isLoading ? :
{JSON.stringify(msg, null, 2)}
}
- ) + ); } function Spinner() { @@ -160,35 +189,35 @@ function Spinner() {
- ) + ); } function Nav() { - const { isLoggedIn } = React.useContext(IdentityContext) + const { isLoggedIn } = useIdentityCtx(); return ( - ) + ); } function Logout() { - const { logoutUser } = React.useContext(IdentityContext) - return + const { logoutUser } = useIdentityCtx(); + return ; } function App() { // TODO: SUPPLY A URL EITHER FROM ENVIRONMENT VARIABLES OR SOME OTHER STRATEGY // e.g. 'https://unruffled-roentgen-04c3b8.netlify.com' const domainToUse = - new URL(window.location.origin).hostname === "localhost" - ? "http://react-netlify-identity.netlify.com" - : window.location.origin - const [url, setUrl] = React.useState(domainToUse) - const identity = useNetlifyIdentity(url) - console.log({ identity, url }) + new URL(window.location.origin).hostname === 'localhost' + ? 'http://react-netlify-identity.netlify.com' + : window.location.origin; + const [url, setUrl] = React.useState(domainToUse); return ( - +

@@ -196,7 +225,10 @@ function App() { & Reach Router