├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── docs
├── README.md
├── assets
│ └── img
│ │ ├── authenticator.png
│ │ ├── daily_journal.png
│ │ ├── download_aws_exports.png
│ │ ├── host_and_streaming.png
│ │ ├── journal_history.png
│ │ ├── live.png
│ │ ├── login_form.png
│ │ └── welcome.png
├── blog
│ ├── 2018
│ │ └── 01
│ │ │ ├── 10
│ │ │ └── staging-step.html
│ │ │ └── 08
│ │ │ └── why-dochameleon.html
│ ├── atom.xml
│ ├── feed.xml
│ ├── index.html
│ └── page1
│ │ └── index.html
├── docs
│ ├── assets
│ │ └── img
│ │ │ ├── authenticator.png
│ │ │ ├── daily_journal.png
│ │ │ ├── download_aws_exports.png
│ │ │ ├── host_and_streaming.png
│ │ │ ├── journal_history.png
│ │ │ ├── live.png
│ │ │ ├── login_form.png
│ │ │ └── welcome.png
│ ├── step-01-cbra.html
│ ├── step-01-home-n-login.html
│ ├── step-01-navigator.html
│ ├── step-01-react-router.html
│ ├── step-01-routing.html
│ ├── step-01-run-app.html
│ ├── step-01-semantic-ui-react.html
│ ├── step-02-authenticator.html
│ ├── step-02-aware-of-authstate.html
│ ├── step-02-configure.html
│ ├── step-02-greetings.html
│ ├── step-02-prepare.html
│ ├── step-02-run-app.html
│ ├── step-03-check-contact.html
│ ├── step-03-federated.html
│ ├── step-03-login-form.html
│ ├── step-03-replace-all.html
│ ├── step-03-replace-sign-in.html
│ ├── step-03-run-app.html
│ ├── step-03-sign-up.html
│ ├── step-04-daily-album.html
│ ├── step-04-refresh-album.html
│ ├── step-04-run-app.html
│ ├── step-04-user-info.html
│ ├── step-04-write-text.html
│ ├── step-05-display-dates.html
│ ├── step-05-get-list.html
│ ├── step-05-run-app.html
│ ├── step-05-switch-date.html
│ ├── step-06-build.html
│ ├── step-06-publish.html
│ └── step-06-touch-ups.html
├── img
│ ├── algolia.svg
│ ├── dochameleon.png
│ ├── f-r.png
│ ├── favicon.ico
│ ├── favicon.png
│ ├── fsts.png
│ ├── github.png
│ ├── language.svg
│ ├── markdown.png
│ ├── octocat.jpg
│ ├── progressive.png
│ ├── react.svg
│ ├── setup.png
│ └── translation.svg
├── index.html
├── languages.html
├── readme.html
└── sitemap.xml
├── step-01
├── README.md
├── bootstrap-starter.png
├── journal
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── components
│ │ │ ├── Main.jsx
│ │ │ ├── Navigator.jsx
│ │ │ └── index.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ ├── pages
│ │ │ ├── Home.jsx
│ │ │ ├── Login.jsx
│ │ │ └── index.js
│ │ └── registerServiceWorker.js
│ └── template.md
└── starter.png
├── step-02
├── README.md
├── authenticator.png
├── journal
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ └── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── components
│ │ ├── Main.jsx
│ │ ├── Navigator.jsx
│ │ └── index.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ ├── pages
│ │ ├── Home.jsx
│ │ ├── Login.jsx
│ │ └── index.js
│ │ └── registerServiceWorker.js
└── welcome.png
├── step-03
├── README.md
├── authentication.png
├── journal
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ └── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── components
│ │ ├── Main.jsx
│ │ ├── Navigator.jsx
│ │ ├── auth
│ │ │ ├── JConfirmSignIn.jsx
│ │ │ ├── JConfirmSignUp.jsx
│ │ │ ├── JForgotPassword.jsx
│ │ │ ├── JForgotPasswordReset.jsx
│ │ │ ├── JSignIn.jsx
│ │ │ ├── JSignOut.jsx
│ │ │ ├── JSignUp.jsx
│ │ │ └── index.js
│ │ └── index.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ ├── pages
│ │ ├── Home.jsx
│ │ ├── Login.jsx
│ │ └── index.js
│ │ └── registerServiceWorker.js
├── sign-in.png
└── sign-out.png
├── step-04
├── README.md
├── journal
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ └── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── components
│ │ ├── Main.jsx
│ │ ├── Navigator.jsx
│ │ ├── auth
│ │ │ ├── JConfirmSignIn.jsx
│ │ │ ├── JConfirmSignUp.jsx
│ │ │ ├── JForgotPassword.jsx
│ │ │ ├── JForgotPasswordReset.jsx
│ │ │ ├── JSignIn.jsx
│ │ │ ├── JSignOut.jsx
│ │ │ ├── JSignUp.jsx
│ │ │ └── index.js
│ │ └── index.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ ├── pages
│ │ ├── Home.jsx
│ │ ├── Login.jsx
│ │ ├── Profile.jsx
│ │ └── index.js
│ │ └── registerServiceWorker.js
├── profile-edit.png
└── profile.png
├── step-05
├── README.md
├── journal
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ └── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── components
│ │ ├── Main.jsx
│ │ ├── Navigator.jsx
│ │ ├── auth
│ │ │ ├── JConfirmSignIn.jsx
│ │ │ ├── JConfirmSignUp.jsx
│ │ │ ├── JForgotPassword.jsx
│ │ │ ├── JForgotPasswordReset.jsx
│ │ │ ├── JSignIn.jsx
│ │ │ ├── JSignOut.jsx
│ │ │ ├── JSignUp.jsx
│ │ │ └── index.js
│ │ └── index.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ ├── pages
│ │ ├── Home.jsx
│ │ ├── Login.jsx
│ │ ├── Profile.jsx
│ │ └── index.js
│ │ ├── registerServiceWorker.js
│ │ └── store
│ │ ├── AmplifyBridge.js
│ │ ├── actions.js
│ │ ├── index.js
│ │ └── reducers.js
└── redux.png
├── step-06
├── README.md
├── album-render-key.png
├── album_with_picker.png
├── everyday-journal.png
├── journal
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ └── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── components
│ │ ├── Main.jsx
│ │ ├── Navigator.jsx
│ │ ├── Unauthorized.jsx
│ │ ├── Unexpected.jsx
│ │ ├── album
│ │ │ ├── Album.jsx
│ │ │ ├── AlbumItem.jsx
│ │ │ ├── FilePicker.jsx
│ │ │ ├── NoteEditor.jsx
│ │ │ └── index.js
│ │ ├── auth
│ │ │ ├── JConfirmSignIn.jsx
│ │ │ ├── JConfirmSignUp.jsx
│ │ │ ├── JForgotPassword.jsx
│ │ │ ├── JForgotPasswordReset.jsx
│ │ │ ├── JSignIn.jsx
│ │ │ ├── JSignOut.jsx
│ │ │ ├── JSignUp.jsx
│ │ │ └── index.js
│ │ └── index.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ ├── pages
│ │ ├── Home.jsx
│ │ ├── Login.jsx
│ │ ├── Profile.jsx
│ │ └── index.js
│ │ ├── registerServiceWorker.js
│ │ └── store
│ │ ├── AmplifyBridge.js
│ │ ├── actions.js
│ │ ├── index.js
│ │ └── reducers.js
├── note-editor.png
└── s3album.png
├── step-07
├── README.md
├── journal-by-day.png
└── journal
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
│ └── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── components
│ ├── Main.jsx
│ ├── Navigator.jsx
│ ├── Unauthorized.jsx
│ ├── Unexpected.jsx
│ ├── WhichDay.jsx
│ ├── album
│ │ ├── Album.jsx
│ │ ├── AlbumItem.jsx
│ │ ├── FilePicker.jsx
│ │ ├── NoteEditor.jsx
│ │ └── index.js
│ ├── auth
│ │ ├── JConfirmSignIn.jsx
│ │ ├── JConfirmSignUp.jsx
│ │ ├── JForgotPassword.jsx
│ │ ├── JForgotPasswordReset.jsx
│ │ ├── JSignIn.jsx
│ │ ├── JSignOut.jsx
│ │ ├── JSignUp.jsx
│ │ └── index.js
│ └── index.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── pages
│ ├── Home.jsx
│ ├── Login.jsx
│ ├── Profile.jsx
│ └── index.js
│ ├── registerServiceWorker.js
│ └── store
│ ├── AmplifyBridge.js
│ ├── actions.js
│ ├── index.js
│ └── reducers.js
├── step-08
├── README.md
├── journal
│ ├── .gitignore
│ ├── README.md
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ └── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── components
│ │ ├── Main.jsx
│ │ ├── Navigator.jsx
│ │ ├── Unauthorized.jsx
│ │ ├── Unexpected.jsx
│ │ ├── WhichDay.jsx
│ │ ├── album
│ │ │ ├── Album.jsx
│ │ │ ├── AlbumItem.jsx
│ │ │ ├── FilePicker.jsx
│ │ │ ├── NoteEditor.jsx
│ │ │ └── index.js
│ │ ├── auth
│ │ │ ├── JConfirmSignIn.jsx
│ │ │ ├── JConfirmSignUp.jsx
│ │ │ ├── JForgotPassword.jsx
│ │ │ ├── JForgotPasswordReset.jsx
│ │ │ ├── JSignIn.jsx
│ │ │ ├── JSignOut.jsx
│ │ │ ├── JSignUp.jsx
│ │ │ └── index.js
│ │ └── index.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ ├── pages
│ │ ├── Home.jsx
│ │ ├── Login.jsx
│ │ ├── Profile.jsx
│ │ └── index.js
│ │ ├── registerServiceWorker.js
│ │ └── store
│ │ ├── AmplifyBridge.js
│ │ ├── actions.js
│ │ ├── index.js
│ │ └── reducers.js
└── seattle.png
└── website
├── .gitignore
├── blog
├── 2018-01-08-why-dochameleon.md
└── 2018-01-10-staging-step.md
├── components
├── docs
│ └── DocsLayout.js
└── headerLinks.json
├── docs
├── assets
│ └── img
│ │ ├── authenticator.png
│ │ ├── daily_journal.png
│ │ ├── download_aws_exports.png
│ │ ├── host_and_streaming.png
│ │ ├── journal_history.png
│ │ ├── live.png
│ │ ├── login_form.png
│ │ └── welcome.png
├── sidebars.json
├── step-01-cbra.md
├── step-01-home-n-login.md
├── step-01-navigator.md
├── step-01-react-router.md
├── step-01-routing.md
├── step-01-run-app.md
├── step-01-semantic-ui-react.md
├── step-02-authenticator.md
├── step-02-aware-of-authstate.md
├── step-02-configure.md
├── step-02-greetings.md
├── step-02-prepare.md
├── step-02-run-app.md
├── step-03-check-contact.md
├── step-03-federated.md
├── step-03-login-form.md
├── step-03-replace-all.md
├── step-03-replace-sign-in.md
├── step-03-run-app.md
├── step-03-sign-up.md
├── step-04-daily-album.md
├── step-04-refresh-album.md
├── step-04-run-app.md
├── step-04-user-info.md
├── step-04-write-text.md
├── step-05-display-dates.md
├── step-05-get-list.md
├── step-05-run-app.md
├── step-05-switch-date.md
├── step-06-build.md
├── step-06-publish.md
└── step-06-touch-ups.md
├── package-lock.json
├── package.json
├── pages
├── README.md
├── index.js
└── languages.js
├── siteConfig.js
├── static
└── img
│ ├── f-r.png
│ └── fsts.png
└── theme
├── custom.js
└── pages.js
/.gitignore:
--------------------------------------------------------------------------------
1 | **node_modules**
2 | **aws-exports.js
3 | **awsmobilejs**
4 |
5 | **.DS_Store
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Richard Zhang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://opensource.org/licenses/MIT)
2 | []()
3 | [](https://gitter.im/AWS-Amplify/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link)
4 | [](https://twitter.com/intent/tweet?text=AWS%20Amplify%20Tutorial%0A&url=https://github.com/richardzcode/Journal-AWS-Amplify-Tutorial&hashtags=react,bootstrap,aws)
5 |
6 | # Journal
7 | Step by step tutorial to build a personal journal web app with ReactJS + AWS
8 |
9 | **Update:** This tutorial is rewritten with [AWS Amplify 1.1](https://aws-amplify.github.io/) and [Bootstrap 4.1](https://bootstrap-4-react.com/).
10 |
11 | * [Step 01 - Create a Basic React App with Bootstrap](step-01)
12 | * [Step 02 - Amplify Authentication](step-02)
13 | * [Step 03 - Customize Authentication UI](step-03)
14 | * [Step 04 - User Profile](step-04)
15 | * [Step 05 - State Management via Redux](step-05)
16 | * [Step 06 - Everyday Journal](step-06)
17 | * [Step 07 - List of Journals](step-07)
18 | * [Step 08 - Go Live](step-08)
19 |
20 | Go to `journal` sub-folder of each step to check full source code, and run app.
21 |
22 |
23 |
24 | [Live Demo](https://s3-us-west-1.amazonaws.com/journal-hosting-mobilehub-142591078/index.html#/)
25 |
26 | **Note:** This is a personal experiment. Trying to see what would developers encounter when building a somewhat real app. This tutorial does not cover all aspects of AWS Amplify.
27 |
28 | For most complete and up-to-date documentation, always check [official AWS Amplify site](https://aws-amplify.github.io/). The [Getting Started](https://aws-amplify.github.io/amplify-js/media/quick_start) is very nicely written.
29 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | [](https://opensource.org/licenses/MIT)
2 | []()
3 |
4 | # Journal
5 | Step by step tutorial to build a personal journal web app with ReactJS + AWS
6 |
7 | **Update:** I am updating this tutorial with AWS Amplify 1.1 and Bootstrap 4.1. So far re-worked through step-05. Stay tuned.
8 |
9 | * [Step 01 - Create a Basic React App with Bootstrap](step-01)
10 | * [Step 02 - Authentication](step-02)
11 | * [Step 03 - Authentication UI](step-03)
12 | * [Step 04 - User Profile](step-04)
13 | * [Step 05 - State Management via Redux](step-05)
14 | * [Step 06 - Everyday Journal](step-06) (rewrite in progress ...)
15 | * [Step 07 - List of Journals](step-07) (rewrite in progress ...)
16 | * [Step 08 - Go Live](step-08) (rewrite in progress ...)
17 |
18 | Go to `journal` sub-folder of each step to check full source code, and run app.
19 |
20 |
21 |
22 | [Live Demo](http://journal-hosting-mobilehub-1908112296.s3-website-us-east-1.amazonaws.com/)
23 | [Documentation](https://richardzcode.github.io/Journal-AWS-Amplify-Tutorial/)
24 |
25 | **Note:** This is a personal experiment. Trying to see what would developers encounter when building a somewhat real app. This tutorial does not cover all aspects of AWS Amplify.
26 |
27 | For most complete and up-to-date documentation, always check [official AWS Amplify site](https://aws-amplify.github.io/). The [Getting Started](https://aws-amplify.github.io/amplify-js/media/quick_start) is very nicely written.
28 |
--------------------------------------------------------------------------------
/docs/assets/img/authenticator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/assets/img/authenticator.png
--------------------------------------------------------------------------------
/docs/assets/img/daily_journal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/assets/img/daily_journal.png
--------------------------------------------------------------------------------
/docs/assets/img/download_aws_exports.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/assets/img/download_aws_exports.png
--------------------------------------------------------------------------------
/docs/assets/img/host_and_streaming.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/assets/img/host_and_streaming.png
--------------------------------------------------------------------------------
/docs/assets/img/journal_history.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/assets/img/journal_history.png
--------------------------------------------------------------------------------
/docs/assets/img/live.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/assets/img/live.png
--------------------------------------------------------------------------------
/docs/assets/img/login_form.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/assets/img/login_form.png
--------------------------------------------------------------------------------
/docs/assets/img/welcome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/assets/img/welcome.png
--------------------------------------------------------------------------------
/docs/blog/atom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | https://richardzcode.github.io/Journal-AWS-Amplify-Tutorial/blog
4 | Journal Tutorial Blog
5 | 2018-01-10T14:00:00Z
6 | Feed for Node.js
7 |
8 | The best place to stay up-to-date with the latest Journal Tutorial news and events.
9 | Copyright © 2018 Richard Zhang
10 |
11 |
12 | https://richardzcode.github.io/Journal-AWS-Amplify-Tutorial/blog/2018/01/10/staging-step.html
13 |
14 |
15 | 2018-01-10T14:00:00Z
16 |
19 |
20 | Richard Zhang
21 |
22 |
23 |
24 |
25 | https://richardzcode.github.io/Journal-AWS-Amplify-Tutorial/blog/2018/01/08/why-dochameleon.html
26 |
27 |
28 | 2018-01-08T14:00:00Z
29 |
34 |
35 | Richard Zhang
36 |
37 |
38 |
--------------------------------------------------------------------------------
/docs/blog/feed.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Journal Tutorial Blog
5 | https://richardzcode.github.io/Journal-AWS-Amplify-Tutorial/blog
6 | The best place to stay up-to-date with the latest Journal Tutorial news and events.
7 | Wed, 10 Jan 2018 14:00:00 GMT
8 | http://blogs.law.harvard.edu/tech/rss
9 | Feed for Node.js
10 | Copyright © 2018 Richard Zhang
11 | -
12 |
13 | https://richardzcode.github.io/Journal-AWS-Amplify-Tutorial/blog/2018/01/10/staging-step.html
14 | https://richardzcode.github.io/Journal-AWS-Amplify-Tutorial/blog/2018/01/10/staging-step.html
15 | Wed, 10 Jan 2018 14:00:00 GMT
16 |
19 |
20 | -
21 |
22 | https://richardzcode.github.io/Journal-AWS-Amplify-Tutorial/blog/2018/01/08/why-dochameleon.html
23 | https://richardzcode.github.io/Journal-AWS-Amplify-Tutorial/blog/2018/01/08/why-dochameleon.html
24 | Mon, 08 Jan 2018 14:00:00 GMT
25 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/docs/docs/assets/img/authenticator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/docs/assets/img/authenticator.png
--------------------------------------------------------------------------------
/docs/docs/assets/img/daily_journal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/docs/assets/img/daily_journal.png
--------------------------------------------------------------------------------
/docs/docs/assets/img/download_aws_exports.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/docs/assets/img/download_aws_exports.png
--------------------------------------------------------------------------------
/docs/docs/assets/img/host_and_streaming.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/docs/assets/img/host_and_streaming.png
--------------------------------------------------------------------------------
/docs/docs/assets/img/journal_history.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/docs/assets/img/journal_history.png
--------------------------------------------------------------------------------
/docs/docs/assets/img/live.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/docs/assets/img/live.png
--------------------------------------------------------------------------------
/docs/docs/assets/img/login_form.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/docs/assets/img/login_form.png
--------------------------------------------------------------------------------
/docs/docs/assets/img/welcome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/docs/assets/img/welcome.png
--------------------------------------------------------------------------------
/docs/img/dochameleon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/img/dochameleon.png
--------------------------------------------------------------------------------
/docs/img/f-r.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/img/f-r.png
--------------------------------------------------------------------------------
/docs/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/img/favicon.ico
--------------------------------------------------------------------------------
/docs/img/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/img/favicon.png
--------------------------------------------------------------------------------
/docs/img/fsts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/img/fsts.png
--------------------------------------------------------------------------------
/docs/img/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/img/github.png
--------------------------------------------------------------------------------
/docs/img/language.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/docs/img/markdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/img/markdown.png
--------------------------------------------------------------------------------
/docs/img/octocat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/img/octocat.jpg
--------------------------------------------------------------------------------
/docs/img/progressive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/img/progressive.png
--------------------------------------------------------------------------------
/docs/img/react.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
22 |
--------------------------------------------------------------------------------
/docs/img/setup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/docs/img/setup.png
--------------------------------------------------------------------------------
/docs/img/translation.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/step-01/bootstrap-starter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-01/bootstrap-starter.png
--------------------------------------------------------------------------------
/step-01/journal/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/step-01/journal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "journal",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "bootstrap-4-react": "0.0.53",
7 | "react": "^16.5.2",
8 | "react-dom": "^16.5.2",
9 | "react-router-dom": "^4.3.1",
10 | "react-scripts": "1.1.5"
11 | },
12 | "scripts": {
13 | "start": "react-scripts start",
14 | "build": "react-scripts build",
15 | "test": "react-scripts test --env=jsdom",
16 | "eject": "react-scripts eject"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/step-01/journal/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-01/journal/public/favicon.ico
--------------------------------------------------------------------------------
/step-01/journal/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
23 | Bootstrap 4 React
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
35 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/step-01/journal/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Bootstrap 4 React",
3 | "name": "Bootstrap 4 React Documentation",
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 |
--------------------------------------------------------------------------------
/step-01/journal/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 5rem;
3 | }
4 | .starter-template {
5 | padding: 3rem 1.5rem;
6 | text-align: center;
7 | }
8 |
--------------------------------------------------------------------------------
/step-01/journal/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | import { Navigator, Main } from './components';
4 | import './App.css';
5 |
6 | class App extends Component {
7 | render() {
8 | return (
9 |
10 |
11 |
12 |
13 | );
14 | }
15 | }
16 |
17 | export default App;
18 |
--------------------------------------------------------------------------------
/step-01/journal/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/step-01/journal/src/components/Main.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Container } from 'bootstrap-4-react';
3 | import { HashRouter, Route, Switch } from 'react-router-dom';
4 |
5 | import { Home, Login } from '../pages';
6 |
7 | export default class Main extends Component {
8 | render() {
9 | return (
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | )
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/step-01/journal/src/components/Navigator.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Navbar, Nav, BSpan } from 'bootstrap-4-react';
3 | import { HashRouter, Route, Switch } from 'react-router-dom';
4 |
5 | const HomeItems = props => (
6 |
7 |
8 | Home
9 | (current}
10 |
11 |
12 | Login
13 |
14 |
15 | )
16 |
17 | const LoginItems = props => (
18 |
19 |
20 | Home
21 |
22 |
23 | Login
24 | (current}
25 |
26 |
27 | )
28 |
29 | export default class Navigator extends Component {
30 | render() {
31 | return (
32 |
33 | Journal
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | Greetings
46 |
47 |
48 | )
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/step-01/journal/src/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as Navigator } from './Navigator';
2 | export { default as Main } from './Main';
3 |
--------------------------------------------------------------------------------
/step-01/journal/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/step-01/journal/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(, document.getElementById('root'));
6 |
--------------------------------------------------------------------------------
/step-01/journal/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/step-01/journal/src/pages/Home.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | export default class Home extends Component {
4 | render() {
5 | return (
6 | Home
7 | )
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/step-01/journal/src/pages/Login.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | export default class Login extends Component {
4 | render() {
5 | return (
6 | Login
7 | )
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/step-01/journal/src/pages/index.js:
--------------------------------------------------------------------------------
1 | export { default as Home } from './Home';
2 | export { default as Login } from './Login';
3 |
--------------------------------------------------------------------------------
/step-01/journal/template.md:
--------------------------------------------------------------------------------
1 | ## Bootstrap starter template
2 |
3 | Use this document as a way to quickly start any new project.
4 | All you get is this text and a mostly barebones HTML document.
5 |
--------------------------------------------------------------------------------
/step-01/starter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-01/starter.png
--------------------------------------------------------------------------------
/step-02/authenticator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-02/authenticator.png
--------------------------------------------------------------------------------
/step-02/journal/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
23 | #awsmobilejs
24 | appsync-info.json
25 | aws-info.json
26 | project-info.json
27 | aws-exports.*
28 | awsmobilejs/.awsmobile/backend-build
29 | awsmobilejs/\#current-backend-info
30 | ~awsmobilejs-*/
--------------------------------------------------------------------------------
/step-02/journal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "journal",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "aws-amplify": "^1.1.1",
7 | "aws-amplify-react": "^2.0.2",
8 | "bootstrap-4-react": "0.0.53",
9 | "react": "^16.5.2",
10 | "react-dom": "^16.5.2",
11 | "react-router-dom": "^4.3.1",
12 | "react-scripts": "1.1.5"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test --env=jsdom",
18 | "eject": "react-scripts eject"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/step-02/journal/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-02/journal/public/favicon.ico
--------------------------------------------------------------------------------
/step-02/journal/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
23 | Bootstrap 4 React
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
35 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/step-02/journal/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Bootstrap 4 React",
3 | "name": "Bootstrap 4 React Documentation",
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 |
--------------------------------------------------------------------------------
/step-02/journal/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 5rem;
3 | }
4 | .starter-template {
5 | padding: 3rem 1.5rem;
6 | text-align: center;
7 | }
8 |
--------------------------------------------------------------------------------
/step-02/journal/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Amplify from 'aws-amplify';
3 |
4 | import { Navigator, Main } from './components';
5 | import './App.css';
6 | import aws_exports from './aws-exports';
7 |
8 | Amplify.configure(aws_exports);
9 |
10 | class App extends Component {
11 | render() {
12 | return (
13 |
14 |
15 |
16 |
17 | );
18 | }
19 | }
20 |
21 | export default App;
22 |
--------------------------------------------------------------------------------
/step-02/journal/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/step-02/journal/src/components/Main.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Container } from 'bootstrap-4-react';
3 | import { HashRouter, Route, Switch } from 'react-router-dom';
4 | import { Auth, Hub, Logger } from 'aws-amplify';
5 |
6 | import { Home, Login } from '../pages';
7 |
8 | const logger = new Logger('Main');
9 |
10 | export default class Main extends Component {
11 | constructor(props) {
12 | super(props);
13 |
14 | this.loadUser = this.loadUser.bind(this);
15 |
16 | Hub.listen('auth', this, 'main');
17 |
18 | this.state = { user: null }
19 | }
20 |
21 | componentDidMount() {
22 | this.loadUser();
23 | }
24 |
25 | onHubCapsule(capsule) {
26 | logger.info('on Auth event', capsule);
27 | this.loadUser();
28 | }
29 |
30 | loadUser() {
31 | Auth.currentAuthenticatedUser()
32 | .then(user => this.setState({ user: user }))
33 | .catch(err => this.setState({ user: null }));
34 | }
35 |
36 | render() {
37 | const { user } = this.state;
38 |
39 | return (
40 |
41 |
42 |
43 |
44 | }
48 | />
49 | }
53 | />
54 |
55 |
56 |
57 |
58 | )
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/step-02/journal/src/components/Navigator.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Navbar, Nav, BSpan } from 'bootstrap-4-react';
3 | import { HashRouter, Route, Switch } from 'react-router-dom';
4 | import { Auth, Hub, Logger } from 'aws-amplify';
5 | import { SignOut } from 'aws-amplify-react';
6 |
7 | const HomeItems = props => (
8 |
9 |
10 | Home
11 | (current}
12 |
13 |
14 | Login
15 |
16 |
17 | )
18 |
19 | const LoginItems = props => (
20 |
21 |
22 | Home
23 |
24 |
25 | Login
26 | (current}
27 |
28 |
29 | )
30 |
31 | const logger = new Logger('Navigator');
32 |
33 | export default class Navigator extends Component {
34 | constructor(props) {
35 | super(props);
36 |
37 | this.loadUser = this.loadUser.bind(this);
38 |
39 | Hub.listen('auth', this, 'navigator'); // Add this component as a listener of auth events.
40 |
41 | this.state = { user: null }
42 | }
43 |
44 | componentDidMount() {
45 | this.loadUser(); // The first check
46 | }
47 |
48 | onHubCapsule(capsule) {
49 | logger.info('on Auth event', capsule);
50 | this.loadUser(); // Triggered every time user sign in / out.
51 | }
52 |
53 | loadUser() {
54 | Auth.currentAuthenticatedUser()
55 | .then(user => this.setState({ user: user }))
56 | .catch(err => this.setState({ user: null }));
57 | }
58 |
59 | render() {
60 | const { user } = this.state;
61 |
62 | return (
63 |
64 | Journal
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | { user? 'Hi ' + user.username : 'Please sign in' }
78 |
79 | { user && }
80 |
81 |
82 | )
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/step-02/journal/src/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as Navigator } from './Navigator';
2 | export { default as Main } from './Main';
3 |
--------------------------------------------------------------------------------
/step-02/journal/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/step-02/journal/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(, document.getElementById('root'));
6 |
--------------------------------------------------------------------------------
/step-02/journal/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/step-02/journal/src/pages/Home.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Lead, BSpan } from 'bootstrap-4-react';
3 |
4 | export default class Home extends Component {
5 | render() {
6 | const { user } = this.props;
7 |
8 | return (
9 |
10 | Home
11 | { user && You are signed in as {user.username}. }
12 |
13 | )
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/step-02/journal/src/pages/Login.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Lead, BSpan } from 'bootstrap-4-react';
3 | import { Authenticator } from 'aws-amplify-react';
4 |
5 | export default class Login extends Component {
6 | render() {
7 | const { user } = this.props;
8 |
9 | return (
10 |
11 | { !user && }
12 | { user && You are signed in as {user.username}. }
13 |
14 | )
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/step-02/journal/src/pages/index.js:
--------------------------------------------------------------------------------
1 | export { default as Home } from './Home';
2 | export { default as Login } from './Login';
3 |
--------------------------------------------------------------------------------
/step-02/welcome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-02/welcome.png
--------------------------------------------------------------------------------
/step-03/authentication.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-03/authentication.png
--------------------------------------------------------------------------------
/step-03/journal/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
23 | #awsmobilejs
24 | appsync-info.json
25 | aws-info.json
26 | project-info.json
27 | aws-exports.*
28 | awsmobilejs/.awsmobile/backend-build
29 | awsmobilejs/\#current-backend-info
30 | ~awsmobilejs-*/
--------------------------------------------------------------------------------
/step-03/journal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "journal",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "aws-amplify": "^1.1.1",
7 | "aws-amplify-react": "^2.0.2",
8 | "bootstrap-4-react": "0.0.54",
9 | "react": "^16.5.2",
10 | "react-dom": "^16.5.2",
11 | "react-router-dom": "^4.3.1",
12 | "react-scripts": "1.1.5"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test --env=jsdom",
18 | "eject": "react-scripts eject"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/step-03/journal/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-03/journal/public/favicon.ico
--------------------------------------------------------------------------------
/step-03/journal/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
23 | Bootstrap 4 React
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
35 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/step-03/journal/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Bootstrap 4 React",
3 | "name": "Bootstrap 4 React Documentation",
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 |
--------------------------------------------------------------------------------
/step-03/journal/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 5rem;
3 | }
4 | .starter-template {
5 | padding: 3rem 1.5rem;
6 | text-align: center;
7 | }
8 |
--------------------------------------------------------------------------------
/step-03/journal/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Amplify from 'aws-amplify';
3 |
4 | import { Navigator, Main } from './components';
5 | import './App.css';
6 | import aws_exports from './aws-exports';
7 |
8 | Amplify.Logger.LOG_LEVEL = 'INFO'; // We write INFO level logs throughout app
9 | Amplify.configure(aws_exports);
10 |
11 | class App extends Component {
12 | render() {
13 | return (
14 |
15 |
16 |
17 |
18 | );
19 | }
20 | }
21 |
22 | export default App;
23 |
--------------------------------------------------------------------------------
/step-03/journal/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/step-03/journal/src/components/Main.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Container } from 'bootstrap-4-react';
3 | import { HashRouter, Route, Switch } from 'react-router-dom';
4 | import { Auth, Hub, Logger } from 'aws-amplify';
5 |
6 | import { Home, Login } from '../pages';
7 |
8 | const logger = new Logger('Main');
9 |
10 | export default class Main extends Component {
11 | constructor(props) {
12 | super(props);
13 |
14 | this.loadUser = this.loadUser.bind(this);
15 |
16 | Hub.listen('auth', this, 'main');
17 |
18 | this.state = { user: null }
19 | }
20 |
21 | componentDidMount() {
22 | this.loadUser();
23 | }
24 |
25 | onHubCapsule(capsule) {
26 | logger.info('on Auth event', capsule);
27 | this.loadUser();
28 | }
29 |
30 | loadUser() {
31 | Auth.currentAuthenticatedUser()
32 | .then(user => this.setState({ user: user }))
33 | .catch(err => this.setState({ user: null }));
34 | }
35 |
36 | render() {
37 | const { user } = this.state;
38 |
39 | return (
40 |
41 |
42 |
43 |
44 | }
48 | />
49 | }
53 | />
54 |
55 |
56 |
57 |
58 | )
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/step-03/journal/src/components/Navigator.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Navbar, Nav, BSpan } from 'bootstrap-4-react';
3 | import { HashRouter, Route, Switch } from 'react-router-dom';
4 | import { Auth, Hub, Logger } from 'aws-amplify';
5 | import { JSignOut } from './auth';
6 |
7 | const HomeItems = props => (
8 |
9 |
10 | Home
11 | (current}
12 |
13 |
14 | Login
15 |
16 |
17 | )
18 |
19 | const LoginItems = props => (
20 |
21 |
22 | Home
23 |
24 |
25 | Login
26 | (current}
27 |
28 |
29 | )
30 |
31 | const logger = new Logger('Navigator');
32 |
33 | export default class Navigator extends Component {
34 | constructor(props) {
35 | super(props);
36 |
37 | this.loadUser = this.loadUser.bind(this);
38 |
39 | Hub.listen('auth', this, 'navigator'); // Add this component as a listener of auth events.
40 |
41 | this.state = { user: null }
42 | }
43 |
44 | componentDidMount() {
45 | this.loadUser(); // The first check
46 | }
47 |
48 | onHubCapsule(capsule) {
49 | logger.info('on Auth event', capsule);
50 | this.loadUser(); // Triggered every time user sign in / out.
51 | }
52 |
53 | loadUser() {
54 | Auth.currentAuthenticatedUser()
55 | .then(user => this.setState({ user: user }))
56 | .catch(err => this.setState({ user: null }));
57 | }
58 |
59 | render() {
60 | const { user } = this.state;
61 |
62 | return (
63 |
64 | Journal
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | { user? 'Hi ' + user.username : 'Please sign in' }
78 |
79 | { user && }
80 |
81 |
82 | )
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/step-03/journal/src/components/auth/JSignOut.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Button } from 'bootstrap-4-react';
3 | import { Auth, Logger } from 'aws-amplify';
4 |
5 | const logger = new Logger('JSignOut');
6 |
7 | export default class JSignOut extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.signOut = this.signOut.bind(this);
11 | }
12 |
13 | signOut() {
14 | Auth.signOut()
15 | .then(() => logger.info('sign out success'))
16 | .catch(err => logger.info('sign out error', err));
17 | }
18 |
19 | render() {
20 | return (
21 |
31 | )
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/step-03/journal/src/components/auth/index.js:
--------------------------------------------------------------------------------
1 | export { default as JSignOut } from './JSignOut';
2 | export { default as JSignIn } from './JSignIn';
3 | export { default as JConfirmSignIn } from './JConfirmSignIn';
4 | export { default as JSignUp } from './JSignUp';
5 | export { default as JConfirmSignUp } from './JConfirmSignUp';
6 | export { default as JForgotPassword } from './JForgotPassword';
7 | export { default as JForgotPasswordReset } from './JForgotPasswordReset';
8 |
--------------------------------------------------------------------------------
/step-03/journal/src/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as Navigator } from './Navigator';
2 | export { default as Main } from './Main';
3 |
--------------------------------------------------------------------------------
/step-03/journal/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/step-03/journal/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(, document.getElementById('root'));
6 |
--------------------------------------------------------------------------------
/step-03/journal/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/step-03/journal/src/pages/Home.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Lead, BSpan } from 'bootstrap-4-react';
3 |
4 | export default class Home extends Component {
5 | render() {
6 | const { user } = this.props;
7 |
8 | return (
9 |
10 | Home
11 | { user && You are signed in as {user.username}. }
12 |
13 | )
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/step-03/journal/src/pages/Login.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Lead, BSpan } from 'bootstrap-4-react';
3 | import { Authenticator } from 'aws-amplify-react';
4 |
5 | import {
6 | JSignIn,
7 | JConfirmSignIn,
8 | JSignUp,
9 | JConfirmSignUp,
10 | JForgotPassword,
11 | JForgotPasswordReset
12 | } from '../components/auth';
13 |
14 | const CustomAuthenticator = props => (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | )
24 |
25 | export default class Login extends Component {
26 | render() {
27 | const { user } = this.props;
28 |
29 | return (
30 |
31 | { !user && }
32 | { user && You are signed in as {user.username}. }
33 |
34 | )
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/step-03/journal/src/pages/index.js:
--------------------------------------------------------------------------------
1 | export { default as Home } from './Home';
2 | export { default as Login } from './Login';
3 |
--------------------------------------------------------------------------------
/step-03/sign-in.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-03/sign-in.png
--------------------------------------------------------------------------------
/step-03/sign-out.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-03/sign-out.png
--------------------------------------------------------------------------------
/step-04/journal/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
23 | #awsmobilejs
24 | appsync-info.json
25 | aws-info.json
26 | project-info.json
27 | aws-exports.*
28 | awsmobilejs/.awsmobile/backend-build
29 | awsmobilejs/\#current-backend-info
30 | ~awsmobilejs-*/
--------------------------------------------------------------------------------
/step-04/journal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "journal",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "aws-amplify": "^1.1.1",
7 | "aws-amplify-react": "^2.0.2",
8 | "bootstrap-4-react": "0.0.54",
9 | "react": "^16.5.2",
10 | "react-dom": "^16.5.2",
11 | "react-router-dom": "^4.3.1",
12 | "react-scripts": "1.1.5"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test --env=jsdom",
18 | "eject": "react-scripts eject"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/step-04/journal/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-04/journal/public/favicon.ico
--------------------------------------------------------------------------------
/step-04/journal/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
23 | Bootstrap 4 React
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
35 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/step-04/journal/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Bootstrap 4 React",
3 | "name": "Bootstrap 4 React Documentation",
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 |
--------------------------------------------------------------------------------
/step-04/journal/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 5rem;
3 | }
4 | .starter-template {
5 | padding: 3rem 1.5rem;
6 | text-align: center;
7 | }
8 |
--------------------------------------------------------------------------------
/step-04/journal/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Amplify from 'aws-amplify';
3 |
4 | import { Navigator, Main } from './components';
5 | import './App.css';
6 | import aws_exports from './aws-exports';
7 |
8 | Amplify.Logger.LOG_LEVEL = 'INFO'; // We write INFO level logs throughout app
9 | Amplify.configure(aws_exports);
10 |
11 | class App extends Component {
12 | render() {
13 | return (
14 |
15 |
16 |
17 |
18 | );
19 | }
20 | }
21 |
22 | export default App;
23 |
--------------------------------------------------------------------------------
/step-04/journal/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/step-04/journal/src/components/Main.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Container } from 'bootstrap-4-react';
3 | import { HashRouter, Route, Switch } from 'react-router-dom';
4 | import { Auth, Hub, Logger } from 'aws-amplify';
5 |
6 | import { Home, Profile, Login } from '../pages';
7 |
8 | const logger = new Logger('Main');
9 |
10 | export default class Main extends Component {
11 | constructor(props) {
12 | super(props);
13 |
14 | this.loadUser = this.loadUser.bind(this);
15 |
16 | Hub.listen('auth', this, 'main');
17 |
18 | this.state = { user: null }
19 | }
20 |
21 | componentDidMount() {
22 | this.loadUser();
23 | }
24 |
25 | onHubCapsule(capsule) {
26 | logger.info('on Auth event', capsule);
27 | this.loadUser();
28 | }
29 |
30 | loadUser() {
31 | Auth.currentAuthenticatedUser()
32 | .then(user => this.setState({ user: user }))
33 | .catch(err => this.setState({ user: null }));
34 | }
35 |
36 | render() {
37 | const { user } = this.state;
38 |
39 | return (
40 |
41 |
42 |
43 |
44 | }
48 | />
49 | }
53 | />
54 | }
58 | />
59 |
60 |
61 |
62 |
63 | )
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/step-04/journal/src/components/auth/JSignOut.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Button } from 'bootstrap-4-react';
3 | import { Auth, Logger } from 'aws-amplify';
4 |
5 | const logger = new Logger('JSignOut');
6 |
7 | export default class JSignOut extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.signOut = this.signOut.bind(this);
11 | }
12 |
13 | signOut() {
14 | Auth.signOut()
15 | .then(() => logger.info('sign out success'))
16 | .catch(err => logger.info('sign out error', err));
17 | }
18 |
19 | render() {
20 | return (
21 |
31 | )
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/step-04/journal/src/components/auth/index.js:
--------------------------------------------------------------------------------
1 | export { default as JSignOut } from './JSignOut';
2 | export { default as JSignIn } from './JSignIn';
3 | export { default as JConfirmSignIn } from './JConfirmSignIn';
4 | export { default as JSignUp } from './JSignUp';
5 | export { default as JConfirmSignUp } from './JConfirmSignUp';
6 | export { default as JForgotPassword } from './JForgotPassword';
7 | export { default as JForgotPasswordReset } from './JForgotPasswordReset';
8 |
--------------------------------------------------------------------------------
/step-04/journal/src/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as Navigator } from './Navigator';
2 | export { default as Main } from './Main';
3 |
--------------------------------------------------------------------------------
/step-04/journal/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/step-04/journal/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(, document.getElementById('root'));
6 |
--------------------------------------------------------------------------------
/step-04/journal/src/pages/Home.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Lead, BSpan } from 'bootstrap-4-react';
3 |
4 | export default class Home extends Component {
5 | render() {
6 | const { user } = this.props;
7 |
8 | return (
9 |
10 | Home
11 | { user && You are signed in as {user.username}. }
12 |
13 | )
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/step-04/journal/src/pages/Login.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Lead, BSpan } from 'bootstrap-4-react';
3 | import { Authenticator } from 'aws-amplify-react';
4 |
5 | import {
6 | JSignIn,
7 | JConfirmSignIn,
8 | JSignUp,
9 | JConfirmSignUp,
10 | JForgotPassword,
11 | JForgotPasswordReset
12 | } from '../components/auth';
13 |
14 | const CustomAuthenticator = props => (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | )
24 |
25 | export default class Login extends Component {
26 | render() {
27 | const { user } = this.props;
28 |
29 | return (
30 |
31 | { !user && }
32 | { user && You are signed in as {user.username}. }
33 |
34 | )
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/step-04/journal/src/pages/index.js:
--------------------------------------------------------------------------------
1 | export { default as Home } from './Home';
2 | export { default as Login } from './Login';
3 | export { default as Profile } from './Profile';
4 |
--------------------------------------------------------------------------------
/step-04/profile-edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-04/profile-edit.png
--------------------------------------------------------------------------------
/step-04/profile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-04/profile.png
--------------------------------------------------------------------------------
/step-05/journal/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
23 | #awsmobilejs
24 | appsync-info.json
25 | aws-info.json
26 | project-info.json
27 | aws-exports.*
28 | awsmobilejs/.awsmobile/backend-build
29 | awsmobilejs/\#current-backend-info
30 | ~awsmobilejs-*/
--------------------------------------------------------------------------------
/step-05/journal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "journal",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "aws-amplify": "^1.1.1",
7 | "aws-amplify-react": "^2.0.2",
8 | "bootstrap-4-react": "0.0.54",
9 | "react": "^16.5.2",
10 | "react-dom": "^16.5.2",
11 | "react-router-dom": "^4.3.1",
12 | "react-scripts": "1.1.5",
13 | "redux": "^4.0.0"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test --env=jsdom",
19 | "eject": "react-scripts eject"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/step-05/journal/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-05/journal/public/favicon.ico
--------------------------------------------------------------------------------
/step-05/journal/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
23 | Bootstrap 4 React
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
35 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/step-05/journal/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Bootstrap 4 React",
3 | "name": "Bootstrap 4 React Documentation",
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 |
--------------------------------------------------------------------------------
/step-05/journal/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 5rem;
3 | }
4 | .starter-template {
5 | padding: 3rem 1.5rem;
6 | text-align: center;
7 | }
8 |
--------------------------------------------------------------------------------
/step-05/journal/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Amplify from 'aws-amplify';
3 |
4 | import { Navigator, Main } from './components';
5 | import './App.css';
6 | import aws_exports from './aws-exports';
7 |
8 | import store, { AmplifyBridge } from './store';
9 |
10 | Amplify.Logger.LOG_LEVEL = 'INFO'; // We write INFO level logs throughout app
11 | Amplify.configure(aws_exports);
12 |
13 | new AmplifyBridge(store);
14 |
15 | class App extends Component {
16 | render() {
17 | return (
18 |
19 |
20 |
21 |
22 | );
23 | }
24 | }
25 |
26 | export default App;
27 |
--------------------------------------------------------------------------------
/step-05/journal/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/step-05/journal/src/components/Main.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Container } from 'bootstrap-4-react';
3 | import { HashRouter, Route, Switch } from 'react-router-dom';
4 | import { Logger } from 'aws-amplify';
5 |
6 | import store from '../store';
7 | import { Home, Profile, Login } from '../pages';
8 |
9 | const logger = new Logger('Main');
10 |
11 | export default class Main extends Component {
12 | constructor(props) {
13 | super(props);
14 |
15 | this.storeListener = this.storeListener.bind(this);
16 |
17 | this.state = { user: null }
18 | }
19 |
20 | componentDidMount() {
21 | this.unsubscribeStore = store.subscribe(this.storeListener);
22 | }
23 |
24 | componentWillUnmount() {
25 | this.unsubscribeStore();
26 | }
27 |
28 | storeListener() {
29 | logger.info('redux notification');
30 | this.setState({ user: store.getState().user });
31 | }
32 |
33 | render() {
34 | const { user } = this.state;
35 |
36 | return (
37 |
38 |
39 |
40 |
41 | }
45 | />
46 | }
50 | />
51 | }
55 | />
56 |
57 |
58 |
59 |
60 | )
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/step-05/journal/src/components/auth/JSignOut.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Button } from 'bootstrap-4-react';
3 | import { Auth, Logger } from 'aws-amplify';
4 |
5 | const logger = new Logger('JSignOut');
6 |
7 | export default class JSignOut extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.signOut = this.signOut.bind(this);
11 | }
12 |
13 | signOut() {
14 | Auth.signOut()
15 | .then(() => logger.info('sign out success'))
16 | .catch(err => logger.info('sign out error', err));
17 | }
18 |
19 | render() {
20 | return (
21 |
31 | )
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/step-05/journal/src/components/auth/index.js:
--------------------------------------------------------------------------------
1 | export { default as JSignOut } from './JSignOut';
2 | export { default as JSignIn } from './JSignIn';
3 | export { default as JConfirmSignIn } from './JConfirmSignIn';
4 | export { default as JSignUp } from './JSignUp';
5 | export { default as JConfirmSignUp } from './JConfirmSignUp';
6 | export { default as JForgotPassword } from './JForgotPassword';
7 | export { default as JForgotPasswordReset } from './JForgotPasswordReset';
8 |
--------------------------------------------------------------------------------
/step-05/journal/src/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as Navigator } from './Navigator';
2 | export { default as Main } from './Main';
3 |
--------------------------------------------------------------------------------
/step-05/journal/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/step-05/journal/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(, document.getElementById('root'));
6 |
--------------------------------------------------------------------------------
/step-05/journal/src/pages/Home.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Lead, BSpan } from 'bootstrap-4-react';
3 |
4 | export default class Home extends Component {
5 | render() {
6 | const { user } = this.props;
7 |
8 | return (
9 |
10 | Home
11 | { user && You are signed in as {user.username}. }
12 |
13 | )
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/step-05/journal/src/pages/Login.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Lead, BSpan } from 'bootstrap-4-react';
3 | import { Authenticator } from 'aws-amplify-react';
4 |
5 | import {
6 | JSignIn,
7 | JConfirmSignIn,
8 | JSignUp,
9 | JConfirmSignUp,
10 | JForgotPassword,
11 | JForgotPasswordReset
12 | } from '../components/auth';
13 |
14 | const CustomAuthenticator = props => (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | )
24 |
25 | export default class Login extends Component {
26 | render() {
27 | const { user } = this.props;
28 |
29 | return (
30 |
31 | { !user && }
32 | { user && You are signed in as {user.username}. }
33 |
34 | )
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/step-05/journal/src/pages/index.js:
--------------------------------------------------------------------------------
1 | export { default as Home } from './Home';
2 | export { default as Login } from './Login';
3 | export { default as Profile } from './Profile';
4 |
--------------------------------------------------------------------------------
/step-05/journal/src/store/AmplifyBridge.js:
--------------------------------------------------------------------------------
1 | import { Auth, Hub, Logger } from 'aws-amplify';
2 |
3 | import { switchUser, updateProfile, deleteProfile } from './actions';
4 |
5 | const logger = new Logger('AmplifyBridge');
6 |
7 | export default class AmplifyBridge {
8 | constructor(store) {
9 | this.store = store;
10 |
11 | this.onHubCapsule = this.onHubCapsule.bind(this);
12 | Hub.listen('auth', this, 'AmplifyBridge'); // Add this component as a listener of auth events.
13 |
14 | this.checkUser(); // first check
15 | }
16 |
17 | onHubCapsule(capsule) {
18 | logger.info('on Auth event', capsule);
19 | this.checkUser(); // triggered every time user sign in / out
20 | }
21 |
22 | checkUser() {
23 | Auth.currentAuthenticatedUser()
24 | .then(user => this.checkUserSuccess(user))
25 | .catch(err => this.checkUserError(err));
26 | }
27 |
28 | loadProfile(user) {
29 | Auth.userAttributes(user)
30 | .then(data => this.loadProfileSuccess(data))
31 | .catch(err => this.loadProfileError(err));
32 | }
33 |
34 | checkUserSuccess(user) {
35 | logger.info('check user success', user);
36 | this.store.dispatch(switchUser(user));
37 | this.loadProfile(user);
38 | }
39 |
40 | checkUserError(err) {
41 | logger.info('check user error', err);
42 | this.store.dispatch(switchUser(null));
43 | this.store.dispatch(deleteProfile());
44 | }
45 |
46 | loadProfileSuccess(data) {
47 | logger.info('load profile success', data);
48 | const profile = this.translateAttributes(data);
49 | this.store.dispatch(updateProfile(profile));
50 | }
51 |
52 | loadProfileError(err) {
53 | logger.info('load profile error', err);
54 | this.store.dispatch(deleteProfile());
55 | }
56 |
57 | // Auth.userAttributes returns an array of attributes.
58 | // We map it to an object for easy use.
59 | translateAttributes(data) {
60 | const attributes = {};
61 | data
62 | .filter(attr => ['given_name', 'family_name'].includes(attr.Name))
63 | .forEach(attr => attributes[attr.Name] = attr.Value);
64 | return attributes;
65 | }
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/step-05/journal/src/store/actions.js:
--------------------------------------------------------------------------------
1 | const SWITCH_USER = 'SWITCH_USER';
2 |
3 | const UPDATE_PROFILE = 'UPDATE_PROFILE';
4 | const DELETE_PROFILE = 'DELETE_PROFILE';
5 |
6 | // when user sign in / out
7 | function switchUser(user) {
8 | return {
9 | type: SWITCH_USER,
10 | user
11 | }
12 | }
13 |
14 | // when user updates profile
15 | function updateProfile(profile) {
16 | return {
17 | type: UPDATE_PROFILE,
18 | profile
19 | }
20 | }
21 |
22 | // when user sign out
23 | function deleteProfile() {
24 | return { type: DELETE_PROFILE }
25 | }
26 |
27 | export { SWITCH_USER, UPDATE_PROFILE, DELETE_PROFILE }
28 | export { switchUser, updateProfile, deleteProfile }
29 |
--------------------------------------------------------------------------------
/step-05/journal/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore } from 'redux';
2 |
3 | import { updateProfile } from './actions';
4 | import Journal from './reducers';
5 |
6 | const store = createStore(Journal);
7 |
8 | export default store;
9 | export {
10 | updateProfile
11 | }
12 | export { default as AmplifyBridge } from './AmplifyBridge';
13 |
--------------------------------------------------------------------------------
/step-05/journal/src/store/reducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 |
3 | import { SWITCH_USER, UPDATE_PROFILE, DELETE_PROFILE } from './actions';
4 |
5 | function user(state={}, action) {
6 | switch(action.type) {
7 | case SWITCH_USER:
8 | return action.user;
9 | default:
10 | return state;
11 | }
12 | }
13 |
14 | function profile(state={}, action) {
15 | switch(action.type) {
16 | case UPDATE_PROFILE:
17 | return Object.assign(
18 | {},
19 | state,
20 | action.profile
21 | );
22 | case DELETE_PROFILE:
23 | return null;
24 | default:
25 | return state;
26 | }
27 | }
28 |
29 | const Journal = combineReducers({
30 | user,
31 | profile
32 | });
33 |
34 | export default Journal;
35 |
--------------------------------------------------------------------------------
/step-05/redux.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-05/redux.png
--------------------------------------------------------------------------------
/step-06/album-render-key.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-06/album-render-key.png
--------------------------------------------------------------------------------
/step-06/album_with_picker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-06/album_with_picker.png
--------------------------------------------------------------------------------
/step-06/everyday-journal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-06/everyday-journal.png
--------------------------------------------------------------------------------
/step-06/journal/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
23 | #awsmobilejs
24 | appsync-info.json
25 | aws-info.json
26 | project-info.json
27 | aws-exports.*
28 | awsmobilejs/.awsmobile/backend-build
29 | awsmobilejs/\#current-backend-info
30 | ~awsmobilejs-*/
--------------------------------------------------------------------------------
/step-06/journal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "journal",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "aws-amplify": "^1.1.1",
7 | "aws-amplify-react": "^2.0.2",
8 | "bootstrap-4-react": "0.0.54",
9 | "react": "^16.5.2",
10 | "react-dom": "^16.5.2",
11 | "react-router-dom": "^4.3.1",
12 | "react-scripts": "1.1.5",
13 | "redux": "^4.0.0"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test --env=jsdom",
19 | "eject": "react-scripts eject"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/step-06/journal/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-06/journal/public/favicon.ico
--------------------------------------------------------------------------------
/step-06/journal/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
23 | Bootstrap 4 React
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
35 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/step-06/journal/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Bootstrap 4 React",
3 | "name": "Bootstrap 4 React Documentation",
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 |
--------------------------------------------------------------------------------
/step-06/journal/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 5rem;
3 | }
4 | .starter-template {
5 | padding: 3rem 1.5rem;
6 | text-align: center;
7 | }
8 |
--------------------------------------------------------------------------------
/step-06/journal/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Amplify from 'aws-amplify';
3 |
4 | import { Navigator, Main } from './components';
5 | import './App.css';
6 | import aws_exports from './aws-exports';
7 |
8 | import store, { AmplifyBridge } from './store';
9 |
10 | Amplify.Logger.LOG_LEVEL = 'INFO'; // We write INFO level logs throughout app
11 | Amplify.configure(aws_exports);
12 |
13 | new AmplifyBridge(store);
14 |
15 | class App extends Component {
16 | render() {
17 | return (
18 |
19 |
20 |
21 |
22 | );
23 | }
24 | }
25 |
26 | export default App;
27 |
--------------------------------------------------------------------------------
/step-06/journal/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/step-06/journal/src/components/Main.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Container } from 'bootstrap-4-react';
3 | import { HashRouter, Route, Switch } from 'react-router-dom';
4 | import { Logger } from 'aws-amplify';
5 |
6 | import store from '../store';
7 | import { Home, Profile, Login } from '../pages';
8 |
9 | const logger = new Logger('Main');
10 |
11 | export default class Main extends Component {
12 | constructor(props) {
13 | super(props);
14 |
15 | this.storeListener = this.storeListener.bind(this);
16 |
17 | this.state = { user: null }
18 | }
19 |
20 | componentDidMount() {
21 | this.unsubscribeStore = store.subscribe(this.storeListener);
22 | }
23 |
24 | componentWillUnmount() {
25 | this.unsubscribeStore();
26 | }
27 |
28 | storeListener() {
29 | logger.info('redux notification');
30 | this.setState({ user: store.getState().user });
31 | }
32 |
33 | render() {
34 | const { user } = this.state;
35 |
36 | return (
37 |
38 |
39 |
40 |
41 | }
45 | />
46 | }
50 | />
51 | }
55 | />
56 |
57 |
58 |
59 |
60 | )
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/step-06/journal/src/components/Unauthorized.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Container } from 'bootstrap-4-react';
3 |
4 | export default class Unauthorized extends Component {
5 | render() {
6 | return (
7 |
8 | Not Authenticated
9 |
10 | )
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/step-06/journal/src/components/Unexpected.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Container } from 'bootstrap-4-react';
3 |
4 | export default class Unexpected extends Component {
5 | render() {
6 | return (
7 |
8 | Unexpected error happened
9 |
10 | )
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/step-06/journal/src/components/album/Album.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Card, BDiv } from 'bootstrap-4-react';
3 | import { Storage, Logger } from 'aws-amplify';
4 |
5 | import AlbumItem from './AlbumItem';
6 | import FilePicker from './FilePicker';
7 | import NoteEditor from './NoteEditor';
8 |
9 | const logger = new Logger('Album');
10 |
11 | const Cell = props => (
12 |
13 | {props.children}
14 |
15 | )
16 |
17 | export default class Album extends Component {
18 | constructor(props) {
19 | super(props);
20 | this.load = this.load.bind(this);
21 | this.handleUploaded = this.handleUploaded.bind(this);
22 |
23 | this.state = { items: [] }
24 | }
25 |
26 | componentDidMount() {
27 | this.load();
28 | }
29 |
30 | load() {
31 | const { path } = this.props;
32 | Storage.list(path)
33 | .then(data => this.loadSuccess(data))
34 | .catch(err => this.loadError(err));
35 | }
36 |
37 | loadSuccess(data) {
38 | logger.info('load album success', data);
39 | this.setState({ items: data });
40 | }
41 |
42 | loadError(err) {
43 | logger.info('load album error', err);
44 | }
45 |
46 | handleUploaded(key) {
47 | this.load();
48 | }
49 |
50 | render() {
51 | const { path } = this.props;
52 | const { items } = this.state;
53 |
54 | return (
55 |
56 |
57 | { items.map(item => {
58 | return (
59 |
60 |
61 | |
62 | )
63 | })}
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | )
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/step-06/journal/src/components/album/AlbumItem.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { BImg, BPre, BH5, BDiv } from 'bootstrap-4-react';
3 | import { Storage, Logger } from 'aws-amplify';
4 |
5 | const logger = new Logger('AlbumItem');
6 |
7 | const Note = props => {
8 | const note = JSON.parse(props.json);
9 | return (
10 |
11 | {note.subject}
12 |
13 | {note.content}
14 |
15 |
16 | )
17 | }
18 |
19 | export default class AlbumItem extends Component {
20 | constructor(props) {
21 | super(props);
22 | this.load = this.load.bind(this);
23 |
24 | this.state = { url: null, json: null }
25 | }
26 |
27 | componentDidMount() {
28 | this.load();
29 | }
30 |
31 | load() {
32 | const { item } = this.props;
33 | if (item.key.endsWith('.json')) {
34 | Storage.get(item.key, { download: true })
35 | .then(data => this.loadJsonSuccess(data))
36 | .catch(err => this.loadError(err));
37 | } else {
38 | Storage.get(item.key)
39 | .then(url => this.loadImageSuccess(url))
40 | .catch(err => this.loadError(err));
41 | }
42 | }
43 |
44 | loadJsonSuccess(data) {
45 | logger.info('load album item success', data);
46 | this.setState({ json: data.Body.toString('utf8') });
47 | }
48 |
49 | loadImageSuccess(url) {
50 | logger.info('load album item success', url);
51 | this.setState({ url: url });
52 | }
53 |
54 | loadError(err) {
55 | logger.info('load album item error', err);
56 | }
57 |
58 | render() {
59 | const { url, json } = this.state;
60 |
61 | return (
62 |
63 | { json && }
64 | { url && }
65 |
66 | )
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/step-06/journal/src/components/album/FilePicker.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Form, Button } from 'bootstrap-4-react';
3 | import { Storage, Logger } from 'aws-amplify';
4 |
5 | const logger = new Logger('FilePicker');
6 |
7 | const style = {
8 | position: 'relative',
9 | height: '3rem',
10 | button: {
11 | width: '100%',
12 | height: '100%',
13 | display: 'inline-block',
14 | position: 'absolute',
15 | left: 0,
16 | top: 0
17 | },
18 | file: {
19 | width: '100%',
20 | height: '100%',
21 | display: 'inline-block',
22 | position: 'absolute',
23 | left: 0,
24 | top: 0,
25 | opacity: 0,
26 | cursor: 'pointer'
27 | }
28 | }
29 |
30 | export default class FilePicker extends Component {
31 | constructor(props) {
32 | super(props);
33 | this.handleChange = this.handleChange.bind(this);
34 | }
35 |
36 | handleChange(event) {
37 | const file = event.target.files[0];
38 | logger.info('picked file', file);
39 | // Clear file input
40 | window.document.getElementById('fileInputForm').reset();
41 |
42 | this.uploadFile(file);
43 | }
44 |
45 | uploadFile(file) {
46 | const { path } = this.props;
47 | if (!path) {
48 | logger.warn('missing path property for FilePicker');
49 | return;
50 | }
51 |
52 | const key = this.calcS3Key(file);
53 | const { type } = file;
54 | Storage.put(key, file, { contentType: type })
55 | .then(data => this.uploadFileSuccess(data))
56 | .catch(err => this.uploadFileError(err));
57 | }
58 |
59 | uploadFileSuccess(data) {
60 | logger.info('upload file success', data);
61 | const { onUploaded } = this.props;
62 | if (onUploaded) { onUploaded(data.key); }
63 | }
64 |
65 | uploadFileError(err) {
66 | logger.info('upload file error', err);
67 | }
68 |
69 | calcS3Key(file) {
70 | return this.props.path + encodeURI(file.name).replace(/\s/g, '_');
71 | }
72 |
73 | render() {
74 | return (
75 |
76 |
83 |
84 |
85 | )
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/step-06/journal/src/components/album/NoteEditor.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Card, Form, Button } from 'bootstrap-4-react';
3 | import { Storage, Logger } from 'aws-amplify';
4 |
5 | const logger = new Logger('NoteEditor');
6 |
7 | export default class NoteEditor extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.handleSave = this.handleSave.bind(this);
11 | this.inputs = {
12 | subject: '',
13 | content: '',
14 | s3Key: props.s3Key || ''
15 | };
16 | }
17 |
18 | handleSave() {
19 | const { subject, content } = this.inputs;
20 | const note = { subject, content };
21 | logger.info('saving note', note);
22 |
23 | const key = this.getS3Key();
24 | Storage.put(key, JSON.stringify(note), { contentType: 'application/json' })
25 | .then(data => this.uploadNoteSuccess(data))
26 | .catch(err => this.uploadNoteError(err));
27 | }
28 |
29 | uploadNoteSuccess(data) {
30 | logger.info('upload note success', data);
31 | const { onUploaded } = this.props;
32 | if (onUploaded) { onUploaded(data.key); }
33 | }
34 |
35 | uploadNoteError(err) {
36 | logger.info('upload note error', err);
37 | }
38 |
39 | getS3Key() {
40 | if (!this.inputs.s3Key) {
41 | this.inputs.s3Key = this.props.path + new Date().getTime() + '.json';
42 | }
43 | return this.inputs.s3Key;
44 | }
45 |
46 | render() {
47 | return (
48 |
49 |
50 | this.inputs.subject = event.target.value}
56 | />
57 | this.inputs.content = event.target.value}
62 | />
63 |
64 |
65 |
66 |
67 |
68 | )
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/step-06/journal/src/components/album/index.js:
--------------------------------------------------------------------------------
1 | export { default as Album } from './Album';
2 |
--------------------------------------------------------------------------------
/step-06/journal/src/components/auth/JSignOut.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Button } from 'bootstrap-4-react';
3 | import { Auth, Logger } from 'aws-amplify';
4 |
5 | const logger = new Logger('JSignOut');
6 |
7 | export default class JSignOut extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.signOut = this.signOut.bind(this);
11 | }
12 |
13 | signOut() {
14 | Auth.signOut()
15 | .then(() => logger.info('sign out success'))
16 | .catch(err => logger.info('sign out error', err));
17 | }
18 |
19 | render() {
20 | return (
21 |
31 | )
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/step-06/journal/src/components/auth/index.js:
--------------------------------------------------------------------------------
1 | export { default as JSignOut } from './JSignOut';
2 | export { default as JSignIn } from './JSignIn';
3 | export { default as JConfirmSignIn } from './JConfirmSignIn';
4 | export { default as JSignUp } from './JSignUp';
5 | export { default as JConfirmSignUp } from './JConfirmSignUp';
6 | export { default as JForgotPassword } from './JForgotPassword';
7 | export { default as JForgotPasswordReset } from './JForgotPasswordReset';
8 |
--------------------------------------------------------------------------------
/step-06/journal/src/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as Navigator } from './Navigator';
2 | export { default as Main } from './Main';
3 |
4 | export { default as Unexpected } from './Unexpected';
5 | export { default as Unauthorized } from './Unauthorized';
6 |
--------------------------------------------------------------------------------
/step-06/journal/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/step-06/journal/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(, document.getElementById('root'));
6 |
--------------------------------------------------------------------------------
/step-06/journal/src/pages/Home.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | import { Unexpected, Unauthorized } from '../components';
4 | import { Album } from '../components/album';
5 |
6 | const padding = n => {
7 | return n > 9 ? n : '0' + n;
8 | }
9 |
10 | const today = () => {
11 | const dt = new date();
12 | return [
13 | dt.getfullyear(),
14 | padding(dt.getmonth() + 1),
15 | padding(dt.getdate())
16 | ].join('-');
17 | }
18 |
19 | const album_path = user_id => {
20 | return user_id + '/' + today() + '/';
21 | }
22 |
23 | export default class Home extends Component {
24 | render() {
25 | const { user } = this.props;
26 |
27 | if (!user) { return }
28 | if (!user.id) { return }
29 |
30 | return (
31 |
32 |
33 |
34 | )
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/step-06/journal/src/pages/Login.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Lead, BSpan } from 'bootstrap-4-react';
3 | import { Authenticator } from 'aws-amplify-react';
4 |
5 | import {
6 | JSignIn,
7 | JConfirmSignIn,
8 | JSignUp,
9 | JConfirmSignUp,
10 | JForgotPassword,
11 | JForgotPasswordReset
12 | } from '../components/auth';
13 |
14 | const CustomAuthenticator = props => (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | )
24 |
25 | export default class Login extends Component {
26 | render() {
27 | const { user } = this.props;
28 |
29 | return (
30 |
31 | { !user && }
32 | { user && You are signed in as {user.username}. }
33 |
34 | )
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/step-06/journal/src/pages/index.js:
--------------------------------------------------------------------------------
1 | export { default as Home } from './Home';
2 | export { default as Login } from './Login';
3 | export { default as Profile } from './Profile';
4 |
--------------------------------------------------------------------------------
/step-06/journal/src/store/actions.js:
--------------------------------------------------------------------------------
1 | const SWITCH_USER = 'SWITCH_USER';
2 |
3 | const UPDATE_PROFILE = 'UPDATE_PROFILE';
4 | const DELETE_PROFILE = 'DELETE_PROFILE';
5 |
6 | // when user sign in / out
7 | function switchUser(user) {
8 | return {
9 | type: SWITCH_USER,
10 | user
11 | }
12 | }
13 |
14 | // when user updates profile
15 | function updateProfile(profile) {
16 | return {
17 | type: UPDATE_PROFILE,
18 | profile
19 | }
20 | }
21 |
22 | // when user sign out
23 | function deleteProfile() {
24 | return { type: DELETE_PROFILE }
25 | }
26 |
27 | export { SWITCH_USER, UPDATE_PROFILE, DELETE_PROFILE }
28 | export { switchUser, updateProfile, deleteProfile }
29 |
--------------------------------------------------------------------------------
/step-06/journal/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore } from 'redux';
2 |
3 | import { updateProfile } from './actions';
4 | import Journal from './reducers';
5 |
6 | const store = createStore(Journal);
7 |
8 | export default store;
9 | export {
10 | updateProfile
11 | }
12 | export { default as AmplifyBridge } from './AmplifyBridge';
13 |
--------------------------------------------------------------------------------
/step-06/journal/src/store/reducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 |
3 | import { SWITCH_USER, UPDATE_PROFILE, DELETE_PROFILE } from './actions';
4 |
5 | function user(state={}, action) {
6 | switch(action.type) {
7 | case SWITCH_USER:
8 | return action.user;
9 | default:
10 | return state;
11 | }
12 | }
13 |
14 | function profile(state={}, action) {
15 | switch(action.type) {
16 | case UPDATE_PROFILE:
17 | return Object.assign(
18 | {},
19 | state,
20 | action.profile
21 | );
22 | case DELETE_PROFILE:
23 | return null;
24 | default:
25 | return state;
26 | }
27 | }
28 |
29 | const Journal = combineReducers({
30 | user,
31 | profile
32 | });
33 |
34 | export default Journal;
35 |
--------------------------------------------------------------------------------
/step-06/note-editor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-06/note-editor.png
--------------------------------------------------------------------------------
/step-06/s3album.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-06/s3album.png
--------------------------------------------------------------------------------
/step-07/journal-by-day.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-07/journal-by-day.png
--------------------------------------------------------------------------------
/step-07/journal/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/step-07/journal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "journal",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "aws-amplify": "^1.1.6",
7 | "aws-amplify-react": "^2.0.7",
8 | "bootstrap-4-react": "0.0.54",
9 | "react": "^16.5.2",
10 | "react-dom": "^16.5.2",
11 | "react-router-dom": "^4.3.1",
12 | "react-scripts": "1.1.5",
13 | "redux": "^4.0.0"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test --env=jsdom",
19 | "eject": "react-scripts eject"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/step-07/journal/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-07/journal/public/favicon.ico
--------------------------------------------------------------------------------
/step-07/journal/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
23 | Bootstrap 4 React
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
35 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/step-07/journal/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Bootstrap 4 React",
3 | "name": "Bootstrap 4 React Documentation",
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 |
--------------------------------------------------------------------------------
/step-07/journal/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 5rem;
3 | }
4 | .starter-template {
5 | padding: 3rem 1.5rem;
6 | text-align: center;
7 | }
8 |
--------------------------------------------------------------------------------
/step-07/journal/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Amplify from 'aws-amplify';
3 |
4 | import { Navigator, Main } from './components';
5 | import './App.css';
6 | import aws_exports from './aws-exports';
7 |
8 | import store, { AmplifyBridge } from './store';
9 |
10 | Amplify.Logger.LOG_LEVEL = 'INFO'; // We write INFO level logs throughout app
11 | Amplify.configure(aws_exports);
12 |
13 | new AmplifyBridge(store);
14 |
15 | class App extends Component {
16 | render() {
17 | return (
18 |
19 |
20 |
21 |
22 | );
23 | }
24 | }
25 |
26 | export default App;
27 |
--------------------------------------------------------------------------------
/step-07/journal/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/step-07/journal/src/components/Main.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Container } from 'bootstrap-4-react';
3 | import { HashRouter, Route, Switch } from 'react-router-dom';
4 | import { Logger } from 'aws-amplify';
5 |
6 | import store from '../store';
7 | import { Home, Profile, Login } from '../pages';
8 |
9 | const logger = new Logger('Main');
10 |
11 | export default class Main extends Component {
12 | constructor(props) {
13 | super(props);
14 |
15 | this.storeListener = this.storeListener.bind(this);
16 |
17 | this.state = { user: null }
18 | }
19 |
20 | componentDidMount() {
21 | this.unsubscribeStore = store.subscribe(this.storeListener);
22 | }
23 |
24 | componentWillUnmount() {
25 | this.unsubscribeStore();
26 | }
27 |
28 | storeListener() {
29 | logger.info('redux notification');
30 | this.setState({ user: store.getState().user });
31 | }
32 |
33 | render() {
34 | const { user } = this.state;
35 |
36 | return (
37 |
38 |
39 |
40 |
41 | }
45 | />
46 | }
50 | />
51 | }
55 | />
56 |
57 |
58 |
59 |
60 | )
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/step-07/journal/src/components/Unauthorized.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Container } from 'bootstrap-4-react';
3 |
4 | export default class Unauthorized extends Component {
5 | render() {
6 | return (
7 |
8 | Not Authenticated
9 |
10 | )
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/step-07/journal/src/components/Unexpected.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Container } from 'bootstrap-4-react';
3 |
4 | export default class Unexpected extends Component {
5 | render() {
6 | return (
7 |
8 | Unexpected error happened
9 |
10 | )
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/step-07/journal/src/components/WhichDay.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Row, Col, BH4, Dropdown } from 'bootstrap-4-react';
3 | import { Storage, Logger } from 'aws-amplify';
4 |
5 | const padding = n => {
6 | return n > 9 ? n : '0' + n;
7 | }
8 |
9 | const today = () => {
10 | const dt = new Date();
11 | return [
12 | dt.getFullYear(),
13 | padding(dt.getMonth() + 1),
14 | padding(dt.getDate())
15 | ].join('-');
16 | }
17 |
18 | const logger = new Logger('WhichDay');
19 |
20 | export default class WhichDay extends Component {
21 | constructor(props) {
22 | super(props);
23 | this.load = this.load.bind(this);
24 | this.setDay = this.setDay.bind(this);
25 | this.state = { day: today(), days: [today()] }
26 | }
27 |
28 | componentDidMount() {
29 | this.load();
30 | }
31 |
32 | load() {
33 | const { rootPath } = this.props;
34 | Storage.list(rootPath)
35 | .then(data => this.loadSuccess(data))
36 | .catch(err => this.loadError(err));
37 | }
38 |
39 | loadSuccess(data) {
40 | logger.info('load list success', data);
41 | const days = data.map(item => {
42 | const match = item.key.match(/\/(\d{4}-\d{1,2}-\d{1,2})\//);
43 | return match? match[1] : null;
44 | })
45 | .filter (day => !!day);
46 | const day_set = new Set([today()].concat(days));
47 | const unique_days = Array.from(day_set).sort().reverse();
48 | this.setState({ days: unique_days });
49 | }
50 |
51 | loadError(err) {
52 | logger.info('load list error', err);
53 | }
54 |
55 | setDay(day) {
56 | this.setState({ day: day });
57 |
58 | const { onDaySelected } = this.props;
59 | if (onDaySelected) { onDaySelected(day); }
60 | }
61 |
62 | render() {
63 | const { day, days } = this.state;
64 |
65 | return (
66 |
67 |
68 | {day}
69 |
70 |
71 |
72 | {day}
73 |
74 | { days.map(day => {
75 | return (
76 | this.setDay(day)}
80 | >
81 | {day}
82 |
83 | )
84 | })}
85 |
86 |
87 |
88 |
89 | )
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/step-07/journal/src/components/album/Album.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Card, BDiv } from 'bootstrap-4-react';
3 | import { Storage, Logger } from 'aws-amplify';
4 |
5 | import AlbumItem from './AlbumItem';
6 | import FilePicker from './FilePicker';
7 | import NoteEditor from './NoteEditor';
8 |
9 | const logger = new Logger('Album');
10 |
11 | const Cell = props => (
12 |
13 | {props.children}
14 |
15 | )
16 |
17 | export default class Album extends Component {
18 | constructor(props) {
19 | super(props);
20 | this.load = this.load.bind(this);
21 | this.handleUploaded = this.handleUploaded.bind(this);
22 |
23 | this.state = { items: [] }
24 | }
25 |
26 | componentDidMount() {
27 | this.load();
28 | }
29 |
30 | load() {
31 | const { path } = this.props;
32 | Storage.list(path)
33 | .then(data => this.loadSuccess(data))
34 | .catch(err => this.loadError(err));
35 | }
36 |
37 | loadSuccess(data) {
38 | logger.info('load album success', data);
39 | this.setState({ items: data });
40 | }
41 |
42 | loadError(err) {
43 | logger.info('load album error', err);
44 | }
45 |
46 | handleUploaded(key) {
47 | this.load();
48 | }
49 |
50 | render() {
51 | const { path } = this.props;
52 | const { items } = this.state;
53 |
54 | return (
55 |
56 |
57 | { items.map(item => {
58 | return (
59 |
60 |
61 | |
62 | )
63 | })}
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | )
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/step-07/journal/src/components/album/AlbumItem.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { BImg, BPre, BH5, BDiv } from 'bootstrap-4-react';
3 | import { Storage, Logger } from 'aws-amplify';
4 |
5 | const logger = new Logger('AlbumItem');
6 |
7 | const Note = props => {
8 | const note = JSON.parse(props.json);
9 | return (
10 |
11 | {note.subject}
12 |
13 | {note.content}
14 |
15 |
16 | )
17 | }
18 |
19 | export default class AlbumItem extends Component {
20 | constructor(props) {
21 | super(props);
22 | this.load = this.load.bind(this);
23 |
24 | this.state = { url: null, json: null }
25 | }
26 |
27 | componentDidMount() {
28 | this.load();
29 | }
30 |
31 | load() {
32 | const { item } = this.props;
33 | if (item.key.endsWith('.json')) {
34 | Storage.get(item.key, { download: true })
35 | .then(data => this.loadJsonSuccess(data))
36 | .catch(err => this.loadError(err));
37 | } else {
38 | Storage.get(item.key)
39 | .then(url => this.loadImageSuccess(url))
40 | .catch(err => this.loadError(err));
41 | }
42 | }
43 |
44 | loadJsonSuccess(data) {
45 | logger.info('load album item success', data);
46 | this.setState({ json: data.Body.toString('utf8') });
47 | }
48 |
49 | loadImageSuccess(url) {
50 | logger.info('load album item success', url);
51 | this.setState({ url: url });
52 | }
53 |
54 | loadError(err) {
55 | logger.info('load album item error', err);
56 | }
57 |
58 | render() {
59 | const { url, json } = this.state;
60 |
61 | return (
62 |
63 | { json && }
64 | { url && }
65 |
66 | )
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/step-07/journal/src/components/album/FilePicker.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Form, Button } from 'bootstrap-4-react';
3 | import { Storage, Logger } from 'aws-amplify';
4 |
5 | const logger = new Logger('FilePicker');
6 |
7 | const style = {
8 | position: 'relative',
9 | height: '3rem',
10 | button: {
11 | width: '100%',
12 | height: '100%',
13 | display: 'inline-block',
14 | position: 'absolute',
15 | left: 0,
16 | top: 0
17 | },
18 | file: {
19 | width: '100%',
20 | height: '100%',
21 | display: 'inline-block',
22 | position: 'absolute',
23 | left: 0,
24 | top: 0,
25 | opacity: 0,
26 | cursor: 'pointer'
27 | }
28 | }
29 |
30 | export default class FilePicker extends Component {
31 | constructor(props) {
32 | super(props);
33 | this.handleChange = this.handleChange.bind(this);
34 | }
35 |
36 | handleChange(event) {
37 | const file = event.target.files[0];
38 | logger.info('picked file', file);
39 | // Clear file input
40 | window.document.getElementById('fileInputForm').reset();
41 |
42 | this.uploadFile(file);
43 | }
44 |
45 | uploadFile(file) {
46 | const { path } = this.props;
47 | if (!path) {
48 | logger.warn('missing path property for FilePicker');
49 | return;
50 | }
51 |
52 | const key = this.calcS3Key(file);
53 | const { type } = file;
54 | Storage.put(key, file, { contentType: type })
55 | .then(data => this.uploadFileSuccess(data))
56 | .catch(err => this.uploadFileError(err));
57 | }
58 |
59 | uploadFileSuccess(data) {
60 | logger.info('upload file success', data);
61 | const { onUploaded } = this.props;
62 | if (onUploaded) { onUploaded(data.key); }
63 | }
64 |
65 | uploadFileError(err) {
66 | logger.info('upload file error', err);
67 | }
68 |
69 | calcS3Key(file) {
70 | return this.props.path + encodeURI(file.name).replace(/\s/g, '_');
71 | }
72 |
73 | render() {
74 | return (
75 |
76 |
83 |
84 |
85 | )
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/step-07/journal/src/components/album/NoteEditor.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Card, Form, Button } from 'bootstrap-4-react';
3 | import { Storage, Logger } from 'aws-amplify';
4 |
5 | const logger = new Logger('NoteEditor');
6 |
7 | export default class NoteEditor extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.handleSave = this.handleSave.bind(this);
11 | this.inputs = {
12 | subject: '',
13 | content: '',
14 | s3Key: props.s3Key || ''
15 | };
16 | }
17 |
18 | handleSave() {
19 | const { subject, content } = this.inputs;
20 | const note = { subject, content };
21 | logger.info('saving note', note);
22 |
23 | const key = this.getS3Key();
24 | Storage.put(key, JSON.stringify(note), { contentType: 'application/json' })
25 | .then(data => this.uploadNoteSuccess(data))
26 | .catch(err => this.uploadNoteError(err));
27 | }
28 |
29 | uploadNoteSuccess(data) {
30 | logger.info('upload note success', data);
31 | const { onUploaded } = this.props;
32 | if (onUploaded) { onUploaded(data.key); }
33 | }
34 |
35 | uploadNoteError(err) {
36 | logger.info('upload note error', err);
37 | }
38 |
39 | getS3Key() {
40 | if (!this.inputs.s3Key) {
41 | this.inputs.s3Key = this.props.path + new Date().getTime() + '.json';
42 | }
43 | return this.inputs.s3Key;
44 | }
45 |
46 | render() {
47 | return (
48 |
49 |
50 | this.inputs.subject = event.target.value}
56 | />
57 | this.inputs.content = event.target.value}
62 | />
63 |
64 |
65 |
66 |
67 |
68 | )
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/step-07/journal/src/components/album/index.js:
--------------------------------------------------------------------------------
1 | export { default as Album } from './Album';
2 |
--------------------------------------------------------------------------------
/step-07/journal/src/components/auth/JSignOut.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Button } from 'bootstrap-4-react';
3 | import { Auth, Logger } from 'aws-amplify';
4 |
5 | const logger = new Logger('JSignOut');
6 |
7 | export default class JSignOut extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.signOut = this.signOut.bind(this);
11 | }
12 |
13 | signOut() {
14 | Auth.signOut()
15 | .then(() => logger.info('sign out success'))
16 | .catch(err => logger.info('sign out error', err));
17 | }
18 |
19 | render() {
20 | return (
21 |
31 | )
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/step-07/journal/src/components/auth/index.js:
--------------------------------------------------------------------------------
1 | export { default as JSignOut } from './JSignOut';
2 | export { default as JSignIn } from './JSignIn';
3 | export { default as JConfirmSignIn } from './JConfirmSignIn';
4 | export { default as JSignUp } from './JSignUp';
5 | export { default as JConfirmSignUp } from './JConfirmSignUp';
6 | export { default as JForgotPassword } from './JForgotPassword';
7 | export { default as JForgotPasswordReset } from './JForgotPasswordReset';
8 |
--------------------------------------------------------------------------------
/step-07/journal/src/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as Navigator } from './Navigator';
2 | export { default as Main } from './Main';
3 | export { default as WhichDay } from './WhichDay';
4 |
5 | export { default as Unexpected } from './Unexpected';
6 | export { default as Unauthorized } from './Unauthorized';
7 |
--------------------------------------------------------------------------------
/step-07/journal/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/step-07/journal/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(, document.getElementById('root'));
6 |
--------------------------------------------------------------------------------
/step-07/journal/src/pages/Home.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | import { Unexpected, Unauthorized, WhichDay } from '../components';
4 | import { Album } from '../components/album';
5 |
6 | const padding = n => {
7 | return n > 9 ? n : '0' + n;
8 | }
9 |
10 | const today = () => {
11 | const dt = new Date();
12 | return [
13 | dt.getFullYear(),
14 | padding(dt.getMonth() + 1),
15 | padding(dt.getDate())
16 | ].join('-');
17 | }
18 |
19 | const album_path = (user_id, day) => {
20 | return user_id + '/' + day + '/';
21 | }
22 |
23 | export default class Home extends Component {
24 | constructor(props) {
25 | super(props);
26 | this.state = { day: today() }
27 | }
28 |
29 | render() {
30 | const { user } = this.props;
31 | const { day } = this.state;
32 |
33 | if (!user) { return }
34 | if (!user.id) { return }
35 |
36 | return (
37 |
38 | this.setState({ day: day })} />
39 |
40 |
41 | )
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/step-07/journal/src/pages/Login.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Lead, BSpan } from 'bootstrap-4-react';
3 | import { Authenticator } from 'aws-amplify-react';
4 |
5 | import {
6 | JSignIn,
7 | JConfirmSignIn,
8 | JSignUp,
9 | JConfirmSignUp,
10 | JForgotPassword,
11 | JForgotPasswordReset
12 | } from '../components/auth';
13 |
14 | const CustomAuthenticator = props => (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | )
24 |
25 | export default class Login extends Component {
26 | render() {
27 | const { user } = this.props;
28 |
29 | return (
30 |
31 | { !user && }
32 | { user && You are signed in as {user.username}. }
33 |
34 | )
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/step-07/journal/src/pages/index.js:
--------------------------------------------------------------------------------
1 | export { default as Home } from './Home';
2 | export { default as Login } from './Login';
3 | export { default as Profile } from './Profile';
4 |
--------------------------------------------------------------------------------
/step-07/journal/src/store/actions.js:
--------------------------------------------------------------------------------
1 | const SWITCH_USER = 'SWITCH_USER';
2 |
3 | const UPDATE_PROFILE = 'UPDATE_PROFILE';
4 | const DELETE_PROFILE = 'DELETE_PROFILE';
5 |
6 | // when user sign in / out
7 | function switchUser(user) {
8 | return {
9 | type: SWITCH_USER,
10 | user
11 | }
12 | }
13 |
14 | // when user updates profile
15 | function updateProfile(profile) {
16 | return {
17 | type: UPDATE_PROFILE,
18 | profile
19 | }
20 | }
21 |
22 | // when user sign out
23 | function deleteProfile() {
24 | return { type: DELETE_PROFILE }
25 | }
26 |
27 | export { SWITCH_USER, UPDATE_PROFILE, DELETE_PROFILE }
28 | export { switchUser, updateProfile, deleteProfile }
29 |
--------------------------------------------------------------------------------
/step-07/journal/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore } from 'redux';
2 |
3 | import { updateProfile } from './actions';
4 | import Journal from './reducers';
5 |
6 | const store = createStore(Journal);
7 |
8 | export default store;
9 | export {
10 | updateProfile
11 | }
12 | export { default as AmplifyBridge } from './AmplifyBridge';
13 |
--------------------------------------------------------------------------------
/step-07/journal/src/store/reducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 |
3 | import { SWITCH_USER, UPDATE_PROFILE, DELETE_PROFILE } from './actions';
4 |
5 | function user(state={}, action) {
6 | switch(action.type) {
7 | case SWITCH_USER:
8 | return action.user;
9 | default:
10 | return state;
11 | }
12 | }
13 |
14 | function profile(state={}, action) {
15 | switch(action.type) {
16 | case UPDATE_PROFILE:
17 | return Object.assign(
18 | {},
19 | state,
20 | action.profile
21 | );
22 | case DELETE_PROFILE:
23 | return null;
24 | default:
25 | return state;
26 | }
27 | }
28 |
29 | const Journal = combineReducers({
30 | user,
31 | profile
32 | });
33 |
34 | export default Journal;
35 |
--------------------------------------------------------------------------------
/step-08/README.md:
--------------------------------------------------------------------------------
1 | # Step 08 - Go Live
2 |
3 | Now let's finalize and go live
4 |
5 | * [1. Touch Ups](#1-touch-ups)
6 | * [2. Build](#2-build)
7 | * [3. Publish](#3-publish)
8 |
9 | ## 1. Touch Ups
10 |
11 | Change icon, app title, and some small updates.
12 |
13 | Not necessary to explain details in this step. Just check out the code should be fine.
14 |
15 | ## 2. Build
16 |
17 | ```
18 | npm run build
19 | ```
20 |
21 | The command generate everything needed into `build/` folder.
22 |
23 | Remember to set `homepage` in your `package.json` if publishing to a [relative path](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#building-for-relative-paths)
24 |
25 | ## 3. Publish
26 |
27 | This app is built completely in HTML/JS/CSS, no backend server needed. Just a static file hosting will do. Here we choose Amazon S3.
28 |
29 | In [Step 02](../step-02) we created an AWS Mobile Hub project. During that step we have created everything that we needed for this app.
30 |
31 | Go to AWS Console -> Mobile Hub. Click `Resources` on the top-right corner. Look for "Amazon S3 Buckets", then find the bucket that has `hosting` inside its name.
32 |
33 | Upload files under `build/` folder to this bucket. Then open in browser. We are [live!](https://s3-us-west-1.amazonaws.com/journal-hosting-mobilehub-142591078/index.html)
34 |
35 |
36 |
--------------------------------------------------------------------------------
/step-08/journal/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/step-08/journal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "journal",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "aws-amplify": "^1.1.1",
7 | "aws-amplify-react": "^2.0.2",
8 | "bootstrap-4-react": "0.0.54",
9 | "react": "^16.5.2",
10 | "react-dom": "^16.5.2",
11 | "react-router-dom": "^4.3.1",
12 | "react-scripts": "1.1.5",
13 | "redux": "^4.0.0"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test --env=jsdom",
19 | "eject": "react-scripts eject"
20 | },
21 | "homepage": "https://s3-us-west-1.amazonaws.com/journal-hosting-mobilehub-142591078"
22 | }
23 |
--------------------------------------------------------------------------------
/step-08/journal/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-08/journal/public/favicon.ico
--------------------------------------------------------------------------------
/step-08/journal/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
23 | Journal
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
35 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/step-08/journal/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Bootstrap 4 React",
3 | "name": "Bootstrap 4 React Documentation",
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 |
--------------------------------------------------------------------------------
/step-08/journal/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 5rem;
3 | }
4 | .starter-template {
5 | padding: 3rem 1.5rem;
6 | text-align: center;
7 | }
8 |
--------------------------------------------------------------------------------
/step-08/journal/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Amplify from 'aws-amplify';
3 |
4 | import { Navigator, Main } from './components';
5 | import './App.css';
6 | import aws_exports from './aws-exports';
7 |
8 | import store, { AmplifyBridge } from './store';
9 |
10 | Amplify.Logger.LOG_LEVEL = 'INFO'; // We write INFO level logs throughout app
11 | Amplify.configure(aws_exports);
12 |
13 | new AmplifyBridge(store);
14 |
15 | class App extends Component {
16 | render() {
17 | return (
18 |
19 |
20 |
21 |
22 | );
23 | }
24 | }
25 |
26 | export default App;
27 |
--------------------------------------------------------------------------------
/step-08/journal/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/step-08/journal/src/components/Main.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Container } from 'bootstrap-4-react';
3 | import { HashRouter, Route, Switch } from 'react-router-dom';
4 | import { Logger } from 'aws-amplify';
5 |
6 | import store from '../store';
7 | import { Home, Profile, Login } from '../pages';
8 |
9 | const logger = new Logger('Main');
10 |
11 | export default class Main extends Component {
12 | constructor(props) {
13 | super(props);
14 |
15 | this.storeListener = this.storeListener.bind(this);
16 |
17 | this.state = { user: null }
18 | }
19 |
20 | componentDidMount() {
21 | this.unsubscribeStore = store.subscribe(this.storeListener);
22 | }
23 |
24 | componentWillUnmount() {
25 | this.unsubscribeStore();
26 | }
27 |
28 | storeListener() {
29 | logger.info('redux notification');
30 | this.setState({ user: store.getState().user });
31 | }
32 |
33 | render() {
34 | const { user } = this.state;
35 |
36 | return (
37 |
38 |
39 |
40 |
41 | }
45 | />
46 | }
50 | />
51 | }
55 | />
56 |
57 |
58 |
59 |
60 | )
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/step-08/journal/src/components/Unauthorized.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Container } from 'bootstrap-4-react';
3 |
4 | export default class Unauthorized extends Component {
5 | render() {
6 | return (
7 |
8 | Not Authenticated
9 |
10 | )
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/step-08/journal/src/components/Unexpected.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Container } from 'bootstrap-4-react';
3 |
4 | export default class Unexpected extends Component {
5 | render() {
6 | return (
7 |
8 | Unexpected error happened
9 |
10 | )
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/step-08/journal/src/components/WhichDay.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Row, Col, BH4, Dropdown } from 'bootstrap-4-react';
3 | import { Storage, Logger } from 'aws-amplify';
4 |
5 | const padding = n => {
6 | return n > 9 ? n : '0' + n;
7 | }
8 |
9 | const today = () => {
10 | const dt = new Date();
11 | return [
12 | dt.getFullYear(),
13 | padding(dt.getMonth() + 1),
14 | padding(dt.getDate())
15 | ].join('-');
16 | }
17 |
18 | const logger = new Logger('WhichDay');
19 |
20 | export default class WhichDay extends Component {
21 | constructor(props) {
22 | super(props);
23 | this.load = this.load.bind(this);
24 | this.setDay = this.setDay.bind(this);
25 | this.state = { day: today(), days: [today()] }
26 | }
27 |
28 | componentDidMount() {
29 | this.load();
30 | }
31 |
32 | load() {
33 | const { rootPath } = this.props;
34 | Storage.list(rootPath)
35 | .then(data => this.loadSuccess(data))
36 | .catch(err => this.loadError(err));
37 | }
38 |
39 | loadSuccess(data) {
40 | logger.info('load list success', data);
41 | const days = data.map(item => {
42 | const match = item.key.match(/\/(\d{4}-\d{1,2}-\d{1,2})\//);
43 | return match? match[1] : null;
44 | })
45 | .filter (day => !!day);
46 | const day_set = new Set([today()].concat(days));
47 | const unique_days = Array.from(day_set).sort().reverse();
48 | this.setState({ days: unique_days });
49 | }
50 |
51 | loadError(err) {
52 | logger.info('load list error', err);
53 | }
54 |
55 | setDay(day) {
56 | this.setState({ day: day });
57 |
58 | const { onDaySelected } = this.props;
59 | if (onDaySelected) { onDaySelected(day); }
60 | }
61 |
62 | render() {
63 | const { day, days } = this.state;
64 |
65 | return (
66 |
67 |
68 | {day}
69 |
70 |
71 |
72 | {day}
73 |
74 | { days.map(day => {
75 | return (
76 | this.setDay(day)}
80 | >
81 | {day}
82 |
83 | )
84 | })}
85 |
86 |
87 |
88 |
89 | )
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/step-08/journal/src/components/album/AlbumItem.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { BImg, BPre, BH5, BDiv } from 'bootstrap-4-react';
3 | import { Storage, Logger } from 'aws-amplify';
4 |
5 | const logger = new Logger('AlbumItem');
6 |
7 | const Note = props => {
8 | const note = JSON.parse(props.json);
9 | return (
10 |
11 | {note.subject}
12 |
13 | {note.content}
14 |
15 |
16 | )
17 | }
18 |
19 | export default class AlbumItem extends Component {
20 | constructor(props) {
21 | super(props);
22 | this.load = this.load.bind(this);
23 | this.handleClick = this.handleClick.bind(this);
24 |
25 | this.state = { url: null, json: null }
26 | }
27 |
28 | componentDidMount() {
29 | this.load();
30 | }
31 |
32 | load() {
33 | const { item } = this.props;
34 | if (item.key.endsWith('.json')) {
35 | Storage.get(item.key, { download: true })
36 | .then(data => this.loadJsonSuccess(data))
37 | .catch(err => this.loadError(err));
38 | } else {
39 | Storage.get(item.key)
40 | .then(url => this.loadImageSuccess(url))
41 | .catch(err => this.loadError(err));
42 | }
43 | }
44 |
45 | loadJsonSuccess(data) {
46 | logger.info('load album item success', data);
47 | this.setState({ json: data.Body.toString('utf8') });
48 | }
49 |
50 | loadImageSuccess(url) {
51 | logger.info('load album item success', url);
52 | this.setState({ url: url });
53 | }
54 |
55 | loadError(err) {
56 | logger.info('load album item error', err);
57 | }
58 |
59 | handleClick() {
60 | const { item, onClick } = this.props;
61 | if (onClick) {
62 | onClick(item.key);
63 | }
64 | }
65 |
66 | render() {
67 | const { url, json } = this.state;
68 |
69 | return (
70 |
71 | { json && }
72 | { url && }
73 |
74 | )
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/step-08/journal/src/components/album/FilePicker.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Form, Button } from 'bootstrap-4-react';
3 | import { Storage, Logger } from 'aws-amplify';
4 |
5 | const logger = new Logger('FilePicker');
6 |
7 | const style = {
8 | position: 'relative',
9 | height: '3rem',
10 | button: {
11 | width: '100%',
12 | height: '100%',
13 | display: 'inline-block',
14 | position: 'absolute',
15 | left: 0,
16 | top: 0
17 | },
18 | file: {
19 | width: '100%',
20 | height: '100%',
21 | display: 'inline-block',
22 | position: 'absolute',
23 | left: 0,
24 | top: 0,
25 | opacity: 0,
26 | cursor: 'pointer'
27 | }
28 | }
29 |
30 | export default class FilePicker extends Component {
31 | constructor(props) {
32 | super(props);
33 | this.handleChange = this.handleChange.bind(this);
34 | }
35 |
36 | handleChange(event) {
37 | const file = event.target.files[0];
38 | logger.info('picked file', file);
39 | // Clear file input
40 | window.document.getElementById('fileInputForm').reset();
41 |
42 | this.uploadFile(file);
43 | }
44 |
45 | uploadFile(file) {
46 | const { path } = this.props;
47 | if (!path) {
48 | logger.warn('missing path property for FilePicker');
49 | return;
50 | }
51 |
52 | const key = this.calcS3Key(file);
53 | const { type } = file;
54 | Storage.put(key, file, { contentType: type })
55 | .then(data => this.uploadFileSuccess(data))
56 | .catch(err => this.uploadFileError(err));
57 | }
58 |
59 | uploadFileSuccess(data) {
60 | logger.info('upload file success', data);
61 | const { onUploaded } = this.props;
62 | if (onUploaded) { onUploaded(data.key); }
63 | }
64 |
65 | uploadFileError(err) {
66 | logger.info('upload file error', err);
67 | }
68 |
69 | calcS3Key(file) {
70 | return this.props.path + encodeURI(file.name).replace(/\s/g, '_');
71 | }
72 |
73 | render() {
74 | return (
75 |
76 |
83 |
84 |
85 | )
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/step-08/journal/src/components/album/index.js:
--------------------------------------------------------------------------------
1 | export { default as Album } from './Album';
2 |
--------------------------------------------------------------------------------
/step-08/journal/src/components/auth/JSignOut.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Button } from 'bootstrap-4-react';
3 | import { Auth, Logger } from 'aws-amplify';
4 |
5 | const logger = new Logger('JSignOut');
6 |
7 | export default class JSignOut extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.signOut = this.signOut.bind(this);
11 | }
12 |
13 | signOut() {
14 | Auth.signOut()
15 | .then(() => logger.info('sign out success'))
16 | .catch(err => logger.info('sign out error', err));
17 | }
18 |
19 | render() {
20 | return (
21 |
31 | )
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/step-08/journal/src/components/auth/index.js:
--------------------------------------------------------------------------------
1 | export { default as JSignOut } from './JSignOut';
2 | export { default as JSignIn } from './JSignIn';
3 | export { default as JConfirmSignIn } from './JConfirmSignIn';
4 | export { default as JSignUp } from './JSignUp';
5 | export { default as JConfirmSignUp } from './JConfirmSignUp';
6 | export { default as JForgotPassword } from './JForgotPassword';
7 | export { default as JForgotPasswordReset } from './JForgotPasswordReset';
8 |
--------------------------------------------------------------------------------
/step-08/journal/src/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as Navigator } from './Navigator';
2 | export { default as Main } from './Main';
3 | export { default as WhichDay } from './WhichDay';
4 |
5 | export { default as Unexpected } from './Unexpected';
6 | export { default as Unauthorized } from './Unauthorized';
7 |
--------------------------------------------------------------------------------
/step-08/journal/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: sans-serif;
5 | }
6 |
--------------------------------------------------------------------------------
/step-08/journal/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | ReactDOM.render(, document.getElementById('root'));
6 |
--------------------------------------------------------------------------------
/step-08/journal/src/pages/Home.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | import { Unexpected, Unauthorized, WhichDay } from '../components';
4 | import { Album } from '../components/album';
5 |
6 | const padding = n => {
7 | return n > 9 ? n : '0' + n;
8 | }
9 |
10 | const today = () => {
11 | const dt = new Date();
12 | return [
13 | dt.getFullYear(),
14 | padding(dt.getMonth() + 1),
15 | padding(dt.getDate())
16 | ].join('-');
17 | }
18 |
19 | const album_path = (user_id, day) => {
20 | return user_id + '/' + day + '/';
21 | }
22 |
23 | export default class Home extends Component {
24 | constructor(props) {
25 | super(props);
26 | this.state = { day: today() }
27 | }
28 |
29 | render() {
30 | const { user } = this.props;
31 | const { day } = this.state;
32 |
33 | if (!user) { return }
34 | if (!user.id) { return }
35 |
36 | return (
37 |
38 | this.setState({ day: day })} />
39 |
40 |
41 | )
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/step-08/journal/src/pages/Login.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Lead, BSpan } from 'bootstrap-4-react';
3 | import { Authenticator } from 'aws-amplify-react';
4 |
5 | import {
6 | JSignIn,
7 | JConfirmSignIn,
8 | JSignUp,
9 | JConfirmSignUp,
10 | JForgotPassword,
11 | JForgotPasswordReset
12 | } from '../components/auth';
13 |
14 | const CustomAuthenticator = props => (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | )
24 |
25 | export default class Login extends Component {
26 | render() {
27 | const { user } = this.props;
28 |
29 | return (
30 |
31 | { !user && }
32 | { user && You are signed in as {user.username}. }
33 |
34 | )
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/step-08/journal/src/pages/index.js:
--------------------------------------------------------------------------------
1 | export { default as Home } from './Home';
2 | export { default as Login } from './Login';
3 | export { default as Profile } from './Profile';
4 |
--------------------------------------------------------------------------------
/step-08/journal/src/store/actions.js:
--------------------------------------------------------------------------------
1 | const SWITCH_USER = 'SWITCH_USER';
2 |
3 | const UPDATE_PROFILE = 'UPDATE_PROFILE';
4 | const DELETE_PROFILE = 'DELETE_PROFILE';
5 |
6 | // when user sign in / out
7 | function switchUser(user) {
8 | return {
9 | type: SWITCH_USER,
10 | user
11 | }
12 | }
13 |
14 | // when user updates profile
15 | function updateProfile(profile) {
16 | return {
17 | type: UPDATE_PROFILE,
18 | profile
19 | }
20 | }
21 |
22 | // when user sign out
23 | function deleteProfile() {
24 | return { type: DELETE_PROFILE }
25 | }
26 |
27 | export { SWITCH_USER, UPDATE_PROFILE, DELETE_PROFILE }
28 | export { switchUser, updateProfile, deleteProfile }
29 |
--------------------------------------------------------------------------------
/step-08/journal/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore } from 'redux';
2 |
3 | import { updateProfile } from './actions';
4 | import Journal from './reducers';
5 |
6 | const store = createStore(Journal);
7 |
8 | export default store;
9 | export {
10 | updateProfile
11 | }
12 | export { default as AmplifyBridge } from './AmplifyBridge';
13 |
--------------------------------------------------------------------------------
/step-08/journal/src/store/reducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 |
3 | import { SWITCH_USER, UPDATE_PROFILE, DELETE_PROFILE } from './actions';
4 |
5 | function user(state={}, action) {
6 | switch(action.type) {
7 | case SWITCH_USER:
8 | return action.user;
9 | default:
10 | return state;
11 | }
12 | }
13 |
14 | function profile(state={}, action) {
15 | switch(action.type) {
16 | case UPDATE_PROFILE:
17 | return Object.assign(
18 | {},
19 | state,
20 | action.profile
21 | );
22 | case DELETE_PROFILE:
23 | return null;
24 | default:
25 | return state;
26 | }
27 | }
28 |
29 | const Journal = combineReducers({
30 | user,
31 | profile
32 | });
33 |
34 | export default Journal;
35 |
--------------------------------------------------------------------------------
/step-08/seattle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/step-08/seattle.png
--------------------------------------------------------------------------------
/website/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | build/
4 | yarn.lock
5 | **/node_modules
6 | searchConfig.js
7 |
--------------------------------------------------------------------------------
/website/blog/2018-01-08-why-dochameleon.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Why Dochameleon
3 | author: Richard Zhang
4 | authorGitHub: richardzcode
5 | ---
6 |
7 | It is not hard to guess where the idea of Dochameleon is from.
8 |
9 | With [Docusaurus](https://github.com/facebook/Docusaurus) built and maintained by awesome team from Facebook, why do I want to build another one?
10 |
11 | One personal reason is I am so excited about Docusaurus. Can't stop checking around, rewrite, restruct source code. So only way to satisfy is to create a new project.
12 |
13 |
14 |
15 | Here are some technical reasons,
16 |
17 | 1. Dev server and site generation are written separately. Inconsistency is inevitable.
18 | 2. Pages can not share building blocks.
19 | 3. Big CSS file makes styling hard.
20 |
21 | At the same time some features are removed. I feel they are a bit too opinionated with complexity, may not suited for all open source projects.
22 |
23 | 1. Search with [algolia](https://www.algolia.com/).
24 | 2. Multi-Language with [crowdin](https://crowdin.com/).
25 | 3. Project version support.
26 |
27 | For example, at the end of the day multi-language and versioning are grouped by file hierarchies. One options is to just take pull requests on GitHub.
28 |
--------------------------------------------------------------------------------
/website/blog/2018-01-10-staging-step.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Staging Step
3 | author: Richard Zhang
4 | authorGitHub: richardzcode
5 | ---
6 |
7 | The basic idea of Dochameleon is to generate a static web server by using [Server-Side-Rendering](https://reactjs.org/docs/react-dom-server.html). Contents are combined from core library and specific project.
8 |
9 | Combining from two sources can be tricky. So there is a staging step. Which in essence copies files from the two sources into one place, for SSR to generate from, and for developers to inspect on.
10 |
11 | The one place can be found at `node_modules/dochameleon/lib/stage`, contains a file structure like this:
12 |
13 | ```bash
14 | node_modules/dochameleon/lib/stage/
15 | ├── Blog.js
16 | ├── Docs.js
17 | ├── Pages.js
18 | ├── blog
19 | │ └── 2018-01-08-why-dochameleon.md
20 | │ └── 2018-01-10-stage-step.md
21 | ├── components
22 | │ ├── Button.js
23 | │ ├── CollapseIcon.js
24 | │ ├── FeatureCallout.js
25 | │ ├── FeatureCallouts.js
26 | │ ├── Features.js
27 | │ ├── Footer.js
28 | │ ├── Head.js
29 | │ ├── HeaderNav.js
30 | │ ├── HelpDetails.js
31 | │ ├── HomeSplash.js
32 | │ ├── MarkdownBlock.js
33 | │ ├── Page.js
34 | │ ├── Showcase.js
35 | │ ├── SideNav.js
36 | │ ├── blog
37 | │ ├── docs
38 | │ ├── featureCallouts.json
39 | │ ├── features.json
40 | │ ├── help.json
41 | │ └── users.json
42 | ├── dfs.js
43 | ├── docs
44 | │ ├── doc1.md
45 | │ ├── doc2.md
46 | │ ├── doc3.md
47 | │ ├── exampledoc4.md
48 | │ ├── exampledoc5.md
49 | │ └── sidebars.json
50 | ├── pages
51 | │ ├── help.js
52 | │ ├── index.js
53 | │ └── users.js
54 | ├── parse
55 | │ ├── Markdown.js
56 | │ └── toSlug.js
57 | ├── static
58 | │ ├── css
59 | │ └── img
60 | └── theme
61 | ├── blog.js
62 | ├── main.js
63 | ├── markdown.js
64 | └── pages.js
65 | ```
66 |
--------------------------------------------------------------------------------
/website/components/docs/DocsLayout.js:
--------------------------------------------------------------------------------
1 | const React = require('react');
2 | const { Row, Col } = require('fluid-react');
3 |
4 | const Page = require('../Page.js');
5 | const Button = require('../Button.js');
6 | const Doc = require('./Doc.js');
7 | const DocsSidebar = require('./DocsSidebar.js');
8 |
9 | const PrevNext = props => {
10 | const { site, metadata, lang } = props;
11 | const { theme } = site;
12 | if (!metadata.previous && !metadata.next) { return null; }
13 |
14 | return (
15 |
16 | {metadata.previous && (
17 |
20 | )}
21 |
22 | {metadata.next && (
23 |
26 | )}
27 |
28 | )
29 | }
30 |
31 | class DocsLayout extends React.Component {
32 | render() {
33 | const { site, metadata, lang } = this.props;
34 | const { theme } = site;
35 | const { title, brief } = metadata;
36 | const content = this.props.children;
37 | const url = site.urlWithRoot(site.docUrl(metadata.id));
38 |
39 | return (
40 |
41 |
42 | { metadata.sidebar
43 | ? (
44 |
45 |
46 |
47 |
48 |
49 | {content}
50 |
51 |
52 |
53 | )
54 | : (
55 |
56 | {content}
57 |
58 |
59 | )
60 | }
61 |
62 |
63 | );
64 | }
65 | }
66 | module.exports = DocsLayout;
67 |
--------------------------------------------------------------------------------
/website/components/headerLinks.json:
--------------------------------------------------------------------------------
1 | [
2 | { "type": "doc", "value": "step-01-cra", "label": "Steps" },
3 | {
4 | "type": "url",
5 | "value": "https://github.com/richardzcode/Journal-AWS-Amplify-Tutorial",
6 | "img": "img/github.png",
7 | "label": "GitHub"
8 | }
9 | ]
10 |
--------------------------------------------------------------------------------
/website/docs/assets/img/authenticator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/website/docs/assets/img/authenticator.png
--------------------------------------------------------------------------------
/website/docs/assets/img/daily_journal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/website/docs/assets/img/daily_journal.png
--------------------------------------------------------------------------------
/website/docs/assets/img/download_aws_exports.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/website/docs/assets/img/download_aws_exports.png
--------------------------------------------------------------------------------
/website/docs/assets/img/host_and_streaming.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/website/docs/assets/img/host_and_streaming.png
--------------------------------------------------------------------------------
/website/docs/assets/img/journal_history.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/website/docs/assets/img/journal_history.png
--------------------------------------------------------------------------------
/website/docs/assets/img/live.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/website/docs/assets/img/live.png
--------------------------------------------------------------------------------
/website/docs/assets/img/login_form.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/website/docs/assets/img/login_form.png
--------------------------------------------------------------------------------
/website/docs/assets/img/welcome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/website/docs/assets/img/welcome.png
--------------------------------------------------------------------------------
/website/docs/sidebars.json:
--------------------------------------------------------------------------------
1 | {
2 | "Steps": {
3 | "Step 01 - Create a Basic React App": [
4 | "step-01-cbra",
5 | "step-01-react-router",
6 | "step-01-navigator",
7 | "step-01-routing",
8 | "step-01-home-n-login",
9 | "step-01-run-app"
10 | ],
11 | "Step 02 - Authentication": [
12 | "step-02-prepare",
13 | "step-02-configure",
14 | "step-02-authenticator",
15 | "step-02-greetings",
16 | "step-02-aware-of-authstate",
17 | "step-02-run-app"
18 | ],
19 | "Step 03 - Authentication UI": [
20 | "step-03-replace-sign-in",
21 | "step-03-login-form",
22 | "step-03-federated",
23 | "step-03-check-contact",
24 | "step-03-sign-up",
25 | "step-03-replace-all",
26 | "step-03-run-app"
27 | ],
28 | "Step 04 - Everyday Journal": [
29 | "step-04-user-info",
30 | "step-04-daily-album",
31 | "step-04-write-text",
32 | "step-04-refresh-album",
33 | "step-04-run-app"
34 | ],
35 | "Step 05 - List of Journals": [
36 | "step-05-get-list",
37 | "step-05-display-dates",
38 | "step-05-switch-date",
39 | "step-05-run-app"
40 | ],
41 | "Step 06 - Go Live": [
42 | "step-06-touch-ups",
43 | "step-06-build",
44 | "step-06-publish"
45 | ]
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/website/docs/step-01-cbra.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-01-cbra
3 | title: Step 01 - Create a Basic React App with Bootstrap
4 | sidebar_label: Create a Bootstrap React App
5 | ---
6 |
7 | # Create Bootstrap React App
8 |
9 | `create-bootstrap-react-app` creates basic React App with a Bootstrap starter template.
10 |
11 | If not already installed, run
12 | ```
13 | npm install --global create-react-app
14 | npm install --global create-bootstrap-react-app
15 | ```
16 |
17 | Then create the app
18 | ```
19 | create-bootstrap-react-app journal
20 | cd journal
21 | npm start
22 | ```
23 |
--------------------------------------------------------------------------------
/website/docs/step-01-home-n-login.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-01-home-n-login
3 | title: Step 01 - Create a Basic React App with Bootstrap
4 | sidebar_label: Home and Login Page
5 | ---
6 |
7 | ## 5. Create Home and Login Page
8 |
9 | We don't have Home and Login page yet. Let's create them.
10 |
11 | `src/pages/Home.jsx`
12 | ```
13 | import React, { Component } from 'react';
14 |
15 | export default class Home extends Component {
16 | render() {
17 | return (
18 | Home
19 | )
20 | }
21 | }
22 | ```
23 |
24 | `src/pages/Login.jsx`
25 | ```
26 | import React, { Component } from 'react';
27 |
28 | export default class Home extends Component {
29 | render() {
30 | return (
31 | Login
32 | )
33 | }
34 | }
35 | ```
36 |
--------------------------------------------------------------------------------
/website/docs/step-01-navigator.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-01-navigator
3 | title: Step 01 - Create a Basic React App with Bootstrap
4 | sidebar_label: Adjust Navigator
5 | ---
6 |
7 | # Adjust Navigator
8 |
9 | We don't need everything from starter template. Let's adjust. Assume we start with a Home page and a Login page.
10 |
11 | Open `src/components/Navigator.jsx`
12 |
13 | * Update `` content to 'Journal'.
14 | * Remove 'Disabled' and 'Dropdown' menu items.
15 | * Update 'Link' to 'Login'.
16 | * Replace search with 'Greetings' text.
17 |
18 | With `react-router` components. `Navigator.jsx` become,
19 |
20 | ```
21 | import React, { Component } from 'react';
22 | import { Navbar, Nav, BSpan } from 'bootstrap-4-react';
23 | import { HashRouter, Route, Switch } from 'react-router-dom';
24 |
25 | const HomeItems = props => (
26 |
27 |
28 | Home
29 | (current}
30 |
31 |
32 | Login
33 |
34 |
35 | )
36 |
37 | const LoginItems = props => (
38 |
39 |
40 | Home
41 |
42 |
43 | Login
44 | (current}
45 |
46 |
47 | )
48 |
49 | export default class Navigator extends Component {
50 | render() {
51 | return (
52 |
53 | Journal
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | Greetings
66 |
67 |
68 | )
69 | }
70 | }
71 | ```
72 |
--------------------------------------------------------------------------------
/website/docs/step-01-react-router.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-01-react-router
3 | title: Step 01 - Create a Basic React App with Bootstrap
4 | sidebar_label: Add React Router
5 | ---
6 |
7 | # Add React Router
8 | Let's use [react-router](https://github.com/ReactTraining/react-router) for routing.
9 | ```
10 | npm install --save react-router-dom
11 | ```
12 |
--------------------------------------------------------------------------------
/website/docs/step-01-routing.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-01-routing
3 | title: Step 01 - Create a Basic React App with Bootstrap
4 | sidebar_label: Page Routing
5 | ---
6 |
7 | # Page Routing
8 |
9 | Modify `src/components/Main.jsx` to route to Home or Login page.
10 |
11 | ```
12 | import React, { Component } from 'react';
13 | import { Container } from 'bootstrap-4-react';
14 | import { HashRouter, Route, Switch } from 'react-router-dom';
15 |
16 | import { Home, Login } from '../pages';
17 |
18 | export default class Main extends Component {
19 | render() {
20 | return (
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | )
32 | }
33 | }
34 | ```
35 |
--------------------------------------------------------------------------------
/website/docs/step-01-run-app.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-01-run-app
3 | title: Step 01 - Create a Basic React App
4 | sidebar_label: Run App
5 | ---
6 |
7 | # Run App
8 |
9 | ```
10 | npm start
11 | ```
12 |
--------------------------------------------------------------------------------
/website/docs/step-01-semantic-ui-react.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-01-semantic-ui-react
3 | title: Step 01 - Create a Basic React App
4 | sidebar_label: Add Semantic UI React
5 | ---
6 |
7 | # Add Semantic UI React
8 |
9 | Let's use [Semantic UI React](https://react.semantic-ui.com) for nicer looking UI.
10 | ```
11 | npm install --save semantic-ui-react
12 | ```
13 |
14 | There are a few ways to add CSS for Semantic UI, here is one way:
15 | ```
16 | npm install --save semantic-ui-css
17 | ```
18 |
19 | Then open `src/index.js`, add
20 | ```
21 | import 'semantic-ui-css/semantic.min.css';
22 | ```
23 |
--------------------------------------------------------------------------------
/website/docs/step-02-authenticator.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-02-authenticator
3 | title: Step 02 - Authentication
4 | sidebar_label: Add Authenticator
5 | ---
6 |
7 | # Add Authenticator
8 |
9 | Open `src/modules/Login.jsx`, change content to:
10 | ```
11 | import React, { Component } from 'react';
12 |
13 | import { Authenticator } from 'aws-amplify-react';
14 |
15 | export default class Login extends Component {
16 | render() {
17 | return
18 | }
19 | }
20 | ```
21 |
22 | Now `npm start`. Login becomes real
23 |
24 |
25 |
26 | Got ahead sign up and sign in. Create a test user.
27 |
--------------------------------------------------------------------------------
/website/docs/step-02-aware-of-authstate.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-02-aware-of-authstate
3 | title: Step 02 - Authentication
4 | sidebar_label: Home Page Aware of authState
5 | ---
6 |
7 | # Home Page Aware of authState
8 |
9 | Now sign in works. How does Home page know if an user is signed in or not?
10 |
11 | We have `Greetings` now. So just listen to its `onStateChange` event, then pass to Home component in router.
12 |
13 | In `src/App.js`
14 | ```
15 | constructor(props) {
16 | ...
17 | this.handleStateChange = this.handleStateChange.bind(this);
18 | }
19 |
20 | handleStateChange(authState, authData) {
21 | this.setState({
22 | authState: authState,
23 | authData: authData
24 | });
25 | }
26 |
27 |
28 | ```
29 |
30 | ```
31 | 'Hi ' + username}
35 | onStateChange={this.handleStateChange}
36 | />
37 | ```
38 |
39 | ```
40 | (
41 |
42 | )}/>
43 | ```
44 |
45 | Then, in `src/modules/Home.jsx`, just check `authState` property
46 | ```
47 | render() {
48 | const { authState, authData } = this.props;
49 | return (
50 |
51 |
52 |
{authState}
53 |
54 | );
55 | }
56 | ```
57 |
--------------------------------------------------------------------------------
/website/docs/step-02-configure.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-02-configure
3 | title: Step 02 - Authentication
4 | sidebar_label: Configure AWS Amplify
5 | ---
6 |
7 | # Configure AWS Amplify
8 |
9 | Open `src/App.js`, add these lines
10 | ```
11 | import Amplify from 'aws-amplify';
12 | import aws_exports from './aws-exports';
13 |
14 | Amplify.configure(aws_exports);
15 | ```
16 |
--------------------------------------------------------------------------------
/website/docs/step-02-greetings.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-02-greetings
3 | title: Step 02 - Authentication
4 | sidebar_label: Greetings
5 | ---
6 |
7 | # Greetings
8 |
9 | Notice after sign in, there is a sign out button. It makes sense to have sign out button. However in our case the place is not right.
10 |
11 | **Hide Greetings**
12 |
13 | Let's hide this one. `src/modules/Login.jsx` becomes:
14 | ```
15 | import React, { Component } from 'react';
16 |
17 | import { Authenticator, Greetings } from 'aws-amplify-react';
18 |
19 | export default class Login extends Component {
20 | render() {
21 | return
22 | }
23 | }
24 | ```
25 |
26 | `Authenticator` is composed of a group of pieces, `Greetings` is one of them. `hide` defines a list of pieces to be hidden.
27 |
28 | **Greetings on Menu**
29 |
30 | What we actually want is greetings on the top-right corner. Let's edit `src/App.js` to add menu item with Greetings.
31 |
32 | First import Greetings
33 | ```
34 | import { Greetings } from 'aws-amplify-react';
35 | ```
36 |
37 | The default styling doesn't fit in our UI, lets add the menu item and remove default theme of Greetings.
38 | ```
39 | const GreetingsTheme = {
40 | navBar: {
41 | },
42 | navRight: {
43 | },
44 | navButton: {
45 | border: '0',
46 | background: 'white',
47 | color: 'blue',
48 | borderBottom: '1px solid',
49 | fontSize: '0.8em'
50 | }
51 | }
52 |
53 | ...
54 |
55 |
56 |
57 |
58 |
59 |
60 | ```
61 |
62 | **Custom Greetings**
63 |
64 | Change the greetings
65 | ```
66 | 'Hi ' + username}
70 | />
71 | ```
72 |
73 |
74 |
--------------------------------------------------------------------------------
/website/docs/step-02-prepare.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-02-prepare
3 | title: Step 02 - Authentication
4 | sidebar_label: Prepare
5 | ---
6 |
7 | # Prepare
8 |
9 | ### Library
10 |
11 | Install package, core library and react specific.
12 | ```
13 | npm install --save aws-amplify
14 | npm install --save aws-amplify-react
15 | ```
16 |
17 | ### Service
18 |
19 | Create a AWS Mobile Hub project with [awsmobile-CLI](https://github.com/aws/awsmobile-cli)
20 |
21 | ```
22 | npm install -g awsmobile-cli
23 |
24 | awsmobile init
25 | awsmobile user-signin enable
26 | awsmobile user-files enable
27 | awsmobile push
28 | ```
29 |
--------------------------------------------------------------------------------
/website/docs/step-02-run-app.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-02-run-app
3 | title: Step 02 - Authentication
4 | sidebar_label: Run App
5 | ---
6 |
7 | # Run App
8 |
9 | ```
10 | npm start
11 | ```
12 |
--------------------------------------------------------------------------------
/website/docs/step-03-check-contact.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-03-check-contact
3 | title: Step 03 - Authentication UI
4 | sidebar_label: Check Contact Verification
5 | ---
6 |
7 | # Check Contact Verification
8 |
9 | User may forget password. In order to be able to recover password, user has to have one of the contact info verified. We should prompt user about this.
10 |
11 | Update `src/components/LoginForm`:
12 | ```
13 | constructor(props) {
14 | super(props);
15 |
16 | this.checkContat = this.checkContact.bind(this);
17 | this.signIn = this.signIn.bind(this);
18 | }
19 |
20 | checkContact(user) {
21 | Auth.verifiedContact(user)
22 | .then(data => {
23 | if (!JS.isEmpty(data.verified)) {
24 | this.changeState('signedIn', user);
25 | } else {
26 | user = Object.assign(user, data);
27 | this.changeState('verifyContact', user);
28 | }
29 | });
30 | }
31 |
32 | signIn() {
33 | const { username, password } = this.inputs;
34 | logger.debug('username: ' + username);
35 | Auth.signIn(username, password)
36 | .then(user => this.checkContact(user))
37 | .catch(err => this.error(err));
38 | }
39 | ```
40 |
--------------------------------------------------------------------------------
/website/docs/step-03-federated.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-03-federated
3 | title: Step 03 - Authentication UI
4 | sidebar_label: Federated Sign In
5 | ---
6 |
7 | # Federated Sign In
8 |
9 | `withFederated` HOC turns buttons into Federated sign in button.
10 |
11 | * Build UI
12 | - style your own button
13 | - trigger `props.facebookSignIn | props.googleSignIn | props.amazonSignIn` at the right time
14 | * Transform UI
15 | - withFederated(...)
16 | * Render UI
17 | - pass in `federated` property with app ids
18 | - handle `onStateChange` to notify sign in event
19 |
20 | ```
21 | import { AuthPiece, withFederated } from 'aws-amplify-react';
22 |
23 | const FederatedButtons = (props) => (
24 |
25 |
31 |
32 | );
33 |
34 | const Federated = withFederated(FederatedButtons);
35 |
36 | const federated_data = {
37 | google_client_id: '',
38 | facebook_app_id: '__replace_with_your_facebook_app_id__',
39 | amazon_client_id: ''
40 | };
41 |
42 | ...
43 |
44 | // in login form render method,
45 | // trigger AuthPiece.handleAuthStateChange when state changes, i.e. signed in
46 |
47 |
48 | ```
49 |
--------------------------------------------------------------------------------
/website/docs/step-03-replace-all.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-03-replace-all
3 | title: Step 03 - Authentication UI
4 | sidebar_label: Replace all Auth components
5 | ---
6 |
7 | # Replace all Auth components
8 |
9 | In process of replacing all Auth components. Here are a couple small things.
10 |
11 | **Semantic UI radio button**
12 |
13 | The radio button from Semantic UI fires `onChange` event on label as target. Which will cause an issue in collecting state. The actual radio button state is passed as the second parameter. We need to translate it before calling `this.handleInputChange`
14 |
15 | For example in `src/components/VerifyContactForm.js`
16 | ```
17 | this.handleInputChange({ target: semantic_data })}
21 | />
22 | this.handleInputChange({ target: semantic_data })}
26 | />
27 | ```
28 |
29 | **hideDefault**
30 |
31 | In order to replace default Auth forms, we provide `hide` list to `Authenticator`. Once all reaplaced, we could simply pass a `hideDefault` property, no need to write the whole list.
32 |
33 | ```
34 |
35 | ```
36 |
--------------------------------------------------------------------------------
/website/docs/step-03-replace-sign-in.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-03-replace-sign-in
3 | title: Step 03 - Authentication UI
4 | sidebar_label: Replace Sign In
5 | ---
6 |
7 | # Replace Sign In
8 |
9 | Let's add a Semantic UI [LoginForm](https://react.semantic-ui.com/layouts/login).
10 |
11 | Save the form to `src/components/LoginForm.js`.
12 |
13 | Then modify `src/modules/Login.jsx`, hide the default SignIn, add our LoginForm
14 | ```
15 | import { Authenticator, Greetings, SignIn } from 'aws-amplify-react';
16 | import { LoginForm } from '../components';
17 |
18 | render() {
19 | return (
20 |
21 |
22 |
23 | )
24 | }
25 | ```
26 | Now, looks better
27 |
28 |
29 |
--------------------------------------------------------------------------------
/website/docs/step-03-run-app.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-03-run-app
3 | title: Step 03 - Authentication UI
4 | sidebar_label: Run App
5 | ---
6 |
7 | # Run App
8 |
9 | ```
10 | npm start
11 | ```
12 |
--------------------------------------------------------------------------------
/website/docs/step-03-sign-up.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-03-sign-up
3 | title: Step 03 - Authentication UI
4 | sidebar_label: Sign Up
5 | ---
6 |
7 | # Sign Up
8 |
9 | LoginForm has a 'Sign Up' link. On click it should show Sign Up form.
10 |
11 | This can be achieved by `changeState()` method from `AuthPiece`. The method notifies `Authenticator` state change, and then `Authenticator` notify all the Auth Pieces it contains to render properly.
12 |
13 | ```
14 |
15 | New to us? this.changeState('signUp')}>Sign Up
16 |
17 | ```
18 |
19 | Now on click we'll see Sign Up form, but it is the default form. Go through the same process create a RegisterForm. Same to other UI components in auth flow.
20 |
--------------------------------------------------------------------------------
/website/docs/step-04-daily-album.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-04-daily-album
3 | title: Step 04 - Everyday Journal
4 | sidebar_label: Daily Album
5 | ---
6 |
7 | # Daily Album
8 |
9 | To keep it simple, we organize our journal base on datetime. One day one album. Journal contains image and text.
10 |
11 | This can be easily achieved with `S3Album` from `aws-amplify-react`
12 |
13 | Imports
14 | ```
15 | import { Container, Segment, Header } from 'semantic-ui-react';
16 | import { S3Album } from 'aws-amplify-react';
17 | ```
18 |
19 | Today as string
20 | ```
21 | const today = () => {
22 | const dt = new Date();
23 | return dt.getFullYear() + '-' + (dt.getMonth() + 1) + '-' + dt.getDate();
24 | }
25 | ```
26 |
27 | Render `S3Album` with userId and date as path in `memberView`
28 | ```
29 | memberView() {
30 | const { user } = this.state;
31 | if (!user) { return null; }
32 |
33 | const path = user.id + '/' + today() + '/';
34 | return (
35 |
36 |
37 |
38 |
39 |
40 |
41 | )
42 | }
43 | ```
44 |
--------------------------------------------------------------------------------
/website/docs/step-04-refresh-album.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-04-refresh-album
3 | title: Step 04 - Everyday Journal
4 | sidebar_label: Refresh Album
5 | ---
6 |
7 | # Refresh Album
8 |
9 | Notice on `S3Text.onLoad` we set a state `ts`. This is to tell `S3Album` to reload so new writing can be displayed in album.
10 |
11 | ```
12 |
13 | ```
14 |
--------------------------------------------------------------------------------
/website/docs/step-04-run-app.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-04-run-app
3 | title: Step 04 - Everyday Journal
4 | sidebar_label: Run App
5 | ---
6 |
7 | # Run App
8 |
9 | ```
10 | npm start
11 | ```
12 |
13 |
14 |
--------------------------------------------------------------------------------
/website/docs/step-04-user-info.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-04-user-info
3 | title: Step 04 - Everyday Journal
4 | sidebar_label: User Information
5 | ---
6 |
7 | # User Information
8 |
9 | First we need to make sure every user has his/her own space. Let's get user information first.
10 |
11 | Import `Auth`
12 | ```
13 | import { Auth } from 'aws-amplify';
14 | ```
15 |
16 | Get current user info
17 | ```
18 | componentDidMount() {
19 | Auth.currentUserInfo()
20 | .then(user => this.setState({ user: user })) // we need user.id
21 | .catch(err => console.log(err));
22 | }
23 | ```
24 |
--------------------------------------------------------------------------------
/website/docs/step-05-display-dates.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-05-display-dates
3 | title: Step 05 - List of Journals
4 | sidebar_label: Display Dates
5 | ---
6 |
7 | # Display Dates
8 |
9 | Convert to dropdown options
10 | ```
11 | const history = dates.map(date => {
12 | return {
13 | key: date,
14 | value: date,
15 | text: date
16 | };
17 | });
18 | ```
19 |
20 | Display
21 | ```
22 |
28 | ```
29 |
--------------------------------------------------------------------------------
/website/docs/step-05-get-list.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-05-get-list
3 | title: Step 05 - List of Journals
4 | sidebar_label: Get List
5 | ---
6 |
7 | # Get List
8 |
9 | Over time user may record lots of daily happenings. That can be handled by a little bit more data structure. In this turorial we keep it simple, just list all memories and extract out dates from the list.
10 |
11 | Get list of everything
12 | ```
13 | Storage.list(user.id)
14 | .then(data => {
15 | logger.debug('list of everything', data);
16 | this.extractDates(data);
17 | })
18 | .catch(err => logger.error('error when get list of everything', err));
19 | ```
20 |
21 | Extract dates
22 | ```
23 | extractDates(list) {
24 | const date_list = list.map(item => {
25 | const match = item.key.match(/\/(\d{4}-\d{2}-\d{2})\//);
26 | return match? match[1] : null;
27 | });
28 |
29 | const unique_dates = Array.from(new Set(date_list)).sort().reverse();
30 | this.setState({ dates: unique_dates });
31 | }
32 | ```
33 |
--------------------------------------------------------------------------------
/website/docs/step-05-run-app.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-05-run-app
3 | title: Step 05 - List of Journals
4 | sidebar_label: Run App
5 | ---
6 |
7 | # Run App
8 |
9 | ```
10 | npm start
11 | ```
12 |
13 |
14 |
--------------------------------------------------------------------------------
/website/docs/step-05-switch-date.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-05-switch-date
3 | title: Step 05 - List of Journals
4 | sidebar_label: Switch Date
5 | ---
6 |
7 | # Switch Date
8 |
9 | ```
10 | handleDateChange(evt, data) {
11 | const date = data.value;
12 | const { user } = this.state;
13 | const path = user.id + '/' + date + '/';
14 |
15 | this.setState({
16 | date: date,
17 | path: path
18 | });
19 | }
20 | ```
21 |
--------------------------------------------------------------------------------
/website/docs/step-06-build.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-06-build
3 | title: Step 06 - Go Live
4 | sidebar_label: Build
5 | ---
6 |
7 | ## Build
8 |
9 | ```
10 | npm run build
11 | ```
12 |
13 | The command generate everything needed into `build/` folder.
14 |
--------------------------------------------------------------------------------
/website/docs/step-06-publish.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-06-publish
3 | title: Step 06 - Go Live
4 | sidebar_label: Publish
5 | ---
6 |
7 | # Publish
8 |
9 | This app is built completely in HTML/JS/CSS, no backend server needed. Just a static file hosting will do. Here we choose Amazon S3.
10 |
11 | In [Step 02](step-02-prepare.html) we created an AWS Mobile Hub project. During that step we have created everything that we needed for this app.
12 |
13 | Go to AWS Console -> Mobile Hub. Click `Resources` on the top-right corner. Look for "Amazon S3 Buckets", then find the bucket that has `hosting` inside its name.
14 |
15 | Upload files under `build/` folder to this bucket. Then open in browser. We are live!
16 |
17 |
18 |
--------------------------------------------------------------------------------
/website/docs/step-06-touch-ups.md:
--------------------------------------------------------------------------------
1 | ---
2 | id: step-06-touch-ups
3 | title: Step 06 - Go Live
4 | sidebar_label: Touch Ups
5 | ---
6 |
7 | # Touch Ups
8 |
9 | We need to change icon, app title. Also I've added a sidebar point to this GitHub repo.
10 |
11 | Not necessary to explain details in this step. Just check out the code should be fine.
12 |
--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "examples": "dochameleon-examples",
4 | "start": "dochameleon-start",
5 | "build": "dochameleon-build",
6 | "publish-gh-pages": "dochameleon-publish"
7 | },
8 | "devDependencies": {
9 | "dochameleon": "0.0.51"
10 | },
11 | "dependencies": {
12 | "react": "^16.5.2"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/website/pages/README.md:
--------------------------------------------------------------------------------
1 | [](https://opensource.org/licenses/MIT)
2 |
3 | # Journal
4 | Step by step tutorial to build a personal journal web app with ReactJS + AWS
5 |
6 | * [Step 01 - Create a Basic React App](docs/step-01-cra.html)
7 | * [Step 02 - Authentication](docs/step-02-prepare.html)
8 | * [Step 03 - Authentication UI](docs/step-03-replace-sign-in.html)
9 | * [Step 04 - Everyday Journal](docs/step-04-user-info.html)
10 | * [Step 05 - List of Journals](docs/step-05-get-list.html)
11 | * [Step 06 - Go Live](docs/step-06-touch-ups.html)
12 |
13 | Go to `journal` sub-folder of each step to check full source code, and run app.
14 |
15 |
16 |
17 | [Live Demo](http://journal-hosting-mobilehub-1908112296.s3-website-us-east-1.amazonaws.com/)
18 |
19 | **Disclaimer:** This is a personal experiment. Not an official sample.
20 |
--------------------------------------------------------------------------------
/website/pages/index.js:
--------------------------------------------------------------------------------
1 | const React = require('react');
2 | const path = require('path');
3 |
4 | const dfs = require('../dfs.js');
5 |
6 | const Doc = require('../components/docs/Doc.js');
7 |
8 | const join = path.join;
9 |
10 | class ReadMe extends React.Component {
11 | render() {
12 | const { site, lang } = this.props;
13 |
14 | let readme_path = join(__dirname, './README.md');
15 | const doc = site.docs.processMetadata(readme_path);
16 | return (
17 |
18 |
19 | {doc.content}
20 |
21 |
22 | );
23 | }
24 | }
25 |
26 | module.exports = ReadMe;
27 |
--------------------------------------------------------------------------------
/website/pages/languages.js:
--------------------------------------------------------------------------------
1 | const React = require('react');
2 | const { Row, Col } = require('fluid-react');
3 |
4 | const MarkdownBlock = require('../components/MarkdownBlock.js');
5 |
6 | const no_languages_content = `
7 | To have multi-language, write translations under \`website/i18n/$lang_code$\`
8 |
9 | [Example](https://github.com/richardzcode/Dochameleon/tree/master/website/i18n)
10 | `;
11 |
12 | class Languages extends React.Component {
13 | render() {
14 | const { site, lang } = this.props;
15 | const { theme } = site;
16 | const langs = site.i18n.langs();
17 | const languages = langs && langs.length > 0
18 | ? langs.map((language, i) => {
19 | return (
20 |
21 |
22 | {site.i18n.translate(language, lang)}
23 |
24 |
25 | );
26 | })
27 | : {no_languages_content}
28 |
29 | return (
30 |
31 |
32 |
33 | {languages}
34 |
35 |
36 |
37 | );
38 | }
39 | }
40 |
41 | module.exports = Languages;
42 |
--------------------------------------------------------------------------------
/website/siteConfig.js:
--------------------------------------------------------------------------------
1 | const currentYear = new Date().getFullYear();
2 |
3 | const siteConfig = {
4 | projectName: 'Journal-AWS-Amplify-Tutorial',
5 | title: 'Journal Tutorial',
6 | tagline: 'Step by step tutorial to build a personal journal web app with ReactJS + AWS',
7 | copyright: 'Copyright © ' + currentYear + ' Richard Zhang',
8 |
9 | rootUrl: 'https://richardzcode.github.io',
10 | baseUrl: '/Journal-AWS-Amplify-Tutorial',
11 |
12 | icon: 'img/dochameleon.png',
13 | favicon: 'img/favicon.png',
14 |
15 | js: [
16 | 'https://buttons.github.io/buttons.js'
17 | ],
18 |
19 | buildDir: '../docs'
20 | };
21 |
22 | module.exports = siteConfig;
23 |
--------------------------------------------------------------------------------
/website/static/img/f-r.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/website/static/img/f-r.png
--------------------------------------------------------------------------------
/website/static/img/fsts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/richardzcode/Journal-AWS-Amplify-Tutorial/4ba93b50ca45525d4691d808ff93bd2fd49efecb/website/static/img/fsts.png
--------------------------------------------------------------------------------
/website/theme/custom.js:
--------------------------------------------------------------------------------
1 | const custom = {
2 | sideNavCategoryCap: {
3 | lineHeight: '1.2em',
4 | fontSize: '1em'
5 | },
6 | sideNavItem: {
7 | padding: '2px 0'
8 | },
9 | sideNavItemLink: {
10 | padding: '0',
11 | fontSize: '0.9em'
12 | },
13 | prevnext: {
14 | fontSize: '0.8em'
15 | }
16 | };
17 |
18 | module.exports = custom;
19 |
--------------------------------------------------------------------------------
/website/theme/pages.js:
--------------------------------------------------------------------------------
1 | const color = require('./color.js');
2 |
3 | const pages = {
4 | block: {
5 | padding: '30px 10px'
6 | },
7 | blockEven: {
8 | padding: '30px 10px',
9 | background: '#e9e9e9'
10 | },
11 | homeSplash: {
12 | padding: '2em 10px',
13 | textAlign: 'center'
14 | },
15 | promoSection: {
16 | display: 'flex',
17 | flexFlow: 'row wrap',
18 | justifyContent: 'center',
19 | fontSize: '125%',
20 | lineHeight: '1.6em',
21 | position: 'relative',
22 | zIndex: '99'
23 | },
24 | featureImageContainer: {
25 | textAlign: 'center'
26 | },
27 | featureImage: {
28 | maxHeight: '80px'
29 | },
30 | calloutTitle: {
31 | textAlign: 'left',
32 | textDecoration: 'none',
33 | color: color.title,
34 | fontWeight: '300',
35 | fontSize: '180%',
36 | lineHeight: '1em'
37 | },
38 | calloutImageContainer: {
39 | textAlign: 'center'
40 | },
41 | calloutImage: {
42 | maxWidth: '80%'
43 | },
44 | showcaseBox: {
45 | display: 'block',
46 | padding: '20px',
47 | width: '80px',
48 | textAlign: 'center'
49 | },
50 | showcaseImage: {
51 | maxWidth: '100%',
52 | maxHeight: '80px'
53 | },
54 | helpTitle: {
55 | textAlign: 'left',
56 | color: color.title,
57 | fontWeight: '300',
58 | fontSize: '200%',
59 | lineHeight: '1em'
60 | },
61 | helpSection: {
62 | padding: '20px'
63 | },
64 | helpSectionTitle: {
65 | textAlign: 'left',
66 | color: color.title,
67 | fontWeight: '300',
68 | fontSize: '150%',
69 | lineHeight: '1em'
70 | }
71 | };
72 |
73 | module.exports = pages;
74 |
--------------------------------------------------------------------------------