Skip to content

Commit 149fccf

Browse files
author
sw-yx
committedMay 30, 2019
working contextapi
1 parent 013e1fd commit 149fccf

15 files changed

+3802
-10877
lines changed
 

‎.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ node_modules
88
build
99
dist
1010
.rpt2_cache
11+
.rts2_cache_cjs
12+
.rts2_cache_es
13+
.rts2_cache_umd
1114

1215
# misc
1316
.DS_Store

‎example/.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/node_modules
55
/.pnp
66
.pnp.js
7-
7+
.cache
88
# testing
99
/coverage
1010

‎example/index.html

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<html lang="en">
2+
<head>
3+
<meta charset="UTF-8" />
4+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
5+
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
6+
<title>Playground</title>
7+
</head>
8+
9+
<body>
10+
<div id="root"></div>
11+
<script src="./src/index.tsx"></script>
12+
</body>
13+
</html>

‎example/package.json

+19-31
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,29 @@
11
{
22
"name": "example",
3-
"version": "0.1.0",
4-
"private": true,
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"license": "MIT",
6+
"scripts": {
7+
"start": "parcel index.html",
8+
"build": "parcel build index.html"
9+
},
510
"dependencies": {
611
"@reach/router": "^1.2.1",
12+
"react-app-polyfill": "^1.0.1",
713
"react-netlify-identity": "latest",
8-
"react-scripts": "3.0.0",
14+
"react": "latest",
15+
"react-dom": "latest",
916
"typescript": "3.4.5"
1017
},
11-
"scripts": {
12-
"start": "react-scripts start",
13-
"build": "react-scripts build",
14-
"test": "react-scripts test",
15-
"eject": "react-scripts eject"
16-
},
17-
"eslintConfig": {
18-
"extends": "react-app"
19-
},
20-
"browserslist": {
21-
"production": [
22-
">0.2%",
23-
"not dead",
24-
"not op_mini all"
25-
],
26-
"development": [
27-
"last 1 chrome version",
28-
"last 1 firefox version",
29-
"last 1 safari version"
30-
]
31-
},
3218
"devDependencies": {
33-
"@types/jest": "24.0.11",
34-
"@types/node": "11.13.8",
3519
"@types/reach__router": "^1.2.4",
36-
"@types/react": "16.8.14",
37-
"@types/react-dom": "16.8.4",
38-
"react": "^16.8.6",
39-
"react-dom": "^16.8.6"
40-
}
20+
"@types/react": "^16.8.15",
21+
"@types/react-dom": "^16.8.4",
22+
"parcel": "^1.12.3",
23+
"typescript": "^3.4.5"
24+
},
25+
"browserslist": [
26+
"last 1 chrome version",
27+
"not dead"
28+
]
4129
}

‎example/src/App.tsx

+108-73
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,65 @@
1-
import React from "react"
2-
import { Router, Link, navigate } from "@reach/router"
3-
import "./App.css"
4-
import { useNetlifyIdentity, Settings } from "react-netlify-identity"
5-
import useLoading from "./useLoading"
1+
import React from 'react';
2+
import { Router, Link, navigate } from '@reach/router';
3+
import './App.css';
4+
import { useIdentityCtx, IdentityContextProvider, Settings } from '../../src';
5+
import useLoading from './useLoading';
66

7-
let IdentityContext = React.createContext<ReturnType<typeof useNetlifyIdentity>>(undefined!)
8-
type MaybePathProps = { path?: string }
9-
function PrivateRoute(props: React.PropsWithoutRef<MaybePathProps & { as: React.ComponentType<MaybePathProps> }>) {
10-
const identity = React.useContext(IdentityContext)
11-
let { as: Comp } = props
7+
type MaybePathProps = { path?: string };
8+
function PrivateRoute(
9+
props: React.PropsWithoutRef<
10+
MaybePathProps & { as: React.ComponentType<MaybePathProps> }
11+
>
12+
) {
13+
const identity = useIdentityCtx();
14+
let { as: Comp } = props;
1215
return identity.user ? (
1316
<Comp />
1417
) : (
1518
<div>
1619
<h3>You are trying to view a protected page. Please log in</h3>
1720
<Login />
1821
</div>
19-
)
22+
);
2023
}
2124

