diff --git a/todo-android-1/build.gradle b/todo-android-1/build.gradle
index 54dcf60cc..5c8b07d1c 100644
--- a/todo-android-1/build.gradle
+++ b/todo-android-1/build.gradle
@@ -8,7 +8,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.3.0'
+ classpath 'com.android.tools.build:gradle:3.3.2'
}
}
diff --git a/todo-web-1/package-lock.json b/todo-web-1/package-lock.json
index aec12d965..21d2a52b0 100644
--- a/todo-web-1/package-lock.json
+++ b/todo-web-1/package-lock.json
@@ -1013,6 +1013,29 @@
"warning": "^3.0.0"
}
},
+ "@react-bootstrap/react-popper": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@react-bootstrap/react-popper/-/react-popper-1.2.1.tgz",
+ "integrity": "sha512-4l3q7LcZEhrSkI4d3Ie3g4CdrXqqTexXX4PFT45CB0z5z2JUbaxgRwKNq7r5j2bLdVpZm+uvUGqxJw8d9vgbJQ==",
+ "requires": {
+ "babel-runtime": "6.x.x",
+ "create-react-context": "^0.2.1",
+ "popper.js": "^1.14.4",
+ "prop-types": "^15.6.1",
+ "typed-styles": "^0.0.5",
+ "warning": "^3.0.0"
+ }
+ },
+ "@restart/context": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz",
+ "integrity": "sha512-INJYZQJP7g+IoDUh/475NlGiTeMfwTXUEr3tmRneckHIxNolGOW9CTq83S8cxq0CgJwwcMzMJFchxvlwe7Rk8Q=="
+ },
+ "@restart/hooks": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.1.6.tgz",
+ "integrity": "sha512-eP1NykkdPlagH60YV+k2qGDCq3rkNtY6Sz0T2DVU1MQPa4pVRofIj+leFM2SLZLzy5nQFga4YnX7oM2d2LJRpA=="
+ },
"@rooks/use-timeout": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@rooks/use-timeout/-/use-timeout-1.2.0.tgz",
@@ -7383,6 +7406,19 @@
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
},
+ "history": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/history/-/history-4.9.0.tgz",
+ "integrity": "sha512-H2DkjCjXf0Op9OAr6nJ56fcRkTSNrUiv41vNJ6IswJjif6wlpZK0BTfFbi7qK9dXLSYZxkq5lBsj3vUjlYBYZA==",
+ "requires": {
+ "@babel/runtime": "^7.1.2",
+ "loose-envify": "^1.2.0",
+ "resolve-pathname": "^2.2.0",
+ "tiny-invariant": "^1.0.2",
+ "tiny-warning": "^1.0.0",
+ "value-equal": "^0.4.0"
+ }
+ },
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@@ -9189,6 +9225,11 @@
"safe-buffer": "^5.0.1"
}
},
+ "keycode": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
+ "integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ="
+ },
"killable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
@@ -13042,6 +13083,15 @@
"react-is": "^16.8.1"
}
},
+ "prop-types-extra": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.0.tgz",
+ "integrity": "sha512-QFyuDxvMipmIVKD2TwxLVPzMnO4e5oOf1vr3tJIomL8E7d0lr6phTHd5nkPhFIzTD1idBLLEPeylL9g+rrTzRg==",
+ "requires": {
+ "react-is": "^16.3.2",
+ "warning": "^3.0.0"
+ }
+ },
"property-information": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/property-information/-/property-information-5.0.1.tgz",
@@ -13258,6 +13308,43 @@
}
}
},
+ "react-bootstrap": {
+ "version": "1.0.0-beta.6",
+ "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.0.0-beta.6.tgz",
+ "integrity": "sha512-4RwXBCm45dHar1mMq7Wz8X9dI3b84R+Un696S+amgjePcxb6DqqqcxYla1FR7FJ+IIODr12HTjxu4LRVtIEsSQ==",
+ "requires": {
+ "@babel/runtime": "^7.3.4",
+ "@react-bootstrap/react-popper": "1.2.1",
+ "@restart/context": "^2.1.2",
+ "@restart/hooks": "^0.1.1",
+ "classnames": "^2.2.6",
+ "dom-helpers": "^3.4.0",
+ "invariant": "^2.2.3",
+ "keycode": "^2.1.2",
+ "popper.js": "^1.14.7",
+ "prop-types": "^15.7.2",
+ "prop-types-extra": "^1.1.0",
+ "react-overlays": "^1.2.0",
+ "react-transition-group": "^2.6.0",
+ "uncontrollable": "^6.1.0",
+ "warning": "^4.0.3"
+ },
+ "dependencies": {
+ "warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ }
+ }
+ },
+ "react-context-toolbox": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/react-context-toolbox/-/react-context-toolbox-2.0.2.tgz",
+ "integrity": "sha512-tY4j0imkYC3n5ZlYSgFkaw7fmlCp3IoQQ6DxpqeNHzcD0hf+6V+/HeJxviLUZ1Rv1Yn3N3xyO2EhkkZwHn0m1A=="
+ },
"react-dev-utils": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-8.0.0.tgz",
@@ -13394,6 +13481,11 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-5.1.4.tgz",
"integrity": "sha512-fp+U98OMZcnduQ+NSEiQa4s/XMsbp+5KlydmkbESOw4P69iWZ68ZMFM5a2BuE0FgqPBKApJyRuYHR95jM8lAmg=="
},
+ "react-facebook-login": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/react-facebook-login/-/react-facebook-login-4.1.1.tgz",
+ "integrity": "sha512-COnHEHlYGTKipz4963safFAK9PaNTcCiXfPXMS/yxo8El+/AJL5ye8kMJf23lKSSGGPgqFQuInskIHVqGqTvSw=="
+ },
"react-is": {
"version": "16.8.5",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.5.tgz",
@@ -13425,6 +13517,58 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
+ "react-overlays": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-1.2.0.tgz",
+ "integrity": "sha512-i/FCV8wR6aRaI+Kz/dpJhOdyx+ah2tN1RhT9InPrexyC4uzf3N4bNayFTGtUeQVacj57j1Mqh1CwV60/5153Iw==",
+ "requires": {
+ "classnames": "^2.2.6",
+ "dom-helpers": "^3.4.0",
+ "prop-types": "^15.6.2",
+ "prop-types-extra": "^1.1.0",
+ "react-context-toolbox": "^2.0.2",
+ "react-popper": "^1.3.2",
+ "uncontrollable": "^6.0.0",
+ "warning": "^4.0.2"
+ },
+ "dependencies": {
+ "create-react-context": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.2.tgz",
+ "integrity": "sha512-KkpaLARMhsTsgp0d2NA/R94F/eDLbhXERdIq3LvX2biCAXcDvHYoOqHfWCHf1+OLj+HKBotLG3KqaOOf+C1C+A==",
+ "requires": {
+ "fbjs": "^0.8.0",
+ "gud": "^1.0.0"
+ }
+ },
+ "react-popper": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.3.tgz",
+ "integrity": "sha512-ynMZBPkXONPc5K4P5yFWgZx5JGAUIP3pGGLNs58cfAPgK67olx7fmLp+AdpZ0+GoQ+ieFDa/z4cdV6u7sioH6w==",
+ "requires": {
+ "@babel/runtime": "^7.1.2",
+ "create-react-context": "<=0.2.2",
+ "popper.js": "^1.14.4",
+ "prop-types": "^15.6.1",
+ "typed-styles": "^0.0.7",
+ "warning": "^4.0.2"
+ }
+ },
+ "typed-styles": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz",
+ "integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q=="
+ },
+ "warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ }
+ }
+ },
"react-popper": {
"version": "0.10.4",
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-0.10.4.tgz",
@@ -13434,6 +13578,52 @@
"prop-types": "^15.6.1"
}
},
+ "react-router": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.0.0.tgz",
+ "integrity": "sha512-6EQDakGdLG/it2x9EaCt9ZpEEPxnd0OCLBHQ1AcITAAx7nCnyvnzf76jKWG1s2/oJ7SSviUgfWHofdYljFexsA==",
+ "requires": {
+ "@babel/runtime": "^7.1.2",
+ "create-react-context": "^0.2.2",
+ "history": "^4.9.0",
+ "hoist-non-react-statics": "^3.1.0",
+ "loose-envify": "^1.3.1",
+ "path-to-regexp": "^1.7.0",
+ "prop-types": "^15.6.2",
+ "react-is": "^16.6.0",
+ "tiny-invariant": "^1.0.2",
+ "tiny-warning": "^1.0.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+ },
+ "path-to-regexp": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
+ "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
+ "requires": {
+ "isarray": "0.0.1"
+ }
+ }
+ }
+ },
+ "react-router-dom": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.0.0.tgz",
+ "integrity": "sha512-wSpja5g9kh5dIteZT3tUoggjnsa+TPFHSMrpHXMpFsaHhQkm/JNVGh2jiF9Dkh4+duj4MKCkwO6H08u6inZYgQ==",
+ "requires": {
+ "@babel/runtime": "^7.1.2",
+ "history": "^4.9.0",
+ "loose-envify": "^1.3.1",
+ "prop-types": "^15.6.2",
+ "react-router": "5.0.0",
+ "tiny-invariant": "^1.0.2",
+ "tiny-warning": "^1.0.0"
+ }
+ },
"react-scripts": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-2.1.8.tgz",
@@ -14146,6 +14336,11 @@
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
},
+ "resolve-pathname": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz",
+ "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg=="
+ },
"resolve-url": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@@ -15609,6 +15804,16 @@
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
},
+ "tiny-invariant": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.4.tgz",
+ "integrity": "sha512-lMhRd/djQJ3MoaHEBrw8e2/uM4rs9YMNk0iOr8rHQ0QdbM7D4l0gFl3szKdeixrlyfm9Zqi4dxHCM2qVG8ND5g=="
+ },
+ "tiny-warning": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.2.tgz",
+ "integrity": "sha512-rru86D9CpQRLvsFG5XFdy0KdLAvjdQDyZCsRcuu60WtzFylDM3eAWSxEVz5kzL2Gp544XiUvPbVKtOA/txLi9Q=="
+ },
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@@ -15760,6 +15965,11 @@
"mime-types": "~2.1.18"
}
},
+ "typed-styles": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.5.tgz",
+ "integrity": "sha512-ht+rEe5UsdEBAa3gr64+QjUOqjOLJfWLvl5HZR5Ev9uo/OnD3p43wPeFSB1hNFc13GXQF/JU1Bn0YHLUqBRIlw=="
+ },
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
@@ -15792,6 +16002,14 @@
}
}
},
+ "uncontrollable": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-6.1.0.tgz",
+ "integrity": "sha512-2TzEm0pLKauMBZfAZXsgQvLpZHEp95891frCZdGDrSG7dWYaIQhedwLAzi0X8pR8KHNqlmuYEb2cEgbQzr050A==",
+ "requires": {
+ "invariant": "^2.2.4"
+ }
+ },
"unicode-canonical-property-names-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
@@ -16054,6 +16272,11 @@
"spdx-expression-parse": "^3.0.0"
}
},
+ "value-equal": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz",
+ "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw=="
+ },
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
diff --git a/todo-web-1/package.json b/todo-web-1/package.json
index 50b1e38c0..f5287d7c1 100755
--- a/todo-web-1/package.json
+++ b/todo-web-1/package.json
@@ -18,9 +18,12 @@
"leaflet.awesome-markers": "2.0.5",
"mongodb-stitch-browser-sdk": "4.3.2",
"react": "16.8.3",
+ "react-bootstrap": "^1.0.0-beta.6",
"react-dom": "16.8.3",
"react-error-boundary": "1.2.3",
+ "react-facebook-login": "^4.1.1",
"react-leaflet": "2.2.1",
+ "react-router-dom": "^5.0.0",
"react-scripts": "2.1.8",
"reactstrap": "7.1.0"
},
diff --git a/todo-web-1/public/index.html b/todo-web-1/public/index.html
index 456bddbdc..3a9650cf4 100755
--- a/todo-web-1/public/index.html
+++ b/todo-web-1/public/index.html
@@ -22,7 +22,7 @@
-->
-
React App
+ Stitch Todo App Tutorial
diff --git a/todo-web-1/public/manifest.json b/todo-web-1/public/manifest.json
new file mode 100644
index 000000000..0a74c22be
--- /dev/null
+++ b/todo-web-1/public/manifest.json
@@ -0,0 +1,15 @@
+{
+ "short_name": "Stitch Todo App Tutorial",
+ "name": "Stitch Todo App Tutorial",
+ "icons": [
+ {
+ "src": "favicon.ico",
+ "sizes": "64x64 32x32 24x24 16x16",
+ "type": "image/x-icon"
+ }
+ ],
+ "start_url": ".",
+ "display": "standalone",
+ "theme_color": "#000000",
+ "background_color": "#ffffff"
+ }
\ No newline at end of file
diff --git a/todo-web-1/src/components/App.js b/todo-web-1/src/components/App.js
index 45474222d..6f00c2bb1 100755
--- a/todo-web-1/src/components/App.js
+++ b/todo-web-1/src/components/App.js
@@ -1,9 +1,12 @@
-import React, { useState, useEffect } from "react";
+import React from "react";
import styled from "@emotion/styled";
import TodoList from "./TodoList";
import Banner from "./Banner";
import Navbar from "./Navbar";
-import PropTypes from 'prop-types';
+import PropTypes from "prop-types";
+import { BrowserRouter as Router, Route, Link } from "react-router-dom";
+import ConfirmUser from "./ConfirmUser";
+import ConfirmUserAPI from "./ConfirmUserAPI";
const AppLayout = styled.div`
display: grid;
@@ -18,16 +21,33 @@ const AppLayout = styled.div`
`;
App.propTypes = {
- children: PropTypes.node,
+ children: PropTypes.node
};
export default function App(props) {
+
return (
-
-
-
-
-
-
+
+
+
+
+
+
+
+ Home
+
+
+ Confirm
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/todo-web-1/src/components/ConfirmUser.js b/todo-web-1/src/components/ConfirmUser.js
new file mode 100644
index 000000000..0ea1807fc
--- /dev/null
+++ b/todo-web-1/src/components/ConfirmUser.js
@@ -0,0 +1,89 @@
+import React, { Component } from "react";
+import {
+ Button,
+ FormGroup,
+ FormControl,
+ FormLabel
+} from "react-bootstrap";
+import { Card, CardBody } from "reactstrap";
+import styled from "@emotion/styled";
+import confirmUser from "../stitch/authentication";
+
+const LoginCard = styled(Card)`
+ background-color: #383a3f !important;
+ background-color: #3e4348 !important;
+ background-color: #1f2124 !important;
+ background-color: #011627 !important;
+`;
+export default class Confirm extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ token: "",
+ tokenId: "",
+ message: ""
+ };
+ }
+
+ validateForm() {
+ return this.state.token.length > 0 && this.state.tokenId.length > 0;
+ }
+
+ handleChange = event => {
+ this.setState({
+ [event.target.id]: event.target.value
+ });
+ };
+
+ handleSubmit = event => {
+ event.preventDefault();
+ console.log(this.state.token);
+
+ confirmUser(this.state.token, this.state.tokenId)
+ .then(() => {
+ this.setState({message:"Successfully confirmed user's email"});
+ })
+ .catch(err => {
+ console.log("Error confirming new user:", err);
+ this.setState({message:err.message});
+ });
+ };
+
+ render() {
+ return (
+
+ );
+ }
+}
diff --git a/todo-web-1/src/components/ConfirmUserAPI.js b/todo-web-1/src/components/ConfirmUserAPI.js
new file mode 100644
index 000000000..e9e8c13cd
--- /dev/null
+++ b/todo-web-1/src/components/ConfirmUserAPI.js
@@ -0,0 +1,33 @@
+import React, { Component } from "react";
+import confirmUser from "../stitch/authentication";
+
+
+export default class ConfirmAPI extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ token: "",
+ tokenId: ""
+ };
+ }
+ componentDidMount() {
+ let location = window.location;
+ let params = new URLSearchParams(location.search);
+ let token = params.get("token");
+ let tokenId = params.get("tokenId");
+
+ confirmUser(token, tokenId)
+ .then(() => {
+ this.setState({ message: "Successfully confirmation user's email" });
+ })
+ .catch(err => {
+ console.log("Error confirming new user:", err);
+ this.setState({ message: err.message });
+ });
+ }
+
+ render() {
+ return
;
+ }
+}
diff --git a/todo-web-1/src/components/LoginAnon.js b/todo-web-1/src/components/LoginAnon.js
index a4b3e8ad3..83f1e9073 100644
--- a/todo-web-1/src/components/LoginAnon.js
+++ b/todo-web-1/src/components/LoginAnon.js
@@ -1,4 +1,4 @@
-import React, { useState } from "react";
+import React from "react";
import styled from "@emotion/styled";
import ErrorBoundary from "react-error-boundary";
import {
@@ -6,14 +6,12 @@ import {
CardBody,
Button,
Form,
- FormGroup,
- Label,
- Input,
- FormText,
} from "reactstrap";
import Banner from "./Banner";
import PropTypes from 'prop-types';
-
+import RegisterUser from "./RegisterUser";
+import Login from "./LoginUser";
+import LoginFacebook from "./LoginFacebook";
const LoginLayout = styled.div`
display: grid;
@@ -38,6 +36,7 @@ const LoginContent = styled.div`
grid-area: content;
position: absolute;
top: 150px;
+ left: 190px;
`;
@@ -47,6 +46,12 @@ export default function LoginAnon(props) {
+
+ or
+
+ or
+
+ or
diff --git a/todo-web-1/src/components/LoginFacebook.js b/todo-web-1/src/components/LoginFacebook.js
new file mode 100644
index 000000000..7d9c18baa
--- /dev/null
+++ b/todo-web-1/src/components/LoginFacebook.js
@@ -0,0 +1,34 @@
+import React, { Component } from "react";
+import { Button } from "react-bootstrap";
+import { app } from "./../stitch/stitch";
+import { FacebookRedirectCredential } from "mongodb-stitch-browser-sdk";
+export default class Login extends Component {
+ async componentDidMount() {
+ console.log("Component did mount");
+
+ if (app.auth.hasRedirectResult()) {
+ await app.auth.handleRedirectResult();
+ return;
+ }
+
+ if (!app.auth.isLoggedIn) {
+ const credential = new FacebookRedirectCredential();
+ await app.auth.loginWithRedirect(credential);
+ return;
+ }
+ }
+
+ handleSubmit = event => {};
+
+ render() {
+ return (
+
+
+
+ );
+ }
+}
diff --git a/todo-web-1/src/components/LoginUser.js b/todo-web-1/src/components/LoginUser.js
new file mode 100644
index 000000000..5450f4604
--- /dev/null
+++ b/todo-web-1/src/components/LoginUser.js
@@ -0,0 +1,73 @@
+import React, { Component } from "react";
+import { Button, FormGroup, FormControl, FormLabel } from "react-bootstrap";
+import { Card, CardBody } from "reactstrap";
+import styled from "@emotion/styled";
+import { loginEmailPasswordUser } from "../stitch/authentication";
+
+const LoginCard = styled(Card)`
+ background-color: #383a3f !important;
+ background-color: #3e4348 !important;
+ background-color: #1f2124 !important;
+ background-color: #011627 !important;
+`;
+export default class Login extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ email: "",
+ password: ""
+ };
+ }
+
+ validateForm() {
+ return this.state.email.length > 0 && this.state.password.length > 0;
+ }
+
+ handleChange = event => {
+ this.setState({
+ [event.target.id]: event.target.value
+ });
+ };
+
+ handleSubmit = event => {
+ event.preventDefault();
+ console.log(this.state.email);
+ loginEmailPasswordUser(this.state.email, this.state.password).then(() => {
+ window.location.reload();
+ });
+ };
+
+ render() {
+ return (
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/todo-web-1/src/components/RegisterUser.js b/todo-web-1/src/components/RegisterUser.js
new file mode 100644
index 000000000..36faf56c9
--- /dev/null
+++ b/todo-web-1/src/components/RegisterUser.js
@@ -0,0 +1,110 @@
+import React, { Component } from "react";
+import {
+ Button,
+ FormGroup,
+ FormControl,
+ FormLabel
+} from "react-bootstrap";
+import { Card, CardBody } from "reactstrap";
+import styled from "@emotion/styled";
+import { registerUser, resendConfirmationEmail } from "../stitch/authentication";
+
+const LoginCard = styled(Card)`
+ background-color: #383a3f !important;
+ background-color: #3e4348 !important;
+ background-color: #1f2124 !important;
+ background-color: #011627 !important;
+`;
+
+export default class RegisterUser extends Component {
+ constructor(props) {
+ super(props);
+
+ this.handleResendEmail = this.handleResendEmail.bind(this);
+
+ this.state = {
+ email: "",
+ password: "",
+ message: ""
+ };
+ }
+
+ validateForm() {
+ return this.state.email.length > 0 && this.state.password.length > 0;
+ }
+
+ validateEmail() {
+ return this.state.email.length > 0;
+ }
+
+ handleChange = event => {
+ this.setState({
+ [event.target.id]: event.target.value
+ });
+ };
+
+ handleSubmit = event => {
+ event.preventDefault();
+ console.log(this.state.email);
+
+ registerUser(this.state.email, this.state.password)
+ .then(() => {
+ this.setState({message:"Successfully sent account confirmation email"});
+ })
+ .catch(err => {
+ console.log("Error registering new user:", err.message);
+ this.setState({message:err.message});
+ });
+ };
+
+ handleResendEmail() {
+ resendConfirmationEmail(this.state.email)
+ .then(() => {
+ this.setState({message:"Successfully resent account confirmation email"});
+ })
+ .catch(err => {
+ console.log("Error resending account confirmation email:", err.message);
+ this.setState({message:err.message});
+ });
+}
+
+ render() {
+ return (
+
+ );
+ }
+}
diff --git a/todo-web-1/src/components/TodoItem.js b/todo-web-1/src/components/TodoItem.js
index b781a84f4..899045478 100644
--- a/todo-web-1/src/components/TodoItem.js
+++ b/todo-web-1/src/components/TodoItem.js
@@ -1,5 +1,5 @@
import React from "react";
-import {app, items} from "./../stitch";
+import {items} from "./../stitch";
import PropTypes from 'prop-types';
var TodoItem = class extends React.Component {
@@ -8,7 +8,7 @@ var TodoItem = class extends React.Component {
items
.updateOne(
{ _id: this.props.item._id },
- { $set: { checked: !this.props.item.checked } }
+ { $set: { checked: !this.props.item.checked }, $inc: { "__stitch_sync_version.v": 1 } }
)
.then(() => this.props.onChange());
}
diff --git a/todo-web-1/src/components/TodoList.js b/todo-web-1/src/components/TodoList.js
index 3fd748653..21a010814 100644
--- a/todo-web-1/src/components/TodoList.js
+++ b/todo-web-1/src/components/TodoList.js
@@ -1,102 +1,115 @@
import React from "react";
import TodoItem from "./TodoItem";
-import {app, items} from "./../stitch";
+import { app, items } from "./../stitch";
var TodoList = class extends React.Component {
-
- loadList() {
- console.log(app.auth.user);
- let obj = this;
- items.find({}, {limit: 1000}).asArray().then(docs => {
- obj.setState({ items: docs, requestPending: false });
+ loadList() {
+ console.log(app.auth.user);
+ let obj = this;
+ items
+ .find({}, { limit: 1000 })
+ .asArray()
+ .then(docs => {
+ obj.setState({ items: docs, requestPending: false });
});
- }
+ }
-constructor(props) {
- super(props);
+ constructor(props) {
+ super(props);
- this.state = {
+ this.state = {
items: []
- };
-}
-
+ };
+ }
-checkHandler(id, status) {
- items.updateOne({ _id: id }, { $set: { checked: status } }).then(() => {
- this.loadList();
- }, { rule: "checked" });
-}
+ // Update MongoDB
+ checkHandler(id, status) {
+ items
+ .updateOne(
+ { _id: id },
+ { $set: { checked: status } }
+ )
+ .then(
+ () => {
+ this.loadList();
+ },
+ { rule: "checked" }
+ );
+ }
-componentDidMount() {
- this.loadList();
-}
+ componentDidMount() {
+ this.loadList();
+ }
-addItem(event) {
- if (event.keyCode != 13) {
+ addItem(event) {
+ if (event.keyCode !== 13) {
return;
- }
- this.setState({ requestPending: true });
- items.insertOne({ text: event.target.value, owner_id: app.auth.user.id })
+ }
+ this.setState({ requestPending: true });
+ items
+ .insertOne({ text: event.target.value, owner_id: app.auth.user.id })
.then(() => {
- this._newitem.value = "";
- this.loadList();
+ this._newitem.value = "";
+ this.loadList();
});
-}
+ }
-clear() {
- this.setState({ requestPending: true });
- items.deleteMany({ checked: true }).then(() => {
+ clear() {
+ this.setState({ requestPending: true });
+ items.deleteMany({ checked: true }).then(() => {
this.loadList();
- });
-}
+ });
+ }
-setPending() {
- this.setState({ requestPending: true });
-}
+ setPending() {
+ this.setState({ requestPending: true });
+ }
-render() {
- let foo = (
+ render() {
+ let foo = (
-
-
+
{
- this._newitem = n;
+ this._newitem = n;
}}
onKeyDown={e => this.addItem(e)}
- />
- {this.state.items.filter(x => x.checked).length > 0
- ?
this.clear()}
+ />
+ {this.state.items.filter(x => x.checked).length > 0 ? (
+
this.clear()}
>
- delete selected item(s)
+ delete selected item(s)
- : null}
-
-
- {this.state.items.length == 0
- ? empty list.
- : this.state.items.map(item => {
- return (
-
+
+ {this.state.items.length === 0 ? (
+ empty list.
+ ) : (
+ this.state.items.map(item => {
+ return (
+ this.loadList()}
onStartChange={() => this.setPending()}
- />
- );
- })}
-
+ />
+ );
+ })
+ )}
+
- );
- return foo;
-}
+ );
+ return foo;
+ }
};
TodoList.displayName = "TodoList";
-export default TodoList;
\ No newline at end of file
+export default TodoList;
diff --git a/todo-web-1/src/index.js b/todo-web-1/src/index.js
index bea30d458..280123f93 100644
--- a/todo-web-1/src/index.js
+++ b/todo-web-1/src/index.js
@@ -1,10 +1,6 @@
import React from "react";
import ReactDOM from "react-dom";
-import {app,
- isLoggedIn,
- loginAnonymous,
- logoutUser,
-} from "./stitch";
+import { app, isLoggedIn, loginAnonymous, logoutUser } from "./stitch";
import LoginAnon from "./components/LoginAnon";
import App from "./components/App";
import "bootstrap/dist/css/bootstrap.min.css";
@@ -15,7 +11,9 @@ function MyApp(props) {
return isLoggedIn() ? (
logoutUser(app.currentUser)} />
) : (
-
+
+
+
);
}
diff --git a/todo-web-1/src/stitch/authentication.js b/todo-web-1/src/stitch/authentication.js
index 030e16e48..2e6feca01 100644
--- a/todo-web-1/src/stitch/authentication.js
+++ b/todo-web-1/src/stitch/authentication.js
@@ -1,11 +1,13 @@
-import { useState, useEffect } from "react";
-import { UserPasswordCredential, AnonymousCredential } from "mongodb-stitch-browser-sdk";
-import {app} from "./stitch.js";
+import {
+ UserPasswordCredential,
+ AnonymousCredential
+} from "mongodb-stitch-browser-sdk";
+import { app, emailPassClient } from "./stitch.js";
// Log in a user with the specified email and password
// Note: The user must already be registered with the Stitch app.
// See https://docs.mongodb.com/stitch/authentication/userpass/#create-a-new-user-account
-export function loginEmailPasswordUser({ email, password }) {
+export function loginEmailPasswordUser(email, password) {
return app.auth
.loginWithCredential(new UserPasswordCredential(email, password))
.then(stitchUser => {
@@ -14,7 +16,25 @@ export function loginEmailPasswordUser({ email, password }) {
});
}
-// Log in a user anonymously.
+// Register a user with the specified email and password
+export function registerUser(email, password) {
+ return emailPassClient.registerWithEmail(email, password).then(() => {
+ console.log("Successfully sent account confirmation email");
+ });
+}
+
+// Confirm the user's email/password account
+export default function confirmUser(token, tokenId) {
+ return emailPassClient.confirmUser(token, tokenId);
+}
+
+export function resendConfirmationEmail(email) {
+ return emailPassClient.resendConfirmationEmail(email).then(() => {
+ console.log("Successfully sent confirmation email");
+ })
+}
+
+// Log in a user anonymously.
// Note: When the user logs out, all data is lost.
// See https://docs.mongodb.com/stitch/authentication/anonymous/
export function loginAnonymous() {
@@ -42,6 +62,5 @@ export function logoutUser(stitchUser) {
}
export function isLoggedIn() {
- return app.auth.isLoggedIn;
+ return app.auth.isLoggedIn;
}
-