diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 89fc307..c21af39 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -28,6 +28,7 @@ module.exports = { } ], rules: { - '@typescript-eslint/no-explicit-any': 'off' + '@typescript-eslint/no-explicit-any': 'off', + 'no-undef': 'off' } }; diff --git a/src/entries/popup/Popup.svelte b/src/entries/popup/Popup.svelte index 46972fb..28957ff 100644 --- a/src/entries/popup/Popup.svelte +++ b/src/entries/popup/Popup.svelte @@ -1,117 +1,93 @@ <script lang="ts"> - import { matches } from '~/lib/match'; + import { type Match, matches } from '~/lib/match'; import { Hosters, Other } from '~/lib/settings'; + import Toggle from './toggle.svelte'; let hostersEnabled: boolean; - let hosters = []; + let hosters: (Match & { active: boolean; disabled: boolean })[] = []; (async () => { hostersEnabled = !(await Hosters.getAllDisabled()); const disabled = await Hosters.getDisabled(); - hosters = Object.values(matches).map((m) => { + hosters = Object.values(matches).map((m: any) => { m['active'] = disabled.findIndex((p) => p.id == m.id) == -1; - m['disabled'] = !hostersEnabled; return m; - }); + }) as typeof hosters; + })(); + + let isMobile: boolean; + (async () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + isMobile = (await browser.runtime.getPlatformInfo()).os === 'android'; })(); let ff2mpvEnabled: boolean; (async () => { - ff2mpvEnabled = await Other.getFf2mpv(); + ff2mpvEnabled = (await Other.getFf2mpv()) as boolean; })(); </script> -<main> - <div> - <h3 class="header">Hosters</h3> - <div class="buttons super-buttons"> - <button - class:active={hostersEnabled} - on:click={async () => { - await Hosters.enableAll(); - hostersEnabled = true; - hosters = hosters.map((m) => { - m['disabled'] = false; - return m; - }); - }}>On</button - > - <button - class:active={!hostersEnabled} - on:click={async () => { - await Hosters.disableAll(); - hostersEnabled = false; - hosters = hosters.map((m) => { - m['disabled'] = true; - return m; - }); - }}>Off</button - > - </div> - <table class="setting-table"> - {#each hosters as hoster} - <tr> - <td class="setting-name"> - <p>{hoster.name}</p> - </td> - <td class="buttons"> - <button - class:disabled={hoster.disabled} - class:active={hoster.active} - on:click={async () => { - if (hoster.disabled) return; +<main + style={isMobile + ? 'height: 100vh; display: flex; flex-direction: column; align-items: center' + : 'height: 500px'} +> + <fieldset> + <legend>Hoster</legend> + <div class="setting-container" style={isMobile ? 'grid-column-gap: 5rem' : ''}> + <label for="hosters-enabled">Enabled</label> + <div> + <Toggle + bind:checked={hostersEnabled} + id="hosters-enabled" + on:change={() => Hosters.setAll(hostersEnabled)} + /> + </div> + <hr /> + {#each hosters as hoster, i} + <label for="hoster-{i}" style="cursor: {hostersEnabled ? 'pointer' : 'default'}" + >{hoster.name}</label + > + <div> + <Toggle + bind:checked={hoster.active} + disabled={!hostersEnabled} + id="hoster-{i}" + on:change={async () => { + if (hoster.active) { await Hosters.enable(hoster); - hoster.active = true; - }}>On</button - > - <button - class:disabled={hoster.disabled} - class:active={!hoster.active} - on:click={async () => { - if (hoster.disabled) return; + } else { await Hosters.disable(hoster); - hoster.active = false; - }}>Off</button - > - </td> - </tr> + } + }} + ></Toggle> + </div> {/each} - </table> - </div> - <hr /> - <div> - <h3 class="header">Other</h3> - <table> - <tr> - <td class="setting-name"> - <p>ff2mpv</p> - </td> - <td class="buttons"> - <button - class:active={ff2mpvEnabled} - on:click={async () => { - await Other.setFf2mpv(true); - ff2mpvEnabled = true; - }}>On</button - > - <button - class:active={!ff2mpvEnabled} - on:click={async () => { - await Other.setFf2mpv(false); - ff2mpvEnabled = false; - }}>Off</button - > + </div> + </fieldset> + {#if !isMobile} + <fieldset> + <legend>Other</legend> + <div class="setting-container"> + <label for="ff2mpv">ff2mpv</label> + <div> + <Toggle + bind:checked={ff2mpvEnabled} + id="ff2mpv" + on:change={async () => Other.setFf2mpv(ff2mpvEnabled)} + ></Toggle> <a + class="info-questionmark" href="https://github.com/ByteDream/stream-bypass/tree/master#ff2mpv-use-mpv-to-directly-play-streams" - >🛈</a + >?</a > - </td> - </tr> - </table> - </div> - <hr /> - <a id="bug-notice" href="https://github.com/ByteDream/stream-bypass/issues" - >Something does not work</a + </div> + </div> + </fieldset> + {/if} + <a id="report-notice" href="https://github.com/ByteDream/stream-bypass/issues" + >Report issues or requests</a > </main> @@ -125,7 +101,12 @@ padding: 0 8px; } - #bug-notice { + fieldset { + border-radius: 5px; + border-color: gray; + } + + #report-notice { border: none; color: white; display: block; @@ -148,52 +129,33 @@ text-align: center; } - .setting-table { - border-collapse: collapse; - border-spacing: 0; - } + .setting-container { + display: grid; + grid-template-columns: auto auto; + grid-column-gap: 5px; + grid-row-gap: 4px; + align-items: end; + width: 100%; - .setting-name { - height: 34px; - - p { + & > label { + height: 34px; margin: 0; - cursor: default; - } - } - - .buttons { - display: flex; - flex-direction: row; - height: 34px; - - button, - a { - border: 1px solid #281515; - background-color: transparent; - color: white; + user-select: none; cursor: pointer; - padding: 5px 8px; - margin: 0; - text-decoration: none; - - &.active { - background-color: rgba(255, 65, 65, 0.74); - cursor: default; - } - - &.disabled { - background-color: gray; - cursor: not-allowed; - } - } - - &.super-buttons { display: flex; - justify-content: center; - gap: 4px; - width: 100%; - margin-bottom: 10px; + align-items: center; } + + & > hr { + grid-column: 1 / span 2; + width: 100%; + } + } + + .info-questionmark { + display: inline-block; + transform: translateX(-40%) translateY(-100%); + color: black; + text-decoration: none; } </style> diff --git a/src/entries/popup/index.html b/src/entries/popup/index.html index d8ffad5..e6fbe27 100644 --- a/src/entries/popup/index.html +++ b/src/entries/popup/index.html @@ -1,10 +1,11 @@ <!doctype html> -<html style="width: fit-content; height: 500px; overflow-y: hidden" lang="en"> +<html style="overflow-y: hidden" lang="en"> <head> <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width" /> <title>Stream Bypass</title> </head> - <body style="width: fit-content; height: 500px; overflow-y: scroll"> + <body style="overflow-y: scroll"> <script type="module"> import Popup from '~/entries/popup/Popup.svelte'; diff --git a/src/entries/popup/toggle.svelte b/src/entries/popup/toggle.svelte new file mode 100644 index 0000000..b7986a3 --- /dev/null +++ b/src/entries/popup/toggle.svelte @@ -0,0 +1,65 @@ +<!-- https://flowbite.com/docs/forms/toggle/ --> +<script lang="ts"> + import { createEventDispatcher } from 'svelte'; + + export let checked = false; + export let disabled = false; + export let id: string | null = null; + + const dispatch = createEventDispatcher(); +</script> + +<label class="toggle"> + <slot /> + <input type="checkbox" {id} bind:checked {disabled} on:change={(e) => dispatch('change', e)} /> + <span /> +</label> + +<style lang="scss" global> + .toggle { + display: inline-flex; + align-items: center; + cursor: pointer; + + input { + clip: rect(0, 0, 0, 0); + position: absolute; + + &:checked + span { + background: limegreen; + + &:after { + transform: translateX(100%); + } + } + + &:disabled + span { + background: gray; + } + } + + span { + position: relative; + width: 2.75rem; + height: 1.5rem; + background: #cf0000; + border-radius: 9999px; + + &:after { + content: ''; + position: absolute; + top: 2px; + inset-inline-start: 2px; + background: white; + border-radius: 9999px; + height: 1.25rem; + width: 1.25rem; + transition: all 0.15s; + } + } + + &:has(input:disabled) { + cursor: default; + } + } +</style> diff --git a/src/lib/settings.ts b/src/lib/settings.ts index 9d9ebae..985dabd 100644 --- a/src/lib/settings.ts +++ b/src/lib/settings.ts @@ -25,11 +25,8 @@ export const Hosters = { getAllDisabled: async () => { return await storageGet<boolean>('hosters.allDisabled', false); }, - disableAll: async () => { - await storageSet('hosters.allDisabled', true); - }, - enableAll: async () => { - await storageSet('hosters.allDisabled', false); + setAll: async (enable: boolean) => { + await storageSet('hosters.allDisabled', !enable); } }; @@ -47,7 +44,7 @@ export const Redirect = { export const Other = { getFf2mpv: async () => { - return await storageGet('other.ff2mpv', true); + return await storageGet('other.ff2mpv', false); }, setFf2mpv: async (enable: boolean) => { await storageSet('other.ff2mpv', enable);