Add Dexie for persistence; user management with dexie; this is the way
parent
8036aa2942
commit
23d275acec
|
@ -11,10 +11,10 @@
|
||||||
"@emotion/styled": "latest",
|
"@emotion/styled": "latest",
|
||||||
"@mui/icons-material": "^5.4.2",
|
"@mui/icons-material": "^5.4.2",
|
||||||
"@mui/material": "latest",
|
"@mui/material": "latest",
|
||||||
"@mui/styles": "^5.4.2",
|
"dexie": "^3.2.1",
|
||||||
|
"dexie-react-hooks": "^1.1.1",
|
||||||
"react": "latest",
|
"react": "latest",
|
||||||
"react-dom": "latest",
|
"react-dom": "latest",
|
||||||
"react-router-dom": "^6.2.1",
|
|
||||||
"react-scripts": "^3.0.1"
|
"react-scripts": "^3.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -2364,46 +2364,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mui/styles": {
|
|
||||||
"version": "5.4.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mui/styles/-/styles-5.4.2.tgz",
|
|
||||||
"integrity": "sha512-BX75fNHmRF51yove9dBkH28gpSFjClOPDEnUwLTghPYN913OsqViS/iuCd61dxzygtEEmmeYuWfQjxu/F6vF5g==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.17.0",
|
|
||||||
"@emotion/hash": "^0.8.0",
|
|
||||||
"@mui/private-theming": "^5.4.2",
|
|
||||||
"@mui/types": "^7.1.2",
|
|
||||||
"@mui/utils": "^5.4.2",
|
|
||||||
"clsx": "^1.1.1",
|
|
||||||
"csstype": "^3.0.10",
|
|
||||||
"hoist-non-react-statics": "^3.3.2",
|
|
||||||
"jss": "^10.8.2",
|
|
||||||
"jss-plugin-camel-case": "^10.8.2",
|
|
||||||
"jss-plugin-default-unit": "^10.8.2",
|
|
||||||
"jss-plugin-global": "^10.8.2",
|
|
||||||
"jss-plugin-nested": "^10.8.2",
|
|
||||||
"jss-plugin-props-sort": "^10.8.2",
|
|
||||||
"jss-plugin-rule-value-function": "^10.8.2",
|
|
||||||
"jss-plugin-vendor-prefixer": "^10.8.2",
|
|
||||||
"prop-types": "^15.7.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/mui"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@types/react": "^16.8.6 || ^17.0.0",
|
|
||||||
"react": "^17.0.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@types/react": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@mui/system": {
|
"node_modules/@mui/system": {
|
||||||
"version": "5.4.2",
|
"version": "5.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.4.2.tgz",
|
||||||
|
@ -5683,15 +5643,6 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/css-vendor": {
|
|
||||||
"version": "2.0.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz",
|
|
||||||
"integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.8.3",
|
|
||||||
"is-in-browser": "^1.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/css-what": {
|
"node_modules/css-what": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
|
||||||
|
@ -6174,6 +6125,24 @@
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||||
},
|
},
|
||||||
|
"node_modules/dexie": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dexie/-/dexie-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-Y8oz3t2XC9hvjkP35B5I8rUkKKwM36GGRjWQCMjzIYScg7W+GHKDXobSYswkisW7CxL1/tKQtggMDsiWqDUc1g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dexie-react-hooks": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dexie-react-hooks/-/dexie-react-hooks-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-Cam5JP6PxHN564RvWEoe8cqLhosW0O4CAZ9XEVYeGHJBa6KEJlOpd9CUpV3kmU9dm2MrW97/lk7qkf1xpij7gA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": ">=16",
|
||||||
|
"dexie": ">=3.1.0-alpha.1 <5.0.0",
|
||||||
|
"react": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/diff-sequences": {
|
"node_modules/diff-sequences": {
|
||||||
"version": "24.9.0",
|
"version": "24.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz",
|
||||||
|
@ -8364,14 +8333,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
|
||||||
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
|
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
|
||||||
},
|
},
|
||||||
"node_modules/history": {
|
|
||||||
"version": "5.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz",
|
|
||||||
"integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.7.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/hmac-drbg": {
|
"node_modules/hmac-drbg": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||||
|
@ -8571,11 +8532,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
|
||||||
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
|
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
|
||||||
},
|
},
|
||||||
"node_modules/hyphenate-style-name": {
|
|
||||||
"version": "1.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
|
|
||||||
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
|
|
||||||
},
|
|
||||||
"node_modules/iconv-lite": {
|
"node_modules/iconv-lite": {
|
||||||
"version": "0.4.24",
|
"version": "0.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
|
@ -9168,11 +9124,6 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-in-browser": {
|
|
||||||
"version": "1.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
|
|
||||||
"integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU="
|
|
||||||
},
|
|
||||||
"node_modules/is-negative-zero": {
|
"node_modules/is-negative-zero": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
|
||||||
|
@ -10290,88 +10241,6 @@
|
||||||
"node": ">=0.6.0"
|
"node": ">=0.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jss": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jss/-/jss-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-YpzpreB6kUunQBbrlArlsMpXYyndt9JATbt95tajx0t4MTJJcCJdd4hdNpHmOIDiUJrF/oX5wtVFrS3uofWfGw==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.3.1",
|
|
||||||
"csstype": "^3.0.2",
|
|
||||||
"is-in-browser": "^1.1.3",
|
|
||||||
"tiny-warning": "^1.0.2"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/jss"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jss-plugin-camel-case": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-UH6uPpnDk413/r/2Olmw4+y54yEF2lRIV8XIZyuYpgPYTITLlPOsq6XB9qeqv+75SQSg3KLocq5jUBXW8qWWww==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.3.1",
|
|
||||||
"hyphenate-style-name": "^1.0.3",
|
|
||||||
"jss": "10.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jss-plugin-default-unit": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-7Ju4Q9wJ/MZPsxfu4T84mzdn7pLHWeqoGd/D8O3eDNNJ93Xc8PxnLmV8s8ZPNRYkLdxZqKtm1nPQ0BM4JRlq2w==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.3.1",
|
|
||||||
"jss": "10.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jss-plugin-global": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-4G8PHNJ0x6nwAFsEzcuVDiBlyMsj2y3VjmFAx/uHk/R/gzJV+yRHICjT4MKGGu1cJq2hfowFWCyrr/Gg37FbgQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.3.1",
|
|
||||||
"jss": "10.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jss-plugin-nested": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-2UJnDrfCZpMYcpPYR16oZB7VAC6b/1QLsRiAutOt7wJaaqwCBvNsosLEu/fUyKNQNGdvg2PPJFDO5AX7dwxtoA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.3.1",
|
|
||||||
"jss": "10.9.0",
|
|
||||||
"tiny-warning": "^1.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jss-plugin-props-sort": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-7A76HI8bzwqrsMOJTWKx/uD5v+U8piLnp5bvru7g/3ZEQOu1+PjHvv7bFdNO3DwNPC9oM0a//KwIJsIcDCjDzw==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.3.1",
|
|
||||||
"jss": "10.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jss-plugin-rule-value-function": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-IHJv6YrEf8pRzkY207cPmdbBstBaE+z8pazhPShfz0tZSDtRdQua5jjg6NMz3IbTasVx9FdnmptxPqSWL5tyJg==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.3.1",
|
|
||||||
"jss": "10.9.0",
|
|
||||||
"tiny-warning": "^1.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jss-plugin-vendor-prefixer": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-MbvsaXP7iiVdYVSEoi+blrW+AYnTDvHTW6I6zqi7JcwXdc6I9Kbm234nEblayhF38EftoenbM+5218pidmC5gA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/runtime": "^7.3.1",
|
|
||||||
"css-vendor": "^2.0.8",
|
|
||||||
"jss": "10.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jsx-ast-utils": {
|
"node_modules/jsx-ast-utils": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz",
|
||||||
|
@ -13824,30 +13693,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
||||||
},
|
},
|
||||||
"node_modules/react-router": {
|
|
||||||
"version": "6.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz",
|
|
||||||
"integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==",
|
|
||||||
"dependencies": {
|
|
||||||
"history": "^5.2.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": ">=16.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-router-dom": {
|
|
||||||
"version": "6.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz",
|
|
||||||
"integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==",
|
|
||||||
"dependencies": {
|
|
||||||
"history": "^5.2.0",
|
|
||||||
"react-router": "6.2.1"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": ">=16.8",
|
|
||||||
"react-dom": ">=16.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-scripts": {
|
"node_modules/react-scripts": {
|
||||||
"version": "3.4.4",
|
"version": "3.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.4.tgz",
|
||||||
|
@ -16357,11 +16202,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
||||||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
|
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
|
||||||
},
|
},
|
||||||
"node_modules/tiny-warning": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
|
|
||||||
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
|
|
||||||
},
|
|
||||||
"node_modules/tmp": {
|
"node_modules/tmp": {
|
||||||
"version": "0.0.33",
|
"version": "0.0.33",
|
||||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||||
|
@ -19518,30 +19358,6 @@
|
||||||
"prop-types": "^15.7.2"
|
"prop-types": "^15.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@mui/styles": {
|
|
||||||
"version": "5.4.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@mui/styles/-/styles-5.4.2.tgz",
|
|
||||||
"integrity": "sha512-BX75fNHmRF51yove9dBkH28gpSFjClOPDEnUwLTghPYN913OsqViS/iuCd61dxzygtEEmmeYuWfQjxu/F6vF5g==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.17.0",
|
|
||||||
"@emotion/hash": "^0.8.0",
|
|
||||||
"@mui/private-theming": "^5.4.2",
|
|
||||||
"@mui/types": "^7.1.2",
|
|
||||||
"@mui/utils": "^5.4.2",
|
|
||||||
"clsx": "^1.1.1",
|
|
||||||
"csstype": "^3.0.10",
|
|
||||||
"hoist-non-react-statics": "^3.3.2",
|
|
||||||
"jss": "^10.8.2",
|
|
||||||
"jss-plugin-camel-case": "^10.8.2",
|
|
||||||
"jss-plugin-default-unit": "^10.8.2",
|
|
||||||
"jss-plugin-global": "^10.8.2",
|
|
||||||
"jss-plugin-nested": "^10.8.2",
|
|
||||||
"jss-plugin-props-sort": "^10.8.2",
|
|
||||||
"jss-plugin-rule-value-function": "^10.8.2",
|
|
||||||
"jss-plugin-vendor-prefixer": "^10.8.2",
|
|
||||||
"prop-types": "^15.7.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@mui/system": {
|
"@mui/system": {
|
||||||
"version": "5.4.2",
|
"version": "5.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.4.2.tgz",
|
||||||
|
@ -22155,15 +21971,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"css-vendor": {
|
|
||||||
"version": "2.0.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz",
|
|
||||||
"integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.8.3",
|
|
||||||
"is-in-browser": "^1.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"css-what": {
|
"css-what": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
|
||||||
|
@ -22542,6 +22349,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dexie": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dexie/-/dexie-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-Y8oz3t2XC9hvjkP35B5I8rUkKKwM36GGRjWQCMjzIYScg7W+GHKDXobSYswkisW7CxL1/tKQtggMDsiWqDUc1g=="
|
||||||
|
},
|
||||||
|
"dexie-react-hooks": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dexie-react-hooks/-/dexie-react-hooks-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-Cam5JP6PxHN564RvWEoe8cqLhosW0O4CAZ9XEVYeGHJBa6KEJlOpd9CUpV3kmU9dm2MrW97/lk7qkf1xpij7gA==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"diff-sequences": {
|
"diff-sequences": {
|
||||||
"version": "24.9.0",
|
"version": "24.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz",
|
||||||
|
@ -24243,14 +24061,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
|
||||||
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
|
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
|
||||||
},
|
},
|
||||||
"history": {
|
|
||||||
"version": "5.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz",
|
|
||||||
"integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.7.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"hmac-drbg": {
|
"hmac-drbg": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||||
|
@ -24418,11 +24228,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
|
||||||
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
|
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
|
||||||
},
|
},
|
||||||
"hyphenate-style-name": {
|
|
||||||
"version": "1.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
|
|
||||||
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
|
|
||||||
},
|
|
||||||
"iconv-lite": {
|
"iconv-lite": {
|
||||||
"version": "0.4.24",
|
"version": "0.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
|
@ -24846,11 +24651,6 @@
|
||||||
"is-extglob": "^2.1.1"
|
"is-extglob": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"is-in-browser": {
|
|
||||||
"version": "1.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
|
|
||||||
"integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU="
|
|
||||||
},
|
|
||||||
"is-negative-zero": {
|
"is-negative-zero": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
|
||||||
|
@ -25729,84 +25529,6 @@
|
||||||
"verror": "1.10.0"
|
"verror": "1.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jss": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jss/-/jss-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-YpzpreB6kUunQBbrlArlsMpXYyndt9JATbt95tajx0t4MTJJcCJdd4hdNpHmOIDiUJrF/oX5wtVFrS3uofWfGw==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.3.1",
|
|
||||||
"csstype": "^3.0.2",
|
|
||||||
"is-in-browser": "^1.1.3",
|
|
||||||
"tiny-warning": "^1.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"jss-plugin-camel-case": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-UH6uPpnDk413/r/2Olmw4+y54yEF2lRIV8XIZyuYpgPYTITLlPOsq6XB9qeqv+75SQSg3KLocq5jUBXW8qWWww==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.3.1",
|
|
||||||
"hyphenate-style-name": "^1.0.3",
|
|
||||||
"jss": "10.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"jss-plugin-default-unit": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-7Ju4Q9wJ/MZPsxfu4T84mzdn7pLHWeqoGd/D8O3eDNNJ93Xc8PxnLmV8s8ZPNRYkLdxZqKtm1nPQ0BM4JRlq2w==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.3.1",
|
|
||||||
"jss": "10.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"jss-plugin-global": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-4G8PHNJ0x6nwAFsEzcuVDiBlyMsj2y3VjmFAx/uHk/R/gzJV+yRHICjT4MKGGu1cJq2hfowFWCyrr/Gg37FbgQ==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.3.1",
|
|
||||||
"jss": "10.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"jss-plugin-nested": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-2UJnDrfCZpMYcpPYR16oZB7VAC6b/1QLsRiAutOt7wJaaqwCBvNsosLEu/fUyKNQNGdvg2PPJFDO5AX7dwxtoA==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.3.1",
|
|
||||||
"jss": "10.9.0",
|
|
||||||
"tiny-warning": "^1.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"jss-plugin-props-sort": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-7A76HI8bzwqrsMOJTWKx/uD5v+U8piLnp5bvru7g/3ZEQOu1+PjHvv7bFdNO3DwNPC9oM0a//KwIJsIcDCjDzw==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.3.1",
|
|
||||||
"jss": "10.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"jss-plugin-rule-value-function": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-IHJv6YrEf8pRzkY207cPmdbBstBaE+z8pazhPShfz0tZSDtRdQua5jjg6NMz3IbTasVx9FdnmptxPqSWL5tyJg==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.3.1",
|
|
||||||
"jss": "10.9.0",
|
|
||||||
"tiny-warning": "^1.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"jss-plugin-vendor-prefixer": {
|
|
||||||
"version": "10.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.9.0.tgz",
|
|
||||||
"integrity": "sha512-MbvsaXP7iiVdYVSEoi+blrW+AYnTDvHTW6I6zqi7JcwXdc6I9Kbm234nEblayhF38EftoenbM+5218pidmC5gA==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.3.1",
|
|
||||||
"css-vendor": "^2.0.8",
|
|
||||||
"jss": "10.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"jsx-ast-utils": {
|
"jsx-ast-utils": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz",
|
||||||
|
@ -28599,23 +28321,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
||||||
},
|
},
|
||||||
"react-router": {
|
|
||||||
"version": "6.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz",
|
|
||||||
"integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==",
|
|
||||||
"requires": {
|
|
||||||
"history": "^5.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"react-router-dom": {
|
|
||||||
"version": "6.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz",
|
|
||||||
"integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==",
|
|
||||||
"requires": {
|
|
||||||
"history": "^5.2.0",
|
|
||||||
"react-router": "6.2.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"react-scripts": {
|
"react-scripts": {
|
||||||
"version": "3.4.4",
|
"version": "3.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.4.tgz",
|
||||||
|
@ -30620,11 +30325,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
||||||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
|
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
|
||||||
},
|
},
|
||||||
"tiny-warning": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
|
|
||||||
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
|
|
||||||
},
|
|
||||||
"tmp": {
|
"tmp": {
|
||||||
"version": "0.0.33",
|
"version": "0.0.33",
|
||||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
"@emotion/styled": "latest",
|
"@emotion/styled": "latest",
|
||||||
"@mui/icons-material": "^5.4.2",
|
"@mui/icons-material": "^5.4.2",
|
||||||
"@mui/material": "latest",
|
"@mui/material": "latest",
|
||||||
|
"dexie": "^3.2.1",
|
||||||
|
"dexie-react-hooks": "^1.1.1",
|
||||||
"react": "latest",
|
"react": "latest",
|
||||||
"react-dom": "latest",
|
"react-dom": "latest",
|
||||||
"react-scripts": "^3.0.1"
|
"react-scripts": "^3.0.1"
|
||||||
|
|
|
@ -7,9 +7,11 @@ import {
|
||||||
topicShortUrl,
|
topicShortUrl,
|
||||||
topicUrlJsonPollWithSince
|
topicUrlJsonPollWithSince
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
|
import db from "./db";
|
||||||
|
|
||||||
class Api {
|
class Api {
|
||||||
async poll(baseUrl, topic, since, user) {
|
async poll(baseUrl, topic, since) {
|
||||||
|
const user = await db.users.get(baseUrl);
|
||||||
const shortUrl = topicShortUrl(baseUrl, topic);
|
const shortUrl = topicShortUrl(baseUrl, topic);
|
||||||
const url = (since)
|
const url = (since)
|
||||||
? topicUrlJsonPollWithSince(baseUrl, topic, since)
|
? topicUrlJsonPollWithSince(baseUrl, topic, since)
|
||||||
|
@ -24,7 +26,8 @@ class Api {
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
async publish(baseUrl, topic, user, message) {
|
async publish(baseUrl, topic, message) {
|
||||||
|
const user = await db.users.get(baseUrl);
|
||||||
const url = topicUrl(baseUrl, topic);
|
const url = topicUrl(baseUrl, topic);
|
||||||
console.log(`[Api] Publishing message to ${url}`);
|
console.log(`[Api] Publishing message to ${url}`);
|
||||||
await fetch(url, {
|
await fetch(url, {
|
||||||
|
|
|
@ -85,7 +85,7 @@ class Connection {
|
||||||
if (this.since) {
|
if (this.since) {
|
||||||
params.push(`since=${this.since}`);
|
params.push(`since=${this.since}`);
|
||||||
}
|
}
|
||||||
if (this.user !== null) {
|
if (this.user) {
|
||||||
const auth = encodeBase64Url(basicAuth(this.user.username, this.user.password));
|
const auth = encodeBase64Url(basicAuth(this.user.username, this.user.password));
|
||||||
params.push(`auth=${auth}`);
|
params.push(`auth=${auth}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,11 @@ class ConnectionManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh(subscriptions, users, onNotification) {
|
refresh(subscriptions, users, onNotification) {
|
||||||
|
if (!subscriptions || !users) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
console.log(`[ConnectionManager] Refreshing connections`);
|
console.log(`[ConnectionManager] Refreshing connections`);
|
||||||
|
console.log(users);
|
||||||
const subscriptionIds = subscriptions.ids();
|
const subscriptionIds = subscriptions.ids();
|
||||||
const deletedIds = Array.from(this.connections.keys()).filter(id => !subscriptionIds.includes(id));
|
const deletedIds = Array.from(this.connections.keys()).filter(id => !subscriptionIds.includes(id));
|
||||||
|
|
||||||
|
@ -16,7 +20,7 @@ class ConnectionManager {
|
||||||
if (added) {
|
if (added) {
|
||||||
const baseUrl = subscription.baseUrl;
|
const baseUrl = subscription.baseUrl;
|
||||||
const topic = subscription.topic;
|
const topic = subscription.topic;
|
||||||
const user = users.get(baseUrl);
|
const [user] = users.filter(user => user.baseUrl === baseUrl);
|
||||||
const since = subscription.last;
|
const since = subscription.last;
|
||||||
const connection = new Connection(id, baseUrl, topic, user, since, onNotification);
|
const connection = new Connection(id, baseUrl, topic, user, since, onNotification);
|
||||||
this.connections.set(id, connection);
|
this.connections.set(id, connection);
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import Subscription from "./Subscription";
|
import Subscription from "./Subscription";
|
||||||
import Subscriptions from "./Subscriptions";
|
import Subscriptions from "./Subscriptions";
|
||||||
import Users from "./Users";
|
|
||||||
import User from "./User";
|
|
||||||
|
|
||||||
class Repository {
|
class Repository {
|
||||||
loadSubscriptions() {
|
loadSubscriptions() {
|
||||||
|
@ -43,40 +41,6 @@ class Repository {
|
||||||
localStorage.setItem('subscriptions', serialized);
|
localStorage.setItem('subscriptions', serialized);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadUsers() {
|
|
||||||
console.log(`[Repository] Loading users from localStorage`);
|
|
||||||
const users = new Users();
|
|
||||||
users.loaded = true;
|
|
||||||
const serialized = localStorage.getItem('users');
|
|
||||||
if (serialized === null) {
|
|
||||||
return users;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
JSON.parse(serialized).forEach(u => {
|
|
||||||
users.add(new User(u.baseUrl, u.username, u.password));
|
|
||||||
});
|
|
||||||
return users;
|
|
||||||
} catch (e) {
|
|
||||||
console.log(`[Repository] Unable to deserialize users: ${e.message}`);
|
|
||||||
return users;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
saveUsers(users) {
|
|
||||||
if (!users.loaded) {
|
|
||||||
return; // Avoid saving invalid state, triggered by initial useEffect hook
|
|
||||||
}
|
|
||||||
console.log(`[Repository] Saving users to localStorage`);
|
|
||||||
const serialized = JSON.stringify(users.map(user => {
|
|
||||||
return {
|
|
||||||
baseUrl: user.baseUrl,
|
|
||||||
username: user.username,
|
|
||||||
password: user.password
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
localStorage.setItem('users', serialized);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSelectedSubscriptionId() {
|
loadSelectedSubscriptionId() {
|
||||||
console.log(`[Repository] Loading selected subscription ID from localStorage`);
|
console.log(`[Repository] Loading selected subscription ID from localStorage`);
|
||||||
const selectedSubscriptionId = localStorage.getItem('selectedSubscriptionId');
|
const selectedSubscriptionId = localStorage.getItem('selectedSubscriptionId');
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
class User {
|
|
||||||
constructor(baseUrl, username, password) {
|
|
||||||
this.baseUrl = baseUrl;
|
|
||||||
this.username = username;
|
|
||||||
this.password = password;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default User;
|
|
|
@ -1,38 +0,0 @@
|
||||||
class Users {
|
|
||||||
constructor() {
|
|
||||||
this.loaded = false; // FIXME I hate this
|
|
||||||
this.users = new Map();
|
|
||||||
}
|
|
||||||
|
|
||||||
add(user) {
|
|
||||||
this.users.set(user.baseUrl, user);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
get(baseUrl) {
|
|
||||||
const user = this.users.get(baseUrl);
|
|
||||||
return (user) ? user : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
update(user) {
|
|
||||||
return this.add(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(baseUrl) {
|
|
||||||
this.users.delete(baseUrl);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
map(cb) {
|
|
||||||
return Array.from(this.users.values()).map(cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
clone() {
|
|
||||||
const c = new Users();
|
|
||||||
c.loaded = this.loaded;
|
|
||||||
c.users = new Map(this.users);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Users;
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import Dexie from 'dexie';
|
||||||
|
|
||||||
|
// Uses Dexie.js
|
||||||
|
// https://dexie.org/docs/API-Reference#quick-reference
|
||||||
|
//
|
||||||
|
// Notes:
|
||||||
|
// - As per docs, we only declare the indexable columns, not all columns
|
||||||
|
|
||||||
|
const db = new Dexie('ntfy');
|
||||||
|
|
||||||
|
db.version(1).stores({
|
||||||
|
users: '&baseUrl, username',
|
||||||
|
});
|
||||||
|
|
||||||
|
export default db;
|
|
@ -37,7 +37,6 @@ const ActionBar = (props) => {
|
||||||
</Typography>
|
</Typography>
|
||||||
{props.selectedSubscription !== null && <IconSubscribeSettings
|
{props.selectedSubscription !== null && <IconSubscribeSettings
|
||||||
subscription={props.selectedSubscription}
|
subscription={props.selectedSubscription}
|
||||||
users={props.users}
|
|
||||||
onClearAll={props.onClearAll}
|
onClearAll={props.onClearAll}
|
||||||
onUnsubscribe={props.onUnsubscribe}
|
onUnsubscribe={props.onUnsubscribe}
|
||||||
/>}
|
/>}
|
||||||
|
|
|
@ -12,19 +12,20 @@ import connectionManager from "../app/ConnectionManager";
|
||||||
import Subscriptions from "../app/Subscriptions";
|
import Subscriptions from "../app/Subscriptions";
|
||||||
import Navigation from "./Navigation";
|
import Navigation from "./Navigation";
|
||||||
import ActionBar from "./ActionBar";
|
import ActionBar from "./ActionBar";
|
||||||
import Users from "../app/Users";
|
|
||||||
import notificationManager from "../app/NotificationManager";
|
import notificationManager from "../app/NotificationManager";
|
||||||
import NoTopics from "./NoTopics";
|
import NoTopics from "./NoTopics";
|
||||||
import Preferences from "./Preferences";
|
import Preferences from "./Preferences";
|
||||||
|
import db from "../app/db";
|
||||||
|
import {useLiveQuery} from "dexie-react-hooks";
|
||||||
|
|
||||||
// TODO subscribe dialog:
|
// TODO subscribe dialog:
|
||||||
// - check/use existing user
|
// - check/use existing user
|
||||||
// - add baseUrl
|
// - add baseUrl
|
||||||
// TODO user management
|
|
||||||
// TODO embed into ntfy server
|
// TODO embed into ntfy server
|
||||||
// TODO make default server functional
|
// TODO make default server functional
|
||||||
// TODO indexeddb for notifications + subscriptions
|
// TODO indexeddb for notifications + subscriptions
|
||||||
// TODO business logic with callbacks
|
// TODO business logic with callbacks
|
||||||
|
// TODO connection indicator in subscription list
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
console.log(`[App] Rendering main view`);
|
console.log(`[App] Rendering main view`);
|
||||||
|
@ -32,21 +33,18 @@ const App = () => {
|
||||||
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
|
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
|
||||||
const [prefsOpen, setPrefsOpen] = useState(false);
|
const [prefsOpen, setPrefsOpen] = useState(false);
|
||||||
const [subscriptions, setSubscriptions] = useState(new Subscriptions());
|
const [subscriptions, setSubscriptions] = useState(new Subscriptions());
|
||||||
const [users, setUsers] = useState(new Users());
|
|
||||||
const [selectedSubscription, setSelectedSubscription] = useState(null);
|
const [selectedSubscription, setSelectedSubscription] = useState(null);
|
||||||
const [notificationsGranted, setNotificationsGranted] = useState(notificationManager.granted());
|
const [notificationsGranted, setNotificationsGranted] = useState(notificationManager.granted());
|
||||||
|
const users = useLiveQuery(() => db.users.toArray());
|
||||||
const handleSubscriptionClick = (subscriptionId) => {
|
const handleSubscriptionClick = (subscriptionId) => {
|
||||||
setSelectedSubscription(subscriptions.get(subscriptionId));
|
setSelectedSubscription(subscriptions.get(subscriptionId));
|
||||||
setPrefsOpen(false);
|
setPrefsOpen(false);
|
||||||
}
|
}
|
||||||
const handleSubscribeSubmit = (subscription, user) => {
|
const handleSubscribeSubmit = (subscription) => {
|
||||||
console.log(`[App] New subscription: ${subscription.id}`);
|
console.log(`[App] New subscription: ${subscription.id}`);
|
||||||
if (user !== null) {
|
|
||||||
setUsers(prev => prev.add(user).clone());
|
|
||||||
}
|
|
||||||
setSubscriptions(prev => prev.add(subscription).clone());
|
setSubscriptions(prev => prev.add(subscription).clone());
|
||||||
setSelectedSubscription(subscription);
|
setSelectedSubscription(subscription);
|
||||||
poll(subscription, user);
|
poll(subscription);
|
||||||
handleRequestPermission();
|
handleRequestPermission();
|
||||||
};
|
};
|
||||||
const handleDeleteNotification = (subscriptionId, notificationId) => {
|
const handleDeleteNotification = (subscriptionId, notificationId) => {
|
||||||
|
@ -80,9 +78,9 @@ const App = () => {
|
||||||
setPrefsOpen(true);
|
setPrefsOpen(true);
|
||||||
setSelectedSubscription(null);
|
setSelectedSubscription(null);
|
||||||
};
|
};
|
||||||
const poll = (subscription, user) => {
|
const poll = (subscription) => {
|
||||||
const since = subscription.last;
|
const since = subscription.last;
|
||||||
api.poll(subscription.baseUrl, subscription.topic, since, user)
|
api.poll(subscription.baseUrl, subscription.topic, since)
|
||||||
.then(notifications => {
|
.then(notifications => {
|
||||||
setSubscriptions(prev => {
|
setSubscriptions(prev => {
|
||||||
subscription.addNotifications(notifications);
|
subscription.addNotifications(notifications);
|
||||||
|
@ -94,12 +92,10 @@ const App = () => {
|
||||||
// Define hooks: Note that the order of the hooks is important. The "loading" hooks
|
// Define hooks: Note that the order of the hooks is important. The "loading" hooks
|
||||||
// must be before the "saving" hooks.
|
// must be before the "saving" hooks.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Load subscriptions and users
|
// Load subscriptions
|
||||||
const subscriptions = repository.loadSubscriptions();
|
const subscriptions = repository.loadSubscriptions();
|
||||||
const selectedSubscriptionId = repository.loadSelectedSubscriptionId();
|
const selectedSubscriptionId = repository.loadSelectedSubscriptionId();
|
||||||
const users = repository.loadUsers();
|
|
||||||
setSubscriptions(subscriptions);
|
setSubscriptions(subscriptions);
|
||||||
setUsers(users);
|
|
||||||
|
|
||||||
// Set selected subscription
|
// Set selected subscription
|
||||||
const maybeSelectedSubscription = subscriptions.get(selectedSubscriptionId);
|
const maybeSelectedSubscription = subscriptions.get(selectedSubscriptionId);
|
||||||
|
@ -109,8 +105,7 @@ const App = () => {
|
||||||
|
|
||||||
// Poll all subscriptions
|
// Poll all subscriptions
|
||||||
subscriptions.forEach((subscriptionId, subscription) => {
|
subscriptions.forEach((subscriptionId, subscription) => {
|
||||||
const user = users.get(subscription.baseUrl); // May be null
|
poll(subscription);
|
||||||
poll(subscription, user);
|
|
||||||
});
|
});
|
||||||
}, [/* initial render */]);
|
}, [/* initial render */]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -127,7 +122,6 @@ const App = () => {
|
||||||
connectionManager.refresh(subscriptions, users, handleNotification);
|
connectionManager.refresh(subscriptions, users, handleNotification);
|
||||||
}, [subscriptions, users]);
|
}, [subscriptions, users]);
|
||||||
useEffect(() => repository.saveSubscriptions(subscriptions), [subscriptions]);
|
useEffect(() => repository.saveSubscriptions(subscriptions), [subscriptions]);
|
||||||
useEffect(() => repository.saveUsers(users), [users]);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const subscriptionId = (selectedSubscription) ? selectedSubscription.id : "";
|
const subscriptionId = (selectedSubscription) ? selectedSubscription.id : "";
|
||||||
repository.saveSelectedSubscriptionId(subscriptionId)
|
repository.saveSelectedSubscriptionId(subscriptionId)
|
||||||
|
@ -140,7 +134,6 @@ const App = () => {
|
||||||
<CssBaseline/>
|
<CssBaseline/>
|
||||||
<ActionBar
|
<ActionBar
|
||||||
selectedSubscription={selectedSubscription}
|
selectedSubscription={selectedSubscription}
|
||||||
users={users}
|
|
||||||
onClearAll={handleDeleteAllNotifications}
|
onClearAll={handleDeleteAllNotifications}
|
||||||
onUnsubscribe={handleUnsubscribe}
|
onUnsubscribe={handleUnsubscribe}
|
||||||
onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)}
|
onMobileDrawerToggle={() => setMobileDrawerOpen(!mobileDrawerOpen)}
|
||||||
|
|
|
@ -14,7 +14,6 @@ import api from "../app/Api";
|
||||||
const IconSubscribeSettings = (props) => {
|
const IconSubscribeSettings = (props) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const anchorRef = useRef(null);
|
const anchorRef = useRef(null);
|
||||||
const users = props.users;
|
|
||||||
|
|
||||||
const handleToggle = () => {
|
const handleToggle = () => {
|
||||||
setOpen((prevOpen) => !prevOpen);
|
setOpen((prevOpen) => !prevOpen);
|
||||||
|
@ -40,8 +39,7 @@ const IconSubscribeSettings = (props) => {
|
||||||
const handleSendTestMessage = () => {
|
const handleSendTestMessage = () => {
|
||||||
const baseUrl = props.subscription.baseUrl;
|
const baseUrl = props.subscription.baseUrl;
|
||||||
const topic = props.subscription.topic;
|
const topic = props.subscription.topic;
|
||||||
const user = users.get(baseUrl); // May be null
|
api.publish(baseUrl, topic,
|
||||||
api.publish(baseUrl, topic, user,
|
|
||||||
`This is a test notification sent by the ntfy Web UI at ${new Date().toString()}.`); // FIXME result ignored
|
`This is a test notification sent by the ntfy Web UI at ${new Date().toString()}.`); // FIXME result ignored
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,9 +57,9 @@ const NavList = (props) => {
|
||||||
setSubscribeDialogOpen(false);
|
setSubscribeDialogOpen(false);
|
||||||
setSubscribeDialogKey(prev => prev+1);
|
setSubscribeDialogKey(prev => prev+1);
|
||||||
}
|
}
|
||||||
const handleSubscribeSubmit = (subscription, user) => {
|
const handleSubscribeSubmit = (subscription) => {
|
||||||
handleSubscribeReset();
|
handleSubscribeReset();
|
||||||
props.onSubscribeSubmit(subscription, user);
|
props.onSubscribeSubmit(subscription);
|
||||||
}
|
}
|
||||||
const showSubscriptionsList = props.subscriptions.size() > 0;
|
const showSubscriptionsList = props.subscriptions.size() > 0;
|
||||||
const showGrantPermissionsBox = props.subscriptions.size() > 0 && !props.notificationsGranted;
|
const showGrantPermissionsBox = props.subscriptions.size() > 0 && !props.notificationsGranted;
|
||||||
|
|
|
@ -1,6 +1,18 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {useState} from 'react';
|
import {useEffect, useState} from 'react';
|
||||||
import {FormControl, Select, Stack, Table, TableBody, TableCell, TableHead, TableRow} from "@mui/material";
|
import {
|
||||||
|
CardActions,
|
||||||
|
CardContent,
|
||||||
|
FormControl,
|
||||||
|
Select,
|
||||||
|
Stack,
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableRow,
|
||||||
|
useMediaQuery
|
||||||
|
} from "@mui/material";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import Paper from "@mui/material/Paper";
|
import Paper from "@mui/material/Paper";
|
||||||
import repository from "../app/Repository";
|
import repository from "../app/Repository";
|
||||||
|
@ -11,6 +23,15 @@ import IconButton from "@mui/material/IconButton";
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import MenuItem from "@mui/material/MenuItem";
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
|
import Card from "@mui/material/Card";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import db from "../app/db";
|
||||||
|
import {useLiveQuery} from "dexie-react-hooks";
|
||||||
|
import theme from "./theme";
|
||||||
|
import Dialog from "@mui/material/Dialog";
|
||||||
|
import DialogTitle from "@mui/material/DialogTitle";
|
||||||
|
import DialogContent from "@mui/material/DialogContent";
|
||||||
|
import DialogActions from "@mui/material/DialogActions";
|
||||||
|
|
||||||
const Preferences = (props) => {
|
const Preferences = (props) => {
|
||||||
return (
|
return (
|
||||||
|
@ -26,7 +47,7 @@ const Preferences = (props) => {
|
||||||
|
|
||||||
const Notifications = (props) => {
|
const Notifications = (props) => {
|
||||||
return (
|
return (
|
||||||
<Paper sx={{p: 3}}>
|
<Card sx={{p: 3}}>
|
||||||
<Typography variant="h5">
|
<Typography variant="h5">
|
||||||
Notifications
|
Notifications
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -34,7 +55,7 @@ const Notifications = (props) => {
|
||||||
<MinPriority/>
|
<MinPriority/>
|
||||||
<DeleteAfter/>
|
<DeleteAfter/>
|
||||||
</PrefGroup>
|
</PrefGroup>
|
||||||
</Paper>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -66,7 +87,7 @@ const DeleteAfter = () => {
|
||||||
repository.setDeleteAfter(ev.target.value);
|
repository.setDeleteAfter(ev.target.value);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Pref title="Minimum priority">
|
<Pref title="Delete notifications">
|
||||||
<FormControl fullWidth variant="standard" sx={{ m: 1 }}>
|
<FormControl fullWidth variant="standard" sx={{ m: 1 }}>
|
||||||
<Select value={deleteAfter} onChange={handleChange}>
|
<Select value={deleteAfter} onChange={handleChange}>
|
||||||
<MenuItem value={0}>Never</MenuItem>
|
<MenuItem value={0}>Never</MenuItem>
|
||||||
|
@ -139,53 +160,191 @@ const DefaultServer = (props) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Users = (props) => {
|
const Users = (props) => {
|
||||||
|
const [dialogKey, setDialogKey] = useState(0);
|
||||||
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
|
const users = useLiveQuery(() => db.users.toArray());
|
||||||
|
const handleAddClick = () => {
|
||||||
|
setDialogKey(prev => prev+1);
|
||||||
|
setDialogOpen(true);
|
||||||
|
};
|
||||||
|
const handleDialogCancel = () => {
|
||||||
|
setDialogOpen(false);
|
||||||
|
};
|
||||||
|
const handleDialogSubmit = async (user) => {
|
||||||
|
setDialogOpen(false);
|
||||||
|
try {
|
||||||
|
await db.users.add(user);
|
||||||
|
console.debug(`[Preferences] User ${user.username} for ${user.baseUrl} added`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`[Preferences] Error adding user.`, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<Paper sx={{p: 3}}>
|
<Card sx={{p: 3}}>
|
||||||
<Typography variant="h5">
|
<CardContent>
|
||||||
Manage users
|
<Typography variant="h5">
|
||||||
</Typography>
|
Manage users
|
||||||
<Paragraph>
|
</Typography>
|
||||||
You may manage users for your protected topics here. Please note that since this is a client
|
<Paragraph>
|
||||||
application only, username and password are stored in the browser's local storage.
|
Add/remove users for your protected topics here. Please note that username and password are
|
||||||
</Paragraph>
|
stored in the browser's local storage.
|
||||||
<UserTable/>
|
</Paragraph>
|
||||||
</Paper>
|
{users?.length > 0 && <UserTable users={users}/>}
|
||||||
|
</CardContent>
|
||||||
|
<CardActions>
|
||||||
|
<Button onClick={handleAddClick}>Add user</Button>
|
||||||
|
<UserDialog
|
||||||
|
key={`userAddDialog${dialogKey}`}
|
||||||
|
open={dialogOpen}
|
||||||
|
user={null}
|
||||||
|
users={users}
|
||||||
|
onCancel={handleDialogCancel}
|
||||||
|
onSubmit={handleDialogSubmit}
|
||||||
|
/>
|
||||||
|
</CardActions>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const UserTable = () => {
|
const UserTable = (props) => {
|
||||||
const users = repository.loadUsers();
|
const [dialogKey, setDialogKey] = useState(0);
|
||||||
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
|
const [dialogUser, setDialogUser] = useState(null);
|
||||||
|
const handleEditClick = (user) => {
|
||||||
|
setDialogKey(prev => prev+1);
|
||||||
|
setDialogUser(user);
|
||||||
|
setDialogOpen(true);
|
||||||
|
};
|
||||||
|
const handleDialogCancel = () => {
|
||||||
|
setDialogOpen(false);
|
||||||
|
};
|
||||||
|
const handleDialogSubmit = async (user) => {
|
||||||
|
setDialogOpen(false);
|
||||||
|
try {
|
||||||
|
await db.users.put(user); // put() is an upsert
|
||||||
|
console.debug(`[Preferences] User ${user.username} for ${user.baseUrl} updated`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`[Preferences] Error updating user.`, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleDeleteClick = async (user) => {
|
||||||
|
try {
|
||||||
|
await db.users.delete(user.baseUrl);
|
||||||
|
console.debug(`[Preferences] User ${user.username} for ${user.baseUrl} deleted`);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`[Preferences] Error deleting user for ${user.baseUrl}`, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<Table size="small">
|
<Table size="small">
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell>User</TableCell>
|
<TableCell>User</TableCell>
|
||||||
<TableCell>Service URL</TableCell>
|
<TableCell>Service URL</TableCell>
|
||||||
<TableCell/>
|
<TableCell/>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{props.users?.map(user => (
|
||||||
|
<TableRow
|
||||||
|
key={user.baseUrl}
|
||||||
|
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
|
||||||
|
>
|
||||||
|
<TableCell component="th" scope="row">{user.username}</TableCell>
|
||||||
|
<TableCell>{user.baseUrl}</TableCell>
|
||||||
|
<TableCell align="right">
|
||||||
|
<IconButton onClick={() => handleEditClick(user)}>
|
||||||
|
<EditIcon/>
|
||||||
|
</IconButton>
|
||||||
|
<IconButton onClick={() => handleDeleteClick(user)}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
))}
|
||||||
<TableBody>
|
</TableBody>
|
||||||
{users.map((user, i) => (
|
<UserDialog
|
||||||
<TableRow
|
key={`userEditDialog${dialogKey}`}
|
||||||
key={i}
|
open={dialogOpen}
|
||||||
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
|
user={dialogUser}
|
||||||
>
|
users={props.users}
|
||||||
<TableCell component="th" scope="row">{user.username}</TableCell>
|
onCancel={handleDialogCancel}
|
||||||
<TableCell>{user.baseUrl}</TableCell>
|
onSubmit={handleDialogSubmit}
|
||||||
<TableCell align="right">
|
/>
|
||||||
<IconButton>
|
</Table>
|
||||||
<EditIcon/>
|
|
||||||
</IconButton>
|
|
||||||
<IconButton>
|
|
||||||
<CloseIcon />
|
|
||||||
</IconButton>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
const UserDialog = (props) => {
|
||||||
|
const [baseUrl, setBaseUrl] = useState("");
|
||||||
|
const [username, setUsername] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
||||||
|
const editMode = props.user !== null;
|
||||||
|
const addButtonEnabled = (() => {
|
||||||
|
if (editMode) {
|
||||||
|
return username.length > 0 && password.length > 0;
|
||||||
|
}
|
||||||
|
const baseUrlExists = props.users?.map(user => user.baseUrl).includes(baseUrl);
|
||||||
|
return !baseUrlExists && username.length > 0 && password.length > 0;
|
||||||
|
})();
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
props.onSubmit({
|
||||||
|
baseUrl: baseUrl,
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
})
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
if (editMode) {
|
||||||
|
setBaseUrl(props.user.baseUrl);
|
||||||
|
setUsername(props.user.username);
|
||||||
|
setPassword(props.user.password);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<Dialog open={props.open} onClose={props.onCancel} fullScreen={fullScreen}>
|
||||||
|
<DialogTitle>{editMode ? "Edit user" : "Add user"}</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
{!editMode && <TextField
|
||||||
|
autoFocus
|
||||||
|
margin="dense"
|
||||||
|
id="baseUrl"
|
||||||
|
label="Service URL, e.g. https://ntfy.sh"
|
||||||
|
value={baseUrl}
|
||||||
|
onChange={ev => setBaseUrl(ev.target.value)}
|
||||||
|
type="url"
|
||||||
|
fullWidth
|
||||||
|
variant="standard"
|
||||||
|
/>}
|
||||||
|
<TextField
|
||||||
|
autoFocus={editMode}
|
||||||
|
margin="dense"
|
||||||
|
id="username"
|
||||||
|
label="Username, e.g. phil"
|
||||||
|
value={username}
|
||||||
|
onChange={ev => setUsername(ev.target.value)}
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
variant="standard"
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
margin="dense"
|
||||||
|
id="password"
|
||||||
|
label="Password"
|
||||||
|
type="password"
|
||||||
|
value={password}
|
||||||
|
onChange={ev => setPassword(ev.target.value)}
|
||||||
|
fullWidth
|
||||||
|
variant="standard"
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={props.onCancel}>Cancel</Button>
|
||||||
|
<Button onClick={handleSubmit} disabled={!addButtonEnabled}>{editMode ? "Save" : "Add"}</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Preferences;
|
export default Preferences;
|
||||||
|
|
|
@ -12,8 +12,8 @@ import {Autocomplete, Checkbox, FormControlLabel, useMediaQuery} from "@mui/mate
|
||||||
import theme from "./theme";
|
import theme from "./theme";
|
||||||
import api from "../app/Api";
|
import api from "../app/Api";
|
||||||
import {topicUrl, validTopic, validUrl} from "../app/utils";
|
import {topicUrl, validTopic, validUrl} from "../app/utils";
|
||||||
import User from "../app/User";
|
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
|
import db from "../app/db";
|
||||||
|
|
||||||
const defaultBaseUrl = "http://127.0.0.1"
|
const defaultBaseUrl = "http://127.0.0.1"
|
||||||
//const defaultBaseUrl = "https://ntfy.sh"
|
//const defaultBaseUrl = "https://ntfy.sh"
|
||||||
|
@ -23,10 +23,10 @@ const SubscribeDialog = (props) => {
|
||||||
const [topic, setTopic] = useState("");
|
const [topic, setTopic] = useState("");
|
||||||
const [showLoginPage, setShowLoginPage] = useState(false);
|
const [showLoginPage, setShowLoginPage] = useState(false);
|
||||||
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
const fullScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
||||||
const handleSuccess = (user) => {
|
const handleSuccess = () => {
|
||||||
const actualBaseUrl = (baseUrl) ? baseUrl : defaultBaseUrl; // FIXME
|
const actualBaseUrl = (baseUrl) ? baseUrl : defaultBaseUrl; // FIXME
|
||||||
const subscription = new Subscription(actualBaseUrl, topic);
|
const subscription = new Subscription(actualBaseUrl, topic);
|
||||||
props.onSuccess(subscription, user);
|
props.onSuccess(subscription);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Dialog open={props.open} onClose={props.onCancel} fullScreen={fullScreen}>
|
<Dialog open={props.open} onClose={props.onCancel} fullScreen={fullScreen}>
|
||||||
|
@ -65,7 +65,7 @@ const SubscribePage = (props) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(`[SubscribeDialog] Successful login to ${topicUrl(baseUrl, topic)} for anonymous user`);
|
console.log(`[SubscribeDialog] Successful login to ${topicUrl(baseUrl, topic)} for anonymous user`);
|
||||||
props.onSuccess(null);
|
props.onSuccess();
|
||||||
};
|
};
|
||||||
const handleUseAnotherChanged = (e) => {
|
const handleUseAnotherChanged = (e) => {
|
||||||
props.setBaseUrl("");
|
props.setBaseUrl("");
|
||||||
|
@ -129,7 +129,7 @@ const LoginPage = (props) => {
|
||||||
const baseUrl = (props.baseUrl) ? props.baseUrl : defaultBaseUrl;
|
const baseUrl = (props.baseUrl) ? props.baseUrl : defaultBaseUrl;
|
||||||
const topic = props.topic;
|
const topic = props.topic;
|
||||||
const handleLogin = async () => {
|
const handleLogin = async () => {
|
||||||
const user = new User(baseUrl, username, password);
|
const user = {baseUrl, username, password};
|
||||||
const success = await api.auth(baseUrl, topic, user);
|
const success = await api.auth(baseUrl, topic, user);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
console.log(`[SubscribeDialog] Login to ${topicUrl(baseUrl, topic)} failed for user ${username}`);
|
console.log(`[SubscribeDialog] Login to ${topicUrl(baseUrl, topic)} failed for user ${username}`);
|
||||||
|
@ -137,7 +137,8 @@ const LoginPage = (props) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(`[SubscribeDialog] Successful login to ${topicUrl(baseUrl, topic)} for user ${username}`);
|
console.log(`[SubscribeDialog] Successful login to ${topicUrl(baseUrl, topic)} for user ${username}`);
|
||||||
props.onSuccess(user);
|
db.users.put(user);
|
||||||
|
props.onSuccess();
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {styled} from "@mui/styles";
|
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import theme from "./theme";
|
import theme from "./theme";
|
||||||
import Container from "@mui/material/Container";
|
import Container from "@mui/material/Container";
|
||||||
|
import {styled} from "@mui/material";
|
||||||
|
|
||||||
export const Paragraph = styled(Typography)({
|
export const Paragraph = styled(Typography)({
|
||||||
paddingTop: 8,
|
paddingTop: 8,
|
||||||
|
|
Loading…
Reference in New Issue