Dwayne Harris 5 years ago
parent
commit
f61e1e1271
  1. 241
      package-lock.json
  2. 20
      package.json
  3. 11
      src/actions/authentication.ts
  4. 12
      src/components/app/app.scss
  5. 5
      src/components/app/app.tsx
  6. 7
      src/components/app/index.ts
  7. 44
      src/components/create-group-step/create-group-step.tsx
  8. 13
      src/components/create-group-step/index.ts
  9. 27
      src/components/create-user-step/create-user-step.tsx
  10. 5
      src/components/create-user-step/index.ts
  11. 49
      src/components/group-list/group-list-item/index.tsx
  12. 17
      src/components/group-list/index.tsx
  13. 8
      src/components/pages/directory/directory.tsx
  14. 2
      src/components/pages/register-group/index.ts
  15. 11
      src/components/pages/register/index.ts
  16. 8
      src/components/pages/register/register.tsx
  17. 5
      src/components/pages/self/index.ts
  18. 11
      src/components/pages/self/self.tsx
  19. 17
      src/components/user-info/index.ts
  20. 56
      src/components/user-info/user-info.tsx
  21. 7
      src/reducers/authentication.ts
  22. 1
      src/selectors/authentication.ts
  23. 4
      src/selectors/directory.ts
  24. 6
      src/selectors/entities.ts
  25. 2
      src/types/entities.ts
  26. 29
      src/types/index.ts
  27. 1
      src/types/store.ts

241
package-lock.json