2225
function Login({ }: MaybePathProps) {
23-
const { loginUser, signupUser, settings, loginProvider } = React.useContext(IdentityContext)
24-
const formRef = React.useRef<HTMLFormElement>(null!)
25-
const [msg, setMsg] = React.useState("")
26-
const [isLoading, load] = useLoading()
27-
const [setting, setSetting] = React.useState<Settings | null>(null)
26+
const { loginUser, signupUser, settings, loginProvider } = useIdentityCtx();
27+
const formRef = React.useRef<HTMLFormElement>(null!);
28+
const [msg, setMsg] = React.useState('');
29+
const [isLoading, load] = useLoading();
30+
const [setting, setSetting] = React.useState<Settings | null>(null);
2831
React.useEffect(() => {
29-
settings().then(x => setSetting(x))
30-
}, [settings])
32+
settings().then(x => setSetting(x));
33+
}, [settings]);
3134
const signup = () => {
32-
const email = formRef.current.email.value
33-
const password = formRef.current.password.value
34-
load(signupUser(email, password, { data: "signed up thru react-netlify-identity" }))
35+
const email = formRef.current.email.value;
36+
const password = formRef.current.password.value;
37+
load(
38+
signupUser(email, password, {
39+
data: 'signed up thru react-netlify-identity',
40+
})
41+
)
3542
.then(user => {
36-
console.log("Success! Signed up", user)
37-
navigate("/dashboard")
43+
console.log('Success! Signed up', user);
44+
navigate('/dashboard');
3845
})
39-
.catch(err => void console.error(err) || setMsg("Error: " + err.message))
40-
}
46+
.catch(err => void console.error(err) || setMsg('Error: ' + err.message));
47+
};
4148
return (
4249
<form
4350
ref={formRef}
4451
onSubmit={e => {
45-
e.preventDefault()
46-
const email = formRef.current.email.value
47-
const password = formRef.current.password.value
52+
e.preventDefault();
53+
const email = formRef.current.email.value;
54+
const password = formRef.current.password.value;
4855
load(loginUser(email, password))
4956
.then(user => {
50-
console.log("Success! Logged in", user)
51-
navigate("/dashboard")
57+
console.log('Success! Logged in', user);
58+
navigate('/dashboard');
5259
})
53-
.catch(err => void console.error(err) || setMsg("Error: " + err.message))
60+
.catch(
61+
err => void console.error(err) || setMsg('Error: ' + err.message)
62+
);
5463
}}
5564
>
5665
<div>
@@ -76,27 +85,39 @@ function Login({ }: MaybePathProps) {
7685
)}
7786
{setting && <pre>{JSON.stringify(setting, null, 2)}</pre>}
7887
{setting && setting.external.bitbucket && (
79-
<button className="btn" onClick={() => loginProvider("bitbucket")}>
88+
<button className="btn" onClick={() => loginProvider('bitbucket')}>
8089
bitbucket
8190
</button>
8291
)}
8392
{setting && setting.external.github && (
84-
<button className="btn" style={{ background: "lightblue" }} onClick={() => loginProvider("github")}>
93+
<button
94+
className="btn"
95+
style={{ background: 'lightblue' }}
96+
onClick={() => loginProvider('github')}
97+
>
8598
GitHub
8699
</button>
87100
)}
88101
{setting && setting.external.gitlab && (
89-
<button className="btn" style={{ background: "darkgreen" }} onClick={() => loginProvider("gitlab")}>
102+
<button
103+
className="btn"
104+
style={{ background: 'darkgreen' }}
105+
onClick={() => loginProvider('gitlab')}
106+
>
90107
Gitlab
91108
</button>
92109
)}
93110
{setting && setting.external.google && (
94-
<button className="btn" style={{ background: "lightsalmon" }} onClick={() => loginProvider("google")}>
111+
<button
112+
className="btn"
113+
style={{ background: 'lightsalmon' }}
114+
onClick={() => loginProvider('google')}
115+
>
95116
Google
96117
</button>
97118
)}
98119
</form>
99-
)
120+
);
100121
}
101122

102123
function Home({ }: MaybePathProps) {
@@ -106,50 +127,58 @@ function Home({ }: MaybePathProps) {
106127
<p>
107128
this is a <b>Public Page</b>, not behind an authentication wall
108129
</p>
109-
<div style={{ backgroundColor: "#EEE", padding: "1rem" }}>
130+
<div style={{ backgroundColor: '#EEE', padding: '1rem' }}>
110131
<div>
111132
<a
112133
href={`https://app.netlify.com/start/deploy?repository=https://github.com/netlify/create-react-app-lambda/tree/reachRouterAndGoTrueDemo&stack=cms`}
113134
>
114-
<img src="https://www.netlify.com/img/deploy/button.svg" alt="Deploy to Netlify" />
135+
<img
136+
src="https://www.netlify.com/img/deploy/button.svg"
137+
alt="Deploy to Netlify"
138+
/>
115139
</a>
116140
</div>
117-
This demo is{" "}
118-
<a href="https://github.com/netlify/create-react-app-lambda/tree/reachRouterAndGoTrueDemo">Open Source.</a>{" "}
141+
This demo is{' '}
142+
<a href="https://github.com/netlify/create-react-app-lambda/tree/reachRouterAndGoTrueDemo">
143+
Open Source.
144+
</a>{' '}
119145
</div>
120146
</div>
121-
)
147+
);
122148
}
123149

124150
function About({ }: MaybePathProps) {
125-
return <div>About</div>
151+
return <div>About</div>;
126152
}
127153

