Skip to content

Commit

Permalink
Support opening the settings dialog directly from dangerous modal dia…
Browse files Browse the repository at this point in the history
…logs
  • Loading branch information
csillag committed Oct 19, 2022
1 parent dabe05e commit d1f488e
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 15 deletions.
60 changes: 51 additions & 9 deletions src/app/components/Modal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { createContext, useCallback, useContext, useEffect, useState } from 'react'
import { Box, Button, Layer, Heading, Paragraph } from 'grommet'
import { Box, Button, Layer, Paragraph } from 'grommet'
import { useTranslation } from 'react-i18next'
import { Alert, Checkmark, Close } from 'grommet-icons'
import { Alert, Checkmark, Close, Configure } from 'grommet-icons'
import { ModalHeader } from 'app/components/Header'
import { AlertBox } from '../AlertBox'
import { selectAllowDangerousSetting } from '../SettingsDialog/slice/selectors'
import { useSelector } from 'react-redux'
import { useDispatch, useSelector } from 'react-redux'
import { settingsActions } from '../SettingsDialog/slice'

interface Modal {
title: string
Expand All @@ -30,11 +31,29 @@ interface Modal {
interface ModalContainerProps {
modal: Modal
closeModal: () => void
hideModal: () => void
}

interface ModalContextProps {
/**
* Show a new modal
*/
launchModal: (modal: Modal) => void

/**
* Close the current modal
*/
closeModal: () => void

/**
* Hide the current modal (with the intention of showing in again later)
*/
hideModal: () => void

/**
* Show the previously hidden modal again
*/
showModal: () => void
}

interface ModalProviderProps {
Expand All @@ -43,7 +62,8 @@ interface ModalProviderProps {

const ModalContext = createContext<ModalContextProps>({} as ModalContextProps)

const ModalContainer = ({ modal, closeModal }: ModalContainerProps) => {
const ModalContainer = ({ modal, closeModal, hideModal }: ModalContainerProps) => {
const dispatch = useDispatch()
const { t } = useTranslation()
const confirm = useCallback(() => {
modal.handleConfirm()
Expand All @@ -59,6 +79,10 @@ const ModalContainer = ({ modal, closeModal }: ModalContainerProps) => {
: mustWaitSecs ?? 0 // For normal, non-dangerous operations, just use what was specified

const [secsLeft, setSecsLeft] = useState(0)
const openSettings = () => {
hideModal()
setTimeout(() => dispatch(settingsActions.setOpen(true)), 100)
}

useEffect(() => {
if (waitingTime) {
Expand Down Expand Up @@ -99,9 +123,6 @@ const ModalContainer = ({ modal, closeModal }: ModalContainerProps) => {
</AlertBox>
)}
<Box direction="row" gap="small" justify="between" pad={{ top: 'large' }}>
{/* This is a placeholder, so that if we have only one valid button,
the Cancel, it should be on the right side, not the left side.*/}
{!!forbidden && <span />}
<Button
label={t('common.cancel', 'Cancel')}
onClick={closeModal}
Expand All @@ -118,6 +139,9 @@ const ModalContainer = ({ modal, closeModal }: ModalContainerProps) => {
icon={modal.isDangerous ? <Alert size="18px" /> : <Checkmark size="18px" />}
/>
)}
{forbidden && (
<Button label={t('menu.settings', 'Settings')} onClick={openSettings} icon={<Configure />} />
)}
</Box>
</Box>
</Layer>
Expand All @@ -126,14 +150,32 @@ const ModalContainer = ({ modal, closeModal }: ModalContainerProps) => {

const ModalProvider = (props: ModalProviderProps) => {
const [modal, setModal] = useState<Modal | null>(null)
const [hiddenModal, setHiddenModal] = useState<Modal | null>(null)
const closeModal = useCallback(() => {
setModal(null)
}, [])
const hideModal = useCallback(() => {
if (!modal) {
throw new Error("You can't call hideModal if no model is shown!")
}
setHiddenModal(modal)
setModal(null)
}, [modal])
const showModal = useCallback(() => {
if (modal) {
throw new Error("You can't call showModal when a modal is already visible!")
}
if (!hiddenModal) {
return
}
setModal(hiddenModal)
setHiddenModal(null)
}, [modal, hiddenModal])

return (
<ModalContext.Provider value={{ closeModal, launchModal: setModal }}>
<ModalContext.Provider value={{ closeModal, launchModal: setModal, hideModal, showModal }}>
{props.children}
{modal && <ModalContainer modal={modal} closeModal={closeModal} />}
{modal && <ModalContainer modal={modal} closeModal={closeModal} hideModal={hideModal} />}
</ModalContext.Provider>
)
}
Expand Down
21 changes: 15 additions & 6 deletions src/app/components/SettingsButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
import React, { useState } from 'react'
import React from 'react'
import { SidebarButton } from '../Sidebar'
import { Configure } from 'grommet-icons'
import { useTranslation } from 'react-i18next'
import { SettingsDialog } from '../SettingsDialog'
import { selectIsSettingsDialogOpen } from '../SettingsDialog/slice/selectors'
import { useDispatch, useSelector } from 'react-redux'
import { settingsActions } from '../SettingsDialog/slice'
import { useModal } from '../Modal'

export const SettingsButton = () => {
const [layerVisibility, setLayerVisibility] = useState(false)
const dispatch = useDispatch()
const layerVisibility = useSelector(selectIsSettingsDialogOpen)
const { t } = useTranslation()
const { showModal } = useModal()
const setLayerVisibility = (value: boolean) => dispatch(settingsActions.setOpen(value))
const closeSettings = () => {
setLayerVisibility(false)
showModal()
}
return (
<>
<SidebarButton
icon={<Configure />}
label={t('menu.settings', 'Settings')}
onClick={() => {
setLayerVisibility(true)
}}
onClick={() => setLayerVisibility(true)}
/>
{layerVisibility && <SettingsDialog closeHandler={() => setLayerVisibility(false)} />}
{layerVisibility && <SettingsDialog closeHandler={closeSettings} />}
</>
)
}
4 changes: 4 additions & 0 deletions src/app/components/SettingsDialog/slice/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ import { createSlice } from 'utils/@reduxjs/toolkit'
import { SettingsState } from './types'

export const initialState: SettingsState = {
dialogOpen: false,
allowDangerous: false,
}

const slice = createSlice({
name: 'settings',
initialState,
reducers: {
setOpen(state, action: PayloadAction<boolean>) {
state.dialogOpen = action.payload
},
setAllowDangerous(state, action: PayloadAction<boolean>) {
state.allowDangerous = action.payload
},
Expand Down
1 change: 1 addition & 0 deletions src/app/components/SettingsDialog/slice/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ import { initialState } from '.'
const selectSlice = (state: RootState) => state.settings || initialState

export const selectAllowDangerousSetting = createSelector([selectSlice], settings => settings.allowDangerous)
export const selectIsSettingsDialogOpen = createSelector([selectSlice], settings => settings.dialogOpen)
1 change: 1 addition & 0 deletions src/app/components/SettingsDialog/slice/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export interface SettingsState {
dialogOpen: boolean
allowDangerous: boolean
}

0 comments on commit d1f488e

Please sign in to comment.