@ -13,24 +13,24 @@
} }
}, },
"@fortawesome/fontawesome-common-types": { "@fortawesome/fontawesome-common-types": {
"version": "0.2.22",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.22.tgz",
"integrity": "sha512-QmEuZsipX5/cR9JOg0fsTN4Yr/9lieYWM8AQpmRa0eIfeOcl/HLYoEa366BCGRSrgNJEexuvOgbq9jnJ22IY5g=="
"version": "0.2.24",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.24.tgz",
"integrity": "sha512-IPBT/1LdUVQpHcqdrh8uI2/86Fbu7933hkA/HweiCmP5QgF/8PecFM00gYvykxf0RZud8bg8zu+YfggDFUc1Kw=="
}, },
"@fortawesome/fontawesome-svg-core": { "@fortawesome/fontawesome-svg-core": {
"version": "1.2.22",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.22.tgz",
"integrity": "sha512-Q941E4x8UfnMH3308n0qrgoja+GoqyiV846JTLoCcCWAKokLKrixCkq6RDBs8r+TtAWaLUrBpI+JFxQNX/WNPQ==",
"version": "1.2.24",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.24.tgz",
"integrity": "sha512-9uVGOEZwviZKbkOVX8nn8cErVqOHBAd1Fqd2OH7Iwu0vxGWdb3fFOMhaAyMXUHZpq1u5C9/HClCV49ci4WmJAg==",
"requires": { "requires": {
"@fortawesome/fontawesome-common-types": "^0.2.22"
"@fortawesome/fontawesome-common-types": "^0.2.24"
} }
}, },
"@fortawesome/free-solid-svg-icons": { "@fortawesome/free-solid-svg-icons": {
"version": "5.10.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.10.2.tgz",
"integrity": "sha512-9Os/GRUcy+iVaznlg8GKcPSQFpIQpAg14jF0DWsMdnpJfIftlvfaQCWniR/ex9FoOpSEOrlXqmUCFL+JGeciuA==",
"version": "5.11.1",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.11.1.tgz",
"integrity": "sha512-bB3hXON1K6mVOetTTg5VXZ4CAHg866p7MqenDkJ/eVcbWbGQRE45ojHEwkf37tWx3E8z6lcEameRwU9r5tGwjg==",
"requires": { "requires": {
"@fortawesome/fontawesome-common-types": "^0.2.22"
"@fortawesome/fontawesome-common-types": "^0.2.24"
} }
}, },
"@fortawesome/react-fontawesome": { "@fortawesome/react-fontawesome": {
@ -1907,9 +1907,9 @@
} }
}, },
"cyclist": { "cyclist": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
"integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
"integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
"dev": true "dev": true
}, },
"dashdash": { "dashdash": {
@ -2217,9 +2217,9 @@
"dev": true "dev": true
}, },
"elliptic": { "elliptic": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.0.tgz",
"integrity": "sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg==",
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz",
"integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==",
"dev": true, "dev": true,
"requires": { "requires": {
"bn.js": "^4.4.0", "bn.js": "^4.4.0",
@ -2362,9 +2362,9 @@
"dev": true "dev": true
}, },
"eventemitter3": { "eventemitter3": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
"integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz",
"integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==",
"dev": true "dev": true
}, },
"events": { "events": {
@ -2794,9 +2794,9 @@
} }
}, },
"follow-redirects": { "follow-redirects": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.8.1.tgz",
"integrity": "sha512-micCIbldHioIegeKs41DoH0KS3AXfFzgS30qVkM6z/XOE/GJgvmsoc839NUqa1B9udYe9dQxgv7KFwng6+p/dw==",
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.9.0.tgz",
"integrity": "sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A==",
"dev": true, "dev": true,
"requires": { "requires": {
"debug": "^3.0.0" "debug": "^3.0.0"
@ -3813,16 +3813,16 @@
"dev": true "dev": true
}, },
"history": { "history": {
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/history/-/history-4.9.0.tgz",
"integrity": "sha512-H2DkjCjXf0Op9OAr6nJ56fcRkTSNrUiv41vNJ6IswJjif6wlpZK0BTfFbi7qK9dXLSYZxkq5lBsj3vUjlYBYZA==",
"version": "4.10.1",
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
"integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
"requires": { "requires": {
"@babel/runtime": "^7.1.2", "@babel/runtime": "^7.1.2",
"loose-envify": "^1.2.0", "loose-envify": "^1.2.0",
"resolve-pathname": "^2.2.0",
"resolve-pathname": "^3.0.0",
"tiny-invariant": "^1.0.2", "tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0", "tiny-warning": "^1.0.0",
"value-equal": "^0.4.0"
"value-equal": "^1.0.1"
} }
}, },
"hmac-drbg": { "hmac-drbg": {
@ -4008,12 +4008,12 @@
"dev": true "dev": true
}, },
"http-proxy": { "http-proxy": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz",
"integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==",
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz",
"integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"eventemitter3": "^3.0.0",
"eventemitter3": "^4.0.0",
"follow-redirects": "^1.0.0", "follow-redirects": "^1.0.0",
"requires-port": "^1.0.0" "requires-port": "^1.0.0"
} }
@ -4301,9 +4301,9 @@
"dev": true "dev": true
}, },
"is-absolute-url": { "is-absolute-url": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.1.tgz",
"integrity": "sha512-c2QjUwuMxLsld90sj3xYzpFYWJtuxkIn1f5ua9RTEYJt/vV2IsM+Py00/6qjV7qExgifUvt7qfyBGBBKm+2iBg==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.2.tgz",
"integrity": "sha512-+5g/wLlcm1AcxSP7014m6GvbPHswDx980vD/3bZaap8aGV9Yfs7Q6y6tfaupgZ5O74Byzc8dGrSCJ+bFXx0KdA==",
"dev": true "dev": true
}, },
"is-accessor-descriptor": { "is-accessor-descriptor": {
@ -4705,9 +4705,9 @@
"dev": true "dev": true
}, },
"loglevel": { "loglevel": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.3.tgz",
"integrity": "sha512-LoEDv5pgpvWgPF4kNYuIp0qqSJVWak/dML0RY74xlzMZiT9w77teNAwKYKWBTYjlokMirg+o3jBwp+vlLrcfAA==",
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.4.tgz",
"integrity": "sha512-p0b6mOGKcGa+7nnmKbpzR6qloPbrgLcnio++E+14Vo/XffOGwZtRpUhr8dTH/x2oCMmEoIU0Zwm3ZauhvYD17g==",
"dev": true "dev": true
}, },
"loose-envify": { "loose-envify": {
@ -5118,9 +5118,9 @@
} }
}, },
"node-forge": { "node-forge": {
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz",
"integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==",
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.2.tgz",
"integrity": "sha512-mXQ9GBq1N3uDCyV1pdSzgIguwgtVpM7f5/5J4ipz12PKWElmPpVWLDuWl8iXmhysr21+WmX/OJ5UKx82wjomgg==",
"dev": true "dev": true
}, },
"node-gyp": { "node-gyp": {
@ -5621,12 +5621,12 @@
"dev": true "dev": true
}, },
"parallel-transform": { "parallel-transform": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz",
"integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz",
"integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==",
"dev": true, "dev": true,
"requires": { "requires": {
"cyclist": "~0.2.2",
"cyclist": "^1.0.1",
"inherits": "^2.0.3", "inherits": "^2.0.3",
"readable-stream": "^2.1.5" "readable-stream": "^2.1.5"
} }
@ -5641,9 +5641,9 @@
} }
}, },
"parse-asn1": { "parse-asn1": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz",
"integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==",
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz",
"integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"asn1.js": "^4.0.0", "asn1.js": "^4.0.0",
@ -5809,9 +5809,9 @@
} }
}, },
"portfinder": { "portfinder": {
"version": "1.0.23",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.23.tgz",
"integrity": "sha512-B729mL/uLklxtxuiJKfQ84WPxNw5a7Yhx3geQZdcA4GjNjZSTSSMMWyoennMVnTWSmAR0lMdzWYN0JLnHrg1KQ==",
"version": "1.0.24",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.24.tgz",
"integrity": "sha512-ekRl7zD2qxYndYflwiryJwMioBI7LI7rVXg3EnLK3sjkouT5eOuhS3gS255XxBksa30VG8UPZYZCdgfGOfkSUg==",
"dev": true, "dev": true,
"requires": { "requires": {
"async": "^1.5.2", "async": "^1.5.2",
@ -6666,9 +6666,9 @@
"dev": true "dev": true
}, },
"resolve-pathname": { "resolve-pathname": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz",
"integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg=="
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
}, },
"resolve-url": { "resolve-url": {
"version": "0.2.1", "version": "0.2.1",
@ -6964,12 +6964,12 @@
"dev": true "dev": true
}, },
"selfsigned": { "selfsigned": {
"version": "1.10.4",
"resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz",
"integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==",
"version": "1.10.6",
"resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.6.tgz",
"integrity": "sha512-i3+CeqxL7DpAazgVpAGdKMwHuL63B5nhJMh9NQ7xmChGkA3jNFflq6Jyo1LLJYcr3idWiNOPWHCrm4zMayLG4w==",
"dev": true, "dev": true,
"requires": { "requires": {
"node-forge": "0.7.5"
"node-forge": "0.8.2"
} }
}, },
"semver": { "semver": {
@ -7008,9 +7008,9 @@
} }
}, },
"serialize-javascript": { "serialize-javascript": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.8.0.tgz",
"integrity": "sha512-3tHgtF4OzDmeKYj6V9nSyceRS0UJ3C7VqyD2Yj28vC/z2j6jG5FmFGahOKMD9CrglxTm3tETr87jEypaYV8DUg==",
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
"integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==",
"dev": true "dev": true
}, },
"serve-index": { "serve-index": {
@ -7186,6 +7186,12 @@
"requires": { "requires": {
"is-extendable": "^0.1.0" "is-extendable": "^0.1.0"
} }
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
"dev": true
} }
} }
}, },
@ -7271,9 +7277,9 @@
} }
}, },
"sockjs-client": { "sockjs-client": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz",
"integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz",
"integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==",
"dev": true, "dev": true,
"requires": { "requires": {
"debug": "^3.2.5", "debug": "^3.2.5",
@ -7326,9 +7332,9 @@
"dev": true "dev": true
}, },
"source-map": { "source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true "dev": true
}, },
"source-map-resolve": { "source-map-resolve": {
@ -7352,14 +7358,6 @@
"requires": { "requires": {
"buffer-from": "^1.0.0", "buffer-from": "^1.0.0",
"source-map": "^0.6.0" "source-map": "^0.6.0"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
} }
}, },
"source-map-url": { "source-map-url": {
@ -7703,22 +7701,14 @@
} }
}, },
"terser": { "terser": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.2.1.tgz",
"integrity": "sha512-cGbc5utAcX4a9+2GGVX4DsenG6v0x3glnDi5hx8816X1McEAwPlPgRtXPJzSBsbpILxZ8MQMT0KvArLuE0HP5A==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.3.1.tgz",
"integrity": "sha512-pnzH6dnFEsR2aa2SJaKb1uSCl3QmIsJ8dEkj0Fky+2AwMMcC9doMqLOQIH6wVTEKaVfKVvLSk5qxPBEZT9mywg==",
"dev": true, "dev": true,
"requires": { "requires": {
"commander": "^2.20.0", "commander": "^2.20.0",
"source-map": "~0.6.1", "source-map": "~0.6.1",
"source-map-support": "~0.5.12" "source-map-support": "~0.5.12"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
} }
}, },
"terser-webpack-plugin": { "terser-webpack-plugin": {
@ -7736,14 +7726,6 @@
"terser": "^4.1.2", "terser": "^4.1.2",
"webpack-sources": "^1.4.0", "webpack-sources": "^1.4.0",
"worker-farm": "^1.7.0" "worker-farm": "^1.7.0"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
} }
}, },
"through2": { "through2": {
@ -7874,9 +7856,9 @@
} }
}, },
"ts-loader": { "ts-loader": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-6.0.4.tgz",
"integrity": "sha512-p2zJYe7OtwR+49kv4gs7v4dMrfYD1IPpOtqiSPCbe8oR+4zEBtdHwzM7A7M91F+suReqgzZrlClk4LRSSp882g==",
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-6.1.2.tgz",
"integrity": "sha512-dudxFKm0Ellrg/gLNlu+97/UgwvoMK0SdUVImPUSzq3IcRUVtShylZvcMX+CgvCQL1BEKb913NL0gAP1GA/OFw==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "^2.3.0", "chalk": "^2.3.0",
@ -7887,9 +7869,9 @@
} }
}, },
"ts-node": { "ts-node": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz",
"integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==",
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.4.1.tgz",
"integrity": "sha512-5LpRN+mTiCs7lI5EtbXmF/HfMeCjzt7DH9CZwtkr6SywStrNQC723wG+aOWFiLNn7zT3kD/RnFqi3ZUfr4l5Qw==",
"dev": true, "dev": true,
"requires": { "requires": {
"arg": "^4.1.0", "arg": "^4.1.0",
@ -7943,9 +7925,9 @@
"dev": true "dev": true
}, },
"typescript": { "typescript": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.2.tgz",
"integrity": "sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw==",
"version": "3.6.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz",
"integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==",
"dev": true "dev": true
}, },
"uglify-js": { "uglify-js": {
@ -8055,9 +8037,9 @@
} }
}, },
"upath": { "upath": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz",
"integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
"integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
"dev": true "dev": true
}, },
"upper-case": { "upper-case": {
@ -8182,9 +8164,9 @@
} }
}, },
"value-equal": { "value-equal": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz",
"integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw=="
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
}, },
"vary": { "vary": {
"version": "1.1.2", "version": "1.1.2",
@ -8230,9 +8212,9 @@
} }
}, },
"webpack": { "webpack": {
"version": "4.39.3",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.39.3.tgz",
"integrity": "sha512-BXSI9M211JyCVc3JxHWDpze85CvjC842EvpRsVTc/d15YJGlox7GIDd38kJgWrb3ZluyvIjgenbLDMBQPDcxYQ==",
"version": "4.40.2",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.40.2.tgz",
"integrity": "sha512-5nIvteTDCUws2DVvP9Qe+JPla7kWPPIDFZv55To7IycHWZ+Z5qBdaBYPyuXWdhggTufZkQwfIK+5rKQTVovm2A==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.8.5", "@webassemblyjs/ast": "1.8.5",
@ -8366,9 +8348,9 @@
} }
}, },
"webpack-cli": { "webpack-cli": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.7.tgz",
"integrity": "sha512-OhTUCttAsr+IZSMVwGROGRHvT+QAs8H6/mHIl4SvhAwYywjiylYjpwybGx7WQ9Hkb45FhjtsymkwiRRbGJ1SZQ==",
"version": "3.3.9",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.9.tgz",
"integrity": "sha512-xwnSxWl8nZtBl/AFJCOn9pG7s5CYUYdZxmmukv+fAHLcBIHM36dImfpQg3WfShZXeArkWlf6QRw24Klcsv8a5A==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "2.4.2", "chalk": "2.4.2",
@ -8396,13 +8378,14 @@
} }
}, },
"webpack-dev-middleware": { "webpack-dev-middleware": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.0.tgz",
"integrity": "sha512-qvDesR1QZRIAZHOE3iQ4CXLZZSQ1lAUsSpnQmlB1PBfoN/xdRjmge3Dok0W4IdaVLJOGJy3sGI4sZHwjRU0PCA==",
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.1.tgz",
"integrity": "sha512-5MWu9SH1z3hY7oHOV6Kbkz5x7hXbxK56mGHNqHTe6d+ewxOwKUxoUJBs7QIaJb33lPjl9bJZ3X0vCoooUzC36A==",
"dev": true, "dev": true,
"requires": { "requires": {
"memory-fs": "^0.4.1", "memory-fs": "^0.4.1",
"mime": "^2.4.2",
"mime": "^2.4.4",
"mkdirp": "^0.5.1",
"range-parser": "^1.2.1", "range-parser": "^1.2.1",
"webpack-log": "^2.0.0" "webpack-log": "^2.0.0"
}, },
@ -8416,14 +8399,14 @@
} }
}, },
"webpack-dev-server": { "webpack-dev-server": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.8.0.tgz",
"integrity": "sha512-Hs8K9yI6pyMvGkaPTeTonhD6JXVsigXDApYk9JLW4M7viVBspQvb1WdAcWxqtmttxNW4zf2UFLsLNe0y87pIGQ==",
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.8.1.tgz",
"integrity": "sha512-9F5DnfFA9bsrhpUCAfQic/AXBVHvq+3gQS+x6Zj0yc1fVVE0erKh2MV4IV12TBewuTrYeeTIRwCH9qLMvdNvTw==",
"dev": true, "dev": true,
"requires": { "requires": {
"ansi-html": "0.0.7", "ansi-html": "0.0.7",
"bonjour": "^3.5.0", "bonjour": "^3.5.0",
"chokidar": "^2.1.6",
"chokidar": "^2.1.8",
"compression": "^1.7.4", "compression": "^1.7.4",
"connect-history-api-fallback": "^1.6.0", "connect-history-api-fallback": "^1.6.0",
"debug": "^4.1.1", "debug": "^4.1.1",
@ -8434,23 +8417,23 @@
"import-local": "^2.0.0", "import-local": "^2.0.0",
"internal-ip": "^4.3.0", "internal-ip": "^4.3.0",
"ip": "^1.1.5", "ip": "^1.1.5",
"is-absolute-url": "^3.0.0",
"is-absolute-url": "^3.0.2",
"killable": "^1.0.1", "killable": "^1.0.1",
"loglevel": "^1.6.3",
"loglevel": "^1.6.4",
"opn": "^5.5.0", "opn": "^5.5.0",
"p-retry": "^3.0.1", "p-retry": "^3.0.1",
"portfinder": "^1.0.21",
"portfinder": "^1.0.24",
"schema-utils": "^1.0.0", "schema-utils": "^1.0.0",
"selfsigned": "^1.10.4",
"selfsigned": "^1.10.6",
"semver": "^6.3.0", "semver": "^6.3.0",
"serve-index": "^1.9.1", "serve-index": "^1.9.1",
"sockjs": "0.3.19", "sockjs": "0.3.19",
"sockjs-client": "1.3.0",
"sockjs-client": "1.4.0",
"spdy": "^4.0.1", "spdy": "^4.0.1",
"strip-ansi": "^3.0.1", "strip-ansi": "^3.0.1",
"supports-color": "^6.1.0", "supports-color": "^6.1.0",
"url": "^0.11.0", "url": "^0.11.0",
"webpack-dev-middleware": "^3.7.0",
"webpack-dev-middleware": "^3.7.1",
"webpack-log": "^2.0.0", "webpack-log": "^2.0.0",
"ws": "^6.2.1", "ws": "^6.2.1",
"yargs": "12.0.5" "yargs": "12.0.5"

20
package.json

@ -38,20 +38,20 @@
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"sass-loader": "^8.0.0", "sass-loader": "^8.0.0",
"style-loader": "^1.0.0", "style-loader": "^1.0.0",
"ts-loader": "^6.0.4",
"ts-node": "^8.3.0",
"typescript": "^3.6.2",
"webpack": "^4.39.3",
"webpack-cli": "^3.3.7",
"webpack-dev-server": "^3.8.0"
"ts-loader": "^6.1.2",
"ts-node": "^8.4.1",
"typescript": "^3.6.3",
"webpack": "^4.40.2",
"webpack-cli": "^3.3.9",
"webpack-dev-server": "^3.8.1"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-common-types": "^0.2.22",
"@fortawesome/fontawesome-svg-core": "^1.2.22",
"@fortawesome/free-solid-svg-icons": "^5.10.2",
"@fortawesome/fontawesome-common-types": "^0.2.24",
"@fortawesome/fontawesome-svg-core": "^1.2.24",
"@fortawesome/free-solid-svg-icons": "^5.11.1",
"@fortawesome/react-fontawesome": "^0.1.4", "@fortawesome/react-fontawesome": "^0.1.4",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"history": "^4.9.0",
"history": "^4.10.1",
"lodash": "^4.17.15", "lodash": "^4.17.15",
"normalizr": "^3.4.1", "normalizr": "^3.4.1",
"react": "^16.9.0", "react": "^16.9.0",

11
src/actions/authentication.ts

@ -9,6 +9,10 @@ import { userSchema } from 'src/store/schemas'
import { REQUEST_KEYS } from 'src/constants' import { REQUEST_KEYS } from 'src/constants'
import { AppThunkAction, Entity } from 'src/types' import { AppThunkAction, Entity } from 'src/types'
export interface SetCheckedAction extends Action {
type: 'AUTHENTICATION_SET_CHECKED'
}
export interface SetAuthenticatedAction extends Action { export interface SetAuthenticatedAction extends Action {
type: 'AUTHENTICATION_SET_AUTHENTICATED' type: 'AUTHENTICATION_SET_AUTHENTICATED'
payload: boolean payload: boolean
@ -19,7 +23,11 @@ export interface SetUserAction extends Action {
payload: string payload: string
} }
export type AuthenticationActions = SetAuthenticatedAction | SetUserAction
export type AuthenticationActions = SetCheckedAction | SetAuthenticatedAction | SetUserAction
export const setChecked = (): SetCheckedAction => ({
type: 'AUTHENTICATION_SET_CHECKED',
})
export const setAuthenticated = (authenticated: boolean): SetAuthenticatedAction => ({ export const setAuthenticated = (authenticated: boolean): SetAuthenticatedAction => ({
type: 'AUTHENTICATION_SET_AUTHENTICATED', type: 'AUTHENTICATION_SET_AUTHENTICATED',
@ -47,6 +55,7 @@ export const fetchSelf = (): AppThunkAction => async dispatch => {
dispatch(finishRequest(REQUEST_KEYS.FETCH_GROUP_AVAILABILITY, true)) dispatch(finishRequest(REQUEST_KEYS.FETCH_GROUP_AVAILABILITY, true))
} catch (err) { } catch (err) {
dispatch(setAuthenticated(false))
dispatch(finishRequest(REQUEST_KEYS.FETCH_GROUP_AVAILABILITY, false)) dispatch(finishRequest(REQUEST_KEYS.FETCH_GROUP_AVAILABILITY, false))
throw err throw err
} }

12
src/components/app/app.scss

@ -34,7 +34,7 @@ $body-size: 14px;
div#main-menu { div#main-menu {
background-color: $primary; background-color: $primary;
border-left: 2px solid $purple;
border-left: 1px solid $grey-lighter;
bottom: 0; bottom: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -59,8 +59,11 @@ div.centered-content {
width: 80%; width: 80%;
div.centered-content-icon { div.centered-content-icon {
border-radius: 100px;
margin: auto;
margin-top: -20px; margin-top: -20px;
text-align: center; text-align: center;
width: 3rem;
} }
} }
@ -75,3 +78,10 @@ div#navigation {
footer { footer {
padding: $size-normal; padding: $size-normal;
} }
div.group-list-item {
background-color: $white;
border-radius: 15px;
margin: 10px 0px;
padding: 20px;
}

5
src/components/app/app.tsx

@ -25,15 +25,18 @@ interface Props {
collapsed: boolean collapsed: boolean
fetching: boolean fetching: boolean
fetchSelf: () => void fetchSelf: () => void
setChecked: () => void
} }
const App: FC<Props> = ({ collapsed, fetching, fetchSelf }) => {
const App: FC<Props> = ({ collapsed, fetching, fetchSelf, setChecked }) => {
const mainMenuWidth = 275 const mainMenuWidth = 275
const mainColumnMargin = collapsed ? 0 : mainMenuWidth const mainColumnMargin = collapsed ? 0 : mainMenuWidth
useEffect(() => { useEffect(() => {
if (localStorage.getItem(LOCAL_STORAGE_ACCESS_TOKEN_KEY)) { if (localStorage.getItem(LOCAL_STORAGE_ACCESS_TOKEN_KEY)) {
fetchSelf() fetchSelf()
} else {
setChecked()
} }
}, []) }, [])

7
src/components/app/index.ts

@ -1,6 +1,6 @@
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { fetchSelf } from 'src/actions/authentication'
import { fetchSelf, setChecked } from 'src/actions/authentication'
import { getFetching } from 'src/selectors' import { getFetching } from 'src/selectors'
import { getCollapsed } from 'src/selectors/menu' import { getCollapsed } from 'src/selectors/menu'
@ -16,7 +16,10 @@ const mapStateToProps = (state: AppState) => ({
const mapDispatchToProps = (dispatch: AppThunkDispatch) => ({ const mapDispatchToProps = (dispatch: AppThunkDispatch) => ({
fetchSelf: () => { fetchSelf: () => {
dispatch(fetchSelf()) dispatch(fetchSelf())
}
},
setChecked: () => {
dispatch(setChecked())
}
}) })
export default connect( export default connect(

44
src/components/create-group-step/create-group-step.tsx

@ -1,22 +1,30 @@
import React, { FC } from 'react' import React, { FC } from 'react'
import noop from 'lodash/noop' import noop from 'lodash/noop'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faBuilding, faArrowRight } from '@fortawesome/free-solid-svg-icons'
import { faBuilding, faArrowLeft } from '@fortawesome/free-solid-svg-icons'
import CreateGroupForm from '../create-group-form' import CreateGroupForm from '../create-group-form'
export interface Props { export interface Props {
name: string
registration: string
agree: boolean
next?: (name: string, agree: boolean) => void
name?: string
registration?: string
agree?: boolean
previous?: () => void
next?: (name: string, registration: string, agree: boolean) => void
register: () => void
} }
const CreateGroupStep: FC<Props> = ({ name, agree, next = noop }) => (
const CreateGroupStep: FC<Props> = ({
name = '',
registration = '',
agree = false,
previous = noop,
next = noop,
}) => (
<div className="centered-content"> <div className="centered-content">
<div className="centered-content-icon">
<span className="icon is-large has-text-primary">
<FontAwesomeIcon icon={faBuilding} size="lg" />
<div className="centered-content-icon has-background-primary">
<span className="icon is-large has-text-white">
<FontAwesomeIcon icon={faBuilding} size="2x" />
</span> </span>
</div> </div>
@ -24,20 +32,22 @@ const CreateGroupStep: FC<Props> = ({ name, agree, next = noop }) => (
<hr /> <hr />
<nav className="level"> <nav className="level">
<div className="level-left">
<p className="level-item"></p>
</div>
<div className="level-right">
<div className="level-left">
<p className="level-item"> <p className="level-item">
<button className="button is-success" onClick={() => next(name, agree)}>
<span>Your Account</span>
<button className="button" onClick={() => previous()}>
<span className="icon is-small"> <span className="icon is-small">
<FontAwesomeIcon icon={faArrowRight} />
<FontAwesomeIcon icon={faArrowLeft} />
</span> </span>
<span>Your Account</span>
</button> </button>
</p> </p>
</div> </div>
<div className="level-right">
<p className="level-item">
<button className="button is-success" onClick={() => next(name, registration, agree)}>Finish</button>
</p>
</div>
</nav> </nav>
</div> </div>
) )

13
src/components/create-group-step/index.ts

@ -1,4 +1,3 @@
import { Dispatch } from 'redux'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { setFieldNotification } from 'src/actions/forms' import { setFieldNotification } from 'src/actions/forms'
@ -8,7 +7,7 @@ import { getFieldValue } from 'src/selectors/forms'
import { MAX_ID_LENGTH } from 'src/constants' import { MAX_ID_LENGTH } from 'src/constants'
import { AppState, AppThunkDispatch } from 'src/types' import { AppState, AppThunkDispatch } from 'src/types'
import CreateGroupStep from './create-group-step'
import CreateGroupStep, { Props } from './create-group-step'
const mapStateToProps = (state: AppState) => ({ const mapStateToProps = (state: AppState) => ({
name: getFieldValue<string>(state, 'group-name', ''), name: getFieldValue<string>(state, 'group-name', ''),
@ -16,8 +15,11 @@ const mapStateToProps = (state: AppState) => ({
agree: getFieldValue<boolean>(state, 'group-agree', false), agree: getFieldValue<boolean>(state, 'group-agree', false),
}) })
const mapDispatchToProps = (dispatch: AppThunkDispatch) => ({
next: (name: string, agree: boolean) => {
const mapDispatchToProps = (dispatch: AppThunkDispatch, ownProps: Props) => ({
previous: () => {
dispatch(setStep(0))
},
next: (name: string, registration: string, agree: boolean) => {
let invalid = false let invalid = false
if (!name) { if (!name) {
@ -37,8 +39,7 @@ const mapDispatchToProps = (dispatch: AppThunkDispatch) => ({
} }
if (invalid) return if (invalid) return
dispatch(setStep(1))
if (ownProps.register) ownProps.register()
}, },
}) })

27
src/components/create-user-step/create-user-step.tsx

@ -1,7 +1,7 @@
import React, { FC } from 'react' import React, { FC } from 'react'
import noop from 'lodash/noop' import noop from 'lodash/noop'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faUser, faArrowLeft } from '@fortawesome/free-solid-svg-icons'
import { faUser, faArrowRight } from '@fortawesome/free-solid-svg-icons'
import CreateUserForm from '../create-user-form' import CreateUserForm from '../create-user-form'
@ -11,9 +11,7 @@ export interface Props {
email?: string email?: string
password?: string password?: string
agree?: boolean agree?: boolean
previous?: () => void
next?: (userId: string, name: string, email: string, password: string, agree: boolean) => void next?: (userId: string, name: string, email: string, password: string, agree: boolean) => void
register: () => void
} }
const CreateUserStep: FC<Props> = ({ const CreateUserStep: FC<Props> = ({
@ -22,13 +20,12 @@ const CreateUserStep: FC<Props> = ({
email = '', email = '',
password = '', password = '',
agree = false, agree = false,
previous = noop,
next = noop, next = noop,
}) => ( }) => (
<div className="centered-content"> <div className="centered-content">
<div className="centered-content-icon">
<span className="icon is-large has-text-primary">
<FontAwesomeIcon icon={faUser} size="lg" />
<div className="centered-content-icon has-background-primary">
<span className="icon is-large has-text-white">
<FontAwesomeIcon icon={faUser} size="2x" />
</span> </span>
</div> </div>
@ -37,19 +34,17 @@ const CreateUserStep: FC<Props> = ({
<nav className="level"> <nav className="level">
<div className="level-left"> <div className="level-left">
<p className="level-item">
<button className="button" onClick={() => previous()}>
<span className="icon is-small">
<FontAwesomeIcon icon={faArrowLeft} />
</span>
<span>Community</span>
</button>
</p>
<p className="level-item"></p>
</div> </div>
<div className="level-right"> <div className="level-right">
<p className="level-item"> <p className="level-item">
<button className="button is-success" onClick={() => next(userId, name, email, password, agree)}>Finish</button>
<button className="button is-success" onClick={() => next(userId, name, email, password, agree)}>
<span>Community</span>
<span className="icon is-small">
<FontAwesomeIcon icon={faArrowRight} />
</span>
</button>
</p> </p>
</div> </div>
</nav> </nav>

5
src/components/create-user-step/index.ts

@ -19,9 +19,6 @@ const mapStateToProps = (state: AppState) => ({
}) })
const mapDispatchToProps = (dispatch: AppThunkDispatch, ownProps: Props) => ({ const mapDispatchToProps = (dispatch: AppThunkDispatch, ownProps: Props) => ({
previous: () => {
dispatch(setStep(0))
},
next: (userId: string, name: string, email: string, password: string, agree: boolean) => { next: (userId: string, name: string, email: string, password: string, agree: boolean) => {
let invalid = false let invalid = false
@ -64,7 +61,7 @@ const mapDispatchToProps = (dispatch: AppThunkDispatch, ownProps: Props) => ({
} }
if (invalid) return if (invalid) return
if (ownProps.register) ownProps.register()
dispatch(setStep(1))
}, },
}) })

49
src/components/group-list/group-list-item/index.tsx

@ -0,0 +1,49 @@
import React, { FC } from 'react'
import { Link } from 'react-router-dom'
import { Group } from 'src/types'
interface Props {
group: Group
}
const GroupListItem: FC<Props> = ({ group }) => (
<div className="group-list-item">
<Link to={`/c/${group.id}/register`} className="title has-text-primary">{group.name}</Link>
{group.about && <p>{group.about}</p>}
<br /><br />
<nav className="level">
<div className="level-item has-text-centered">
<div>
<p className="heading">Members</p>
<p className="title">{group.members}</p>
</div>
</div>
<div className="level-item has-text-centered">
<div>
<p className="heading">Posts</p>
<p className="title">{group.posts}</p>
</div>
</div>
<div className="level-item has-text-centered">
<div>
<p className="heading has-text-success">Awards</p>
<p className="title">{group.posts}</p>
</div>
</div>
<div className="level-item has-text-centered">
<div>
<p className="heading">Points</p>
<p className="title">{group.points}</p>
</div>
</div>
</nav>
</div>
)
export default GroupListItem

17
src/components/group-list/index.tsx

@ -0,0 +1,17 @@
import React, { FC } from 'react'
import { Group } from 'src/types'
import GroupListItem from './group-list-item'
interface Props {
groups: Group[]
}
const GroupList: FC<Props> = ({ groups }) => (
<div>
{groups.map(group => <GroupListItem group={group} />)}
</div>
)
export default GroupList

8
src/components/pages/directory/directory.tsx

@ -2,12 +2,13 @@ import React, { FC, useEffect } from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { setTitle } from 'src/utils' import { setTitle } from 'src/utils'
import { Entity } from 'src/types'
import { Group } from 'src/types'
import PageHeader from 'src/components/page-header' import PageHeader from 'src/components/page-header'
import GroupList from 'src/components/group-list'
interface Props { interface Props {
groups: Entity[]
groups: Group[]
fetchGroups: () => void fetchGroups: () => void
} }
@ -25,7 +26,10 @@ const Directory: FC<Props> = ({ groups, fetchGroups }) => {
<PageHeader title="Communities" /> <PageHeader title="Communities" />
<div className="main-content"> <div className="main-content">
<GroupList groups={groups} />
{groups.length === 0 && <p>No Communities</p>} {groups.length === 0 && <p>No Communities</p>}
<br /><br />
<p className="has-text-centered"> <p className="has-text-centered">
<Link className="has-text-primary" to="/register">Create your own Community</Link> <Link className="has-text-primary" to="/register">Create your own Community</Link>

2
src/components/pages/register-group/index.ts

@ -8,7 +8,7 @@ import { AppState, AppThunkDispatch } from 'src/types'
import RegisterGroup, { Props } from './register-group' import RegisterGroup, { Props } from './register-group'
const mapStateToProps = (state: AppState, ownProps: Props) => ({ const mapStateToProps = (state: AppState, ownProps: Props) => ({
group: getEntity(state, 'group', ownProps.match.params.id),
group: getEntity(state, 'groups', ownProps.match.params.id),
}) })
const mapDispatchToProps = (dispatch: AppThunkDispatch, ownProps: Props) => ({ const mapDispatchToProps = (dispatch: AppThunkDispatch, ownProps: Props) => ({

11
src/components/pages/register/index.ts

@ -41,17 +41,16 @@ const mapDispatchToProps = (dispatch: AppThunkDispatch, ownProps: Props) => ({
} }
try { try {
const group = await dispatch(createGroup({
name: valueFromForm<string>(form, 'group-name', ''),
registration: valueFromForm<string>(form, 'group-registration', ''),
}))
await dispatch(register({ await dispatch(register({
id: valueFromForm<string>(form, 'user-id', ''), id: valueFromForm<string>(form, 'user-id', ''),
email: valueFromForm<string>(form, 'user-email', ''), email: valueFromForm<string>(form, 'user-email', ''),
password: valueFromForm<string>(form, 'password', ''), password: valueFromForm<string>(form, 'password', ''),
name: valueFromForm<string>(form, 'user-name', ''), name: valueFromForm<string>(form, 'user-name', ''),
group,
}))
await dispatch(createGroup({
name: valueFromForm<string>(form, 'group-name', ''),
registration: valueFromForm<string>(form, 'group-registration', ''),
})) }))
ownProps.history.push('/self') ownProps.history.push('/self')

8
src/components/pages/register/register.tsx

@ -18,15 +18,15 @@ export interface Props extends RouteComponentProps {
const Register: FC<Props> = ({ stepIndex, form, initForm, register }) => { const Register: FC<Props> = ({ stepIndex, form, initForm, register }) => {
const title = () => { const title = () => {
switch (stepIndex) { switch (stepIndex) {
case 0: return 'Create a Community'
default: return 'Create Your Account'
case 0: return 'Create Your Account'
default: return 'Create a Community'
} }
} }
const component = () => { const component = () => {
switch (stepIndex) { switch (stepIndex) {
case 0: return <CreateGroupStep />
default: return <CreateUserStep register={() => register(form)} />
case 0: return <CreateUserStep />
default: return <CreateGroupStep register={() => register(form)} />
} }
} }

5
src/components/pages/self/index.ts

@ -1,6 +1,6 @@
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { getAuthenticated, getAuthenticatedUserId } from 'src/selectors/authentication'
import { getAuthenticated, getAuthenticatedUserId, getChecked } from 'src/selectors/authentication'
import { getEntity } from 'src/selectors/entities' import { getEntity } from 'src/selectors/entities'
import { AppState } from 'src/types' import { AppState } from 'src/types'
@ -8,9 +8,10 @@ import Self from './self'
const mapStateToProps = (state: AppState) => { const mapStateToProps = (state: AppState) => {
const userId = getAuthenticatedUserId(state) const userId = getAuthenticatedUserId(state)
const user = userId ? getEntity(state, 'user', userId) : undefined
const user = userId ? getEntity(state, 'users', userId) : undefined
return { return {
checked: getChecked(state),
authenticated: getAuthenticated(state), authenticated: getAuthenticated(state),
user, user,
} }

11
src/components/pages/self/self.tsx

@ -7,22 +7,23 @@ import { Entity } from 'src/types'
import PageHeader from 'src/components/page-header' import PageHeader from 'src/components/page-header'
interface Props extends RouteComponentProps { interface Props extends RouteComponentProps {
checked: boolean
authenticated: boolean authenticated: boolean
user?: Entity user?: Entity
} }
const Self: FC<Props> = ({ authenticated, user, history }) => {
const Self: FC<Props> = ({ checked, authenticated, user, history }) => {
useEffect(() => { useEffect(() => {
if (!authenticated) history.push('/login')
}, [authenticated])
if (checked && !authenticated) history.push('/login')
}, [checked, authenticated])
useEffect(() => { useEffect(() => {
if (user) setTitle(user.name as string)
if (user) setTitle(`${user.name} (@${user.id})`)
}, [user]) }, [user])
return ( return (
<div> <div>
<PageHeader title={user ? user.name as string : '?'} />
<PageHeader title={user ? user.name as string : '?'} subtitle={user ? user.id : '?'} />
<div className="main-content"> <div className="main-content">
<p> <p>

17
src/components/user-info/index.ts

@ -1,13 +1,20 @@
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { getAuthenticated } from 'src/selectors/authentication'
import { AppState } from 'src/types'
import { getAuthenticated, getAuthenticatedUserId } from 'src/selectors/authentication'
import { getEntity } from 'src/selectors/entities'
import { AppState, User } from 'src/types'
import UserInfo from './user-info' import UserInfo from './user-info'
const mapStateToProps = (state: AppState) => ({
authenticated: getAuthenticated(state),
})
const mapStateToProps = (state: AppState) => {
const userId = getAuthenticatedUserId(state)
const user = userId ? getEntity<User>(state, 'users', userId) : undefined
return {
authenticated: getAuthenticated(state),
user,
}
}
export default connect( export default connect(
mapStateToProps mapStateToProps

56
src/components/user-info/user-info.tsx

@ -14,16 +14,40 @@ const UserInfo: FC<Props> = ({ authenticated, user }) => {
const hasAvatar = authenticated && user && user.imageUrl const hasAvatar = authenticated && user && user.imageUrl
const imageUrl = hasAvatar ? user!.imageUrl : undefined const imageUrl = hasAvatar ? user!.imageUrl : undefined
return (
<article id="user-info" className="media has-background-black">
{hasAvatar &&
<figure className="media-left">
<p className="image is-64x64">
<img src={imageUrl} />
</p>
</figure>
}
const name = () => {
if (!user) return <span></span>
if (user.name) {
return (
<>
<span className="is-size-4 has-text-white">{user.name}</span>
&nbsp;&nbsp;
<Link to="/self" className="is-size-6 has-text-white-ter">@{user.id}</Link>
</>
)
}
return <Link to="/self" className="is-size-4 has-text-white-ter">@{user.id}</Link>
}
const content = () => {
if (authenticated && user) {
return (
<div className="media-content">
<div className="content">
<div>
{name()}
<br />
{user.group &&
<Link to={`/c/${user.group.id}`} className="is-size-5 has-text-success">{user.group.name}</Link>
}
</div>
</div>
</div>
)
}
return (
<div className="media-content"> <div className="media-content">
<div className="content"> <div className="content">
<div className="has-text-centered"> <div className="has-text-centered">
@ -35,6 +59,20 @@ const UserInfo: FC<Props> = ({ authenticated, user }) => {
</div> </div>
</div> </div>
</div> </div>
)
}
return (
<article id="user-info" className="media has-background-black">
{hasAvatar &&
<figure className="media-left">
<p className="image is-64x64">
<img src={imageUrl} />
</p>
</figure>
}
{content()}
</article> </article>
) )
} }

7
src/reducers/authentication.ts

@ -4,16 +4,23 @@ import { AuthenticationActions } from '../actions/authentication'
import { AuthenticationState } from '../types' import { AuthenticationState } from '../types'
const initialState: AuthenticationState = { const initialState: AuthenticationState = {
checked: false,
authenticated: false, authenticated: false,
userId: undefined, userId: undefined,
} }
const reducer: Reducer<AuthenticationState, AuthenticationActions> = (state = initialState, action) => { const reducer: Reducer<AuthenticationState, AuthenticationActions> = (state = initialState, action) => {
switch (action.type) { switch (action.type) {
case 'AUTHENTICATION_SET_CHECKED':
return {
...state,
checked: true,
}
case 'AUTHENTICATION_SET_AUTHENTICATED': case 'AUTHENTICATION_SET_AUTHENTICATED':
return { return {
...state, ...state,
authenticated: action.payload, authenticated: action.payload,
checked: true,
} }
case 'AUTHENTICATION_SET_USER': case 'AUTHENTICATION_SET_USER':
return { return {

1
src/selectors/authentication.ts

@ -1,4 +1,5 @@
import { AppState } from '../types' import { AppState } from '../types'
export const getChecked = (state: AppState) => state.authentication.checked
export const getAuthenticated = (state: AppState) => state.authentication.authenticated export const getAuthenticated = (state: AppState) => state.authentication.authenticated
export const getAuthenticatedUserId = (state: AppState) => state.authentication.userId export const getAuthenticatedUserId = (state: AppState) => state.authentication.userId

4
src/selectors/directory.ts

@ -3,13 +3,13 @@ import { createSelector } from 'reselect'
import { groupSchema } from '../store/schemas' import { groupSchema } from '../store/schemas'
import { getEntityStore } from './entities' import { getEntityStore } from './entities'
import { AppState, Entity } from '../types'
import { AppState, Group } from '../types'
export const getGroupIds = (state: AppState) => state.directory.groups export const getGroupIds = (state: AppState) => state.directory.groups
export const getGroups = createSelector( export const getGroups = createSelector(
[getEntityStore, getGroupIds], [getEntityStore, getGroupIds],
(store, groups) => { (store, groups) => {
return denormalize(groups, [groupSchema], store) as Entity[]
return denormalize(groups, [groupSchema], store) as Group[]
} }
) )

6
src/selectors/entities.ts

@ -1,10 +1,10 @@
import { AppState } from '../types'
import { AppState, Entity, EntityTypes } from '../types'
export const getEntityStore = (state: AppState) => state.entities export const getEntityStore = (state: AppState) => state.entities
export const getEntity = (state: AppState, type: string, id: string) => {
export const getEntity = <T extends Entity = Entity>(state: AppState, type: EntityTypes, id: string) => {
const store = getEntityStore(state) const store = getEntityStore(state)
const collection = store[type] const collection = store[type]
return collection ? collection[id] : undefined
return collection ? collection[id] as T : undefined
} }

2
src/types/entities.ts

@ -1,3 +1,5 @@
export type EntityTypes = 'users' | 'groups'
export interface Entity { export interface Entity {
[key: string]: string | number | boolean | object | any[] [key: string]: string | number | boolean | object | any[]
id: string id: string

29
src/types/index.ts

@ -15,33 +15,8 @@ export interface ClassDictionary {
[name: string]: boolean [name: string]: boolean
} }
export {
Entity,
Group,
User,
EntityCollection,
EntityStore,
} from './entities'
export {
NotificationType,
FormValue,
FormNotification,
APIRequest,
APIRequestCollection,
Notification,
AuthenticationState,
MenuState,
FormField,
Form,
FormsState,
DirectoryState,
RequestsState,
NotificationsState,
EntitiesState,
RegistrationState,
AppState,
} from './store'
export * from './entities'
export * from './store'
export type AppThunkDispatch = ThunkDispatch<AppState, void, AnyAction> export type AppThunkDispatch = ThunkDispatch<AppState, void, AnyAction>
export type AppThunkAction<T = void> = ThunkAction<Promise<T>, AppState, void, AnyAction> export type AppThunkAction<T = void> = ThunkAction<Promise<T>, AppState, void, AnyAction>

1
src/types/store.ts

@ -29,6 +29,7 @@ export interface Notification {
} }
export interface AuthenticationState { export interface AuthenticationState {
checked: boolean
authenticated: boolean authenticated: boolean
userId?: string userId?: string
} }

Loading…
Cancel
Save