Notify — Electron компонент, элемент представления уведомлений.
Структура папок.
index.js — Файл, в котором создается Electron компонент.
Cодержимое файла index.js.
fly.html — HTML страница окна уведомлений типа «fly».
Cодержимое файла fly.html.
static.html — HTML страница окна уведомлений типа «static».
Cодержимое файла static.html.
style.css — Стили для страниц.
Cодержимое файла style.css
Application
Использование в приложении: /app/Notify.
API
Интерфейс компонента Notify. В этом примере уже более явно должна просматриваться та организация кода, о которой я говорил.
Test
Версия для тестирования: /app_test/Notify.
Типы уведомлений Notify.setType.
7 часть — Context компонент
Структура папок.
notify
¦
¦ index.js
¦
L---client // Все что относится к клиенту
¦ fly.html
¦ static.html
¦ style.css
¦
L---fonts
font1.woff2
font2.woff2
font3.woff2
font4.woff2
font5.woff2
font6.woff2
font7.woff2
index.js — Файл, в котором создается Electron компонент.
Cодержимое файла index.js.
Код
const { BrowserWindow, ipcMain } = require('electron')
module.exports = class Notification {
constructor(parent) {
const window_tmp = {
frame: false, // Убираем рамку
transparent: true, // Устанавливаем прозрачность
resizable: false, // Запрещаем масштабирование
show: false, // Запрещаем показывать окно после загрузки
focusable: true, // Окно может принимать фокус
height: 175,
width: 200,
maxWidth: 200,
parent
}
// Создаем окна
this.root = new BrowserWindow(window_tmp)
this.root2 = new BrowserWindow(window_tmp)
// Уведомление порящее над основным окном
this.root.loadURL(`${__dirname}/client/fly.html`)
// Статическое уведомление
this.root2.loadURL(`${__dirname}/client/static.html`)
// Тип уведомлений используемый по дефолту
this.type = 'fly'
// Ожидается ли ответ на уведомление confirm если
// ожидается то он не может быть скрыт обычным alert
this.waitingConfirm = 0
}
ready() {
// Ожидаем пока окна полностью инициализируются
return Promise.all([
new Promise(res => {
this.root.once('ready-to-show', res)
}),
new Promise(res => {
this.root2.once('ready-to-show', res)
})
])
}
setType(value) {
// Метод изменяющий тип уведомления
this.hide()
this.type = value
}
hide() {
// Скрываем оба окна
this.root.hide()
this.root2.hide()
}
alert(message = 'message', hideTime = 6000, callback = () => {}) {
// Если есть активный confirm пропускаем
if (this.waitingConfirm) {
return
}
// Удаляем старый таймер
clearTimeout(this.t)
// Скрываем старое уведомление
this.hide()
// В зависимости от типа уведомлений выбираем к какому окну обращаться
const ROOT = this.type == 'fly' ? 'root' : 'root2'
// Сигнал ALERT и сообщение на страницу
this[ROOT].webContents.send('ALERT', message)
// Показываем окно
this[ROOT].show()
// Снимаем фокус с окна
this[ROOT].blur()
// Окно ROOT будет скрыто через hideTime миллисекунд и вызван callback
this.t = setTimeout(() => {
this[ROOT].hide()
callback()
}, hideTime)
// Если из окна поступает сигнал ALERT_HIDE то окно будет
// скрыто, а таймер удален ну и вызван callback
ipcMain.once('ALERT_HIDE', () => {
clearTimeout(this.t)
this[ROOT].hide()
callback()
})
}
confirm(message, button, callback) {
// Удаляем старый таймер
clearTimeout(this.t)
// Ставим блок
this.waitingConfirm = 1
// Скрываем старое уведомление
this.hide()
// В зависимости от типа уведомлений выбираем к какому окну обращаться
const ROOT = this.type == 'fly' ? 'root' : 'root2'
// Показываем окно
this[ROOT].show()
// Снимаем фокус с окна
this[ROOT].blur()
// Сигнал ALERT и сообщение на страницу и кнопки
this[ROOT].webContents.send('CONFIRM', JSON.stringify({
message, button
}))
// Если из окна поступает сигнал CONFIRM_CALLBACK то окно будет
// скрыто и вызван callback в который будет передана data
// информация о той кнопке которая выбрана
ipcMain.once('CONFIRM_CALLBACK', (e, data) => {
// Снимаем блокировку
this.waitingConfirm = 0
try {
this[ROOT].hide()
callback(data)
} catch (e) {}
})
}
}
fly.html — HTML страница окна уведомлений типа «fly».
Cодержимое файла fly.html.
Код
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<link rel='stylesheet' type='text/css' href='style.css'>
</head>
<body>
<div class='body'>
<div class='message-box'>
<div class='message'>
<div class='text'>Загрузка ...</div>
<div class='other'>
</div>
</div>
<div class='vector-box'>
<div class='vector'></div>
</div>
</div>
</div>
<script>
const Text = document.getElementsByClassName('text')[0]
, Other = document.getElementsByClassName('other')[0]
, { ipcRenderer, remote } = require('electron')
// Получаем контроль над окном
const win = remote.getCurrentWindow()
// Устанавливаем позицию окна основываясь на позиции окна vpn
setInterval(() => {
win.setPosition(
parseInt(localStorage.x - 43),
parseInt(localStorage.y - 150)
)
}, 3)
// Обработчик сигналов от компонента типа "alert"
ipcRenderer.on('ALERT', (e, data) => {
Text.innerHTML = data
Other.innerHTML = ''
})
// Обработчик сигналов от компонента типа "confirm"
ipcRenderer.on('CONFIRM', (e, data) => {
const {
message, button
} = JSON.parse(data)
Text.innerHTML = message
Other.innerHTML = ''
// Рендерим кнопки
button.forEach(elem => {
const btn = document.createElement('div')
btn.className = 'btn'
btn.innerHTML = elem.title
btn.onclick = e => {
ipcRenderer.send('CONFIRM_CALLBACK', elem.return)
e.stopPropagation()
}
Other.appendChild(btn)
})
})
// Отправляем компоненту сигнал что уведомление нужно скрыть
document.body.addEventListener('click', () => {
ipcRenderer.send('ALERT_HIDE')
})
</script>
</body>
</html>
static.html — HTML страница окна уведомлений типа «static».
Cодержимое файла static.html.
Код
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<link rel='stylesheet' type='text/css' href='style.css'>
</head>
<body>
<div class='body'>
<div class='message-box'>
<div class='message'>
<div class='text'>Загрузка ...</div>
<div class='other'>
</div>
</div>
</div>
</div>
<script>
const Text = document.getElementsByClassName('text')[0]
, Other = document.getElementsByClassName('other')[0]
, Body = document.getElementsByClassName('body')[0]
, { ipcRenderer, remote } = require('electron')
// Получаем контроль над окном
const win = remote.getCurrentWindow()
// Отступы от окна
const padding = 13
setInterval(() => {
// Получаем расположение уведомлений
const static_notify = JSON.parse(localStorage.vpn_setting || "{}").PositionNotify || 'BR'
const quickLaunch_Width = (window.screen.width - window.screen.availWidth),
quickLaunch_Height = (window.screen.height - window.screen.availHeight),
screen_Width = window.screen.width,
screen_Height = window.screen.height,
notify_Width = 200,
notify_Heigth = 175
// Низ право
if (static_notify == 'BR') {
win.setPosition(
(screen_Width - quickLaunch_Width) - (notify_Width + padding), (screen_Height - quickLaunch_Height) - (notify_Heigth + padding)
)
Body.style.alignItems = 'flex-end'
Body.style.justifyContent = 'flex-end'
}
// Верх право
if (static_notify == 'TR') {
win.setPosition((screen_Width - quickLaunch_Width) - (notify_Width + padding), quickLaunch_Height + padding)
Body.style.alignItems = 'flex-end'
Body.style.justifyContent = 'flex-start'
}
// Верх лево
if (static_notify == 'TL') {
win.setPosition(quickLaunch_Width + padding, quickLaunch_Height + padding)
Body.style.alignItems = 'flex-start'
Body.style.justifyContent = 'flex-start'
}
// Низ лево
if (static_notify == 'BL') {
win.setPosition(quickLaunch_Width + padding, window.screen.availHeight - 188)
Body.style.alignItems = 'flex-start'
Body.style.justifyContent = 'flex-end'
}
}, 100)
// Обработчик сигналов от компонента типа "alert"
ipcRenderer.on('ALERT', (e, data) => {
Text.innerHTML = data
Other.innerHTML = ''
})
// Обработчик сигналов от компонента типа "confirm"
ipcRenderer.on('CONFIRM', (e, data) => {
const {
message, button
} = JSON.parse(data)
Text.innerHTML = message
Other.innerHTML = ''
// Рендерим кнопки
button.forEach(elem => {
const btn = document.createElement('div')
btn.className = 'btn'
btn.innerHTML = elem.title
btn.onclick = e => {
ipcRenderer.send('CONFIRM_CALLBACK', elem.return)
e.stopPropagation()
}
Other.appendChild(btn)
})
})
// Отправляем компоненту что уведомление нужно скрыть
document.body.addEventListener('click', () => {
ipcRenderer.send('ALERT_HIDE')
})
</script>
</body>
</html>
style.css — Стили для страниц.
Cодержимое файла style.css
Код
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(fonts/font1.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(fonts/font2.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(fonts/font3.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(fonts/font4.woff2) format('woff2');
unicode-range: U+0370-03FF;
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(fonts/font5.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(fonts/font6.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(fonts/font7.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
* {
font-family: 'Source Sans Pro', sans-serif;
padding: 0;
margin: 0;
overflow: hidden;
background: rgba(0, 0, 0, 0);
}
.body {
display: flex;
justify-content: flex-end;
align-items: center;
flex-direction: column;
height: 175px;
}
.message-box {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.message {
border-radius: 10px;
font-size: 14px;
background: rgba(36, 39, 39, 0.9);
padding: 10px 10px 10px 10px;
color: #eee;
width: auto;
max-width: 180px;
min-width: 40px;
}
.other {
display: flex;
justify-content: space-between;
align-items: center;
}
.btn {
min-width: 35px;
width: 100%;
color: #dcdcdc;
background: #7b7b7b;
margin: 8px 2px 0px 2px;
padding: 3px;
font-size: 13px;
border-radius: 3px;
text-align: center;
user-select: none;
cursor: pointer;
}
.button:hover {
background: #e6e6e6;
background: #868686;
}
input {
cursor: pointer;
border: none;
border-radius: 6px;
padding: 4px 10px 4px 10px;
margin: 2px;
color: #eee;
outline: none;
background: rgba(51, 55, 55, 0.9);
}
input:hover {
color: #ccc
}
.vector {
width: 30px;
height: 30px;
background: rgba(36, 39, 39, 0.9);
transform: rotateZ(45deg);
border-radius: 5px;
}
.vector-box {
padding: 4px;
margin-top: 0px;
height: 8px;
overflow: hidden;
position: relative;
z-index: 0;
transform: rotateZ(180deg);
}
Application
Использование в приложении: /app/Notify.
API
Интерфейс компонента Notify. В этом примере уже более явно должна просматриваться та организация кода, о которой я говорил.
const { app } = require('electron')
, VPN = require('./../../app/components/vpn')
, NOTIFY = require('./../../app/components/notify')
app.on('ready', async() => {
const Vpn = new VPN()
, Notify = new NOTIFY(Vpn.root)
// Только после того как окна инициализируются программа продолжит исполнятся
await Promise.all([
Notify.ready(),
Vpn.ready()
])
Vpn.show()
Vpn.showTray()
const CONFIRN_FSB = [{
title: 'Да',
return: 'Да, подключайте меня!'
}, {
title: 'Нет',
return: false
}]
setTimeout(() => {
// Уведомление типа "alert"
Notify.alert('Прогресс не остановить!', 3000, () => {
// Уведомление типа "confirm"
Notify.confirm('Вы хотите подключиться к VPN', CONFIRN_FSB, data => {
console.log(data)
if (data == false) {
// Устанавливает тип уведомления
Notify.setType('static')
// Уведомление типа "confirm"
Notify.confirm('Вы хотите выйти ?', CONFIRN_FSB, data => {
if (data != false) {
app.quit()
}
})
} else {
Vpn.setStatus('resolve', 'blue')
}
})
})
}, 2000)
// Устанавливает тип уведомления
// Notify.setType('static')
// Уведомление типа "alert"
// Notify.alert('Прогресс не остановить!', 3000, () => {})
// Уведомление типа "confirm"
//Notify.confirm('Вы хотите подключиться к VPN', CONFIRN_FSB, console.log)
})
Test
Версия для тестирования: /app_test/Notify.
Типы уведомлений Notify.setType.
Тип | Описание |
---|---|
static | Уведомление, всплывающее в одной из четырех сторон экрана |
fly | Уведомление, всплывающее всегда над основным окном |
7 часть — Context компонент
Навигация
1 часть — Вводная
2 часть — Разработка
3 часть — OpenVPN компонент
4 часть — Configs компонент
5 часть — Vpn компонент
6 часть — Notify компонент
7 часть — Context компонент
8 часть — Setting компонент
9 часть — Callback компонент
10 часть — Объединение всех компонентов
11 часть — Сборка приложения под Windows
2 часть — Разработка
3 часть — OpenVPN компонент
4 часть — Configs компонент
5 часть — Vpn компонент
6 часть — Notify компонент
7 часть — Context компонент
8 часть — Setting компонент
9 часть — Callback компонент
10 часть — Объединение всех компонентов
11 часть — Сборка приложения под Windows