recents
This commit is contained in:
@@ -5,6 +5,8 @@ const elements = {
|
||||
url: document.querySelector('#stream-url'),
|
||||
next: document.querySelector('#next'),
|
||||
entryMessage: document.querySelector('#entry-message'),
|
||||
recentPanel: document.querySelector('#recent-panel'),
|
||||
recentList: document.querySelector('#recent-list'),
|
||||
audio: document.querySelector('#audio'),
|
||||
canvas: document.querySelector('#screen'),
|
||||
stage: document.querySelector('#video-stage'),
|
||||
@@ -28,8 +30,11 @@ const state = {
|
||||
frameCount: 0,
|
||||
controlsVisible: true,
|
||||
hideControlsTimer: 0,
|
||||
recentUrls: [],
|
||||
};
|
||||
|
||||
void loadRecentUrls();
|
||||
|
||||
elements.form.addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
setEntryMessage('');
|
||||
@@ -51,6 +56,7 @@ elements.form.addEventListener('submit', async (event) => {
|
||||
}
|
||||
|
||||
showPlayer();
|
||||
void loadRecentUrls();
|
||||
startSession(payload);
|
||||
} catch (error) {
|
||||
stopSession();
|
||||
@@ -60,6 +66,23 @@ elements.form.addEventListener('submit', async (event) => {
|
||||
}
|
||||
});
|
||||
|
||||
elements.recentList.addEventListener('click', (event) => {
|
||||
const button = event.target.closest('[data-recent-index]');
|
||||
|
||||
if (!button) {
|
||||
return;
|
||||
}
|
||||
|
||||
const item = state.recentUrls[Number(button.dataset.recentIndex)];
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
elements.url.value = item.url;
|
||||
elements.form.requestSubmit();
|
||||
});
|
||||
|
||||
elements.stage.addEventListener('pointerup', (event) => {
|
||||
if (!state.session || event.target.closest('.controls')) {
|
||||
return;
|
||||
@@ -387,6 +410,7 @@ function syncControlLabels() {
|
||||
function showEntry() {
|
||||
elements.playerScreen.hidden = true;
|
||||
elements.entryScreen.hidden = false;
|
||||
void loadRecentUrls();
|
||||
elements.url.focus();
|
||||
}
|
||||
|
||||
@@ -410,4 +434,40 @@ function setEntryMessage(message) {
|
||||
function setFormBusy(isBusy) {
|
||||
elements.url.disabled = isBusy;
|
||||
elements.next.disabled = isBusy;
|
||||
|
||||
for (const button of elements.recentList.querySelectorAll('button')) {
|
||||
button.disabled = isBusy;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadRecentUrls() {
|
||||
try {
|
||||
const response = await fetch('/api/recent-urls', { cache: 'no-store' });
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to load recent URLs.');
|
||||
}
|
||||
|
||||
const payload = await response.json();
|
||||
state.recentUrls = Array.isArray(payload.urls) ? payload.urls : [];
|
||||
renderRecentUrls();
|
||||
} catch {
|
||||
state.recentUrls = [];
|
||||
renderRecentUrls();
|
||||
}
|
||||
}
|
||||
|
||||
function renderRecentUrls() {
|
||||
elements.recentList.replaceChildren(
|
||||
...state.recentUrls.map((item, index) => {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'recent-url';
|
||||
button.dataset.recentIndex = String(index);
|
||||
button.textContent = item.displayUrl || item.url;
|
||||
return button;
|
||||
}),
|
||||
);
|
||||
|
||||
elements.recentPanel.hidden = state.recentUrls.length === 0;
|
||||
}
|
||||
|
||||
@@ -9,18 +9,23 @@
|
||||
<body>
|
||||
<main class="app">
|
||||
<section id="entry-screen" class="entry-screen" aria-label="Stream URL">
|
||||
<form id="stream-form" class="url-form">
|
||||
<label class="sr-only" for="stream-url">Video stream URL</label>
|
||||
<input
|
||||
id="stream-url"
|
||||
name="url"
|
||||
type="url"
|
||||
placeholder="Video stream URL"
|
||||
autocomplete="off"
|
||||
required
|
||||
>
|
||||
<button id="next" type="submit">Next</button>
|
||||
</form>
|
||||
<div class="entry-stack">
|
||||
<form id="stream-form" class="url-form">
|
||||
<label class="sr-only" for="stream-url">Video stream URL</label>
|
||||
<input
|
||||
id="stream-url"
|
||||
name="url"
|
||||
type="url"
|
||||
placeholder="Video stream URL"
|
||||
autocomplete="off"
|
||||
required
|
||||
>
|
||||
<button id="next" type="submit">Next</button>
|
||||
</form>
|
||||
<div id="recent-panel" class="recent-panel" aria-label="Recently played URLs" hidden>
|
||||
<div id="recent-list" class="recent-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="entry-message" class="message" role="status" aria-live="polite"></div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -62,9 +62,15 @@ button:disabled {
|
||||
linear-gradient(135deg, #070707 0%, #11100d 48%, #030303 100%);
|
||||
}
|
||||
|
||||
.entry-stack {
|
||||
display: grid;
|
||||
width: min(760px, 100%);
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.url-form {
|
||||
display: flex;
|
||||
width: min(760px, 100%);
|
||||
width: 100%;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid var(--line);
|
||||
@@ -105,6 +111,43 @@ button:disabled {
|
||||
color: #070707;
|
||||
}
|
||||
|
||||
.recent-panel {
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 28px;
|
||||
background: rgba(255, 255, 255, 0.07);
|
||||
box-shadow: 0 18px 80px rgba(0, 0, 0, 0.28);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.recent-list {
|
||||
display: grid;
|
||||
max-height: min(42vh, 24rem);
|
||||
overflow: auto;
|
||||
padding: 0.35rem;
|
||||
}
|
||||
|
||||
.recent-url {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
border: 0;
|
||||
border-radius: 20px;
|
||||
padding: 0.85rem 1rem;
|
||||
color: var(--soft);
|
||||
text-align: left;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.recent-url:hover,
|
||||
.recent-url:focus {
|
||||
color: var(--fg);
|
||||
background: rgba(255, 255, 255, 0.09);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.message {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
|
||||
Reference in New Issue
Block a user