Gatsby Migration (#1)

Gatsby Migration
This commit is contained in:
gillkyle
2019-05-30 23:33:49 -05:00
committed by GitHub
73 changed files with 6405 additions and 959 deletions

View File

@@ -1,8 +1,8 @@
REACT_APP_FIREBASE_API_KEY=<BUNCHofRandomNumbersAndChars>
REACT_APP_DEV_AUTH_DOMAIN=<name>.firebaseapp.com
REACT_APP_DEV_DATABASE_URL=https://<name>.firebaseio.com
REACT_APP_DEV_PROJECT_ID=<name>
REACT_APP_DEV_STORAGE_BUCKET=<name>.appspot.com
REACT_APP_DEV_MESSAGING_SENDER_ID=############
GATSBY_FIREBASE_API_KEY=<BUNCHofRandomNumbersAndChars>
GATSBY_DEV_AUTH_DOMAIN=<name>.firebaseapp.com
GATSBY_DEV_DATABASE_URL=https://<name>.firebaseio.com
GATSBY_DEV_PROJECT_ID=<name>
GATSBY_DEV_STORAGE_BUCKET=<name>.appspot.com
GATSBY_DEV_MESSAGING_SENDER_ID=############
REACT_APP_CONFIRMATION_EMAIL_REDIRECT=https://<name>.firebaseapp.com
GATSBY_CONFIRMATION_EMAIL_REDIRECT=https://<name>.firebaseapp.com

View File

@@ -1,17 +1,17 @@
asset-manifest.json,1556898463490,f723129505a2111ca263ef5479bd10b1f0efc470c951b596f13dcf48d8649c7e
index.html,1556898463490,d2791f63e8652b46a6a92df716e200ef6670799c079df85344ad44c0732a0338
precache-manifest.a096a268b779b4480b9a46727f75551b.js,1556898463490,3ee2e9cfd0185210ebe9c8fe3dd342b86254c667c2e9b51369438728c9f7e324
manifest.json,1555998067072,20792c911b58103a520ddb6b0f7bcc7ce3bceaa15480cbfd913f8b3620864341
precache-manifest.a096a268b779b4480b9a46727f75551b.js,1556898463490,3ee2e9cfd0185210ebe9c8fe3dd342b86254c667c2e9b51369438728c9f7e324
index.html,1556898463490,d2791f63e8652b46a6a92df716e200ef6670799c079df85344ad44c0732a0338
icon.png,1555998067072,40bb863e95a5ea2c01be6eba0beb6801183746f99d40eb393a6b4776a11d8636
service-worker.js,1556898463490,de05f8df6933f0d1ff232f36ce73e94b0faafe0ac3ce9882fbbacc7fc608fc2d
static/css/main.1716334c.chunk.css,1556898463519,dadd472a021e6f3502d58df7b11455a233a9a25f87ce536e693c18db20036e09
reactfavicon.ico,1555998067072,b72f7455f00e4e58792d2bca892abb068e2213838c0316d6b7a0d6d16acd1955
static/css/main.1716334c.chunk.css.map,1556898463519,c50d028b9046b1664c1a246227bee082eaf7a5f46f8857aad8963283c298dacd
static/css/main.1716334c.chunk.css,1556898463519,dadd472a021e6f3502d58df7b11455a233a9a25f87ce536e693c18db20036e09
favicon.ico,1555998067071,229055d54fe1f70f3d835e9d723ea2fef78f2af82ed7ce45efa2f4623c1c1131
static/js/runtime~main.a8a9905a.js,1556898463519,e1af5f94fdd13901b2e433d0d7607e27c01458151c35b1fe4b7feda2a32b7aa9
static/js/runtime~main.a8a9905a.js.map,1556898463519,c337bf8b58896da637a6e50ab8cfc779eb1ec42c55f8ec429030a03454a549db
static/js/main.a10ab5ea.chunk.js,1556898463491,04608cac454dbaa153b80f00064f3cb33edc8aad4a2710d56a385fd8e86a3bc5
splash.png,1555998067072,e06cb28b9a2a8275ce53eb5eead2851f684f537a6a30f0f0bf360b8813fa273f
favicon.ico,1555998067071,229055d54fe1f70f3d835e9d723ea2fef78f2af82ed7ce45efa2f4623c1c1131
static/js/main.a10ab5ea.chunk.js,1556898463491,04608cac454dbaa153b80f00064f3cb33edc8aad4a2710d56a385fd8e86a3bc5
static/js/runtime~main.a8a9905a.js.map,1556898463519,c337bf8b58896da637a6e50ab8cfc779eb1ec42c55f8ec429030a03454a549db
static/js/main.a10ab5ea.chunk.js.map,1556898463521,777ecf9469d772313dec6ecc4b5d7de7425b13cf8529747c21e02254c9251081
static/js/2.3f4eb03d.chunk.js,1556898463520,47a71ca572b94638afd405721c3ef6fb4aef5962dd0db71eb6ff9f284be1cd48
static/js/2.3f4eb03d.chunk.js.map,1556898463521,79d34370558394348a525ac8b13043f85a4b8ba81ecc94a4d7ed54ebb1b33bcd

View File

@@ -0,0 +1,259 @@
1-e637d8c9742134eeed9a.js,1559191150475,d773519988e3ed95001136377d9c3daa0bb66c8d05ad1b6348505b438faff072
chunk-map.json,1559191150480,fb1fd11cd2955b8a5626d470cba151ba68735df822ef21977fcb5360452650f1
component---node-modules-gatsby-plugin-offline-app-shell-js-4df7b9cc57a68e118679.js,1557813278931,48f5df7bf9940bc34de5a07796f504ae7e8a4e4a136f55a628b54d6c92a99783
component---node-modules-gatsby-plugin-offline-app-shell-js-4df7b9cc57a68e118679.js.map,1557813278929,8ef60e63ba5ad56d598db9b9298f5fa24bb065225193f09dca53153f86fa641b
component---node-modules-gatsby-plugin-offline-app-shell-js-87b2b9037874577fa624.js,1557877612117,776c67e827973c39d9add084f5c602205ba3c449bb422f7a1c5f97144de5ab01
component---node-modules-gatsby-plugin-offline-app-shell-js-87b2b9037874577fa624.js.map,1557877612117,2e1b8f7c386954b6f07f838e8947423f111a4ad3dff3e62af544862b3c13e867
component---node-modules-gatsby-plugin-offline-app-shell-js-987953a5e480edd8b12e.js,1559191150475,b8e5cb85d96d4eca56929f940371b51c4239360d905d13c54ecda25bb2c496be
component---node-modules-gatsby-plugin-offline-app-shell-js-987953a5e480edd8b12e.js.map,1559191150475,d8c9fe7ad0dd182449f3ab560c7f9fd26e1fe8ea9e7978579304f6519bc5da0f
component---node-modules-gatsby-plugin-offline-app-shell-js-a5adf491a4b51b678ab4.js,1557813216313,e6f7d784763351c637ca695438635900ede18ddf16b66a4bc225f16423cb042f
12-621a4fab8181238d89d1.js,1559191150474,060b1f5b0e3a304782b0cff43acec7d12913fdb26076dc7081b0fd196b8fecbd
6-7cd46754f59c438121c6.js,1557809399806,424f2cee551dec444421bb78ae3b4533c2151c4b68f919618663f188fa187207
7-48de5896cc12a0770376.js,1557811597005,a8c0867f17831389f02aee5a07ed13156325d2e2392a711e1c2a5e3476878344
7-5ac312645a29bf1fdd6e.js,1557813216311,ed6c365c5b1751de24bdb5e38e5484cef38487ed81c63fbda8c626ac989b6222
7-6e2f1d702590008337ab.js,1557877612116,89244d69b84a3af3042427da487981d5b0caac814f01ab1d3fb354963f636d18
component---node-modules-gatsby-plugin-offline-app-shell-js-a5adf491a4b51b678ab4.js.map,1557813216313,63dd693cee3393293c73a004c41eae51f56e1c429e0379e9bb46880d403ca96a
component---src-pages-app-js-11514e76c0925398c882.js,1557811909955,a7c3e7b7b5cf57e88aebd17ee39372e8a1370e9d14fc95a1ab44d1004626af0e
component---src-pages-app-js-16fe4e3d65cc37c78c96.js,1557809399807,b1e5d4565eca9b861dd1f2da45d174d70e044f7da6014e2137f2b3ef2b2b3364
component---src-pages-app-js-64bfe6508afc2cd91848.js,1557810051653,8e1c2b557e6cadb78f9c44518a4fcaed1dbe394634258bd4d560b94d2f7383b7
0-7ff3ca7547845a6eef79.js,1557811597004,c0936b373315e803a5e622b6473ac8261940d765a55c9a086017b83016c746f8
0-9f9a6583e4c048f127d3.js,1557877612116,dfdcfefa102a72ee16c3592c9ac93d92de025bd676d543c5e4b50c3d9d55e8c3
0-ab041ef2e6d4095125c2.js,1559191150474,dd97c5b802bfbe89a6b6c23cd5ee7dba7ca95784d4de31d1e851b817d8edd1da
component---src-pages-app-js-7b489f7de785c20384cb.js,1559191150474,0bdae9aeec4f420b96a9ea3b3cde3da4af168a431ddd60199e50465c4f52ad0d
0-1d5e926418bee134ae0c.js,1557813216311,8af847ec9607f090f98defb7769e4d95a30df93d81c72c5055cf3d2a5c11b821
0-3920b011ae88884f1e53.js,1557811909954,f7dabbe23155a95b0ab3bf6407520b2b31cbf8f75235ce3a746d96337fbe65f6
0-de6d673a626f526f2349.js,1557809399806,a7576b62a4d4b153353783d83631ddd2d96d5ebb0d0506c4e64c9b4f9a8ce3a1
component---src-pages-app-js-91da5dd68de736a15f66.js,1557877612118,b93126fd53acf1f24edd6bb0cf38e468d05ddf9e2c898117b9030b618df761b9
component---src-pages-app-js-92fe68136ca22600651c.js,1557813216312,8ca1d90b2b578f532ef57585885a8a93560be02a7dc9d91d7a8653790f174315
1-e637d8c9742134eeed9a.js.map,1559191150475,403f871d4eb99079539771d0f86cdef38557bc425b3c282ab5ecdb704e37658f
component---src-pages-app-js-121b0cd7c31a75bd0edd.js,1557813278929,c447094dee450c5c2c70f14bc66d1a5cad61d738ca49a97b88e257e1e7ff0215
component---src-pages-app-js-11514e76c0925398c882.js.map,1557811909955,8e69424910b314123d7f0b32d92d075195f23d66f768c25c0bc16ef32d8750fe
component---src-pages-app-js-16fe4e3d65cc37c78c96.js.map,1557809399807,eb7a85a5825ed52210c195f8f609eece01eee90ea74ebd419e25791d6bab5b7a
component---src-pages-app-js-121b0cd7c31a75bd0edd.js.map,1557813278929,06fd1d0564287da0da7d70b5ce3b11f91970b69302a432cb38da12a2e6a3692b
component---src-pages-app-js-64bfe6508afc2cd91848.js.map,1557810051652,86417b73a0ddb1f7f22eb1139a28ecb7d41b633eef45c3795ef3d3e83b23a771
component---src-pages-app-js-a76fdea18f43b1a178f5.js,1557811597005,ecc4000202fc3dcd1be338c93a6fbd6a8b8369c231a055a867ad4cda1c90254d
component---src-pages-app-js-af1366e225e5376a01fb.js,1557811050241,3d6a830876df721ff19a6789e8850a153e8748f30e1d2574b9aac8af16a533b9
component---src-pages-app-js-bdc761eeb8883dcaf92a.js,1557810007758,39945b55212c034bfe05a87e5619ba249bc80fc4cc203cd1b5f7ad5fba302b21
component---src-pages-app-js-d107a6e61077ad7586d3.js,1557811141801,b3231eebfbe19c9e918e52c1e54587645a679cc0549e1358e5a1ecd274d06d99
component---src-pages-app-js-7b489f7de785c20384cb.js.map,1559191150475,0a7dfec8297e82a6f1072966b83ab02f298414e1ad24af38bda507a8a054c4db
component---src-pages-app-js-91da5dd68de736a15f66.js.map,1557877612117,aaac7290461de3d087be8f25db73a4ea39e267870901973e931950aa12763037
7-c42318b2b0377cf8e28a.js,1557813278929,4ead356810f3595119dcc428684f173a25bf918690baa215a8d58acc93b2b93a
component---src-pages-index-js-33762b085d58d5b79183.js,1557811909954,a2e057f2537b951ae8c8c6c71d959bfce16c8ca161d92ea76cf2bda5ce1a3fd3
component---src-pages-index-js-33762b085d58d5b79183.js.map,1557811909955,a7cc6e27f9fb25aea07404619c2c23e0a0b1749a71e4b11b5482db5acd3d0f11
component---src-pages-index-js-3ede77932f1232b52454.js,1557811597004,dd98bbb30c6f71d26b022ddbe84ec667c4a10b8164f131b46b79d637b933c6ef
component---src-pages-app-js-92fe68136ca22600651c.js.map,1557813216312,8a492143fd955d1876ba4d150e6a4becaef61689f2d30ee6b71db2a6337d9665
component---src-pages-index-js-3ede77932f1232b52454.js.map,1557811597005,d602dfb5969134a90f1f2db2f983d38788e126cb2e824eca7a16ccb1888bddeb
12-621a4fab8181238d89d1.js.map,1559191150476,9bb09fa12dc8f1ad3ef95cf955afb10d4d1f742fa6befce7dac0d934aea24fac
component---src-pages-index-js-6438e06fb1d67b4e3708.js,1557809399807,149bc70e32c37609c5f07e52a8794c190b0847e1ca2039f18d8b13ee4e73358e
component---src-pages-app-js-a76fdea18f43b1a178f5.js.map,1557811597005,77d87689cf8d8d28223457a71a13fbb695dcb8250725b53c0fe653c549bb970f
component---src-pages-app-js-af1366e225e5376a01fb.js.map,1557811050241,a487d92a97bbc142b9e650a76a0a8f86578916a4f1eb3c91728356b1169678fe
component---src-pages-app-js-bdc761eeb8883dcaf92a.js.map,1557810007757,2e706f7d36cea5dcd091dbb5b8ea6245cab10c5b60d160c1073e99e95b78fd7b
component---src-pages-index-js-afd3974173a9f520ca8e.js,1557813278928,1830bc93b51dbb80c899a3851d954b55535f296927a706f00e482dc59592b3be
component---src-pages-index-js-afd3974173a9f520ca8e.js.map,1557813278931,f10a3d084249a23614177379cfa7905deccaf89e06348b3b5447745cf3b19627
component---src-pages-index-js-6438e06fb1d67b4e3708.js.map,1557809399807,3272a2b613c4da0b44957c4144665e4e74aba875e49b8b1a7b590a68e55b08d4
component---src-pages-index-js-73ba9f81d523e25221d8.js,1559191150474,314fcd639ace8093e39b187094f17c2a160dcf64047f6b176ebca4994a530544
component---src-pages-app-js-d107a6e61077ad7586d3.js.map,1557811141801,8ad10232fdc319ca7dae6f817d0526bd6aa0445f2d4bcfceaaffa72553c398df
0-ffd58bca1c1d419808cb.js,1557813278928,bcae49aae328a5d854849040795b4bea811ce3308abed67f765b4d9dc81d16f3
component---src-pages-index-js-b5c05671b8f1e94dbf09.js,1557813216311,e6f0c7dd39f4c9270b2866ada671f692783b2968a918f22841e6b5c444f6ae92
component---src-pages-index-js-b5c05671b8f1e94dbf09.js.map,1557813216312,5469666e0b0203652d87896f84bd025cd0385a8123f7aa42ef1e3fa4224108dc
component---src-pages-index-js-eff01f6896a59a2d7c53.js,1557877612116,b2085ca07625d8ce9e6990aee305f484da7db76779f227625b7cb34252ca48ba
component---src-pages-index-js-eff01f6896a59a2d7c53.js.map,1557877612117,0d03ece318a95d2acd1d9c002a9603bd477227cce722c6012912cd6f49f0a978
component---src-pages-login-js-2bd423148f09d42974a8.js,1559191150474,a7bc7ee356a571edaaeea234c04df8ad72b30aa3f78197b53cefbfd7cb597cd6
component---src-pages-login-js-2bd423148f09d42974a8.js.map,1559191150475,aef8c24e6439eb0b0bf8651f523bb4d28959321e20c0d9f6318816bba114c26c
component---src-pages-privacy-js-7455142186b1fb391cfb.js,1559191150474,1c50ad66ebbe295ee6ade1e1710e6f1dfdc2f2a318c60f128248794b97884ed1
component---src-pages-privacy-js-7455142186b1fb391cfb.js.map,1559191150475,38d500f6151173acb4caa8482ee1139c4ba7d7017f2bd49c9b2c0de1baf7f8f6
component---src-pages-register-js-807ae98fc7ee9ce582ce.js,1559191150474,c6d2ffad67d4bff8cd74a9f32efe334cc10c67699dc4e4eb59697fcce2d18824
component---src-pages-register-js-807ae98fc7ee9ce582ce.js.map,1559191150476,a83be1b0fe237dc89949c5f79f6a60380e09f8fc060881d6df15b55efc6005b0
component---src-pages-terms-js-b061df86d1349e23ef0f.js,1559191150474,714a7b2f5ed732ee120d4f567e647d9bd6edb24654754f1cd6be79bfa8ef7c9f
component---src-pages-terms-js-b061df86d1349e23ef0f.js.map,1559191150475,9fa6ceedcbe9d779c26613ecbb6a899641d9f3da44eda88c6260ef887eb084a5
component---src-pages-index-js-73ba9f81d523e25221d8.js.map,1559191150475,cbaf4270f052d4c2744daac7293d6ea612174f4911294210a5d46c63b071aa5a
favicon.ico,1555998067071,229055d54fe1f70f3d835e9d723ea2fef78f2af82ed7ce45efa2f4623c1c1131
icon.png,1555998067072,40bb863e95a5ea2c01be6eba0beb6801183746f99d40eb393a6b4776a11d8636
idb-keyval-iife.min.js,1559191154709,9c4125183adfe3e08e424eed0f3f811c0c512af9ce287ab17b375656a0a4873c
index.html,1559191154665,74de5c7e94e5f0799822a0b9e3f354dde5e19df734efbd7dbd67c9284264f0fb
manifest.json,1555998067072,20792c911b58103a520ddb6b0f7bcc7ce3bceaa15480cbfd913f8b3620864341
manifest.webmanifest,1559191135607,ca070aceb9b306afe6b908b63b264a7356955552172c49bdf58805050c0a013e
pages-manifest-121566f92b19b2952737.js,1559191150474,bf7e19860670a6dccca494c61ec0405ab80e5182bb676f4e14a9c451befd83aa
pages-manifest-121566f92b19b2952737.js.map,1559191150475,487251b34a7371d969facd05520f2a2ab81c4e2d93b3eeeeed60a227723208c7
0-ffd58bca1c1d419808cb.js.map,1557813278929,bbe47b2a02fe210665ace9744b05ef02649b64bbf7f927b0598d62d6eef345e5
pages-manifest-3ceff0312593811de18d.js,1557813216311,f139e9f032774fa0af2a3cb80e7f0ef99df619ca6e8ef8c07406d4f9e3ec211f
pages-manifest-3ceff0312593811de18d.js.map,1557813216312,a784318fdede6f5673debd63195401650400424e1f79c10b9c38dc24e2c378e9
pages-manifest-81118a764c3e6e30d44a.js,1557809399806,4bbe7147c22b8cad92ce5d0e8af6be61467d7e34cf1735b836a43908450b534a
pages-manifest-81118a764c3e6e30d44a.js.map,1557809399807,6f30bde9776e193d6a6af1ea806fbe9f71490fb67bd889a94a700da0b25fcb04
pages-manifest-8509e70eda082230a7ba.js,1557811909954,10563ffe7ad63205e8b2ba40ade8a8396249bee0cd53f1ed0274cae6a677bb1c
pages-manifest-8509e70eda082230a7ba.js.map,1557811909955,4fee75d4ff2ff9df7a0e1d9d6c3aee60ef06d2e77165977857f5400e07901471
7-c42318b2b0377cf8e28a.js.map,1557813278930,e770d051445bf67785537e9f87183c85a044d1d600df05a7e709038b9cf616e4
pages-manifest-aa19bb1317098ff68e7e.js,1557813278928,fd25d37d6bedb33c6b08ff39bbb85ae10289045004c7b059e9f74f3b4f696a21
pages-manifest-aa19bb1317098ff68e7e.js.map,1557813278930,4b13daa2f616df52d3b638fb802c351cf81c2df55c1b8d51648d13ce0488f6f3
pages-manifest-e2ee64996c474b49ea09.js,1557877612116,acd632ec43db64084c9b8ffaf5e2ef51e0b61e311e7fd981ba8d09650452614b
pages-manifest-e2ee64996c474b49ea09.js.map,1557877612117,68fb144a220a193ebf11535b23e9084e670fd905dfd779347f4c5856123abb1b
6-7cd46754f59c438121c6.js.map,1557809399807,98f28a349454c079d7ebbfb78771634f52c4f6ffec47f2d29ddacbf5d4d02ba6
reactfavicon.ico,1555998067072,b72f7455f00e4e58792d2bca892abb068e2213838c0316d6b7a0d6d16acd1955
sw.js,1559191154742,4ac5f3e5e281ed35452daa0e1d8b8d9b20fde6b280edf5549e234e9f139df568
splash.png,1555998067072,e06cb28b9a2a8275ce53eb5eead2851f684f537a6a30f0f0bf360b8813fa273f
webpack-runtime-16d3d5c6c8f562bea7bb.js,1557877612116,d51a0fba8c8ffebcc257da377f022360c37134a3b94d7722c5aabd4a646fc248
webpack-runtime-16d3d5c6c8f562bea7bb.js.map,1557877612117,fb08c32ecebf8a5f824862e39be3d2afb9f9873392048fc119d3316ac19cfe9c
webpack-runtime-232977f20ebd1288102f.js,1557811597004,34b5958fd4171f2ea2eccd9d47379436ebf4bb2921f7aad01e5e7bf72c3a0438
webpack-runtime-232977f20ebd1288102f.js.map,1557811597006,f77ce094ad617f99ff2ded5c86d442c74b5c4eaafe4b6a291b273ddf891aa377
webpack-runtime-3bbe49222b61394661af.js,1557810051651,1da6d945c59c54b3c140ec0706b1c62f0346854ca7754e0808d650c618325c32
0-9f9a6583e4c048f127d3.js.map,1557877612116,1d3027ae5649795eee4fe684395ba8e341f50cfc658a1dea627779fae452852f
webpack-runtime-3bbe49222b61394661af.js.map,1557810051652,33bd5e7eb44b28d505ecd97f83d7f496caa2ca2cb0288147ea4fd77b7671a6b7
7-5ac312645a29bf1fdd6e.js.map,1557813216313,6e0ef24b1ec1c56454cadf4c46fe13520636868a179481a64a38514c93a0083c
webpack-runtime-71b1de03f480e8a05b86.js,1557813278928,75e01d5f87df1a9176f52448edd9de0e7e8192438f62101866291fff6b98f6ae
webpack-runtime-71b1de03f480e8a05b86.js.map,1557813278929,1c201fa72239a89d4695bbf362bd70653158fadcfceca260ddcee0702fde5fa6
webpack-runtime-730f869f42b97ecf0111.js,1557809399806,348e3496f5d3c7f62197522c1a66d17c1fa341578d7a4c4be77160f67b4d4343
webpack-runtime-730f869f42b97ecf0111.js.map,1557809399807,629ad7214e8fb9cebb6bac1f5ebe674243703a29300026d553f367cc11e2cb22
webpack-runtime-87a76d3c03698d51092c.js,1559191150474,6c899d0e0d103d1b8e50121c70c06da8672a9747e2ffe331e9b2972e95ddb4e3
webpack-runtime-87a76d3c03698d51092c.js.map,1559191150476,3ec44450f02d9b3229f28ad55bd8f9d221fe06f2200b3cd9b76dfb9fe0cb6dda
webpack-runtime-90cd096772b6aa22a87e.js,1557813216311,da1ea42c05fa0b95f66d9d058f673ac98f7c396065c8623392f02da949bc65cb
webpack-runtime-90cd096772b6aa22a87e.js.map,1557813216312,d958be870d9edea0fa0dbb8f75322c60a5ac59443c1db7c8d3d02d26925233cc
webpack-runtime-9ab98137c8d259164d47.js,1557811909955,f69a4381fba54866917cec54e1bc5ecf652674df36cc361ed9b3d0a40c459660
webpack-runtime-9ab98137c8d259164d47.js.map,1557811909955,d53efc503817af7dda11ab5627fdd1fa0707ac38cbfd928661605a001da3a1d5
webpack-runtime-b67f32eecccd6f4df46a.js,1557811050241,ad0561ae294e6798427ea0ab19bd35a0c218b35758a6a771292fbbf1522acd45
7-6e2f1d702590008337ab.js.map,1557877612117,7b9471190ece31effc7f2b5152654bd366350b75d56ff559849838ce3f10219b
webpack-runtime-b67f32eecccd6f4df46a.js.map,1557811050241,21f2836575f163fb393f8e5d84bdc84ae70d8bf04f4477182d8c4258d4b642b7
webpack-runtime-bc44a0a762f560bf5409.js,1557811141799,24e2349a4f5007de1d12713681eba0abab7e820a391078d5bf79d94d395cd142
webpack-runtime-fbc6a54768345aa362a9.js,1557810007756,5d99469d4243a82bb4ffd275016ee20e51a67e72c751c5720be30500b80199f6
webpack-runtime-bc44a0a762f560bf5409.js.map,1557811141801,296d7901183e1fa2114d4f0496b17c21266f4909f92198658356697554fdf46a
webpack-runtime-fbc6a54768345aa362a9.js.map,1557810007757,b949e1b3c207244a1fb6848f2569d63b47755e0e8addab8be95e82a5a8665976
webpack.stats.json,1559191150481,1d89aacd1d82bfd41bb10491bffbb145c16e62e76c89c6681efc95e2155c4624
0-ab041ef2e6d4095125c2.js.map,1559191150474,9f8580210a0589b8a0441f2f48d419bb99b4d8917745dbcc5ba311c91cc34882
app/index.html,1559191154665,729c7d09effe5857e14880dee28ed047ce3007f55fbe45d858ec3f10800e9e4f
0-de6d673a626f526f2349.js.map,1557809399808,21d12e191499dc0b0b89acace0d0dad2029e3158e727bb0f1d81f1809f7ae280
icons/icon-144x144.png,1559191135548,b15ffb4ec2a0a0c3b94fec11ee0eb9c979bce08539e6d2643955c0a79253f858
icons/icon-192x192.png,1559191135564,bb1c5ddc47a848a01393c61fa518ea870f852c92ccc779e9dcbd86c85e2a74aa
0-7ff3ca7547845a6eef79.js.map,1557811597005,6606b3d016eb59cc84d29abbe412bd0df2bce0c58b7b943fdec5b141e304af00
icons/icon-48x48.png,1559191135527,e6f681244044cc32ddbdd5475113e6c69b1b897c82ca86fdbe99ef8bc7734be7
icons/icon-512x512.png,1559191135576,2c57aa583e667577bb1895d8b7c1d0d8894645aa1336576718ca26449fd5d02a
icons/icon-72x72.png,1559191135536,aba2399ff54f53ec86eb6aaea94c56024b5590c549c71675f993f84ce30c931d
icons/icon-96x96.png,1559191135542,01a29c55127108c1cb649e568e0746f610828b910db0beacc925eadba64eacd0
login/index.html,1559191154667,f48e485fc30ad9b0bf36a7941f4ae5c7da9e744a5ddf81061ac85e4c16dc9cb0
google-fonts/s/montserrat/v13/JTURjIg1_i6t8kCHKm45_dJE3gnD-A.woff,1559184315809,daeb49428c9086a93fbd9fd5c4b788c1f609dd76e61a79eb6bb263414c41a2e9
google-fonts/s/montserrat/v13/JTURjIg1_i6t8kCHKm45_dJE3gnD_g.woff2,1559184315896,ff876f7e3a23118dd4e74aedbe78472aa3316268aebee52ee408c96e60920cc1
offline-plugin-app-shell-fallback/index.html,1559191154665,a28b87d8f4dd9e4dcae048b8fbc38fe278512e81e74d2deecf15e085f71c06d2
privacy/index.html,1559191154668,72adb08ca4d0130448fb9986d24d323ee37e76b9ce960579347344de1646465f
google-fonts/s/montserrat/v13/JTUSjIg1_i6t8kCHKm459WlhzQ.woff,1559184315994,2786ba68a3c0b7323ed9a5b5199435f3186c0677459edb68a58359d156fa52c1
0-3920b011ae88884f1e53.js.map,1557811909955,ccd4e005310a5f7f37dcdcbfb78636b717f5f608462fa178741f9da59a6f343f
7-48de5896cc12a0770376.js.map,1557811597006,65389dfd7f9dfb8401d6a730ae2c29f9c6c7f98e521b6af80452f0a437efde88
icons/icon-256x256.png,1559191135579,851415406ee67057fa625de5b6072bf0fb4b4e26c04bf65993de6c8bdd678382
register/index.html,1559191154668,2d977d0dc7a0d07eebed5c414bf56d088b279da05357afa78096ae116f653cd6
0-1d5e926418bee134ae0c.js.map,1557813216312,ca87eb43611e9432755bd51ffbdab2173a4ad4099cff7095fd2c0e24c73dd08c
static/84b155cbc6291cd983595a8bc1ba4360/1f686/landing-graphic-dark.png,1558210993774,3226c17652cb835baea5db1628b77d93b2810789cba0454cb20bf669aefb0490
static/84b155cbc6291cd983595a8bc1ba4360/a79b5/landing-graphic-dark.png,1558210993799,4fd9ec33da04e98ebf840d453d2567f323adc933c41d9217110e56d856145b49
static/84b155cbc6291cd983595a8bc1ba4360/af214/landing-graphic-dark.png,1558210993794,66909fa88ba86ff3d85973cca3f798c88bec847f62a39e0400b62e203cf99f83
google-fonts/s/montserrat/v13/JTUSjIg1_i6t8kCHKm459Wlhyw.woff2,1559184316101,fbd3baccdf94dd3595a3b259c5d917ebdd6508501b18d09bfb81cffd21b0f7c3
static/8c1ce00c604ffec2de59d6b88ea38bf5/1f686/landing-graphic-light.png,1558210993702,4c443c2ab4cc2619a4a040529349194f1dedbbe383c818c4ba5eba60eef8d8c9
icons/icon-384x384.png,1559191135604,351f5a1534c56febf22093d49eed3cbe35ecf63032bfdfe0cc777a43b30eefc7
static/8c1ce00c604ffec2de59d6b88ea38bf5/a79b5/landing-graphic-light.png,1558210993716,1d743d97a5da0b16afd4dc9620d8f56e6ed2a5956b0da55b70079a54e82167dc
static/8c1ce00c604ffec2de59d6b88ea38bf5/af214/landing-graphic-light.png,1558210993716,35af3e55d7e54247e907d6a4ea576f03416d7b22df563ea0d54c0435297cf474
static/d/1671509314.json,1558210681132,9bffff23084e1fa4827aaf8ada09e5c35255f45a7cc78390a157b85997905513
static/d/3828651140.json,1559191135474,6cfb20d081a8779573c9f17d748692baa5fc50459facd9b785e2c879ce39bea8
static/d/107/path---dev-404-page-5-f-9-fab-Oe6ArANhU8Vz5qTmGlUMUTGz0k.json,1557812561298,d73c632a5ce5b4f2a3660f396c3e9c864b4f3f91d6c37b799907ef64864f4fd8
static/d/109/path---user-50-d-1f4-0SUcWyAf8ecbYDsMhQkEfPzV8.json,1557791272922,3c6f21270f201243aeb6103344b0c9b7fbcdfe7684d48ad45f54ef9c74287673
static/d/140/path---index-6a9-0SUcWyAf8ecbYDsMhQkEfPzV8.json,1559191135480,3c6f21270f201243aeb6103344b0c9b7fbcdfe7684d48ad45f54ef9c74287673
static/d/142/path---dev-404-page-5-f-9-fab-bfDVx85rKBSZKYobhh0xz8NXpN8.json,1558143810414,e173975cc9463a37680e56a0b7c346abac36c1d4d92f756790366e94511cc994
static/d/156/path---dev-404-page-5-f-9-fab-0lHfmoILn29P5B3VzoKMRN0MGQ.json,1557812408750,ed41989158051bcf6c05c2627471057240158bc036f81cc4561f2655fa09d417
static/d/256/path---app-user-2-f-0-9a2-EL1Va90kR4oqYPhXw5v5axaD3JQ.json,1557812404221,8d87d70b92fdb651f5428d9a53d1213f9956c477655b9ec5393fe5f74ed67c83
app-895d651bee860628b349.js,1557811597004,802cca0a5571d08249597d7c15e22a5f18ad4ad6f884e9475cc08562d89afa81
static/d/266/path---app-f-4-d-201-0SUcWyAf8ecbYDsMhQkEfPzV8.json,1557794915702,3c6f21270f201243aeb6103344b0c9b7fbcdfe7684d48ad45f54ef9c74287673
static/d/274/path---dev-404-page-5-f-9-fab-g7SNJLoOWmRhQGZS7AT9qXVmis.json,1557960913927,ded6b64295f9bc0286c3afbbf2e43dea75630429517b601bb0acf9299129df9c
static/d/275/path---dev-404-page-5-f-9-fab-7lHBP7GsKlZkMPCTRxgKxLrkxBM.json,1557791394482,575176c53b51672ae3e669e3e2c88676647d4945d4ce637c1ba9b1abf56d7ffa
app-fb394d03a36f4752b604.js,1557809399806,49d83bbb318fa9f029d012879e9994634c4e4ac7aea60e668ce0729f023ea5bb
static/d/285/path---dev-404-page-5-f-9-fab-PpN8VPOAUwhi0z7MBHklrX1I.json,1559184316660,1a16a9eeacd5dab2ea68c50ce8fd503655c0ae403645605a6d3ebfa4560af33b
static/d/296/path---terms-8-c-4-c6a-0SUcWyAf8ecbYDsMhQkEfPzV8.json,1559191135482,3c6f21270f201243aeb6103344b0c9b7fbcdfe7684d48ad45f54ef9c74287673
static/d/329/path---dev-404-page-5-f-9-fab-ypDXSKzc49eH9uf0n35qJvC7Zqo.json,1557585087142,4ed19f97246e3a4558314e3a2dc15bf718720d14380d985b27015f5e4924c9f9
app-eadb88dc3e8f941f2ae9.js,1557813216312,ed2e35ad3dde974fe175460987912be736281fd672d22b2603a2738bcf7d0b0f
static/d/330/path---dev-404-page-5-f-9-fab-eOYp6h7SVMiZ3XWAglV9JDgWjCc.json,1557961609064,e4d04a5ac60cdf8242130174f8a908c3ce41009441ffed81d96e7c8948f645a3
static/d/386/path---dev-404-page-5-f-9-fab-0SUcWyAf8ecbYDsMhQkEfPzV8.json,1558210629760,3c6f21270f201243aeb6103344b0c9b7fbcdfe7684d48ad45f54ef9c74287673
static/d/403/path---user-00-d-0c4-0SUcWyAf8ecbYDsMhQkEfPzV8.json,1557793005559,3c6f21270f201243aeb6103344b0c9b7fbcdfe7684d48ad45f54ef9c74287673
static/d/456/path---dev-404-page-5-f-9-fab-oq8bWxqDOoqJ57xcxUwyedhpfrQ.json,1558144315175,818197c97bfe0f373fdbbdb61330c4595294e78c36693962e6359936e3e39630
static/d/482/path---year-30-b-72f-0SUcWyAf8ecbYDsMhQkEfPzV8.json,1557791262554,3c6f21270f201243aeb6103344b0c9b7fbcdfe7684d48ad45f54ef9c74287673
static/d/511/path---privacy-4-ed-e67-0SUcWyAf8ecbYDsMhQkEfPzV8.json,1557962570873,3c6f21270f201243aeb6103344b0c9b7fbcdfe7684d48ad45f54ef9c74287673
static/d/53/path---app-f-4-d-201-EL1Va90kR4oqYPhXw5v5axaD3JQ.json,1559191135480,8d87d70b92fdb651f5428d9a53d1213f9956c477655b9ec5393fe5f74ed67c83
static/d/535/path---dev-404-page-5-f-9-fab-JZOWdQRbQwXKp05LLHAfc8WxTg.json,1557962570874,b10e7a36265a8e5bcf6e0328e85878f358105921e1eee8bb0c6cd6ded5f0b185
static/d/543/path---login-829-9b2-0SUcWyAf8ecbYDsMhQkEfPzV8.json,1558144315175,3c6f21270f201243aeb6103344b0c9b7fbcdfe7684d48ad45f54ef9c74287673
static/d/562/path---register-ab-5-675-0SUcWyAf8ecbYDsMhQkEfPzV8.json,1559191135482,3c6f21270f201243aeb6103344b0c9b7fbcdfe7684d48ad45f54ef9c74287673
static/d/566/path---app-user-8-cc-07f-EL1Va90kR4oqYPhXw5v5axaD3JQ.json,1557812561297,8d87d70b92fdb651f5428d9a53d1213f9956c477655b9ec5393fe5f74ed67c83
static/d/603/path---privacy-06-e-82f-0SUcWyAf8ecbYDsMhQkEfPzV8.json,1559191135482,3c6f21270f201243aeb6103344b0c9b7fbcdfe7684d48ad45f54ef9c74287673
static/d/604/path---offline-plugin-app-shell-fallback-a-30-c5a-BawJvyh36KKFwbrWPg4a4aYuc8.json,1559191135481,56d9a0c519587d158bf2b14c3e85d85c5058fc2c417584d0aed283a32efe7c3c
static/d/66/path---dev-404-page-5-f-9-fab-tmTaVcQPdlVyr5nMFZ3AD2ZsnIQ.json,1558144324418,334d166a0ea0f86485684e01575fd9e1aae74ba7f6698e32e6b2479934d401cc
static/d/7/path---register-233-7b8-0SUcWyAf8ecbYDsMhQkEfPzV8.json,1558144320503,3c6f21270f201243aeb6103344b0c9b7fbcdfe7684d48ad45f54ef9c74287673
static/d/710/path---dev-404-page-5-f-9-fab-0afLBjpmUk0liuixjBX4a6ggE.json,1557960918411,3839fd636684fcaa7c7cd26f445f4d9028dd7c772b12be48e890820af2c430a5
static/d/750/path---dev-404-page-5-f-9-fab-KqGoYC24nNalnT08MydFwo8PN8.json,1557791262554,8608bb7fa02f6af4f181d5e2b8dfd5378c8dcfa82b35eec702fbd3911b415eda
app-fda3ed7d59bc2e65cbed.js,1559191150476,7610f92a7c8843a26880c54b21412476da0f72f82d26d5557776fef3c18780db
static/d/750/path---dev-404-page-5-f-9-fab-olHuxZO3hbLTEnU5n89Yy1aON0M.json,1558144320503,7c20fca2a82b18be6295c2099f390ec39dd64f28f29a059106cbe4d4c1909219
static/d/754/path---terms-c-57-a14-0SUcWyAf8ecbYDsMhQkEfPzV8.json,1557960913927,3c6f21270f201243aeb6103344b0c9b7fbcdfe7684d48ad45f54ef9c74287673
app-c7f161e371102bbd741c.js,1557877612118,648944d783a48ee240ad3a5c8ab0fb949976115aea0ff2cc1adf1acda6faca33
static/d/776/path---dev-404-page-5-f-9-fab-nA29NXyrUxPizjd0q3r6q4UsXVQ.json,1557793409115,032ba8e508f5a5a287afa363786a8b627f2debaa014f3d6efa410957f23a12e8
static/d/792/path---dev-404-page-5-f-9-fab-7spOoyDeLUxd48g5NTqrcPngVMw.json,1558144328419,884ac15a5d8505b779821497b4d0e98d5a7fce270e2952624158d2df7395812c
static/d/812/path---dev-404-page-5-f-9-fab-fdIM0MsRHSYfhFLvj6uTgXL93RA.json,1557791397877,22a2395e99b6a91e91517d55855511f32e08e3272809f149319fc7811a838d20
static/d/829/path---dev-404-page-5-f-9-fab-o7af702RpOxvpRo1x1JR8NttFI.json,1557790335151,f4d17034ad75fb79d4df9cf46d875f09d1964c8d84637a8c5968425e61b55f62
static/d/851/path---dev-404-page-5-f-9-fab-SiQrH1RpDdAQNsRvRvTTlCkTa9Q.json,1557812404221,9d4b15f1a4008cbbc7211ff3190db5809869bbff55b6022aaf032fd0e6bdffc6
static/d/852/path---dev-404-page-5-f-9-fab-ckUv9SblqtFNnH2wT0VK4F8I.json,1557793401208,8a7c1d5486304983a72e629436ea85081baa09e7620c982290fee1078d5a13df
static/d/854/path---dev-404-page-5-f-9-fab-RYlGcedJe6I3R75B3YCt93ZfpRA.json,1557962577713,848c6ce156c02f20f3805b8767b82d133acd12dfac177a3a6ad717886ff6861c
static/d/86/path---dev-404-page-5-f-9-fab-pkUqVDZsKmlJGIKI1kXB5UaDw.json,1557793417755,b61656ca79670f8030e83735b63a7022a04d76e40c9c6668320706a5ad2c4ff7
static/d/89/path---dev-404-page-5-f-9-fab-e0fJdkAO2HvW9LcacfoFl8DTXfI.json,1557959468569,2500e17b9026f43ec09d8f10c7371e82466b8f6eee8230b434131e83e8314f5a
static/d/936/path---year-8-dc-5b4-0SUcWyAf8ecbYDsMhQkEfPzV8.json,1557793005559,3c6f21270f201243aeb6103344b0c9b7fbcdfe7684d48ad45f54ef9c74287673
static/d/944/path---login-557-29c-0SUcWyAf8ecbYDsMhQkEfPzV8.json,1559191135480,3c6f21270f201243aeb6103344b0c9b7fbcdfe7684d48ad45f54ef9c74287673
static/d/953/path---dev-404-page-5-f-9-fab-XPHjMMR3cHucHYiNAKDAM1g22w.json,1557793005559,09290bbe063bcf24e0fa983cdb5708ec806fc4dd3b4fa896cdad938e5915a61a
static/d/970/path---dev-404-page-5-f-9-fab-avTDW6yAYqjQnpQf8FHK6Ay9OZs.json,1557791272922,2ffa3c1950ede19556cb5e12d87f8645fa7e8564ef43d8533c6d5338a52b03a1
app-c5b938aeccd7fc80368e.js,1557810933019,2ef1cbf070eee83297a5cfd52657ada5aa32effea00af6b25f7f0bd56089e9a6
terms/index.html,1559191154668,5ecf9f4b31e4795d759034aeef8cd292ee537a8ed8c03b2f8b18ffec9482d3e7
workbox-v3.6.3/workbox-background-sync.dev.js,1559191154719,ac804438e84e5510f7af402494af59b8d3e366073e931a47585058c6ff2b6415
workbox-v3.6.3/workbox-background-sync.prod.js,1559191154727,b500ff18680a9041f2e3a90166fb2cbc76ae34fcc4a7add25d9cf098bb83b800
workbox-v3.6.3/workbox-background-sync.dev.js.map,1559191154719,00ee13919bc4d5f6d75cc4779cfea5c1cb8ae54528d05f35c1bf52da0687eeea
workbox-v3.6.3/workbox-background-sync.prod.js.map,1559191154719,e62a639ab7416db8a8aedebd1d70521de44b9282f0bbc686598789839ce7f9c7
workbox-v3.6.3/workbox-broadcast-cache-update.dev.js,1559191154720,a82f9b92bc6364e6c19aef4061137baa5f0018b186095c5bb7c6b80d6c5dd80f
workbox-v3.6.3/workbox-broadcast-cache-update.dev.js.map,1559191154719,bcde8c74931fe0ce9d4c198665fcf64faf1ad1018c5d8f7606117139f72fb1fa
workbox-v3.6.3/workbox-broadcast-cache-update.prod.js,1559191154719,b573854c912b9327790d35b0b33150b620fb74b15ff2fbe592f9da1c258c2894
workbox-v3.6.3/workbox-broadcast-cache-update.prod.js.map,1559191154720,a5c68b36e3ae09571e7162f2568520bf2575f6ab31d73aeb017f93e530d7089a
workbox-v3.6.3/workbox-cache-expiration.dev.js,1559191154719,48f75ddb3c9f2cd7d19d3d1315c5dede05a41e6f66b729edb396e3cb3b653709
workbox-v3.6.3/workbox-cache-expiration.dev.js.map,1559191154719,d1f035224e4b6e46c68c9b94d9bbcd7e20f10714a8bb372e5b5dcc8c8bff0248
workbox-v3.6.3/workbox-cache-expiration.prod.js,1559191154719,3099f5d0a38688d0192daba6a066fcf8f6872e0f828e8035d05d9c705fcf27e0
workbox-v3.6.3/workbox-cache-expiration.prod.js.map,1559191154719,4748da4b302ce375413faff0aa145964bd851a1e756e0e507123326ad2cd32cc
workbox-v3.6.3/workbox-cacheable-response.dev.js,1559191154719,8e074782705c28c0b116946a027163ab9dc81f7a955330f4a23915c7d773e4d2
workbox-v3.6.3/workbox-cacheable-response.dev.js.map,1559191154719,6baf8b5f9be8df30a29e568574cc70d55e26b725226d134736f5e9fe0db7039c
workbox-v3.6.3/workbox-cacheable-response.prod.js,1559191154719,02b3c02c3e4b172065cc363f549522a467f1193e8932fc1c73cb3ea96e6b43ce
workbox-v3.6.3/workbox-cacheable-response.prod.js.map,1559191154719,6a39e7c4d29693321d6a8aca0142638b33a4df779bca0d6264d97598c479defa
workbox-v3.6.3/workbox-core.prod.js,1559191154719,93ee32c25bf6a164429f1c5d6c82c0ca0b903bc19374ba11e427976c3b75f6b2
workbox-v3.6.3/workbox-core.dev.js.map,1559191154719,8b2dd6c35646bcec6c0398334285ffc0b8988a9f4d128b4fc135a9eccc29c561
workbox-v3.6.3/workbox-core.prod.js.map,1559191154719,9434c6eae79466484110e0feb20d76647b2992e35dddd2facf9b1fb9a87baa4b
workbox-v3.6.3/workbox-google-analytics.dev.js,1559191154719,8aa7efc7901a56a6eaba5e79e03ef600cf4506b1300cff7ff8ddb1cabe4c75ce
workbox-v3.6.3/workbox-google-analytics.dev.js.map,1559191154719,24eeb53b75980b683532c462a161d46f45f8e0ed06dde2e2e25cbabc37d9ee05
workbox-v3.6.3/workbox-google-analytics.prod.js,1559191154719,7002e3236691685f59f38903a3077627ac780a3c8e68441e6580879d1af10ac4
workbox-v3.6.3/workbox-google-analytics.prod.js.map,1559191154719,ab0982e5eb954127a5d56b72c90203072c0b392127c2397b871d0822daeab8ed
workbox-v3.6.3/workbox-navigation-preload.dev.js,1559191154719,b010c30a8487588d296e11c082fd9072593810abc1ee4abf810f6ce8fb703e54
workbox-v3.6.3/workbox-navigation-preload.dev.js.map,1559191154719,8255c995f30096fa4f3dc33d5b7f0b9a127f5496b373b2efde57015faf3a3fb9
workbox-v3.6.3/workbox-navigation-preload.prod.js,1559191154720,914ba19f33a24e0d3cca58281d1d22bc948b3136332756184c7497142773f751
workbox-v3.6.3/workbox-core.dev.js,1559191154719,e97174cd772178c3f0d6d4cfaafd39fa2ee4da2789e09e5d98c02771c46178d9
workbox-v3.6.3/workbox-navigation-preload.prod.js.map,1559191154719,94b8af64cf134df1196eac25dd95d614b5877be1147f374953c5978909b2e8ae
workbox-v3.6.3/workbox-precaching.dev.js.map,1559191154720,c765d71f4c125e1c8757a7017276e6df3f5dfd31d1fc598d4323d9780382b0e8
workbox-v3.6.3/workbox-precaching.dev.js,1559191154720,3c0dc543e36cd60908d638d3805b6abe16df6be77e926657a44beccbf3f0f582
workbox-v3.6.3/workbox-precaching.prod.js,1559191154720,474d3dcb24c8a1432f4cdd2b825193b63df6df9341a331553c415c2f834b02c7
workbox-v3.6.3/workbox-precaching.prod.js.map,1559191154720,b9e82010a346e1a02ce1d9d3a7fe6087caec916490b8fea9fc0ce49157828846
workbox-v3.6.3/workbox-range-requests.dev.js,1559191154720,11c6b6bbf381791984dff07de8a55f8cc6f4233b4f189266140193314f776a38
workbox-v3.6.3/workbox-range-requests.dev.js.map,1559191154720,e4942562c7a00de4b4244d286763373cf084f8ce108bf48a9bc90ae98e3424e6
workbox-v3.6.3/workbox-range-requests.prod.js,1559191154720,6c54ba842d1142ad630554cd43b716d0e1967779486d08db2c9055cbd98735a3
workbox-v3.6.3/workbox-range-requests.prod.js.map,1559191154720,14934216d94d1709106833144eb0ea6d93038594d6435c39f7d30af5e7919d04
workbox-v3.6.3/workbox-routing.dev.js,1559191154720,2c628cfbb0d319651064e7b3d2bac0937cfb6bfa4a0a125db38f32784c0b0796
workbox-v3.6.3/workbox-routing.prod.js,1559191154720,21a3e3ec3bc1876aa28bc5002986bd0a286dc3d1dd7025d8d29dfced05bc6414
workbox-v3.6.3/workbox-routing.dev.js.map,1559191154720,47456496d22cdcd260cb2fae01d832bb24c62be9af477872368fe1de5730b6a7
workbox-v3.6.3/workbox-routing.prod.js.map,1559191154720,bb5aa00b3c144bbf42ca81cd089f2b52f623a9b015b8a5ca3be4e86e15fec7a8
workbox-v3.6.3/workbox-strategies.dev.js,1559191154720,dc7656e0bc31e36c5a216732ed5dd69612a2591e281e3271af265bfd27afe916
workbox-v3.6.3/workbox-strategies.dev.js.map,1559191154721,4b930ea0b0ce1c979e0233fba40a0ed7587c5cbfc310b7b213545ae566cda12c
workbox-v3.6.3/workbox-strategies.prod.js,1559191154720,66cb572656c6f67c8ccfa9ed50fd0ef5f43812460a8e66ec5e1331bd35e74fb4
workbox-v3.6.3/workbox-strategies.prod.js.map,1559191154720,bd614f0a7c9f746a5711c80701afd5fbd1f9a4badaa3d41a9b6d2209e964c94b
workbox-v3.6.3/workbox-streams.dev.js,1559191154721,ebd9ce89ba8783a9c7cedbb649fd2c0e72d16c0e49fc75c55af3cbc2a06f6229
workbox-v3.6.3/workbox-streams.dev.js.map,1559191154727,db0ab4193f8fd5f3cb46000c464d0812534eb593830fe6f209edbdf444aba391
workbox-v3.6.3/workbox-streams.prod.js,1559191154720,a38b6330c98447b0f6046fef7432bcae71ca64d112bf9e4d0c97f91f6585a74a
workbox-v3.6.3/workbox-streams.prod.js.map,1559191154721,74d4cde66c4cfef524824fc286f178d8670de1aadd1d559be4ca1cd0a2942f24
workbox-v3.6.3/workbox-sw.js,1559191154727,e07638726911853e795bbd8a2172e49ed788f456d9ebaf2412a3e894139b76fc
workbox-v3.6.3/workbox-sw.js.map,1559191154721,12bddb94b59cc432f8db0760cf49fdfcff462b966305d09fd265be5b267a10d5
app-5240259ae487bdf09f0d.js,1557813278931,04da3d4832ac13a4e860903d0b70486f8314373a6b76fe49f2cfb82a75758912
app-5240259ae487bdf09f0d.js.map,1557813278929,358adcb70befc58d69ee8b6700450752e6dcb4f0ca17a4bbeb6f5c97cc8ac0f3
app-eadb88dc3e8f941f2ae9.js.map,1557813216314,bed2226123bc1ed270d12afb04fad8d873a88d2adda2676229e02dfe8d7ee2b3
app-c7f161e371102bbd741c.js.map,1557877612117,1b103871e0ddb305a5a4d53b021107d30acb4053d2fd2f14be0b8507129197c3
app-895d651bee860628b349.js.map,1557811597005,6b37ec886fab84a7eb072c67913cf7eff6c45f5e9f9d4596759b19fb734966ee
app-c5b938aeccd7fc80368e.js.map,1557810933019,1522deeb95c35d6bc4ed00c542c91288e0f57f835cfc1c94865b1e3eec590ec3
app-fda3ed7d59bc2e65cbed.js.map,1559191150475,37bd1b78efd536b053c0d1134aa109891b6059eba318652aa990c8ea6d18ea20
app-fb394d03a36f4752b604.js.map,1557809399807,f356f070bf4e3e581c4c044cb4e707e368c45eddcbc785c8b22bcbfaa14e6733

View File

@@ -2,5 +2,14 @@
"projects": {
"default": "journal-app-service",
"prod": "sol-journal"
},
"targets": {
"sol-journal": {
"hosting": {
"soljournal": [
"soljournal"
]
}
}
}
}