128154
function Dashboard({ }: MaybePathProps) {
129-
const props = React.useContext(IdentityContext)
130-
const { isConfirmedUser, authedFetch } = props
131-
const [isLoading, load] = useLoading()
132-
const [msg, setMsg] = React.useState("Click to load something")
155+
const props = useIdentityCtx();
156+
const { isConfirmedUser, authedFetch } = props;
157+
const [isLoading, load] = useLoading();
158+
const [msg, setMsg] = React.useState('Click to load something');
133159
const handler = () => {
134-
load(authedFetch.get("/.netlify/functions/authEndPoint")).then(setMsg)
135-
}
160+
load(authedFetch.get('/.netlify/functions/authEndPoint')).then(setMsg);
161+
};
136162
return (
137163
<div>
138164
<h3>This is a Protected Dashboard!</h3>
139165
{!isConfirmedUser && (
140-
<pre style={{ backgroundColor: "papayawhip" }}>
141-
You have not confirmed your email. Please confirm it before you ping the API.
166+
<pre style={{ backgroundColor: 'papayawhip' }}>
167+
You have not confirmed your email. Please confirm it before you ping
168+
the API.
142169
</pre>
143170
)}
144171
<hr />
145172
<div>
146173
<p>You can try pinging our authenticated API here.</p>
147-
<p>If you are logged in, you should be able to see a `user` info here.</p>
174+
<p>
175+
If you are logged in, you should be able to see a `user` info here.
176+
</p>
148177
<button onClick={handler}>Ping authenticated API</button>
149178
{isLoading ? <Spinner /> : <pre>{JSON.stringify(msg, null, 2)}</pre>}
150179
</div>
151180
</div>
152-
)
181+
);
153182
}
154183

155184
function Spinner() {
@@ -160,43 +189,46 @@ function Spinner() {
160189
<div className="sk-cube4 sk-cube" />
161190
<div className="sk-cube3 sk-cube" />
162191
</div>
163-
)
192+
);
164193
}
165194
function Nav() {
166-
const { isLoggedIn } = React.useContext(IdentityContext)
195+
const { isLoggedIn } = useIdentityCtx();
167196
return (
168197
<nav>
169198
<Link to="/">Home</Link> | <Link to="dashboard">Dashboard</Link>
170-
{" | "}
171-
<span>{isLoggedIn ? <Logout /> : <Link to="login">Log In/Sign Up</Link>}</span>
199+
{' | '}
200+
<span>
201+
{isLoggedIn ? <Logout /> : <Link to="login">Log In/Sign Up</Link>}
202+
</span>
172203
</nav>
173-
)
204+
);
174205
}
175206
function Logout() {
176-
const { logoutUser } = React.useContext(IdentityContext)
177-
return <button onClick={logoutUser}>You are signed in. Log Out</button>
207+
const { logoutUser } = useIdentityCtx();
208+
return <button onClick={logoutUser}>You are signed in. Log Out</button>;
178209
}
179210

