added toast notifications
This commit is contained in:
parent
e512862aed
commit
018b4d3b30
7 changed files with 159 additions and 20 deletions
|
@ -5,6 +5,7 @@
|
|||
<meta charset='utf-8'>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel='stylesheet' href='/style.css'>
|
||||
<link rel='stylesheet' href='/toastify.min.css'>
|
||||
<link rel='icon' type='image/png' sizes='32x32' href='/favicon-32x32.png'>
|
||||
<link rel='icon' type='image/png' sizes='16x16' href='/favicon-16x16.png'>
|
||||
<link rel='icon' type='image/svg' href='/favicon.svg'>
|
||||
|
@ -155,6 +156,7 @@
|
|||
</footer>
|
||||
<script src='/htmx.min.js'></script>
|
||||
<script src='/howler.min.js'></script>
|
||||
<script src='/toastify.min.js'></script>
|
||||
<script src='/radiostasis.js'></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -101,7 +101,7 @@ var Player = /** @class */ (function () {
|
|||
this.stopPlaybackAndResetUi();
|
||||
this.setPauseButtonUI();
|
||||
this.episode = episode;
|
||||
this.playlist.removeEpisode(episode);
|
||||
this.playlist.removeEpisode(episode, false, false);
|
||||
this.updateNowPlayingUI(true);
|
||||
void fetch("/api/r/".concat(episode.file))
|
||||
.then(function (res) { return __awaiter(_this, void 0, void 0, function () {
|
||||
|
@ -111,7 +111,7 @@ var Player = /** @class */ (function () {
|
|||
switch (_a.label) {
|
||||
case 0:
|
||||
if (!res.ok) {
|
||||
this.setErrorUI("API returned ".concat(res.status));
|
||||
this.setErrorUI("Error fetching episode (".concat(res.status, ")"));
|
||||
return [2 /*return*/];
|
||||
}
|
||||
return [4 /*yield*/, res.json()];
|
||||
|
@ -139,14 +139,14 @@ var Player = /** @class */ (function () {
|
|||
_this.stopTicker();
|
||||
},
|
||||
onend: function () { return _this.nextEpisode(); },
|
||||
onloaderror: function () { return _this.setErrorUI('Playback error'); },
|
||||
onloaderror: function () { return _this.setErrorUI('Error playing episode.'); },
|
||||
});
|
||||
return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
}); })
|
||||
.catch(function () {
|
||||
_this.setErrorUI('Fetch episode error');
|
||||
_this.setErrorUI('Error downloading episode.');
|
||||
});
|
||||
};
|
||||
Player.prototype.isPlaying = function () {
|
||||
|
@ -157,9 +157,16 @@ var Player = /** @class */ (function () {
|
|||
};
|
||||
Player.prototype.setErrorUI = function (message) {
|
||||
this.stopPlaybackAndResetUi();
|
||||
this.seriesName.innerHTML = 'Error playing episode';
|
||||
this.episodeName.innerHTML = message;
|
||||
this.nowPlaying.classList.add('error');
|
||||
Toastify({
|
||||
text: message,
|
||||
gravity: 'bottom',
|
||||
position: 'right',
|
||||
style: {
|
||||
marginBottom: '10ex',
|
||||
background: '#a00',
|
||||
color: '#fff',
|
||||
},
|
||||
}).showToast();
|
||||
};
|
||||
Player.prototype.playPause = function () {
|
||||
if (this.howl && this.howl.playing())
|
||||
|
@ -351,28 +358,35 @@ var Playlist = /** @class */ (function () {
|
|||
for (var _i = 0, episodes_1 = episodes; _i < episodes_1.length; _i++) {
|
||||
var episode = episodes_1[_i];
|
||||
if (this.isQueued(episode))
|
||||
this.removeEpisode(episode, true);
|
||||
this.removeEpisode(episode, true, false);
|
||||
var litem = this.createQueueListItem(episode);
|
||||
this.episodes.push([episode, litem]);
|
||||
this.episodeHash.add(episode.id);
|
||||
this.queueList.appendChild(litem);
|
||||
}
|
||||
this.notify(episodes.length == 1
|
||||
? 'Episode added to queue.'
|
||||
: "".concat(episodes.length, " episodes added to queue."));
|
||||
this.playlistChanged();
|
||||
};
|
||||
Playlist.prototype.unshiftEpisodes = function (episodes) {
|
||||
for (var i = episodes.length - 1; i >= 0; i--) {
|
||||
var episode = episodes[i];
|
||||
if (this.isQueued(episode))
|
||||
this.removeEpisode(episode, true);
|
||||
this.removeEpisode(episode, true, false);
|
||||
var litem = this.createQueueListItem(episode);
|
||||
this.episodes.unshift([episode, litem]);
|
||||
this.episodeHash.add(episode.id);
|
||||
this.queueList.prepend(litem);
|
||||
}
|
||||
this.notify(episodes.length == 1
|
||||
? 'Episode added to queue.'
|
||||
: "".concat(episodes.length, " episodes added to queue."));
|
||||
this.playlistChanged();
|
||||
};
|
||||
Playlist.prototype.removeEpisode = function (episode, ignoreChanged) {
|
||||
Playlist.prototype.removeEpisode = function (episode, ignoreChanged, notify) {
|
||||
if (ignoreChanged === void 0) { ignoreChanged = false; }
|
||||
if (notify === void 0) { notify = true; }
|
||||
if (this.isQueued(episode)) {
|
||||
var idx = this.episodes.findIndex(function (e) { return e[0].id == episode.id; });
|
||||
var deleted = this.episodes.splice(idx, 1);
|
||||
|
@ -381,6 +395,8 @@ var Playlist = /** @class */ (function () {
|
|||
}
|
||||
if (!ignoreChanged)
|
||||
this.playlistChanged();
|
||||
if (notify)
|
||||
this.notify('Episode removed from queue.');
|
||||
};
|
||||
Playlist.prototype.isQueued = function (episode) {
|
||||
return this.episodeHash.has(episode.id);
|
||||
|
@ -389,10 +405,14 @@ var Playlist = /** @class */ (function () {
|
|||
this.playlistChanged();
|
||||
};
|
||||
Playlist.prototype.clearPlaylist = function () {
|
||||
var removed = this.episodes.length;
|
||||
this.episodes.length = 0;
|
||||
this.episodeHash.clear();
|
||||
this.queueList.innerHTML = '';
|
||||
this.playlistChanged();
|
||||
if (removed > 0) {
|
||||
this.notify('Playlist cleared.');
|
||||
}
|
||||
};
|
||||
Playlist.prototype.playlistChanged = function () {
|
||||
for (var _i = 0, _a = this.changedHandlers; _i < _a.length; _i++) {
|
||||
|
@ -449,6 +469,18 @@ var Playlist = /** @class */ (function () {
|
|||
item.appendChild(controls);
|
||||
return item;
|
||||
};
|
||||
Playlist.prototype.notify = function (message) {
|
||||
Toastify({
|
||||
text: message,
|
||||
gravity: 'bottom',
|
||||
position: 'right',
|
||||
style: {
|
||||
marginBottom: '10ex',
|
||||
background: '#090',
|
||||
color: '#fff',
|
||||
},
|
||||
}).showToast();
|
||||
};
|
||||
return Playlist;
|
||||
}());
|
||||
var Radiostasis = /** @class */ (function () {
|
||||
|
|
15
site/toastify.min.css
vendored
Normal file
15
site/toastify.min.css
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Minified by jsDelivr using clean-css v5.3.0.
|
||||
* Original file: /npm/toastify-js@1.12.0/src/toastify.css
|
||||
*
|
||||
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
|
||||
*/
|
||||
/*!
|
||||
* Toastify js 1.12.0
|
||||
* https://github.com/apvarun/toastify-js
|
||||
* @license MIT licensed
|
||||
*
|
||||
* Copyright (C) 2018 Varun A P
|
||||
*/
|
||||
.toastify{padding:12px 20px;color:#fff;display:inline-block;box-shadow:0 3px 6px -1px rgba(0,0,0,.12),0 10px 36px -4px rgba(77,96,232,.3);background:-webkit-linear-gradient(315deg,#73a5ff,#5477f5);background:linear-gradient(135deg,#73a5ff,#5477f5);position:fixed;opacity:0;transition:all .4s cubic-bezier(.215, .61, .355, 1);border-radius:2px;cursor:pointer;text-decoration:none;max-width:calc(50% - 20px);z-index:2147483647}.toastify.on{opacity:1}.toast-close{background:0 0;border:0;color:#fff;cursor:pointer;font-family:inherit;font-size:1em;opacity:.4;padding:0 5px}.toastify-right{right:15px}.toastify-left{left:15px}.toastify-top{top:-150px}.toastify-bottom{bottom:-150px}.toastify-rounded{border-radius:25px}.toastify-avatar{width:1.5em;height:1.5em;margin:-7px 5px;border-radius:2px}.toastify-center{margin-left:auto;margin-right:auto;left:0;right:0;max-width:fit-content;max-width:-moz-fit-content}@media only screen and (max-width:360px){.toastify-left,.toastify-right{margin-left:auto;margin-right:auto;left:0;right:0;max-width:fit-content}}
|
||||
/*# sourceMappingURL=/sm/cb4335d1b03e933ed85cb59fffa60cf51f07567ed09831438c60f59afd166464.map */
|
15
site/toastify.min.js
vendored
Normal file
15
site/toastify.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -111,13 +111,13 @@ class Player {
|
|||
this.stopPlaybackAndResetUi();
|
||||
this.setPauseButtonUI();
|
||||
this.episode = episode;
|
||||
this.playlist.removeEpisode(episode);
|
||||
this.playlist.removeEpisode(episode, false, false);
|
||||
this.updateNowPlayingUI(true);
|
||||
|
||||
void fetch(`/api/r/${episode.file}`)
|
||||
.then(async (res) => {
|
||||
if (!res.ok) {
|
||||
this.setErrorUI(`API returned ${res.status}`);
|
||||
this.setErrorUI(`Error fetching episode (${res.status})`);
|
||||
return;
|
||||
}
|
||||
const link = <UrlResponse>await res.json();
|
||||
|
@ -142,11 +142,11 @@ class Player {
|
|||
this.stopTicker();
|
||||
},
|
||||
onend: () => this.nextEpisode(),
|
||||
onloaderror: () => this.setErrorUI('Playback error'),
|
||||
onloaderror: () => this.setErrorUI('Error playing episode.'),
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
this.setErrorUI('Fetch episode error');
|
||||
this.setErrorUI('Error downloading episode.');
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -159,9 +159,16 @@ class Player {
|
|||
|
||||
private setErrorUI(message: string): void {
|
||||
this.stopPlaybackAndResetUi();
|
||||
this.seriesName.innerHTML = 'Error playing episode';
|
||||
this.episodeName.innerHTML = message;
|
||||
this.nowPlaying.classList.add('error');
|
||||
Toastify({
|
||||
text: message,
|
||||
gravity: 'bottom',
|
||||
position: 'right',
|
||||
style: {
|
||||
marginBottom: '10ex',
|
||||
background: '#a00',
|
||||
color: '#fff',
|
||||
},
|
||||
}).showToast();
|
||||
}
|
||||
|
||||
private playPause(): void {
|
||||
|
|
|
@ -63,28 +63,42 @@ class Playlist {
|
|||
|
||||
public pushEpisodes(episodes: Array<Episode>): void {
|
||||
for (const episode of episodes) {
|
||||
if (this.isQueued(episode)) this.removeEpisode(episode, true);
|
||||
if (this.isQueued(episode)) this.removeEpisode(episode, true, false);
|
||||
const litem = this.createQueueListItem(episode);
|
||||
this.episodes.push([episode, litem]);
|
||||
this.episodeHash.add(episode.id);
|
||||
this.queueList.appendChild(litem);
|
||||
}
|
||||
this.notify(
|
||||
episodes.length == 1
|
||||
? 'Episode added to queue.'
|
||||
: `${episodes.length} episodes added to queue.`
|
||||
);
|
||||
this.playlistChanged();
|
||||
}
|
||||
|
||||
public unshiftEpisodes(episodes: Array<Episode>): void {
|
||||
for (let i = episodes.length - 1; i >= 0; i--) {
|
||||
const episode = episodes[i];
|
||||
if (this.isQueued(episode)) this.removeEpisode(episode, true);
|
||||
if (this.isQueued(episode)) this.removeEpisode(episode, true, false);
|
||||
const litem = this.createQueueListItem(episode);
|
||||
this.episodes.unshift([episode, litem]);
|
||||
this.episodeHash.add(episode.id);
|
||||
this.queueList.prepend(litem);
|
||||
}
|
||||
this.notify(
|
||||
episodes.length == 1
|
||||
? 'Episode added to queue.'
|
||||
: `${episodes.length} episodes added to queue.`
|
||||
);
|
||||
this.playlistChanged();
|
||||
}
|
||||
|
||||
public removeEpisode(episode: Episode, ignoreChanged = false): void {
|
||||
public removeEpisode(
|
||||
episode: Episode,
|
||||
ignoreChanged = false,
|
||||
notify = true
|
||||
): void {
|
||||
if (this.isQueued(episode)) {
|
||||
const idx = this.episodes.findIndex((e) => e[0].id == episode.id);
|
||||
const deleted = this.episodes.splice(idx, 1);
|
||||
|
@ -92,6 +106,7 @@ class Playlist {
|
|||
deleted[0][1].remove();
|
||||
}
|
||||
if (!ignoreChanged) this.playlistChanged();
|
||||
if (notify) this.notify('Episode removed from queue.');
|
||||
}
|
||||
|
||||
public isQueued(episode: Episode): boolean {
|
||||
|
@ -103,10 +118,14 @@ class Playlist {
|
|||
}
|
||||
|
||||
public clearPlaylist(): void {
|
||||
const removed = this.episodes.length;
|
||||
this.episodes.length = 0;
|
||||
this.episodeHash.clear();
|
||||
this.queueList.innerHTML = '';
|
||||
this.playlistChanged();
|
||||
if (removed > 0) {
|
||||
this.notify('Playlist cleared.');
|
||||
}
|
||||
}
|
||||
|
||||
private playlistChanged(): void {
|
||||
|
@ -162,4 +181,17 @@ class Playlist {
|
|||
item.appendChild(controls);
|
||||
return item;
|
||||
}
|
||||
|
||||
private notify(message: string): void {
|
||||
Toastify({
|
||||
text: message,
|
||||
gravity: 'bottom',
|
||||
position: 'right',
|
||||
style: {
|
||||
marginBottom: '10ex',
|
||||
background: '#090',
|
||||
color: '#fff',
|
||||
},
|
||||
}).showToast();
|
||||
}
|
||||
}
|
||||
|
|
36
src/typings/toastify.d.ts
vendored
Normal file
36
src/typings/toastify.d.ts
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
declare namespace StartToastifyInstance {
|
||||
function reposition(): void;
|
||||
interface Offset {
|
||||
x: number | string;
|
||||
y: number | string;
|
||||
}
|
||||
interface Options {
|
||||
text?: string | undefined;
|
||||
node?: Node | undefined;
|
||||
duration?: number | undefined;
|
||||
selector?: string | Node | undefined;
|
||||
destination?: string | undefined;
|
||||
newWindow?: boolean | undefined;
|
||||
close?: boolean | undefined;
|
||||
gravity?: 'top' | 'bottom' | undefined;
|
||||
position?: 'left' | 'center' | 'right' | undefined;
|
||||
avatar?: string | undefined;
|
||||
className?: string | undefined;
|
||||
stopOnFocus?: boolean | undefined;
|
||||
callback?: (() => void) | undefined;
|
||||
onClick?: (() => void) | undefined;
|
||||
offset?: Offset | undefined;
|
||||
escapeMarkup?: boolean | undefined;
|
||||
style?: { [cssRule: string]: string };
|
||||
oldestFirst?: boolean | undefined;
|
||||
}
|
||||
}
|
||||
declare class Toastify {
|
||||
readonly options: StartToastifyInstance.Options;
|
||||
readonly toastElement: Element | null;
|
||||
showToast(): void;
|
||||
hideToast(): void;
|
||||
}
|
||||
declare function StartToastifyInstance(options?: Toastify.Options): Toastify;
|
||||
export as namespace Toastify;
|
||||
export = StartToastifyInstance;
|
Loading…
Reference in a new issue