3
.gitignore vendored
View File

@@ -25,3 +25,6 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.cache
public

View File

@@ -5,10 +5,13 @@
Personal Journaling Platform
</h2>
Sol Journal is a simple, minimal, journaling platform that works offline and across all devices. It can be self-hosted through Firebase and then installed as a PWA, on mobile devices for easy access on a phone, or on Desktops.
Sol Journal is a simple, minimal, journaling platform that works offline and across all devices. It can be hosted yourself on Firebase and then installed as a PWA, on mobile devices for easy access on a phone, or on Desktops.
<p align="center">
<img alt="preview of page" src="https://raw.githubusercontent.com/gillkyle/images/master/hero-mixed.png" />
<img
alt="preview of page"
src="https://raw.githubusercontent.com/gillkyle/images/master/hero-mixed.png"
/>
</p>
## Introduction
@@ -21,7 +24,7 @@ Having a journal that is available on any device makes journaling easier. Being
There are 2 ways to get started using Sol Journal:
1. You can use the hosted version (this is the simplest way to use the product)
1. You can use the hosted version (this is the simplest way to use the product) at [soljournal.netlify.com](soljournal.netlify.com)
2. You can host your own version and manage it yourself
Sol Journal uses firebase to support offline functionality and authentication, meaning a new Firebase app will need to be setup with Firestore as a database if you wish to host it yourself. Be sure to add documents for `users` and `entries`, as well as enabling email for user authentication.
@@ -31,13 +34,25 @@ Sol Journal uses firebase to support offline functionality and authentication, m
In the spirit of minimalism, key features are what are in place for a quick, lightweight journaling experience that can work across devices, including:
- 🔥 Authentication: Cloud firestore persists registered users to a users document and saved journal entries to an entries document
- 🎨 Dark Theme: the `src/styles/theme.js` file contains a set of colors and default styles that are applied to components with Emotion. A default light and dark theme are already in the file
- 🎨 Theming: the `src/styles/theme.js` file contains a set of colors and default styles that are applied to components with Emotion. A default light and dark theme are already in the file
- 🔍 Search: full-text search of a user's entries stored in Firestore for quick access to past entries
- 🖥 Mobile Friendly: designed to look great on mobile as well as desktop, with easy navigation on both
- 💡 PWA: being a progressive web app makes it installable from Chrome/Safari on desktop, or be added to the homescreen on iOS/Android
- 🔌 Offline Support: read/write when you're offline and let the updates happen when your connection is restored
- 🗄 Export: backup all of your entries at any time to save your data
## Project setup
Files are organized into these folders:
`/components`: user interface pieces to construct the design and layout of the site
`/data`: local data transformed by gatsby to become queryable by Gatsby's GraphQL data layer
`/img`: images used by places like landing pages that are optimized by gatsby-image and then queryable in the GraphQL layer
`/pages`: public pages that can be seen by unauthenticated users and are completely server side rendered by Gatsby during `gatsby build`
`/routes`: private, client only routes only visible to authenticated users that are used by the app section of the journal
`/styles`: role based design tokens and theme definitions
`/util`: simple utility functions, for things like formatting dates
## Developing
Clone the project:
@@ -59,24 +74,25 @@ yarn
```
Then configure a file in a new `.env` file (using the `.env.sample` file as a reference) with the following keys from the firebase console:
```env
REACT_APP_FIREBASE_API_KEY=<BUNCHofRandomNumbersAndChars>
REACT_APP_DEV_AUTH_DOMAIN=<name>.firebaseapp.com
REACT_APP_DEV_DATABASE_URL=https://<name>.firebaseio.com
REACT_APP_DEV_PROJECT_ID=<name>
REACT_APP_DEV_STORAGE_BUCKET=<name>.appspot.com
REACT_APP_DEV_MESSAGING_SENDER_ID=############
REACT_APP_CONFIRMATION_EMAIL_REDIRECT=https://<name>.firebaseapp.com
```env
GATSBY_FIREBASE_API_KEY=<BUNCHofRandomNumbersAndChars>
GATSBY_DEV_AUTH_DOMAIN=<name>.firebaseapp.com
GATSBY_DEV_DATABASE_URL=https://<name>.firebaseio.com
GATSBY_DEV_PROJECT_ID=<name>
GATSBY_DEV_STORAGE_BUCKET=<name>.appspot.com
GATSBY_DEV_MESSAGING_SENDER_ID=############
GATSBY_CONFIRMATION_EMAIL_REDIRECT=https://<name>.firebaseapp.com
```
Navigate into the project directory, and then launch the site with this command:
```bash
yarn develop
gatsby develop
```
The site will be opened up in your default browser on http://localhost:3000
The site will be opened up in your default browser on http://localhost:8000
Edit code in the `/src`, save your changes, and they'll reload instantly in the browser.
@@ -85,13 +101,15 @@ Edit code in the `/src`, save your changes, and they'll reload instantly in the
To create an optimized build of the site run this command:
```bash
yarn build
gatsby build
```
A `/build` folder will be assembled that can be deployed with this command:
A `/public` folder will be assembled that can be deployed to any static file hosting service like Netlify or surge.
It can be deployed to firebase with this command:
```bash
firebase deploy
firebase deploy -p public
```
## Inspiration

