Skip to content

Commit

Permalink
feat(whitelabel): use favicon loaded from configs as default for noti…
Browse files Browse the repository at this point in the history
…fications

* fix(whitelabel): load favicon and title also when request fails

- Set defaults for favicon and title on store.
- Create hook and getters for favicon, title and logo.
- Create a component for the logo which can be reused in different points of the project.
- Cleanup unused assets and move svgs inside assets folder.
- Prepare splash view to use dynamic logo component.
- Use png for the favicon in order to make it usable also for the notification system.
- Set favicon and title on the document even if the request fails, defaults will be used in case.
- Improve typing of svg assets module

* fix: relative path for favicon

* feat(whitelabel): use favicon loaded from configs as default for notifications

refs: SHELL-61 (#220)
  • Loading branch information
beawar authored Mar 13, 2023
1 parent 5504915 commit e2ffcf6
Show file tree
Hide file tree
Showing 16 changed files with 144 additions and 358 deletions.
44 changes: 0 additions & 44 deletions assets/carbonio-beta.svg

This file was deleted.

Binary file removed assets/carbonio-logo.png
Binary file not shown.
File renamed without changes
18 changes: 0 additions & 18 deletions assets/favicon.svg

This file was deleted.

5 changes: 4 additions & 1 deletion src/assets.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
declare module '*.svg';
declare module '*.svg' {
const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
export default content;
}
declare module '*.mp3';
declare module '*.png';
17 changes: 12 additions & 5 deletions src/boot/splash.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import '../splash.css';
import React, { FC } from 'react';
import Logo from '../svg/carbonio.svg';
import Helmet from '../svg/carbonio-head.svg';
import React from 'react';
import styled from 'styled-components';
import Helmet from '../../assets/carbonio-head.svg';
// TODO: change with import from logo component when ready
import Logo from '../../assets/carbonio.svg';
// import { Logo } from '../shell/logo';

const LoadingView: FC = () => (
const StyledLogo = styled(Logo)`
fill: #a3aebc;
width: 50%;
`;
const LoadingView = (): JSX.Element => (
<div className="splash">
<Helmet fill="#A3AEBC" />
<div className="loader">
<div className="bar"></div>
</div>
<Logo fill="#A3AEBC" width="50%" />
<StyledLogo />
</div>
);
export default LoadingView;
18 changes: 9 additions & 9 deletions src/network/login-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@
import { LoginConfigStore } from '../../types/loginConfig';
import { useLoginConfigStore } from '../store/login/store';
import { LOGIN_V3_CONFIG_PATH } from '../constants';
import { getClientTitle, getFavicon } from '../store/login/getters';

export const loginConfig = (): Promise<void> =>
fetch(LOGIN_V3_CONFIG_PATH)
.then((response) => response.json())
.then((data: LoginConfigStore) => {
useLoginConfigStore.setState(data);
const favicon = document.getElementById('favicon');
if (favicon && favicon instanceof HTMLLinkElement) {
favicon.href = data.carbonioWebUiFavicon
? data.carbonioWebUiFavicon
: `${BASE_PATH}favicon.svg`;
}
if (data.carbonioWebUiTitle) {
document.title = data.carbonioWebUiTitle;
}
})
.catch((reason) => {
console.warn(reason);
})
.finally(() => {
useLoginConfigStore.setState({ loaded: true });
const favicon = document.getElementById('favicon');
if (favicon && favicon instanceof HTMLLinkElement) {
favicon.href = getFavicon();
}
document.title = getClientTitle();
});
64 changes: 38 additions & 26 deletions src/notification/NotificationManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,14 @@
*/

import { debounce, noop } from 'lodash';
import {
NotificationConfig,
PopupNotificationConfig,
import type {
AudioNotificationConfig,
INotificationManager
} from '../../types/notification';
INotificationManager,
NotificationConfig,
PopupNotificationConfig
} from '../../types';
import defaultAudio from '../../assets/notification.mp3';
import defaultIcon from '../../assets/carbonio-logo.png';

const PopupNotificationDefaultConfig = {
title: 'Carbonio client',
icon: defaultIcon,
vibrate: [200, 100, 200]
};

const AudioNotificationDefaultConfig = {
sound: defaultAudio
};

const NotificationDefaultConfig: NotificationConfig = {
...PopupNotificationDefaultConfig,
...AudioNotificationDefaultConfig,
showPopup: true,
playSound: false
};
import { getFavicon } from '../store/login/getters';

/**
* The main goals of the NotificationManager are:
Expand All @@ -54,6 +37,35 @@ export class NotificationManager implements INotificationManager {
*/
private static DEBOUNCE_TIME = 1000;

/**
* Default configuration for the popup-only notification
* @private
*/
private PopupNotificationDefaultConfig = {
title: '',
vibrate: [200, 100, 200],
icon: getFavicon()
};

/**
* Default configuration for the audio-only notification
* @private
*/
private AudioNotificationDefaultConfig: AudioNotificationConfig = {
sound: defaultAudio
};

/**
* Default configuration for a notification with both popup and audio
* @private
*/
private NotificationDefaultConfig: NotificationConfig = {
...this.PopupNotificationDefaultConfig,
...this.AudioNotificationDefaultConfig,
showPopup: true,
playSound: false
};

/**
* Map of functions to play a specific audio file
* @private
Expand Down Expand Up @@ -85,7 +97,7 @@ export class NotificationManager implements INotificationManager {
*/
public playSound = (config: AudioNotificationConfig): void => {
const defConfig = {
...AudioNotificationDefaultConfig,
...this.AudioNotificationDefaultConfig,
...config
};
if (!defConfig.sound) {
Expand All @@ -102,7 +114,7 @@ export class NotificationManager implements INotificationManager {
*/
public showPopup = (config: PopupNotificationConfig): void => {
const defConfig = {
...PopupNotificationDefaultConfig,
...this.PopupNotificationDefaultConfig,
...config
};

Expand All @@ -125,7 +137,7 @@ export class NotificationManager implements INotificationManager {
*/
public notify = (config: NotificationConfig): void => {
const defConfig = {
...NotificationDefaultConfig,
...this.NotificationDefaultConfig,
...config
};

Expand Down
21 changes: 21 additions & 0 deletions src/shell/logo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* SPDX-FileCopyrightText: 2023 Zextras <https://www.zextras.com>
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React from 'react';
import { useLogo } from '../store/login/hooks';
import { useLoginConfigStore } from '../store/login/store';

export const Logo = (props: Record<string, unknown>): JSX.Element => {
const { loaded } = useLoginConfigStore();
const LogoElement = useLogo();

return loaded ? (
(typeof LogoElement === 'string' && <img alt={''} {...props} src={LogoElement} />) || (
<LogoElement {...props} />
)
) : (
<></>
);
};
44 changes: 14 additions & 30 deletions src/shell/shell-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/

import React, { FC, useMemo } from 'react';
import React from 'react';
import {
Catcher,
Container,
Expand All @@ -14,39 +14,36 @@ import {
useScreenMode
} from '@zextras/carbonio-design-system';
import styled from 'styled-components';
import Logo from '../svg/carbonio.svg';
import { SearchBar } from '../search/search-bar';
import { CreationButton } from './creation-button';
import { useAppStore } from '../store/app';
import { useLoginConfigStore } from '../store/login/store';
import { useDarkMode } from '../dark-mode/use-dark-mode';
import { Logo } from './logo';

const CustomImg = styled.img`
const StyledLogo = styled(Logo)`
height: 2rem;
`;

const ShellHeader: FC<{
interface ShellHeaderProps {
mobileNavIsOpen: boolean;
onMobileMenuClick: () => void;
}> = ({ mobileNavIsOpen, onMobileMenuClick, children }) => {
const { carbonioWebUiAppLogo, carbonioWebUiDarkAppLogo } = useLoginConfigStore();
children: React.ReactNode | React.ReactNode[];
}

const { darkModeEnabled, darkReaderStatus } = useDarkMode();

const logoSrc = useMemo(() => {
if (darkModeEnabled) {
return carbonioWebUiDarkAppLogo || carbonioWebUiAppLogo;
}
return carbonioWebUiAppLogo || carbonioWebUiDarkAppLogo;
}, [carbonioWebUiDarkAppLogo, carbonioWebUiAppLogo, darkModeEnabled]);
const ShellHeader = ({
mobileNavIsOpen,
onMobileMenuClick,
children
}: ShellHeaderProps): JSX.Element => {
const { darkReaderStatus } = useDarkMode();

const screenMode = useScreenMode();
const searchEnabled = useAppStore((s) => s.views.search.length > 0);
return (
<Container
data-testid="MainHeaderContainer"
orientation="horizontal"
background="gray3"
background={'gray3'}
width="fill"
height="3.75rem"
minHeight="3.75rem"
Expand All @@ -71,9 +68,7 @@ const ShellHeader: FC<{
</Padding>
</Responsive>
<Container width="15.625rem" height="2rem" crossAlignment="flex-start">
{darkReaderStatus && (
<>{logoSrc ? <CustomImg src={logoSrc} /> : <Logo height="2rem" />}</>
)}
{darkReaderStatus && <StyledLogo />}
</Container>
<Padding horizontal="large">
<CreationButton />
Expand All @@ -82,17 +77,6 @@ const ShellHeader: FC<{
</Container>
<Container orientation="horizontal" width="auto" mainAlignment="flex-end">
<Responsive mode="desktop">{children}</Responsive>
<Responsive mode="mobile">
<Container
orientation="horizontal"
mainAlignment="flex-end"
padding={{ right: 'extrasmall' }}
>
{/* <Dropdown items={secondaryActions} placement="bottom-start">
<IconButton icon="Plus" />
</Dropdown> */}
</Container>
</Responsive>
</Container>
</Catcher>
</Container>
Expand Down
14 changes: 14 additions & 0 deletions src/store/login/getters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* SPDX-FileCopyrightText: 2023 Zextras <https://www.zextras.com>
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useLoginConfigStore } from './store';

export function getFavicon(): string {
return useLoginConfigStore.getState().carbonioWebUiFavicon;
}

export function getClientTitle(): string {
return useLoginConfigStore.getState().carbonioWebUiTitle;
}
22 changes: 22 additions & 0 deletions src/store/login/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* SPDX-FileCopyrightText: 2023 Zextras <https://www.zextras.com>
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { useMemo } from 'react';
import { useLoginConfigStore } from './store';
import { useDarkMode } from '../../dark-mode/use-dark-mode';
import DefaultLogo from '../../../assets/carbonio.svg';

export function useLogo(): string | React.ComponentType {
const { carbonioWebUiAppLogo, carbonioWebUiDarkAppLogo } = useLoginConfigStore();

const { darkModeEnabled } = useDarkMode();

return useMemo(() => {
if (darkModeEnabled) {
return carbonioWebUiDarkAppLogo || carbonioWebUiAppLogo || DefaultLogo;
}
return carbonioWebUiAppLogo || carbonioWebUiDarkAppLogo || DefaultLogo;
}, [carbonioWebUiDarkAppLogo, carbonioWebUiAppLogo, darkModeEnabled]);
}
8 changes: 7 additions & 1 deletion src/store/login/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,10 @@
import create from 'zustand';
import { LoginConfigStore } from '../../../types/loginConfig';

export const useLoginConfigStore = create<LoginConfigStore>(() => ({}));
export const useLoginConfigStore = create<LoginConfigStore>(() => ({
loaded: false,
// setup defaults for fields which does not depend on dark mode
carbonioWebUiTitle: 'Carbonio Client',
// default to png because this icon is used also in notification, and svg are not supported there
carbonioWebUiFavicon: `${BASE_PATH}favicon.png`
}));
Loading

0 comments on commit e2ffcf6

Please sign in to comment.