Compare commits

..

3 Commits

19 changed files with 320 additions and 244 deletions

110
package-lock.json generated
View File

@ -10,6 +10,7 @@
"dependencies": { "dependencies": {
"@emotion/react": "^11.11.1", "@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@hookform/resolvers": "^3.3.4",
"@mui/icons-material": "^5.14.19", "@mui/icons-material": "^5.14.19",
"@mui/material": "^5.14.20", "@mui/material": "^5.14.20",
"@nostr-dev-kit/ndk": "^2.0.5", "@nostr-dev-kit/ndk": "^2.0.5",
@ -33,6 +34,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-copy-to-clipboard": "^5.1.0", "react-copy-to-clipboard": "^5.1.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-hook-form": "^7.50.0",
"react-redux": "^9.0.3", "react-redux": "^9.0.3",
"react-router-dom": "^6.20.1", "react-router-dom": "^6.20.1",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
@ -51,7 +53,8 @@
"workbox-range-requests": "^6.6.0", "workbox-range-requests": "^6.6.0",
"workbox-routing": "^6.6.0", "workbox-routing": "^6.6.0",
"workbox-strategies": "^6.6.0", "workbox-strategies": "^6.6.0",
"workbox-streams": "^6.6.0" "workbox-streams": "^6.6.0",
"yup": "^1.3.3"
}, },
"devDependencies": { "devDependencies": {
"@types/lodash.isequal": "^4.5.8", "@types/lodash.isequal": "^4.5.8",
@ -2738,6 +2741,14 @@
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz",
"integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==" "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A=="
}, },
"node_modules/@hookform/resolvers": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.3.4.tgz",
"integrity": "sha512-o5cgpGOuJYrd+iMKvkttOclgwRW86EsWJZZRC23prf0uU2i48Htq4PuT73AVb9ionFyZrwYEITuOFGF+BydEtQ==",
"peerDependencies": {
"react-hook-form": "^7.0.0"
}
},
"node_modules/@humanwhocodes/config-array": { "node_modules/@humanwhocodes/config-array": {
"version": "0.11.13", "version": "0.11.13",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
@ -14286,6 +14297,11 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
}, },
"node_modules/property-expr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz",
"integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA=="
},
"node_modules/proxy-addr": { "node_modules/proxy-addr": {
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@ -14604,6 +14620,21 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
}, },
"node_modules/react-hook-form": {
"version": "7.50.0",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.50.0.tgz",
"integrity": "sha512-AOhuzM3RdP09ZCnq+Z0yvKGHK25yiOX5phwxjV9L7U6HMla10ezkBnvQ+Pk4GTuDfsC5P2zza3k8mawFwFLVuQ==",
"engines": {
"node": ">=12.22.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/react-hook-form"
},
"peerDependencies": {
"react": "^16.8.0 || ^17 || ^18"
}
},
"node_modules/react-is": { "node_modules/react-is": {
"version": "17.0.2", "version": "17.0.2",
"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",
@ -16678,6 +16709,11 @@
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="
}, },
"node_modules/tiny-case": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz",
"integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q=="
},
"node_modules/tmpl": { "node_modules/tmpl": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@ -16715,6 +16751,11 @@
"node": ">=0.6" "node": ">=0.6"
} }
}, },
"node_modules/toposort": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="
},
"node_modules/tough-cookie": { "node_modules/tough-cookie": {
"version": "4.1.3", "version": "4.1.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
@ -18316,6 +18357,28 @@
"funding": { "funding": {
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
},
"node_modules/yup": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/yup/-/yup-1.3.3.tgz",
"integrity": "sha512-v8QwZSsHH2K3/G9WSkp6mZKO+hugKT1EmnMqLNUcfu51HU9MDyhlETT/JgtzprnrnQHPWsjc6MUDMBp/l9fNnw==",
"dependencies": {
"property-expr": "^2.0.5",
"tiny-case": "^1.0.3",
"toposort": "^2.0.2",
"type-fest": "^2.19.0"
}
},
"node_modules/yup/node_modules/type-fest": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
"integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
"engines": {
"node": ">=12.20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
} }
}, },
"dependencies": { "dependencies": {
@ -20090,6 +20153,12 @@
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz",
"integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==" "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A=="
}, },
"@hookform/resolvers": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.3.4.tgz",
"integrity": "sha512-o5cgpGOuJYrd+iMKvkttOclgwRW86EsWJZZRC23prf0uU2i48Htq4PuT73AVb9ionFyZrwYEITuOFGF+BydEtQ==",
"requires": {}
},
"@humanwhocodes/config-array": { "@humanwhocodes/config-array": {
"version": "0.11.13", "version": "0.11.13",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
@ -28331,6 +28400,11 @@
} }
} }
}, },
"property-expr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz",
"integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA=="
},
"proxy-addr": { "proxy-addr": {
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@ -28585,6 +28659,12 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
"integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
}, },
"react-hook-form": {
"version": "7.50.0",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.50.0.tgz",
"integrity": "sha512-AOhuzM3RdP09ZCnq+Z0yvKGHK25yiOX5phwxjV9L7U6HMla10ezkBnvQ+Pk4GTuDfsC5P2zza3k8mawFwFLVuQ==",
"requires": {}
},
"react-is": { "react-is": {
"version": "17.0.2", "version": "17.0.2",
"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",
@ -30123,6 +30203,11 @@
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="
}, },
"tiny-case": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz",
"integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q=="
},
"tmpl": { "tmpl": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@ -30151,6 +30236,11 @@
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
}, },
"toposort": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="
},
"tough-cookie": { "tough-cookie": {
"version": "4.1.3", "version": "4.1.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
@ -31375,6 +31465,24 @@
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
},
"yup": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/yup/-/yup-1.3.3.tgz",
"integrity": "sha512-v8QwZSsHH2K3/G9WSkp6mZKO+hugKT1EmnMqLNUcfu51HU9MDyhlETT/JgtzprnrnQHPWsjc6MUDMBp/l9fNnw==",
"requires": {
"property-expr": "^2.0.5",
"tiny-case": "^1.0.3",
"toposort": "^2.0.2",
"type-fest": "^2.19.0"
},
"dependencies": {
"type-fest": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
"integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="
}
}
} }
} }
} }