View File

@@ -2,7 +2,6 @@
Setting up Sol Journal requires a free Firebase account since many of the features rely on built in Firebase capabilities to allow functionality offline. The free plan gives you 50,000 reads and 20,000 writes per day, meaning you can easily run a personal version for yourself.
## Setting up Firebase
You will need a Google account to create a Firebase project, then navigate to the Firebase Console: https://console.firebase.google.com/
@@ -71,7 +70,7 @@ In order to prevent would be hackers or nefarious folks from messing with data i
##### Configuration
## Code Setup
## Setting up Code
Having node installed is a prerequistie, you can follow instructions to get it setup yourself.
@@ -88,3 +87,30 @@ firebase login
```
Follow the prompted instructions to connect your account.
Clone the repository:
```bash
git clone https://github.com/gillkyle/sol-journal
```
Then change the name of the `.env.sample` file to `.env` (or `.env.production`/`.env.development` if you would like to run the app in different environments) and fill it in, mapping your own keys from your Firebase project to the corresponding names in the file.
```env
GATSBY_FIREBASE_API_KEY=<BUNCHofRandomNumbersAndChars>
GATSBY_DEV_AUTH_DOMAIN=<name>.firebaseapp.com
GATSBY_DEV_DATABASE_URL=https://<name>.firebaseio.com
GATSBY_DEV_PROJECT_ID=<name>
GATSBY_DEV_STORAGE_BUCKET=<name>.appspot.com
GATSBY_DEV_MESSAGING_SENDER_ID=############
GATSBY_CONFIRMATION_EMAIL_REDIRECT=https://<name>.firebaseapp.com
```
Then run the build command to generate an optimized build in the public directory.
```bash
gatsby build
```
You can upload your `/public` folder to S3, or run the surge command inside the directory to deploy it to a free subdomain.

View File

@@ -4,6 +4,7 @@
"indexes": "firestore.indexes.json"
},
"hosting": {
"target": "soljournal",
"public": "build",
"ignore": [
"firebase.json",

21
gatsby-browser.js Normal file
View File

@@ -0,0 +1,21 @@
import React from "react"
import { ThemeProvider as EmotionThemeProvider } from "emotion-theming"
import Firebase, { FirebaseContext } from "components/firebase"
import theme from "styles/theme"
import ThemeTogglerContext, { ThemeToggler } from "components/context/theme"
export const wrapRootElement = ({ element }) => {
return (
<FirebaseContext.Provider value={new Firebase()}>
<ThemeToggler>
<ThemeTogglerContext.Consumer>
{({ themeName }) => (
<EmotionThemeProvider theme={theme[themeName]}>
{element}
</EmotionThemeProvider>
)}
</ThemeTogglerContext.Consumer>
</ThemeToggler>
</FirebaseContext.Provider>
)
}

71
gatsby-config.js Normal file
View File

@@ -0,0 +1,71 @@
module.exports = {
plugins: [
`gatsby-plugin-react-helmet`,
`gatsby-plugin-emotion`,
// create routes for client side routing
{
resolve: `gatsby-plugin-create-client-paths`,
options: { prefixes: [`/app/*`] },
},
// provide fonts from Google fonts
{
resolve: `gatsby-plugin-prefetch-google-fonts`,
options: {
fonts: [
{
family: `Montserrat`,
variants: [`400`, `700`],
},
],
},
},
// plugins for PWA support
`gatsby-plugin-offline`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `Sol Journal`,
short_name: `Sol Journal`,
start_url: `/app`,
background_color: `#FFF`,
theme_color: `#FFF`,
display: `standalone`,
icon: `src/img/splash.png`,
},
},
// plugins for optimized images
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/img`,
},
},
// parse data from /src/data as Javascrip objects
`gatsby-transformer-json`,
{
resolve: `gatsby-source-filesystem`,
options: {
path: `./src/data/`,
},
},
// easier imports and exports by defining aliases
// for commonly used folders
{
resolve: "gatsby-plugin-module-resolver",
options: {
root: "./src",
aliases: {
components: "./components",
data: "./data",
img: "./img",
routes: "./routes",
styles: "./styles",
utils: "./utils",
},
},
},
],
}

9
gatsby-node.js Normal file
View File

@@ -0,0 +1,9 @@
exports.onCreatePage = async ({ page, actions }) => {
const { createPage } = actions
if (page.path.match(/^\/app/)) {
page.matchPath = "/app/*"
createPage(page)
}
}

10
gatsby-ssr.js Normal file
View File

@@ -0,0 +1,10 @@
import React from "react"
import { ThemeProvider } from "emotion-theming"
import Firebase, { FirebaseContext } from "./src/components/firebase"
import theme from "./src/styles/theme"
export const wrapRootElement = ({ element }) => (
<FirebaseContext.Provider value={new Firebase()}>
<ThemeProvider theme={theme.LIGHT}>{element}</ThemeProvider>
</FirebaseContext.Provider>
)

View File

@@ -9,9 +9,23 @@
"date-fns": "^1.30.1",
"emotion-theming": "^10.0.10",
"firebase": "^5.9.0",
"gatsby": "^2.4.3",
"gatsby-image": "^2.1.0",
"gatsby-plugin-create-client-paths": "^2.0.5",
"gatsby-plugin-emotion": "^4.0.6",
"gatsby-plugin-manifest": "^2.1.1",
"gatsby-plugin-module-resolver": "^1.0.3",
"gatsby-plugin-offline": "^2.1.0",
"gatsby-plugin-prefetch-google-fonts": "^1.4.2",
"gatsby-plugin-react-helmet": "^3.0.12",
"gatsby-plugin-sharp": "^2.0.37",
"gatsby-source-filesystem": "^2.0.36",
"gatsby-transformer-json": "^2.1.11",
"gatsby-transformer-sharp": "^2.1.19",
"react": "^16.8.4",
"react-dom": "^16.8.4",
"react-feather": "^1.1.6",
"react-helmet": "^5.2.1",
"react-router-dom": "^5.0.0",
"react-scripts": "2.1.8",
"react-spinners": "^0.5.3",

View File

@@ -1,45 +1 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link
href="https://fonts.googleapis.com/css?family=Montserrat:400,700"
rel="stylesheet"
/>
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Sol Journal</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
<!DOCTYPE html><html><head><meta charSet="utf-8"/><meta http-equiv="x-ua-compatible" content="ie=edge"/><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/><title data-react-helmet="true"></title><link rel="preload" as="font" type="font/woff2" crossorigin="anonymous" href="/google-fonts/s/montserrat/v13/JTURjIg1_i6t8kCHKm45_dJE3gnD_g.woff2"/><link rel="preload" as="font" type="font/woff2" crossorigin="anonymous" href="/google-fonts/s/montserrat/v13/JTUSjIg1_i6t8kCHKm459Wlhyw.woff2"/><style type="text/css">@font-face{font-family:Montserrat;font-style:normal;font-weight:400;src:local('Montserrat Regular'),local('Montserrat-Regular'),url(/google-fonts/s/montserrat/v13/JTUSjIg1_i6t8kCHKm459Wlhyw.woff2) format('woff2'),url(/google-fonts/s/montserrat/v13/JTUSjIg1_i6t8kCHKm459WlhzQ.woff) format('woff');font-display: swap;}@font-face{font-family:Montserrat;font-style:normal;font-weight:700;src:local('Montserrat Bold'),local('Montserrat-Bold'),url(/google-fonts/s/montserrat/v13/JTURjIg1_i6t8kCHKm45_dJE3gnD_g.woff2) format('woff2'),url(/google-fonts/s/montserrat/v13/JTURjIg1_i6t8kCHKm45_dJE3gnD-A.woff) format('woff');font-display: swap;}</style><link rel="shortcut icon" href="/icons/icon-48x48.png?v=54c7134a7e5384a7327f957c04754c6d"/><link rel="manifest" href="/manifest.webmanifest"/><meta name="theme-color" content="#FFF"/><link rel="apple-touch-icon" sizes="48x48" href="/icons/icon-48x48.png?v=54c7134a7e5384a7327f957c04754c6d"/><link rel="apple-touch-icon" sizes="72x72" href="/icons/icon-72x72.png?v=54c7134a7e5384a7327f957c04754c6d"/><link rel="apple-touch-icon" sizes="96x96" href="/icons/icon-96x96.png?v=54c7134a7e5384a7327f957c04754c6d"/><link rel="apple-touch-icon" sizes="144x144" href="/icons/icon-144x144.png?v=54c7134a7e5384a7327f957c04754c6d"/><link rel="apple-touch-icon" sizes="192x192" href="/icons/icon-192x192.png?v=54c7134a7e5384a7327f957c04754c6d"/><link rel="apple-touch-icon" sizes="256x256" href="/icons/icon-256x256.png?v=54c7134a7e5384a7327f957c04754c6d"/><link rel="apple-touch-icon" sizes="384x384" href="/icons/icon-384x384.png?v=54c7134a7e5384a7327f957c04754c6d"/><link rel="apple-touch-icon" sizes="512x512" href="/icons/icon-512x512.png?v=54c7134a7e5384a7327f957c04754c6d"/><script src="/socket.io/socket.io.js"></script></head><body><noscript id="gatsby-noscript">This app works best with JavaScript enabled.</noscript><div id="___gatsby"></div><script src="/commons.js"></script></body></html>

View File

@@ -1,145 +0,0 @@
import React, { Component } from "react"
import { BrowserRouter as Router, Route } from "react-router-dom"
import { compose } from "recompose"
import styled from "@emotion/styled"
import { ThemeProvider } from "emotion-theming"
import { SIZES } from "./styles/constants"
import theme from "./styles/theme"
import Navbar from "./components/Navbar"
import Day from "./components/screens/Day"
import Month from "./components/screens/Month"
import Year from "./components/screens/Year"
import User from "./components/screens/User"
import Login from "./components/screens/Login"
import Search from "./components/screens/Search"
import Register from "./components/screens/Register"
import Start from "./components/screens/Start"
import Terms from "./components/screens/Terms"
import Privacy from "./components/screens/Privacy"
import PrivateRoute from "./components/PrivateRoute"
import { OnlineContext } from "./components/context/online"
import { withAuthentication } from "./components/session"
import { withFirebase } from "./components/firebase"
const FullscreenLayout = styled.div`
background-color: ${props => props.theme.colors.bodyBackground};
`
const RouteLayout = styled.div`
display: flex;
flex-direction: column;
height: 100%;
margin: 0 auto;
padding: 0 10px;
max-width: ${SIZES.maxWidth};
min-height: calc(100vh - 60px);
background-color: ${props => props.theme.colors.bodyBackground};
`
class App extends Component {
state = {
authUser: JSON.parse(localStorage.getItem("authUser")),
selectedTheme:
new Date().getHours() >= 7 && new Date().getHours() <= 21
? "LIGHT"
: "DARK",
online: navigator.onLine,
}
componentDidMount() {
window.addEventListener("online", () => {
this.setState({ online: true })
})
window.addEventListener("offline", () => {
this.setState({ online: false })
})
}
onChangeTheme = () => {
const { selectedTheme } = this.state
const body = document.body
const newTheme = selectedTheme === "LIGHT" ? "DARK" : "LIGHT"
body.style.setProperty(
"background-color",
theme[newTheme].colors.bodyBackground
)
this.setState({ selectedTheme: newTheme })
}
saveUserSettings = newTheme => {
const { authUser, firebase } = this.props
firebase.db
.collection("users")
.doc(authUser.uid)
.update({
theme: newTheme,
})
.then(function() {
console.log("Updated theme settings")
})
}
render() {
const { selectedTheme, authUser, online } = this.state
const { authUser: propAuthUser } = this.props
const authed = !!propAuthUser || !!authUser
const currentTheme = theme[selectedTheme]
return (
<ThemeProvider theme={currentTheme}>
<OnlineContext.Provider value={online}>
<Router>
<FullscreenLayout>
<Navbar toggleTheme={this.onChangeTheme} />
<RouteLayout>
<PrivateRoute
authed={authed}
path="/:year(\d+)"
component={Year}
exact
/>
<PrivateRoute
authed={authed}
path="/:year(\d+)/:month(0[1-9]|1[0-2]+)"
component={Month}
exact
/>
<PrivateRoute
authed={authed}
path="/:year(\d+)/:month(0[1-9]|1[0-2]+)/:day(\d+)"
component={Day}
exact
/>
<PrivateRoute
authed={authed}
path="/search"
component={Search}
exact
/>
<PrivateRoute
authed={authed}
path="/user"
component={User}
exact
/>
<Route path="/login" component={Login} exact />
<Route path="/register" component={Register} exact />
<Route path="/terms" component={Terms} exact />
<Route path="/privacy" component={Privacy} exact />
<Route path="/" component={Start} exact />
</RouteLayout>
</FullscreenLayout>
</Router>
</OnlineContext.Provider>
</ThemeProvider>
)
}
}
export default compose(
withAuthentication,
withFirebase
)(App)

View File

@@ -1,9 +0,0 @@
import React from "react"
import ReactDOM from "react-dom"
import App from "./App"
it("renders without crashing", () => {
const div = document.createElement("div")
ReactDOM.render(<App />, div)
ReactDOM.unmountComponentAtNode(div)
})

115
src/components/App.js Normal file
View File

@@ -0,0 +1,115 @@
import React, { Component } from "react"
import { Router } from "@reach/router"
import { compose } from "recompose"
import styled from "@emotion/styled"
import { withTheme } from "emotion-theming"
import { SIZES } from "styles/constants"
// import theme from "./styles/theme"
import Day from "routes/Day"
import Month from "routes/Month"
import Year from "routes/Year"
import User from "routes/User"
import Search from "routes/Search"
import Welcome from "routes/Welcome"
import Navbar from "components/Navbar"
import PrivateRoute from "components/PrivateRoute"
import { OnlineContext } from "components/context/online"
import { withAuthentication } from "components/session"
import { withFirebase } from "components/firebase"
import ThemeTogglerContext from "components/context/theme"
const FullscreenBgColor = styled.div`
background-color: ${props => props.theme.colors.bodyBackground};
`
const RouteLayout = styled.div`
display: flex;
flex-direction: column;
height: 100%;
margin: 0 auto;
padding: 0 10px;
max-width: ${SIZES.maxWidth};
min-height: calc(100vh - 60px);
`
class App extends Component {
state = {
selectedTheme: this.props.theme.name,
}
componentDidMount() {
window.addEventListener("online", () => {
this.setState({ online: true })
})
window.addEventListener("offline", () => {
this.setState({ online: false })
})
this.setState({
authUser: JSON.parse(localStorage.getItem("authUser")),
online: navigator.onLine,
})
}
render() {
const { authUser, online } = this.state
const { authUser: propAuthUser } = this.props
const authed = !!propAuthUser || !!authUser
return (
<ThemeTogglerContext.Consumer>
{({ toggle }) => (
<OnlineContext.Provider value={online}>
<FullscreenBgColor>
<Navbar toggleTheme={toggle} />
<RouteLayout>
<Router style={{ height: "100%" }}>
<PrivateRoute
authed={authed}
path="/app/:year"
component={Year}
exact
/>
<PrivateRoute
authed={authed}
path="/app/:year/:month"
component={Month}
exact
/>
<PrivateRoute
authed={authed}
path="/app/:year/:month/:day"
component={Day}
exact
/>
<PrivateRoute
authed={authed}
path="/app/search"
component={Search}
exact
/>
<PrivateRoute
authed={authed}
path="/app/user"
component={User}
exact
/>
<Welcome authed={authed} path="/app" exact />
</Router>
</RouteLayout>
</FullscreenBgColor>
</OnlineContext.Provider>
)}
</ThemeTogglerContext.Consumer>
)
}
}
export default compose(
withAuthentication,
withFirebase,
withTheme
)(App)

55
src/components/Footer.js Normal file
View File

@@ -0,0 +1,55 @@
import React from "react"
import { Link } from "gatsby"
import { css } from "@emotion/core"
import styled from "@emotion/styled"
import { withTheme } from "emotion-theming"
import Logo from "components/Logo"
const FooterBlock = styled.footer`
margin-top: 120px;
padding: 30px 0px;
text-align: center;
color: ${props => props.theme.colors.secondary};
`
const linkStyles = css`
cursor: pointer;
text-decoration: none;
margin: 10px;
`
const FooterLink = styled(Link)`
${linkStyles}
color: ${props => props.theme.colors.secondary};
&:hover {
color: ${props => props.theme.colors.tertiary};
}
`
const FooterAnchor = styled.a`
${linkStyles}
color: ${props => props.theme.colors.secondary};
&:hover {
color: ${props => props.theme.colors.tertiary};
}
`
const Footer = ({ theme }) => (
<FooterBlock>
<div>
<Logo color={theme.colors.secondary} />
</div>
<div>
<FooterAnchor
href="https://github.com/gillkyle/sol-journal"
rel="target noopener"
target="_blank"
>
View on GitHub
</FooterAnchor>
<FooterLink to="terms">Terms of Service</FooterLink>
<FooterLink to="privacy">Privacy Policy</FooterLink>
</div>
<div>&copy; 2019</div>
</FooterBlock>
)
export default withTheme(Footer)

View File

@@ -1,2 +0,0 @@
import Icon from "./Icon"
export default Icon

47
src/components/Layout.js Normal file
View File

@@ -0,0 +1,47 @@
import React from "react"
import { Helmet } from "react-helmet"
import { Global, css } from "@emotion/core"
import styled from "@emotion/styled"
import { withTheme } from "emotion-theming"
const Layout = ({ children, theme }) => (
<>
<Helmet title="Sol Journal" />
{/* some styles should applied globally via the layout */}
<Global
styles={css`
* {
transition: 0.2s border-color ease-in-out, 0.2s fill ease-in-out,
0.2s box-shadow ease-in-out, 0.2s background-color ease-in-out,
0.2s color ease-in-out;
overflow: -moz-scrollbars-none;
}
*::-webkit-scrollbar {
width: 0 !important;
}
html {
background-color: ${theme.colors.bodyBackground};
}
h1 {
font-family: "Montserrat", -apple-system, BlinkMacSystemFont,
"Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans",
"Droid Sans", "Helvetica Neue", sans-serif;
}
body {
margin: 0;
padding: 0;
min-height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
"Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
`}
/>
{children}
</>
)
export default withTheme(Layout)

