From eff6792d647c98a13d0124b4db03b6bd019fa88b Mon Sep 17 00:00:00 2001 From: Bekbolsun Date: Mon, 22 Jan 2024 21:27:23 +0600 Subject: [PATCH] reject penging requests on close modals & confirm only selected pending reqs in ConfirmEvent --- src/assets/icons/checked-light.svg | 4 + src/assets/icons/checked.svg | 4 +- src/assets/icons/unchecked-light.svg | 3 + src/assets/icons/unchecked.svg | 2 +- src/assets/index.ts | 4 + .../ModalConfirmConnect.tsx | 39 ++++---- .../ModalConfirmEvent/ModalConfirmEvent.tsx | 99 ++++++++++++------- .../Modal/ModalConnectApp/ModalConnectApp.tsx | 20 +++- .../Modal/ModalSettings/ModalSettings.tsx | 10 +- src/components/Modal/ModalSettings/styled.tsx | 6 +- src/components/Warning/styled.tsx | 1 + src/pages/KeyPage/Key.Page.tsx | 52 +++++----- src/pages/Welcome.Page.tsx | 2 +- src/routes/AppRoutes.tsx | 2 - src/shared/Checkbox/Checkbox.tsx | 37 +++++++ .../InputCopyButton/InputCopyButton.tsx | 11 ++- 16 files changed, 196 insertions(+), 100 deletions(-) create mode 100644 src/assets/icons/checked-light.svg create mode 100644 src/assets/icons/unchecked-light.svg create mode 100644 src/shared/Checkbox/Checkbox.tsx diff --git a/src/assets/icons/checked-light.svg b/src/assets/icons/checked-light.svg new file mode 100644 index 0000000..9f2b11c --- /dev/null +++ b/src/assets/icons/checked-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/checked.svg b/src/assets/icons/checked.svg index 9f2b11c..7722228 100644 --- a/src/assets/icons/checked.svg +++ b/src/assets/icons/checked.svg @@ -1,4 +1,4 @@ - - + + diff --git a/src/assets/icons/unchecked-light.svg b/src/assets/icons/unchecked-light.svg new file mode 100644 index 0000000..a080423 --- /dev/null +++ b/src/assets/icons/unchecked-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/unchecked.svg b/src/assets/icons/unchecked.svg index a080423..17b6fe8 100644 --- a/src/assets/icons/unchecked.svg +++ b/src/assets/icons/unchecked.svg @@ -1,3 +1,3 @@ - + diff --git a/src/assets/index.ts b/src/assets/index.ts index 8599afd..f757f8f 100644 --- a/src/assets/index.ts +++ b/src/assets/index.ts @@ -4,7 +4,9 @@ import { ReactComponent as SettingsIcon } from './icons/settings.svg' import { ReactComponent as CopyIcon } from './icons/copy.svg' import { ReactComponent as CheckmarkIcon } from './icons/checkmark.svg' import { ReactComponent as CheckedIcon } from './icons/checked.svg' +import { ReactComponent as CheckedLightIcon } from './icons/checked-light.svg' import { ReactComponent as UnchekedIcon } from './icons/unchecked.svg' +import { ReactComponent as UnchekedLightIcon } from './icons/unchecked-light.svg' import { default as AddImageIcon } from './icons/add-image.svg' export { @@ -14,6 +16,8 @@ export { CopyIcon, CheckmarkIcon, CheckedIcon, + CheckedLightIcon, UnchekedIcon, + UnchekedLightIcon, AddImageIcon, } diff --git a/src/components/Modal/ModalConfirmConnect/ModalConfirmConnect.tsx b/src/components/Modal/ModalConfirmConnect/ModalConfirmConnect.tsx index 60c1f8e..b341985 100644 --- a/src/components/Modal/ModalConfirmConnect/ModalConfirmConnect.tsx +++ b/src/components/Modal/ModalConfirmConnect/ModalConfirmConnect.tsx @@ -5,7 +5,7 @@ import { call, getShortenNpub } from '@/utils/helpers' import { Avatar, Box, Stack, Typography } from '@mui/material' import { useParams, useSearchParams } from 'react-router-dom' import { useAppSelector } from '@/store/hooks/redux' -import { selectAppsByNpub, selectPendingsByNpub } from '@/store' +import { selectAppsByNpub } from '@/store' import { StyledButton, StyledToggleButtonsGroup } from './styled' import { ActionToggleButton } from './сomponents/ActionToggleButton' import { useState } from 'react' @@ -21,38 +21,34 @@ export const ModalConfirmConnect = () => { const { getModalOpened, handleClose } = useModalSearchParams() const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.CONFIRM_CONNECT) - const handleCloseModal = handleClose( - MODAL_PARAMS_KEYS.CONFIRM_CONNECT, - (sp) => { - sp.delete('appNpub') - }, - ) + const { npub = '' } = useParams<{ npub: string }>() + const apps = useAppSelector((state) => selectAppsByNpub(state, npub)) const [selectedActionType, setSelectedActionType] = useState( ACTION_TYPE.BASIC, ) - const { npub = '' } = useParams<{ npub: string }>() - const apps = useAppSelector((state) => selectAppsByNpub(state, npub)) - const pending = useAppSelector((state) => selectPendingsByNpub(state, npub)) - const [searchParams] = useSearchParams() - const appNpub = searchParams.get('appNpub') || '' const pendingReqId = searchParams.get('reqId') || '' const triggerApp = apps.find((app) => app.appNpub === appNpub) - - const open = Boolean(isModalOpened) - const { name, icon = '' } = triggerApp || {} - const appName = name || getShortenNpub(appNpub) const handleActionTypeChange = (_: any, value: ACTION_TYPE) => { setSelectedActionType(value) } + const handleCloseModal = handleClose( + MODAL_PARAMS_KEYS.CONFIRM_CONNECT, + async (sp) => { + sp.delete('appNpub') + sp.delete('reqId') + await swicCall('confirm', pendingReqId, false, false) + }, + ) + async function confirmPending( id: string, allow: boolean, @@ -62,11 +58,14 @@ export const ModalConfirmConnect = () => { await swicCall('confirm', id, allow, remember) console.log('confirmed', id, allow, remember) }) - handleCloseModal() + handleClose(MODAL_PARAMS_KEYS.CONFIRM_CONNECT, async (sp) => { + sp.delete('appNpub') + sp.delete('reqId') + }) } return ( - + { confirmPending(pendingReqId, true, true)} + onClick={() => + confirmPending(pendingReqId, true, false) + } > Allow {selectedActionType} actions diff --git a/src/components/Modal/ModalConfirmEvent/ModalConfirmEvent.tsx b/src/components/Modal/ModalConfirmEvent/ModalConfirmEvent.tsx index 1258764..93e62db 100644 --- a/src/components/Modal/ModalConfirmEvent/ModalConfirmEvent.tsx +++ b/src/components/Modal/ModalConfirmEvent/ModalConfirmEvent.tsx @@ -5,7 +5,6 @@ import { call, getShortenNpub } from '@/utils/helpers' import { Avatar, Box, - Checkbox, List, ListItem, ListItemIcon, @@ -17,7 +16,7 @@ import { useParams, useSearchParams } from 'react-router-dom' import { useAppSelector } from '@/store/hooks/redux' import { selectAppsByNpub } from '@/store' import { ActionToggleButton } from './сomponents/ActionToggleButton' -import { FC, useState } from 'react' +import { FC, useEffect, useMemo, useState } from 'react' import { StyledActionsListContainer, StyledButton, @@ -26,6 +25,8 @@ import { import { SectionTitle } from '@/shared/SectionTitle/SectionTitle' import { swicCall } from '@/modules/swic' import { IPendingsByAppNpub } from '@/pages/KeyPage/Key.Page' +import { Checkbox } from '@/shared/Checkbox/Checkbox' +import { DbPending } from '@/modules/db' enum ACTION_TYPE { ALWAYS = 'ALWAYS', @@ -48,59 +49,86 @@ export const ACTIONS: { [type: string]: string } = { sign_event: 'Sign event', } +type PendingRequest = DbPending & { checked: boolean } + export const ModalConfirmEvent: FC = ({ confirmEventReqs, }) => { const { getModalOpened, handleClose } = useModalSearchParams() const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.CONFIRM_EVENT) - const handleCloseModal = handleClose( - MODAL_PARAMS_KEYS.CONFIRM_EVENT, - (sp) => sp.delete('appNpub'), - ) - - const [selectedActionType, setSelectedActionType] = useState( - ACTION_TYPE.ALWAYS, - ) - - const { npub = '' } = useParams<{ npub: string }>() - const apps = useAppSelector((state) => selectAppsByNpub(state, npub)) - const [searchParams] = useSearchParams() const appNpub = searchParams.get('appNpub') || '' const pendingReqId = searchParams.get('reqId') || '' - const currentAppPendingReqs = confirmEventReqs[appNpub]?.pending || [] + const { npub = '' } = useParams<{ npub: string }>() + const apps = useAppSelector((state) => selectAppsByNpub(state, npub)) + + const [selectedActionType, setSelectedActionType] = useState( + ACTION_TYPE.ALWAYS, + ) + const [pendingRequests, setPendingRequests] = useState([]) + + const currentAppPendingReqs = useMemo( + () => confirmEventReqs[appNpub]?.pending || [], + [confirmEventReqs, appNpub], + ) + + useEffect(() => { + setPendingRequests( + currentAppPendingReqs.map((pr) => ({ ...pr, checked: true })), + ) + }, [currentAppPendingReqs]) const triggerApp = apps.find((app) => app.appNpub === appNpub) - - const open = Boolean(isModalOpened) - const { name, icon = '' } = triggerApp || {} - const appName = name || getShortenNpub(appNpub) const handleActionTypeChange = (_: any, value: ACTION_TYPE) => { setSelectedActionType(value) } - async function confirmPending( - id: string, - allow: boolean, - remember: boolean, - ) { - currentAppPendingReqs.forEach((req) => { + const selectedPendingRequests = pendingRequests.filter((pr) => pr.checked) + + const handleCloseModal = handleClose( + MODAL_PARAMS_KEYS.CONFIRM_EVENT, + (sp) => { + sp.delete('appNpub') + sp.delete('reqId') + selectedPendingRequests.forEach( + async (req) => await swicCall('confirm', req.id, false, false), + ) + }, + ) + + async function confirmPending(id: string) { + selectedPendingRequests.forEach((req) => { call(async () => { - await swicCall('confirm', req.id, allow, remember) - console.log('confirmed', req.id, id, allow, remember) + if (selectedActionType === ACTION_TYPE.ONCE) { + await swicCall('confirm', req.id, true, false) + } else { + await swicCall('confirm', req.id, true, true) + } + console.log('confirmed', req.id, id, selectedActionType) }) }) - handleCloseModal() + handleClose(MODAL_PARAMS_KEYS.CONFIRM_EVENT, (sp) => { + sp.delete('appNpub') + sp.delete('reqId') + }) + } + + const handleChangeCheckbox = (reqId: string) => () => { + const newPendingRequests = pendingRequests.map((req) => { + if (req.id === reqId) return { ...req, checked: !req.checked } + return req + }) + setPendingRequests(newPendingRequests) } return ( - + = ({ Actions - {currentAppPendingReqs.map((perm) => { + {pendingRequests.map((req) => { return ( - + - {ACTIONS[perm.method]} + {ACTIONS[req.method]} ) @@ -173,7 +206,7 @@ export const ModalConfirmEvent: FC = ({ confirmPending(pendingReqId, true, true)} + onClick={() => confirmPending(pendingReqId)} > Allow {ACTION_LABELS[selectedActionType]} diff --git a/src/components/Modal/ModalConnectApp/ModalConnectApp.tsx b/src/components/Modal/ModalConnectApp/ModalConnectApp.tsx index bd2e32a..414e218 100644 --- a/src/components/Modal/ModalConnectApp/ModalConnectApp.tsx +++ b/src/components/Modal/ModalConnectApp/ModalConnectApp.tsx @@ -8,12 +8,17 @@ import { Modal } from '@/shared/Modal/Modal' import { MODAL_PARAMS_KEYS } from '@/types/modal' import { getBunkerLink } from '@/utils/helpers' import { Stack, Typography } from '@mui/material' +import { useRef } from 'react' import { useParams } from 'react-router-dom' export const ModalConnectApp = () => { const { getModalOpened, handleClose, handleOpen } = useModalSearchParams() + const timerRef = useRef() + const isModalOpened = getModalOpened(MODAL_PARAMS_KEYS.CONNECT_APP) - const handleCloseModal = handleClose(MODAL_PARAMS_KEYS.CONNECT_APP) + const handleCloseModal = handleClose(MODAL_PARAMS_KEYS.CONNECT_APP, () => { + clearTimeout(timerRef.current) + }) const notify = useEnqueueSnackbar() @@ -37,6 +42,12 @@ export const ModalConnectApp = () => { } } + const handleCopy = () => { + timerRef.current = setTimeout(() => { + handleCloseModal() + }, 3000) + } + return ( { }} fullWidth value={bunkerStr} - endAdornment={} + endAdornment={ + + } /> { const { getModalOpened, handleClose } = useModalSearchParams() @@ -62,10 +63,7 @@ export const ModalSettings = () => { )} - } - checkedIcon={} - /> + Use this login on multiple devices diff --git a/src/components/Modal/ModalSettings/styled.tsx b/src/components/Modal/ModalSettings/styled.tsx index 3222fa3..94fda42 100644 --- a/src/components/Modal/ModalSettings/styled.tsx +++ b/src/components/Modal/ModalSettings/styled.tsx @@ -9,11 +9,11 @@ import { export const StyledSettingContainer = styled((props: StackProps) => ( -))(() => ({ +))(({ theme }) => ({ padding: '0.75rem', borderRadius: '1rem', - background: '#000000', - color: '#fff', + background: theme.palette.background.default, + color: theme.palette.text.primary, })) export const StyledButton = styled(Button)(({ theme }) => { diff --git a/src/components/Warning/styled.tsx b/src/components/Warning/styled.tsx index 2398e75..4e86696 100644 --- a/src/components/Warning/styled.tsx +++ b/src/components/Warning/styled.tsx @@ -9,6 +9,7 @@ export const StyledContainer = styled((props: BoxProps) => )( display: 'flex', alignItems: 'center', gap: '1rem', + cursor: 'pointer', } }, ) diff --git a/src/pages/KeyPage/Key.Page.tsx b/src/pages/KeyPage/Key.Page.tsx index 8f4b259..06a5b27 100644 --- a/src/pages/KeyPage/Key.Page.tsx +++ b/src/pages/KeyPage/Key.Page.tsx @@ -51,46 +51,42 @@ const KeyPage = () => { const nofity = useEnqueueSnackbar() + const [profile, setProfile] = useState(null) + const userName = profile?.info?.name || getShortenNpub(npub) + const userNameWithPrefix = userName + '@nsec.app' + + const [showWarning, setShowWarning] = useState(false) + const filteredApps = apps.filter((a) => a.npub === npub) const filteredPendingReqs = pending.filter((p) => p.npub === npub) const filteredPerms = perms.filter((p) => p.npub === npub) - // eslint-disable-next-line const npubConnectPerms = filteredPerms.filter( (perm) => perm.perm === 'connect', ) - - console.log(npubConnectPerms, '=> npubConnectPerms') - const excludeConnectPeqs = filteredPendingReqs.filter( (pr) => pr.method !== 'connect', ) const prepareEventPendings = excludeConnectPeqs.reduce( (acc, current) => { + const isConnected = npubConnectPerms.some( + (cp) => cp.appNpub === current.appNpub, + ) if (!acc[current.appNpub]) { acc[current.appNpub] = { pending: [current], - isConnected: npubConnectPerms.some( - (cp) => cp.appNpub === current.appNpub, - ), + isConnected, } + return acc } acc[current.appNpub].pending.push(current) - acc[current.appNpub].isConnected = npubConnectPerms.some( - (cp) => cp.appNpub === current.appNpub, - ) + acc[current.appNpub].isConnected = isConnected return acc }, {}, ) - const [profile, setProfile] = useState(null) - const [showWarning, setShowWarning] = useState(false) - - const userName = profile?.info?.name || getShortenNpub(npub) - const userNameWithPrefix = userName + '@nsec.app' - const load = useCallback(async () => { try { const npubToken = npub.includes('#') ? npub.split('#')[0] : npub @@ -122,27 +118,25 @@ const KeyPage = () => { const handleOpenSettingsModal = () => handleOpen(MODAL_PARAMS_KEYS.SETTINGS) - useEffect(() => { - const checkBackgroundSigning = async () => { - if (swr) { - const isBackgroundEnable = - await swr.pushManager.getSubscription() - if (!isBackgroundEnable) { - setShowWarning(true) - } else { - setShowWarning(false) - } - } + const checkBackgroundSigning = useCallback(async () => { + if (swr) { + const isBackgroundEnable = await swr.pushManager.getSubscription() + if (!isBackgroundEnable) setShowWarning(true) + else setShowWarning(false) } - checkBackgroundSigning() }, []) + useEffect(() => { + checkBackgroundSigning() + }, [checkBackgroundSigning]) + const handleEnableBackground = async () => { await askNotificationPermission() try { const r = await swicCall('enablePush') if (!r) return nofity(`Failed to enable push subscription`, 'error') nofity('Enabled!', 'success') + checkBackgroundSigning() } catch (e) { nofity(`Failed to enable push subscription`, 'error') } @@ -157,7 +151,7 @@ const KeyPage = () => { shownConnectModals.current = {} shownConfirmEventModals.current = {} } - }, [npub]) + }, [npub, pending.length]) const connectPendings = filteredPendingReqs.filter( (pr) => pr.method === 'connect', diff --git a/src/pages/Welcome.Page.tsx b/src/pages/Welcome.Page.tsx index bf2b6eb..3930e4f 100644 --- a/src/pages/Welcome.Page.tsx +++ b/src/pages/Welcome.Page.tsx @@ -15,7 +15,7 @@ const WelcomePage = () => { const npubInputRef = useRef(null) const passwordInputRef = useRef(null) - if (isKeysExists) return + // if (isKeysExists) return async function generateKey() { try { diff --git a/src/routes/AppRoutes.tsx b/src/routes/AppRoutes.tsx index 4aa6bf6..b7f9eee 100644 --- a/src/routes/AppRoutes.tsx +++ b/src/routes/AppRoutes.tsx @@ -8,7 +8,6 @@ import { CircularProgress, Stack } from '@mui/material' const KeyPage = lazy(() => import('../pages/KeyPage/Key.Page')) const ConfirmPage = lazy(() => import('../pages/Confirm.Page')) const AppPage = lazy(() => import('../pages/App.Page')) -// const AuthPage = lazy(() => import('../pages/AuthPage/Auth.Page')) const LoadingSpinner = () => ( @@ -24,7 +23,6 @@ const AppRoutes = () => { } /> } /> } /> - {/* } /> */} } /> ( + (props, ref) => { + const { themeMode } = useAppSelector((state) => state.ui) + + return + }, +) + +const StyledCheckbox = styled( + forwardRef( + ({ mode, ...restProps }, ref) => { + const isDarkMode = mode === 'dark' + return ( + : } + checkedIcon={ + isDarkMode ? : + } + /> + ) + }, + ), +)(() => ({ + '& .MuiSvgIcon-root': { fontSize: '1.5rem' }, +})) diff --git a/src/shared/InputCopyButton/InputCopyButton.tsx b/src/shared/InputCopyButton/InputCopyButton.tsx index c66b185..2d2b132 100644 --- a/src/shared/InputCopyButton/InputCopyButton.tsx +++ b/src/shared/InputCopyButton/InputCopyButton.tsx @@ -6,12 +6,19 @@ import { StyledContainer } from './styled' type InputCopyButtonProps = { value: string + onCopy?: () => void } -export const InputCopyButton: FC = ({ value }) => { +export const InputCopyButton: FC = ({ + value, + onCopy = () => undefined, +}) => { const [isCopied, setIsCopied] = useState(false) - const handleCopy = () => setIsCopied(true) + const handleCopy = () => { + setIsCopied(true) + onCopy && onCopy() + } useEffect(() => { let timerId: any