• Added a popup system.

• Added a popup when SMTC Bridge is closed
• Added a popup when SMTC Bridge is out of date
This commit is contained in:
nutty
2026-07-01 03:23:09 +10:00
parent 9c435914e5
commit 75fee5ed01
3 changed files with 348 additions and 5 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

+238
View File
@@ -485,3 +485,241 @@ async function GetAccentPalette(imageUrl) {
}); });
}); });
} }
// Generic popup
function showPopup(iconSrc, title, subtitle, attribute, background, button) {
// 1. Check if a popup is already on screen
const existingOverlay = document.getElementById('global-common-overlay');
// If it exists AND isn't already in the middle of fading out, DO NOTHING
if (existingOverlay && !existingOverlay.classList.contains('common-closing')) {
return null;
}
// If there IS a popup but it IS fading out, kill it instantly to make room for the new one
if (existingOverlay) {
existingOverlay.remove();
}
// 2. Inject global styles if they don't already exist in the head
if (!document.getElementById('global-common-popup-styles')) {
const styleSheet = document.createElement('style');
styleSheet.id = 'global-common-popup-styles';
styleSheet.innerText = `
#global-common-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: transparent;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
display: flex;
align-items: center;
justify-content: center;
z-index: 999999;
animation: commonFadeIn 2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.common-popup-card {
width: 90%;
max-width: 500px;
border-radius: 24px;
box-sizing: border-box;
display: flex;
flex-direction: row;
align-items: stretch;
overflow: hidden;
box-shadow: 0 30px 60px rgba(0,0,0,0.4),
0 0 0 1px rgba(255, 255, 255, 0.08);
animation: commonScaleIn 2.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
gap: 32px;
padding: 32px;
}
.common-popup-icon-container {
display: flex;
align-items: center;
justify-content: center;
background: transparent;
}
.common-popup-img {
height: 48px;
width: auto;
object-fit: contain;
display: block;
}
.common-popup-text-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
text-align: left;
gap: 8px;
}
.common-popup-title {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 22px;
font-weight: 700;
color: #ffffff;
letter-spacing: -0.02em;
line-height: 1.2;
}
.common-popup-title:last-child {
margin-bottom: 0;
}
.common-popup-subtitle {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 14px;
font-weight: 400;
color: rgba(255, 255, 255, 0.65);
margin: 0;
line-height: 1.5;
}
.common-popup-attribute {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 13px;
color: rgba(255, 255, 255, 0.4);
word-break: break-all;
}
/* 💎 GLASSMORPHIC BUTTON STYLES */
.common-popup-button {
margin-top: 6px;
padding: 10px 20px;
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.15);
background: rgba(255, 255, 255, 0.1);
color: #ffffff;
font-family: system-ui, sans-serif;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s ease, border-color 0.2s ease, transform 0.1s ease;
text-align: center;
width: fit-content;
}
.common-popup-button:hover {
background: rgba(255, 255, 255, 0.2);
border-color: rgba(255, 255, 255, 0.3);
}
.common-popup-button:active {
transform: scale(0.97);
}
@keyframes commonFadeIn {
from { opacity: 0; } to { opacity: 1; }
}
@keyframes commonScaleIn {
from { transform: scale(0.95) translateY(10px); opacity: 0; }
to { transform: scale(1) translateY(0); opacity: 1; }
}
#global-common-overlay.common-closing {
animation: commonFadeOut 2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.common-closing .common-popup-card {
animation: commonScaleOut 2.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes commonFadeOut {
from { opacity: 1; } to { opacity: 0; }
}
@keyframes commonScaleOut {
from { transform: scale(1) translateY(0); opacity: 1; }
to { transform: scale(0.95) translateY(10px); opacity: 0; }
}
`;
document.head.appendChild(styleSheet);
}
// 3. Create elements
const overlay = document.createElement('div');
overlay.id = 'global-common-overlay';
const card = document.createElement('div');
card.className = 'common-popup-card';
card.style.background = background || 'linear-gradient(135deg, #1a1a1e 0%, #111113 100%)';
// 4. Construct Inner Content (Strict Conditional Rendering)
const iconMarkup = iconSrc
? `<div class="common-popup-icon-container">
<img src="${iconSrc}" class="common-popup-img" alt="Alert Icon" />
</div>`
: '';
const titleMarkup = title
? `<label class="common-popup-title">${title}</label>`
: '';
const subtitleMarkup = subtitle
? `<label class="common-popup-subtitle">${subtitle}</label>`
: '';
const attributeMarkup = attribute
? `<label class="common-popup-attribute">${attribute}</label>`
: '';
// Render button conditional markup
const buttonMarkup = (button && button.text)
? `<button class="common-popup-button">${button.text}</button>`
: '';
card.innerHTML = `
${iconMarkup}
<div class="common-popup-text-content">
${titleMarkup}
${subtitleMarkup}
${attributeMarkup}
${buttonMarkup}
</div>
`;
// 5. Setup Action Listeners if Button Exists
if (button && button.text && button.action) {
const actionBtn = card.querySelector('.common-popup-button');
if (actionBtn) {
actionBtn.addEventListener('click', () => {
if (typeof button.action === 'function') {
button.action();
} else if (typeof button.action === 'string') {
window.open(button.action, '_blank');
}
});
}
}
// 6. Append to page DOM
overlay.appendChild(card);
document.body.appendChild(overlay);
return {
element: overlay,
close: () => {
overlay.classList.add('common-closing');
overlay.addEventListener('animationend', (event) => {
if (event.target === overlay) {
overlay.remove();
}
});
}
};
}
function VersionCheck(requiredVersion, installedVersion) {
const [rMajor, rMinor, rPatch] = requiredVersion.split('.').map(Number);
const [iMajor, iMinor, iPatch] = installedVersion.split('.').map(Number);
// 1. CRITICAL: Major versions must match exactly.
if (iMajor !== rMajor) {
return 'incompatible';
}
// 2. SOFT WARNING: The installed minor version is lower than what is required.
if (iMinor < rMinor) {
return 'soft-warning';
}
// 3. SOFT WARNING: Minors match, but the installed patch version is lagging.
if (iMinor === rMinor && iPatch < rPatch) {
return 'soft-warning';
}
// 4. PERFECT: Installed version meets or exceeds the required baseline.
return 'compatible';
}
+110 -5
View File
@@ -37,6 +37,11 @@ const hideAnimation = urlParams.get('hideAnimation') || 'slide-out-bottom';
// GLOBAL VARS // // GLOBAL VARS //
///////////////// /////////////////
const REQUIRED_VERSION = '0.0.1';
const SMTC_BRIDGE_DOWNLOAD_URL = 'https://github.com/nuttylmao/smtc-bridge/releases';
let smtcBridgePopup = null;
let versionCheckPopup = null;
let skipVersionCheck = false;
let CurrentPlaybackStatus; let CurrentPlaybackStatus;
let CurrentSong; let CurrentSong;
let hideTimeout = null; let hideTimeout = null;
@@ -141,20 +146,49 @@ async function FetchMedia() {
const response = await fetch('http://localhost:5000/now-playing'); const response = await fetch('http://localhost:5000/now-playing');
const data = await response.json(); const data = await response.json();
// Remove the SMTC Bridge popup if it's on screen
if (smtcBridgePopup) {
smtcBridgePopup.close();
smtcBridgePopup = null;
}
// Check the SMTC Bridge version
CheckSMTCBridgeVersion(data.app_version);
// Update the UI with the received data // Update the UI with the received data
// console.log(data); // console.log(data);
UpdatePlayerState(data); UpdatePlayerState(data);
} catch (error) { } catch (error) {
console.error("Failed to connect to Flask media server:", error); console.error("Failed to connect to Flask media server:", error);
// Show a popup to instruct the user to install SMTC Bridge
const newPopup = showPopup(
'/.common/resources/smtc-bridge-icon.png',
'Waiting for SMTC Bridge',
'Please launch SMTC Bridge',
'', // Attribute text
'linear-gradient(0deg, #111111 0%, #11001f 100%)',
{
text: 'Download',
action: () => {
window.open(SMTC_BRIDGE_DOWNLOAD_URL, "_blank");
}
}
);
if (newPopup) {
smtcBridgePopup = newPopup;
}
} }
} }
// Start polling every 1000 milliseconds window.addEventListener('DOMContentLoaded', () => {
setInterval(FetchMedia, 1000); // Start polling every 1000 milliseconds
setInterval(FetchMedia, 1000);
// Run once immediately on script load // Run once immediately on script load
FetchMedia(); FetchMedia();
});
@@ -162,6 +196,77 @@ FetchMedia();
// HELPER FUNCTIONS // // HELPER FUNCTIONS //
////////////////////// //////////////////////
function CheckSMTCBridgeVersion(installedVersion) {
// Check that the server/client are on the same SMTC Bridge version
// const VersionStatus = VersionCheck(REQUIRED_VERSION, installedVersion);
const versionStatus = VersionCheck(REQUIRED_VERSION, installedVersion);
if (skipVersionCheck)
return;
switch (versionStatus)
{
case 'incompatible':
{
const newPopup = showPopup(
'/.common/resources/smtc-bridge-icon.png',
'Update Required',
'Your version of SMTC Bridge is out of date.',
`<b>Installed Version: ${installedVersion}</b><br><b>New Version: ${REQUIRED_VERSION}</b>`,
'linear-gradient(0deg, #1b0005 0%, #4f000d 100%)',
{
text: 'Download',
action: () => {
window.open(SMTC_BRIDGE_DOWNLOAD_URL, "_blank");
}
}
);
if (newPopup) {
versionCheckPopup = newPopup;
}
}
skipVersionCheck = false;
break;
case 'soft-warning':
{
const newPopup = showPopup(
'/.common/resources/smtc-bridge-icon.png',
'Update Available',
'A new version of SMTC Bridge is available.',
`<b>Installed Version: ${installedVersion}</b><br><b>New Version: ${REQUIRED_VERSION}</b>`,
'linear-gradient(0deg, #2a004f 0%, #4f2675 100%)',
{
text: 'Download',
action: () => {
window.open(SMTC_BRIDGE_DOWNLOAD_URL, "_blank");
}
}
);
if (newPopup) {
versionCheckPopup = newPopup;
}
setTimeout(() => {
if (versionCheckPopup) {
versionCheckPopup.close();
versionCheckPopup = null;
}
}, 10000);
}
skipVersionCheck = true;
break;
default:
if (versionCheckPopup) {
versionCheckPopup.close();
versionCheckPopup = null;
}
break;
}
}
function ConvertMillisecondsToMinutesSoThatItLooksBetterOnTheOverlay(time) { function ConvertMillisecondsToMinutesSoThatItLooksBetterOnTheOverlay(time) {
if (isNaN(time) || time <= 0) return "0:00"; if (isNaN(time) || time <= 0) return "0:00";