158
src/components/Navbar.js Normal file
View File

@@ -0,0 +1,158 @@
import React from "react"
import { navigate, Link } from "gatsby"
import { StyledLink } from "components/elements"
import styled from "@emotion/styled"
/** @jsx jsx */
import { jsx } from "@emotion/core"
import { compose } from "recompose"
import { withTheme } from "emotion-theming"
import { SIZES } from "styles/constants"
import { todayUrl, yearUrl } from "utils/date"
import Logo from "components/Logo"
import Icon from "components/Icon"
import { withAuthentication } from "components/session"
import ThemeTogglerContext from "components/context/theme"
const Header = styled.div`
background-color: ${props => props.theme.colors.headerBackground};
height: 60px;
display: grid;
grid-template-areas: "... nav ...";
grid-template-columns: 1fr minmax(240px, ${SIZES.maxWidth}) 1fr;
grid-gap: 10px;
align-items: center;
border-width: 1px;
border-color: ${props => props.theme.colors.quarternary};
border-style: solid;
`
const Nav = styled.div`
grid-area: nav;
max-width: ${SIZES.maxWidth};
display: flex;
flex-direction: row;
justify-content: space-between;
align-content: center;
`
const LogoSection = styled.div`
height: 40px;
display: flex;
flex-direction: row;
align-items: center;
font-family: "Montserrat", sans-serif;
font-weight: 700;
font-size: 18px;
&:hover {
cursor: pointer;
}
`
const LogoText = styled.span`
color: ${props => props.color};
margin-left: 5px;
@media (max-width: 400px) {
display: none;
}
`
const NavIcons = styled.div`
display: flex;
flex-direction: row;
align-items: center;
* + * {
margin-left: 10px;
}
`
const Navbar = ({ authUser, theme, toggleTheme }) => (
<Header>
<Nav>
<LogoSection onClick={() => navigate("/app")}>
<Logo color={theme.colors.logo} />
<LogoText color={theme.colors.primary}>SOL</LogoText>{" "}
<LogoText color={theme.colors.secondary}>JOURNAL</LogoText>
</LogoSection>
<NavIcons>
{authUser ? (
<React.Fragment>
<StyledLink to={todayUrl()}>
<Icon name="Edit2" />
</StyledLink>
<StyledLink to={yearUrl()}>
<Icon name="Calendar" />
</StyledLink>
<StyledLink to={"/search"}>
<Icon name="Search" />
</StyledLink>
<StyledLink to={"/user"}>
<Icon name="User" />
</StyledLink>
<Icon
tabindex={0}
onClick={() => toggleTheme()}
onKeyPress={() => toggleTheme()}
name={theme.name === "Dark" ? "Sun" : "Moon"}
/>
</React.Fragment>
) : (
<>
<Link to={"/login"} style={{ textDecoration: "none" }}>
<Icon name="ArrowRight" label="Login" size={20} />
</Link>
<Icon
tabindex={0}
onClick={() => toggleTheme()}
onKeyPress={() => toggleTheme()}
name={theme.name === "Dark" ? "Sun" : "Moon"}
/>
</>
)}
</NavIcons>
</Nav>
</Header>
)
// on langing page and simple pages outside the app, simplify link options
const SimpleNav = ({ authUser, theme }) => (
<Header>
<Nav>
<LogoSection onClick={() => navigate("/")}>
<Logo color={theme.colors.logo} />
<LogoText color={theme.colors.primary}>SOL</LogoText>{" "}
<LogoText color={theme.colors.secondary}>JOURNAL</LogoText>
</LogoSection>
<NavIcons>
{authUser ? (
<StyledLink to={"/"}>
<Icon name="Circle" />
</StyledLink>
) : (
<Link to={"/login"} style={{ textDecoration: "none" }}>
<Icon name="ArrowRight" label="Login" size={20} />
</Link>
)}
<ThemeTogglerContext.Consumer>
{({ toggle }) => (
<Icon
tabindex={0}
onClick={() => toggle()}
onKeyPress={() => toggle()}
name={theme.name === "Dark" ? "Sun" : "Moon"}
/>
)}
</ThemeTogglerContext.Consumer>
</NavIcons>
</Nav>
</Header>
)
const SimpleNavbar = compose(
withAuthentication,
withTheme
)(SimpleNav)
export { SimpleNavbar }
export default compose(
withAuthentication,
withTheme
)(Navbar)

