summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/DeviceRevokedView.tsx14
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/ErrorView.tsx8
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountAddTime.tsx19
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorView.tsx16
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorViewStyles.tsx5
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/HeaderBar.tsx210
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/Layout.tsx5
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/Login.tsx11
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/TooManyDevices.tsx11
-rw-r--r--desktop/packages/mullvad-vpn/src/renderer/components/main-view/MainView.tsx7
10 files changed, 42 insertions, 264 deletions
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/DeviceRevokedView.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/DeviceRevokedView.tsx
index c591c816f4..0036876bb4 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/DeviceRevokedView.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/DeviceRevokedView.tsx
@@ -7,14 +7,9 @@ import { useSelector } from '../redux/store';
import * as AppButton from './AppButton';
import { bigText, measurements, smallText } from './common-styles';
import CustomScrollbars from './CustomScrollbars';
-import { calculateHeaderBarStyle, DefaultHeaderBar } from './HeaderBar';
import ImageView from './ImageView';
-import { Container, Footer } from './Layout';
-import { Layout } from './Layout';
-
-export const StyledHeader = styled(DefaultHeaderBar)({
- flex: 0,
-});
+import { Container, Footer, Layout } from './Layout';
+import { MainHeader } from './main-header';
export const StyledCustomScrollbars = styled(CustomScrollbars)({
flex: 1,
@@ -59,7 +54,10 @@ export function DeviceRevokedView() {
return (
<Layout>
- <StyledHeader barStyle={calculateHeaderBarStyle(tunnelState)} />
+ <MainHeader variant="basedOnConnectionStatus" size="basedOnLoginStatus">
+ <MainHeader.AccountButton />
+ <MainHeader.SettingsButton />
+ </MainHeader>
<StyledCustomScrollbars fillContainer>
<StyledContainer>
<StyledBody>
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/ErrorView.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/ErrorView.tsx
index 08ed7b0962..edd4f78c7a 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/ErrorView.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/ErrorView.tsx
@@ -3,9 +3,9 @@ import styled from 'styled-components';
import { colors } from '../../config.json';
import { measurements } from './common-styles';
-import { HeaderBarSettingsButton } from './HeaderBar';
import ImageView from './ImageView';
-import { Container, Header, Layout } from './Layout';
+import { Container, Layout } from './Layout';
+import { MainHeader } from './main-header';
const StyledContainer = styled(Container)({
flex: 1,
@@ -56,7 +56,9 @@ interface ErrorViewProps {
export default function ErrorView(props: ErrorViewProps) {
return (
<Layout>
- <Header>{!props.settingsUnavailable && <HeaderBarSettingsButton />}</Header>
+ <MainHeader logoVariant="none">
+ {!props.settingsUnavailable && <MainHeader.SettingsButton />}
+ </MainHeader>
<StyledContainer>
<StyledContent>
<Logo height={106} width={106} source="logo-icon" />
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountAddTime.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountAddTime.tsx
index 2433a29644..e15709275f 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountAddTime.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountAddTime.tsx
@@ -18,9 +18,9 @@ import * as AppButton from './AppButton';
import { AriaDescribed, AriaDescription, AriaDescriptionGroup } from './AriaGroup';
import { hugeText, measurements, tinyText } from './common-styles';
import CustomScrollbars from './CustomScrollbars';
-import { calculateHeaderBarStyle, DefaultHeaderBar, HeaderBarStyle } from './HeaderBar';
import ImageView from './ImageView';
import { Container, Footer, Layout } from './Layout';
+import { MainHeader } from './main-header';
import {
RedeemVoucherContainer,
RedeemVoucherInput,
@@ -28,10 +28,6 @@ import {
RedeemVoucherSubmitButton,
} from './RedeemVoucher';
-export const StyledHeader = styled(DefaultHeaderBar)({
- flex: 0,
-});
-
export const StyledCustomScrollbars = styled(CustomScrollbars)({
flex: 1,
});
@@ -261,12 +257,15 @@ function HeaderBar() {
const isNewAccount = useSelector(
(state) => state.account.status.type === 'ok' && state.account.status.method === 'new_account',
);
- const tunnelState = useSelector((state) => state.connection.status);
- const headerBarStyle = isNewAccount
- ? HeaderBarStyle.default
- : calculateHeaderBarStyle(tunnelState);
- return <StyledHeader barStyle={headerBarStyle} />;
+ return (
+ <MainHeader
+ variant={isNewAccount ? 'default' : 'basedOnConnectionStatus'}
+ size="basedOnLoginStatus">
+ <MainHeader.AccountButton />
+ <MainHeader.SettingsButton />
+ </MainHeader>
+ );
}
function useFinishedCallback() {
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorView.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorView.tsx
index a98d3e441c..2c95936a4d 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorView.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorView.tsx
@@ -21,15 +21,14 @@ import {
StyledContainer,
StyledCustomScrollbars,
StyledDeviceLabel,
- StyledHeader,
StyledMessage,
StyledModalCellContainer,
StyledStatusIcon,
StyledTitle,
} from './ExpiredAccountErrorViewStyles';
-import { calculateHeaderBarStyle, HeaderBarStyle } from './HeaderBar';
import ImageView from './ImageView';
import { Footer, Layout } from './Layout';
+import { MainHeader } from './main-header';
import { ModalAlert, ModalAlertType, ModalMessage } from './Modal';
enum RecoveryAction {
@@ -50,15 +49,9 @@ function ExpiredAccountErrorViewComponent() {
const { push } = useHistory();
const { disconnectTunnel } = useAppContext();
- const connection = useSelector((state) => state.connection);
-
const { recoveryAction } = useRecoveryAction();
const isNewAccount = useIsNewAccount();
- const headerBarStyle = isNewAccount
- ? HeaderBarStyle.default
- : calculateHeaderBarStyle(connection.status);
-
const onDisconnect = useCallback(async () => {
try {
await disconnectTunnel();
@@ -74,7 +67,12 @@ function ExpiredAccountErrorViewComponent() {
return (
<Layout>
- <StyledHeader barStyle={headerBarStyle} />
+ <MainHeader
+ variant={isNewAccount ? 'default' : 'basedOnConnectionStatus'}
+ size="basedOnLoginStatus">
+ <MainHeader.AccountButton />
+ <MainHeader.SettingsButton />
+ </MainHeader>
<StyledCustomScrollbars fillContainer>
<StyledContainer>
<StyledBody>{isNewAccount ? <WelcomeView /> : <Content />}</StyledBody>
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorViewStyles.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorViewStyles.tsx
index f41178a627..5e62751570 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorViewStyles.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/ExpiredAccountErrorViewStyles.tsx
@@ -5,13 +5,8 @@ import AccountNumberLabel from './AccountNumberLabel';
import * as Cell from './cell';
import { hugeText, measurements, tinyText } from './common-styles';
import CustomScrollbars from './CustomScrollbars';
-import { DefaultHeaderBar } from './HeaderBar';
import { Container } from './Layout';
-export const StyledHeader = styled(DefaultHeaderBar)({
- flex: 0,
-});
-
export const StyledAccountNumberLabel = styled(AccountNumberLabel)({
fontFamily: 'Open Sans',
lineHeight: '20px',
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/HeaderBar.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/HeaderBar.tsx
deleted file mode 100644
index 08c7219ded..0000000000
--- a/desktop/packages/mullvad-vpn/src/renderer/components/HeaderBar.tsx
+++ /dev/null
@@ -1,210 +0,0 @@
-import React, { useCallback } from 'react';
-import { sprintf } from 'sprintf-js';
-import styled from 'styled-components';
-
-import { colors } from '../../config.json';
-import { closeToExpiry, formatRemainingTime, hasExpired } from '../../shared/account-expiry';
-import { TunnelState } from '../../shared/daemon-rpc-types';
-import { messages } from '../../shared/gettext';
-import { capitalizeEveryWord } from '../../shared/string-helpers';
-import { Flex, FootnoteMini, IconButton } from '../lib/components';
-import { Colors, Spacings } from '../lib/foundations';
-import { transitions, useHistory } from '../lib/history';
-import { RoutePath } from '../lib/routes';
-import { useSelector } from '../redux/store';
-import { FocusFallback } from './Focus';
-import ImageView from './ImageView';
-
-export enum HeaderBarStyle {
- default = 'default',
- defaultDark = 'defaultDark',
- error = 'error',
- success = 'success',
-}
-
-const headerBarStyleColorMap = {
- [HeaderBarStyle.default]: colors.blue,
- [HeaderBarStyle.defaultDark]: colors.darkBlue,
- [HeaderBarStyle.error]: colors.red,
- [HeaderBarStyle.success]: colors.green,
-};
-
-const StyledHeader = styled.header<{
- $barStyle?: HeaderBarStyle;
- $accountInfoVisible: boolean;
-}>(({ $accountInfoVisible, $barStyle }) => ({
- height: $accountInfoVisible ? '80px' : '68px',
- minHeight: $accountInfoVisible ? '80px' : '68px',
-
- backgroundColor: headerBarStyleColorMap[$barStyle ?? HeaderBarStyle.default],
- transition: 'height 250ms ease-in-out, min-height 250ms ease-in-out',
-}));
-
-const StyledLogoRow = styled(Flex)({
- height: '38px',
-});
-
-interface IHeaderBarProps {
- barStyle?: HeaderBarStyle;
- className?: string;
- children?: React.ReactNode;
- showAccountInfo?: boolean;
-}
-
-export default function HeaderBar(props: IHeaderBarProps) {
- return (
- <StyledHeader
- $barStyle={props.barStyle}
- $accountInfoVisible={props.showAccountInfo ?? false}
- className={props.className}>
- <Flex
- $flexDirection="column"
- $justifyContent="center"
- $margin={{
- horizontal: Spacings.spacing5,
- top: Spacings.spacing5,
- bottom: Spacings.spacing3,
- }}>
- <StyledLogoRow $justifyContent="space-between" $alignItems="center">
- {props.children}
- </StyledLogoRow>
- {props.showAccountInfo && <HeaderBarDeviceInfo />}
- </Flex>
- </StyledHeader>
- );
-}
-
-export function Brand(props: React.HTMLAttributes<HTMLDivElement>) {
- return (
- <Flex $flex={1} $alignItems="center" $gap={Spacings.spacing3} {...props}>
- <ImageView width={38} height={38} source="logo-icon" />
- <ImageView height={15.4} source="logo-text" />
- </Flex>
- );
-}
-
-const StyledDeviceInfoContainer = styled(Flex)({
- minHeight: '18px',
-});
-
-const StyledDeviceLabel = styled(FootnoteMini)({
- whiteSpace: 'nowrap',
- overflow: 'hidden',
- textOverflow: 'ellipsis',
-});
-
-const StyledTimeLeftLabel = styled(FootnoteMini)({
- whiteSpace: 'nowrap',
-});
-
-function HeaderBarDeviceInfo() {
- const deviceName = useSelector((state) => state.account.deviceName);
- const accountExpiry = useSelector((state) => state.account.expiry);
- const isOutOfTime = accountExpiry ? hasExpired(accountExpiry) : false;
- const formattedExpiry = isOutOfTime
- ? sprintf(messages.ngettext('1 day', '%d days', 0), 0)
- : accountExpiry
- ? formatRemainingTime(accountExpiry)
- : '';
-
- return (
- <StyledDeviceInfoContainer $flex={1} $alignItems="flex-end" $gap={Spacings.spacing6}>
- <StyledDeviceLabel color={Colors.white80}>
- {sprintf(
- // TRANSLATORS: A label that will display the newly created device name to inform the user
- // TRANSLATORS: about it.
- // TRANSLATORS: Available placeholders:
- // TRANSLATORS: %(deviceName)s - The name of the current device
- messages.pgettext('device-management', 'Device name: %(deviceName)s'),
- {
- deviceName: capitalizeEveryWord(deviceName ?? ''),
- },
- )}
- </StyledDeviceLabel>
- {accountExpiry && !closeToExpiry(accountExpiry) && !isOutOfTime && (
- <StyledTimeLeftLabel color={Colors.white80}>
- {sprintf(messages.pgettext('device-management', 'Time left: %(timeLeft)s'), {
- timeLeft: formattedExpiry,
- })}
- </StyledTimeLeftLabel>
- )}
- </StyledDeviceInfoContainer>
- );
-}
-
-interface IHeaderBarSettingsButtonProps {
- disabled?: boolean;
-}
-
-export function HeaderBarSettingsButton(props: IHeaderBarSettingsButtonProps) {
- const history = useHistory();
-
- const openSettings = useCallback(() => {
- if (!props.disabled) {
- history.push(RoutePath.settings, { transition: transitions.show });
- }
- }, [history, props.disabled]);
-
- return (
- <IconButton
- icon="icon-settings"
- variant="secondary"
- onClick={openSettings}
- aria-label={messages.gettext('Settings')}
- />
- );
-}
-
-export function HeaderBarAccountButton() {
- const history = useHistory();
- const openAccount = useCallback(
- () => history.push(RoutePath.account, { transition: transitions.show }),
- [history],
- );
-
- return (
- <IconButton
- icon="icon-account"
- variant="secondary"
- onClick={openAccount}
- data-testid="account-button"
- aria-label={messages.gettext('Account settings')}
- />
- );
-}
-
-export function DefaultHeaderBar(props: IHeaderBarProps) {
- const loggedIn = useSelector((state) => state.account.status.type === 'ok');
-
- return (
- <HeaderBar showAccountInfo={loggedIn} {...props}>
- <FocusFallback>
- <Brand />
- </FocusFallback>
- <Flex $gap={Spacings.spacing5} $alignItems="center">
- {loggedIn && <HeaderBarAccountButton />}
- <HeaderBarSettingsButton />
- </Flex>
- </HeaderBar>
- );
-}
-
-export function calculateHeaderBarStyle(tunnelState: TunnelState): HeaderBarStyle {
- switch (tunnelState.state) {
- case 'disconnected':
- return HeaderBarStyle.error;
- case 'connecting':
- case 'connected':
- return HeaderBarStyle.success;
- case 'error':
- return !tunnelState.details.blockingError ? HeaderBarStyle.success : HeaderBarStyle.error;
- case 'disconnecting':
- switch (tunnelState.details) {
- case 'block':
- case 'reconnect':
- return HeaderBarStyle.success;
- case 'nothing':
- return HeaderBarStyle.error;
- }
- }
-}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/Layout.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/Layout.tsx
index 62babaac38..9ea55431f0 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/Layout.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/Layout.tsx
@@ -3,13 +3,8 @@ import styled from 'styled-components';
import { Flex } from '../lib/components';
import { Colors, Spacings } from '../lib/foundations';
import { measurements } from './common-styles';
-import HeaderBar from './HeaderBar';
import { NavigationScrollbars } from './NavigationBar';
-export const Header = styled(HeaderBar)({
- flex: 0,
-});
-
export const Container = styled.div({
display: 'flex',
flexDirection: 'column',
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/Login.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/Login.tsx
index 0d234bd66b..37f8cc9d48 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/Login.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/Login.tsx
@@ -14,9 +14,8 @@ import { useSelector } from '../redux/store';
import Accordion from './Accordion';
import * as AppButton from './AppButton';
import { AriaControlGroup, AriaControlled, AriaControls } from './AriaGroup';
-import { Brand, HeaderBarSettingsButton } from './HeaderBar';
import ImageView from './ImageView';
-import { Container, Header, Layout } from './Layout';
+import { Container, Layout } from './Layout';
import {
StyledAccountDropdownContainer,
StyledAccountDropdownItem,
@@ -41,6 +40,7 @@ import {
StyledTitle,
StyledTopInfo,
} from './LoginStyles';
+import { MainHeader } from './main-header';
export default function LoginContainer() {
const { openUrl, login, clearAccountHistory, createNewAccount } = useAppContext();
@@ -126,10 +126,9 @@ class Login extends React.Component<IProps, IState> {
return (
<Layout>
- <Header>
- <Brand />
- <HeaderBarSettingsButton disabled={!allowInteraction} />
- </Header>
+ <MainHeader>
+ <MainHeader.SettingsButton disabled={!allowInteraction} />
+ </MainHeader>
<Container>
<StyledTopInfo>
{this.props.showBlockMessage ? <BlockMessage /> : this.getStatusIcon()}
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/TooManyDevices.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/TooManyDevices.tsx
index 42c5a61373..17adb8c0f2 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/TooManyDevices.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/TooManyDevices.tsx
@@ -17,10 +17,10 @@ import * as AppButton from './AppButton';
import * as Cell from './cell';
import { bigText, measurements, normalText, tinyText } from './common-styles';
import CustomScrollbars from './CustomScrollbars';
-import { Brand, HeaderBarSettingsButton } from './HeaderBar';
import ImageView from './ImageView';
-import { Footer, Header, Layout, SettingsContainer } from './Layout';
+import { Footer, Layout, SettingsContainer } from './Layout';
import List from './List';
+import { MainHeader } from './main-header';
import { ModalAlert, ModalAlertType, ModalContainer, ModalMessage } from './Modal';
const StyledCustomScrollbars = styled(CustomScrollbars)({
@@ -124,10 +124,9 @@ export default function TooManyDevices() {
return (
<ModalContainer>
<Layout>
- <Header>
- <Brand />
- <HeaderBarSettingsButton />
- </Header>
+ <MainHeader>
+ <MainHeader.SettingsButton />
+ </MainHeader>
<StyledCustomScrollbars fillContainer>
<StyledContainer>
<StyledBody>
diff --git a/desktop/packages/mullvad-vpn/src/renderer/components/main-view/MainView.tsx b/desktop/packages/mullvad-vpn/src/renderer/components/main-view/MainView.tsx
index 9327094a51..f658f37bb4 100644
--- a/desktop/packages/mullvad-vpn/src/renderer/components/main-view/MainView.tsx
+++ b/desktop/packages/mullvad-vpn/src/renderer/components/main-view/MainView.tsx
@@ -1,9 +1,9 @@
import styled from 'styled-components';
import { useSelector } from '../../redux/store';
-import { calculateHeaderBarStyle, DefaultHeaderBar } from '../HeaderBar';
import ImageView from '../ImageView';
import { Container, Layout } from '../Layout';
+import { MainHeader } from '../main-header';
import Map from '../Map';
import NotificationArea from '../NotificationArea';
import ConnectionPanel from './ConnectionPanel';
@@ -49,7 +49,10 @@ export default function MainView() {
return (
<Layout>
- <DefaultHeaderBar barStyle={calculateHeaderBarStyle(connection.status)} />
+ <MainHeader size="basedOnLoginStatus" variant="basedOnConnectionStatus">
+ <MainHeader.AccountButton />
+ <MainHeader.SettingsButton />
+ </MainHeader>
<StyledContainer>
<Map />
<Content>