├── .gitignore
├── README.md
├── images.d.ts
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
├── src
├── components
│ ├── App.tsx
│ ├── Navigation.tsx
│ └── SignOutButton.tsx
├── constants
│ └── routes.ts
├── firebase
│ ├── AuthUserContext.ts
│ ├── auth.ts
│ ├── db.ts
│ ├── firebase.ts
│ ├── index.ts
│ ├── withAuthentication.tsx
│ └── withAuthorization.tsx
├── index.css
├── index.tsx
├── pages
│ ├── Account
│ │ ├── PasswordChangeForm.tsx
│ │ └── index.tsx
│ ├── Home
│ │ ├── UserList.tsx
│ │ └── index.tsx
│ ├── Landing
│ │ └── index.tsx
│ ├── PasswordForget
│ │ ├── PasswordForgetForm.tsx
│ │ └── index.tsx
│ ├── SignIn
│ │ ├── SignInForm.tsx
│ │ └── index.tsx
│ └── SignUp
│ │ ├── SingUpForm.tsx
│ │ └── index.tsx
└── registerServiceWorker.ts
├── tsconfig.json
├── tsconfig.prod.json
├── tsconfig.test.json
├── tslint.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | ### https://raw.github.com/github/gitignore/22a5eb3da72c0a224a179033e92722bdbf5a05ad/Ruby.gitignore
2 |
3 | *.gem
4 | *.rbc
5 | /.config
6 | /coverage/
7 | /InstalledFiles
8 | /pkg/
9 | /spec/reports/
10 | /spec/examples.txt
11 | /test/tmp/
12 | /test/version_tmp/
13 | /tmp/
14 |
15 | # Used by dotenv library to load environment variables.
16 | # .env
17 |
18 | ## Specific to RubyMotion:
19 | .dat*
20 | .repl_history
21 | build/
22 | *.bridgesupport
23 | build-iPhoneOS/
24 | build-iPhoneSimulator/
25 |
26 | ## Specific to RubyMotion (use of CocoaPods):
27 | #
28 | # We recommend against adding the Pods directory to your .gitignore. However
29 | # you should judge for yourself, the pros and cons are mentioned at:
30 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
31 | #
32 | # vendor/Pods/
33 |
34 | ## Documentation cache and generated files:
35 | /.yardoc/
36 | /_yardoc/
37 | /doc/
38 | /rdoc/
39 |
40 | ## Environment normalization:
41 | /.bundle/
42 | /vendor/bundle
43 | /lib/bundler/man/
44 |
45 | # for a library or gem, you might want to ignore these files since the code is
46 | # intended to run in multiple environments; otherwise, check them in:
47 | # Gemfile.lock
48 | # .ruby-version
49 | # .ruby-gemset
50 |
51 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
52 | .rvmrc
53 |
54 |
55 | ### https://raw.github.com/github/gitignore/22a5eb3da72c0a224a179033e92722bdbf5a05ad/Global/MacOS.gitignore
56 |
57 | # General
58 | .DS_Store
59 | .AppleDouble
60 | .LSOverride
61 |
62 | # Icon must end with two \r
63 | Icon
64 |
65 |
66 | # Thumbnails
67 | ._*
68 |
69 | # Files that might appear in the root of a volume
70 | .DocumentRevisions-V100
71 | .fseventsd
72 | .Spotlight-V100
73 | .TemporaryItems
74 | .Trashes
75 | .VolumeIcon.icns
76 | .com.apple.timemachine.donotpresent
77 |
78 | # Directories potentially created on remote AFP share
79 | .AppleDB
80 | .AppleDesktop
81 | Network Trash Folder
82 | Temporary Items
83 | .apdisk
84 |
85 |
86 | ### https://raw.github.com/github/gitignore/22a5eb3da72c0a224a179033e92722bdbf5a05ad/Global/JetBrains.gitignore
87 |
88 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
89 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
90 |
91 | # User-specific stuff:
92 | .idea/**/workspace.xml
93 | .idea/**/tasks.xml
94 | .idea/dictionaries
95 |
96 | # Sensitive or high-churn files:
97 | .idea/**/dataSources/
98 | .idea/**/dataSources.ids
99 | .idea/**/dataSources.xml
100 | .idea/**/dataSources.local.xml
101 | .idea/**/sqlDataSources.xml
102 | .idea/**/dynamic.xml
103 | .idea/**/uiDesigner.xml
104 |
105 | # Gradle:
106 | .idea/**/gradle.xml
107 | .idea/**/libraries
108 |
109 | # CMake
110 | cmake-build-debug/
111 | cmake-build-release/
112 |
113 | # Mongo Explorer plugin:
114 | .idea/**/mongoSettings.xml
115 |
116 | ## File-based project format:
117 | *.iws
118 |
119 | ## Plugin-specific files:
120 |
121 | # IntelliJ
122 | out/
123 |
124 | # mpeltonen/sbt-idea plugin
125 | .idea_modules/
126 |
127 | # JIRA plugin
128 | atlassian-ide-plugin.xml
129 |
130 | # Cursive Clojure plugin
131 | .idea/replstate.xml
132 |
133 | # Crashlytics plugin (for Android Studio and IntelliJ)
134 | com_crashlytics_export_strings.xml
135 | crashlytics.properties
136 | crashlytics-build.properties
137 | fabric.properties
138 |
139 |
140 | ### https://raw.github.com/github/gitignore/22a5eb3da72c0a224a179033e92722bdbf5a05ad/Node.gitignore
141 |
142 | # Logs
143 | logs
144 | *.log
145 | npm-debug.log*
146 | yarn-debug.log*
147 | yarn-error.log*
148 |
149 | # Runtime data
150 | pids
151 | *.pid
152 | *.seed
153 | *.pid.lock
154 |
155 | # Directory for instrumented libs generated by jscoverage/JSCover
156 | lib-cov
157 |
158 | # Coverage directory used by tools like istanbul
159 | coverage
160 |
161 | # nyc test coverage
162 | .nyc_output
163 |
164 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
165 | .grunt
166 |
167 | # Bower dependency directory (https://bower.io/)
168 | bower_components
169 |
170 | # node-waf configuration
171 | .lock-wscript
172 |
173 | # Compiled binary addons (https://nodejs.org/api/addons.html)
174 | build/Release
175 |
176 | # Dependency directories
177 | node_modules/
178 | jspm_packages/
179 |
180 | # Typescript v1 declaration files
181 | typings/
182 |
183 | # Optional npm cache directory
184 | .npm
185 |
186 | # Optional eslint cache
187 | .eslintcache
188 |
189 | # Optional REPL history
190 | .node_repl_history
191 |
192 | # Output of 'npm pack'
193 | *.tgz
194 |
195 | # Yarn Integrity file
196 | .yarn-integrity
197 |
198 | # dotenv environment variables file
199 | .env
200 |
201 | # next.js build output
202 | .next
203 |
204 |
205 | ### https://raw.github.com/github/gitignore/22a5eb3da72c0a224a179033e92722bdbf5a05ad/Global/VisualStudioCode.gitignore
206 |
207 | .vscode/*
208 | !.vscode/settings.json
209 | !.vscode/tasks.json
210 | !.vscode/launch.json
211 | !.vscode/extensions.json
212 |
213 | # Private
214 | .idea
215 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-typescript-firebase-auth
2 |
3 | create-react-app with TypeScript and Firebase authentication
4 |
--------------------------------------------------------------------------------
/images.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.svg'
2 | declare module '*.png'
3 | declare module '*.jpg'
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-typescript-firebase-auth",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "firebase": "^5.1.0",
7 | "react": "^16.4.1",
8 | "react-dom": "^16.4.1",
9 | "react-router-dom": "^4.3.1",
10 | "react-scripts-ts": "2.16.0"
11 | },
12 | "scripts": {
13 | "start": "react-scripts-ts start",
14 | "build": "react-scripts-ts build",
15 | "test": "react-scripts-ts test --env=jsdom",
16 | "eject": "react-scripts-ts eject",
17 | "tslint": "tslint --project tsconfig.json",
18 | "tslint:fix": "tslint --fix --project tsconfig.json"
19 | },
20 | "devDependencies": {
21 | "@types/jest": "^23.1.1",
22 | "@types/node": "^10.3.5",
23 | "@types/react": "^16.4.1",
24 | "@types/react-dom": "^16.0.6",
25 | "@types/react-router-dom": "^4.2.7",
26 | "typescript": "^2.9.2"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/morizyun/react-typescript-firebase-auth/08ee9470e5d79d32fa453482d0f8b57da5ff00dc/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | React App
23 |
24 |
25 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/App.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { BrowserRouter, Route, Switch } from "react-router-dom";
3 | import * as routes from "../constants/routes";
4 | import { firebase } from "../firebase";
5 | import { withAuthentication } from "../firebase/withAuthentication";
6 | import { Account } from "../pages/Account";
7 | import { Home } from "../pages/Home";
8 | import { Landing } from "../pages/Landing";
9 | import { PasswordForget } from "../pages/PasswordForget";
10 | import { SignIn } from "../pages/SignIn";
11 | import { SignUp } from "../pages/SignUp";
12 | import { Navigation } from "./Navigation";
13 |
14 | class AppComponent extends React.Component {
15 | constructor(props: any) {
16 | super(props);
17 |
18 | this.state = {
19 | authUser: null
20 | };
21 | }
22 |
23 | public componentDidMount() {
24 | firebase.auth.onAuthStateChanged(authUser => {
25 | authUser
26 | ? this.setState(() => ({ authUser }))
27 | : this.setState(() => ({ authUser: null }));
28 | });
29 | }
30 |
31 | public render() {
32 | return (
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
46 |
47 |
48 |
49 |
50 |
51 | );
52 | }
53 | }
54 |
55 | export const App = withAuthentication(AppComponent);
56 |
--------------------------------------------------------------------------------
/src/components/Navigation.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Link } from "react-router-dom";
3 | import * as routes from "../constants/routes";
4 | import { AuthUserContext } from "../firebase/AuthUserContext";
5 | import { SignOutButton } from "./SignOutButton";
6 |
7 | export const Navigation = () => (
8 |
9 | {authUser => (authUser ? : )}
10 |
11 | );
12 |
13 | const NavigationAuth = () => (
14 |
15 | -
16 | Landing
17 |
18 | -
19 | Home
20 |
21 | -
22 | Account
23 |
24 | -
25 |
26 |
27 |
28 | );
29 |
30 | const NavigationNonAuth = () => (
31 |
32 | -
33 | Landing
34 |
35 | -
36 | Sign In
37 |
38 |
39 | );
40 |
--------------------------------------------------------------------------------
/src/components/SignOutButton.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { auth } from "../firebase";
3 |
4 | export const SignOutButton = () => (
5 |
8 | );
9 |
--------------------------------------------------------------------------------
/src/constants/routes.ts:
--------------------------------------------------------------------------------
1 | export const SIGN_UP = "/signup";
2 | export const SIGN_IN = "/signin";
3 | export const LANDING = "/";
4 | export const HOME = "/home";
5 | export const ACCOUNT = "/account";
6 | export const PASSWORD_FORGET = "/password_forget";
7 |
--------------------------------------------------------------------------------
/src/firebase/AuthUserContext.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | export const AuthUserContext = React.createContext(null);
4 |
--------------------------------------------------------------------------------
/src/firebase/auth.ts:
--------------------------------------------------------------------------------
1 | import { auth } from "./firebase";
2 |
3 | // Sign Up
4 | export const doCreateUserWithEmailAndPassword = (
5 | email: string,
6 | password: string
7 | ) => auth.createUserWithEmailAndPassword(email, password);
8 |
9 | // Sign In
10 | export const doSignInWithEmailAndPassword = (email: string, password: string) =>
11 | auth.signInWithEmailAndPassword(email, password);
12 |
13 | // Sign out
14 | export const doSignOut = () => auth.signOut();
15 |
16 | // Password Reset
17 | export const doPasswordReset = (email: string) =>
18 | auth.sendPasswordResetEmail(email);
19 |
20 | // Password Change
21 | export const doPasswordUpdate = async (password: string) => {
22 | if (auth.currentUser) {
23 | await auth.currentUser.updatePassword(password);
24 | }
25 | throw Error("No auth.currentUser!");
26 | };
27 |
--------------------------------------------------------------------------------
/src/firebase/db.ts:
--------------------------------------------------------------------------------
1 | import { db } from "./firebase";
2 |
3 | // User API
4 | export const doCreateUser = (id: string, username: string, email: string) =>
5 | db.ref(`users/${id}`).set({
6 | email,
7 | username
8 | });
9 |
10 | export const onceGetUsers = () => db.ref("users").once("value");
11 |
--------------------------------------------------------------------------------
/src/firebase/firebase.ts:
--------------------------------------------------------------------------------
1 | import * as firebase from "firebase/app";
2 | import "firebase/auth";
3 | import "firebase/database";
4 |
5 | const config = {
6 | apiKey: "YOUR_API_KEY",
7 | authDomain: "YOUR_AUTH_DOMAIN",
8 | databaseURL: "YOUR_DATABASE_URL",
9 | messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
10 | projectId: "YOUR PROJECT_ID",
11 | storageBucket: "YOUR_STORAGE_BUCKET"
12 | };
13 |
14 | if (!firebase.apps.length) {
15 | firebase.initializeApp(config);
16 | }
17 |
18 | export const auth = firebase.auth();
19 | export const db = firebase.database();
20 |
--------------------------------------------------------------------------------
/src/firebase/index.ts:
--------------------------------------------------------------------------------
1 | import * as auth from "./auth";
2 | import * as db from "./db";
3 | import * as firebase from "./firebase";
4 |
5 | export { auth, db, firebase };
6 |
--------------------------------------------------------------------------------
/src/firebase/withAuthentication.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { firebase } from "../firebase";
3 | import { AuthUserContext } from "./AuthUserContext";
4 |
5 | interface InterfaceProps {
6 | authUser?: any;
7 | }
8 |
9 | interface InterfaceState {
10 | authUser?: any;
11 | }
12 |
13 | export const withAuthentication = (Component: any) => {
14 | class WithAuthentication extends React.Component<
15 | InterfaceProps,
16 | InterfaceState
17 | > {
18 | constructor(props: any) {
19 | super(props);
20 |
21 | this.state = {
22 | authUser: null
23 | };
24 | }
25 |
26 | public componentDidMount() {
27 | firebase.auth.onAuthStateChanged(authUser => {
28 | authUser
29 | ? this.setState(() => ({ authUser }))
30 | : this.setState(() => ({ authUser: null }));
31 | });
32 | }
33 |
34 | public render() {
35 | const { authUser } = this.state;
36 |
37 | return (
38 |
39 |
40 |
41 | );
42 | }
43 | }
44 | return WithAuthentication;
45 | };
46 |
--------------------------------------------------------------------------------
/src/firebase/withAuthorization.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { withRouter } from "react-router-dom";
3 | import * as routes from "../constants/routes";
4 | import { firebase } from "../firebase";
5 | import { AuthUserContext } from "./AuthUserContext";
6 |
7 | interface InterfaceProps {
8 | history?: any;
9 | }
10 |
11 | export const withAuthorization = (condition: any) => (Component: any) => {
12 | class WithAuthorization extends React.Component {
13 | public componentDidMount() {
14 | firebase.auth.onAuthStateChanged(authUser => {
15 | if (!condition(authUser)) {
16 | this.props.history.push(routes.SIGN_IN);
17 | }
18 | });
19 | }
20 |
21 | public render() {
22 | return (
23 |
24 | {authUser => (authUser ? : null)}
25 |
26 | );
27 | }
28 | }
29 |
30 | return withRouter(WithAuthorization as any);
31 | };
32 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as ReactDOM from "react-dom";
3 | import { App } from "./components/App";
4 | import "./index.css";
5 | import registerServiceWorker from "./registerServiceWorker";
6 |
7 | ReactDOM.render(, document.getElementById("root"));
8 | registerServiceWorker();
9 |
--------------------------------------------------------------------------------
/src/pages/Account/PasswordChangeForm.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { auth } from "../../firebase";
3 |
4 | interface InterfaceProps {
5 | error?: any;
6 | history?: any;
7 | passwordOne?: string;
8 | passwordTwo?: string;
9 | }
10 |
11 | interface InterfaceState {
12 | error?: any;
13 | passwordOne?: string;
14 | passwordTwo?: string;
15 | }
16 |
17 | export class PasswordChangeForm extends React.Component<
18 | InterfaceProps,
19 | InterfaceState
20 | > {
21 | private static INITIAL_STATE = {
22 | error: null,
23 | passwordOne: "",
24 | passwordTwo: ""
25 | };
26 |
27 | private static propKey(propertyName: string, value: string): object {
28 | return { [propertyName]: value };
29 | }
30 |
31 | constructor(props: any) {
32 | super(props);
33 | this.state = { ...PasswordChangeForm.INITIAL_STATE };
34 | }
35 |
36 | public onSubmit = (event: any) => {
37 | const { passwordOne }: any = this.state;
38 |
39 | auth
40 | .doPasswordUpdate(passwordOne)
41 | .then(() => {
42 | this.setState(() => ({ ...PasswordChangeForm.INITIAL_STATE }));
43 | })
44 | .catch(error => {
45 | this.setState(PasswordChangeForm.propKey("error", error));
46 | });
47 |
48 | event.preventDefault();
49 | };
50 |
51 | public render() {
52 | const { passwordOne, passwordTwo, error }: any = this.state;
53 |
54 | const isInvalid = passwordOne !== passwordTwo || passwordOne === "";
55 |
56 | return (
57 |
76 | );
77 | }
78 |
79 | private setStateWithEvent(event: any, columnType: string): void {
80 | this.setState(
81 | PasswordChangeForm.propKey(columnType, (event.target as any).value)
82 | );
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/pages/Account/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { AuthUserContext } from "../../firebase/AuthUserContext";
3 | import { withAuthorization } from "../../firebase/withAuthorization";
4 | import { PasswordForgetForm } from "../PasswordForget/PasswordForgetForm";
5 | import { PasswordChangeForm } from "./PasswordChangeForm";
6 |
7 | export const AccountComponent = () => (
8 |
9 | {authUser => (
10 |
11 |
Account: {(authUser as any).email}
12 |
13 |
14 |
15 | )}
16 |
17 | );
18 |
19 | const authCondition = (authUser: any) => !!authUser;
20 |
21 | export const Account = withAuthorization(authCondition)(AccountComponent);
22 |
--------------------------------------------------------------------------------
/src/pages/Home/UserList.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | interface InterfaceProps {
4 | users?: any;
5 | }
6 |
7 | export class UserList extends React.Component {
8 | constructor(props: any) {
9 | super(props);
10 | }
11 |
12 | public render() {
13 | const { users }: any = this.props;
14 |
15 | return (
16 |
17 |
List of User name
18 |
(Saved on Sign Up in Firebase Database)
19 |
20 |
21 | {Object.keys(users).map(key => {
22 | return - {users[key].username}
;
23 | })}
24 |
25 |
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/pages/Home/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { db } from "../../firebase";
3 | import { withAuthorization } from "../../firebase/withAuthorization";
4 | import { UserList } from "./UserList";
5 |
6 | class HomeComponent extends React.Component {
7 | constructor(props: any) {
8 | super(props);
9 |
10 | this.state = {
11 | users: null
12 | };
13 | }
14 |
15 | public componentDidMount() {
16 | db.onceGetUsers().then(snapshot =>
17 | this.setState(() => ({ users: snapshot.val() }))
18 | );
19 | }
20 |
21 | public render() {
22 | const { users }: any = this.state;
23 |
24 | return (
25 |
26 |
Home Page
27 |
The Home Page is accessible by every signed in user.
28 |
29 | {!!users &&
}
30 |
31 | );
32 | }
33 | }
34 |
35 | const authCondition = (authUser: any) => !!authUser;
36 |
37 | export const Home = withAuthorization(authCondition)(HomeComponent);
38 |
--------------------------------------------------------------------------------
/src/pages/Landing/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | export const Landing = () => {
4 | return (
5 |
6 |
Landing Page
7 |
8 | );
9 | };
10 |
--------------------------------------------------------------------------------
/src/pages/PasswordForget/PasswordForgetForm.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { auth } from "../../firebase";
3 |
4 | export class PasswordForgetForm extends React.Component {
5 | private static INITIAL_STATE = {
6 | email: "",
7 | error: null
8 | };
9 |
10 | private static propKey(propertyName: string, value: string) {
11 | return { [propertyName]: value };
12 | }
13 |
14 | constructor(props: any) {
15 | super(props);
16 |
17 | this.state = { ...PasswordForgetForm.INITIAL_STATE };
18 | }
19 |
20 | public onSubmit = (event: any) => {
21 | const { email }: any = this.state;
22 |
23 | auth
24 | .doPasswordReset(email)
25 | .then(() => {
26 | this.setState(() => ({ ...PasswordForgetForm.INITIAL_STATE }));
27 | })
28 | .catch(error => {
29 | this.setState(PasswordForgetForm.propKey("error", error));
30 | });
31 |
32 | event.preventDefault();
33 | };
34 |
35 | public render() {
36 | const { email, error }: any = this.state;
37 | const isInvalid = email === "";
38 |
39 | return (
40 |
53 | );
54 | }
55 |
56 | private setStateWithEvent(event: any, columnType: string): void {
57 | this.setState(
58 | PasswordForgetForm.propKey(columnType, (event.target as any).value)
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/pages/PasswordForget/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Link } from "react-router-dom";
3 | import { PasswordForgetForm } from "./PasswordForgetForm";
4 |
5 | export const PasswordForget = () => (
6 |
7 |
PasswordForget
8 |
9 |
10 | );
11 |
12 | export const PasswordForgetLink = () => (
13 |
14 | Forgot Password?
15 |
16 | );
17 |
--------------------------------------------------------------------------------
/src/pages/SignIn/SignInForm.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as routes from "../../constants/routes";
3 | import { auth } from "../../firebase";
4 |
5 | interface InterfaceProps {
6 | email?: string;
7 | error?: any;
8 | history?: any;
9 | password?: string;
10 | }
11 |
12 | interface InterfaceState {
13 | email: string;
14 | error: any;
15 | password: string;
16 | }
17 |
18 | export class SignInForm extends React.Component<
19 | InterfaceProps,
20 | InterfaceState
21 | > {
22 | private static INITIAL_STATE = {
23 | email: "",
24 | error: null,
25 | password: ""
26 | };
27 |
28 | private static propKey(propertyName: string, value: any): object {
29 | return { [propertyName]: value };
30 | }
31 |
32 | constructor(props: InterfaceProps) {
33 | super(props);
34 |
35 | this.state = { ...SignInForm.INITIAL_STATE };
36 | }
37 |
38 | public onSubmit = (event: any) => {
39 | const { email, password } = this.state;
40 |
41 | const { history } = this.props;
42 |
43 | auth
44 | .doSignInWithEmailAndPassword(email, password)
45 | .then(() => {
46 | this.setState(() => ({ ...SignInForm.INITIAL_STATE }));
47 | history.push(routes.HOME);
48 | })
49 | .catch(error => {
50 | this.setState(SignInForm.propKey("error", error));
51 | });
52 |
53 | event.preventDefault();
54 | };
55 |
56 | public render() {
57 | const { email, password, error } = this.state;
58 |
59 | const isInvalid = password === "" || email === "";
60 |
61 | return (
62 |
81 | );
82 | }
83 |
84 | private setStateWithEvent(event: any, columnType: string): void {
85 | this.setState(SignInForm.propKey(columnType, (event.target as any).value));
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/pages/SignIn/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { withRouter } from "react-router-dom";
3 | import { PasswordForgetLink } from "../PasswordForget";
4 | import { SignUpLink } from "../SignUp";
5 | import { SignInForm } from "./SignInForm";
6 |
7 | const SignInComponent = ({ history }: { [key: string]: any }) => (
8 |
9 |
SignIn
10 |
11 |
12 |
13 |
14 | );
15 |
16 | export const SignIn = withRouter(SignInComponent);
17 |
--------------------------------------------------------------------------------
/src/pages/SignUp/SingUpForm.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as routes from "../../constants/routes";
3 | import { auth, db } from "../../firebase";
4 |
5 | interface InterfaceProps {
6 | email?: string;
7 | error?: any;
8 | history?: any;
9 | passwordOne?: string;
10 | passwordTwo?: string;
11 | username?: string;
12 | }
13 |
14 | interface InterfaceState {
15 | email: string;
16 | error: any;
17 | passwordOne: string;
18 | passwordTwo: string;
19 | username: string;
20 | }
21 |
22 | export class SignUpForm extends React.Component<
23 | InterfaceProps,
24 | InterfaceState
25 | > {
26 | private static INITIAL_STATE = {
27 | email: "",
28 | error: null,
29 | passwordOne: "",
30 | passwordTwo: "",
31 | username: ""
32 | };
33 |
34 | private static propKey(propertyName: string, value: any): object {
35 | return { [propertyName]: value };
36 | }
37 |
38 | constructor(props: InterfaceProps) {
39 | super(props);
40 | this.state = { ...SignUpForm.INITIAL_STATE };
41 | }
42 |
43 | public onSubmit(event: any) {
44 | event.preventDefault();
45 |
46 | const { email, passwordOne, username } = this.state;
47 | const { history } = this.props;
48 |
49 | auth
50 | .doCreateUserWithEmailAndPassword(email, passwordOne)
51 | .then((authUser: any) => {
52 |
53 | // Create a user in your own accessible Firebase Database too
54 | db.doCreateUser(authUser.user.uid, username, email)
55 | .then(() => {
56 |
57 | this.setState(() => ({ ...SignUpForm.INITIAL_STATE }));
58 | history.push(routes.HOME);
59 | })
60 | .catch(error => {
61 | this.setState(SignUpForm.propKey("error", error));
62 | });
63 | })
64 | .catch(error => {
65 | this.setState(SignUpForm.propKey("error", error));
66 | });
67 | }
68 |
69 | public render() {
70 | const { username, email, passwordOne, passwordTwo, error } = this.state;
71 |
72 | const isInvalid =
73 | passwordOne !== passwordTwo ||
74 | passwordOne === "" ||
75 | email === "" ||
76 | username === "";
77 |
78 | return (
79 |
110 | );
111 | }
112 |
113 | private setStateWithEvent(event: any, columnType: string) {
114 | this.setState(SignUpForm.propKey(columnType, (event.target as any).value));
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/pages/SignUp/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Link, withRouter } from "react-router-dom";
3 | import * as routes from "../../constants/routes";
4 | import { SignUpForm } from "./SingUpForm";
5 |
6 | const SignUpComponent = () => (
7 |
8 |
SignUp
9 |
10 |
11 | );
12 |
13 | export const SignUpLink = () => (
14 |
15 | Don't have an account? Sign Up
16 |
17 | );
18 |
19 | export const SignUp = withRouter(SignUpComponent);
20 |
--------------------------------------------------------------------------------
/src/registerServiceWorker.ts:
--------------------------------------------------------------------------------
1 | // tslint:disable:no-console
2 | // In production, we register a service worker to serve assets from local cache.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on the 'N+1' visit to a page, since previously
7 | // cached resources are updated in the background.
8 |
9 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
10 | // This link also includes instructions on opting out of this behavior.
11 |
12 | const isLocalhost = Boolean(
13 | window.location.hostname === "localhost" ||
14 | // [::1] is the IPv6 localhost address.
15 | window.location.hostname === "[::1]" ||
16 | // 127.0.0.1/8 is considered localhost for IPv4.
17 | window.location.hostname.match(
18 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
19 | )
20 | );
21 |
22 | export default function register() {
23 | if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
24 | // The URL constructor is available in all browsers that support SW.
25 | const publicUrl = new URL(
26 | process.env.PUBLIC_URL!,
27 | window.location.toString()
28 | );
29 | if (publicUrl.origin !== window.location.origin) {
30 | // Our service worker won't work if PUBLIC_URL is on a different origin
31 | // from what our page is served on. This might happen if a CDN is used to
32 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
33 | return;
34 | }
35 |
36 | window.addEventListener("load", () => {
37 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
38 |
39 | if (isLocalhost) {
40 | // This is running on localhost. Lets check if a service worker still exists or not.
41 | checkValidServiceWorker(swUrl);
42 |
43 | // Add some additional logging to localhost, pointing developers to the
44 | // service worker/PWA documentation.
45 | navigator.serviceWorker.ready.then(() => {
46 | console.log(
47 | "This web app is being served cache-first by a service " +
48 | "worker. To learn more, visit https://goo.gl/SC7cgQ"
49 | );
50 | });
51 | } else {
52 | // Is not local host. Just register service worker
53 | registerValidSW(swUrl);
54 | }
55 | });
56 | }
57 | }
58 |
59 | function registerValidSW(swUrl: string) {
60 | navigator.serviceWorker
61 | .register(swUrl)
62 | .then(registration => {
63 | registration.onupdatefound = () => {
64 | const installingWorker = registration.installing;
65 | if (installingWorker) {
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === "installed") {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the old content will have been purged and
70 | // the fresh content will have been added to the cache.
71 | // It's the perfect time to display a 'New content is
72 | // available; please refresh.' message in your web app.
73 | console.log("New content is available; please refresh.");
74 | } else {
75 | // At this point, everything has been precached.
76 | // It's the perfect time to display a
77 | // 'Content is cached for offline use.' message.
78 | console.log("Content is cached for offline use.");
79 | }
80 | }
81 | };
82 | }
83 | };
84 | })
85 | .catch(error => {
86 | console.error("Error during service worker registration:", error);
87 | });
88 | }
89 |
90 | function checkValidServiceWorker(swUrl: string) {
91 | // Check if the service worker can be found. If it can't reload the page.
92 | fetch(swUrl)
93 | .then(response => {
94 | // Ensure service worker exists, and that we really are getting a JS file.
95 | if (
96 | response.status === 404 ||
97 | response.headers.get("content-type")!.indexOf("javascript") === -1
98 | ) {
99 | // No service worker found. Probably a different app. Reload the page.
100 | navigator.serviceWorker.ready.then(registration => {
101 | registration.unregister().then(() => {
102 | window.location.reload();
103 | });
104 | });
105 | } else {
106 | // Service worker found. Proceed as normal.
107 | registerValidSW(swUrl);
108 | }
109 | })
110 | .catch(() => {
111 | console.log(
112 | "No internet connection found. App is running in offline mode."
113 | );
114 | });
115 | }
116 |
117 | export function unregister() {
118 | if ("serviceWorker" in navigator) {
119 | navigator.serviceWorker.ready.then(registration => {
120 | registration.unregister();
121 | });
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "outDir": "build/dist",
5 | "module": "esnext",
6 | "target": "es5",
7 | "lib": ["es6", "dom"],
8 | "sourceMap": true,
9 | "allowJs": true,
10 | "jsx": "react",
11 | "moduleResolution": "node",
12 | "rootDir": "src",
13 | "forceConsistentCasingInFileNames": true,
14 | "noImplicitReturns": true,
15 | "noImplicitThis": true,
16 | "noImplicitAny": true,
17 | "strictNullChecks": true,
18 | "suppressImplicitAnyIndexErrors": true,
19 | "noUnusedLocals": true
20 | },
21 | "exclude": [
22 | "node_modules",
23 | "build",
24 | "scripts",
25 | "acceptance-tests",
26 | "webpack",
27 | "jest",
28 | "src/setupTests.ts"
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/tsconfig.prod.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json"
3 | }
--------------------------------------------------------------------------------
/tsconfig.test.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "commonjs"
5 | }
6 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["tslint:recommended", "tslint-react", "tslint-config-prettier"],
3 | "rules": {
4 | "jsx-no-lambda": false
5 | },
6 | "linterOptions": {
7 | "exclude": ["config/**/*.js", "node_modules/**/*.ts"]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------