180211
function App() {
181212
// TODO: SUPPLY A URL EITHER FROM ENVIRONMENT VARIABLES OR SOME OTHER STRATEGY
182213
// e.g. 'https://unruffled-roentgen-04c3b8.netlify.com'
183214
const domainToUse =
184-
new URL(window.location.origin).hostname === "localhost"
185-
? "http://react-netlify-identity.netlify.com"
186-
: window.location.origin
187-
const [url, setUrl] = React.useState(domainToUse)
188-
const identity = useNetlifyIdentity(url)
189-
console.log({ identity, url })
215+
new URL(window.location.origin).hostname === 'localhost'
216+
? 'http://react-netlify-identity.netlify.com'
217+
: window.location.origin;
218+
const [url, setUrl] = React.useState(domainToUse);
190219
return (
191-
<IdentityContext.Provider value={identity}>
220+
<IdentityContextProvider url={url}>
192221
<div className="App">
193222
<div className="Appheader">
194223
<h1 className="title">
195224
<span>Netlify Identity</span>
196225
<span className="italic">&</span> <span>Reach Router</span>
197226
</h1>
198227
<label>
199-
<a href="https://www.netlify.com/docs/identity/">Netlify Identity</a> Instance:{" "}
228+
<a href="https://www.netlify.com/docs/identity/">
229+
Netlify Identity
230+
</a>{' '}
231+
Instance:{' '}
200232
<input
201233
type="text"
202234
placeholder="your instance here e.g. https://unruffled-roentgen-04c3b8.netlify.com"
@@ -205,11 +237,14 @@ function App() {
205237
size={50}
206238
/>
207239
<div>
208-
<div style={{ display: "inline-block" }}>
209-
{window.location.hostname === "localhost" ? (
240+
<div style={{ display: 'inline-block' }}>
241+
{window.location.hostname === 'localhost' ? (
210242
<pre>WARNING: this demo doesn't work on localhost</pre>
211243
) : (
212-
<pre>your instance here e.g. https://unruffled-roentgen-04c3b8.netlify.com</pre>
244+
<pre>
245+
your instance here e.g.
246+
https://unruffled-roentgen-04c3b8.netlify.com
247+
</pre>
213248
)}
214249
</div>
215250
</div>
@@ -223,8 +258,8 @@ function App() {
223258
<PrivateRoute as={Dashboard} path="/dashboard" />
224259
</Router>
225260
</div>
226-
</IdentityContext.Provider>
227-
)
261+
</IdentityContextProvider>
262+
);
228263
}
229264

230-
export default App
265+
export default App;

‎example/src/react-app-env.d.ts

-1
This file was deleted.

‎example/tsconfig.json

+14-20
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,19 @@
11
{
22
"compilerOptions": {
3+
"allowSyntheticDefaultImports": false,
34
"target": "es5",
4-
"lib": [
5-
"dom",
6-
"dom.iterable",
7-
"esnext"
8-
],
9-
"allowJs": true,
10-
"skipLibCheck": true,
11-
"esModuleInterop": true,
12-
"allowSyntheticDefaultImports": true,
13-
"strict": true,
14-
"forceConsistentCasingInFileNames": true,
15-
"module": "esnext",
5+
"module": "commonjs",
6+
"jsx": "react",
167
"moduleResolution": "node",
17-
"resolveJsonModule": true,
18-
"isolatedModules": true,
19-
"noEmit": true,
20-
"jsx": "preserve"
21-
},
22-
"include": [
23-
"src"
24-
]
8+
"noImplicitAny": false,
9+
"noUnusedLocals": false,
10+
"noUnusedParameters": false,
11+
"removeComments": true,
12+
"strictNullChecks": true,
13+
"preserveConstEnums": true,
14+
"sourceMap": true,
15+
"lib": ["es2015", "es2016", "dom"],
16+
"baseUrl": ".",
17+
"types": ["node"]
18+
}
2519
}

‎example/yarn.lock

+1,108-5,944
Large diffs are not rendered by default.

‎package.json

+24-20
Original file line numberDiff line numberDiff line change
@@ -6,51 +6,55 @@
66
"license": "MIT",
77
"repository": "https://www.github.com/sw-yx/react-netlify-identity",
88
"main": "dist/index.js",
9-
"module": "dist/index.es.js",
9+
"umd:main": "dist/react-netlify-identity.umd.production.js",
10+
"module": "dist/react-netlify-identity.es.production.js",
11+
"typings": "dist/index.d.ts",
1012
"jsnext:main": "dist/index.es.js",
1113
"engines": {
1214
"node": ">=8",
1315
"npm": ">=5"
1416
},
1517
"scripts": {
16-
"test": "cross-env CI=1 react-scripts-ts test --env=jsdom",
17-
"test:watch": "react-scripts-ts test --env=jsdom",
18-
"build": "rollup -c",
19-
"start": "rollup -c -w",
18+
"start": "tsdx watch",
19+
"build": "tsdx build",
2020
"prepare": "yarn run build",
2121
"version": "yarn run build && auto-changelog -p --template keepachangelog && git add .",
2222
"prepublishOnly": "git push && git push --tags && gh-release",
2323
"predeploy": "cd example && yarn install && yarn run build",
2424
"deploy": "gh-pages -d example/build"
2525
},
2626
"dependencies": {
27-
"gotrue-js": "^0.9.25"
27+
"gotrue-js": "^0.9.25",
28+
"tsdx": "^0.6.0"
2829
},
2930
"peerDependencies": {
3031
"react": "^15.0.0 || ^16.0.0",
3132
"react-dom": "^15.0.0 || ^16.0.0"
3233
},
34+
"husky": {
35+
"hooks": {
36+
"pre-commit": "pretty-quick --staged"
37+
}
38+
},
39+
"prettier": {
40+
"printWidth": 80,
41+
"semi": true,
42+
"singleQuote": true,
43+
"trailingComma": "es5"
44+
},
3345
"devDependencies": {
3446
"@svgr/rollup": "^4.2.0",
3547
"@types/jest": "^23.1.5",
3648
"@types/react": "^16.7.13",
3749
"@types/react-dom": "^16.0.11",
3850
"auto-changelog": "^1.13.0",
39-
"babel-core": "^6.26.3",
40-
"babel-runtime": "^6.26.0",
41-
"cross-env": "^5.1.4",
42-
"gh-pages": "^1.2.0",
4351
"gh-release": "^3.5.0",
44-
"react-scripts-ts": "^2.16.0",
45-
"rollup": "^0.62.0",
46-
"rollup-plugin-babel": "^3.0.7",
47-
"rollup-plugin-commonjs": "^9.1.3",
48-
"rollup-plugin-node-resolve": "^3.3.0",
49-
"rollup-plugin-peer-deps-external": "^2.2.0",
50-
"rollup-plugin-postcss": "^1.6.2",
51-
"rollup-plugin-typescript2": "^0.17.0",
52-
"rollup-plugin-url": "^1.4.0",
53-
"typescript": "^3.2.2"
52+
"husky": "^2.3.0",
53+
"prettier": "^1.17.1",
54+
"pretty-quick": "^1.11.0",
55+
"react": "^16.8.6",
56+
"react-dom": "^16.8.6",
57+
"typescript": "^3.5.1"
5458
},
5559
"files": [
5660
"dist"

‎rollup.config.js

-42
This file was deleted.

‎src/index.tsx

+160-96
Original file line numberDiff line numberDiff line change
@@ -1,145 +1,181 @@
1-
import React from "react"
1+
import React from 'react';
22

3-
import GoTrue, { User, Settings } from "gotrue-js"
4-
import { runRoutes } from "./runRoutes"
3+
import GoTrue, { User, Settings } from 'gotrue-js';
4+
import { runRoutes } from './runRoutes';
55

6-
type authChangeParam = (user?: User) => string | void
6+
type authChangeParam = (user?: User) => string | void;
77

8-
export type Settings = Settings
9-
export type User = User
8+
export type Settings = Settings;
9+
export type User = User;
1010

1111
export type ReactNetlifyIdentityAPI = {
12-
user: User | undefined
12+
user: User | undefined;
1313
/** not meant for normal use! you should mostly use one of the other exported methods to update the user instance */
14-
setUser: React.Dispatch<React.SetStateAction<User | undefined>>
15-
isConfirmedUser: boolean
16-
isLoggedIn: boolean
17-
signupUser: (email: string, password: string, data: Object) => Promise<User | undefined>
18-
loginUser: (email: string, password: string, remember?: boolean) => Promise<User | undefined>
19-
logoutUser: () => Promise<User | undefined>
20-
requestPasswordRecovery: (email: string) => Promise<void>
21-
recoverAccount: (token: string, remember?: boolean | undefined) => Promise<User>
22-
updateUser: (fields: Object) => Promise<User | undefined>
23-
getFreshJWT: () => Promise<string>
14+
setUser: React.Dispatch<React.SetStateAction<User | undefined>>;
15+
isConfirmedUser: boolean;
16+
isLoggedIn: boolean;
17+
signupUser: (
18+
email: string,
19+
password: string,
20+
data: Object
21+
) => Promise<User | undefined>;
22+
loginUser: (
23+
email: string,
24+
password: string,
25+
remember?: boolean
26+
) => Promise<User | undefined>;
27+
logoutUser: () => Promise<User | undefined>;
28+
requestPasswordRecovery: (email: string) => Promise<void>;
29+
recoverAccount: (
30+
token: string,
31+
remember?: boolean | undefined
32+
) => Promise<User>;
33+
updateUser: (fields: Object) => Promise<User | undefined>;
34+
getFreshJWT: () => Promise<string>;
2435
authedFetch: {
25-
get: (endpoint: string, obj?: {}) => Promise<any>
26-
post: (endpoint: string, obj?: {}) => Promise<any>
27-
put: (endpoint: string, obj?: {}) => Promise<any>
28-
delete: (endpoint: string, obj?: {}) => Promise<any>
29-
}
30-
_goTrueInstance: GoTrue
31-
_url: string
32-
loginProvider: (provider: "bitbucket" | "github" | "gitlab" | "google") => void
33-
acceptInviteExternalUrl: (provider: "bitbucket" | "github" | "gitlab" | "google", token: string) => string
34-
settings: () => Promise<Settings>
35-
}
36+
get: (endpoint: string, obj?: {}) => Promise<any>;
37+
post: (endpoint: string, obj?: {}) => Promise<any>;
38+
put: (endpoint: string, obj?: {}) => Promise<any>;
39+
delete: (endpoint: string, obj?: {}) => Promise<any>;
40+
};
41+
_goTrueInstance: GoTrue;
42+
_url: string;
43+
loginProvider: (
44+
provider: 'bitbucket' | 'github' | 'gitlab' | 'google'
45+
) => void;
46+
acceptInviteExternalUrl: (
47+
provider: 'bitbucket' | 'github' | 'gitlab' | 'google',
48+
token: string
49+
) => string;
50+
settings: () => Promise<Settings>;
51+
};
52+
53+
const [_useIdentityCtx, _IdentityCtxProvider] = createCtx<
54+
ReactNetlifyIdentityAPI
55+
>();
56+
export const useIdentityCtx = _useIdentityCtx; // we dont want to expose _IdentityCtxProvider
3657

37-
export const IdentityContext = React.createContext<ReactNetlifyIdentityAPI | undefined>(undefined)
58+
/** most people should use this provider directly */
3859
export function IdentityContextProvider({
3960
url,
4061
children,
41-
onAuthChange = () => {}
62+
onAuthChange = () => {},
4263
}: {
43-
url: string
44-
children: React.ReactNode
45-
onAuthChange: authChangeParam
64+
url: string;
65+
children: React.ReactNode;
66+
onAuthChange?: authChangeParam;
4667
}) {
4768
/******** SETUP */
4869
if (!url || !validateUrl(url)) {
4970
// just a safety check in case a JS user tries to skip this
5071
throw new Error(
51-
"invalid netlify instance URL: " + url + ". Please check the docs for proper usage or file an issue."
52-
)
72+
'invalid netlify instance URL: ' +
73+
url +
74+
'. Please check the docs for proper usage or file an issue.'
75+
);
5376
}
54-
const identity = React.useMemo(() => useNetlifyIdentity(url, onAuthChange), [url, onAuthChange])
55-
return <IdentityContext.Provider value={identity}>{children}</IdentityContext.Provider>
77+
const identity = React.useMemo(() => useNetlifyIdentity(url, onAuthChange), [
78+
url,
79+
onAuthChange,
80+
]);
81+
return (
82+
<_IdentityCtxProvider value={identity}>{children}</_IdentityCtxProvider>
83+
);
5684
}
5785

