JavaScriptowa fizyka kwantowa
czyli Twoja pierwsza, użyteczna apka w Electronie
👨💻1️⃣
- Krystian Kościelniak
- Front-End Developer @ Brainhub
- Zdalnie @ Kraków
- Zespół @ BH Bielsko-Biała
👨💻2️⃣
- Łukasz Pluszczewski
- Full-stack developer @ Brainhub
- Zdalnie @ Kraków
📅
- Electron Framework?
- Co stworzymy?
- Jakich narzędzi użyjemy?
- Jak uruchomić projekt?
- Show Time!
✅
- 4 zadania
- GUI
- Git-branche exercise-* zawierają rozwiązania zadań
⚛︎
- Otwartoźródłowy, multiplatformowy framework dla aplikacji desktopowych
- Aplikacje są pisane w JavaScript
- Bazuje na projekcie Chromium i Node.js
- Obsługuje natywne funkcjonalności systemów operacyjnych
- Używany m.in. przez Atom, VS Code i Notion
🏗
- Funkcjonalna aplikacja w Electronie
- Wyświetlająca dane pogodowe
- Korzystająca z natywnych funkcjonalności systemu operacyjnego
🛠
- Maszyna wirtualna z istniejącym środowiskiem
- Git-branche z poszczególnymi zadaniami
- https://wttr.in/ proxy API
🚀
- Uruchomienie maszyny wirtualnej
-
osboxes/osboxes.org
- Uruchomienie terminala
-
cd electron-workshop
-
git fetch origin
-
npm install
-
npm start
🚀
1️⃣
- Pobranie danych z Wttr.in
- Wyświetlenie ich w oknie mainWindow
💡
-
renderer.js
API
https://pogodynka.ml/Bielsko-biała
// OR
https://wttr.pluszczewski.pl/Bielsko-biała
✅
const getWeatherData = (city = 'Bielsko-biała') => {
return fetch(`https://pogodynka.ml/${encodeURI(city)}`)
.then(response => response.json());
};
getWeatherData()
.then(weatherData => updateCurrentWeather(weatherData.now));
2️⃣
- Zapisanie danych do storage
- Wyświetlenie zapisanych danych gdy brakuje dostępu do sieci
💡
-
electron-json-storage
-
renderer.js
-
Chrome DevTools
✅
const storage = require('electron-json-storage'); // 👈
const getWeatherData = (city = 'Bielsko-biała') => {
return fetch(`https://pogodynka.ml/${encodeURI(city)}`)
.then(response => response.json())
.then(weatherData => {
storage.set('weatherData', weatherData); // 👈
return weatherData;
})
.catch(() => { // 👈
return new Promise((resolve, reject) =>
storage.get('weatherData', (error, data) => {
error ? reject(error) : resolve(data);
}));
});
};
3️⃣
- Zapisywanie danych do pliku JSON
- Skrót klawiaturowy
💡
-
Szyna IPC
-
dialog
-
Menu
-
fs
Main
- proces główny
- pojedynczy
- kontroluje cykl życia aplikacji
- zarządza natywnymi komponentami
- zawiera pełne Node API
- proces przeglądarki
Renderer
- proces renderowania
- może być ich wiele
- zarządza treścią pojedynczego oknem aplikacji
- w osobnych procesach
- zawiera część Node API
- proces zakładki
Komunikacja
- Oparta o zdarzenia
- Wykorzystuje szynę IPC (Inter-Process Communication) do komunikacji pomiędzy procesami Main i Renderer
- Komunikacja pomiędzy oknami odbywa się za pośrednictwem procesu Main
Komunikacja
ipcMain
ipcRenderer
// emisja zdarzeń
window.webContents.send(
'event-name',
dataToSend
);
// obsługa zdarzeń
const { ipcMain } =
require('electron');
ipcMain.on(
'another-event',
(event, data) => {
console.log(data);
}
);
// emisja zdarzeń
const { ipcRenderer } =
require('electron');
ipcRenderer.send(
'another-event',
data
);
// obsługa zdarzeń
ipcRenderer.on(
'event-name',
(event, data) => {
console.log(data);
}
);
✅
// renderer.js
const { ipcRenderer } = require('electron'); // 👈
const saveToJson = async () => { // 👈
try {
const data = await getWeatherData();
updateCurrentWeather(data);
ipcRenderer.send('save-to-json', data);
} catch (error) { /* [...] */ }
};
getWeatherData()
.then((weatherData) => {
// [...]
document.getElementById('saveToJsonBtn')
.addEventListener('click', saveToJson); // 👈
});
// main.js
const { ipcMain, dialog } = require('electron'); // 👈
const fs = require('fs');
ipcMain.on('save-to-json', (event, data) => { // 👈
dialog.showSaveDialog(mainWindow, {
title: 'Save to JSON',
}, (filename) => {
if (filename) {
const serializedData = JSON.stringify(data, null, 2);
fs.writeFile(filename, serializedData, (error) => { // 👈
if (error) {
console.error(error);
}
});
}
});
});
// main.js
const { Menu } = require('electron');
const menuTemplate = [{ // 👈
label: app.getName(),
submenu: [
{
label: 'Save to JSON',
click: () => {
window.webContents.send('save-to-json-shortcut'); // 👈
},
accelerator: 'CmdOrCtrl+S',
},
// [...]
],
}];
const menu = Menu.buildFromTemplate(menuTemplate); // 👈
Menu.setApplicationMenu(menu);
// main.js
app.on('ready', () => {
const window = createWindow();
createMenu(window); // 👈
});
// renderer.js
getWeatherData()
.then((weatherData) => {
// [...]
ipcRenderer.on('save-to-json-shortcut', saveToJson); // 👈
});
4️⃣
- Ikona Tray'a
- Menu kontekstowe
- Powiadomienie po kliknięciu Show weather
💡
-
getWeatherData()
-
Tray
-
Menu
-
electron-main-notification
✅
// main.js
let tray;
const createTray = (window) => {
const trayMenuTemplate = [{ // 👈
label: 'Show application',
click: () => { window.show(); },
},
{ label: 'Show weather', click: showNotification }, // 👈
{ role: 'separator' },
{ label: 'Quit', click: () => app.exit(0) },
];
tray = new Tray('icons/cloud.png'); // 👈
const contextMenu = Menu.buildFromTemplate(trayMenuTemplate); // 👈
tray.setContextMenu(contextMenu); // 👈
};
// main.js
app.on('ready', () => {
const window = createWindow();
createMenu(window);
createTray(window); // 👈
});
// main.js
const notify = require('electron-main-notification');
const getWeatherData = /* require it */
const showNotification = async () => {
const weatherData = await getWeatherData();
notify('Weather', {
body: `
Temperature: ${weatherData.now.temperatureLow},
Wind direction: ${weatherData.now.windDirectionIcon},
Wind speed: ${weatherData.now.windLow} km/h`,
});
};
*️⃣
- Dynamiczna ikona Tray'a
- Globalny skrót klawiaturowy
💡
-
Tray.setImage()
-
globalShortcut
Podsumowując
- Napisaliśmy pierwszą aplikacje w Electron
- Wykorzystaliśmy 3 natywne API
- Stworzyliśmy okno aplikacji z menu
- Dodaliśmy ikonę zasobnika
- wykorzystaliśmy zapisywanie danych na dysku
- Użyliśmy systemowych powiadomień
- I zrobiliśmy to w niecałe dwie godziny
Dzięki!
- @kkoscielniak
- @Lukasz-pluszczewski
JavaScriptowa fizyka kwantowa
By kkoscielniak
JavaScriptowa fizyka kwantowa
...czyli Twoja pierwsza, użyteczna apka w elektronie
- 594