1
0
Fork 0
mirror of https://gitlab.com/futo-org/fcast.git synced 2025-08-06 09:12:50 +00:00

Added toasts and UI update on device connection

This commit is contained in:
Michael Hollister 2025-01-06 20:35:57 -06:00
parent 5328087d64
commit 3142709d7f
17 changed files with 640 additions and 121 deletions

View file

@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/no-require-imports */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { toast, ToastIcon } from '../components/Toast';
declare global {
interface Window {
@ -19,45 +20,69 @@ if (TARGET === 'electron') {
// @ts-ignore
const electronAPI = __non_webpack_require__('electron');
electronAPI.ipcRenderer.on("device-info", (_event, value) => {
// Since event is sent async during window startup, could fire off before or after renderer.js is loaded
electronAPI.ipcRenderer.on('startup-storage-clear', () => {
localStorage.clear();
});
electronAPI.ipcRenderer.on("device-info", (_event, value: any) => {
deviceInfo = value;
})
electronAPI.contextBridge.exposeInMainWorld('targetAPI', {
onStartupStorageClear: (callback: any) => electronAPI.ipcRenderer.on('startup-storage-clear', callback),
onDeviceInfo: (callback: any) => electronAPI.ipcRenderer.on("device-info", callback),
onConnect: (callback: any) => electronAPI.ipcRenderer.on("connect", callback),
onDisconnect: (callback: any) => electronAPI.ipcRenderer.on("disconnect", callback),
getDeviceInfo: () => deviceInfo,
});
// @ts-ignore
} else if (TARGET === 'webOS') {
require('lib/webOSTVjs-1.2.10/webOSTV.js');
require('lib/webOSTVjs-1.2.10/webOSTV-dev.js');
const serviceId = 'com.futo.fcast.receiver.service';
let onDeviceInfoCb = () => { console.log('Main: Callback not set while fetching device info'); };
try {
require('lib/webOSTVjs-1.2.10/webOSTV.js');
require('lib/webOSTVjs-1.2.10/webOSTV-dev.js');
const serviceId = 'com.futo.fcast.receiver.service';
let onStartupStorageClearCb = () => { localStorage.clear(); };
let onDeviceInfoCb = () => { console.log('Main: Callback not set while fetching device info'); };
let onConnectCb = (_, value: any) => { console.log('Main: Callback not set while calling onConnect'); };
let onDisconnectCb = (_, value: any) => { console.log('Main: Callback not set while calling onDisconnect'); };
const getDeviceInfoService = window.webOS.service.request(`luna://${serviceId}/`, {
method:"getDeviceInfo",
parameters: {},
onSuccess: (message: any) => {
console.log(`Main: getDeviceInfo ${JSON.stringify(message)}`);
const getDeviceInfoService = window.webOS.service.request(`luna://${serviceId}/`, {
method:"getDeviceInfo",
parameters: {},
onSuccess: (message: any) => {
console.log(`Main: getDeviceInfo ${JSON.stringify(message)}`);
deviceInfo = message.value;
onDeviceInfoCb();
},
onFailure: (message: any) => {
console.error(`Main: getDeviceInfo ${JSON.stringify(message)}`);
},
// onComplete: (message) => {},
});
deviceInfo = message.value;
onDeviceInfoCb();
},
onFailure: (message: any) => {
console.error(`Main: getDeviceInfo ${JSON.stringify(message)}`);
toast(`Main: getDeviceInfo ${JSON.stringify(message)}`, ToastIcon.ERROR);
},
// onComplete: (message) => {},
});
window.targetAPI = {
onDeviceInfo: (callback: () => void) => onDeviceInfoCb = callback,
getDeviceInfo: () => deviceInfo,
};
window.targetAPI = {
onStartupStorageClear: (callback: () => void) => onStartupStorageClearCb = callback,
onDeviceInfo: (callback: () => void) => onDeviceInfoCb = callback,
onConnect: (callback: () => void) => onConnectCb = callback,
onDisconnect: (callback: () => void) => onDisconnectCb = callback,
getDeviceInfo: () => deviceInfo,
};
preloadData = {
getDeviceInfoService: getDeviceInfoService,
};
preloadData = {
getDeviceInfoService: getDeviceInfoService,
onStartupStorageClearCb: onStartupStorageClearCb,
onConnectCb: onConnectCb,
onDisconnectCb: onDisconnectCb,
};
}
catch (err) {
console.error(`Main: preload ${JSON.stringify(err)}`);
toast(`Main: preload ${JSON.stringify(err)}`, ToastIcon.ERROR);
}
} else {
// @ts-ignore
console.log(`Attempting to run FCast player on unsupported target: ${TARGET}`);

View file

@ -1,14 +1,57 @@
import QRCode from 'modules/qrcode';
import { onQRCodeRendered } from 'src/main/Renderer';
import { toast, ToastIcon } from '../components/Toast';
const connectionStatusText = document.getElementById("connection-status-text");
const connectionStatusSpinner = document.getElementById("connection-spinner");
const connectionStatusCheck = document.getElementById("connection-check");
let connections = JSON.parse(localStorage.getItem('connections')) ?? [];
if (connections.length > 0) {
connections.forEach(connection => {
onConnect(connection);
});
}
window.targetAPI.onStartupStorageClear((_event, value: any) => {
localStorage.clear();
localStorage.setItem('connections', JSON.stringify(connections));
});
window.targetAPI.onDeviceInfo(renderIPsAndQRCode);
window.targetAPI.onConnect((_event, value: any) => {
connections.push(value.id);
localStorage.setItem('connections', JSON.stringify(connections));
onConnect(value);
});
window.targetAPI.onDisconnect((_event, value: any) => {
console.log(`Device disconnected: ${JSON.stringify(value)}`);
const index = connections.indexOf(value.id);
if (index != -1) {
connections.splice(index, 1);
localStorage.setItem('connections', JSON.stringify(connections));
}
if (connections.length === 0) {
connectionStatusText.textContent = 'Waiting for a connection';
connectionStatusSpinner.style.display = 'inline-block';
connectionStatusCheck.style.display = 'none';
toast("Device disconnected", ToastIcon.INFO);
}
});
if(window.targetAPI.getDeviceInfo()) {
console.log("device info already present");
renderIPsAndQRCode();
}
function onConnect(value: any) {
console.log(`Device connected: ${JSON.stringify(value)}`);
connectionStatusText.textContent = 'Connected: Ready to cast';
connectionStatusSpinner.style.display = 'none';
connectionStatusCheck.style.display = 'inline-block';
}
function renderIPsAndQRCode() {
const value = window.targetAPI.getDeviceInfo();
console.log("device info", value);
@ -46,6 +89,7 @@ function renderIPsAndQRCode() {
(err) => {
if (err) {
console.error(`Error rendering QR Code: ${err}`);
toast(`Error rendering QR Code: ${err}`, ToastIcon.ERROR);
}
else {
console.log(`Rendered QR Code`);

View file

@ -134,11 +134,11 @@ body, html {
font-weight: bold;
}
#waiting-for-connection, #ips, #automatic-discovery {
#connection-status-text, #ips, #automatic-discovery {
margin-top: 20px;
}
#spinner {
#connection-spinner {
padding: 20px;
}
@ -189,3 +189,108 @@ body, html {
transform: rotate(360deg);
}
}
#connection-check {
/* display: inline-block; */
display: none;
position: relative;
width: 64px;
height: 64px;
margin: 18px;
padding: 10px;
background-color: #019BE7;
border-radius: 50%;
z-index: 0;
}
#connection-check-mark {
position: relative;
top: -10px;
left: -10px;
width: 100%;
height: 100%;
padding: 10px;
animation: check 0.5s cubic-bezier(0.5, 0, 0.5, 1) 1;
background-image: url(../assets/icons/app/checked.svg);
background-size: cover;
background-color: #019BE7;
border-radius: 50%;
z-index: 1;
}
@keyframes check {
0% {
clip-path: inset(0px 64px 0px 0px);
}
100% {
clip-path: inset(0px 0px 0px 0px);
}
}
#toast-notification {
display: flex;
flex-direction: row;
align-items: center;
padding: 16px 20px;
gap: 12px;
position: relative;
top: -200px;
max-width: 70%;
background: #F0F0F0;
border: 3px solid rgba(0, 0, 0, 0.08);
box-shadow: 0px 100px 80px rgba(0, 0, 0, 0.33), 0px 64.8148px 46.8519px rgba(0, 0, 0, 0.250556), 0px 38.5185px 25.4815px rgba(0, 0, 0, 0.200444), 0px 20px 13px rgba(0, 0, 0, 0.165), 0px 8.14815px 6.51852px rgba(0, 0, 0, 0.129556), 0px 1.85185px 3.14815px rgba(0, 0, 0, 0.0794444);
border-radius: 12px;
opacity: 0;
}
#toast-icon {
width: 48px;
height: 48px;
background-image: url(../assets/icons/app/info.svg);
background-size: cover;
flex-shrink: 0;
}
#toast-text {
display: -webkit-box;
-webkit-line-clamp: 4;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-word;
font-family: InterVariable;
font-size: 20px;
font-style: normal;
font-weight: 400;
}
.toast-fade-in {
animation: toast-fade-in 1.0s cubic-bezier(0.5, 0, 0.5, 1) 1;
}
.toast-fade-out {
animation: toast-fade-out 1.0s cubic-bezier(0.5, 0, 0.5, 1) 1;
}
@keyframes toast-fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes toast-fade-out {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}