mirror of
https://github.com/nuttylmao/nutty.gg.git
synced 2026-09-19 04:00:59 -04:00
• 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:
Binary file not shown.
|
After Width: | Height: | Size: 380 KiB |
@@ -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
@@ -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";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user