"use strict";
// SPDX-License-Identifier: GPL-3.0-or-later
// myMPD (c) 2018-2024 Juergen Mang <mail@jcgames.de>
// https://github.com/jcorporation/mympd
/** @module viewHome_js */
/**
* Handles home
* @returns {void}
*/
function handleHome() {
sendAPI("MYMPD_API_HOME_ICON_LIST", {}, parseHomeIcons, false);
}
/**
* Moves a home icon
* @param {number} oldPos Old icon pos
* @param {number} newPos New icon pos
* @returns {void}
*/
function homeMoveIcon(oldPos, newPos) {
sendAPI("MYMPD_API_HOME_ICON_MOVE", {
"from": oldPos,
"to": newPos
}, null, false);
}
/**
* Click event handler for home icons
* @param {MouseEvent} event click event
* @param {HTMLElement} target calculated target
* @returns {void}
*/
function viewHomeClickHandler(event, target) {
if (event.target.classList.contains('card-body')) {
showContextMenu(event);
return;
}
const href = getData(target, 'href');
if (href !== undefined) {
parseCmd(event, href);
}
}
/**
* Returns the friendly type of the home icon
* @param {string} cmd the command
* @param {string} action action of the command
* @returns {string} friendly type
*/
function getHomeIconType(cmd, action) {
switch(cmd) {
case 'appGoto':
case 'execScriptFromOptions':
case 'openExternalLink':
case 'openModal':
return typeFriendly[cmd];
default:
return typeFriendly[action];
}
}
/**
* Parses the MYMPD_API_HOME_ICON_LIST response
* @param {object} obj jsonrpc response object
* @returns {void}
*/
function parseHomeIcons(obj) {
widgetRefresh.length = 0;
const cardContainer = elGetById('HomeList');
unsetUpdateView(cardContainer);
const cols = cardContainer.querySelectorAll('.col');
if (cols.length === 0) {
// remove warning messages
elClear(cardContainer);
}
if (obj.result && obj.result.returnedEntities === 0) {
elClear(cardContainer);
const div = elCreateNodes('div', {"class": ["px-3", "py-1"]}, [
elCreateTextTn('h3', {}, 'Homescreen'),
elCreateNodes('p', {}, [
document.createTextNode(tn('Homescreen welcome')),
elCreateText('span', {"class": ["mi"]}, 'add_to_home_screen'),
document.createTextNode(' '),
elCreateText('span', {"class": ["mi"]}, 'library_add')
])
]);
cardContainer.appendChild(div);
return;
}
if (checkResult(obj, cardContainer, undefined) === false) {
return;
}
for (let i = 0; i < obj.result.returnedEntities; i++) {
const col = obj.result.data[i].type === 'widget'
? createHomeWidget(obj.result.data[i], i)
: createHomeIcon(obj.result.data[i], i);
if (i < cols.length) {
cols[i].replaceWith(col);
}
else {
cardContainer.append(col);
}
if (obj.result.data[i].type === 'widget') {
updateHomeWidget(col.firstElementChild);
if (obj.result.data[i].refresh > 0) {
widgetRefresh.push(col.firstElementChild);
}
}
}
for (let i = cols.length - 1; i >= obj.result.returnedEntities; i--) {
cols[i].remove();
}
setScrollViewHeight(cardContainer);
}
/**
* Updates the widget by calling the script
* @param {HTMLElement | ChildNode} card Widget to populate
* @returns {void}
*/
function updateHomeWidget(card) {
card.querySelector('.card-title a').textContent = 'autorenew';
const data = getData(card, 'data');
const query = [];
for (const key in data.arguments) {
query.push(myEncodeURIComponent(key) + '=' + myEncodeURIComponent(data.arguments[key]));
}
httpGet(getMyMPDuri('http') + '/script/' + localSettings.partition + '/' + data.script + '?' + query.join('&'),
function(response) {
setTimeout(function() {
card.querySelector('.card-title a').textContent = 'refresh';
}, 200);
const body = card.querySelector('.card-body');
elClear(body);
if (response === null) {
body.appendChild(elCreateTextTn('div', {'class': ['alert', 'alert-danger', 'm-3']}, 'Error executing script'));
return;
}
const parser = new DOMParser();
const html = parser.parseFromString(response, "text/html");
body.appendChild(... html.body.childNodes);
body.addEventListener('click', function(event) {
event.preventDefault();
event.stopPropagation();
const href = getData(event.target, 'href');
if (href === undefined) {
return;
}
parseCmd(event, JSON.parse(href));
}, false);
setData(card, 'lastUpdate', getTimestamp());
},
false);
}
/**
* Creates a home widget
* @param {object} data Widegt data
* @param {number} pos Widget position
* @returns {HTMLElement} Created widget wrapped in col
*/
function createHomeWidget(data, pos) {
const col = elCreateEmpty('div', {"class": ["col", "px-0", "flex-grow-0", "float-start"]});
const card = elCreateNodes('div', {"data-contextmenu": "homeWidget", "class": ["card", "home-widgets", "bg-secondary", "rounded-2", "home-widget-" + data.size], "draggable": "true"},
[
elCreateNodes('div', {'class': ['card-title', 'py-2', 'px-3', 'mb-0']}, [
document.createTextNode(data.name),
elCreateText('a', {'href':'#', 'data-title-phrase': 'Reload', 'title': tn('Reload'), 'data-action': 'refreshWidget', 'class': ['mi', 'float-end']}, 'refresh')
]),
elCreateEmpty('div', {'class': ['card-body', 'overflow-auto', 'p-0', 'bg-dark', 'rounded-bottom']})
]
);
setData(card, 'type', 'widget');
setData(card, 'pos', pos);
setData(card, 'data', data);
col.appendChild(card);
return col;
}
/**
* Creates a home icon
* @param {object} data Icon data
* @param {number} pos Icon position
* @returns {HTMLElement} Created icon wrapped in col
*/
function createHomeIcon(data, pos) {
const homeType = getHomeIconType(data.cmd, data.options[0]);
const actionType = friendlyActions[data.cmd];
if (data.cmd !== 'appGoto') {
const opt0 = data.options[0];
const opt1 = [];
// convert array to [opt0, [opt1,...]] and parse
if (data.options[1] !== undefined) {
for (let j = 1; j < data.options.length; j++) {
opt1.push(convertType(data.options[j]));
}
}
data.options = [opt0, opt1];
}
const col = elCreateEmpty('div', {"class": ["col", "px-0", "flex-grow-0", "float-start"]});
const card = elCreateEmpty('div', {"data-contextmenu": "homeIcon", "class": ["card", "home-icons"], "draggable": "true",
"title": tn(homeType) + ':' + smallSpace + data.name +
'\n' + tn(actionType)});
//decode json options
for (let j = 0, k = data.options.length; j < k; j++) {
if (data.options[j].indexOf('{"') === 0 ||
data.options[j].indexOf('["') === 0)
{
data.options[j] = JSON.parse(data.options[j]);
}
}
setData(card, 'type', 'icon');
setData(card, 'name', data.name);
setData(card, 'href', {"cmd": data.cmd, "options": data.options});
setData(card, 'pos', pos);
const cardTitle = elCreateText('div', {"class": ["card-title", "mi", "rounded", "clickable"]}, data.ligature);
if (data.image !== '') {
cardTitle.style.backgroundImage = getCssImageUri(data.image);
}
if (data.bgcolor !== '') {
cardTitle.style.backgroundColor = data.bgcolor;
}
if (data.color !== '' &&
data.color !== undefined)
{
cardTitle.style.color = data.color;
}
card.appendChild(cardTitle);
card.appendChild(
elCreateText('div', {"class": ["card-body", "card-body-grid", "p-2", "clickable"]}, data.name)
);
col.appendChild(card);
return col;
}
/**
* Refreshes the home widgets. This function is called by the websocket
* keepalive timer if the websocket is connected.
* @returns {void}
*/
function homeWidgetsRefresh() {
const ts = getTimestamp();
for (const widget of widgetRefresh) {
const data = getData(widget, 'data');
const lastUpdate = getData(widget, 'lastUpdate');
if (lastUpdate + data.refresh < ts) {
updateHomeWidget(widget);
}
}
}