View File

@@ -1,113 +0,0 @@
import React from "react"
import { withRouter } from "react-router-dom"
import { StyledLink as Link } from "../elements"
import styled from "@emotion/styled"
/** @jsx jsx */
import { jsx } from "@emotion/core"
import { compose } from "recompose"
import { withTheme } from "emotion-theming"
import { SIZES } from "../../styles/constants"
import { todayUrl, yearUrl } from "../../utils/date"
import Logo from "../Logo"
import Icon from "../Icon"
import { withAuthentication } from "../session"
const Header = styled.div`
background-color: ${props => props.theme.colors.headerBackground};
height: 60px;
display: grid;
grid-template-areas: "... nav ...";
grid-template-columns: 1fr minmax(240px, ${SIZES.maxWidth}) 1fr;
grid-gap: 10px;
align-items: center;
border-width: 1px;
border-color: ${props => props.theme.colors.quarternary};
border-style: solid;
`
const Nav = styled.div`
grid-area: nav;
max-width: ${SIZES.maxWidth};
display: flex;
flex-direction: row;
justify-content: space-between;
align-content: center;
`
const LogoSection = styled.div`
height: 40px;
display: flex;
flex-direction: row;
align-items: center;
font-family: "Montserrat", sans-serif;
font-weight: 700;
font-size: 18px;
&:hover {
cursor: pointer;
}
`
const LogoText = styled.span`
color: ${props => props.color};
margin-left: 5px;
@media (max-width: 400px) {
display: none;
}
`
const NavIcons = styled.div`
display: flex;
flex-direction: row;
align-items: center;
* + * {
margin-left: 10px;
}
`
const Navbar = ({ authUser, theme, toggleTheme, history }) => (
<Header>
<Nav>
<LogoSection onClick={() => history.push("/")}>
<Logo color={theme.colors.logo} />
<LogoText color={theme.colors.primary}>SOL</LogoText>{" "}
<LogoText color={theme.colors.secondary}>JOURNAL</LogoText>
</LogoSection>
<NavIcons>
{authUser ? (
<React.Fragment>
<Link to={todayUrl()}>
<Icon name="Edit2" />
</Link>
<Link to={yearUrl()}>
<Icon name="Calendar" />
</Link>
<Link to={"/search"}>
<Icon name="Search" />
</Link>
<Link to={"/user"}>
<Icon name="User" />
</Link>
<Icon
tabindex={0}
onClick={() => toggleTheme()}
onKeyPress={() => toggleTheme()}
name={theme.name === "Dark" ? "Sun" : "Moon"}
/>
</React.Fragment>
) : (
<React.Fragment>
{/* <Link to={"/"}>Landing</Link> */}
<Link to={"/login"}>
<Icon name="LogIn" label="Get Started" />
</Link>
{/* <Link to={"/register"}>Register</Link> */}
</React.Fragment>
)}
</NavIcons>
</Nav>
</Header>
)
export default compose(
withAuthentication,
withTheme,
withRouter
)(Navbar)

