feat: SDA-1991 (Redesign Notifications) (#997)

* SDA-1991 - Redesign notification

* SDA-1991 - Include 1.5 default notification color

* SDA-1991 - Support custom notifications for Linux

* SDA-1991 - Turn off ext badge by default

* SDA-1991 - Hide close button by default
This commit is contained in:
Kiran Niranjan
2020-05-15 19:20:29 +05:30
committed by GitHub
parent 1d0818490a
commit 401120dbea
6 changed files with 187 additions and 56 deletions
+4
View File
@@ -111,6 +111,8 @@ export enum KeyCodes {
Alt = 18,
}
type Theme = '' | 'light' | 'dark';
/**
* Notification
*/
@@ -125,6 +127,8 @@ export interface INotificationData {
sticky: boolean;
company: string;
displayTime: number;
isExternal: boolean;
theme: Theme;
}
export enum NotificationActions {
+16 -2
View File
@@ -73,13 +73,24 @@
<label for='flash'>flash:</label>
<input type='checkbox' id='flash'/>
</p>
<p>
<label for='isExternal'>external:</label>
<input type='checkbox' id='isExternal'/>
</p>
<p>Change theme:<p>
<select id="select-theme" name="theme">
<option value="">select</option>
<option value="dark">dark</option>
<option value="light">light</option>
</select>
<br>
<p>
<label for='sticky'>sticky:</label>
<input type='checkbox' id='sticky'/>
</p>
<p>
<label for='color'>color:</label>
<input type='text' id='color' value='white'/>
<input type='text' id='color' value='#FFFFFF'/>
</p>
<p>
<label for='tag'>tag:</label>
@@ -262,6 +273,7 @@
var body = document.getElementById('body').value;
var imageUrl = document.getElementById('image').value;
var shouldFlash = document.getElementById('flash').checked;
var isExternal = document.getElementById('isExternal').checked;
var shouldStick = document.getElementById('sticky').checked;
var color = document.getElementById('color').value;
var tag = document.getElementById('tag').value;
@@ -269,6 +281,7 @@
num++;
var theme = document.getElementById('select-theme').value
var notf = {
id: num,
title,
@@ -277,11 +290,12 @@
flash: shouldFlash,
color: color || 'white',
sticky: shouldStick,
isExternal: isExternal,
theme: theme,
data: {
hello: `Notification with id ${num} clicked')`
},
tag: tag,
company: company,
method: 'notification',
};
+86 -14
View File
@@ -1,10 +1,11 @@
import classNames from 'classnames';
import { ipcRenderer } from 'electron';
import * as React from 'react';
import { i18n } from '../../common/i18n-preload';
const whiteColorRegExp = new RegExp(/^(?:white|#fff(?:fff)?|rgba?\(\s*255\s*,\s*255\s*,\s*255\s*(?:,\s*1\s*)?\))$/i);
const darkTheme = [ '#e23030', '#b5616a', '#ab8ead', '#ebc875', '#a3be77', '#58c6ff', '#ebab58' ];
type Theme = '' | 'light' | 'dark';
interface IState {
title: string;
@@ -15,6 +16,8 @@ interface IState {
id: number;
color: string;
flash: boolean;
isExternal: boolean;
theme: Theme;
}
type mouseEventButton = React.MouseEvent<HTMLDivElement>;
@@ -41,6 +44,8 @@ export default class NotificationComp extends React.Component<{}, IState> {
id: 0,
color: '',
flash: false,
isExternal: false,
theme: '',
};
this.updateState = this.updateState.bind(this);
}
@@ -58,10 +63,16 @@ export default class NotificationComp extends React.Component<{}, IState> {
* Renders the custom title bar
*/
public render(): JSX.Element {
const { title, company, body, image, icon, id, color } = this.state;
const isLightTheme = (color && color.match(whiteColorRegExp)) || false;
const { title, body, image, icon, id, color, isExternal, theme } = this.state;
let themeClassName;
if (theme) {
themeClassName = theme;
} else if (darkTheme.includes(color.toLowerCase())) {
themeClassName = 'light';
} else {
themeClassName = color && color.match(whiteColorRegExp) ? 'light' : 'dark';
}
const theme = classNames({ light: isLightTheme, dark: !isLightTheme });
const bgColor = { backgroundColor: color || '#ffffff' };
return (
@@ -73,23 +84,84 @@ export default class NotificationComp extends React.Component<{}, IState> {
onMouseEnter={this.eventHandlers.onMouseEnter(id)}
onMouseLeave={this.eventHandlers.onMouseLeave(id)}
>
{isExternal ? <div className='ext-border'/> : null}
<div className='logo-container'>
<img className={`logo ${theme}`} alt='symphony logo'/>
<div className='logo'>
<svg width='40' height='40' viewBox='0 0 40 40' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path d='M0 20C0 12.1746 0 8.26188 1.80534 5.41094C2.72586 3.95728 3.95728 2.72586 5.41094 1.80534C8.26188 0 12.1746 0 20 0C27.8254 0 31.7381 0 34.5891 1.80534C36.0427 2.72586 37.2741 3.95728 38.1947 5.41094C40 8.26188 40 12.1746 40 20C40 27.8254 40 31.7381 38.1947 34.5891C37.2741 36.0427 36.0427 37.2741 34.5891 38.1947C31.7381 40 27.8254 40 20 40C12.1746 40 8.26188 40 5.41094 38.1947C3.95728 37.2741 2.72586 36.0427 1.80534 34.5891C0 31.7381 0 27.8254 0 20Z' fill='#000028'/>
<path d='M0 20C0 12.1746 0 8.26188 1.80534 5.41094C2.72586 3.95728 3.95728 2.72586 5.41094 1.80534C8.26188 0 12.1746 0 20 0C27.8254 0 31.7381 0 34.5891 1.80534C36.0427 2.72586 37.2741 3.95728 38.1947 5.41094C40 8.26188 40 12.1746 40 20C40 27.8254 40 31.7381 38.1947 34.5891C37.2741 36.0427 36.0427 37.2741 34.5891 38.1947C31.7381 40 27.8254 40 20 40C12.1746 40 8.26188 40 5.41094 38.1947C3.95728 37.2741 2.72586 36.0427 1.80534 34.5891C0 31.7381 0 27.8254 0 20Z' fill='url(#paint0_linear)'/>
<path d='M28 17.1029V13.4094C28 12.6528 27.56 11.9425 26.8467 11.5534C25.7833 10.9728 23.48 10 20 10C16.52 10 14.2167 10.9728 13.1533 11.5565C12.44 11.9425 12 12.6528 12 13.4094V18.9559L24.6667 22.3529V24.8235C24.6667 25.1571 24.44 25.3918 24.0533 25.5678L20 27.4485L15.9233 25.5585C15.56 25.3918 15.3333 25.1571 15.3333 24.8235V22.9706L12 22.0441V24.8235C12 26.3491 12.9433 27.6462 14.4433 28.3225L20 31L25.5333 28.3349C27.0567 27.6462 28 26.3491 28 24.8235V20.1912L15.3333 16.7941V13.9746C16.24 13.57 17.78 13.0882 20 13.0882C22.22 13.0882 23.76 13.57 24.6667 13.9746V16.1765L28 17.1029Z' fill='#0098FF'/>
<path d='M28 17.1029V13.4094C28 12.6528 27.56 11.9425 26.8467 11.5534C25.7833 10.9728 23.48 10 20 10C16.52 10 14.2167 10.9728 13.1533 11.5565C12.44 11.9425 12 12.6528 12 13.4094V18.9559L24.6667 22.3529V24.8235C24.6667 25.1571 24.44 25.3918 24.0533 25.5678L20 27.4485L15.9233 25.5585C15.56 25.3918 15.3333 25.1571 15.3333 24.8235V22.9706L12 22.0441V24.8235C12 26.3491 12.9433 27.6462 14.4433 28.3225L20 31L25.5333 28.3349C27.0567 27.6462 28 26.3491 28 24.8235V20.1912L15.3333 16.7941V13.9746C16.24 13.57 17.78 13.0882 20 13.0882C22.22 13.0882 23.76 13.57 24.6667 13.9746V16.1765L28 17.1029Z' fill='url(#paint1_radial)'/>
<defs>
<linearGradient id='paint0_linear' x1='20' y1='0' x2='20' y2='40' gradientUnits='userSpaceOnUse'>
<stop stopColor='white' stopOpacity='0.2'/>
<stop offset='1' stopColor='white' stopOpacity='0'/>
</linearGradient>
<radialGradient id='paint1_radial' cx='0' cy='0' r='1' gradientUnits='userSpaceOnUse' gradientTransform='translate(20.0278 10) rotate(90) scale(14.2187 20.1481)'>
<stop stopColor='white' stopOpacity='0.4'/>
<stop offset='1' stopColor='white' stopOpacity='0'/>
</radialGradient>
</defs>
</svg>
</div>
</div>
<div className='header'>
<span className={`title ${theme}`}>{title}</span>
<span className='company' style={{color: color || '#4a4a4a'}}>{company}</span>
<span className={`message ${theme}`}>{body}</span>
<div className='title-container'>
<span className={`title ${themeClassName}`}>{title}</span>
{this.renderExtBadge(isExternal)}
</div>
<span className={`message ${themeClassName}`}>{body}</span>
</div>
{this.renderProfile(icon, image, title)}
<div className='close' title={i18n.t('Close')()} onClick={this.eventHandlers.onClose(id)}>
<svg width='8' height='8' viewBox='0 0 8 8' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path d='M1.35355 0.646447C1.15829 0.451184 0.841709 0.451184 0.646447 0.646447C0.451184 0.841709 0.451184 1.15829 0.646447 1.35355L3.29289 4L0.646447 6.64645C0.451185 6.84171 0.451185 7.15829 0.646447 7.35356C0.841709 7.54882 1.15829 7.54882 1.35355 7.35356L4 4.70711L6.64645 7.35355C6.84171 7.54882 7.15829 7.54882 7.35355 7.35355C7.54882 7.15829 7.54882 6.84171 7.35355 6.64645L4.70711 4L7.35355 1.35356C7.54882 1.15829 7.54882 0.84171 7.35355 0.646448C7.15829 0.451186 6.84171 0.451186 6.64645 0.646448L4 3.29289L1.35355 0.646447Z' fill='#525760'/>
<path d='M1.35355 0.646447C1.15829 0.451184 0.841709 0.451184 0.646447 0.646447C0.451184 0.841709 0.451184 1.15829 0.646447 1.35355L3.29289 4L0.646447 6.64645C0.451185 6.84171 0.451185 7.15829 0.646447 7.35356C0.841709 7.54882 1.15829 7.54882 1.35355 7.35356L4 4.70711L6.64645 7.35355C6.84171 7.54882 7.15829 7.54882 7.35355 7.35355C7.54882 7.15829 7.54882 6.84171 7.35355 6.64645L4.70711 4L7.35355 1.35356C7.54882 1.15829 7.54882 0.84171 7.35355 0.646448C7.15829 0.451186 6.84171 0.451186 6.64645 0.646448L4 3.29289L1.35355 0.646447Z' fill='white' fill-opacity='0.96'/>
</svg>
</div>
</div>
);
}
/**
* Renders external badge if the content is from external
* @param isExternal
*/
private renderExtBadge(isExternal: boolean): JSX.Element | undefined {
if (!isExternal) {
return;
}
return (
<div className='ext-badge-container'>
<svg width='32' height='16' viewBox='0 0 32 16' fill='none' xmlns='http://www.w3.org/2000/svg'>
<rect width='32' height='16' rx='8' fill='#F6B202'/>
<rect width='32' height='16' rx='8' fill='white' fill-opacity='0.24'/>
<path d='M11.4414 13H6.72461V4.59766H11.2539V5.78125H8.11914V8.16016H11.0078V9.33789H8.11914V11.8223H11.4414V13ZM19.3574 13H17.6875L15.9648 9.91797C15.9141 9.82422 15.8574 9.69141 15.7949 9.51953H15.7715C15.7363 9.60547 15.6777 9.73828 15.5957 9.91797L13.8203 13H12.1387L14.8926 8.77539L12.3613 4.59766H14.0664L15.584 7.43359C15.6816 7.62109 15.7695 7.80859 15.8477 7.99609H15.8652C15.9785 7.75 16.0762 7.55469 16.1582 7.41016L17.7344 4.59766H19.3047L16.7148 8.76367L19.3574 13ZM26.1191 5.78125H23.7051V13H22.3105V5.78125H19.9023V4.59766H26.1191V5.78125Z' fill='#525760'/>
<path d='M11.4414 13H6.72461V4.59766H11.2539V5.78125H8.11914V8.16016H11.0078V9.33789H8.11914V11.8223H11.4414V13ZM19.3574 13H17.6875L15.9648 9.91797C15.9141 9.82422 15.8574 9.69141 15.7949 9.51953H15.7715C15.7363 9.60547 15.6777 9.73828 15.5957 9.91797L13.8203 13H12.1387L14.8926 8.77539L12.3613 4.59766H14.0664L15.584 7.43359C15.6816 7.62109 15.7695 7.80859 15.8477 7.99609H15.8652C15.9785 7.75 16.0762 7.55469 16.1582 7.41016L17.7344 4.59766H19.3047L16.7148 8.76367L19.3574 13ZM26.1191 5.78125H23.7051V13H22.3105V5.78125H19.9023V4.59766H26.1191V5.78125Z' fill='black' fill-opacity='0.24'/>
</svg>
</div>
);
}
/**
* Renders user profile image if present else use
* the first char of the notification title
*
* @param icon
* @param image
* @param title
*/
private renderProfile(icon: string, image: string, title: string): JSX.Element {
if (icon || image) {
return (
<div className='user-profile-pic-container'>
<img src={image || icon || '../renderer/assets/symphony-default-profile-pic.png'} className='user-profile-pic' alt='user profile picture'/>
</div>
<div className='close' title={i18n.t('Close')()} onClick={this.eventHandlers.onClose(id)}>
<svg fill='#000000' height='16' viewBox='0 0 24 24' width='16' xmlns='http://www.w3.org/2000/svg'>
<path d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/>
<path d='M0 0h24v24H0z' fill='none'/>
</svg>
</div>
);
}
return (
<div className='user-name-text-container'>
<span className='user-name-text'>{title.substr(0, 1)}</span>
</div>
);
}
+4 -4
View File
@@ -64,8 +64,8 @@ export default class NotificationHandler {
* Initializes / resets the notification positional values
*/
public setupNotificationPosition() {
// This feature only applies to windows
if (isMac || isLinux || !app.isReady()) {
// This feature only applies to windows & mac
if (!app.isReady()) {
return;
}
@@ -78,8 +78,8 @@ export default class NotificationHandler {
}
const display = this.externalDisplay || electron.screen.getPrimaryDisplay();
this.settings.corner.x = display.workArea.x;
this.settings.corner.y = display.workArea.y;
this.settings.corner.x = display.workArea.x - (isMac || isLinux ? 20 : 10);
this.settings.corner.y = display.workArea.y + (isMac || isLinux ? 20 : 10);
// update corner x/y based on corner of screen where notification should appear
const workAreaWidth = display.workAreaSize.width;
+5 -5
View File
@@ -24,8 +24,8 @@ type startCorner = 'upper-right' | 'upper-left' | 'lower-right' | 'lower-left';
const notificationSettings = {
startCorner: 'upper-right' as startCorner,
display: '',
width: 380,
height: 100,
width: 344,
height: 64,
totalHeight: 0,
totalWidth: 0,
corner: {
@@ -38,7 +38,7 @@ const notificationSettings = {
},
templatePath: '',
maxVisibleNotifications: 6,
borderRadius: 5,
borderRadius: 8,
displayTime: 5000,
animationSteps: 5,
animationStepMs: 5,
@@ -436,8 +436,8 @@ class Notification extends NotificationHandler {
*/
private getNotificationOpts(): Electron.BrowserWindowConstructorOptions {
return {
width: 380,
height: 100,
width: 344,
height: 64,
alwaysOnTop: true,
skipTaskbar: true,
resizable: true,
+72 -31
View File
@@ -1,12 +1,12 @@
@import "theme";
.light {
--text-color: #4a4a4a;
--logo-bg: url('../assets/symphony-logo-black.png');
--text-color: #525760;
--logo-bg: url('../assets/symphony-logo.png');
}
.dark {
--text-color: #000000;
--logo-bg: url('../assets/symphony-logo-white.png');
--text-color: #FFFFFF;
--logo-bg: url('../assets/symphony-logo.png');
}
body {
@@ -17,8 +17,8 @@ body {
}
.container {
width: 380px;
height: 100px;
width: 344px;
height: 64px;
display: flex;
justify-content: center;
background-color: #ffffff;
@@ -26,52 +26,94 @@ body {
position: relative;
line-height: 15px;
box-sizing: border-box;
border-radius: 5px;
border-radius: 8px;
&:hover .close {
visibility: visible;
}
}
.ext-border {
border: 2px solid #f6b202;
border-radius: 8px;
width: 340px;
height: 60px;
position: absolute;
}
.header {
width: 245px;
min-width: 230px;
width: 232px;
min-width: 215px;
margin: auto;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.title-container {
display: flex;
}
.user-profile-pic-container {
align-items: center;
display: flex;
}
.user-profile-pic {
height: 43px;
.user-name-text-container {
display: flex;
height: 40px;
border-radius: 4px;
width: 43px;
border: 1px solid rgba(82, 87, 96, .32);
margin: 12px;
width: 40px;
background: #FFFFFF;
}
.user-name-text {
font-size: 14px;
line-height: 24px;
align-items: center;
margin: auto;
text-align: center;
color: #008EFF;
}
.ext-badge-container {
height: 16px;
width: 32px;
margin: auto 8px;
align-items: center;
}
.user-profile-pic {
height: 40px;
border-radius: 50%;
width: 40px;
margin: 12px;
}
.close {
width: 16px;
height: 80px;
display: flex;
margin: auto;
opacity: 0.54;
font-size: 12px;
color: #CCC;
cursor: pointer;
position: absolute;
right: 0;
visibility: hidden;
padding: 0 5.5px 5.5px 5.5px;
}
.title {
font-family: sans-serif;
font-weight: 700;
width: 100%;
font-weight: 600;
width: auto;
max-width: 175px;
overflow-wrap: break-word;
font-size: 14px;
font-style: normal;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 1;
white-space: nowrap;
text-overflow: ellipsis;
line-height: 24px;
-webkit-box-orient: vertical;
cursor: default;
text-overflow: ellipsis;
color: var(--text-color);
}
@@ -88,13 +130,13 @@ body {
.message {
font-family: sans-serif;
width: 100%;
width: 220px;
overflow-wrap: break-word;
font-size: 12px;
margin-top: 5px;
line-height: 16px;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 3;
white-space: nowrap;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
cursor: default;
text-overflow: ellipsis;
@@ -102,13 +144,12 @@ body {
}
.logo-container {
margin: 10px;
display: flex;
align-items: center;
}
.logo {
margin-left: 5px;
opacity: 0.6;
width: 43px;
width: 40px;
content: var(--logo-bg);
}