├── .babelrc
├── .codeclimate.yml
├── .eslintrc
├── .gitignore
├── .npmignore
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── README.md
├── examples
├── example
│ ├── .babelrc
│ ├── .eslintrc
│ ├── README.md
│ ├── jsconfig.json
│ ├── package-lock.json
│ ├── package.json
│ ├── src
│ │ ├── api
│ │ │ └── sessionApi.js
│ │ ├── components
│ │ │ ├── App.js
│ │ │ ├── Home.js
│ │ │ ├── Input.js
│ │ │ ├── Login.js
│ │ │ ├── Logout.js
│ │ │ └── SessionInfo.js
│ │ ├── index.html
│ │ ├── index.js
│ │ └── stores
│ │ │ └── UserStore.js
│ └── webpack.config.js
└── react-router-v4-example
│ ├── .babelrc
│ ├── .eslintrc
│ ├── README.md
│ ├── jsconfig.json
│ ├── package-lock.json
│ ├── package.json
│ ├── src
│ ├── api
│ │ └── sessionApi.js
│ ├── components
│ │ ├── App.js
│ │ ├── Home.js
│ │ ├── Input.js
│ │ ├── Login.js
│ │ ├── Logout.js
│ │ ├── PrivateRoute.js
│ │ └── SessionInfo.js
│ ├── index.html
│ ├── index.js
│ └── stores
│ │ └── UserStore.js
│ └── webpack.config.js
├── package-lock.json
├── package.json
└── src
├── __tests__
└── index.test.js
├── constants.js
├── index.js
└── storage.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env",
4 | ],
5 | "plugins": [
6 | "@babel/plugin-proposal-class-properties",
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | exclude_patterns:
2 | - "examples/"
3 | - "src/__tests__/"
4 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "eslint:recommended",
4 | "plugin:import/errors",
5 | "plugin:import/warnings"
6 | ],
7 | "plugins": [],
8 | "parser": "babel-eslint",
9 | "parserOptions": {
10 | "ecmaVersion": 6,
11 | "sourceType": "module",
12 | "ecmaFeatures": {
13 | "jsx": true,
14 | "experimentalObjectRestSpread": true
15 | }
16 | },
17 | "env": {
18 | "es6": true,
19 | "browser": true,
20 | "node": true,
21 | "jquery": true,
22 | "jest": true
23 | },
24 | "rules": {
25 | "quotes": 0,
26 | "no-console": 1,
27 | "no-debugger": 1,
28 | "no-var": 1,
29 | "semi": [1, "always"],
30 | "no-trailing-spaces": 1,
31 | "eol-last": 1,
32 | "no-underscore-dangle": 0,
33 | "no-alert": 0,
34 | "no-lone-blocks": 0,
35 | "jsx-quotes": 1,
36 | "no-multi-spaces": 1,
37 | "block-spacing": 1,
38 | "brace-style": 1,
39 | "comma-spacing": [1, { "before": false, "after": true }],
40 | "comma-style": 1,
41 | "key-spacing": 1,
42 | "no-multiple-empty-lines": [1, { "max": 1 }],
43 | "arrow-spacing": 1,
44 | "no-const-assign": 1,
45 | "object-curly-spacing": [1, "always"],
46 | "space-before-blocks" : [1, "always"],
47 | "keyword-spacing": 1,
48 | "indent": [1, 2, { "SwitchCase": 1 }]
49 | },
50 | "globals": {}
51 | }
52 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | dist
4 |
5 | coverage
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | src
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "10"
4 | cache: npm
5 |
6 | env:
7 | global:
8 | - CC_TEST_REPORTER_ID=5f93914c53e72710d78b95109895e11eaea0ca6aad5b1b9d2ab6cdd183902b00
9 |
10 | before_script:
11 | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
12 | - chmod +x ./cc-test-reporter
13 | - ./cc-test-reporter before-build
14 |
15 | script:
16 | - "npm run test:cover"
17 | - "npm run lint"
18 |
19 | after_script:
20 | - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
21 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | Contributor Covenant Code of Conduct
2 |
3 | Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 |
18 | * Using welcoming and inclusive language
19 | * Being respectful of differing viewpoints and experiences
20 | * Gracefully accepting constructive criticism
21 | * Focusing on what is best for the community
22 | * Showing empathy towards other community members
23 |
24 |
25 | Examples of unacceptable behavior by participants include:
26 |
27 |
28 | * The use of sexualized language or imagery and unwelcome sexual attention or
29 | advances
30 | * Trolling, insulting/derogatory comments, and personal or political attacks
31 | * Public or private harassment
32 | * Publishing others’ private information, such as a physical or electronic
33 | address, without explicit permission
34 | * Other conduct which could reasonably be considered inappropriate in a
35 | professional setting
36 |
37 |
38 | Our Responsibilities
39 |
40 | Project maintainers are responsible for clarifying the standards of acceptable
41 | behavior and are expected to take appropriate and fair corrective action in
42 | response to any instances of unacceptable behavior.
43 |
44 | Project maintainers have the right and responsibility to remove, edit, or
45 | reject comments, commits, code, wiki edits, issues, and other contributions
46 | that are not aligned to this Code of Conduct, or to ban temporarily or
47 | permanently any contributor for other behaviors that they deem inappropriate,
48 | threatening, offensive, or harmful.
49 |
50 | Scope
51 |
52 | This Code of Conduct applies both within project spaces and in public spaces
53 | when an individual is representing the project or its community. Examples of
54 | representing a project or community include using an official project e-mail
55 | address, posting via an official social media account, or acting as an appointed
56 | representative at an online or offline event. Representation of a project may be
57 | further defined and clarified by project maintainers.
58 |
59 | Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported by contacting the project team at ricardo@rootstrap.com. All
63 | complaints will be reviewed and investigated and will result in a response that
64 | is deemed necessary and appropriate to the circumstances. The project team is
65 | obligated to maintain confidentiality with regard to the reporter of an incident.
66 | Further details of specific enforcement policies may be posted separately.
67 |
68 | Project maintainers who do not follow or enforce the Code of Conduct in good
69 | faith may face temporary or permanent repercussions as determined by other
70 | members of the project’s leadership.
71 |
72 | Attribution
73 |
74 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
75 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
76 |
77 | For answers to common questions about this code of conduct, see
78 | https://www.contributor-covenant.org/faq
79 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing ##
2 |
3 | You can contribute to this repo if you have an issue, found a bug or think there's some functionality required that would add value to the gem. To do so, please check if there's not already an [issue](https://github.com/rootstrap/mobx-session/issues) for that, if you find there's not, create a new one with as much detail as possible.
4 |
5 | If you want to contribute with code as well, please follow the next steps:
6 |
7 | 1. Read, understand and agree to our [code of conduct](https://github.com/rootstrap/mobx-session/blob/master/CODE_OF_CONDUCT.md)
8 | 2. [Fork the repo](https://help.github.com/articles/about-forks/)
9 | 3. Clone the project into your machine:
10 | `$ git clone git@github.com:[YOUR GITHUB USERNAME]/mobx-session.git`
11 | 4. Access the repo:
12 | `$ cd mobx-session`
13 | 5. Create your feature/bugfix branch:
14 | `$ git checkout -b your_new_feature`
15 | or
16 | `$ git checkout -b fix/your_fix` in case of a bug fix
17 | (if your PR is to address an existing issue, it would be good to name the branch after the issue, for example: if you are trying to solve issue 182, then a good idea for the branch name would be `182_your_new_feature`)
18 | 6. Write tests for your changes (feature/bug)
19 | 7. Code your (feature/bugfix)
20 | 8. Run the code analysis tool by doing:
21 | `$ rake code_analysis`
22 | 9. Run the tests:
23 | `$ bundle exec rspec`
24 | All tests must pass. If all tests (both code analysis and rspec) do pass, then you are ready to go to the next step:
25 | 10. Commit your changes:
26 | `$ git commit -m 'Your feature or bugfix title'`
27 | 11. Push to the branch `$ git push origin your_new_feature`
28 | 12. Create a new [pull request](https://help.github.com/articles/creating-a-pull-request/)
29 |
30 | Some helpful guides that will help you know how we work:
31 | 1. [Code review](https://github.com/rootstrap/tech-guides/tree/master/code-review)
32 | 2. [GIT workflow](https://github.com/rootstrap/tech-guides/tree/master/git)
33 | 3. [Javascript](https://github.com/airbnb/javascript)
34 |
35 | For more information or guides like the ones mentioned above, please check our [tech guides](https://github.com/rootstrap/tech-guides). Keep in mind that the more you know about these guides, the easier it will be for your code to get approved and merged.
36 |
37 | Note: We work with one commit per pull request, so if you make your commit and realize you were missing something or want to add something more to it, don't create a new commit with the changes, but use `$ git commit --amend` instead. This same principle also applies for when changes are requested on an open pull request.
38 |
39 |
40 | Thank you very much for your time and for considering helping in this project.
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.com/rootstrap/mobx-session)
2 | [](https://codeclimate.com/github/rootstrap/mobx-session/maintainability)
3 | [](https://codeclimate.com/github/rootstrap/mobx-session/test_coverage)
4 |
5 | # Mobx Session
6 |
7 | Mobx Session helps you manage your session data providing an API to save and access your info whenever and wherever you want.
8 |
9 | The stored data will be saved with [localforage](https://github.com/localForage/localForage).
10 |
11 | ## Installation
12 | yarn:
13 |
14 | `yarn add mobx-session`
15 |
16 | npm:
17 |
18 | `npm install mobx-session`
19 |
20 | ## Usage
21 |
22 | It's really straight forward to use.
23 |
24 | First you must initialize the Storage. For that, you should call
25 |
26 | ```javascript
27 | import SessionStore from 'mobx-session';
28 |
29 | SessionStore.initialize();
30 | ```
31 |
32 | **This should be called before any other method call, so I would recommend putting it on the index.js or App of your site, or in the top of another Store that uses SessionStore (like in [this example](examples/example/src/stores/UserStore.js)).**
33 |
34 | There are several config options to customize this method, go to the [API doc](https://github.com/rootstrap/mobx-session#initializeconfig-object-promise) to see more.
35 |
36 | For example
37 |
38 | ```javascript
39 | import SessionStore from 'mobx-session';
40 |
41 | SessionStore.initialize({ name: 'my-app-name' });
42 | ```
43 |
44 | Then, you can use it wherever you want.
45 |
46 | ### Inside a Component
47 |
48 | ```javascript
49 | import SessionStore from 'mobx-session';
50 |
51 | const MyComponent = observer(() => {
52 | if (SessionStore.initialized) {
53 | return (
54 |
55 | {
56 | SessionStore.hasSession
57 | ?
Hello!
58 | :
Login
59 | }
60 |
61 | );
62 | }
63 |
64 | return null;
65 | })
66 | ```
67 |
68 | You can use the decorator syntax
69 |
70 | ```javascript
71 | import SessionStore from 'mobx-session';
72 |
73 | @observer
74 | const MyComponent = () => {
75 | if (SessionStore.initialized) {
76 | return (
77 |
78 | {
79 | SessionStore.hasSession
80 | ?
Hello!
81 | :
Login
82 | }
83 |
84 | );
85 | }
86 |
87 | return null;
88 | }
89 | ```
90 |
91 | *TIP: don't forget to make your component an observer so you don't lose the reference.*
92 |
93 | ### Inside a Store
94 |
95 | ```javascript
96 | import SessionStore from 'mobx-session';
97 |
98 | class UserStore {
99 | constructor() {
100 | extendObservable(this, {
101 | user: null,
102 | get loggedIn() {
103 | return this.user !== null && SessionStore.hasSession;
104 | },
105 | });
106 | }
107 | ......
108 | }
109 | ```
110 |
111 | You can also use the decorator syntax
112 |
113 | ```javascript
114 | import SessionStore from 'mobx-session';
115 |
116 | class UserStore {
117 | @observable user = null;
118 | @computed get loggedIn() {
119 | return this.user !== null && SessionStore.hasSession;
120 | }
121 | ......
122 | }
123 | ```
124 |
125 | *TIP: inside a store, don't forget to use it inside a computed value or an auto-run so you don't loose the reference.*
126 |
127 | ## Examples
128 |
129 | - [Basic example using React](examples/example)
130 | - [Example using React and React Router v4](examples/react-router-v4-example) that uses the session data to determine if a user can access a private route.
131 |
132 | ## API
133 |
134 | ### initialize(config: object): Promise
135 |
136 | Initialize an instance of the storage inside the session.
137 |
138 | Options can be the ones listed in the [localforage library](https://github.com/localForage/localForage#configuration)
139 |
140 | ### saveSession(session: object): Promise
141 |
142 | Saves the session object in the store and storage
143 |
144 | ### deleteSession(): Promise
145 |
146 | Deletes the session object from the store and the storage
147 |
148 | ### getSession(): Promise(session: object)
149 |
150 | Returns the session object saved if there's any
151 |
152 | ### initialized: boolean
153 |
154 | Returns true if the session store has been initialized. Could be useful to check this property before relying on other methods or properties from the store.
155 |
156 | ### hasSession: boolean
157 |
158 | Returns true if there's a session object saved and false if there's not.
159 |
160 | ## Contributing
161 | Bug reports (please use Issues) and pull requests are welcome on GitHub at https://github.com/rootstrap/mobx-session. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
162 |
163 | ## License
164 | The library is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
165 |
166 | ## Credits
167 | **mobx-session** is maintained by [Rootstrap](http://www.rootstrap.com) with the help of our [contributors](https://github.com/rootstrap/mobx-session/contributors).
168 |
169 | [ ](http://www.rootstrap.com)
170 |
--------------------------------------------------------------------------------
/examples/example/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/env",
4 | "@babel/react"
5 | ],
6 | "plugins": [
7 | [
8 | "@babel/plugin-proposal-decorators",
9 | {
10 | "legacy": true
11 | }
12 | ],
13 | [
14 | "@babel/plugin-proposal-class-properties",
15 | {
16 | "loose": true
17 | }
18 | ]
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/example/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "eslint:recommended",
4 | "plugin:import/errors",
5 | "plugin:import/warnings",
6 | "airbnb"
7 | ],
8 | "plugins": [
9 | "react",
10 | "react-hooks"
11 | ],
12 | "parser": "babel-eslint",
13 | "parserOptions": {
14 | "ecmaVersion": 6,
15 | "sourceType": "module",
16 | "ecmaFeatures": {
17 | "jsx": true,
18 | "experimentalObjectRestSpread": true
19 | }
20 | },
21 | "env": {
22 | "es6": true,
23 | "browser": true,
24 | "node": true,
25 | "jquery": true,
26 | "jest": true
27 | },
28 | "rules": {
29 | "object-curly-newline": 0,
30 | "no-class-assign": 0,
31 | "no-param-reassign": 0,
32 | "class-methods-use-this": 0,
33 | "consistent-return": 0,
34 | "no-shadow": 0,
35 | "global-require": 0,
36 | "eqeqeq": 0,
37 | "no-unused-expressions": [1, { "allowShortCircuit": true, "allowTernary": true }],
38 | "array-callback-return": 0,
39 | "no-console": [1, { "allow": ["warn", "error"] }],
40 | "no-debugger": 1,
41 | "no-var": 1,
42 | "semi": [1, "always"],
43 | "no-trailing-spaces": 1,
44 | "eol-last": 1,
45 | "no-underscore-dangle": 0,
46 | "no-alert": 0,
47 | "no-lone-blocks": 0,
48 | "jsx-quotes": 1,
49 | "no-multi-spaces": 1,
50 | "block-spacing": 1,
51 | "brace-style": 1,
52 | "comma-dangle": 0,
53 | "comma-spacing": [1, { "before": false, "after": true }],
54 | "comma-style": 1,
55 | "key-spacing": 1,
56 | "no-empty": [1, { "allowEmptyCatch": true }],
57 | "no-multiple-empty-lines": [1, { "max": 1 }],
58 | "arrow-spacing": 1,
59 | "no-const-assign": 1,
60 | "object-curly-spacing": [1, "always"],
61 | "space-before-blocks" : [1, "always"],
62 | "keyword-spacing": 1,
63 | "indent": [1, 2, { "SwitchCase": 1 }],
64 | "max-len": 0,
65 | "jsx-a11y/click-events-have-key-events": 0,
66 | "jsx-a11y/label-has-for": 0,
67 | "jsx-a11y/no-static-element-interactions": 0,
68 | "jsx-a11y/anchor-is-valid": 0,
69 | "react/display-name": [ 1, { "ignoreTranspilerName": false }],
70 | "react/forbid-prop-types": [1, { "forbid": ["any", "array", "object"] }],
71 | "react/jsx-curly-spacing": 1,
72 | "react/jsx-filename-extension": 0,
73 | "react/jsx-indent-props": 0,
74 | "react/jsx-key": 1,
75 | "react/jsx-max-props-per-line": 0,
76 | "react/jsx-no-duplicate-props": 1,
77 | "react/jsx-no-literals": 0,
78 | "react/jsx-no-undef": 1,
79 | "react/jsx-pascal-case": 1,
80 | "react/jsx-sort-prop-types": 0,
81 | "react/jsx-sort-props": 0,
82 | "react/jsx-uses-react": 1,
83 | "react/jsx-uses-vars": 1,
84 | "react/no-danger": 1,
85 | "react/no-did-mount-set-state": 1,
86 | "react/no-did-update-set-state": 1,
87 | "react/no-direct-mutation-state": 1,
88 | "react/no-multi-comp": 1,
89 | "react/no-set-state": 0,
90 | "react/no-unknown-property": 1,
91 | "react/prefer-es6-class": 1,
92 | "react/prop-types": 1,
93 | "react/react-in-jsx-scope": 1,
94 | "react/require-default-props": 0,
95 | "react/self-closing-comp": 1,
96 | "react/sort-comp": 1,
97 | "react/jsx-wrap-multilines": 0,
98 | "react/no-array-index-key": 0,
99 | "import/extensions": 1,
100 | "import/prefer-default-export": 0,
101 | "import/no-extraneous-dependencies": 0,
102 | "import/no-named-as-default": 0,
103 | "react-hooks/rules-of-hooks": "error"
104 | },
105 | "settings": {
106 | "import/resolver": {
107 | "node": {
108 | "paths": ["src"]
109 | }
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/examples/example/README.md:
--------------------------------------------------------------------------------
1 | # Simple Example
2 |
3 | Example that shows basic usage of [mobx-session](../..)
4 |
5 | ## Installation
6 | ### Install dependencies
7 | `npm install`
8 |
9 | ## Running the example
10 | `npm run start`
11 |
--------------------------------------------------------------------------------
/examples/example/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "paths": {
5 | "*": ["src/*"],
6 | "api/*": ["src/api/*"],
7 | "components/*": ["src/components/*"],
8 | "constants/*": ["src/constants/*"],
9 | "pages/*": ["src/pages/*"],
10 | "utils/*": ["src/utils/*"],
11 | "stores/*": ["src/stores/*"],
12 | }
13 | },
14 | "exclude": ["node_modules", "dist"]
15 | }
16 |
--------------------------------------------------------------------------------
/examples/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-for-mobx-session",
3 | "version": "1.0.0",
4 | "description": "Basic example for the mobx-session library",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "babel-node ./node_modules/webpack-dev-server/bin/webpack-dev-server --open",
8 | "test": "echo \"Error: no test specified\" && exit 1",
9 | "lint": "eslint src"
10 | },
11 | "keywords": [],
12 | "author": "Rootstrap",
13 | "license": "MIT",
14 | "devDependencies": {
15 | "@babel/core": "^7.4.5",
16 | "@babel/node": "^7.4.5",
17 | "@babel/plugin-proposal-class-properties": "^7.4.4",
18 | "@babel/plugin-proposal-decorators": "^7.4.4",
19 | "@babel/preset-env": "^7.4.5",
20 | "@babel/preset-react": "^7.0.0",
21 | "babel-loader": "^8.0.6",
22 | "eslint": "4.19.1",
23 | "eslint-config-airbnb": "16.1.0",
24 | "eslint-plugin-import": "2.11.0",
25 | "eslint-plugin-jsx-a11y": "6.0.3",
26 | "eslint-plugin-react": "7.7.0",
27 | "eslint-plugin-react-hooks": "1.0.0",
28 | "file-loader": "^4.0.0",
29 | "html-webpack-plugin": "^3.2.0",
30 | "path": "^0.12.7",
31 | "webpack": "^4.33.0",
32 | "webpack-cli": "^3.3.4",
33 | "webpack-dev-server": "^3.7.1"
34 | },
35 | "dependencies": {
36 | "mobx": "^5.10.1",
37 | "mobx-react": "^6.0.3",
38 | "mobx-session": "file:../../",
39 | "prop-types": "^15.7.2",
40 | "react": "^16.8.6",
41 | "react-dom": "^16.8.6"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/examples/example/src/api/sessionApi.js:
--------------------------------------------------------------------------------
1 | const login = (user) => {
2 | const response = {
3 | token: '1a2b3c4d',
4 | data: {
5 | email: user.email,
6 | firstName: 'test',
7 | lastName: 'test'
8 | }
9 | };
10 | return new Promise(resolve => setTimeout(resolve(response), 300));
11 | };
12 |
13 | const logout = () => new Promise(resolve => setTimeout(resolve, 300));
14 |
15 | export default { login, logout };
16 |
--------------------------------------------------------------------------------
/examples/example/src/components/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Home from './Home';
4 |
5 | const App = () => (
6 |
7 |
8 |
9 | );
10 |
11 | export default App;
12 |
--------------------------------------------------------------------------------
/examples/example/src/components/Home.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { observer } from 'mobx-react';
3 | import SessionStore from 'mobx-session';
4 |
5 | import Login from 'components/Login';
6 | import Logout from 'components/Logout';
7 | import SessionInfo from 'components/SessionInfo';
8 |
9 | const Home = observer(() => {
10 | if (SessionStore.initialized) {
11 | return (
12 |
13 |
14 | {
15 | SessionStore.hasSession
16 | ?
17 | :
18 | }
19 |
20 | );
21 | }
22 |
23 | return null;
24 | });
25 |
26 | export default Home;
27 |
--------------------------------------------------------------------------------
/examples/example/src/components/Input.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { func, string } from 'prop-types';
3 |
4 | const inputStyle = {
5 | padding: 10
6 | };
7 |
8 | const Input = ({ onChange, type, name }) => (
9 |
10 | {`${name.charAt(0).toUpperCase()}${name.slice(1)}`}
11 |
12 | onChange(value)}
17 | />
18 |
19 | );
20 |
21 | Input.propTypes = {
22 | onChange: func.isRequired,
23 | type: string.isRequired,
24 | name: string.isRequired
25 | };
26 |
27 | export default Input;
28 |
--------------------------------------------------------------------------------
/examples/example/src/components/Login.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 |
3 | import UserStore from 'stores/UserStore';
4 | import Input from 'components/Input';
5 |
6 | const Login = () => {
7 | const [email, updateEmail] = useState('');
8 | const [password, updatePassword] = useState('');
9 |
10 | const onSubmit = () => {
11 | if (email && password) {
12 | UserStore.login({ email, password });
13 | }
14 | };
15 |
16 | return (
17 |
18 |
19 |
20 |
21 | Login
22 |
23 | );
24 | };
25 |
26 | export default Login;
27 |
--------------------------------------------------------------------------------
/examples/example/src/components/Logout.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import UserStore from 'stores/UserStore';
4 |
5 | const Logout = () => (
6 |
7 | Try refreshing the page to see that the session is saved
8 |
9 | Logout
10 |
11 | );
12 |
13 | export default Logout;
14 |
--------------------------------------------------------------------------------
/examples/example/src/components/SessionInfo.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { observer } from 'mobx-react';
3 |
4 | import UserStore from 'stores/UserStore';
5 |
6 | const infoStyles = {
7 | backgroundColor: 'lightgray',
8 | padding: 20,
9 | margin: 10
10 | };
11 |
12 | const SessionInfo = observer(({ userInfo: { user } }) => (
13 |
14 |
Current Session Info:
15 | { user ?
16 |
17 | email: { user.email }
18 | firstName: { user.firstName }
19 | lastName: { user.lastName }
20 |
21 | :
No session info
22 | }
23 |
24 |
25 | ));
26 |
27 | const ConnectedSessionInfo = () => ;
28 |
29 | export default ConnectedSessionInfo;
30 |
--------------------------------------------------------------------------------
/examples/example/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/example/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from 'components/App';
4 |
5 | ReactDOM.render(
6 | ,
7 | document.getElementById('root')
8 | );
9 |
--------------------------------------------------------------------------------
/examples/example/src/stores/UserStore.js:
--------------------------------------------------------------------------------
1 | import { extendObservable, runInAction } from 'mobx';
2 | import SessionStore from 'mobx-session';
3 |
4 | import sessionApi from 'api/sessionApi';
5 |
6 | class Store {
7 | constructor() {
8 | SessionStore.initialize({ name: 'example-app' });
9 |
10 | extendObservable(this, {
11 | user: null,
12 | loginError: false,
13 | logoutError: false,
14 | get loggedIn() {
15 | return this.user !== null && SessionStore.hasSession;
16 | }
17 | });
18 |
19 | runInAction('Load user', async () => {
20 | this.user = await SessionStore.getSession();
21 | });
22 | }
23 |
24 | saveUser = (session) => {
25 | SessionStore.saveSession(session);
26 | runInAction('Save user', () => {
27 | this.user = session;
28 | });
29 | }
30 |
31 | removeUser = () => {
32 | SessionStore.deleteSession();
33 | runInAction('Logout user', () => {
34 | this.user = null;
35 | });
36 | }
37 |
38 | login = async (user) => {
39 | try {
40 | runInAction('Init Login', () => {
41 | this.loginError = false;
42 | });
43 | const { data } = await sessionApi.login(user);
44 | this.saveUser(data);
45 | } catch (error) {
46 | runInAction('Error Login', () => {
47 | this.loginError = error.errors;
48 | });
49 | }
50 | }
51 |
52 | logout = async () => {
53 | try {
54 | runInAction('Init Logout', () => {
55 | this.logoutError = false;
56 | });
57 | await sessionApi.logout();
58 | this.removeUser();
59 | } catch (error) {
60 | runInAction('Error Logout', () => {
61 | this.logoutError = error.errors;
62 | });
63 | }
64 | }
65 | }
66 |
67 | const UserStore = new Store();
68 |
69 | export default UserStore;
70 |
--------------------------------------------------------------------------------
/examples/example/webpack.config.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import "@babel/polyfill";
3 | import HtmlWebpackPlugin from 'html-webpack-plugin';
4 |
5 | module.exports = {
6 | entry: [
7 | "@babel/polyfill",
8 | path.resolve(__dirname, 'src/index.js')
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'build'),
12 | filename: 'index.bundle.js'
13 | },
14 | mode: process.env.NODE_ENV || 'development',
15 | resolve: {
16 | modules: [path.resolve(__dirname, 'src'), 'node_modules']
17 | },
18 | devServer: {
19 | contentBase: path.join(__dirname, 'src')
20 | },
21 | module: {
22 | rules: [
23 | {
24 | // this is so that we can compile any React,
25 | // ES6 and above into normal ES5 syntax
26 | test: /\.(js|jsx)$/,
27 | // we do not want anything from node_modules to be compiled
28 | exclude: /node_modules/,
29 | use: ['babel-loader']
30 | },
31 | {
32 | test: /\.(jpg|jpeg|png|gif|mp3|svg)$/,
33 | loaders: ['file-loader']
34 | }
35 | ]
36 | },
37 | plugins: [
38 | new HtmlWebpackPlugin({
39 | template: path.join(__dirname, 'src', 'index.html')
40 | })
41 | ]
42 | };
43 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/env",
4 | "@babel/react"
5 | ],
6 | "plugins": [
7 | [
8 | "@babel/plugin-proposal-decorators",
9 | {
10 | "legacy": true
11 | }
12 | ],
13 | [
14 | "@babel/plugin-proposal-class-properties",
15 | {
16 | "loose": true
17 | }
18 | ]
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "eslint:recommended",
4 | "plugin:import/errors",
5 | "plugin:import/warnings",
6 | "airbnb"
7 | ],
8 | "plugins": [
9 | "react",
10 | "react-hooks"
11 | ],
12 | "parser": "babel-eslint",
13 | "parserOptions": {
14 | "ecmaVersion": 6,
15 | "sourceType": "module",
16 | "ecmaFeatures": {
17 | "jsx": true,
18 | "experimentalObjectRestSpread": true
19 | }
20 | },
21 | "env": {
22 | "es6": true,
23 | "browser": true,
24 | "node": true,
25 | "jquery": true,
26 | "jest": true
27 | },
28 | "rules": {
29 | "object-curly-newline": 0,
30 | "no-class-assign": 0,
31 | "no-param-reassign": 0,
32 | "class-methods-use-this": 0,
33 | "consistent-return": 0,
34 | "no-shadow": 0,
35 | "global-require": 0,
36 | "eqeqeq": 0,
37 | "no-unused-expressions": [1, { "allowShortCircuit": true, "allowTernary": true }],
38 | "array-callback-return": 0,
39 | "no-console": [1, { "allow": ["warn", "error"] }],
40 | "no-debugger": 1,
41 | "no-var": 1,
42 | "semi": [1, "always"],
43 | "no-trailing-spaces": 1,
44 | "eol-last": 1,
45 | "no-underscore-dangle": 0,
46 | "no-alert": 0,
47 | "no-lone-blocks": 0,
48 | "jsx-quotes": 1,
49 | "no-multi-spaces": 1,
50 | "block-spacing": 1,
51 | "brace-style": 1,
52 | "comma-dangle": 0,
53 | "comma-spacing": [1, { "before": false, "after": true }],
54 | "comma-style": 1,
55 | "key-spacing": 1,
56 | "no-empty": [1, { "allowEmptyCatch": true }],
57 | "no-multiple-empty-lines": [1, { "max": 1 }],
58 | "arrow-spacing": 1,
59 | "no-const-assign": 1,
60 | "object-curly-spacing": [1, "always"],
61 | "space-before-blocks" : [1, "always"],
62 | "keyword-spacing": 1,
63 | "indent": [1, 2, { "SwitchCase": 1 }],
64 | "max-len": 0,
65 | "jsx-a11y/click-events-have-key-events": 0,
66 | "jsx-a11y/label-has-for": 0,
67 | "jsx-a11y/no-static-element-interactions": 0,
68 | "jsx-a11y/anchor-is-valid": 0,
69 | "react/display-name": [ 1, { "ignoreTranspilerName": false }],
70 | "react/forbid-prop-types": [1, { "forbid": ["any", "array", "object"] }],
71 | "react/jsx-curly-spacing": 1,
72 | "react/jsx-filename-extension": 0,
73 | "react/jsx-indent-props": 0,
74 | "react/jsx-key": 1,
75 | "react/jsx-max-props-per-line": 0,
76 | "react/jsx-no-duplicate-props": 1,
77 | "react/jsx-no-literals": 0,
78 | "react/jsx-no-undef": 1,
79 | "react/jsx-pascal-case": 1,
80 | "react/jsx-sort-prop-types": 0,
81 | "react/jsx-sort-props": 0,
82 | "react/jsx-uses-react": 1,
83 | "react/jsx-uses-vars": 1,
84 | "react/no-danger": 1,
85 | "react/no-did-mount-set-state": 1,
86 | "react/no-did-update-set-state": 1,
87 | "react/no-direct-mutation-state": 1,
88 | "react/no-multi-comp": 1,
89 | "react/no-set-state": 0,
90 | "react/no-unknown-property": 1,
91 | "react/prefer-es6-class": 1,
92 | "react/prop-types": 1,
93 | "react/react-in-jsx-scope": 1,
94 | "react/require-default-props": 0,
95 | "react/self-closing-comp": 1,
96 | "react/sort-comp": 1,
97 | "react/jsx-wrap-multilines": 0,
98 | "react/no-array-index-key": 0,
99 | "import/extensions": 1,
100 | "import/prefer-default-export": 0,
101 | "import/no-extraneous-dependencies": 0,
102 | "import/no-named-as-default": 0,
103 | "react-hooks/rules-of-hooks": "error"
104 | },
105 | "settings": {
106 | "import/resolver": {
107 | "node": {
108 | "paths": ["src"]
109 | }
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/README.md:
--------------------------------------------------------------------------------
1 | # Example with React Router v4
2 |
3 | Example that shows usage of [mobx-session](../..) with React and React Router v4. It uses the `hasSession` property to determine if the user can access a specific route.
4 |
5 | ## Installation
6 | ### Install dependencies
7 | `npm install`
8 |
9 | ## Running the example
10 | `npm run start`
11 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "paths": {
5 | "*": ["src/*"],
6 | "api/*": ["src/api/*"],
7 | "components/*": ["src/components/*"],
8 | "constants/*": ["src/constants/*"],
9 | "pages/*": ["src/pages/*"],
10 | "utils/*": ["src/utils/*"],
11 | "stores/*": ["src/stores/*"],
12 | }
13 | },
14 | "exclude": ["node_modules", "dist"]
15 | }
16 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-for-mobx-session-with-react-router-v4",
3 | "version": "1.0.0",
4 | "description": "Example for the mobx-session library with react-router v4",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "babel-node ./node_modules/webpack-dev-server/bin/webpack-dev-server --open",
8 | "test": "echo \"Error: no test specified\" && exit 1",
9 | "lint": "eslint src"
10 | },
11 | "keywords": [],
12 | "author": "Rootstrap",
13 | "license": "MIT",
14 | "devDependencies": {
15 | "@babel/core": "^7.4.5",
16 | "@babel/node": "^7.4.5",
17 | "@babel/plugin-proposal-class-properties": "^7.4.4",
18 | "@babel/plugin-proposal-decorators": "^7.4.4",
19 | "@babel/preset-env": "^7.4.5",
20 | "@babel/preset-react": "^7.0.0",
21 | "babel-loader": "^8.0.6",
22 | "eslint": "4.19.1",
23 | "eslint-config-airbnb": "16.1.0",
24 | "eslint-plugin-import": "2.11.0",
25 | "eslint-plugin-jsx-a11y": "6.0.3",
26 | "eslint-plugin-react": "7.7.0",
27 | "eslint-plugin-react-hooks": "1.0.0",
28 | "file-loader": "^4.0.0",
29 | "html-webpack-plugin": "^3.2.0",
30 | "path": "^0.12.7",
31 | "webpack": "^4.33.0",
32 | "webpack-cli": "^3.3.4",
33 | "webpack-dev-server": "^3.7.1"
34 | },
35 | "dependencies": {
36 | "mobx": "^5.10.1",
37 | "mobx-react": "^6.0.3",
38 | "mobx-session": "file:../../",
39 | "prop-types": "^15.7.2",
40 | "react": "^16.8.6",
41 | "react-dom": "^16.8.6",
42 | "react-router-dom": "^5.0.1"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/src/api/sessionApi.js:
--------------------------------------------------------------------------------
1 | const login = (user) => {
2 | const response = {
3 | token: '1a2b3c4d',
4 | data: {
5 | email: user.email,
6 | firstName: 'test',
7 | lastName: 'test'
8 | }
9 | };
10 | return new Promise(resolve => setTimeout(resolve(response), 300));
11 | };
12 |
13 | const logout = () => new Promise(resolve => setTimeout(resolve, 300));
14 |
15 | export default { login, logout };
16 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/src/components/App.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react';
2 | import { BrowserRouter as Router, Route } from 'react-router-dom';
3 | import { observer } from 'mobx-react';
4 | import SessionStore from 'mobx-session';
5 |
6 | import PrivateRoute from './PrivateRoute';
7 | import Home from './Home';
8 | import Login from './Login';
9 |
10 | const App = observer(() => (
11 |
12 |
13 | {
14 | SessionStore.initialized &&
15 |
16 |
17 |
18 |
19 | }
20 |
21 |
22 | ));
23 |
24 | export default App;
25 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/src/components/Home.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import Logout from 'components/Logout';
4 | import SessionInfo from 'components/SessionInfo';
5 |
6 | const Home = () => (
7 |
8 |
9 |
10 |
11 | );
12 |
13 | export default Home;
14 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/src/components/Input.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { func, string } from 'prop-types';
3 |
4 | const inputStyle = {
5 | padding: 10
6 | };
7 |
8 | const Input = ({ onChange, type, name }) => (
9 |
10 | {`${name.charAt(0).toUpperCase()}${name.slice(1)}`}
11 |
12 | onChange(value)}
17 | />
18 |
19 | );
20 |
21 | Input.propTypes = {
22 | onChange: func.isRequired,
23 | type: string.isRequired,
24 | name: string.isRequired
25 | };
26 |
27 | export default Input;
28 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/src/components/Login.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { withRouter } from 'react-router-dom';
3 | import { shape } from 'prop-types';
4 |
5 | import UserStore from 'stores/UserStore';
6 | import Input from 'components/Input';
7 |
8 | const Login = ({ history }) => {
9 | const [email, updateEmail] = useState('');
10 | const [password, updatePassword] = useState('');
11 |
12 | const onSubmit = () => {
13 | if (email && password) {
14 | UserStore.login({ email, password }, history);
15 | }
16 | };
17 |
18 | return (
19 |
20 |
21 |
22 |
23 | Login
24 |
25 | );
26 | };
27 |
28 | Login.propTypes = {
29 | history: shape()
30 | };
31 |
32 | export default withRouter(Login);
33 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/src/components/Logout.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import UserStore from 'stores/UserStore';
4 |
5 | const Logout = () => (
6 |
7 | Try refreshing the page to see that the session is saved
8 |
9 | Logout
10 |
11 | );
12 |
13 | export default Logout;
14 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/src/components/PrivateRoute.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shape, bool, string, func } from 'prop-types';
3 | import { Route, Redirect } from 'react-router-dom';
4 |
5 | const PrivateRoute = ({ component, exact = false, path, authenticated }) => (
6 | (
10 | authenticated ? (
11 | React.createElement(component, props)
12 | ) : (
13 |
19 | )
20 | )}
21 | />
22 | );
23 |
24 | PrivateRoute.propTypes = {
25 | component: func.isRequired,
26 | exact: bool,
27 | path: string.isRequired,
28 | authenticated: bool.isRequired,
29 | location: shape()
30 | };
31 |
32 | export default PrivateRoute;
33 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/src/components/SessionInfo.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { observer } from 'mobx-react';
3 |
4 | import UserStore from 'stores/UserStore';
5 |
6 | const infoStyles = {
7 | backgroundColor: 'lightgray',
8 | padding: 20,
9 | margin: 10
10 | };
11 |
12 | const SessionInfo = observer(({ userInfo: { user } }) => (
13 |
14 |
Current Session Info:
15 | { user ?
16 |
17 | email: { user.email }
18 | firstName: { user.firstName }
19 | lastName: { user.lastName }
20 |
21 | :
No session info
22 | }
23 |
24 |
25 | ));
26 |
27 | const ConnectedSessionInfo = () => ;
28 |
29 | export default ConnectedSessionInfo;
30 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from 'components/App';
4 |
5 | ReactDOM.render(
6 | ,
7 | document.getElementById('root')
8 | );
9 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/src/stores/UserStore.js:
--------------------------------------------------------------------------------
1 | import { extendObservable, runInAction } from 'mobx';
2 | import SessionStore from 'mobx-session';
3 |
4 | import sessionApi from 'api/sessionApi';
5 |
6 | class Store {
7 | constructor() {
8 | SessionStore.initialize({ name: 'example-app' });
9 |
10 | extendObservable(this, {
11 | user: null,
12 | loginError: false,
13 | logoutError: false,
14 | get loggedIn() {
15 | return this.user !== null && SessionStore.hasSession;
16 | }
17 | });
18 |
19 | runInAction('Load user', async () => {
20 | this.user = await SessionStore.getSession();
21 | });
22 | }
23 |
24 | saveUser = async (session) => {
25 | await SessionStore.saveSession(session);
26 | runInAction('Save user', () => {
27 | this.user = session;
28 | });
29 | }
30 |
31 | removeUser = () => {
32 | SessionStore.deleteSession();
33 | runInAction('Logout user', () => {
34 | this.user = null;
35 | });
36 | }
37 |
38 | login = async (user, history) => {
39 | try {
40 | runInAction('Init Login', () => {
41 | this.loginError = false;
42 | });
43 | const { data } = await sessionApi.login(user);
44 | await this.saveUser(data);
45 | history.push('/');
46 | } catch (error) {
47 | runInAction('Error Login', () => {
48 | this.loginError = error.errors;
49 | });
50 | }
51 | }
52 |
53 | logout = async () => {
54 | try {
55 | runInAction('Init Logout', () => {
56 | this.logoutError = false;
57 | });
58 | await sessionApi.logout();
59 | this.removeUser();
60 | } catch (error) {
61 | runInAction('Error Logout', () => {
62 | this.logoutError = error.errors;
63 | });
64 | }
65 | }
66 | }
67 |
68 | const UserStore = new Store();
69 |
70 | export default UserStore;
71 |
--------------------------------------------------------------------------------
/examples/react-router-v4-example/webpack.config.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import "@babel/polyfill";
3 | import HtmlWebpackPlugin from 'html-webpack-plugin';
4 |
5 | module.exports = {
6 | entry: [
7 | "@babel/polyfill",
8 | path.resolve(__dirname, 'src/index.js')
9 | ],
10 | output: {
11 | path: path.join(__dirname, 'build'),
12 | filename: 'index.bundle.js'
13 | },
14 | mode: process.env.NODE_ENV || 'development',
15 | resolve: {
16 | modules: [path.resolve(__dirname, 'src'), 'node_modules']
17 | },
18 | devServer: {
19 | contentBase: path.join(__dirname, 'src')
20 | },
21 | module: {
22 | rules: [
23 | {
24 | // this is so that we can compile any React,
25 | // ES6 and above into normal ES5 syntax
26 | test: /\.(js|jsx)$/,
27 | // we do not want anything from node_modules to be compiled
28 | exclude: /node_modules/,
29 | use: ['babel-loader']
30 | },
31 | {
32 | test: /\.(jpg|jpeg|png|gif|mp3|svg)$/,
33 | loaders: ['file-loader']
34 | }
35 | ]
36 | },
37 | plugins: [
38 | new HtmlWebpackPlugin({
39 | template: path.join(__dirname, 'src', 'index.html')
40 | })
41 | ]
42 | };
43 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mobx-session",
3 | "version": "1.0.0",
4 | "description": "Save your session with MobX, using IndexedDB, WebSQL, or localStorage",
5 | "main": "dist/index.js",
6 | "scripts": {
7 | "build": "npm run clean && babel src --out-dir dist",
8 | "test": "jest /src/__tests__",
9 | "test:watch": "jest /src/__tests__ --watch",
10 | "test:cover": "npm run test -- --coverage",
11 | "lint": "eslint src",
12 | "clean": "rimraf dist",
13 | "prepublishOnly": "npm run build"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/rootstrap/mobx-session.git"
18 | },
19 | "keywords": [
20 | "mobx",
21 | "react",
22 | "session",
23 | "authentication",
24 | "react-router",
25 | "localStorage"
26 | ],
27 | "author": "Rootstrap",
28 | "license": "MIT",
29 | "bugs": {
30 | "url": "https://github.com/rootstrap/mobx-session/issues"
31 | },
32 | "homepage": "https://github.com/rootstrap/mobx-session#readme",
33 | "devDependencies": {
34 | "@babel/cli": "^7.4.4",
35 | "@babel/core": "^7.4.5",
36 | "@babel/plugin-proposal-class-properties": "^7.4.4",
37 | "@babel/polyfill": "^7.4.4",
38 | "@babel/preset-env": "^7.4.5",
39 | "babel-core": "^6.26.3",
40 | "babel-eslint": "^10.0.1",
41 | "babel-jest": "^24.8.0",
42 | "babel-loader": "^8.0.6",
43 | "babel-preset-latest": "^6.24.1",
44 | "babel-preset-stage-1": "^6.24.1",
45 | "babel-register": "^6.26.0",
46 | "coveralls": "^3.0.4",
47 | "eslint": "^5.16.0",
48 | "eslint-plugin-import": "^2.17.3",
49 | "jest": "^24.8.0",
50 | "mobx": "^5.10.1",
51 | "rimraf": "^2.6.3",
52 | "webpack": "^4.33.0"
53 | },
54 | "dependencies": {
55 | "localforage": "^1.7.3"
56 | },
57 | "peerDependencies": {
58 | "mobx": "^5.10.1"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/__tests__/index.test.js:
--------------------------------------------------------------------------------
1 | import "@babel/polyfill";
2 |
3 | import SessionStore from '../index';
4 |
5 | const SESSION = { token: 'abc123' };
6 |
7 | describe('SessionStore', () => {
8 | describe('when the session store has not yet been initialized', () => {
9 | describe('initialized', () => {
10 | it('should return false', () => {
11 | expect(SessionStore.initialized).toEqual(false);
12 | });
13 | });
14 | });
15 |
16 | describe('when session store has been initialized', () => {
17 | beforeEach(async () => {
18 | await SessionStore.initialize();
19 | await SessionStore.deleteSession();
20 | });
21 |
22 | describe('initialized', () => {
23 | it('should return true', () => {
24 | expect(SessionStore.initialized).toBe(true);
25 | });
26 | });
27 |
28 | describe('saveSession', () => {
29 | let session;
30 |
31 | beforeEach(async () => {
32 | await SessionStore.saveSession(SESSION);
33 | session = await SessionStore.getSession();
34 | });
35 |
36 | it('should have saved the passed data', () => {
37 | expect(session).toStrictEqual(SESSION);
38 | });
39 | });
40 |
41 | describe('deleteSession', () => {
42 | let session;
43 |
44 | beforeEach(async () => {
45 | await SessionStore.saveSession(SESSION);
46 | await SessionStore.deleteSession();
47 | session = await SessionStore.getSession();
48 | });
49 |
50 | it('should have the session as null', () => {
51 | expect(session).toBe(null);
52 | });
53 | });
54 |
55 | describe('hasSession', () => {
56 | describe('when the session has been already saved', () => {
57 | beforeEach(async () => {
58 | await SessionStore.saveSession(SESSION);
59 | });
60 |
61 | it('should return true', () => {
62 | expect(SessionStore.hasSession).toBe(true);
63 | });
64 | });
65 |
66 | describe('when the session has not been saved', () => {
67 | it('should return false', () => {
68 | expect(SessionStore.hasSession).toBe(false);
69 | });
70 | });
71 | });
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/src/constants.js:
--------------------------------------------------------------------------------
1 | export const SESSION_KEY = 'session';
2 | export const DEFAULT_INSTANCE_NAME = 'mobx-session';
3 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import { extendObservable, runInAction } from 'mobx';
2 | import { Storage } from './storage';
3 |
4 | import { DEFAULT_INSTANCE_NAME, SESSION_KEY } from './constants';
5 |
6 | class Store {
7 | constructor() {
8 | extendObservable(this, {
9 | hasSession: false,
10 | initialized: false,
11 | });
12 | }
13 |
14 | initialize = async (options = { name: DEFAULT_INSTANCE_NAME }) => {
15 | Storage.initialize(options);
16 | const hasSession = await this.getSession() !== null;
17 | runInAction(() => {
18 | this.initialized = true;
19 | this.hasSession = hasSession;
20 | });
21 | }
22 |
23 | saveSession = async (session) => {
24 | await Storage.set(SESSION_KEY, session);
25 | runInAction(() => {
26 | this.hasSession = true;
27 | });
28 | }
29 |
30 | getSession = () => Storage.get(SESSION_KEY);
31 |
32 | deleteSession = () => {
33 | Storage.remove(SESSION_KEY);
34 | runInAction(() => {
35 | this.hasSession = false;
36 | });
37 | }
38 | }
39 |
40 | export default new Store();
41 |
--------------------------------------------------------------------------------
/src/storage.js:
--------------------------------------------------------------------------------
1 | import localforage from 'localforage';
2 |
3 | class StorageClass {
4 | constructor() {
5 | this.storage = null;
6 | }
7 |
8 | initialize(options) {
9 | this.storage = localforage.createInstance(options);
10 | }
11 |
12 | get(key) {
13 | return this.storage.getItem(key);
14 | }
15 |
16 | set(key, item) {
17 | if (item === undefined) {
18 | return;
19 | }
20 | this.storage.setItem(key, item);
21 | }
22 |
23 | remove(key) {
24 | this.storage.removeItem(key);
25 | }
26 | }
27 |
28 | export const Storage = new StorageClass();
29 |
--------------------------------------------------------------------------------