summaryrefslogtreecommitdiffhomepage
path: root/gui/src/renderer/components
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2023-06-13 11:01:29 +0200
committerOskar Nyberg <oskar@mullvad.net>2023-06-14 10:25:18 +0200
commit0aadb762da306adc7e305388496a384fdb2c4181 (patch)
tree95d4f7b21a659b93f34c45ef1718f81a7ab8a8e5 /gui/src/renderer/components
parentcecccd43d8e3c05e281be19c58f8e08fb2b651c6 (diff)
downloadmullvadvpn-0aadb762da306adc7e305388496a384fdb2c4181.tar.xz
mullvadvpn-0aadb762da306adc7e305388496a384fdb2c4181.zip
Add new device in-app notification
Diffstat (limited to 'gui/src/renderer/components')
-rw-r--r--gui/src/renderer/components/NotificationArea.tsx29
-rw-r--r--gui/src/renderer/components/NotificationBanner.tsx37
2 files changed, 42 insertions, 24 deletions
diff --git a/gui/src/renderer/components/NotificationArea.tsx b/gui/src/renderer/components/NotificationArea.tsx
index 4923bacc7c..79fb3ef17c 100644
--- a/gui/src/renderer/components/NotificationArea.tsx
+++ b/gui/src/renderer/components/NotificationArea.tsx
@@ -5,6 +5,7 @@ import styled from 'styled-components';
import { colors } from '../../config.json';
import { messages } from '../../shared/gettext';
import log from '../../shared/logging';
+import { NewDeviceNotificationProvider } from '../../shared/notifications/new-device';
import {
BlockWhenDisconnectedNotificationProvider,
CloseToAccountExpiryNotificationProvider,
@@ -19,14 +20,18 @@ import {
UpdateAvailableNotificationProvider,
} from '../../shared/notifications/notification';
import { useAppContext } from '../context';
+import useActions from '../lib/actionsHook';
import { transitions, useHistory } from '../lib/history';
+import { formatHtml } from '../lib/html-formatter';
import { RoutePath } from '../lib/routes';
+import accountActions from '../redux/account/actions';
import { IReduxState } from '../redux/store';
import * as AppButton from './AppButton';
import { ModalAlert, ModalAlertType, ModalMessage } from './Modal';
import {
NotificationActions,
NotificationBanner,
+ NotificationCloseAction,
NotificationContent,
NotificationIndicator,
NotificationOpenLinkAction,
@@ -40,7 +45,7 @@ interface IProps {
}
export default function NotificationArea(props: IProps) {
- const accountExpiry = useSelector((state: IReduxState) => state.account.expiry);
+ const account = useSelector((state: IReduxState) => state.account);
const locale = useSelector((state: IReduxState) => state.userInterface.locale);
const tunnelState = useSelector((state: IReduxState) => state.connection.status);
const version = useSelector((state: IReduxState) => state.version);
@@ -52,6 +57,8 @@ export default function NotificationArea(props: IProps) {
state.settings.splitTunneling && state.settings.splitTunnelingApplications.length > 0,
);
+ const { hideNewDeviceBanner } = useActions(accountActions);
+
const notificationProviders: InAppNotificationProvider[] = [
new ConnectingNotificationProvider({ tunnelState }),
new ReconnectingNotificationProvider(tunnelState),
@@ -65,13 +72,20 @@ export default function NotificationArea(props: IProps) {
new UnsupportedVersionNotificationProvider(version),
];
- if (accountExpiry) {
+ if (account.expiry) {
notificationProviders.push(
- new CloseToAccountExpiryNotificationProvider({ accountExpiry, locale }),
+ new CloseToAccountExpiryNotificationProvider({ accountExpiry: account.expiry, locale }),
);
}
- notificationProviders.push(new UpdateAvailableNotificationProvider(version));
+ notificationProviders.push(
+ new NewDeviceNotificationProvider({
+ shouldDisplay: account.status.type === 'ok' && account.status.newDeviceBanner,
+ deviceName: account.deviceName ?? '',
+ close: hideNewDeviceBanner,
+ }),
+ new UpdateAvailableNotificationProvider(version),
+ );
const notificationProvider = notificationProviders.find((notification) =>
notification.mayDisplay(),
@@ -86,7 +100,7 @@ export default function NotificationArea(props: IProps) {
<NotificationIndicator type={notification.indicator} />
<NotificationContent role="status" aria-live="polite">
<NotificationTitle>{notification.title}</NotificationTitle>
- <NotificationSubtitle>{notification.subtitle}</NotificationSubtitle>
+ <NotificationSubtitle>{formatHtml(notification.subtitle ?? '')}</NotificationSubtitle>
</NotificationContent>
{notification.action && <NotificationActionWrapper action={notification.action} />}
</NotificationBanner>
@@ -128,6 +142,9 @@ function NotificationActionWrapper(props: INotificationActionWrapperProps) {
case 'troubleshoot-dialog':
setTroubleshootInfo(props.action.troubleshoot);
break;
+ case 'close':
+ props.action.close();
+ break;
}
}
@@ -154,6 +171,8 @@ function NotificationActionWrapper(props: INotificationActionWrapperProps) {
</>
);
break;
+ case 'close':
+ actionComponent = <NotificationCloseAction onClick={handleClick} />;
}
}
diff --git a/gui/src/renderer/components/NotificationBanner.tsx b/gui/src/renderer/components/NotificationBanner.tsx
index 4f20d315c8..18e18ce4f5 100644
--- a/gui/src/renderer/components/NotificationBanner.tsx
+++ b/gui/src/renderer/components/NotificationBanner.tsx
@@ -35,29 +35,23 @@ export const NotificationActionButton = styled(AppButton.SimpleButton)({
border: 'none',
});
-export const NotificationOpenLinkActionIcon = styled(ImageView)({
- [NotificationActionButton + ':hover &']: {
+export const NotificationActionButtonInner = styled(ImageView)({
+ [NotificationActionButton + ':hover &&']: {
backgroundColor: colors.white80,
},
});
-export const NotificationTroubleshootDialogActionIcon = styled(ImageView)({
- [NotificationActionButton + ':hover &']: {
- backgroundColor: colors.white80,
- },
-});
-
-interface INotifcationOpenLinkActionProps {
+interface NotificationActionProps {
onClick: () => Promise<void>;
}
-export function NotificationOpenLinkAction(props: INotifcationOpenLinkActionProps) {
+export function NotificationOpenLinkAction(props: NotificationActionProps) {
return (
<AppButton.BlockingButton onClick={props.onClick}>
<NotificationActionButton
aria-describedby={NOTIFICATION_AREA_ID}
aria-label={messages.gettext('Open URL')}>
- <NotificationOpenLinkActionIcon
+ <NotificationActionButtonInner
height={12}
width={12}
tintColor={colors.white60}
@@ -68,19 +62,13 @@ export function NotificationOpenLinkAction(props: INotifcationOpenLinkActionProp
);
}
-interface INotifcationTroubleshootDialogActionProps {
- onClick: () => Promise<void>;
-}
-
-export function NotificationTroubleshootDialogAction(
- props: INotifcationTroubleshootDialogActionProps,
-) {
+export function NotificationTroubleshootDialogAction(props: NotificationActionProps) {
return (
<NotificationActionButton
aria-describedby={NOTIFICATION_AREA_ID}
aria-label={messages.gettext('Troubleshoot')}
onClick={props.onClick}>
- <NotificationOpenLinkActionIcon
+ <NotificationActionButtonInner
height={12}
width={12}
tintColor={colors.white60}
@@ -90,6 +78,17 @@ export function NotificationTroubleshootDialogAction(
);
}
+export function NotificationCloseAction(props: NotificationActionProps) {
+ return (
+ <NotificationActionButton
+ aria-describedby={NOTIFICATION_AREA_ID}
+ aria-label={messages.pgettext('accessibility', 'Close notification')}
+ onClick={props.onClick}>
+ <NotificationActionButtonInner source="icon-close" width={16} tintColor={colors.white60} />
+ </NotificationActionButton>
+ );
+}
+
export const NotificationContent = styled.div.attrs({ id: NOTIFICATION_AREA_ID })({
display: 'flex',
flexDirection: 'column',