View File

@ -5,6 +5,7 @@
"dependencies": { "dependencies": {
"@emotion/react": "^11.11.1", "@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@hookform/resolvers": "^3.3.4",
"@mui/icons-material": "^5.14.19", "@mui/icons-material": "^5.14.19",
"@mui/material": "^5.14.20", "@mui/material": "^5.14.20",
"@nostr-dev-kit/ndk": "^2.0.5", "@nostr-dev-kit/ndk": "^2.0.5",
@ -28,6 +29,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-copy-to-clipboard": "^5.1.0", "react-copy-to-clipboard": "^5.1.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-hook-form": "^7.50.0",
"react-redux": "^9.0.3", "react-redux": "^9.0.3",
"react-router-dom": "^6.20.1", "react-router-dom": "^6.20.1",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
@ -46,7 +48,8 @@
"workbox-range-requests": "^6.6.0", "workbox-range-requests": "^6.6.0",
"workbox-routing": "^6.6.0", "workbox-routing": "^6.6.0",
"workbox-strategies": "^6.6.0", "workbox-strategies": "^6.6.0",
"workbox-streams": "^6.6.0" "workbox-streams": "^6.6.0",
"yup": "^1.3.3"
}, },
"overrides": { "overrides": {
"react-scripts": { "react-scripts": {

View File

@ -14,7 +14,7 @@ import { ACTION_TYPE } from '@/utils/consts'
export const ModalConfirmConnect = () => { export const ModalConfirmConnect = () => {
const { getModalOpened, createHandleCloseReplace } = useModalSearchParams() const { getModalOpened, handleClose } = useModalSearchParams()
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.CONFIRM_CONNECT) const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.CONFIRM_CONNECT)
const { npub = '' } = useParams<{ npub: string }>() const { npub = '' } = useParams<{ npub: string }>()
@ -37,24 +37,20 @@ export const ModalConfirmConnect = () => {
return setSelectedActionType(value) return setSelectedActionType(value)
} }
const handleCloseModal = createHandleCloseReplace( const handleCloseModal = handleClose(
MODAL_PARAMS_KEYS.CONFIRM_CONNECT, MODAL_PARAMS_KEYS.CONFIRM_CONNECT,
{ async (sp) => {
onClose: async (sp) => { sp.delete('appNpub')
sp.delete('appNpub') sp.delete('reqId')
sp.delete('reqId') await swicCall('confirm', pendingReqId, false, false)
await swicCall('confirm', pendingReqId, false, false)
}
}, },
) )
const closeModalAfterRequest = createHandleCloseReplace( const closeModalAfterRequest = handleClose(
MODAL_PARAMS_KEYS.CONFIRM_CONNECT, MODAL_PARAMS_KEYS.CONFIRM_CONNECT,
{ (sp) => {
onClose: (sp) => { sp.delete('appNpub')
sp.delete('appNpub') sp.delete('reqId')
sp.delete('reqId') },
},
}
) )
async function confirmPending( async function confirmPending(

View File

@ -50,7 +50,7 @@ type PendingRequest = DbPending & { checked: boolean }
export const ModalConfirmEvent: FC<ModalConfirmEventProps> = ({ export const ModalConfirmEvent: FC<ModalConfirmEventProps> = ({
confirmEventReqs, confirmEventReqs,
}) => { }) => {
const { getModalOpened, createHandleCloseReplace } = useModalSearchParams() const { getModalOpened, handleClose } = useModalSearchParams()
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.CONFIRM_EVENT) const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.CONFIRM_EVENT)
const [searchParams] = useSearchParams() const [searchParams] = useSearchParams()
@ -86,27 +86,23 @@ export const ModalConfirmEvent: FC<ModalConfirmEventProps> = ({
const selectedPendingRequests = pendingRequests.filter((pr) => pr.checked) const selectedPendingRequests = pendingRequests.filter((pr) => pr.checked)
const handleCloseModal = createHandleCloseReplace( const handleCloseModal = handleClose(
MODAL_PARAMS_KEYS.CONFIRM_EVENT, MODAL_PARAMS_KEYS.CONFIRM_EVENT,
{ (sp) => {
onClose: (sp) => { sp.delete('appNpub')
sp.delete('appNpub') sp.delete('reqId')
sp.delete('reqId') selectedPendingRequests.forEach(
selectedPendingRequests.forEach( async (req) => await swicCall('confirm', req.id, false, false),
async (req) => await swicCall('confirm', req.id, false, false), )
) },
}
}
) )
const closeModalAfterRequest = createHandleCloseReplace( const closeModalAfterRequest = handleClose(
MODAL_PARAMS_KEYS.CONFIRM_EVENT, MODAL_PARAMS_KEYS.CONFIRM_EVENT,
{ (sp) => {
onClose: (sp) => { sp.delete('appNpub')
sp.delete('appNpub') sp.delete('reqId')
sp.delete('reqId') },
}
}
) )
async function confirmPending(allow: boolean) { async function confirmPending(allow: boolean) {

View File

@ -12,18 +12,13 @@ import { useRef } from 'react'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
export const ModalConnectApp = () => { export const ModalConnectApp = () => {
const { getModalOpened, createHandleCloseReplace, handleOpen } = useModalSearchParams() const { getModalOpened, handleClose, handleOpen } = useModalSearchParams()
const timerRef = useRef<NodeJS.Timeout>() const timerRef = useRef<NodeJS.Timeout>()
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.CONNECT_APP) const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.CONNECT_APP)
const handleCloseModal = createHandleCloseReplace( const handleCloseModal = handleClose(MODAL_PARAMS_KEYS.CONNECT_APP, () => {
MODAL_PARAMS_KEYS.CONNECT_APP, clearTimeout(timerRef.current)
{ })
onClose: () => {
clearTimeout(timerRef.current)
}
}
)
const notify = useEnqueueSnackbar() const notify = useEnqueueSnackbar()

View File

@ -11,9 +11,9 @@ import { StyledAppLogo } from './styled'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
export const ModalImportKeys = () => { export const ModalImportKeys = () => {
const { getModalOpened, createHandleCloseReplace } = useModalSearchParams() const { getModalOpened, handleClose } = useModalSearchParams()
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.IMPORT_KEYS) const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.IMPORT_KEYS)
const handleCloseModal = createHandleCloseReplace(MODAL_PARAMS_KEYS.IMPORT_KEYS) const handleCloseModal = handleClose(MODAL_PARAMS_KEYS.IMPORT_KEYS)
const notify = useEnqueueSnackbar() const notify = useEnqueueSnackbar()
const navigate = useNavigate() const navigate = useNavigate()

View File

@ -7,10 +7,10 @@ import { Fade, Stack } from '@mui/material'
import { AppLink } from '@/shared/AppLink/AppLink' import { AppLink } from '@/shared/AppLink/AppLink'
export const ModalInitial = () => { export const ModalInitial = () => {
const { getModalOpened, createHandleCloseReplace, handleOpen } = useModalSearchParams() const { getModalOpened, handleClose, handleOpen } = useModalSearchParams()
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.INITIAL) const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.INITIAL)
const handleCloseModal = createHandleCloseReplace(MODAL_PARAMS_KEYS.INITIAL) const handleCloseModal = handleClose(MODAL_PARAMS_KEYS.INITIAL)
const [showAdvancedContent, setShowAdvancedContent] = useState(false) const [showAdvancedContent, setShowAdvancedContent] = useState(false)

View File

@ -1,10 +1,10 @@
import React, { useCallback, useEffect, useState } from 'react'
import { useEnqueueSnackbar } from '@/hooks/useEnqueueSnackbar' import { useEnqueueSnackbar } from '@/hooks/useEnqueueSnackbar'
import { useModalSearchParams } from '@/hooks/useModalSearchParams' import { useModalSearchParams } from '@/hooks/useModalSearchParams'
import { swicCall } from '@/modules/swic' import { swicCall } from '@/modules/swic'
import { Modal } from '@/shared/Modal/Modal' import { Modal } from '@/shared/Modal/Modal'
import { MODAL_PARAMS_KEYS } from '@/types/modal' import { MODAL_PARAMS_KEYS } from '@/types/modal'
import { IconButton, Stack, Typography } from '@mui/material' import { IconButton, Stack, Typography } from '@mui/material'
import React, { ChangeEvent, useState } from 'react'
import { StyledAppLogo } from './styled' import { StyledAppLogo } from './styled'
import { nip19 } from 'nostr-tools' import { nip19 } from 'nostr-tools'
import { Input } from '@/shared/Input/Input' import { Input } from '@/shared/Input/Input'
@ -12,39 +12,46 @@ import { Button } from '@/shared/Button/Button'
import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined' import VisibilityOffOutlinedIcon from '@mui/icons-material/VisibilityOffOutlined'
import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined' import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { useForm } from 'react-hook-form'
import { FormInputType, schema } from './const'
import { yupResolver } from '@hookform/resolvers/yup'
export const ModalLogin = () => { export const ModalLogin = () => {
const { getModalOpened, createHandleCloseReplace } = useModalSearchParams() const { getModalOpened, handleClose } = useModalSearchParams()
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.LOGIN) const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.LOGIN)
const handleCloseModal = createHandleCloseReplace(MODAL_PARAMS_KEYS.LOGIN) const handleCloseModal = handleClose(MODAL_PARAMS_KEYS.LOGIN)
const notify = useEnqueueSnackbar() const notify = useEnqueueSnackbar()
const navigate = useNavigate() const navigate = useNavigate()
const [enteredUsername, setEnteredUsername] = useState('') const {
const [enteredPassword, setEnteredPassword] = useState('') handleSubmit,
reset,
register,
formState: { errors },
} = useForm<FormInputType>({
defaultValues: {
username: '',
password: '',
},
resolver: yupResolver(schema),
mode: 'onSubmit',
})
const [isPasswordShown, setIsPasswordShown] = useState(false) const [isPasswordShown, setIsPasswordShown] = useState(false)
const handleUsernameChange = (e: ChangeEvent<HTMLInputElement>) => {
setEnteredUsername(e.target.value)
}
const handlePasswordChange = (e: ChangeEvent<HTMLInputElement>) => {
setEnteredPassword(e.target.value)
}
const handlePasswordTypeChange = () => const handlePasswordTypeChange = () =>
setIsPasswordShown((prevState) => !prevState) setIsPasswordShown((prevState) => !prevState)
const isFormValid = const cleanUpStates = useCallback(() => {
enteredUsername.trim().length > 0 && enteredPassword.trim().length > 0 setIsPasswordShown(false)
reset()
}, [reset])
const handleSubmit = async (e: React.FormEvent) => { const submitHandler = async (values: FormInputType) => {
e.preventDefault()
if (!isFormValid) return undefined
try { try {
const [username, domain] = enteredUsername.split('@') const [username, domain] = values.username.split('@')
const response = await fetch( const response = await fetch(
`https://${domain}/.well-known/nostr.json?name=${username}`, `https://${domain}/.well-known/nostr.json?name=${username}`,
) )
@ -56,18 +63,34 @@ export const ModalLogin = () => {
const pubkey = getNpub.names[username] const pubkey = getNpub.names[username]
const npub = nip19.npubEncode(pubkey) const npub = nip19.npubEncode(pubkey)
const passphrase = enteredPassword const passphrase = values.password
console.log('fetch', npub, passphrase) console.log('fetch', npub, passphrase)
const k: any = await swicCall('fetchKey', npub, passphrase) const k: any = await swicCall('fetchKey', npub, passphrase)
notify(`Fetched ${k.npub}`, 'success') notify(`Fetched ${k.npub}`, 'success')
cleanUpStates()
navigate(`/key/${k.npub}`) navigate(`/key/${k.npub}`)
} catch (error: any) { } catch (error: any) {
notify(error.message, 'error') notify(error?.message || 'Something went wrong!', 'error')
} }
} }
useEffect(() => {
return () => {
if (isModalOpened) {
// modal closed
cleanUpStates()
}
}
}, [isModalOpened, cleanUpStates])
return ( return (
<Modal open={isModalOpened} onClose={handleCloseModal}> <Modal open={isModalOpened} onClose={handleCloseModal}>
<Stack gap={'1rem'} component={'form'} onSubmit={handleSubmit}> <Stack
gap={'1rem'}
component={'form'}
onSubmit={handleSubmit(submitHandler)}
>
<Stack <Stack
direction={'row'} direction={'row'}
gap={'1rem'} gap={'1rem'}
@ -83,15 +106,14 @@ export const ModalLogin = () => {
label='Enter a Username' label='Enter a Username'
fullWidth fullWidth
placeholder='user@nsec.app' placeholder='user@nsec.app'
onChange={handleUsernameChange} {...register('username')}
value={enteredUsername} error={!!errors.username}
/> />
<Input <Input
label='Password' label='Password'
fullWidth fullWidth
placeholder='Your password' placeholder='Your password'
onChange={handlePasswordChange} {...register('password')}
value={enteredPassword}
endAdornment={ endAdornment={
<IconButton <IconButton
size='small' size='small'
@ -105,8 +127,9 @@ export const ModalLogin = () => {
</IconButton> </IconButton>
} }
type={isPasswordShown ? 'text' : 'password'} type={isPasswordShown ? 'text' : 'password'}
error={!!errors.password}
/> />
<Button type='submit' fullWidth disabled={!isFormValid}> <Button type='submit' fullWidth>
Login Login
</Button> </Button>
</Stack> </Stack>

View File

@ -0,0 +1,18 @@
import * as yup from 'yup'
export const schema = yup.object().shape({
username: yup
.string()
.test('Domain validation', 'The domain is required!', function (value) {
if (!value || !value.trim().length) return false
const USERNAME_WITH_DOMAIN_REGEXP = new RegExp(
/^[\w-.]+@([\w-]+\.)+[\w-]{2,8}$/g,
)
return USERNAME_WITH_DOMAIN_REGEXP.test(value)
})
.required(),
password: yup.string().required().min(4),
})
export type FormInputType = yup.InferType<typeof schema>

View File

@ -31,13 +31,13 @@ type ModalSettingsProps = {
} }
export const ModalSettings: FC<ModalSettingsProps> = ({ isSynced }) => { export const ModalSettings: FC<ModalSettingsProps> = ({ isSynced }) => {
const { getModalOpened, createHandleCloseReplace } = useModalSearchParams() const { getModalOpened, handleClose } = useModalSearchParams()
const { npub = '' } = useParams<{ npub: string }>() const { npub = '' } = useParams<{ npub: string }>()
const notify = useEnqueueSnackbar() const notify = useEnqueueSnackbar()
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.SETTINGS) const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.SETTINGS)
const handleCloseModal = createHandleCloseReplace(MODAL_PARAMS_KEYS.SETTINGS) const handleCloseModal = handleClose(MODAL_PARAMS_KEYS.SETTINGS)
const [enteredPassword, setEnteredPassword] = useState('') const [enteredPassword, setEnteredPassword] = useState('')
const [isPasswordShown, setIsPasswordShown] = useState(false) const [isPasswordShown, setIsPasswordShown] = useState(false)
@ -48,7 +48,7 @@ export const ModalSettings: FC<ModalSettingsProps> = ({ isSynced }) => {
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
useEffect(() => setIsChecked(isSynced), [isModalOpened, isSynced]) useEffect(() => setIsChecked(isSynced), [isModalOpened])
const handlePasswordChange = (e: ChangeEvent<HTMLInputElement>) => { const handlePasswordChange = (e: ChangeEvent<HTMLInputElement>) => {
setIsPasswordInvalid(false) setIsPasswordInvalid(false)

View File

@ -12,9 +12,9 @@ import { swicCall } from '@/modules/swic'
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
export const ModalSignUp = () => { export const ModalSignUp = () => {
const { getModalOpened, createHandleCloseReplace } = useModalSearchParams() const { getModalOpened, handleClose } = useModalSearchParams()
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.SIGN_UP) const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.SIGN_UP)
const handleCloseModal = createHandleCloseReplace(MODAL_PARAMS_KEYS.SIGN_UP) const handleCloseModal = handleClose(MODAL_PARAMS_KEYS.SIGN_UP)
const notify = useEnqueueSnackbar() const notify = useEnqueueSnackbar()
const theme = useTheme() const theme = useTheme()
@ -37,6 +37,7 @@ export const ModalSignUp = () => {
) )
const handleSubmit = async (e: React.FormEvent) => { const handleSubmit = async (e: React.FormEvent) => {
if (!enteredValue.trim().length) return
e.preventDefault() e.preventDefault()
try { try {
const k: any = await swicCall('generateKey') const k: any = await swicCall('generateKey')

View File

@ -17,11 +17,6 @@ export type IExtraOptions = {
append?: boolean append?: boolean
} }
export type IExtraCloseOptions = {
replace?: boolean
onClose?: (s: URLSearchParams) => void
}
export const useModalSearchParams = () => { export const useModalSearchParams = () => {
const [searchParams, setSearchParams] = useSearchParams() const [searchParams, setSearchParams] = useSearchParams()
@ -34,20 +29,13 @@ export const useModalSearchParams = () => {
] ]
}, []) }, [])
const createHandleClose = const handleClose =
(modal: MODAL_PARAMS_KEYS, extraOptions?: IExtraCloseOptions) => (modal: MODAL_PARAMS_KEYS, onClose?: (s: URLSearchParams) => void) =>
() => { () => {
const enumKey = getEnumParam(modal) const enumKey = getEnumParam(modal)
searchParams.delete(enumKey) searchParams.delete(enumKey)
extraOptions?.onClose && extraOptions?.onClose(searchParams) onClose && onClose(searchParams)
console.log({ searchParams }) setSearchParams(searchParams)
setSearchParams(searchParams, { replace: !!extraOptions?.replace })
}
const createHandleCloseReplace =
(modal: MODAL_PARAMS_KEYS, extraOptions?: IExtraCloseOptions) =>
() => {
return createHandleClose(modal, { ...extraOptions, replace: true })
} }
const handleOpen = useCallback( const handleOpen = useCallback(
@ -73,7 +61,7 @@ export const useModalSearchParams = () => {
pathname: location.pathname, pathname: location.pathname,
search: searchString, search: searchString,
}, },
{ replace: !!extraOptions?.replace }, { replace: extraOptions?.replace || true },
) )
}, },
[location, navigate, getEnumParam], [location, navigate, getEnumParam],
@ -90,8 +78,7 @@ export const useModalSearchParams = () => {
return { return {
getModalOpened, getModalOpened,
createHandleClose, handleClose,
createHandleCloseReplace,
handleOpen, handleOpen,
} }
} }

View File

@ -1,5 +1,5 @@
import { generatePrivateKey, getPublicKey, nip19 } from 'nostr-tools' import { generatePrivateKey, getPublicKey, nip19 } from 'nostr-tools'
import { DbApp, dbi, DbKey, DbPending, DbPerm } from './db' import { dbi, DbKey, DbPending, DbPerm } from './db'
import { Keys } from './keys' import { Keys } from './keys'
import NDK, { import NDK, {
IEventHandlingStrategy, IEventHandlingStrategy,
@ -10,7 +10,7 @@ import NDK, {
} from '@nostr-dev-kit/ndk' } from '@nostr-dev-kit/ndk'
import { NOAUTHD_URL, WEB_PUSH_PUBKEY, NIP46_RELAYS } from '../utils/consts' import { NOAUTHD_URL, WEB_PUSH_PUBKEY, NIP46_RELAYS } from '../utils/consts'
import { Nip04 } from './nip04' import { Nip04 } from './nip04'
import { getReqPerm, getShortenNpub, isPackagePerm } from '@/utils/helpers/helpers' import { getReqPerm, isPackagePerm } from '@/utils/helpers/helpers'
//import { PrivateKeySigner } from './signer' //import { PrivateKeySigner } from './signer'
//const PERF_TEST = false //const PERF_TEST = false
@ -32,7 +32,6 @@ interface Key {
interface Pending { interface Pending {
req: DbPending req: DbPending
cb: (allow: boolean, remember: boolean, options?: any) => void cb: (allow: boolean, remember: boolean, options?: any) => void
notified?: boolean
} }
interface IAllowCallbackParams { interface IAllowCallbackParams {
@ -146,7 +145,6 @@ export class NoauthBackend {
private enckeys: DbKey[] = [] private enckeys: DbKey[] = []
private keys: Key[] = [] private keys: Key[] = []
private perms: DbPerm[] = [] private perms: DbPerm[] = []
private apps: DbApp[] = []
private doneReqIds: string[] = [] private doneReqIds: string[] = []
private confirmBuffer: Pending[] = [] private confirmBuffer: Pending[] = []
private accessBuffer: DbPending[] = [] private accessBuffer: DbPending[] = []
@ -195,25 +193,16 @@ export class NoauthBackend {
.matchAll({ type: 'window' }) .matchAll({ type: 'window' })
.then((clientList) => { .then((clientList) => {
console.log('clients', clientList.length) console.log('clients', clientList.length)
// FIXME find a client that has our
// key page
for (const client of clientList) { for (const client of clientList) {
console.log('client', client.url) console.log('client', client.url)
if ( if (
new URL(client.url).pathname === '/' && new URL(client.url).pathname === '/' &&
'focus' in client 'focus' in client
) { )
client.focus() return client.focus()
return
}
} }
// if (self.swg.clients.openWindow)
// confirm screen url // return self.swg.clients.openWindow("/");
const req = event.notification.data.req
console.log("req", req)
// const url = `${self.swg.location.origin}/key/${req.npub}?confirm-connect=true&appNpub=${req.appNpub}&reqId=${req.id}`
const url = `${self.swg.location.origin}/key/${req.npub}`
self.swg.clients.openWindow(url)
}), }),
) )
} }
@ -227,8 +216,6 @@ export class NoauthBackend {
console.log('started encKeys', this.listKeys()) console.log('started encKeys', this.listKeys())
this.perms = await dbi.listPerms() this.perms = await dbi.listPerms()
console.log('started perms', this.perms) console.log('started perms', this.perms)
this.apps = await dbi.listApps()
console.log('started apps', this.apps)
const sub = await this.swg.registration.pushManager.getSubscription() const sub = await this.swg.registration.pushManager.getSubscription()
@ -286,7 +273,7 @@ export class NoauthBackend {
}) })
if (r.status !== 200 && r.status !== 201) { if (r.status !== 200 && r.status !== 201) {
console.log('Fetch error', url, method, r.status) console.log('Fetch error', url, method, r.status)
throw new Error('Failed to fetch' + url) throw new Error('Failed to fetch ' + url)
} }
return await r.json() return await r.json()
@ -394,69 +381,21 @@ export class NoauthBackend {
// and update the notifications // and update the notifications
for (const r of this.confirmBuffer) { for (const r of this.confirmBuffer) {
const text = `Confirm "${r.req.method}" by "${r.req.appNpub}"`
if (r.notified) continue this.swg.registration.showNotification('Signer access', {
body: text,
const key = this.keys.find(k => k.npub === r.req.npub) tag: 'confirm-' + r.req.appNpub,
if (!key) continue actions: [
{
const app = this.apps.find(a => a.appNpub === r.req.appNpub) action: 'allow:' + r.req.id,
if (r.req.method !== 'connect' && !app) continue title: 'Yes',
},
// FIXME use Nsec.app icon! {
const icon = 'https://nostr.band/android-chrome-192x192.png' action: 'disallow:' + r.req.id,
title: 'No',
const appName = app?.name || getShortenNpub(r.req.appNpub) },
// FIXME load profile? ],
const keyName = getShortenNpub(r.req.npub) })
const tag = 'confirm-' + r.req.appNpub
const allowAction = 'allow:' + r.req.id
const disallowAction = 'disallow:' + r.req.id
const data = { req: r.req }
if (r.req.method === 'connect') {
const title = `Connect with new app`
const body = `Allow app "${appName}" to connect to key "${keyName}"`
this.swg.registration.showNotification(title, {
body,
tag,
icon,
data,
actions: [
{
action: allowAction,
title: 'Connect',
},
{
action: disallowAction,
title: 'Ignore',
},
],
})
} else {
const title = `Permission request`
const body = `Allow "${r.req.method}" by "${appName}" to "${keyName}"`
this.swg.registration.showNotification(title, {
body,
tag,
icon,
data,
actions: [
{
action: allowAction,
title: 'Yes',
},
{
action: disallowAction,
title: 'No',
},
],
})
}
// mark
r.notified = true
} }
if (this.notifCallback) this.notifCallback() if (this.notifCallback) this.notifCallback()
@ -570,9 +509,6 @@ export class NoauthBackend {
icon: '', icon: '',
url: '', url: '',
}) })
// reload
self.apps = await dbi.listApps()
} }
} }
} else { } else {
@ -835,7 +771,6 @@ export class NoauthBackend {
} }
private async deleteApp(appNpub: string) { private async deleteApp(appNpub: string) {
this.apps = this.apps.filter((a) => a.appNpub !== appNpub)
this.perms = this.perms.filter((p) => p.appNpub !== appNpub) this.perms = this.perms.filter((p) => p.appNpub !== appNpub)
await dbi.removeApp(appNpub) await dbi.removeApp(appNpub)
await dbi.removeAppPerms(appNpub) await dbi.removeAppPerms(appNpub)

View File

@ -55,7 +55,7 @@ const AppPage = () => {
try { try {
await swicCall('deleteApp', appNpub) await swicCall('deleteApp', appNpub)
notify(`App: «${appName}» successfully deleted!`, 'success') notify(`App: «${appName}» successfully deleted!`, 'success')
navigate(`key/${npub}`) navigate(`/key/${npub}`)
} catch (error: any) { } catch (error: any) {
notify(error?.message || 'Failed to delete app', 'error') notify(error?.message || 'Failed to delete app', 'error')
} }
@ -99,7 +99,9 @@ const AppPage = () => {
<Button <Button
fullWidth fullWidth
onClick={() => onClick={() =>
handleOpenModal(MODAL_PARAMS_KEYS.ACTIVITY) handleOpenModal(MODAL_PARAMS_KEYS.ACTIVITY, {
replace: true,
})
} }
> >
Activity Activity

View File

@ -12,9 +12,9 @@ type ModalActivitiesProps = {
} }
export const ModalActivities: FC<ModalActivitiesProps> = ({ appNpub }) => { export const ModalActivities: FC<ModalActivitiesProps> = ({ appNpub }) => {
const { getModalOpened, createHandleCloseReplace } = useModalSearchParams() const { getModalOpened, handleClose } = useModalSearchParams()
const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.ACTIVITY) const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.ACTIVITY)
const handleCloseModal = createHandleCloseReplace(MODAL_PARAMS_KEYS.ACTIVITY) const handleCloseModal = handleClose(MODAL_PARAMS_KEYS.ACTIVITY)
const history = useLiveQuery( const history = useLiveQuery(
getActivityHistoryQuerier(appNpub), getActivityHistoryQuerier(appNpub),
@ -27,7 +27,7 @@ export const ModalActivities: FC<ModalActivitiesProps> = ({ appNpub }) => {
open={isModalOpened} open={isModalOpened}
onClose={handleCloseModal} onClose={handleCloseModal}
fixedHeight='calc(100% - 5rem)' fixedHeight='calc(100% - 5rem)'
title='Activity history' title='Activities history'
> >
<Box overflow={'auto'}> <Box overflow={'auto'}>
{history.map((item) => { {history.map((item) => {

View File

@ -6,11 +6,8 @@ export const getActivityHistoryQuerier = (appNpub: string) => () => {
const result = db.history const result = db.history
.where('appNpub') .where('appNpub')
.equals(appNpub) .equals(appNpub)
.reverse() .limit(30)
.sortBy('timestamp') .toArray()
.then(a => a.slice(0, 30))
// .limit(30)
// .toArray()
return result return result
} }

View File

@ -1,18 +1,22 @@
import { Input, InputProps } from '@/shared/Input/Input' import { Input, InputProps } from '@/shared/Input/Input'
import { Stack, StackProps, styled } from '@mui/material' import { Stack, StackProps, styled } from '@mui/material'
import { forwardRef } from 'react'
export const StyledInput = styled(({ className, ...props }: InputProps) => { export const StyledInput = styled(
return ( forwardRef<HTMLInputElement, InputProps>(({ className, ...props }, ref) => {
<Input return (
{...props} <Input
className='input' {...props}
containerProps={{ ref={ref}
className, className='input'
}} containerProps={{
fullWidth className,
/> }}
) fullWidth
})(({ theme }) => ({ />
)
}),
)(({ theme }) => ({
'& > .input': { '& > .input': {
border: 'none', border: 'none',
background: theme.palette.secondary.main, background: theme.palette.secondary.main,

View File

@ -1,5 +1,6 @@
import { Input, InputProps } from '@/shared/Input/Input' import { Input, InputProps } from '@/shared/Input/Input'
import { Box, Button, ButtonProps, styled, Badge } from '@mui/material' import { Box, Button, ButtonProps, styled, Badge } from '@mui/material'
import { forwardRef } from 'react'
type StyledIconButtonProps = ButtonProps & { type StyledIconButtonProps = ButtonProps & {
bgcolor_variant?: 'primary' | 'secondary' bgcolor_variant?: 'primary' | 'secondary'
@ -57,18 +58,21 @@ export const StyledEmptyAppsBox = styled(Box)(({ theme }) => {
} }
}) })
export const StyledInput = styled(({ className, ...props }: InputProps) => { export const StyledInput = styled(
return ( forwardRef<HTMLInputElement, InputProps>(({ className, ...props }, ref) => {
<Input return (
{...props} <Input
className='input' {...props}
containerProps={{ ref={ref}
className, className='input'
}} containerProps={{
fullWidth className,
/> }}
) fullWidth
})(({ theme }) => ({ />
)
}),
)(({ theme }) => ({
'& > .input': { '& > .input': {
border: 'none', border: 'none',
background: theme.palette.secondary.main, background: theme.palette.secondary.main,

View File

@ -1,4 +1,4 @@
import { FC, ReactNode } from 'react' import { ReactNode, forwardRef } from 'react'
import { import {
Box, Box,
BoxProps, BoxProps,
@ -17,29 +17,33 @@ export type InputProps = InputBaseProps & {
label?: string label?: string
} }
export const Input: FC<InputProps> = ({ export const Input = forwardRef<HTMLInputElement, InputProps>(
helperText, ({ helperText, containerProps, helperTextProps, label, ...props }, ref) => {
containerProps, return (
helperTextProps, <StyledInputContainer {...containerProps}>
label, {label ? (
...props <FormLabel className='label' htmlFor={props.id}>
}) => { {label}
return ( </FormLabel>
<StyledInputContainer {...containerProps}> ) : null}
{label ? ( <InputBase
<FormLabel className='label' htmlFor={props.id}> className='input'
{label} {...props}
</FormLabel> classes={{ error: 'error' }}
) : null} inputRef={ref}
<InputBase className='input' {...props} /> />
{helperText ? ( {helperText ? (
<FormHelperText {...helperTextProps} className='helper_text'> <FormHelperText
{helperText} {...helperTextProps}
</FormHelperText> className='helper_text'
) : null} >
</StyledInputContainer> {helperText}
) </FormHelperText>
} ) : null}
</StyledInputContainer>
)
},
)
const StyledInputContainer = styled((props: BoxProps) => <Box {...props} />)( const StyledInputContainer = styled((props: BoxProps) => <Box {...props} />)(
({ theme }) => { ({ theme }) => {
@ -56,6 +60,9 @@ const StyledInputContainer = styled((props: BoxProps) => <Box {...props} />)(
'& input::placeholder': { '& input::placeholder': {
color: '#fff', color: '#fff',
}, },
'&.error': {
border: '0.3px solid ' + theme.palette.error.main,
},
}, },
'& > .helper_text': { '& > .helper_text': {
margin: '0.5rem 1rem 0', margin: '0.5rem 1rem 0',