58-
// // Deprecated for now
59-
// interface NIProps {
60-
// children: any
61-
// url: string
62-
// onAuthChange?: authChangeParam
63-
// }
64-
// export default function NetlifyIdentity({ children, url, onAuthChange }: NIProps) {
65-
// return children(useNetlifyIdentity(url, onAuthChange))
66-
// }
67-
export function useNetlifyIdentity(url: string, onAuthChange: authChangeParam = () => {}): ReactNetlifyIdentityAPI {
86+
/** some people may want to use this as a hook and bring their own contexts */
87+
export function useNetlifyIdentity(
88+
url: string,
89+
onAuthChange: authChangeParam = () => {}
90+
): ReactNetlifyIdentityAPI {
6891
const goTrueInstance = new GoTrue({
6992
APIUrl: `${url}/.netlify/identity`,
70-
setCookie: true
71-
})
93+
setCookie: true,
94+
});
7295

73-
const [user, setUser] = React.useState<User | undefined>(goTrueInstance.currentUser() || undefined)
96+
const [user, setUser] = React.useState<User | undefined>(
97+
goTrueInstance.currentUser() || undefined
98+
);
7499
const _setUser = (_user: User | undefined) => {
75-
setUser(_user)
76-
onAuthChange(_user) // if someone's subscribed to auth changes, let 'em know
77-
return _user // so that we can continue chaining
78-
}
100+
setUser(_user);
101+
onAuthChange(_user); // if someone's subscribed to auth changes, let 'em know
102+
return _user; // so that we can continue chaining
103+
};
79104

