You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
website/public/main.js

200 lines
4.5 KiB

const audio = document.querySelector("audio");
const playButton = document.querySelector(".play-stream");
const playButtonAnimation = document.getElementById("play-button-animation");
const artistBlock = document.querySelector(".now-playing_artist");
const titleBlock = document.querySelector(".now-playing_song");
const logoDot = document.querySelector(".logo_dot");
const STOP_STATE_POINTS = "650,0, 1350,0, 1350,500, 650,500";
const PLAY_STATE_POINTS = "0,0, 2000,250, 2000,250, 0,500";
const startIcebergsAnimation = () => {
[1, 2, 3].map((i) => {
const polygon = document.getElementById(`iceberg${i}`);
polygon.classList.add("active");
});
};
const stopIcebergsAnimation = () => {
[1, 2, 3].map((i) => {
const polygon = document.getElementById(`iceberg${i}`);
polygon.classList.remove("active");
});
};
const hideAudio = () => {
audio.hidden = true;
};
const playAudio = () => {
audio?.play();
};
const pauseAudio = () => {
audio?.pause();
};
const setButtonPlayState = () => {
playButtonAnimation.setAttribute("from", PLAY_STATE_POINTS);
playButtonAnimation.setAttribute("to", STOP_STATE_POINTS);
playButtonAnimation.beginElement();
};
const setButtonStopState = () => {
playButtonAnimation.setAttribute("from", STOP_STATE_POINTS);
playButtonAnimation.setAttribute("to", PLAY_STATE_POINTS);
playButtonAnimation.beginElement();
};
const handleAudioPlay = () => {
playAudio();
setButtonPlayState();
startIcebergsAnimation();
};
const handleAudioStop = () => {
pauseAudio();
setButtonStopState();
stopIcebergsAnimation();
};
const addPlayerInteractions = () => {
playButton.addEventListener("click", () => {
audio?.paused ? handleAudioPlay() : handleAudioStop();
});
};
const parseMessageFromString = (message) => {
try {
return JSON.parse(message);
} catch {
return {};
}
};
/**
* @typedef {Object} MessageLike
* @property {string} action - every string valid for JS function name.
* @property {Object} data - The user's last name.
*/
/**
* @param {string|MessageLike} data - Message itself of JSON representation of Message
*/
function Message(data = {}) {
switch(Object.prototype.toString.call(data)) {
case "[object String]":
const parsedMessage = parseMessageFromString(data);
this.action = parsedMessage?.action;
this.data = parsedMessage?.data;
break;
case "[object Object]":
this.action = data?.action;
this.data = data?.data;
break;
}
}
Message.prototype.isValid = function() {
return (
this?.action !== undefined
);
};
/**
* @typedef {Object} MessageLike
* @property {string} action - every string valid for JS function name.
* @property {Object} data - The user's last name.
*/
/**
* @param {string|MessageLike} message - Message itself of JSON representation of Message
*/
function processMessage(messageLike = {}) {
const message = new Message(messageLike);
if(!message.isValid()) {
console.error("Got mallformed message:", message);
return;
}
if (this.actions[message.action]) {
this.actions[message.action](message.data);
} else {
this.fallbackAction(message);
}
}
const messageProcessor = {
// Use Map instead?
actions: {},
processMessage,
fallbackAction: function(x) {
console.log("No actions for message:", x);
}
};
const socketProtocol = location.protocol === "http:" ? "ws:" : "wss:";
async function connectToServer() {
const ws = new WebSocket(`${socketProtocol}//${location.host}/meta`);
ws.addEventListener("message", (event) =>
messageProcessor.processMessage(event.data),
);
}
const changeTitles = ({ artist, title }) => {
if (artist) {
artistBlock.innerHTML = artist;
} else {
artistBlock.innerHTML = "";
}
if (title) {
titleBlock.innerHTML = title;
document.title = `${title} on Radioiceberg`;
} else {
titleBlock.innerHTML = "";
}
if (artist && title) {
document.title = `${artist} - ${title} on Radioiceberg`;
}
};
const metadataChange = (data) => {
changeTitles(data);
};
const liveStarted = () => {
logoDot.classList.add("live");
};
const liveEnded = () => {
logoDot.classList.remove("live");
};
const setState = (data) => {
const { lastPlayed, isOnline } = data;
if (lastPlayed) changeTitles(lastPlayed);
if (isOnline) liveStarted();
};
const actions = {
metadataChange,
liveStarted,
liveEnded,
setState,
};
messageProcessor.actions = actions;
const main = () => {
hideAudio();
addPlayerInteractions();
connectToServer();
};
document.addEventListener("DOMContentLoaded", main);