Merge pull request #129 from WardPearce/update/ui-improvements
Update/UI improvements
This commit is contained in:
@@ -21,13 +21,13 @@
|
||||
import { getDynamicTheme } from './theme';
|
||||
|
||||
export let data: { video: VideoPlay; content: PhasedDescription; playlistId: string | null };
|
||||
export let currentTime: number = 0;
|
||||
export let audioMode = false;
|
||||
export let player: MediaPlayerElement;
|
||||
export let isSyncing: boolean = false;
|
||||
|
||||
let src: PlayerSrc = [];
|
||||
let categoryBeingSkipped = '';
|
||||
let playerIsLive = false;
|
||||
|
||||
export function seekTo(time: number) {
|
||||
if (typeof player !== 'undefined') {
|
||||
@@ -38,14 +38,8 @@
|
||||
const proxyVideos = get(playerProxyVideos);
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
const defaultVolume = localStorage.getItem('volume');
|
||||
if (defaultVolume) {
|
||||
player.volume = Number(defaultVolume);
|
||||
}
|
||||
} catch {}
|
||||
|
||||
if (!data.video.hlsUrl) {
|
||||
playerIsLive = false;
|
||||
if (data.video.captions) {
|
||||
data.video.captions.forEach(async (caption) => {
|
||||
player.textTracks.add({
|
||||
@@ -81,10 +75,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
player.addEventListener('time-update', () => {
|
||||
currentTime = player.currentTime;
|
||||
});
|
||||
|
||||
player.addEventListener('pause', () => {
|
||||
savePlayerPos();
|
||||
});
|
||||
@@ -121,6 +111,10 @@
|
||||
}
|
||||
|
||||
if (get(playerDash)) {
|
||||
player.addEventListener('dash-can-play', () => {
|
||||
loadPlayerPos();
|
||||
});
|
||||
|
||||
src = [{ src: data.video.dashUrl + '?local=true', type: 'application/dash+xml' }];
|
||||
} else {
|
||||
let formattedSrc;
|
||||
@@ -138,17 +132,11 @@
|
||||
width: Number(quality[0])
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (get(playerSavePlaybackPosition)) {
|
||||
try {
|
||||
const playerPos = localStorage.getItem(`v_${data.video.videoId}`);
|
||||
if (playerPos) {
|
||||
player.currentTime = Number(playerPos);
|
||||
}
|
||||
} catch {}
|
||||
loadPlayerPos();
|
||||
}
|
||||
} else {
|
||||
playerIsLive = true;
|
||||
src = [
|
||||
{
|
||||
src: data.video.hlsUrl + '?local=true',
|
||||
@@ -189,7 +177,20 @@
|
||||
document.documentElement.style.setProperty('--audio-bg', currentTheme['--surface']);
|
||||
});
|
||||
|
||||
function loadPlayerPos() {
|
||||
if (get(playerSavePlaybackPosition)) {
|
||||
try {
|
||||
const playerPos = localStorage.getItem(`v_${data.video.videoId}`);
|
||||
if (playerPos) {
|
||||
player.currentTime = Number(playerPos);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
function savePlayerPos() {
|
||||
if (data.video.hlsUrl) return;
|
||||
|
||||
try {
|
||||
if (get(playerSavePlaybackPosition) && player.currentTime) {
|
||||
if (player.currentTime < player.duration - 10 && player.currentTime > 10) {
|
||||
@@ -216,7 +217,7 @@
|
||||
autoPlay={$playerAutoPlay && !isSyncing}
|
||||
loop={$playerAlwaysLoop}
|
||||
title={data.video.title}
|
||||
streamType={data.video.hlsUrl ? 'live' : 'on-demand'}
|
||||
streamType={playerIsLive ? 'live' : 'on-demand'}
|
||||
viewType={audioMode ? 'audio' : 'video'}
|
||||
keep-alive
|
||||
{src}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { interfaceSearchSuggestions } from '../store';
|
||||
import { getSearchSuggestions } from './Api';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let searchSuggestions = false;
|
||||
interfaceSearchSuggestions.subscribe((value) => (searchSuggestions = value));
|
||||
|
||||
@@ -11,6 +14,8 @@
|
||||
let suggestionsForSearch: string[] = [];
|
||||
let selectedSuggestionIndex: number = -1;
|
||||
|
||||
let showSearchBox = false;
|
||||
|
||||
let debounceTimer: NodeJS.Timeout;
|
||||
function debouncedSearch(event: any) {
|
||||
if (!searchSuggestions) return;
|
||||
@@ -28,6 +33,7 @@
|
||||
function handleSubmit() {
|
||||
selectedSuggestionIndex = -1;
|
||||
goto(`/search/${encodeURIComponent(search)}`);
|
||||
dispatch('searchSubmitted');
|
||||
}
|
||||
|
||||
function handleKeyDown(event: KeyboardEvent) {
|
||||
@@ -50,43 +56,50 @@
|
||||
<form on:submit|preventDefault={handleSubmit}>
|
||||
<div class="field prefix round fill no-margin search">
|
||||
<i class="front">search</i>
|
||||
<input bind:value={search} on:click={() => document.getElementById('search')?.focus()} />
|
||||
<menu class="min">
|
||||
<div class="field large prefix suffix no-margin fixed">
|
||||
<i class="front">arrow_back</i>
|
||||
<input
|
||||
placeholder={$_('searchPlaceholder')}
|
||||
type="text"
|
||||
id="search"
|
||||
required
|
||||
bind:value={search}
|
||||
on:keydown={handleKeyDown}
|
||||
on:keyup={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
handleSubmit();
|
||||
} else {
|
||||
debouncedSearch(event);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<i class="front" on:click={() => (search = '', suggestionsForSearch = [], selectedSuggestionIndex = -1)}>close</i>
|
||||
</div>
|
||||
{#if searchSuggestions}
|
||||
{#each suggestionsForSearch as suggestion, index}
|
||||
<a
|
||||
on:click={() => {
|
||||
search = suggestion;
|
||||
handleSubmit();
|
||||
<input bind:value={search} on:click={() => (showSearchBox = true)} />
|
||||
{#if showSearchBox}
|
||||
<menu class="min">
|
||||
<div class="field large prefix suffix no-margin fixed">
|
||||
<i class="front" on:click={() => dispatch('searchCancelled')}>arrow_back</i>
|
||||
<input
|
||||
placeholder={$_('searchPlaceholder')}
|
||||
type="text"
|
||||
autofocus={true}
|
||||
required
|
||||
bind:value={search}
|
||||
on:keydown={handleKeyDown}
|
||||
on:keyup={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
handleSubmit();
|
||||
} else {
|
||||
debouncedSearch(event);
|
||||
}
|
||||
}}
|
||||
class="row"
|
||||
class:selected={index === selectedSuggestionIndex}
|
||||
href={`/search/${encodeURIComponent(suggestion)}`}
|
||||
/>
|
||||
<i
|
||||
class="front"
|
||||
on:click={() => (
|
||||
(search = ''), (suggestionsForSearch = []), (selectedSuggestionIndex = -1)
|
||||
)}>close</i
|
||||
>
|
||||
<div>{suggestion}</div>
|
||||
</a>
|
||||
{/each}
|
||||
{/if}
|
||||
</menu>
|
||||
</div>
|
||||
{#if searchSuggestions}
|
||||
{#each suggestionsForSearch as suggestion, index}
|
||||
<a
|
||||
on:click={() => {
|
||||
search = suggestion;
|
||||
handleSubmit();
|
||||
}}
|
||||
class="row"
|
||||
class:selected={index === selectedSuggestionIndex}
|
||||
href={`/search/${encodeURIComponent(suggestion)}`}
|
||||
>
|
||||
<div>{suggestion}</div>
|
||||
</a>
|
||||
{/each}
|
||||
{/if}
|
||||
</menu>
|
||||
{/if}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
@@ -4,18 +4,25 @@
|
||||
import { get } from 'svelte/store';
|
||||
import {
|
||||
deArrowEnabled,
|
||||
interfacePreviewVideoOnHover,
|
||||
playerProxyVideos,
|
||||
playerSavePlaybackPosition,
|
||||
syncPartyConnections,
|
||||
syncPartyPeer
|
||||
} from '../store';
|
||||
import { getDeArrow, getThumbnail, getVideo } from './Api';
|
||||
import type { Notification, PlaylistPageVideo, Video, VideoBase } from './Api/model';
|
||||
import { cleanNumber, proxyVideoUrl, truncate, videoLength } from './misc';
|
||||
import type { Notification, PlaylistPageVideo, Video, VideoBase, VideoPlay } from './Api/model';
|
||||
import { cleanNumber, proxyVideoUrl, videoLength } from './misc';
|
||||
import type { PlayerEvents } from './player';
|
||||
|
||||
export let video: VideoBase | Video | Notification | PlaylistPageVideo;
|
||||
export let playlistId: string = '';
|
||||
|
||||
let showVideoPreview: boolean = false;
|
||||
let videoPreview: VideoPlay | null = null;
|
||||
let videoPreviewMuted: boolean = true;
|
||||
let videoPreviewVolume: number = 0.4;
|
||||
|
||||
let watchUrl = new URL(`${import.meta.env.VITE_DEFAULT_FRONTEND_URL}/watch/${video.videoId}`);
|
||||
|
||||
if (playlistId !== '') {
|
||||
@@ -30,7 +37,6 @@
|
||||
|
||||
let loading = true;
|
||||
let loaded = false;
|
||||
let failed = false;
|
||||
|
||||
let img: HTMLImageElement;
|
||||
|
||||
@@ -118,7 +124,6 @@
|
||||
};
|
||||
img.onerror = () => {
|
||||
loading = false;
|
||||
failed = true;
|
||||
};
|
||||
});
|
||||
|
||||
@@ -139,52 +144,126 @@
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function previewVideo() {
|
||||
if (!get(interfacePreviewVideoOnHover)) return;
|
||||
|
||||
showVideoPreview = true;
|
||||
try {
|
||||
videoPreview = await getVideo(video.videoId, get(playerProxyVideos));
|
||||
if (videoPreview.hlsUrl) {
|
||||
showVideoPreview = false;
|
||||
videoPreview = null;
|
||||
} else {
|
||||
try {
|
||||
const playerSettings = localStorage.getItem('video-player');
|
||||
if (playerSettings && typeof playerSettings === 'object' && 'volume' in playerSettings) {
|
||||
videoPreviewVolume = Number(playerSettings['volume']);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
} catch {
|
||||
showVideoPreview = true;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<a
|
||||
class="wave"
|
||||
style="width: 100%; overflow: hidden;min-height:100px;"
|
||||
href={watchUrl.toString()}
|
||||
data-sveltekit-preload-data="off"
|
||||
on:click={syncChangeVideo}
|
||||
<div
|
||||
style="position: relative;"
|
||||
on:mouseover={previewVideo}
|
||||
on:mouseleave={() => (showVideoPreview = false)}
|
||||
on:focus={() => {}}
|
||||
role="region"
|
||||
>
|
||||
{#if loading}
|
||||
<progress class="circle"></progress>
|
||||
{:else if loaded}
|
||||
<img
|
||||
class="responsive"
|
||||
style="max-width: 100%;min-height: 160px;"
|
||||
src={img.src}
|
||||
alt="Thumbnail for video"
|
||||
/>
|
||||
{:else}
|
||||
<p>{$_('thumbnail.failedToLoadImage')}</p>
|
||||
{/if}
|
||||
{#if progress}
|
||||
<progress
|
||||
class="absolute right bottom"
|
||||
style="z-index: 1;"
|
||||
value={progress}
|
||||
max={video.lengthSeconds}
|
||||
></progress>
|
||||
{/if}
|
||||
{#if !('liveVideo' in video) || !video.liveVideo}
|
||||
{#if video.lengthSeconds !== 0}
|
||||
<div class="absolute right bottom small-margin black white-text small-text thumbnail-corner">
|
||||
{videoLength(video.lengthSeconds)}
|
||||
<a
|
||||
class="wave thumbnail"
|
||||
href={watchUrl.toString()}
|
||||
data-sveltekit-preload-data="off"
|
||||
on:click={syncChangeVideo}
|
||||
>
|
||||
{#if loading}
|
||||
<progress class="circle"></progress>
|
||||
{:else if loaded}
|
||||
{#if showVideoPreview && videoPreview}
|
||||
<div style="max-width: 100%; max-height: 200px;">
|
||||
<video
|
||||
style="max-width: 100%; max-height: 200px;"
|
||||
autoplay
|
||||
poster={img.src}
|
||||
width="100%"
|
||||
height="100%"
|
||||
muted={videoPreviewMuted}
|
||||
controls={false}
|
||||
volume={videoPreviewVolume}
|
||||
src={videoPreview.formatStreams[0].url}
|
||||
>
|
||||
</video>
|
||||
</div>
|
||||
{:else}
|
||||
<img
|
||||
class="responsive"
|
||||
style="max-width: 100%;min-height: 160px;"
|
||||
src={img.src}
|
||||
alt="Thumbnail for video"
|
||||
/>
|
||||
{/if}
|
||||
{:else}
|
||||
<p>{$_('thumbnail.failedToLoadImage')}</p>
|
||||
{/if}
|
||||
{#if progress}
|
||||
<progress
|
||||
class="absolute right bottom"
|
||||
style="z-index: 1;"
|
||||
value={progress}
|
||||
max={video.lengthSeconds}
|
||||
></progress>
|
||||
{/if}
|
||||
{#if !('liveVideo' in video) || !video.liveVideo}
|
||||
{#if video.lengthSeconds !== 0}
|
||||
<div
|
||||
class="absolute right bottom small-margin black white-text small-text thumbnail-corner"
|
||||
>
|
||||
{videoLength(video.lengthSeconds)}
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="absolute right bottom small-margin red white-text small-text thumbnail-corner">
|
||||
{$_('thumbnail.live')}
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="absolute right bottom small-margin red white-text small-text thumbnail-corner">
|
||||
{$_('thumbnail.live')}
|
||||
</div>
|
||||
</a>
|
||||
{#if showVideoPreview && videoPreview}
|
||||
<button
|
||||
class="no-padding"
|
||||
style="position: absolute; bottom: 10px; left: 10px; width: 30px; height: 30px;"
|
||||
on:click={() => {
|
||||
videoPreviewMuted = !videoPreviewMuted;
|
||||
}}
|
||||
>
|
||||
<i>
|
||||
{#if videoPreviewMuted}
|
||||
volume_off
|
||||
{:else}
|
||||
volume_up
|
||||
{/if}
|
||||
</i>
|
||||
</button>
|
||||
{/if}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="small-padding">
|
||||
<nav class="no-margin">
|
||||
<div class="max">
|
||||
<a href={watchUrl.toString()}><div class="bold">{truncate(video.title)}</div></a>
|
||||
<div>
|
||||
<a
|
||||
href={watchUrl.toString()}
|
||||
style="display: flex; justify-content:flex-start; position: absolute; width: 100%;"
|
||||
><div class="bold" style="white-space: nowrap; overflow: hidden;text-overflow: ellipsis;">
|
||||
{video.title}
|
||||
</div>
|
||||
|
||||
<div class="tooltip bottom small">{video.title}</div>
|
||||
</a>
|
||||
<div style="margin-top: 1.4em;">
|
||||
<a href={`/channel/${video.authorId}`}>{video.author}</a
|
||||
>{#if !('publishedText' in video) && 'viewCountText' in video}
|
||||
• {video.viewCountText}{/if}
|
||||
@@ -200,3 +279,17 @@
|
||||
|
||||
<canvas id="canvas" style="display: none;"></canvas>
|
||||
<div id="video-container" style="display: none;"></div>
|
||||
|
||||
<style>
|
||||
.thumbnail {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
max-height: 160px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 650px) {
|
||||
.thumbnail {
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
deArrowEnabled,
|
||||
deArrowInstance,
|
||||
deArrowThumbnailInstance,
|
||||
interfacePreviewVideoOnHover,
|
||||
interfaceSearchSuggestions,
|
||||
playerAlwaysLoop,
|
||||
playerAutoPlay,
|
||||
@@ -87,6 +88,11 @@ const persistedStores = [
|
||||
store: interfaceSearchSuggestions,
|
||||
type: 'boolean'
|
||||
},
|
||||
{
|
||||
name: 'previewVideoOnHover',
|
||||
store: interfacePreviewVideoOnHover,
|
||||
type: 'boolean'
|
||||
},
|
||||
{
|
||||
name: 'sponsorBlock',
|
||||
store: sponsorBlock,
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
"color": "Color"
|
||||
},
|
||||
"searchSuggestions": "Search suggestions",
|
||||
"previewVideoOnHover": "Preview video on hover",
|
||||
"dataPreferences": {
|
||||
"dataPreferences": "Data preferences",
|
||||
"content": "Looking to import/export subscriptions, change password or delete account? Click here and scroll to the bottom of the page."
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { navigating, page } from '$app/stores';
|
||||
import { getFeed } from '$lib/Api/index';
|
||||
import type { Notification } from '$lib/Api/model';
|
||||
import Logo from '$lib/Logo.svelte';
|
||||
import PageLoading from '$lib/PageLoading.svelte';
|
||||
import Search from '$lib/Search.svelte';
|
||||
@@ -24,6 +25,7 @@
|
||||
deArrowEnabled,
|
||||
deArrowInstance,
|
||||
deArrowThumbnailInstance,
|
||||
interfacePreviewVideoOnHover,
|
||||
interfaceSearchSuggestions,
|
||||
playerAlwaysLoop,
|
||||
playerAutoPlay,
|
||||
@@ -43,12 +45,11 @@
|
||||
themeColor
|
||||
} from '../store';
|
||||
|
||||
let mobileSearchShow = false;
|
||||
|
||||
let currentPage: string | null = '';
|
||||
activePage.subscribe((page) => (currentPage = page));
|
||||
|
||||
let searchSuggestions = false;
|
||||
interfaceSearchSuggestions.subscribe((value) => (searchSuggestions = value));
|
||||
|
||||
let sponsorCategoriesList: string[] = [];
|
||||
sponsorBlockCategories.subscribe((value) => (sponsorCategoriesList = value));
|
||||
|
||||
@@ -291,57 +292,75 @@
|
||||
</nav>
|
||||
|
||||
<nav class="top">
|
||||
<button class="circle large transparent s m l small-margin" data-ui="#menu-expanded"
|
||||
{#if !mobileSearchShow}
|
||||
<button
|
||||
on:click={() => (mobileSearchShow = !mobileSearchShow)}
|
||||
class="transparent s circle large"
|
||||
>
|
||||
<i>search</i>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<button class="circle large transparent m l small-margin" data-ui="#menu-expanded"
|
||||
><i>menu</i></button
|
||||
>
|
||||
|
||||
<Logo classes="m l" />
|
||||
<h6 class="m l">Materialious</h6>
|
||||
|
||||
<div class="max"></div>
|
||||
<div class="max m l"></div>
|
||||
|
||||
<div class="m l">
|
||||
<Search />
|
||||
</div>
|
||||
|
||||
<div class="max"></div>
|
||||
<a
|
||||
href="https://github.com/WardPearce/Materialious"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="button circle large transparent"
|
||||
>
|
||||
<i>code</i>
|
||||
<div class="tooltip bottom">{$_('layout.star')}</div>
|
||||
</a>
|
||||
<button data-ui="#sync-party" class="circle large transparent">
|
||||
<i class:primary-text={$syncPartyPeer}>group</i>
|
||||
<div class="tooltip bottom">{$_('layout.syncParty')}</div>
|
||||
</button>
|
||||
{#if isLoggedIn}
|
||||
<button class="circle large transparent" data-ui="#dialog-notifications"
|
||||
><i>notifications</i>
|
||||
<div class="tooltip bottom">{$_('layout.notifications')}</div>
|
||||
</button>
|
||||
{#if !mobileSearchShow}
|
||||
<div class="max"></div>
|
||||
{/if}
|
||||
<button class="circle large transparent" data-ui="#dialog-settings">
|
||||
<i>settings</i>
|
||||
<div class="tooltip bottom">{$_('layout.settings')}</div>
|
||||
</button>
|
||||
|
||||
{#if !isLoggedIn}
|
||||
<button on:click={login} class="circle large transparent">
|
||||
<i>login</i>
|
||||
<div class="tooltip bottom">{$_('layout.login')}</div>
|
||||
</button>
|
||||
{#if mobileSearchShow}
|
||||
<Search on:searchCancelled={() => (mobileSearchShow = false)} />
|
||||
{:else}
|
||||
<button on:click={logout} class="circle large transparent">
|
||||
<i>logout</i>
|
||||
<div class="tooltip bottom">{$_('layout.logout')}</div>
|
||||
<button data-ui="#sync-party" class="m l circle large transparent">
|
||||
<i class:primary-text={$syncPartyPeer}>group</i>
|
||||
<div class="tooltip bottom">{$_('layout.syncParty')}</div>
|
||||
</button>
|
||||
{#if isLoggedIn}
|
||||
<button class="circle large transparent" data-ui="#dialog-notifications"
|
||||
><i>notifications</i>
|
||||
<div class="tooltip bottom">{$_('layout.notifications')}</div>
|
||||
</button>
|
||||
{/if}
|
||||
<button class="circle large transparent" data-ui="#dialog-settings">
|
||||
<i>settings</i>
|
||||
<div class="tooltip bottom">{$_('layout.settings')}</div>
|
||||
</button>
|
||||
|
||||
{#if !isLoggedIn}
|
||||
<button on:click={login} class="circle large transparent">
|
||||
<i>login</i>
|
||||
<div class="tooltip bottom">{$_('layout.login')}</div>
|
||||
</button>
|
||||
{:else}
|
||||
<button on:click={logout} class="circle large transparent">
|
||||
<i>logout</i>
|
||||
<div class="tooltip bottom">{$_('layout.logout')}</div>
|
||||
</button>
|
||||
{/if}
|
||||
{/if}
|
||||
</nav>
|
||||
|
||||
<nav class="bottom s">
|
||||
{#each pages as page}
|
||||
{#if !page.requiresAuth || isLoggedIn}
|
||||
<a class="round" href={page.href} class:active={currentPage === page.name.toLowerCase()}
|
||||
><i>{page.icon}</i>
|
||||
<div class="tooltip top">{page.name}</div>
|
||||
</a>
|
||||
{/if}
|
||||
{/each}
|
||||
</nav>
|
||||
|
||||
<dialog class="right" id="dialog-settings">
|
||||
<nav>
|
||||
<h4 class="max">{$_('layout.settings')}</h4>
|
||||
@@ -384,6 +403,22 @@
|
||||
</label>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="field no-margin">
|
||||
<nav class="no-padding">
|
||||
<div class="max">
|
||||
<div>{$_('layout.previewVideoOnHover')}</div>
|
||||
</div>
|
||||
<label class="switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={$interfacePreviewVideoOnHover}
|
||||
on:click={() => interfacePreviewVideoOnHover.set(!$interfacePreviewVideoOnHover)}
|
||||
/>
|
||||
<span></span>
|
||||
</label>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings">
|
||||
@@ -689,9 +724,6 @@
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div class="s">
|
||||
<Search />
|
||||
</div>
|
||||
<div class="space"></div>
|
||||
{#each pages as page}
|
||||
{#if !page.requiresAuth || isLoggedIn}
|
||||
@@ -766,4 +798,10 @@
|
||||
form {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 650px) {
|
||||
dialog.right {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
let theatreMode = get(playerTheatreModeByDefault);
|
||||
|
||||
let audioMode = get(playerListenByDefault);
|
||||
let currentTime: number;
|
||||
let seekTo: (time: number) => void;
|
||||
let player: MediaPlayerElement;
|
||||
|
||||
@@ -357,14 +356,7 @@
|
||||
<div class="grid">
|
||||
<div class={`s12 m12 l${theatreMode ? '12' : '10'}`}>
|
||||
{#key data.video.videoId}
|
||||
<Player
|
||||
{data}
|
||||
{audioMode}
|
||||
isSyncing={$syncPartyPeer !== null}
|
||||
bind:seekTo
|
||||
bind:currentTime
|
||||
bind:player
|
||||
/>
|
||||
<Player {data} {audioMode} isSyncing={$syncPartyPeer !== null} bind:seekTo bind:player />
|
||||
{/key}
|
||||
|
||||
<h5>{data.video.title}</h5>
|
||||
|
||||
@@ -24,6 +24,7 @@ export const playerAutoplayNextByDefault = persisted('autoplayNextByDefault', fa
|
||||
export const returnYtDislikes = persisted('returnYtDislikes', true);
|
||||
|
||||
export const interfaceSearchSuggestions = persisted('searchSuggestions', true);
|
||||
export const interfacePreviewVideoOnHover = persisted('previewVideoOnHover', true);
|
||||
|
||||
export const auth: Writable<null | { username: string; token: string; }> = persisted(
|
||||
'authToken',
|
||||
|
||||
Reference in New Issue
Block a user