80105
React.useEffect(() => {
81-
runRoutes(goTrueInstance, _setUser)
82-
}, [])
106+
runRoutes(goTrueInstance, _setUser);
107+
}, []);
83108

84109
/******* OPERATIONS */
85110
// make sure the Registration preferences under Identity settings in your Netlify dashboard are set to Open.
86111
// https://react-netlify-identity.netlify.com/login#access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTY0ODY3MjEsInN1YiI6ImNiZjY5MTZlLTNlZGYtNGFkNS1iOTYzLTQ4ZTY2NDcyMDkxNyIsImVtYWlsIjoic2hhd250aGUxQGdtYWlsLmNvbSIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6ImdpdGh1YiJ9LCJ1c2VyX21ldGFkYXRhIjp7ImF2YXRhcl91cmwiOiJodHRwczovL2F2YXRhcnMxLmdpdGh1YnVzZXJjb250ZW50LmNvbS91LzY3NjQ5NTc_dj00IiwiZnVsbF9uYW1lIjoic3d5eCJ9fQ.E8RrnuCcqq-mLi1_Q5WHJ-9THIdQ3ha1mePBKGhudM0&expires_in=3600&refresh_token=OyA_EdRc7WOIVhY7RiRw5w&token_type=bearer
87112
/******* external oauth */
88-
type Provider = "bitbucket" | "github" | "gitlab" | "google"
113+
type Provider = 'bitbucket' | 'github' | 'gitlab' | 'google';
89114

90115
const loginProvider = (provider: Provider) => {
91-
const url = goTrueInstance.loginExternalUrl(provider)
92-
window.location.href = url
93-
}
116+
const url = goTrueInstance.loginExternalUrl(provider);
117+
window.location.href = url;
118+
};
94119
const acceptInviteExternalUrl = (provider: Provider, token: string) =>
95-
goTrueInstance.acceptInviteExternalUrl(provider, token)
96-
const settings: () => Promise<Settings> = goTrueInstance.settings.bind(goTrueInstance)
120+
goTrueInstance.acceptInviteExternalUrl(provider, token);
121+
const settings: () => Promise<Settings> = goTrueInstance.settings.bind(
122+
goTrueInstance
123+
);
97124

