summaryrefslogtreecommitdiffhomepage
path: root/gui/src
diff options
context:
space:
mode:
Diffstat (limited to 'gui/src')
-rw-r--r--gui/src/renderer/components/AdvancedSettings.tsx3
-rw-r--r--gui/src/renderer/components/ExpiredAccountErrorView.tsx3
-rw-r--r--gui/src/renderer/components/LinuxSplitTunnelingSettings.tsx1
-rw-r--r--gui/src/renderer/components/Modal.tsx66
-rw-r--r--gui/src/renderer/components/RedeemVoucher.tsx3
-rw-r--r--gui/src/renderer/components/Support.tsx2
6 files changed, 59 insertions, 19 deletions
diff --git a/gui/src/renderer/components/AdvancedSettings.tsx b/gui/src/renderer/components/AdvancedSettings.tsx
index 2e9dd497be..5baeaced3a 100644
--- a/gui/src/renderer/components/AdvancedSettings.tsx
+++ b/gui/src/renderer/components/AdvancedSettings.tsx
@@ -459,7 +459,8 @@ export default class AdvancedSettings extends React.Component<IProps, IState> {
<AppButton.BlueButton key="back" onClick={this.hideConfirmBlockWhenDisconnectedAlert}>
{messages.gettext('Back')}
</AppButton.BlueButton>,
- ]}>
+ ]}
+ close={this.hideConfirmBlockWhenDisconnectedAlert}>
<ModalMessage>
{messages.pgettext(
'advanced-settings-view',
diff --git a/gui/src/renderer/components/ExpiredAccountErrorView.tsx b/gui/src/renderer/components/ExpiredAccountErrorView.tsx
index 95177765a5..d64c780808 100644
--- a/gui/src/renderer/components/ExpiredAccountErrorView.tsx
+++ b/gui/src/renderer/components/ExpiredAccountErrorView.tsx
@@ -194,7 +194,8 @@ export default class ExpiredAccountErrorView extends React.Component<
onClick={this.onCloseBlockWhenDisconnectedInstructions}>
{messages.gettext('Close')}
</AppButton.BlueButton>,
- ]}>
+ ]}
+ close={this.onCloseBlockWhenDisconnectedInstructions}>
<ModalMessage>
{messages.pgettext(
'connect-view',
diff --git a/gui/src/renderer/components/LinuxSplitTunnelingSettings.tsx b/gui/src/renderer/components/LinuxSplitTunnelingSettings.tsx
index 914f5d818f..c5b5521f05 100644
--- a/gui/src/renderer/components/LinuxSplitTunnelingSettings.tsx
+++ b/gui/src/renderer/components/LinuxSplitTunnelingSettings.tsx
@@ -274,6 +274,7 @@ function ApplicationRow(props: IApplicationRowProps) {
iconColor={warningColor}
message={warningMessage}
buttons={warningDialogButtons}
+ close={hideWarningDialog}
/>
)}
</>
diff --git a/gui/src/renderer/components/Modal.tsx b/gui/src/renderer/components/Modal.tsx
index 3db4dcd626..b045803626 100644
--- a/gui/src/renderer/components/Modal.tsx
+++ b/gui/src/renderer/components/Modal.tsx
@@ -1,12 +1,10 @@
-import * as React from 'react';
+import React, { useContext, useRef, useState } from 'react';
import ReactDOM from 'react-dom';
import styled from 'styled-components';
import { colors } from '../../config.json';
import { Scheduler } from '../../shared/scheduler';
import ImageView from './ImageView';
-const MODAL_CONTAINER_ID = 'modalContainer';
-
const ModalContent = styled.div({
position: 'absolute',
display: 'flex',
@@ -39,11 +37,35 @@ interface IModalContainerProps {
children?: React.ReactNode;
}
+interface IModalContext {
+ activeModal: boolean;
+ setActiveModal: (value: boolean) => void;
+ modalContainerRef: React.RefObject<HTMLDivElement>;
+}
+
+const noActiveModalContextError = new Error('ActiveModalContext.Provider missing');
+const ActiveModalContext = React.createContext<IModalContext>({
+ get activeModal(): boolean {
+ throw noActiveModalContextError;
+ },
+ setActiveModal(_value) {
+ throw noActiveModalContextError;
+ },
+ get modalContainerRef(): React.RefObject<HTMLDivElement> {
+ throw noActiveModalContextError;
+ },
+});
+
export function ModalContainer(props: IModalContainerProps) {
+ const [activeModal, setActiveModal] = useState(false);
+ const modalContainerRef = useRef() as React.RefObject<HTMLDivElement>;
+
return (
- <StyledModalContainer id={MODAL_CONTAINER_ID}>
- <ModalContent>{props.children}</ModalContent>
- </StyledModalContainer>
+ <ActiveModalContext.Provider value={{ activeModal, setActiveModal, modalContainerRef }}>
+ <StyledModalContainer ref={modalContainerRef}>
+ <ModalContent aria-hidden={activeModal}>{props.children}</ModalContent>
+ </StyledModalContainer>
+ </ActiveModalContext.Provider>
);
}
@@ -86,18 +108,24 @@ interface IModalAlertProps {
message?: string;
buttons: React.ReactNode[];
children?: React.ReactNode;
+ close?: () => void;
+}
+
+export function ModalAlert(props: IModalAlertProps) {
+ const activeModalContext = useContext(ActiveModalContext);
+ return <ModalAlertWithContext {...activeModalContext} {...props} />;
}
-export class ModalAlert extends React.Component<IModalAlertProps> {
+class ModalAlertWithContext extends React.Component<IModalAlertProps & IModalContext> {
private element = document.createElement('div');
- private modalContainer?: Element;
private appendScheduler = new Scheduler();
public componentDidMount() {
- const modalContainer = document.getElementById(MODAL_CONTAINER_ID);
- if (modalContainer) {
- this.modalContainer = modalContainer;
+ this.props.setActiveModal(true);
+ document.addEventListener('keydown', this.handleKeyPress);
+ const modalContainer = this.props.modalContainerRef.current;
+ if (modalContainer) {
// Mounting the container element immediately results in a graphical issue with the dialog
// first rendering with the wrong proportions and then changing to the correct proportions.
// Postponing it to the next event cycle solves this issue.
@@ -110,11 +138,11 @@ export class ModalAlert extends React.Component<IModalAlertProps> {
}
public componentWillUnmount() {
- this.appendScheduler.cancel();
+ this.props.setActiveModal(false);
+ document.removeEventListener('keydown', this.handleKeyPress);
- if (this.modalContainer) {
- this.modalContainer.removeChild(this.element);
- }
+ this.appendScheduler.cancel();
+ this.props.modalContainerRef.current?.removeChild(this.element);
}
public render() {
@@ -125,7 +153,7 @@ export class ModalAlert extends React.Component<IModalAlertProps> {
return (
<ModalBackground>
<ModalAlertContainer>
- <StyledModalAlert>
+ <StyledModalAlert role="alertdialog">
{this.props.type && (
<ModalAlertIcon>{this.renderTypeIcon(this.props.type)}</ModalAlertIcon>
)}
@@ -157,6 +185,12 @@ export class ModalAlert extends React.Component<IModalAlertProps> {
<ImageView height={44} width={44} source={source} tintColor={this.props.iconColor ?? color} />
);
}
+
+ private handleKeyPress = (event: KeyboardEvent) => {
+ if (event.key === 'Escape') {
+ this.props.close?.();
+ }
+ };
}
export const ModalMessage = styled.span({
diff --git a/gui/src/renderer/components/RedeemVoucher.tsx b/gui/src/renderer/components/RedeemVoucher.tsx
index 7ca83c43d1..29a84f868c 100644
--- a/gui/src/renderer/components/RedeemVoucher.tsx
+++ b/gui/src/renderer/components/RedeemVoucher.tsx
@@ -198,7 +198,8 @@ export function RedeemVoucherAlert(props: IRedeemVoucherAlertProps) {
<AppButton.BlueButton key="cancel" disabled={cancelDisabled} onClick={props.onClose}>
{messages.pgettext('redeem-voucher-alert', 'Cancel')}
</AppButton.BlueButton>,
- ]}>
+ ]}
+ close={props.onClose}>
<StyledLabel>{messages.pgettext('redeem-voucher-alert', 'Enter voucher code')}</StyledLabel>
<RedeemVoucherInput />
<RedeemVoucherResponse />
diff --git a/gui/src/renderer/components/Support.tsx b/gui/src/renderer/components/Support.tsx
index 295855a9a6..c685ebe946 100644
--- a/gui/src/renderer/components/Support.tsx
+++ b/gui/src/renderer/components/Support.tsx
@@ -252,6 +252,7 @@ export default class Support extends React.Component<ISupportProps, ISupportStat
{messages.gettext('Back')}
</AppButton.BlueButton>,
]}
+ close={this.onCancelNoEmailDialog}
/>
);
}
@@ -286,6 +287,7 @@ export default class Support extends React.Component<ISupportProps, ISupportStat
{messages.gettext('Cancel')}
</AppButton.BlueButton>,
]}
+ close={this.props.onClose}
/>
);
}