Moved settings into separated components

This commit is contained in:
WardPearce
2025-01-24 17:25:31 +13:00
parent a1db68d1b0
commit 422e4d523d
14 changed files with 1024 additions and 907 deletions
+17 -1
View File
@@ -4,7 +4,11 @@
import { createEventDispatcher, onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { getSearchSuggestions } from '../api';
import { interfaceSearchSuggestionsStore } from '../store';
import {
interfaceSearchHistoryEnabled,
interfaceSearchSuggestionsStore,
searchHistoryStore
} from '../store';
const dispatch = createEventDispatcher();
@@ -38,7 +42,12 @@
goto(`/search/${encodeURIComponent(search)}`);
dispatch('searchSubmitted');
suggestionsForSearch = [];
showSearchBox = false;
if ($interfaceSearchHistoryEnabled && !$searchHistoryStore.includes(search)) {
searchHistoryStore.set([search, ...$searchHistoryStore]);
}
}
function handleKeyDown(event: KeyboardEvent) {
@@ -131,6 +140,13 @@
</a>
{/each}
{/if}
{#if !suggestionsForSearch.length && $interfaceSearchHistoryEnabled}
{#each $searchHistoryStore as history}
<a class="row" href={`/search/${encodeURIComponent(history)}`}>
<div>{history}</div>
</a>
{/each}
{/if}
</menu>
{/if}
</div>
@@ -1,905 +0,0 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { bookmarkletSaveToUrl } from '$lib/externalSettings';
import { letterCase, titleCase, titleCases } from '$lib/letterCasing';
import { Clipboard } from '@capacitor/clipboard';
import { Capacitor } from '@capacitor/core';
import ui from 'beercss';
import { iso31661 } from 'iso-3166';
import ISO6391 from 'iso-639-1';
import { onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { ensureNoTrailingSlash } from '../misc';
import { getPages } from '../navPages';
import {
authStore,
darkModeStore,
deArrowEnabledStore,
deArrowInstanceStore,
deArrowThumbnailInstanceStore,
deArrowTitlesOnly,
instanceStore,
interfaceAmoledTheme,
interfaceAutoExpandComments,
interfaceAutoExpandDesc,
interfaceDefaultPage,
interfaceDisplayThumbnailAvatars,
interfaceForceCase,
interfaceLowBandwidthMode,
interfacePreviewVideoOnHoverStore,
interfaceRegionStore,
interfaceSearchSuggestionsStore,
playerAlwaysLoopStore,
playerAndroidBgPlayer,
playerAndroidLockOrientation,
playerAutoPlayStore,
playerAutoplayNextByDefaultStore,
playerDefaultLanguage,
playerListenByDefaultStore,
playerMiniPlayerStore,
playerProxyVideosStore,
playerSavePlaybackPositionStore,
playerTheatreModeByDefaultStore,
playerYouTubeJsAlways,
playerYouTubeJsFallback,
returnYTDislikesInstanceStore,
returnYtDislikesStore,
silenceSkipperStore,
sponsorBlockCategoriesStore,
sponsorBlockDisplayToastStore,
sponsorBlockStore,
sponsorBlockTimelineStore,
sponsorBlockUrlStore,
synciousInstanceStore,
synciousStore,
themeColorStore
} from '../store';
import { setAmoledTheme } from '../theme';
let sponsorCategoriesList: string[] = [];
sponsorBlockCategoriesStore.subscribe((value) => (sponsorCategoriesList = value));
let sponsorBlockInstance = get(sponsorBlockUrlStore);
let synciousInstance = get(synciousInstanceStore);
let returnYTInstance = get(returnYTDislikesInstanceStore);
let invidiousInstance = get(instanceStore);
let deArrowUrl = get(deArrowInstanceStore);
let deArrowThumbnailUrl = get(deArrowThumbnailInstanceStore);
let region = get(interfaceRegionStore);
let forceCase = get(interfaceForceCase);
let defaultLanguage = get(playerDefaultLanguage);
let defaultPage = get(interfaceDefaultPage);
const languageNames = ISO6391.getAllCodes().map((code) =>
ISO6391.getName(code).toLocaleLowerCase()
);
const sponsorCategories = [
{ name: $_('layout.sponsors.sponsor'), category: 'sponsor' },
{ name: $_('layout.sponsors.unpaidSelfPromotion'), category: 'selfpromo' },
{ name: $_('layout.sponsors.interactionReminder'), category: 'interaction' },
{ name: $_('layout.sponsors.intermissionIntroAnimation'), category: 'intro' },
{ name: $_('layout.sponsors.credits'), category: 'outro' },
{ name: $_('layout.sponsors.preViewRecapHook'), category: 'preview' },
{ name: $_('layout.sponsors.tangentJokes'), category: 'filler' }
];
function toggleSponsor(category: string) {
if (sponsorCategoriesList.includes(category)) {
sponsorBlockCategoriesStore.set(sponsorCategoriesList.filter((value) => value !== category));
} else {
sponsorCategoriesList.push(category);
sponsorBlockCategoriesStore.set(sponsorCategoriesList);
}
}
async function setColor(color: any) {
const target = color.target;
const hex = (target as { value: string }).value;
await ui('theme', hex);
setAmoledTheme();
themeColorStore.set(hex);
}
function toggleDarkMode() {
const isDark = get(darkModeStore);
if (isDark) {
ui('mode', 'light');
darkModeStore.set(false);
} else {
ui('mode', 'dark');
darkModeStore.set(true);
}
}
let activeTab:
| 'interface'
| 'player'
| 'data'
| 'ryd'
| 'api extended'
| 'sponsorblock'
| 'dearrow' = 'interface';
let dialogType = '';
function checkWidth() {
if (innerWidth <= 1320) {
dialogType = 'right';
} else {
dialogType = '';
}
}
onMount(() => {
checkWidth();
addEventListener('resize', () => checkWidth());
// Set default language if not set already based off browser.
if (!get(playerDefaultLanguage)) {
const languageCode = navigator.language || navigator.languages[0];
const languageName = ISO6391.getName(languageCode.split('-')[0]);
if (languageName) {
playerDefaultLanguage.set(languageName.toLowerCase());
}
}
});
</script>
<dialog id="dialog-settings" class={dialogType}>
<nav style="margin-bottom: 1em;">
<h4 class="max">{$_('layout.settings')}</h4>
<button class="circle transparent" data-ui="#dialog-settings"><i>close</i></button>
</nav>
<div>
<nav class="tabbed small">
<a class:active={activeTab === 'interface'} on:click={() => (activeTab = 'interface')}>
<i>grid_view</i>
<span>{$_('layout.interface')}</span>
</a>
<a class:active={activeTab === 'player'} on:click={() => (activeTab = 'player')}>
<i>smart_display</i>
<span>{$_('layout.player.title')}</span>
</a>
<a class:active={activeTab === 'data'} on:click={() => (activeTab = 'data')}>
<i>save</i>
<span>{$_('layout.dataPreferences.dataPreferences')}</span>
</a>
<a class:active={activeTab === 'ryd'} on:click={() => (activeTab = 'ryd')}>
<i>thumb_down</i>
<span>RYD</span>
</a>
<a class:active={activeTab === 'api extended'} on:click={() => (activeTab = 'api extended')}>
<i>sync</i>
<span>API Extended</span>
</a>
<a class:active={activeTab === 'sponsorblock'} on:click={() => (activeTab = 'sponsorblock')}>
<i>block</i>
<span>Sponsorblock</span>
</a>
<a class:active={activeTab === 'dearrow'} on:click={() => (activeTab = 'dearrow')}>
<i>keyboard_double_arrow_down</i>
<span>{$_('layout.deArrow.title')}</span>
</a>
<a href="https://github.com/sponsors/WardPearce" target="_blank" referrerpolicy="no-referrer">
<i>favorite</i>
<span>{$_('donate')}</span>
</a>
<a
href="https://github.com/Materialious/Materialious"
target="_blank"
referrerpolicy="no-referrer"
>
<i>code</i>
<span>GitHub</span>
</a>
</nav>
<div class="page padding" class:active={activeTab === 'interface'}>
{#if Capacitor.isNativePlatform()}
<form
on:submit|preventDefault={() => {
instanceStore.set(ensureNoTrailingSlash(invidiousInstance));
authStore.set(null);
goto('/', { replaceState: true });
ui('#dialog-settings');
}}
>
<nav>
<div class="field label border max">
<input bind:value={invidiousInstance} name="invidious-instance" type="text" />
<label for="invidious-instance">{$_('layout.instanceUrl')}</label>
</div>
<button class="square round">
<i>done</i>
</button>
</nav>
</form>
{/if}
<div class="margin"></div>
<button on:click={toggleDarkMode} class="no-margin">
{#if !$darkModeStore}
<i>dark_mode</i>
<span>{$_('layout.theme.darkMode')}</span>
{:else}
<i>light_mode</i>
<span>{$_('layout.theme.lightMode')}</span>
{/if}
</button>
<button>
<i>palette</i>
<span>{$_('layout.theme.color')}</span>
<input on:change={setColor} type="color" />
</button>
<div class="margin"></div>
{#if $darkModeStore}
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.theme.AmoledTheme')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$interfaceAmoledTheme}
on:click={() => interfaceAmoledTheme.set(!$interfaceAmoledTheme)}
/>
<span></span>
</label>
</nav>
</div>
{/if}
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.displayThumbnailAvatars')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$interfaceDisplayThumbnailAvatars}
on:click={() =>
interfaceDisplayThumbnailAvatars.set(!$interfaceDisplayThumbnailAvatars)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.lowBandwidthMode')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$interfaceLowBandwidthMode}
on:click={() => interfaceLowBandwidthMode.set(!$interfaceLowBandwidthMode)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.searchSuggestions')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$interfaceSearchSuggestionsStore}
on:click={() =>
interfaceSearchSuggestionsStore.set(!$interfaceSearchSuggestionsStore)}
/>
<span></span>
</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={$interfacePreviewVideoOnHoverStore}
on:click={() =>
interfacePreviewVideoOnHoverStore.set(!$interfacePreviewVideoOnHoverStore)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.expandDescription')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$interfaceAutoExpandDesc}
on:click={() => interfaceAutoExpandDesc.set(!$interfaceAutoExpandDesc)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.expandComments')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$interfaceAutoExpandComments}
on:click={() => interfaceAutoExpandComments.set(!$interfaceAutoExpandComments)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field label suffix border">
<select
name="region"
bind:value={region}
on:change={() => interfaceRegionStore.set(region)}
>
{#each iso31661 as region}
<option selected={$interfaceRegionStore === region.alpha2} value={region.alpha2}
>{region.alpha2} - {region.name}</option
>
{/each}
</select>
<label for="region">{$_('region')}</label>
<i>arrow_drop_down</i>
</div>
<div class="field label suffix border">
<select
name="case"
bind:value={forceCase}
on:change={() => interfaceForceCase.set(forceCase)}
>
<option selected={$interfaceForceCase === null} value={null}>Default</option>
{#each titleCases as caseType}
<option selected={$interfaceForceCase === caseType} value={caseType}
>{letterCase(`${caseType}`, caseType)}</option
>
{/each}
</select>
<label for="case">{$_('letterCase')}</label>
<i>arrow_drop_down</i>
</div>
<div class="field label suffix border">
<select
name="defaultPage"
bind:value={defaultPage}
on:change={() => interfaceDefaultPage.set(defaultPage)}
>
{#each getPages() as page}
{#if !page.requiresAuth || get(authStore)}
<option selected={$interfaceDefaultPage === page.href} value={page.href}
>{page.name}</option
>
{/if}
{/each}
</select>
<label for="defaultPage">{$_('defaultPage')}</label>
<i>arrow_drop_down</i>
</div>
{#if !Capacitor.isNativePlatform()}
<div class="settings">
<h6>{$_('layout.bookmarklet')}</h6>
<button
class="no-margin"
on:click={async () => await Clipboard.write({ string: bookmarkletSaveToUrl() })}
>{$_('copyUrl')}</button
>
</div>
{/if}
</div>
<div class="page padding" class:active={activeTab === 'player'}>
<div class="margin"></div>
<div class="field label suffix border">
<select
name="case"
bind:value={defaultLanguage}
on:change={() => playerDefaultLanguage.set(defaultLanguage)}
>
{#each languageNames as language}
<option selected={$playerDefaultLanguage === language} value={language}
>{titleCase(language)}</option
>
{/each}
</select>
<label for="case">{$_('player.defaultLanguage')}</label>
<i>arrow_drop_down</i>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.miniPlayer')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerMiniPlayerStore}
on:click={() => playerMiniPlayerStore.set(!$playerMiniPlayerStore)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.autoPlay')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerAutoPlayStore}
on:click={() => playerAutoPlayStore.set(!$playerAutoPlayStore)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.alwaysLoopVideo')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerAlwaysLoopStore}
on:click={() => playerAlwaysLoopStore.set(!$playerAlwaysLoopStore)}
/>
<span></span>
</label>
</nav>
</div>
{#if Capacitor.getPlatform() === 'android'}
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.backgroundPlay')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerAndroidBgPlayer}
on:click={() => playerAndroidBgPlayer.set(!$playerAndroidBgPlayer)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.lockOrientation')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerAndroidLockOrientation}
on:click={() => playerAndroidLockOrientation.set(!$playerAndroidLockOrientation)}
/>
<span></span>
</label>
</nav>
</div>
{/if}
{#if Capacitor.isNativePlatform()}
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.proxyVideos')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerProxyVideosStore}
on:click={() => playerProxyVideosStore.set(!$playerProxyVideosStore)}
/>
<span></span>
</label>
</nav>
</div>
{/if}
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.savePlaybackPosition')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerSavePlaybackPositionStore}
on:click={() =>
playerSavePlaybackPositionStore.set(!$playerSavePlaybackPositionStore)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.listenByDefault')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerListenByDefaultStore}
on:click={() => playerListenByDefaultStore.set(!$playerListenByDefaultStore)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin m l">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.theatreModeByDefault')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerTheatreModeByDefaultStore}
on:click={() =>
playerTheatreModeByDefaultStore.set(!$playerTheatreModeByDefaultStore)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.autoPlayNextByDefault')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerAutoplayNextByDefaultStore}
on:click={() =>
playerAutoplayNextByDefaultStore.set(!$playerAutoplayNextByDefaultStore)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.silenceSkipper')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$silenceSkipperStore}
on:click={() => silenceSkipperStore.set(!$silenceSkipperStore)}
/>
<span></span>
</label>
</nav>
</div>
{#if Capacitor.isNativePlatform()}
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.youtubeJsAlways')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerYouTubeJsAlways}
on:click={() => playerYouTubeJsAlways.set(!$playerYouTubeJsAlways)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.youtubeJsFallback')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerYouTubeJsFallback}
on:click={() => playerYouTubeJsFallback.set(!$playerYouTubeJsFallback)}
/>
<span></span>
</label>
</nav>
</div>
{/if}
</div>
<div class="page padding" class:active={activeTab === 'data'}>
<p style="width: 240px;">
<a
href={`${get(instanceStore)}/preferences`}
target="_blank"
rel="noopener noreferrer"
class="link"
>
{$_('layout.dataPreferences.content')}
</a>
</p>
</div>
<div class="page padding" class:active={activeTab === 'ryd'}>
<form
on:submit|preventDefault={() =>
returnYTDislikesInstanceStore.set(ensureNoTrailingSlash(returnYTInstance))}
>
<nav>
<div class="field label border max">
<input bind:value={returnYTInstance} name="returnyt-instance" type="text" />
<label for="returnyt-instance">{$_('layout.instanceUrl')}</label>
</div>
<button class="square round">
<i>done</i>
</button>
</nav>
</form>
<nav class="no-padding">
<div class="max">
<p>{$_('enabled')}</p>
</div>
<label class="switch">
<input
bind:checked={$returnYtDislikesStore}
on:click={() => returnYtDislikesStore.set(!$returnYtDislikesStore)}
type="checkbox"
/>
<span></span>
</label>
</nav>
</div>
<div class="page padding" class:active={activeTab === 'api extended'}>
<form
on:submit|preventDefault={() =>
synciousInstanceStore.set(ensureNoTrailingSlash(synciousInstance))}
>
<nav>
<div class="field label border max">
<input bind:value={synciousInstance} name="syncious-instance" type="text" />
<label for="syncious-instance">{$_('layout.instanceUrl')}</label>
</div>
<button class="square round">
<i>done</i>
</button>
</nav>
</form>
<nav class="no-padding">
<div class="max">
<p>{$_('enabled')}</p>
</div>
<label class="switch">
<input
bind:checked={$synciousStore}
on:click={() => synciousStore.set(!$synciousStore)}
type="checkbox"
/>
<span></span>
</label>
</nav>
</div>
<div class="page padding" class:active={activeTab === 'sponsorblock'}>
<form
on:submit|preventDefault={() =>
sponsorBlockUrlStore.set(ensureNoTrailingSlash(sponsorBlockInstance))}
>
<nav>
<div class="field label border max">
<input bind:value={sponsorBlockInstance} name="sponsorblock-instance" type="text" />
<label for="sponsorblock-instance">{$_('layout.instanceUrl')}</label>
</div>
<button class="square round">
<i>done</i>
</button>
</nav>
</form>
<nav class="no-padding">
<div class="max">
<p>{$_('enabled')}</p>
</div>
<label class="switch">
<input
bind:checked={$sponsorBlockStore}
on:click={() => sponsorBlockStore.set(!$sponsorBlockStore)}
type="checkbox"
/>
<span></span>
</label>
</nav>
<nav class="no-padding">
<div class="max">
<p>{$_('layout.sponsors.disableToast')}</p>
</div>
<label class="switch">
<input
bind:checked={$sponsorBlockDisplayToastStore}
on:click={() => sponsorBlockDisplayToastStore.set(!$sponsorBlockDisplayToastStore)}
type="checkbox"
/>
<span></span>
</label>
</nav>
<nav class="no-padding">
<div class="max">
<p>{$_('layout.sponsors.disableTimeline')}</p>
</div>
<label class="switch">
<input
bind:checked={$sponsorBlockTimelineStore}
on:click={() => sponsorBlockTimelineStore.set(!$sponsorBlockTimelineStore)}
type="checkbox"
/>
<span></span>
</label>
</nav>
<hr style="margin: 1em 0;" />
<p class="bold">{$_('layout.sponsors.Catagories')}</p>
{#each sponsorCategories as sponsor}
<div class="field middle-align no-margin">
<nav class="no-padding">
<div class="max">
<p>{sponsor.name}</p>
</div>
<label class="switch">
<input
type="checkbox"
checked={sponsorCategoriesList.includes(sponsor.category)}
on:click={() => toggleSponsor(sponsor.category)}
/>
<span></span>
</label>
</nav>
</div>
{/each}
</div>
<div class="page padding" class:active={activeTab === 'dearrow'}>
<form
on:submit|preventDefault={() => deArrowInstanceStore.set(ensureNoTrailingSlash(deArrowUrl))}
>
<nav>
<div class="field label border max">
<input bind:value={deArrowUrl} name="dearrow-instance" type="text" />
<label for="dearrow-instance">{$_('layout.instanceUrl')}</label>
</div>
<button class="square round">
<i>done</i>
</button>
</nav>
</form>
<form on:submit|preventDefault={() => deArrowThumbnailInstanceStore.set(deArrowThumbnailUrl)}>
<nav>
<div class="field label border max">
<input bind:value={deArrowThumbnailUrl} name="dearrow-thumbnail-instance" type="text" />
<label for="dearrow-thumbnail-instance"
>{$_('layout.deArrow.thumbnailInstanceUrl')}</label
>
</div>
<button class="square round">
<i>done</i>
</button>
</nav>
</form>
<nav class="no-padding">
<div class="max">
<p>{$_('layout.deArrow.titleOnly')}</p>
</div>
<label class="switch">
<input
bind:checked={$deArrowTitlesOnly}
on:click={() => deArrowTitlesOnly.set(!$deArrowTitlesOnly)}
type="checkbox"
/>
<span></span>
</label>
</nav>
<nav class="no-padding">
<div class="max">
<p>{$_('enabled')}</p>
</div>
<label class="switch">
<input
bind:checked={$deArrowEnabledStore}
on:click={() => deArrowEnabledStore.set(!$deArrowEnabledStore)}
type="checkbox"
/>
<span></span>
</label>
</nav>
</div>
</div>
</dialog>
<style>
form {
margin: 1em 0;
}
dialog {
width: 1320px;
}
.tabbed > a {
flex: none;
}
@media screen and (max-width: 1320px) {
.tabbed {
display: flex !important;
flex-direction: column !important;
gap: 0 !important;
flex: 0 !important;
margin-bottom: 1em !important;
align-items: start !important;
padding: 1em 0 !important;
background-color: transparent;
border-bottom: solid 1px var(--outline-variant);
}
.tabbed > a {
font-size: 1.5rem !important;
padding: 0.5em 0.3em !important;
border-radius: 3em !important;
}
.tabbed.small {
block-size: 100% !important;
border-radius: 0 !important;
}
}
</style>
@@ -0,0 +1,37 @@
<script lang="ts">
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { ensureNoTrailingSlash } from '../../misc';
import { synciousInstanceStore, synciousStore } from '../../store';
let synciousInstance = get(synciousInstanceStore);
</script>
<form
on:submit|preventDefault={() =>
synciousInstanceStore.set(ensureNoTrailingSlash(synciousInstance))}
>
<nav>
<div class="field label border max">
<input bind:value={synciousInstance} name="syncious-instance" type="text" />
<label for="syncious-instance">{$_('layout.instanceUrl')}</label>
</div>
<button class="square round">
<i>done</i>
</button>
</nav>
</form>
<nav class="no-padding">
<div class="max">
<p>{$_('enabled')}</p>
</div>
<label class="switch">
<input
bind:checked={$synciousStore}
on:click={() => synciousStore.set(!$synciousStore)}
type="checkbox"
/>
<span></span>
</label>
</nav>
@@ -0,0 +1,16 @@
<script lang="ts">
import { instanceStore } from '$lib/store';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
</script>
<p style="width: 240px;">
<a
href={`${get(instanceStore)}/preferences`}
target="_blank"
rel="noopener noreferrer"
class="link"
>
{$_('layout.dataPreferences.content')}
</a>
</p>
@@ -0,0 +1,66 @@
<script lang="ts">
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { ensureNoTrailingSlash } from '../../misc';
import {
deArrowEnabledStore,
deArrowInstanceStore,
deArrowThumbnailInstanceStore,
deArrowTitlesOnly
} from '../../store';
let deArrowUrl = get(deArrowInstanceStore);
let deArrowThumbnailUrl = get(deArrowThumbnailInstanceStore);
</script>
<form on:submit|preventDefault={() => deArrowInstanceStore.set(ensureNoTrailingSlash(deArrowUrl))}>
<nav>
<div class="field label border max">
<input bind:value={deArrowUrl} name="dearrow-instance" type="text" />
<label for="dearrow-instance">{$_('layout.instanceUrl')}</label>
</div>
<button class="square round">
<i>done</i>
</button>
</nav>
</form>
<form on:submit|preventDefault={() => deArrowThumbnailInstanceStore.set(deArrowThumbnailUrl)}>
<nav>
<div class="field label border max">
<input bind:value={deArrowThumbnailUrl} name="dearrow-thumbnail-instance" type="text" />
<label for="dearrow-thumbnail-instance">{$_('layout.deArrow.thumbnailInstanceUrl')}</label>
</div>
<button class="square round">
<i>done</i>
</button>
</nav>
</form>
<nav class="no-padding">
<div class="max">
<p>{$_('layout.deArrow.titleOnly')}</p>
</div>
<label class="switch">
<input
bind:checked={$deArrowTitlesOnly}
on:click={() => deArrowTitlesOnly.set(!$deArrowTitlesOnly)}
type="checkbox"
/>
<span></span>
</label>
</nav>
<nav class="no-padding">
<div class="max">
<p>{$_('enabled')}</p>
</div>
<label class="switch">
<input
bind:checked={$deArrowEnabledStore}
on:click={() => deArrowEnabledStore.set(!$deArrowEnabledStore)}
type="checkbox"
/>
<span></span>
</label>
</nav>
@@ -0,0 +1,262 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { bookmarkletSaveToUrl } from '$lib/externalSettings';
import { letterCase, titleCases } from '$lib/letterCasing';
import { setAmoledTheme } from '$lib/theme';
import { Clipboard } from '@capacitor/clipboard';
import { Capacitor } from '@capacitor/core';
import ui from 'beercss';
import { iso31661 } from 'iso-3166';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { ensureNoTrailingSlash } from '../../misc';
import { getPages } from '../../navPages';
import {
authStore,
darkModeStore,
instanceStore,
interfaceAmoledTheme,
interfaceAutoExpandComments,
interfaceAutoExpandDesc,
interfaceDefaultPage,
interfaceDisplayThumbnailAvatars,
interfaceForceCase,
interfaceLowBandwidthMode,
interfacePreviewVideoOnHoverStore,
interfaceRegionStore,
interfaceSearchSuggestionsStore,
themeColorStore
} from '../../store';
let invidiousInstance = get(instanceStore);
let region = get(interfaceRegionStore);
let forceCase = get(interfaceForceCase);
let defaultPage = get(interfaceDefaultPage);
async function setColor(color: any) {
const target = color.target;
const hex = (target as { value: string }).value;
await ui('theme', hex);
setAmoledTheme();
themeColorStore.set(hex);
}
function toggleDarkMode() {
const isDark = get(darkModeStore);
if (isDark) {
ui('mode', 'light');
darkModeStore.set(false);
} else {
ui('mode', 'dark');
darkModeStore.set(true);
}
}
</script>
{#if Capacitor.isNativePlatform()}
<form
on:submit|preventDefault={() => {
instanceStore.set(ensureNoTrailingSlash(invidiousInstance));
authStore.set(null);
goto('/', { replaceState: true });
ui('#dialog-settings');
}}
>
<nav>
<div class="field label border max">
<input bind:value={invidiousInstance} name="invidious-instance" type="text" />
<label for="invidious-instance">{$_('layout.instanceUrl')}</label>
</div>
<button class="square round">
<i>done</i>
</button>
</nav>
</form>
{/if}
<div class="margin"></div>
<button on:click={toggleDarkMode} class="no-margin">
{#if !$darkModeStore}
<i>dark_mode</i>
<span>{$_('layout.theme.darkMode')}</span>
{:else}
<i>light_mode</i>
<span>{$_('layout.theme.lightMode')}</span>
{/if}
</button>
<button>
<i>palette</i>
<span>{$_('layout.theme.color')}</span>
<input on:change={setColor} type="color" />
</button>
<div class="margin"></div>
{#if $darkModeStore}
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.theme.AmoledTheme')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$interfaceAmoledTheme}
on:click={() => interfaceAmoledTheme.set(!$interfaceAmoledTheme)}
/>
<span></span>
</label>
</nav>
</div>
{/if}
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.displayThumbnailAvatars')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$interfaceDisplayThumbnailAvatars}
on:click={() => interfaceDisplayThumbnailAvatars.set(!$interfaceDisplayThumbnailAvatars)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.lowBandwidthMode')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$interfaceLowBandwidthMode}
on:click={() => interfaceLowBandwidthMode.set(!$interfaceLowBandwidthMode)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.searchSuggestions')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$interfaceSearchSuggestionsStore}
on:click={() => interfaceSearchSuggestionsStore.set(!$interfaceSearchSuggestionsStore)}
/>
<span></span>
</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={$interfacePreviewVideoOnHoverStore}
on:click={() => interfacePreviewVideoOnHoverStore.set(!$interfacePreviewVideoOnHoverStore)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.expandDescription')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$interfaceAutoExpandDesc}
on:click={() => interfaceAutoExpandDesc.set(!$interfaceAutoExpandDesc)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.expandComments')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$interfaceAutoExpandComments}
on:click={() => interfaceAutoExpandComments.set(!$interfaceAutoExpandComments)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field label suffix border">
<select name="region" bind:value={region} on:change={() => interfaceRegionStore.set(region)}>
{#each iso31661 as region}
<option selected={$interfaceRegionStore === region.alpha2} value={region.alpha2}
>{region.alpha2} - {region.name}</option
>
{/each}
</select>
<label for="region">{$_('region')}</label>
<i>arrow_drop_down</i>
</div>
<div class="field label suffix border">
<select name="case" bind:value={forceCase} on:change={() => interfaceForceCase.set(forceCase)}>
<option selected={$interfaceForceCase === null} value={null}>Default</option>
{#each titleCases as caseType}
<option selected={$interfaceForceCase === caseType} value={caseType}
>{letterCase(`${caseType}`, caseType)}</option
>
{/each}
</select>
<label for="case">{$_('letterCase')}</label>
<i>arrow_drop_down</i>
</div>
<div class="field label suffix border">
<select
name="defaultPage"
bind:value={defaultPage}
on:change={() => interfaceDefaultPage.set(defaultPage)}
>
{#each getPages() as page}
{#if !page.requiresAuth || get(authStore)}
<option selected={$interfaceDefaultPage === page.href} value={page.href}>{page.name}</option
>
{/if}
{/each}
</select>
<label for="defaultPage">{$_('defaultPage')}</label>
<i>arrow_drop_down</i>
</div>
{#if !Capacitor.isNativePlatform()}
<div class="settings">
<h6>{$_('layout.bookmarklet')}</h6>
<button
class="no-margin"
on:click={async () => await Clipboard.write({ string: bookmarkletSaveToUrl() })}
>{$_('copyUrl')}</button
>
</div>
{/if}
@@ -0,0 +1,260 @@
<script lang="ts">
import { titleCase } from '$lib/letterCasing';
import { Capacitor } from '@capacitor/core';
import ISO6391 from 'iso-639-1';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import {
playerAlwaysLoopStore,
playerAndroidBgPlayer,
playerAndroidLockOrientation,
playerAutoPlayStore,
playerAutoplayNextByDefaultStore,
playerDefaultLanguage,
playerListenByDefaultStore,
playerMiniPlayerStore,
playerProxyVideosStore,
playerSavePlaybackPositionStore,
playerTheatreModeByDefaultStore,
playerYouTubeJsAlways,
playerYouTubeJsFallback,
silenceSkipperStore
} from '../../store';
let defaultLanguage = get(playerDefaultLanguage);
const languageNames = ISO6391.getAllCodes().map((code) =>
ISO6391.getName(code).toLocaleLowerCase()
);
</script>
<div class="margin"></div>
<div class="field label suffix border">
<select
name="case"
bind:value={defaultLanguage}
on:change={() => playerDefaultLanguage.set(defaultLanguage)}
>
{#each languageNames as language}
<option selected={$playerDefaultLanguage === language} value={language}
>{titleCase(language)}</option
>
{/each}
</select>
<label for="case">{$_('player.defaultLanguage')}</label>
<i>arrow_drop_down</i>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.miniPlayer')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerMiniPlayerStore}
on:click={() => playerMiniPlayerStore.set(!$playerMiniPlayerStore)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.autoPlay')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerAutoPlayStore}
on:click={() => playerAutoPlayStore.set(!$playerAutoPlayStore)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.alwaysLoopVideo')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerAlwaysLoopStore}
on:click={() => playerAlwaysLoopStore.set(!$playerAlwaysLoopStore)}
/>
<span></span>
</label>
</nav>
</div>
{#if Capacitor.getPlatform() === 'android'}
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.backgroundPlay')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerAndroidBgPlayer}
on:click={() => playerAndroidBgPlayer.set(!$playerAndroidBgPlayer)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.lockOrientation')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerAndroidLockOrientation}
on:click={() => playerAndroidLockOrientation.set(!$playerAndroidLockOrientation)}
/>
<span></span>
</label>
</nav>
</div>
{/if}
{#if Capacitor.isNativePlatform()}
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.proxyVideos')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerProxyVideosStore}
on:click={() => playerProxyVideosStore.set(!$playerProxyVideosStore)}
/>
<span></span>
</label>
</nav>
</div>
{/if}
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.savePlaybackPosition')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerSavePlaybackPositionStore}
on:click={() => playerSavePlaybackPositionStore.set(!$playerSavePlaybackPositionStore)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.listenByDefault')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerListenByDefaultStore}
on:click={() => playerListenByDefaultStore.set(!$playerListenByDefaultStore)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin m l">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.theatreModeByDefault')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerTheatreModeByDefaultStore}
on:click={() => playerTheatreModeByDefaultStore.set(!$playerTheatreModeByDefaultStore)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.autoPlayNextByDefault')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerAutoplayNextByDefaultStore}
on:click={() => playerAutoplayNextByDefaultStore.set(!$playerAutoplayNextByDefaultStore)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.silenceSkipper')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$silenceSkipperStore}
on:click={() => silenceSkipperStore.set(!$silenceSkipperStore)}
/>
<span></span>
</label>
</nav>
</div>
{#if Capacitor.isNativePlatform()}
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.youtubeJsAlways')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerYouTubeJsAlways}
on:click={() => playerYouTubeJsAlways.set(!$playerYouTubeJsAlways)}
/>
<span></span>
</label>
</nav>
</div>
<div class="field no-margin">
<nav class="no-padding">
<div class="max">
<div>{$_('layout.player.youtubeJsFallback')}</div>
</div>
<label class="switch">
<input
type="checkbox"
bind:checked={$playerYouTubeJsFallback}
on:click={() => playerYouTubeJsFallback.set(!$playerYouTubeJsFallback)}
/>
<span></span>
</label>
</nav>
</div>
{/if}
@@ -0,0 +1,37 @@
<script lang="ts">
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { ensureNoTrailingSlash } from '../../misc';
import { returnYTDislikesInstanceStore, returnYtDislikesStore } from '../../store';
let returnYTInstance = get(returnYTDislikesInstanceStore);
</script>
<form
on:submit|preventDefault={() =>
returnYTDislikesInstanceStore.set(ensureNoTrailingSlash(returnYTInstance))}
>
<nav>
<div class="field label border max">
<input bind:value={returnYTInstance} name="returnyt-instance" type="text" />
<label for="returnyt-instance">{$_('layout.instanceUrl')}</label>
</div>
<button class="square round">
<i>done</i>
</button>
</nav>
</form>
<nav class="no-padding">
<div class="max">
<p>{$_('enabled')}</p>
</div>
<label class="switch">
<input
bind:checked={$returnYtDislikesStore}
on:click={() => returnYtDislikesStore.set(!$returnYtDislikesStore)}
type="checkbox"
/>
<span></span>
</label>
</nav>
@@ -0,0 +1,205 @@
<script lang="ts">
import ISO6391 from 'iso-639-1';
import { onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import {
deArrowInstanceStore,
deArrowThumbnailInstanceStore,
instanceStore,
interfaceDefaultPage,
interfaceForceCase,
interfaceRegionStore,
playerDefaultLanguage,
returnYTDislikesInstanceStore,
sponsorBlockCategoriesStore,
sponsorBlockUrlStore,
synciousInstanceStore
} from '../../store';
import ApiExtended from './ApiExtended.svelte';
import DataPreferences from './DataPreferences.svelte';
import DeArrow from './DeArrow.svelte';
import Interface from './Interface.svelte';
import Player from './Player.svelte';
import Ryd from './RYD.svelte';
import SponsorBlock from './SponsorBlock.svelte';
let sponsorCategoriesList: string[] = [];
sponsorBlockCategoriesStore.subscribe((value) => (sponsorCategoriesList = value));
let sponsorBlockInstance = get(sponsorBlockUrlStore);
let synciousInstance = get(synciousInstanceStore);
let returnYTInstance = get(returnYTDislikesInstanceStore);
let invidiousInstance = get(instanceStore);
let deArrowUrl = get(deArrowInstanceStore);
let deArrowThumbnailUrl = get(deArrowThumbnailInstanceStore);
let region = get(interfaceRegionStore);
let forceCase = get(interfaceForceCase);
let defaultLanguage = get(playerDefaultLanguage);
let defaultPage = get(interfaceDefaultPage);
const languageNames = ISO6391.getAllCodes().map((code) =>
ISO6391.getName(code).toLocaleLowerCase()
);
const sponsorCategories = [
{ name: $_('layout.sponsors.sponsor'), category: 'sponsor' },
{ name: $_('layout.sponsors.unpaidSelfPromotion'), category: 'selfpromo' },
{ name: $_('layout.sponsors.interactionReminder'), category: 'interaction' },
{ name: $_('layout.sponsors.intermissionIntroAnimation'), category: 'intro' },
{ name: $_('layout.sponsors.credits'), category: 'outro' },
{ name: $_('layout.sponsors.preViewRecapHook'), category: 'preview' },
{ name: $_('layout.sponsors.tangentJokes'), category: 'filler' }
];
function toggleSponsor(category: string) {
if (sponsorCategoriesList.includes(category)) {
sponsorBlockCategoriesStore.set(sponsorCategoriesList.filter((value) => value !== category));
} else {
sponsorCategoriesList.push(category);
sponsorBlockCategoriesStore.set(sponsorCategoriesList);
}
}
let activeTab:
| 'interface'
| 'player'
| 'data'
| 'ryd'
| 'api extended'
| 'sponsorblock'
| 'dearrow' = 'interface';
let dialogType = '';
function checkWidth() {
if (innerWidth <= 1320) {
dialogType = 'right';
} else {
dialogType = '';
}
}
onMount(() => {
checkWidth();
addEventListener('resize', () => checkWidth());
// Set default language if not set already based off browser.
if (!get(playerDefaultLanguage)) {
const languageCode = navigator.language || navigator.languages[0];
const languageName = ISO6391.getName(languageCode.split('-')[0]);
if (languageName) {
playerDefaultLanguage.set(languageName.toLowerCase());
}
}
});
</script>
<dialog id="dialog-settings" class={dialogType}>
<nav style="margin-bottom: 1em;">
<h4 class="max">{$_('layout.settings')}</h4>
<button class="circle transparent" data-ui="#dialog-settings"><i>close</i></button>
</nav>
<div>
<nav class="tabbed small">
<a class:active={activeTab === 'interface'} on:click={() => (activeTab = 'interface')}>
<i>grid_view</i>
<span>{$_('layout.interface')}</span>
</a>
<a class:active={activeTab === 'player'} on:click={() => (activeTab = 'player')}>
<i>smart_display</i>
<span>{$_('layout.player.title')}</span>
</a>
<a class:active={activeTab === 'data'} on:click={() => (activeTab = 'data')}>
<i>save</i>
<span>{$_('layout.dataPreferences.dataPreferences')}</span>
</a>
<a class:active={activeTab === 'ryd'} on:click={() => (activeTab = 'ryd')}>
<i>thumb_down</i>
<span>RYD</span>
</a>
<a class:active={activeTab === 'api extended'} on:click={() => (activeTab = 'api extended')}>
<i>sync</i>
<span>API Extended</span>
</a>
<a class:active={activeTab === 'sponsorblock'} on:click={() => (activeTab = 'sponsorblock')}>
<i>block</i>
<span>Sponsorblock</span>
</a>
<a class:active={activeTab === 'dearrow'} on:click={() => (activeTab = 'dearrow')}>
<i>keyboard_double_arrow_down</i>
<span>{$_('layout.deArrow.title')}</span>
</a>
<a href="https://github.com/sponsors/WardPearce" target="_blank" referrerpolicy="no-referrer">
<i>favorite</i>
<span>{$_('donate')}</span>
</a>
<a
href="https://github.com/Materialious/Materialious"
target="_blank"
referrerpolicy="no-referrer"
>
<i>code</i>
<span>GitHub</span>
</a>
</nav>
<div class="page padding" class:active={activeTab === 'interface'}>
<Interface />
</div>
<div class="page padding" class:active={activeTab === 'player'}>
<Player />
</div>
<div class="page padding" class:active={activeTab === 'data'}>
<DataPreferences />
</div>
<div class="page padding" class:active={activeTab === 'ryd'}>
<Ryd />
</div>
<div class="page padding" class:active={activeTab === 'api extended'}>
<ApiExtended />
</div>
<div class="page padding" class:active={activeTab === 'sponsorblock'}>
<SponsorBlock />
</div>
<div class="page padding" class:active={activeTab === 'dearrow'}>
<DeArrow />
</div>
</div>
</dialog>
<style>
dialog {
width: 1320px;
}
.tabbed > a {
flex: none;
}
@media screen and (max-width: 1320px) {
.tabbed {
display: flex !important;
flex-direction: column !important;
gap: 0 !important;
flex: 0 !important;
margin-bottom: 1em !important;
align-items: start !important;
padding: 1em 0 !important;
background-color: transparent;
border-bottom: solid 1px var(--outline-variant);
}
.tabbed > a {
font-size: 1.5rem !important;
padding: 0.5em 0.3em !important;
border-radius: 3em !important;
}
.tabbed.small {
block-size: 100% !important;
border-radius: 0 !important;
}
}
</style>
@@ -0,0 +1,115 @@
<script lang="ts">
import { _ } from 'svelte-i18n';
import { get } from 'svelte/store';
import { ensureNoTrailingSlash } from '../../misc';
import {
sponsorBlockCategoriesStore,
sponsorBlockDisplayToastStore,
sponsorBlockStore,
sponsorBlockTimelineStore,
sponsorBlockUrlStore
} from '../../store';
let sponsorCategoriesList: string[] = [];
sponsorBlockCategoriesStore.subscribe((value) => (sponsorCategoriesList = value));
let sponsorBlockInstance = get(sponsorBlockUrlStore);
const sponsorCategories = [
{ name: $_('layout.sponsors.sponsor'), category: 'sponsor' },
{ name: $_('layout.sponsors.unpaidSelfPromotion'), category: 'selfpromo' },
{ name: $_('layout.sponsors.interactionReminder'), category: 'interaction' },
{ name: $_('layout.sponsors.intermissionIntroAnimation'), category: 'intro' },
{ name: $_('layout.sponsors.credits'), category: 'outro' },
{ name: $_('layout.sponsors.preViewRecapHook'), category: 'preview' },
{ name: $_('layout.sponsors.tangentJokes'), category: 'filler' }
];
function toggleSponsor(category: string) {
if (sponsorCategoriesList.includes(category)) {
sponsorBlockCategoriesStore.set(sponsorCategoriesList.filter((value) => value !== category));
} else {
sponsorCategoriesList.push(category);
sponsorBlockCategoriesStore.set(sponsorCategoriesList);
}
}
</script>
<form
on:submit|preventDefault={() =>
sponsorBlockUrlStore.set(ensureNoTrailingSlash(sponsorBlockInstance))}
>
<nav>
<div class="field label border max">
<input bind:value={sponsorBlockInstance} name="sponsorblock-instance" type="text" />
<label for="sponsorblock-instance">{$_('layout.instanceUrl')}</label>
</div>
<button class="square round">
<i>done</i>
</button>
</nav>
</form>
<nav class="no-padding">
<div class="max">
<p>{$_('enabled')}</p>
</div>
<label class="switch">
<input
bind:checked={$sponsorBlockStore}
on:click={() => sponsorBlockStore.set(!$sponsorBlockStore)}
type="checkbox"
/>
<span></span>
</label>
</nav>
<nav class="no-padding">
<div class="max">
<p>{$_('layout.sponsors.disableToast')}</p>
</div>
<label class="switch">
<input
bind:checked={$sponsorBlockDisplayToastStore}
on:click={() => sponsorBlockDisplayToastStore.set(!$sponsorBlockDisplayToastStore)}
type="checkbox"
/>
<span></span>
</label>
</nav>
<nav class="no-padding">
<div class="max">
<p>{$_('layout.sponsors.disableTimeline')}</p>
</div>
<label class="switch">
<input
bind:checked={$sponsorBlockTimelineStore}
on:click={() => sponsorBlockTimelineStore.set(!$sponsorBlockTimelineStore)}
type="checkbox"
/>
<span></span>
</label>
</nav>
<hr style="margin: 1em 0;" />
<p class="bold">{$_('layout.sponsors.Catagories')}</p>
{#each sponsorCategories as sponsor}
<div class="field middle-align no-margin">
<nav class="no-padding">
<div class="max">
<p>{sponsor.name}</p>
</div>
<label class="switch">
<input
type="checkbox"
checked={sponsorCategoriesList.includes(sponsor.category)}
on:click={() => toggleSponsor(sponsor.category)}
/>
<span></span>
</label>
</nav>
</div>
{/each}
@@ -107,6 +107,7 @@
"customize": "Customize",
"lowBandwidthMode": "Low bandwidth mode",
"displayThumbnailAvatars": "Display channel avatars on thumbnail (Slow)",
"searchHistory": "Search history",
"theme": {
"theme": "Theme",
"darkMode": "Dark mode",
+3
View File
@@ -68,6 +68,7 @@ export const interfaceAmoledTheme = persisted('amoledTheme', false);
export const interfaceLowBandwidthMode = persisted('lowBandwidthMode', false);
export const interfaceDisplayThumbnailAvatars = persisted('disableThumbnailAvatars', false);
export const interfaceDefaultPage = persisted('defaultPage', '/');
export const interfaceSearchHistoryEnabled = persisted('searchHistoryEnabled', false);
export const sponsorBlockStore = persisted('sponsorBlock', true);
export const sponsorBlockUrlStore: Writable<string | null | undefined> = persisted(
@@ -110,3 +111,5 @@ export const miniPlayerSrcStore: Writable<{ video: VideoPlay; time: number; } |
export const silenceSkipperStore: Writable<boolean> = persisted('silenceSkipper', false);
export const poTokenCacheStore: Writable<PoTokens> = writable();
export const searchHistoryStore: Writable<string[]> = persisted('searchHistory', []);
+1 -1
View File
@@ -10,7 +10,7 @@
import MiniPlayer from '$lib/components/MiniPlayer.svelte';
import PageLoading from '$lib/components/PageLoading.svelte';
import Search from '$lib/components/Search.svelte';
import Settings from '$lib/components/Settings.svelte';
import Settings from '$lib/components/Settings/Settings.svelte';
import SyncParty from '$lib/components/SyncParty.svelte';
import Thumbnail from '$lib/components/Thumbnail.svelte';
import { bookmarkletLoadFromUrl, loadSettingsFromEnv } from '$lib/externalSettings';
+4
View File
@@ -48,6 +48,10 @@
display: none !important;
}
#dialog-settings form {
margin: 1em 0;
}
main.root {
padding-left: 7em !important;
padding-top: 5em !important;