98125
/******* email auth */
99126
const signupUser = (email: string, password: string, data: Object) =>
100-
goTrueInstance.signup(email, password, data).then(_setUser) // TODO: make setUser optional?
101-
const loginUser = (email: string, password: string, remember: boolean = true) =>
102-
goTrueInstance.login(email, password, remember).then(_setUser)
103-
const requestPasswordRecovery = (email: string) => goTrueInstance.requestPasswordRecovery(email)
104-
const recoverAccount = (token: string, remember?: boolean | undefined) => goTrueInstance.recover(token, remember)
127+
goTrueInstance.signup(email, password, data).then(_setUser); // TODO: make setUser optional?
128+
const loginUser = (
129+
email: string,
130+
password: string,
131+
remember: boolean = true
132+
) => goTrueInstance.login(email, password, remember).then(_setUser);
133+
const requestPasswordRecovery = (email: string) =>
134+
goTrueInstance.requestPasswordRecovery(email);
135+
const recoverAccount = (token: string, remember?: boolean | undefined) =>
136+
goTrueInstance.recover(token, remember);
105137
const updateUser = (fields: Object) => {
106138
if (user == null) {
107-
throw new Error("No current user found - are you logged in?")
139+
throw new Error('No current user found - are you logged in?');
108140
} else {
109141
return user!
110142
.update(fields) // e.g. { email: "example@example.com", password: "password" }
111-
.then(_setUser)
143+
.then(_setUser);
112144
}
113-
}
145+
};
114146
const getFreshJWT = () => {
115-
if (!user) throw new Error("No current user found - are you logged in?")
116-
return user.jwt()
117-
}
147+
if (!user) throw new Error('No current user found - are you logged in?');
148+
return user.jwt();
149+
};
118150
const logoutUser = () => {
119-
if (!user) throw new Error("No current user found - are you logged in?")
120-
return user.logout().then(() => _setUser(undefined))
121-
}
151+
if (!user) throw new Error('No current user found - are you logged in?');
152+
return user.logout().then(() => _setUser(undefined));
153+
};
122154

123-
const genericAuthedFetch = (method: string) => (endpoint: string, obj = {}) => {
124-
if (!user || !user.token || !user.token.access_token) throw new Error("no user token found")
155+
const genericAuthedFetch = (method: string) => (
156+
endpoint: string,
157+
obj = {}
158+
) => {
159+
if (!user || !user.token || !user.token.access_token)
160+
throw new Error('no user token found');
125161
const defaultObj = {
126162
headers: {
127-
Accept: "application/json",
128-
"Content-Type": "application/json",
129-
Authorization: "Bearer " + user.token.access_token
130-
}
131-
}
132-
const finalObj = Object.assign(defaultObj, { method }, obj)
133-
return fetch(endpoint, finalObj).then((res) =>
134-
finalObj.headers["Content-Type"] === "application/json" ? res.json() : res
135-
)
136-
}
163+
Accept: 'application/json',
164+
'Content-Type': 'application/json',
165+
Authorization: 'Bearer ' + user.token.access_token,
166+
},
167+
};
168+
const finalObj = Object.assign(defaultObj, { method }, obj);
169+
return fetch(endpoint, finalObj).then(res =>
170+
finalObj.headers['Content-Type'] === 'application/json' ? res.json() : res
171+
);
172+
};
137173
const authedFetch = {
138-
get: genericAuthedFetch("GET"),
139-
post: genericAuthedFetch("POST"),
140-
put: genericAuthedFetch("PUT"),
141-
delete: genericAuthedFetch("DELETE")
142-
}
174+
get: genericAuthedFetch('GET'),
175+
post: genericAuthedFetch('POST'),
176+
put: genericAuthedFetch('PUT'),
177+
delete: genericAuthedFetch('DELETE'),
178+
};
143179

144180
/******* hook API */
145181
return {
@@ -160,12 +196,40 @@ export function useNetlifyIdentity(url: string, onAuthChange: authChangeParam =
160196
_url: url,
161197
loginProvider,
162198
acceptInviteExternalUrl,
163-
settings
164-
}
199+
settings,
200+
};
165201
}
166202

203+
/**
204+
*
205+
*
206+
* Utils
207+
*
208+
*/
209+
167210
function validateUrl(value: string) {
168211
return /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(
169212
value
170-
)
213+
);
171214
}
215+
216+
// lazy initialize contexts without providing a Nullable type upfront
217+
function createCtx<A>() {
218+
const ctx = React.createContext<A | undefined>(undefined);
219+
function useCtx() {
220+
const c = React.useContext(ctx);
221+
if (!c) throw new Error('useCtx must be inside a Provider with a value');
222+
return c;
223+
}
224+
return [useCtx, ctx.Provider] as const;
225+
}
226+
227+
// // Deprecated for now
228+
// interface NIProps {
229+
// children: any
230+
// url: string
231+
// onAuthChange?: authChangeParam
232+
// }
233+
// export default function NetlifyIdentity({ children, url, onAuthChange }: NIProps) {
234+
// return children(useNetlifyIdentity(url, onAuthChange))
235+
// }

‎src/runRoutes.tsx

+30-31
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,63 @@
1-
import GoTrue, { User } from "gotrue-js"
1+
import GoTrue, { User } from 'gotrue-js';
22

33
/**
44
* This code runs on every rerender so keep it light
55
* keep checking the current route and do logic based on the route
66
* as dictated by netlify identity's communication with us via hashes
77
*/
88