View File

@@ -1,2 +0,0 @@
import Navbar from "./Navbar"
export default Navbar

View File

@@ -0,0 +1,19 @@
import React from "react"
import { Redirect, Location } from "@reach/router"
// when a user isn't logged in, force a redirect
const PrivateRoute = ({ component: Component, authed, ...rest }) => {
return (
<Location>
{({ location }) =>
authed === true ? (
<Component {...rest} />
) : (
<Redirect to="/login" from={location.pathname} />
)
}
</Location>
)
}
export default PrivateRoute

View File

@@ -1,21 +0,0 @@
import React from "react"
import { Route, Redirect } from "react-router-dom"
const PrivateRoute = ({ component: Component, authed, ...rest }) => {
return (
<Route
{...rest}
render={props =>
authed === true ? (
<Component {...props} />
) : (
<Redirect
to={{ pathname: "/login", state: { from: props.location } }}
/>
)
}
/>
)
}
export default PrivateRoute

View File

@@ -1,2 +0,0 @@
import PrivateRoute from "./PrivateRoute"
export default PrivateRoute

View File

@@ -2,9 +2,9 @@ import React from "react"
import styled from "@emotion/styled"
import { withTheme } from "emotion-theming"
import Icon from "../Icon"
import { H1 } from "../elements"
import { StyledLink as Link } from "../elements"
import Icon from "components/Icon"
import { H1 } from "components/elements"
import { StyledLink as Link } from "components/elements"
const SeekHeader = styled.header`
display: flex;

View File

@@ -1,2 +0,0 @@
import Seek from "./Seek"
export default Seek

View File

@@ -2,9 +2,9 @@ import React from "react"
import { compose } from "recompose"
import { withTheme } from "emotion-theming"
import { Button } from "../elements"
import { Button } from "components/elements"
import { withFirebase } from "../firebase"
import { withFirebase } from "components/firebase"
const SignOutButton = ({ firebase, theme }) => (
<Button

View File

@@ -1,2 +0,0 @@
import SignOut from "./SignOut"
export default SignOut

View File

@@ -0,0 +1,16 @@
import React from "react"
import styled from "@emotion/styled"
import { SIZES } from "styles/constants"
const Container = styled.div`
display: flex;
flex-direction: column;
height: 100%;
margin: 0 auto;
padding: 0 10px;
max-width: ${SIZES.maxWidth};
min-height: calc(100vh - 60px);
`
export default Container

View File

@@ -1,5 +1,6 @@
import React from 'react'
import React from "react"
// when navigator is available outside of the build phase, provide it through Context
export const OnlineContext = React.createContext({
online: navigator.onLine,
});
online: typeof window !== "undefined" && navigator && navigator.onLine,
})

View File

@@ -0,0 +1,67 @@
import React from "react"
import { Helmet } from "react-helmet"
import theme from "styles/theme"
// create an app-wide context for the theme being used as
// well as a function to toggle it back and forth
const ThemeTogglerContext = React.createContext({
themeName: "LIGHT",
toggle: () => {},
})
class ThemeToggler extends React.Component {
state = {
themeName:
new Date().getHours() >= 7 && new Date().getHours() <= 21
? "LIGHT"
: "DARK",
}
componentDidMount() {
// set the body style property on mount so routes don't flash between transitions
const { themeName } = this.state
this.toggle(themeName)
}
toggle = newThemeName => {
const { themeName } = this.state
const body = document.body
let newTheme
if (newThemeName) {
newTheme = newThemeName
} else {
newTheme = themeName === "LIGHT" ? "DARK" : "LIGHT"
}
body.style.setProperty(
"background-color",
theme[newTheme].colors.bodyBackground
)
this.setState({ themeName: newTheme })
}
render() {
const { children } = this.props
const { themeName } = this.state
return (
<ThemeTogglerContext.Provider
value={{
themeName,
toggle: this.toggle,
}}
>
<Helmet>
<meta
name="theme-color"
content={theme[themeName].colors.bodyBackground}
/>
</Helmet>
{children}
</ThemeTogglerContext.Provider>
)
}
}
export default ThemeTogglerContext
export { ThemeToggler }

View File

@@ -1,8 +1,9 @@
import React from "react"
import { withTheme } from "emotion-theming"
import { Link } from "react-router-dom"
import { Link } from "gatsby"
import styled from "@emotion/styled"
import { SIZES } from "../../styles/constants"
import { SIZES } from "styles/constants"
export const H1 = styled.h1`
display: block;
@@ -10,6 +11,18 @@ export const H1 = styled.h1`
color: ${props => props.color};
`
export const SimpleH1 = styled.h1`
color: ${props => props.color};
`
export const SimpleH2 = styled.h1`
color: ${props => props.color};
`
export const Em = styled.em`
color: ${props => props.color};
`
export const Input = styled.input`
color: ${props => props.colors.primary};
background-color: ${props => props.colors.headerBackground};
@@ -33,23 +46,24 @@ export const Input = styled.input`
export const Button = styled.button`
display: inline;
background-color: ${props => props.colors.primary};
color: ${props => props.colors.primary};
background-color: ${props => props.colors.button};
border-color: ${props => props.colors.quarternary};
padding: 12px 50px;
min-height: 45px;
border-radius: 5px;
border: 0px solid;
border-color: ${props => props.colors.quarternary};
color: ${props => props.colors.quarternary};
font-family: Montserrat;
font-size: ${props => SIZES[props.fontSize || "normal"]};
font-weight: 700;
outline: none;
&:hover {
cursor: pointer;
background-color: ${props => props.colors.secondary};
background-color: ${props => props.colors.hover};
box-shadow: 0 0 0 3px ${props => props.colors.bodyBackground},
0 0 0 5px ${props => props.colors.button};
}
&:focus {
box-shadow: 0 0 0 3px ${props => props.colors.bodyBackground},
0 0 0 5px ${props => props.colors.secondary};
}
`
@@ -57,7 +71,10 @@ export const P = styled.p`
color: ${props => props.theme.colors.secondary};
`
export const StyledLink = withTheme(styled(Link)`
// prepend links used within the app section with app
export const AppLink = props => <Link {...props} to={"/app" + props.to} />
export const StyledLink = withTheme(styled(AppLink)`
text-decoration: none;
border-radius: 12px;
outline: none;

View File

@@ -1 +0,0 @@
export * from "./elements"

View File

@@ -1,5 +1,6 @@
import React from "react"
// create context of firebase instance
const FirebaseContext = React.createContext(null)
export const withFirebase = Component => props => (

View File

@@ -2,17 +2,22 @@ import app from "firebase/app"
import "firebase/auth"
import "firebase/firestore"
// store private keys in .env file
// the prefix GATSBY_ is necessary here
const config = {
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: process.env.REACT_APP_DEV_AUTH_DOMAIN,
databaseURL: process.env.REACT_APP_DEV_DATABASE_URL,
projectId: process.env.REACT_APP_DEV_PROJECT_ID,
storageBucket: process.env.REACT_APP_DEV_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_DEV_MESSAGING_SENDER_ID,
apiKey: process.env.GATSBY_FIREBASE_API_KEY,
authDomain: process.env.GATSBY_DEV_AUTH_DOMAIN,
databaseURL: process.env.GATSBY_DEV_DATABASE_URL,
projectId: process.env.GATSBY_DEV_PROJECT_ID,
storageBucket: process.env.GATSBY_DEV_STORAGE_BUCKET,
messagingSenderId: process.env.GATSBY_DEV_MESSAGING_SENDER_ID,
}
class Firebase {
constructor() {
// protect with conditional so gatsby build doesn't have
// issues trying to access the window object
if (typeof window !== "undefined") {
app.initializeApp(config)
this.auth = app.auth()
this.db = app.firestore()
@@ -21,7 +26,9 @@ class Firebase {
.enablePersistence()
.catch(function(err) {
if (err.code === "failed-precondition") {
console.error("firestore won't work offline with multiple tabs open")
console.error(
"firestore won't work offline with multiple tabs open"
)
} else if (err.code === "unimplemented") {
console.error(
"current browser can't take advantage of firestore offline"
@@ -29,21 +36,27 @@ class Firebase {
}
})
}
}
// Auth
// authentication
// create user in the database
doCreateUserWithEmailAndPassword = (email, password) =>
this.auth.createUserWithEmailAndPassword(email, password)
// login already existing user
doSignInWithEmailAndPassword = (email, password) =>
this.auth.signInWithEmailAndPassword(email, password)
// sign out user
doSignOut = () => {
this.auth.signOut()
window.location.replace("/login")
}
// send email reset to email provided
doPasswordReset = email => this.auth.sendPasswordResetEmail(email)
// change password to password provided
doPasswordUpdate = password => this.auth.currentUser.updatePassword(password)
}

View File

@@ -1,2 +0,0 @@
import Day from "./Day"
export default Day

View File

@@ -1,2 +0,0 @@
import Login from "./Login"
export default Login

View File

@@ -1,2 +0,0 @@
import Month from "./Month"
export default Month

View File

@@ -1,93 +0,0 @@
import React, { Component } from "react"
import { withRouter } from "react-router-dom"
import styled from "@emotion/styled"
import { compose } from "recompose"
import { format } from "date-fns"
import { withTheme } from "emotion-theming"
import { Input, Button, P } from "../../elements"
import { SIZES } from "../../../styles/constants"
import { StyledLink as Link } from "../../elements"
const Terms = ({}) => (
<>
<h1>Privacy Policy</h1>
<em>Last update: April 30, 2019</em>
<P>
Sol Journal supports the following browsers: Chrome (latest), Safari
(latest), Firefox (50+)
</P>
<h2>Rights</h2>
<P>
You don't have to provide your real name when you register to an account,
but you need to use a valid/verifiable email address.
<br />
<br />
You have the right to export your data at any time, in JSON format.
<br />
<br />
Your data will not be intentionally shown to other users or shared with
third parties.
<br />
<br />
Your personal data will not be shared with anyone without your consent.
<br />
<br />
Data saved on the hosted version of Sol Journal is encrypted as it is
transmitted and stored. However, data is not salted or hashed when it is
stored on Google servers so database administrators (me) can view the data
as plaintext. I won't view or use the data of other users for any reason
but if you would like to have more ownership of your data you can setup
the code on your own.
<br />
<br />
If the site ceases operation, you will receive an opportunity to export
all your data before the site dies.
<br />
<br />
Any new features that affect privacy will be strictly opt-in.
</P>
<h2>Responsibilites</h2>
<P>
You will not use the site to store illegal information or data under
United States law (or any law).
<br />
<br />
You have to be at least 18+ to create an account and use the site.
<br />
<br />
You must not abuse the site by knowingly posting malicious code that could
harm you or the other users.
<br />
<br />
You may not make automated requests to the site.
<br />
<br />
You may not abuse the registration system.
<br />
<br />
You are responsible for keeping your account secure.
<br />
<br />I reserve the right to close accounts that abuse the system
(millions of entries or overloading services with requests) or use it in
an unreasonable manner.
</P>
<h2>Other</h2>
<P>
Other important legal stuff Though I want to provide a great service,
there are certain things about the service I cannot promise. For example,
the services and software are provided as-is, at your own risk, without
express or implied warranty or condition of any kind. I also disclaim any
warranties of merchantability, fitness for a particular purpose or
non-infringement. Sol Journal will have no responsibility for any harm to
your computer system, loss or corruption of data, or other harm that
results from your access to or use of the Services or Software.
<br />
<br />
These Terms can change at any time, but I'll try to be reasonable. This is
a service I've always and hope to be able to run it for a long time.
</P>
</>
)
export default withTheme(Terms)

View File

@@ -1,2 +0,0 @@
import Privacy from "./Privacy"
export default Privacy

View File

@@ -1,2 +0,0 @@
import Register from "./Register"
export default Register

View File

@@ -1,2 +0,0 @@
import Search from "./Search"
export default Search

View File

@@ -1,2 +0,0 @@
import Start from "./Start"
export default Start

View File

@@ -1,80 +0,0 @@
import React from "react"
import { P } from "../../elements"
const Terms = () => (
<>
<h1>Terms of Service</h1>
<em>Last update: April 30, 2019</em>
<h2>Scope of Service</h2>
<P>
Sol Journal supports the following browsers: Chrome (latest), Safari
(latest), Firefox (50+)
</P>
<h2>Rights</h2>
<P>
You don't have to provide your real name when you register to an account,
but you need to use a valid/verifiable email address.
<br />
<br />
You have the right to export your data at any time, in JSON format.
<br />
<br />
Your data will not be intentionally shown to other users or shared with
third parties.
<br />
<br />
Your personal data will not be shared with anyone without your consent.
<br />
<br />
We reserve the right to discontinue any feature of the service at any
time. If the site ceases operation, you will receive an opportunity to
export all your data before the site dies.
<br />
<br />
Any new features that affect privacy will be strictly opt-in.
</P>
<h2>Responsibilites</h2>
<P>
You will not use the site to store illegal information or data under
United States law (or any law).
<br />
<br />
You have to be at least 18+ to create an account and use the site.
<br />
<br />
You must not abuse the site by knowingly posting malicious code that could
harm you or the other users.
<br />
<br />
You may not make automated requests to the site.
<br />
<br />
You may not abuse the registration system.
<br />
<br />
You are responsible for keeping your account secure.
<br />
<br />I reserve the right to close accounts that abuse the system
(millions of entries or overloading services with requests) or use it in
an unreasonable manner.
</P>
<h2>Other</h2>
<P>
Other important legal stuff Though I want to provide a great service,
there are certain things about the service I cannot promise. For example,
the services and software are provided “as-is”, at your own risk, without
express or implied warranty or condition of any kind. I also disclaim any
warranties of merchantability, fitness for a particular purpose or
non-infringement. Sol Journal will have no responsibility for any harm to
your computer system, loss or corruption of data, or other harm that
results from your access to or use of the Services or Software.
<br />
<br />
These Terms can change at any time, but I'll try to be reasonable. This is
a service I've always and hope to be able to run it for a long time.
</P>
</>
)
export default Terms

View File

@@ -1,2 +0,0 @@
import Terms from "./Terms"
export default Terms

View File

@@ -1,2 +0,0 @@
import User from "./User"
export default User

View File

@@ -1,2 +0,0 @@
import Year from "./Year"
export default Year

View File

@@ -1,22 +1,31 @@
import React from "react"
import AuthUserContext from "./context"
import { withFirebase } from "../firebase"
import { withFirebase } from "components/firebase"
// use context to provide all app components with information about
// the authUser if it's been put in localStorage
const withAuthentication = Component => {
class WithAuthentication extends React.Component {
constructor(props) {
super(props)
// protect browser API's like localStorage so Gatsby builds don't fail
if (typeof window !== "undefined") {
this.state = {
authUser: JSON.parse(localStorage.getItem("authUser")),
}
} else {
this.state = { authUser: null }
}
}
componentDidMount() {
this.listener = this.props.firebase.auth.onAuthStateChanged(
authUser => {
// accessing localStorage in componentDidMount is fine in Gatsby
localStorage.setItem("authUser", JSON.stringify(authUser))
if (authUser) {
this.setState({
authUser: {
uid: authUser.uid,
@@ -24,6 +33,7 @@ const withAuthentication = Component => {
emailVerified: authUser.emailVerified,
},
})
}
},
() => {
localStorage.removeItem("authUser")

74
src/data/quotes.json Normal file
View File

@@ -0,0 +1,74 @@
[
{
"quote": "Dude, sucking at something is the first step to being sorta good at something.",
"author": "Jake the Dog"
},
{
"quote": "Journal writing is a voyage to the interior.",
"author": "Christina Baldwin"
},
{
"quote": "Blessed is he who keeps daily diary and compares the work of this week with that of the last, for he will realize God quickly!",
"author": "Sivananda Saraswati"
},
{
"quote": "Each Bullet Journal becomes another volume in the story of your life. Does it represent the life you want to live? If not, then leverage the lessons you've learned to change the narrative in the next volume.",
"author": "Ryder Carroll"
},
{
"quote": "Authentic writing cannot be coerced.",
"author": "Ernst Jünger"
},
{
"quote": "If kids reflect on their days, they will become better problem-solvers of life.",
"author": "Trevor Carss"
},
{
"quote": "The best time to begin keeping a journal is whenever you decide to.",
"author": "Hannah Hinchman"
},
{
"quote": "He said, You'll write it not because there's no possibility it'll be found but because it costs too much to not write it.",
"author": "China Miéville"
},
{
"quote": "In the diary you find proof that in situations which today would seem unbearable, you lived, looked around and wrote down observations, that this right hand moved then as it does today...",
"author": "Franz Kafka"
},
{
"quote": "In the journal I do not just express myself more openly than I could to any person; I create myself.",
"author": "Susan Sontag"
},
{
"quote": "But what am I to do? I must have some drug, and reading isnt a strong enough drug now.",
"author": "C.S. Lewis"
},
{
"quote": "What a comfort is this journal. I tell myself to myself and throw the burden on my book and feel relieved.",
"author": "Anne Lister"
},
{
"quote": "If there was no other motive in view [except] to have the privilege of reading over our journals and for our children to read, it would pay for the time spent in writing it.",
"author": "Wilford Woodruff"
},
{
"quote": "As there are a thousand thoughts lying within a man that he does not know till he takes up the pen to write.",
"author": "William Makepeace Thackeray"
},
{
"quote": "I want to write, but more than that, I want to bring out all kinds of things that lie buried deep in my heart.",
"author": "Anne Frank"
},
{
"quote": "A diary is useful during conscious, intentional, and painful spiritual evolutions.",
"author": "André Gide"
},
{
"quote": "Most folks are about as happy as they make up their minds to be.",
"author": "Abraham Lincoln"
},
{
"quote": "It takes no more time to see the good side of life than it takes to see the bad.",
"author": "Jimmy Buffet"
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
src/img/splash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,12 +0,0 @@
* {
transition: 0.2s all ease-in-out;
}
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

@@ -1,19 +0,0 @@
import React from "react"
import ReactDOM from "react-dom"
import "./index.css"
import App from "./App"
import * as serviceWorker from "./serviceWorker"
import Firebase, { FirebaseContext } from "./components/firebase"
ReactDOM.render(
<FirebaseContext.Provider value={new Firebase()}>
<App />
</FirebaseContext.Provider>,
document.getElementById("root")
)
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.register()

15
src/pages/app.js Normal file
View File

@@ -0,0 +1,15 @@
import React from "react"
import App from "components/App"
import Layout from "components/Layout"
/* similar to create-react-app, the App.js is like the
entrypoint to the protected/client only content,
Context providers are moved to gatsby-browser.js
to wrap the root element with necessary providers
and context */
export default () => (
<Layout>
<App />
</Layout>
)

17
src/pages/index.js Normal file
View File

@@ -0,0 +1,17 @@
import React from "react"
import { SimpleNavbar } from "components/Navbar"
import Index from "routes/Start"
import Layout from "components/Layout"
import Container from "components/container"
// the landing page is generated like other Gatsby pages
// other unprotected routes like /login, /privacy, etc
// are completely server side rendered by gatsby build
export default () => (
<Layout>
<SimpleNavbar />
<Container>
<Index />
</Container>
</Layout>
)

View File

@@ -1,15 +1,15 @@
import React, { Component } from "react"
import { withRouter } from "react-router-dom"
import { navigate, Link } from "gatsby"
import styled from "@emotion/styled"
import { compose } from "recompose"
import { format } from "date-fns"
import { withTheme } from "emotion-theming"
import { Input, Button, P } from "../../elements"
import { SIZES } from "../../../styles/constants"
import { StyledLink as Link } from "../../elements"
import { FirebaseContext } from "../../firebase"
import { SimpleNavbar } from "components/Navbar"
import { Input, Button, P } from "components/elements"
import Layout from "components/Layout"
import { FirebaseContext } from "components/firebase"
import { SIZES } from "styles/constants"
const LoginGrid = styled.div`
display: grid;
@@ -19,19 +19,24 @@ const LoginGrid = styled.div`
const LoginLayout = styled.div`
max-width: ${SIZES.smallWidth};
width: 100%;
align-self: center;
margin-top: 20px;
margin: 20px auto;
`
const LoginPage = ({ history, theme }) => (
const LoginPage = ({ theme }) => (
<Layout>
<SimpleNavbar />
<LoginLayout>
<FirebaseContext.Consumer>
{firebase => <LoginForm history={history} firebase={firebase} />}
{firebase => <LoginForm firebase={firebase} />}
</FirebaseContext.Consumer>
<P colors={theme.colors} style={{ fontStyle: "italic" }}>
Don't have an account? <Link to={"/register"}>Sign Up</Link>
Don't have an account?{" "}
<Link style={{ color: theme.colors.primary }} to={"/register"}>
Sign Up
</Link>
</P>
</LoginLayout>
</Layout>
)
class LoginFormBase extends Component {
@@ -49,7 +54,7 @@ class LoginFormBase extends Component {
.doSignInWithEmailAndPassword(email, password)
.then(() => {
this.setState({ email: "", password: "", error: null })
this.props.history.push(format(new Date(), "/"))
navigate(`app/${format(new Date(), "/")}`)
})
.catch(error => {
this.setState({ error })
@@ -89,16 +94,13 @@ class LoginFormBase extends Component {
Login
</Button>
</LoginGrid>
{error && <p>{error.message}</p>}
{error && <P colors={theme.colors}>{error.message}</P>}
</form>
)
}
}
const LoginForm = compose(
withTheme,
withRouter
)(LoginFormBase)
const LoginForm = compose(withTheme)(LoginFormBase)
export default withTheme(LoginPage)

94
src/pages/privacy.js Normal file
View File

@@ -0,0 +1,94 @@
import React from "react"
import { SimpleH1, SimpleH2, P, Em } from "components/elements"
import Layout from "components/Layout"
import Container from "components/container"
import { SimpleNavbar } from "components/Navbar"
import { withTheme } from "emotion-theming"
const Terms = ({ theme }) => (
<Layout>
<SimpleNavbar />
<Container>
<SimpleH1 color={theme.colors.primary}>Privacy Policy</SimpleH1>
<Em color={theme.colors.secondary}>Last update: April 30, 2019</Em>{" "}
<P>
Sol Journal supports the following browsers: Chrome (latest), Safari
(latest), Firefox (50+)
</P>
<SimpleH2 color={theme.colors.secondary}>Rights</SimpleH2>
<P>
You don't have to provide your real name when you register to an
account, but you need to use a valid/verifiable email address.
<br />
<br />
You have the right to export your data at any time, in JSON format.
<br />
<br />
Your data will not be intentionally shown to other users or shared with
third parties.
<br />
<br />
Your personal data will not be shared with anyone without your consent.
<br />
<br />
Data saved on the hosted version of Sol Journal is encrypted as it is
transmitted and stored. However, data is not salted or hashed when it is
stored on Google servers so database administrators (me) can view the
data as plaintext. I won't view or use the data of other users for any
reason but if you would like to have more ownership of your data you can
setup the code on your own.
<br />
<br />
If the site ceases operation, you will receive an opportunity to export
all your data before the site dies.
<br />
<br />
Any new features that affect privacy will be strictly opt-in.
</P>
<SimpleH2 color={theme.colors.secondary}>Responsibilites</SimpleH2>
<P>
You will not use the site to store illegal information or data under
United States law (or any law).
<br />
<br />
You have to be at least 18+ to create an account and use the site.
<br />
<br />
You must not abuse the site by knowingly posting malicious code that
could harm you or the other users.
<br />
<br />
You may not make automated requests to the site.
<br />
<br />
You may not abuse the registration system.
<br />
<br />
You are responsible for keeping your account secure.
<br />
<br />I reserve the right to close accounts that abuse the system
(millions of entries or overloading services with requests) or use it in
an unreasonable manner.
</P>
<SimpleH2 color={theme.colors.secondary}>Other</SimpleH2>
<P>
Other important legal stuff Though I want to provide a great service,
there are certain things about the service I cannot promise. For
example, the services and software are provided as-is, at your own
risk, without express or implied warranty or condition of any kind. I
also disclaim any warranties of merchantability, fitness for a
particular purpose or non-infringement. Sol Journal will have no
responsibility for any harm to your computer system, loss or corruption
of data, or other harm that results from your access to or use of the
Services or Software.
<br />
<br />
These Terms can change at any time, but I'll try to be reasonable. This
is a service I've always and hope to be able to run it for a long time.
</P>
</Container>
</Layout>
)
export default withTheme(Terms)

View File

@@ -1,14 +1,14 @@
import React, { Component } from "react"
import { withRouter } from "react-router-dom"
import styled from "@emotion/styled"
import { navigate, Link } from "gatsby"
import { compose } from "recompose"
import { withTheme } from "emotion-theming"
import { Input, Button, P } from "../../elements"
import { SIZES } from "../../../styles/constants"
import { StyledLink as Link } from "../../elements"
import { FirebaseContext } from "../../firebase"
import { SimpleNavbar } from "components/Navbar"
import { Input, Button, P } from "components/elements"
import Layout from "components/Layout"
import { FirebaseContext } from "components/firebase"
import { SIZES } from "styles/constants"
const RegisterGrid = styled.div`
display: grid;
@@ -18,19 +18,30 @@ const RegisterGrid = styled.div`
const RegisterLayout = styled.div`
max-width: ${SIZES.smallWidth};
width: 100%;
align-self: center;
margin-top: 20px;
margin: 20px auto;
`
const RegisterPage = ({ history, theme }) => (
const RegisterPage = ({ theme }) => (
<Layout>
<SimpleNavbar />
<RegisterLayout>
<FirebaseContext.Consumer>
{firebase => <RegisterForm history={history} firebase={firebase} />}
{firebase => <RegisterForm firebase={firebase} />}
</FirebaseContext.Consumer>
<P colors={theme.colors} style={{ fontStyle: "italic" }}>
Already have an account? <Link to={"/login"}>Login</Link>
By registering for this site you are agreeing to the{" "}
<Link style={{ color: theme.colors.primary }} to={"/terms"}>
Terms of Service
</Link>
</P>
<P colors={theme.colors} style={{ fontStyle: "italic" }}>
Already have an account?{" "}
<Link style={{ color: theme.colors.primary }} to={"/login"}>
Login
</Link>
</P>
</RegisterLayout>
</Layout>
)
class RegisterFormBase extends Component {
@@ -69,7 +80,7 @@ class RegisterFormBase extends Component {
.set({
email: user.email,
})
this.props.history.push("/")
navigate("app/")
})
.catch(error => {
this.setState({ error })
@@ -131,16 +142,13 @@ class RegisterFormBase extends Component {
</Button>
</RegisterGrid>
{error && <p>{error.message}</p>}
{error && <P colors={theme.colors}>{error.message}</P>}
</form>
)
}
}
const RegisterForm = compose(
withTheme,
withRouter
)(RegisterFormBase)
const RegisterForm = compose(withTheme)(RegisterFormBase)
export default withTheme(RegisterPage)

88
src/pages/terms.js Normal file
View File

@@ -0,0 +1,88 @@
import React from "react"
import { SimpleH1, SimpleH2, P, Em } from "components/elements"
import Layout from "components/Layout"
import Container from "components/container"
import { SimpleNavbar } from "components/Navbar"
import { withTheme } from "emotion-theming"
const Terms = ({ theme }) => (
<Layout>
<SimpleNavbar />
<Container>
<SimpleH1 color={theme.colors.primary}>Terms of Service</SimpleH1>
<Em color={theme.colors.secondary}>Last update: April 30, 2019</Em>
<SimpleH2 color={theme.colors.secondary}>Scope of Service</SimpleH2>
<P>
Sol Journal supports the following browsers: Chrome (latest), Safari
(latest), Firefox (50+)
</P>
<SimpleH2 color={theme.colors.secondary}>Rights</SimpleH2>
<P>
You don't have to provide your real name when you register to an
account, but you need to use a valid/verifiable email address.
<br />
<br />
You have the right to export your data at any time, in JSON format.
<br />
<br />
Your data will not be intentionally shown to other users or shared with
third parties.
<br />
<br />
Your personal data will not be shared with anyone without your consent.
<br />
<br />
We reserve the right to discontinue any feature of the service at any
time. If the site ceases operation, you will receive an opportunity to
export all your data before the site dies.
<br />
<br />
Any new features that affect privacy will be strictly opt-in.
</P>
<SimpleH2 color={theme.colors.secondary}>Responsibilites</SimpleH2>
<P>
You will not use the site to store illegal information or data under
United States law (or any law).
<br />
<br />
You have to be at least 18+ to create an account and use the site.
<br />
<br />
You must not abuse the site by knowingly posting malicious code that
could harm you or the other users.
<br />
<br />
You may not make automated requests to the site.
<br />
<br />
You may not abuse the registration system.
<br />
<br />
You are responsible for keeping your account secure.
<br />
<br />I reserve the right to close accounts that abuse the system
(millions of entries or overloading services with requests) or use it in
an unreasonable manner.
</P>
<SimpleH2 color={theme.colors.secondary}>Other</SimpleH2>
<P>
Other important legal stuff Though I want to provide a great service,
there are certain things about the service I cannot promise. For
example, the services and software are provided “as-is”, at your own
risk, without express or implied warranty or condition of any kind. I
also disclaim any warranties of merchantability, fitness for a
particular purpose or non-infringement. Sol Journal will have no
responsibility for any harm to your computer system, loss or corruption
of data, or other harm that results from your access to or use of the
Services or Software.
<br />
<br />
These Terms can change at any time, but I'll try to be reasonable. This
is a service I've always and hope to be able to run it for a long time.
</P>
</Container>
</Layout>
)
export default withTheme(Terms)

View File

@@ -1,21 +1,20 @@
import React from "react"
import { addDays, subDays, format, isAfter, startOfYesterday } from "date-fns"
import { BeatLoader } from "react-spinners"
import styled from "@emotion/styled"
/** @jsx jsx */
import { jsx, css, keyframes } from "@emotion/core"
import { compose } from "recompose"
import { Prompt } from "react-router"
import { withRouter } from "react-router-dom"
import { withTheme } from "emotion-theming"
import { withFirebase } from "../../firebase"
import { withAuthentication } from "../../session"
import { OnlineContext } from "../../context/online"
import { addDays, subDays, format, isAfter, startOfYesterday } from "date-fns"
import { BeatLoader } from "react-spinners"
import { SIZES } from "../../../styles/constants"
import { withFirebase } from "components/firebase"
import { withAuthentication } from "components/session"
import { OnlineContext } from "components/context/online"
import Seek from "../../Seek"
import Icon from "../../Icon"
import { SIZES } from "styles/constants"
import Seek from "components/Seek"
import Icon from "components/Icon"
const EntryHeading = styled.div`
display: flex;
@@ -48,6 +47,7 @@ const OfflineNotice = styled.div`
color: ${props => props.theme.colors.secondary};
border: 1px solid;
border-color: ${props => props.theme.colors.tertiary};
font-size: ${SIZES.tiny};
border-radius: 3px;
`
const JournalEntryArea = styled.textarea`
@@ -58,6 +58,7 @@ const JournalEntryArea = styled.textarea`
background-color: transparent;
line-height: 1.5;
letter-spacing: 0.5px;
height: calc(100vh - 300px);
width: 100%;
border: none;
resize: none;
@@ -106,24 +107,31 @@ class Day extends React.Component {
lastEditedAt: new Date(),
}
timeout = 0
retrievedFromServer = false
static contextType = OnlineContext
componentDidMount() {
const {
history,
match: {
params: { year, month, day },
},
} = this.props
history.listen((location, action) => {
const [, year, month, day] = location.pathname.split("/")
this.onRouteChanged(year, month, day)
})
const { year, month, day } = this.props
this.getDocRef(year, month, day, false)
}
componentDidUpdate(prevProps) {
// check if a new route was hit, to save the entry and load the next one
if (this.props.uri !== prevProps.uri) {
const [
,
,
prevYear,
prevMonth,
prevDay,
] = prevProps.location.pathname.split("/")
const { text } = this.state
this.saveText(text, prevYear, prevMonth, prevDay)
const [, , year, month, day] = this.props.location.pathname.split("/")
this.onRouteChanged(year, month, day)
}
}
onRouteChanged = (year, month, day) => {
this.setState({ loading: true })
this.getDocRef(year, month, day, false)
@@ -168,11 +176,7 @@ class Day extends React.Component {
onChangeText = e => {
if (this.timeout) clearTimeout(this.timeout)
const text = e.target.value
const {
match: {
params: { year, month, day },
},
} = this.props
const { year, month, day } = this.props
this.setState({ text, lastEditedAt: new Date() })
this.timeout = setTimeout(() => {
@@ -184,11 +188,7 @@ class Day extends React.Component {
const entryTextArea = document.getElementById("entry-text-area")
const cursorIndex = entryTextArea.selectionStart
const { text } = this.state
const {
match: {
params: { year, month, day },
},
} = this.props
const { year, month, day } = this.props
const insertAt = (str, sub, pos) =>
`${str.slice(0, pos)}${sub}${str.slice(pos)}`
const newText = insertAt(text, format(new Date(), "h:mma "), cursorIndex)
@@ -227,12 +227,7 @@ class Day extends React.Component {
}
render() {
const {
match: {
params: { year, month, day },
},
theme,
} = this.props
const { year, month, day, theme } = this.props
const online = this.context
const { text, loading, saving, lastSavedAt, lastEditedAt } = this.state
const currentDay = new Date(year, month - 1, day)
@@ -241,10 +236,6 @@ class Day extends React.Component {
return (
<>
<Prompt
when={!hasSavedChanges}
message="You have unsaved changes, are you sure you want to leave?"
/>
<Seek
title={format(currentDay, "YYYY MMM DD - dddd")}
prev={format(subDays(currentDay, 1), "/YYYY/MM/DD")}
@@ -318,6 +309,5 @@ class Day extends React.Component {
export default compose(
withFirebase,
withTheme,
withAuthentication,
withRouter
withAuthentication
)(Day)

View File

@@ -1,5 +1,4 @@
import React, { Component } from "react"
import { Link } from "react-router-dom"
import styled from "@emotion/styled"
import {
isAfter,
@@ -12,7 +11,8 @@ import {
startOfMonth,
} from "date-fns"
import Seek from "../../Seek"
import { AppLink as Link } from "components/elements"
import Seek from "components/Seek"
const YearCardGrid = styled.div`
display: grid;
@@ -38,11 +38,7 @@ const YearCard = styled.div`
class Month extends Component {
render() {
const {
match: {
params: { year, month },
},
} = this.props
const { year, month } = this.props
const currentDay = new Date(year, month - 1)
// include all months unless it's this year

View File

@@ -1,5 +1,4 @@
import { Component } from "react"
import { Link } from "react-router-dom"
/** @jsx jsx */
import { jsx, css, keyframes } from "@emotion/core"
import styled from "@emotion/styled"
@@ -7,11 +6,11 @@ import { compose } from "recompose"
import { withTheme } from "emotion-theming"
import { BeatLoader } from "react-spinners"
import { Input } from "../../elements"
import { pad } from "../../../utils/date"
import { withFirebase } from "../../firebase"
import { withAuthentication } from "../../session"
import { AppLink as Link } from "components/elements"
import { Input } from "components/elements"
import { withFirebase } from "components/firebase"
import { withAuthentication } from "components/session"
import { pad } from "utils/date"
const SearchGrid = styled.div`
display: grid;
@@ -80,6 +79,8 @@ class Search extends Component {
}
}
// all entries for a user are fetched this scales poorly but is much
// simpler than an API key and setting up an index for Algolia
getEntries = async _ => {
const { firebase, authUser } = this.props
const entriesRef = await firebase.db
@@ -87,13 +88,7 @@ class Search extends Component {
.where("userId", "==", authUser.uid)
.get()
const entries = entriesRef.docs.map(doc => doc.data()).reverse()
// const sortedEntries = entries.sort((a, b) => {
// return (
// new Date(b.year, b.month - 1, b.day) -
// new Date(a.year, a.month - 1, a.day)
// )
// })
// console.log(sortedEntries)
this.setState({ entries, allEntries: entries, loading: false })
}

View File

@@ -1,22 +1,26 @@
import React, { Component } from "react"
import { Link } from "react-router-dom"
import { Helmet } from "react-helmet"
import { Link, StaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"
import styled from "@emotion/styled"
import { withTheme } from "emotion-theming"
import { SIZES } from "../../../styles/constants"
import { Button, P } from "../../elements"
import { todayUrl } from "../../../utils/date"
import Icon from "../../Icon"
import Logo from "../../Logo"
import { SIZES } from "styles/constants"
import { Button, P } from "components/elements"
import Icon from "components/Icon"
import Footer from "components/Footer"
import { todayUrl } from "utils/date"
const StartGrid = styled.div`
margin-top: 30px;
text-align: center;
line-height: 1.5;
color: ${props => props.theme.colors.primary};
height: 100%;
`
const FeatureGrid = styled.div`
display: grid;
text-align: left;
grid-template-rows: 1fr;
grid-gap: 30px;
`
@@ -36,21 +40,6 @@ const FeatureTitle = styled.div`
const FeatureDescription = styled.div`
color: ${props => props.theme.colors.secondary};
`
const Footer = styled.footer`
margin-top: 120px;
padding: 30px 0px;
text-align: center;
color: ${props => props.theme.colors.secondary};
`
const FooterLink = styled(Link)`
cursor: pointer;
color: ${props => props.theme.colors.secondary};
text-decoration: none;
margin: 10px;
&:hover {
color: ${props => props.theme.colors.tertiary};
}
`
const features = [
{
@@ -83,6 +72,12 @@ const features = [
desc:
"Download all of your journal entries at any time for back-up or safe keeping ",
},
{
icon: "Moon",
title: "Dark Mode",
desc:
"Take it easy on your eyes, with an easily accessible toggle from all screens and detection of night hours to turn on automatically",
},
]
class Start extends Component {
@@ -90,15 +85,78 @@ class Start extends Component {
const { theme } = this.props
return (
<StartGrid>
<Helmet>
<meta
name="description"
label="Connect with your soul through a simple, beautiful journaling experience from any of your devices."
/>
</Helmet>
<h1>Record what's on your mind, from anywhere</h1>
<P style={{ letterSpacing: 1.1, marginBottom: 30 }}>
Journaling can improve your health and help you take inventory of your
day. Sol Journal works offline and from any device. Use it as a place
to record thoughts and events from the day.
</P>
<Link to={todayUrl()} style={{ textDecoration: "none" }}>
<Button colors={theme.colors}>Write about today</Button>
<Link to={`/app${todayUrl()}`} style={{ textDecoration: "none" }}>
<Button colors={theme.colors}>Start Writing</Button>
</Link>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
marginTop: 30,
maxHeight: 350,
}}
>
<StaticQuery
query={graphql`
query {
landingGraphicLight: file(
relativePath: { eq: "landing-graphic-light.png" }
) {
childImageSharp {
fluid(maxWidth: 320) {
...GatsbyImageSharpFluid
}
}
}
landingGraphicDark: file(
relativePath: { eq: "landing-graphic-dark.png" }
) {
childImageSharp {
fluid(maxWidth: 320) {
...GatsbyImageSharpFluid
}
}
}
}
`}
render={data => {
return theme.name === "LIGHT" ? (
<Img
style={{
maxWidth: 320,
width: "100%",
maxHeight: 350,
height: "100%",
}}
fluid={data.landingGraphicLight.childImageSharp.fluid}
/>
) : (
<Img
style={{
maxWidth: 320,
width: "100%",
maxHeight: 350,
height: "100%",
}}
fluid={data.landingGraphicDark.childImageSharp.fluid}
/>
)
}}
/>
</div>
<div
style={{
margin: "60px 0px",
@@ -129,17 +187,7 @@ class Start extends Component {
</FeatureRow>
))}
</FeatureGrid>
<Footer>
<div>
<Logo color={theme.colors.logo} />
</div>
<div>
<FooterLink>View on GitHub</FooterLink>
<FooterLink to="terms">Terms of Service</FooterLink>
<FooterLink to="privacy">Privacy Policy</FooterLink>
</div>
<div>&copy; 2019</div>
</Footer>
<Footer />
</StartGrid>
)
}

View File

@@ -3,14 +3,13 @@ import styled from "@emotion/styled"
import { withTheme } from "emotion-theming"
import { compose } from "recompose"
import { format } from "date-fns"
import { withFirebase } from "../../firebase"
import { withAuthentication } from "../../session"
import { BeatLoader } from "react-spinners"
import { SIZES } from "../../../styles/constants"
import SignOut from "../../SignOut"
import { Button } from "../../elements"
import { SIZES } from "styles/constants"
import { withFirebase } from "components/firebase"
import { withAuthentication } from "components/session"
import SignOut from "components/SignOut"
import { Button } from "components/elements"
const ProfileGrid = styled.div`
display: grid;

77
src/routes/Welcome.js Normal file
View File

@@ -0,0 +1,77 @@
import React, { Component } from "react"
import { Helmet } from "react-helmet"
import { Link } from "gatsby"
import styled from "@emotion/styled"
import { withTheme } from "emotion-theming"
import { getDayOfYear } from "date-fns"
import { SIZES } from "styles/constants"
import { Button, P } from "components/elements"
import Footer from "components/Footer"
import { todayUrl } from "utils/date"
import Quotes from "data/quotes.json"
const WelcomeGrid = styled.div`
text-align: center;
margin-top: 60px;
line-height: 1.5;
color: ${props => props.theme.colors.primary};
display: flex;
flex-direction: column;
height: 100%;
`
const Quote = styled.blockquote`
margin-top: 30px;
font-family: Montserrat;
font-size: ${SIZES.medium};
color: ${props => props.theme.colors.primary};
`
const QuoteAuthor = styled(P)`
font-style: italic;
`
class Welcome extends Component {
render() {
const todaysQuote = Quotes[getDayOfYear(new Date()) % Quotes.length]
const { theme } = this.props
return (
<WelcomeGrid>
<Helmet>
<meta
name="description"
label="Connect with your soul through a simple, beautiful journaling experience from any device."
/>
</Helmet>
<div
style={{
fontFamily: "cursive",
fontSize: 100,
height: 60,
}}
>
"
</div>
<Quote>{todaysQuote.quote}</Quote>
<QuoteAuthor style={{ letterSpacing: 1.1, marginBottom: 30 }}>
- {todaysQuote.author}
</QuoteAuthor>
<div>
<P style={{ letterSpacing: 1.1, marginBottom: 30 }}>
This your space for wandering thoughts and ideas. Write about
whatever is on your mind.
</P>
</div>
<div>
<Link to={`/app${todayUrl()}`} style={{ textDecoration: "none" }}>
<Button colors={theme.colors}>Write about today</Button>
</Link>
</div>
<Footer />
</WelcomeGrid>
)
}
}
export default withTheme(Welcome)

View File

@@ -1,11 +1,11 @@
import React, { Component } from "react"
import { Link } from "react-router-dom"
import styled from "@emotion/styled"
import { addYears, subYears, format, isThisYear, getMonth } from "date-fns"
import styled from "@emotion/styled"
import { withTheme } from "emotion-theming"
import { months } from "../../../utils/date"
import Seek from "../../Seek"
import { AppLink as Link } from "components/elements"
import Seek from "components/Seek"
import { months } from "utils/date"
const MonthCardGrid = styled.div`
display: grid;
@@ -31,11 +31,7 @@ const MonthCard = styled.div`
class Year extends Component {
render() {
const {
match: {
params: { year },
},
} = this.props
const { year } = this.props
const currentDate = new Date(year, 0, 1)
// include all months unless it's this year

View File

@@ -1,6 +1,9 @@
// standardized role-based design tokens used throughout the whole app
// a name like lightGray doesn't make sense with themese when light
// and dark are possibile
const theme = {
LIGHT: {
name: "Light",
name: "LIGHT",
colors: {
logo: "#344157",
primary: "#2E3136",
@@ -11,10 +14,11 @@ const theme = {
bodyBackground: "#FFF",
inputBackground: "#FAFBFC",
hover: "hsla(233, 5%, 31%, 0.12)",
button: "#f2f3f5",
},
},
DARK: {
name: "Dark",
name: "DARK",
colors: {
logo: "#EAEAEA",
primary: "#F3F6F8",
@@ -25,6 +29,7 @@ const theme = {
bodyBackground: "#262B34",
inputBackground: "#272f3d",
hover: "hsla(233, 100%, 96%, 0.12)",
button: "#464d5d",
},
},
}

4945
yarn.lock

File diff suppressed because it is too large Load Diff