mirror of
https://github.com/iv-org/invidious.git
synced 2025-12-22 07:59:30 +00:00
Updated styling, formatting, structure of frontend
This commit is contained in:
@@ -1,131 +1,197 @@
|
||||
'use strict';
|
||||
var notification_data = JSON.parse(document.getElementById('notification_data').textContent);
|
||||
"use strict";
|
||||
var notification_data = JSON.parse(
|
||||
document.getElementById("notification_data").textContent,
|
||||
);
|
||||
|
||||
/** Boolean meaning 'some tab have stream' */
|
||||
const STORAGE_KEY_STREAM = 'stream';
|
||||
const STORAGE_KEY_STREAM = "stream";
|
||||
/** Number of notifications. May be increased or reset */
|
||||
const STORAGE_KEY_NOTIF_COUNT = 'notification_count';
|
||||
const STORAGE_KEY_NOTIF_COUNT = "notification_count";
|
||||
|
||||
var notifications, delivered;
|
||||
var notifications_mock = { close: function () { } };
|
||||
var notifications_mock = { close: function () {} };
|
||||
|
||||
function get_subscriptions() {
|
||||
helpers.xhr('GET', '/api/v1/auth/subscriptions', {
|
||||
async function get_subscriptions_call() {
|
||||
return new Promise((resolve) => {
|
||||
helpers.xhr(
|
||||
"GET",
|
||||
"/api/v1/auth/subscriptions",
|
||||
{
|
||||
retries: 5,
|
||||
entity_name: 'subscriptions'
|
||||
}, {
|
||||
on200: create_notification_stream
|
||||
});
|
||||
entity_name: "subscriptions",
|
||||
},
|
||||
{
|
||||
on200: function (subscriptions) {
|
||||
create_notification_stream(subscriptions);
|
||||
resolve(subscriptions);
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Start the retry mechanism
|
||||
const get_subscriptions = exponential_backoff(get_subscriptions_call, 100, 1000);
|
||||
|
||||
function create_notification_stream(subscriptions) {
|
||||
// sse.js can't be replaced to EventSource in place as it lack support of payload and headers
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/API/EventSource/EventSource
|
||||
notifications = new SSE(
|
||||
'/api/v1/auth/notifications', {
|
||||
withCredentials: true,
|
||||
payload: 'topics=' + subscriptions.map(function (subscription) { return subscription.authorId; }).join(','),
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
});
|
||||
delivered = [];
|
||||
// sse.js can't be replaced to EventSource in place as it lack support of payload and headers
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/API/EventSource/EventSource
|
||||
notifications = new SSE("/api/v1/auth/notifications", {
|
||||
withCredentials: true,
|
||||
payload:
|
||||
"topics=" +
|
||||
subscriptions
|
||||
.map(function (subscription) {
|
||||
return subscription.authorId;
|
||||
})
|
||||
.join(","),
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
});
|
||||
delivered = [];
|
||||
|
||||
var start_time = Math.round(new Date() / 1000);
|
||||
var start_time = Math.round(new Date() / 1000);
|
||||
|
||||
notifications.onmessage = function (event) {
|
||||
if (!event.id) return;
|
||||
notifications.onmessage = function (event) {
|
||||
if (!event.id) return;
|
||||
|
||||
var notification = JSON.parse(event.data);
|
||||
console.info('Got notification:', notification);
|
||||
var notification = JSON.parse(event.data);
|
||||
console.info("Got notification:", notification);
|
||||
|
||||
// Ignore not actual and delivered notifications
|
||||
if (start_time > notification.published || delivered.includes(notification.videoId)) return;
|
||||
// Ignore not actual and delivered notifications
|
||||
if (
|
||||
start_time > notification.published ||
|
||||
delivered.includes(notification.videoId)
|
||||
)
|
||||
return;
|
||||
|
||||
delivered.push(notification.videoId);
|
||||
delivered.push(notification.videoId);
|
||||
|
||||
let notification_count = helpers.storage.get(STORAGE_KEY_NOTIF_COUNT) || 0;
|
||||
notification_count++;
|
||||
helpers.storage.set(STORAGE_KEY_NOTIF_COUNT, notification_count);
|
||||
let notification_count = helpers.storage.get(STORAGE_KEY_NOTIF_COUNT) || 0;
|
||||
notification_count++;
|
||||
helpers.storage.set(STORAGE_KEY_NOTIF_COUNT, notification_count);
|
||||
|
||||
update_ticker_count();
|
||||
update_ticker_count();
|
||||
|
||||
// permission for notifications handled on settings page. JS handler is in handlers.js
|
||||
if (window.Notification && Notification.permission === 'granted') {
|
||||
var notification_text = notification.liveNow ? notification_data.live_now_text : notification_data.upload_text;
|
||||
notification_text = notification_text.replace('`x`', notification.author);
|
||||
// permission for notifications handled on settings page. JS handler is in handlers.js
|
||||
if (window.Notification && Notification.permission === "granted") {
|
||||
var notification_text = notification.liveNow
|
||||
? notification_data.live_now_text
|
||||
: notification_data.upload_text;
|
||||
notification_text = notification_text.replace("`x`", notification.author);
|
||||
|
||||
var system_notification = new Notification(notification_text, {
|
||||
body: notification.title,
|
||||
icon: '/ggpht' + new URL(notification.authorThumbnails[2].url).pathname,
|
||||
img: '/ggpht' + new URL(notification.authorThumbnails[4].url).pathname
|
||||
});
|
||||
var system_notification = new Notification(notification_text, {
|
||||
body: notification.title,
|
||||
icon: "/ggpht" + new URL(notification.authorThumbnails[2].url).pathname,
|
||||
img: "/ggpht" + new URL(notification.authorThumbnails[4].url).pathname,
|
||||
});
|
||||
|
||||
system_notification.onclick = function (e) {
|
||||
open('/watch?v=' + notification.videoId, '_blank');
|
||||
};
|
||||
}
|
||||
};
|
||||
system_notification.onclick = function (e) {
|
||||
open("/watch?v=" + notification.videoId, "_blank");
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
notifications.addEventListener('error', function (e) {
|
||||
console.warn('Something went wrong with notifications, trying to reconnect...');
|
||||
notifications = notifications_mock;
|
||||
setTimeout(get_subscriptions, 1000);
|
||||
});
|
||||
notifications.addEventListener("error", function (e) {
|
||||
console.warn(
|
||||
"Something went wrong with notifications, trying to reconnect...",
|
||||
);
|
||||
notifications = notifications_mock;
|
||||
|
||||
notifications.stream();
|
||||
});
|
||||
|
||||
notifications.stream();
|
||||
}
|
||||
|
||||
function update_ticker_count() {
|
||||
var notification_ticker = document.getElementById('notification_ticker');
|
||||
var notification_ticker = document.getElementById("notification_ticker");
|
||||
|
||||
const notification_count = helpers.storage.get(STORAGE_KEY_STREAM);
|
||||
if (notification_count > 0) {
|
||||
notification_ticker.innerHTML =
|
||||
'<span id="notification_count">' + notification_count + '</span> <i class="icon ion-ios-notifications"></i>';
|
||||
} else {
|
||||
notification_ticker.innerHTML =
|
||||
'<i class="icon ion-ios-notifications-outline"></i>';
|
||||
}
|
||||
const notification_count = helpers.storage.get(STORAGE_KEY_STREAM);
|
||||
if (notification_count > 0) {
|
||||
notification_ticker.innerHTML =
|
||||
'<span id="notification_count">' +
|
||||
notification_count +
|
||||
'</span> <i class="icon ion-ios-notifications"></i>';
|
||||
} else {
|
||||
notification_ticker.innerHTML =
|
||||
'<i class="icon ion-ios-notifications-outline"></i>';
|
||||
}
|
||||
}
|
||||
|
||||
function start_stream_if_needed() {
|
||||
// random wait for other tabs set 'stream' flag
|
||||
setTimeout(function () {
|
||||
if (!helpers.storage.get(STORAGE_KEY_STREAM)) {
|
||||
// if no one set 'stream', set it by yourself and start stream
|
||||
helpers.storage.set(STORAGE_KEY_STREAM, true);
|
||||
notifications = notifications_mock;
|
||||
get_subscriptions();
|
||||
}
|
||||
}, Math.random() * 1000 + 50); // [0.050 .. 1.050) second
|
||||
// random wait for other tabs set 'stream' flag
|
||||
setTimeout(
|
||||
function () {
|
||||
if (!helpers.storage.get(STORAGE_KEY_STREAM)) {
|
||||
// if no one set 'stream', set it by yourself and start stream
|
||||
helpers.storage.set(STORAGE_KEY_STREAM, true);
|
||||
notifications = notifications_mock;
|
||||
get_subscriptions();
|
||||
}
|
||||
},
|
||||
Math.random() * 1000 + 50,
|
||||
); // [0.050 .. 1.050) second
|
||||
}
|
||||
|
||||
addEventListener("storage", function (e) {
|
||||
if (e.key === STORAGE_KEY_NOTIF_COUNT) update_ticker_count();
|
||||
|
||||
addEventListener('storage', function (e) {
|
||||
if (e.key === STORAGE_KEY_NOTIF_COUNT)
|
||||
update_ticker_count();
|
||||
|
||||
// if 'stream' key was removed
|
||||
if (e.key === STORAGE_KEY_STREAM && !helpers.storage.get(STORAGE_KEY_STREAM)) {
|
||||
if (notifications) {
|
||||
// restore it if we have active stream
|
||||
helpers.storage.set(STORAGE_KEY_STREAM, true);
|
||||
} else {
|
||||
start_stream_if_needed();
|
||||
}
|
||||
// if 'stream' key was removed
|
||||
if (
|
||||
e.key === STORAGE_KEY_STREAM &&
|
||||
!helpers.storage.get(STORAGE_KEY_STREAM)
|
||||
) {
|
||||
if (notifications) {
|
||||
// restore it if we have active stream
|
||||
helpers.storage.set(STORAGE_KEY_STREAM, true);
|
||||
} else {
|
||||
start_stream_if_needed();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
addEventListener('load', function () {
|
||||
var notification_count_el = document.getElementById('notification_count');
|
||||
var notification_count = notification_count_el ? parseInt(notification_count_el.textContent) : 0;
|
||||
helpers.storage.set(STORAGE_KEY_NOTIF_COUNT, notification_count);
|
||||
addEventListener("load", function () {
|
||||
var notification_count_el = document.getElementById("notification_count");
|
||||
var notification_count = notification_count_el
|
||||
? parseInt(notification_count_el.textContent)
|
||||
: 0;
|
||||
helpers.storage.set(STORAGE_KEY_NOTIF_COUNT, notification_count);
|
||||
|
||||
if (helpers.storage.get(STORAGE_KEY_STREAM))
|
||||
helpers.storage.remove(STORAGE_KEY_STREAM);
|
||||
start_stream_if_needed();
|
||||
if (helpers.storage.get(STORAGE_KEY_STREAM))
|
||||
helpers.storage.remove(STORAGE_KEY_STREAM);
|
||||
start_stream_if_needed();
|
||||
});
|
||||
|
||||
addEventListener('unload', function () {
|
||||
// let chance to other tabs to be a streamer via firing 'storage' event
|
||||
if (notifications) helpers.storage.remove(STORAGE_KEY_STREAM);
|
||||
addEventListener("unload", function () {
|
||||
// let chance to other tabs to be a streamer via firing 'storage' event
|
||||
if (notifications) helpers.storage.remove(STORAGE_KEY_STREAM);
|
||||
});
|
||||
|
||||
function exponential_backoff(
|
||||
fn,
|
||||
maxRetries = 5,
|
||||
initialDelay = 1000,
|
||||
randomnessFactor = 0.5,
|
||||
) {
|
||||
let attempt = 0;
|
||||
|
||||
return function tryFunction() {
|
||||
fn()
|
||||
.then((response) => {
|
||||
attempt = 0;
|
||||
})
|
||||
.catch((error) => {
|
||||
if (attempt < maxRetries) {
|
||||
attempt++;
|
||||
let delay = initialDelay * Math.pow(2, attempt); // Exponential backoff
|
||||
let randomMultiplier = 1 + Math.random() * randomnessFactor;
|
||||
delay = delay * randomMultiplier;
|
||||
console.log(
|
||||
`Attempt ${attempt} failed. Retrying in ${(delay / 1000).toPrecision(2)} seconds...`,
|
||||
);
|
||||
setTimeout(tryFunction, delay); // Retry after delay
|
||||
} else {
|
||||
console.log("Max retries reached. Operation failed:", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user