9-
const routes = /(confirmation|invite|recovery|email_change)_token=([^&]+)/
10-
const errorRoute = /error=access_denied&error_description=403/
11-
const accessTokenRoute = /access_token=/
12-
const confirmationRoute = /confirmation_token=/
9+
const routes = /(confirmation|invite|recovery|email_change)_token=([^&]+)/;
10+
const errorRoute = /error=access_denied&error_description=403/;
11+
const accessTokenRoute = /access_token=/;
12+
const confirmationRoute = /confirmation_token=/;
1313

14-
export function runRoutes(gotrue: GoTrue, setUser: (value: User) => User | undefined, remember = true) {
15-
const hash = (document.location.hash || "").replace(/^#\/?/, "")
16-
if (!hash) return // early terminate if no hash
14+
export function runRoutes(
15+
gotrue: GoTrue,
16+
setUser: (value: User) => User | undefined,
17+
remember = true
18+
) {
19+
const hash = (document.location.hash || '').replace(/^#\/?/, '');
20+
if (!hash) return; // early terminate if no hash
1721

18-
const m = hash.match(routes)
22+
const m = hash.match(routes);
1923
if (m) {
2024
// store.verifyToken(m[1], m[2]);
21-
document.location.hash = ""
25+
document.location.hash = '';
2226
}
2327

24-
const em = hash.match(errorRoute)
28+
const em = hash.match(errorRoute);
2529
if (em) {
2630
// store.openModal("signup");
27-
document.location.hash = ""
31+
document.location.hash = '';
2832
}
33+
const params = {} as { [key: string]: string };
34+
hash.split('&').forEach(pair => {
35+
const [key, value] = pair.split('=');
36+
params[key] = value;
37+
});
2938

30-
const am = hash.match(accessTokenRoute)
39+
const am = hash.match(accessTokenRoute);
3140
if (am) {
32-
const params = {}
33-
hash.split("&").forEach((pair) => {
34-
const [key, value] = pair.split("=")
35-
params[key] = value
36-
})
37-
if (!!document && params["access_token"]) {
38-
document.cookie = `nf_jwt=${params["access_token"]}`
41+
if (!!document && params['access_token']) {
42+
document.cookie = `nf_jwt=${params['access_token']}`;
3943
}
40-
document.location.hash = ""
44+
document.location.hash = '';
4145
// store.openModal("login");
4246
// store.completeExternalLogin(params);
4347
gotrue
4448
.createUser(params, remember)
4549
.then(setUser)
46-
.catch(console.error)
50+
.catch(console.error);
4751
}
4852

49-
const cm = hash.match(confirmationRoute)
53+
const cm = hash.match(confirmationRoute);
5054
if (cm) {
51-
const params = {}
52-
hash.split("&").forEach((pair) => {
53-
const [key, value] = pair.split("=")
54-
params[key] = value
55-
})
56-
document.location.hash = ""
55+
document.location.hash = '';
5756
// store.openModal("login");
5857
// store.completeExternalLogin(params);
5958
gotrue
60-
.confirm(params["confirmation_token"])
59+
.confirm(params['confirmation_token'])
6160
.then(setUser)
62-
.catch(console.error)
61+
.catch(console.error);
6362
}
6463
}

‎tsconfig.json

+23-15
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,30 @@
11
{
2+
"include": ["src", "types"],
23
"compilerOptions": {
3-
"outDir": "build",
4-
"module": "esnext",
54
"target": "es5",
6-
"lib": ["es6", "dom", "es2016", "es2017"],
7-
"sourceMap": true,
8-
"allowJs": false,
9-
"jsx": "react",
5+
"module": "esnext",
6+
"lib": ["dom", "esnext"],
7+
"importHelpers": true,
108
"declaration": true,
11-
"moduleResolution": "node",
12-
"forceConsistentCasingInFileNames": true,
13-
"noImplicitReturns": true,
9+
"sourceMap": true,
10+
"rootDir": "./",
1411
"strict": true,
15-
"allowSyntheticDefaultImports": true,
16-
"suppressImplicitAnyIndexErrors": true,
12+
"noImplicitAny": true,
13+
"strictNullChecks": true,
14+
"strictFunctionTypes": true,
15+
"strictPropertyInitialization": true,
16+
"noImplicitThis": true,
17+
"alwaysStrict": true,
1718
"noUnusedLocals": true,
18-
"noUnusedParameters": true
19-
},
20-
"include": ["src"],
21-
"exclude": ["node_modules", "build", "dist", "example", "rollup.config.js"]
19+
"noUnusedParameters": true,
20+
"noImplicitReturns": true,
21+
"noFallthroughCasesInSwitch": true,
22+
"moduleResolution": "node",
23+
"baseUrl": "./",
24+
"paths": {
25+
"*": ["src/*", "node_modules/*"]
26+
},
27+
"jsx": "react",
28+
"esModuleInterop": true
29+
}
2230
}

‎tsconfig.test.json

-6
This file was deleted.

‎yarn.lock

+2,299-4,597
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.