feat: brand new Modal component (#41)

* feat: brand new Modal component

* Apply suggestions from code review

Co-authored-by: Ax333l <main@axelen.xyz>
This commit is contained in:
mirgb
2022-12-10 12:11:17 -05:00
committed by GitHub
parent 31047103d3
commit 2e71974503
9 changed files with 264 additions and 80 deletions

View File

@@ -33,6 +33,17 @@ body {
margin-top: 7rem;
}
.button-reset {
background-color: transparent;
border: none;
font-family: inherit;
font-size: inherit;
font-style: inherit;
font-weight: inherit;
line-height: inherit;
padding: 0;
}
:root {
--main-font: "Manrope", sans-serif;
--mono-font: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
@@ -100,6 +111,12 @@ h6 {
font-size: 0.85rem;
}
p {
color: var(--grey-five);
font-weight: 400;
font-size: 1rem;
}
/*---------------*/
::-webkit-scrollbar {

View File

@@ -1,8 +1,8 @@
import { browser } from "$app/environment";
import { browser } from '$app/environment';
import { dev_log } from "$lib/utils";
import { dev_log } from '$lib/utils';
const CACHE_KEY_PREFIX = "revanced_api_cache_l1";
const CACHE_KEY_PREFIX = 'revanced_api_cache_l1';
const L1_CACHE_VALIDITY = 5 * 60 * 1000; // 5 minutes
function l1_key_name(endpoint: string) {
@@ -16,16 +16,17 @@ export function get(endpoint: string) {
}
const key_name = l1_key_name(endpoint);
const ls_data: { valid_until: number; data: any } | null = JSON.parse(localStorage.getItem(key_name));
const ls_data: { valid_until: number; data: any } | null = JSON.parse(
localStorage.getItem(key_name) as string
);
if (ls_data === null || ls_data.valid_until <= Date.now()) {
dev_log("Cache", `missed "${endpoint}"`);
dev_log('Cache', `missed "${endpoint}"`);
localStorage.removeItem(key_name);
return null;
}
dev_log("Cache", `hit "${endpoint}"`);
dev_log('Cache', `hit "${endpoint}"`);
return ls_data.data;
}
@@ -35,17 +36,21 @@ export function update(endpoint: string, data: any) {
return;
}
localStorage.setItem(l1_key_name(endpoint), JSON.stringify({
localStorage.setItem(
l1_key_name(endpoint),
JSON.stringify({
data,
valid_until: Date.now() + L1_CACHE_VALIDITY
}));
})
);
}
// Clear the cache
export function clear() {
// Clear the cache and reload
export function clear_and_reload() {
for (const key of Object.keys(localStorage)) {
if (key.startsWith(CACHE_KEY_PREFIX)) {
localStorage.removeItem(key);
}
}
location.reload();
}

View File

@@ -2,9 +2,10 @@ import { browser } from "$app/environment";
const URL_KEY = "revanced_api_url";
export const default_base_url = "https://releases.revanced.app";
// Get base URL
export function api_base_url(): string {
const default_base_url = "https://releases.revanced.app";
if (browser) {
return localStorage.getItem(URL_KEY) || default_base_url;
}

View File

@@ -0,0 +1,126 @@
<script lang="ts">
import * as settings from '../../../data/api/settings';
import Modal from '$lib/components/atoms/Modal.svelte';
import { clear_and_reload } from '../../../data/api/cache';
let url = settings.api_base_url();
function save() {
settings.set_api_base_url(url);
clear_and_reload();
}
function reset() {
url = settings.default_base_url;
}
</script>
<Modal icon="/icons/settings.svg" alt="Settings">
<div class="settings-menu">
<h3>Configure the backend</h3>
<div class="url-stuff">
<h4>API URL:</h4>
<div class="input-wrapper">
<input name="api-url" type="text" bind:value={url} />
<button class="button-reset" on:click={reset}>
<img src="/icons/reset.svg" alt="Reset" />
</button>
</div>
</div>
<div class="btns">
<button class="save-url-btn" on:click={save}>
<h4>Save</h4>
</button>
<button class="clear-cache-btn" on:click={clear_and_reload}>
<h4>Clear cache</h4>
</button>
</div>
</div>
</Modal>
<style>
div.settings-menu {
position: fixed;
width: min(95%, 500px);
/* width: clamp(45vw, 850px, 850px); */
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 40px;
border-radius: 12px;
background-color: var(--grey-six);
display: flex;
user-select: none;
gap: 5%;
z-index: 999;
white-space: normal;
display: flex;
flex-direction: column;
gap: 2px;
box-shadow: 0 3rem 5rem rgba(0, 0, 0, 0.3);
}
div.btns {
display: flex;
gap: 1rem;
text-align: center;
flex-wrap: wrap;
}
.btns button {
min-width: max-content;
font-size: 1rem;
color: var(--white);
font-weight: 600;
border: none;
border-radius: 12px;
flex-grow: 2;
padding: 1rem 1.75rem;
display: block;
cursor: pointer;
background-color: var(--grey-two);
transition: transform 0.4s var(--bezier-one), filter 0.4s var(--bezier-one);
user-select: none;
}
.btns .save-url-btn {
background-color: var(--accent-color);
box-shadow: 0px 0px 32px 1px var(--accent-color-glow);
flex-grow: 1;
}
.save-url-btn h4 {
color: var(--grey-four);
}
div.input-wrapper {
display: flex;
background-color: #2a2c33;
padding: 12px;
width: 100%;
margin: 10px 0 10px 0;
border-radius: 10px;
}
img {
height: 25px;
}
input[type='text'] {
background-color: transparent;
border: 0;
color: white;
font-size: 1rem;
outline: none;
width: 100%;
}
h3 {
margin: 0 0 20px 0;
}
@media (max-width: 780px) {
div.settings-menu {
flex-direction: column;
gap: 2rem;
}
}
</style>

View File

@@ -9,7 +9,9 @@
<a {href} {target}>
<div class={type} style="width: {maxWidth ? '100%' : 'max-content'}">
{#if icon}
<img src="../icons/{icon}.svg" alt={icon} />
{/if}
<slot />
</div>
</a>
@@ -46,8 +48,6 @@
filter: brightness(85%);
}
div,
.button-secondary {
display: flex;

View File

@@ -0,0 +1,71 @@
<script lang="ts">
import { click_outside } from '$lib/utils';
import { fade } from 'svelte/transition';
let modalOpen = false;
export let icon: string;
export let alt: string;
</script>
<div>
<button class="modal-btn" on:click={() => (modalOpen = !modalOpen)}>
<img src={icon} {alt} />
</button>
{#if modalOpen}
<div class="overlay" />
<div
class="modal-container"
role="dialog"
aria-modal="true"
use:click_outside
on:click_outside={() => (modalOpen = false)}
transition:fade={{ duration: 125 }}
>
<slot />
</div>
{/if}
</div>
<style>
button.modal-btn {
border: 0;
background-color: transparent;
transition-timing-function: var(--bezier-one);
transition-duration: 0.25s;
padding: 10px 25px;
border-radius: 200px;
display: flex;
align-items: center;
justify-content: center;
}
button.modal-btn:hover {
color: var(--white);
background-color: var(--grey-one);
}
img {
height: 1.75rem;
width: auto;
cursor: pointer;
display: flex;
align-items: center;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1;
}
@media (max-width: 768px) {
button.modal-btn {
padding: 1rem 1.5rem;
border-radius: 16px;
}
}
</style>

View File

@@ -1,10 +1,10 @@
<script lang="ts">
import Navigation from '../atoms/NavButton.svelte';
import { page } from '$app/stores';
import Button from '../atoms/Button.svelte';
import MobileDropdown from './MobileDropdown.svelte';
import { onMount } from 'svelte';
import RouterEvents from '../../../data/RouterEvents';
import ApiSettingsButton from '$lib/components/atoms/ApiSettingsButton.svelte';
let menuOpen = false;
@@ -25,7 +25,6 @@
<img src="/logo.svg" class="logo-image" alt="ReVanced Logo" />
</span>
</a>
<span class="desktop">
<Navigation href="/">Home</Navigation>
<Navigation href="/download">Download</Navigation>
@@ -44,14 +43,12 @@
<Navigation href="/contributors/">
<img src="/icons/contrib.svg" alt="Contributors" />
</Navigation>
<Navigation href="/api-settings/">
<img src="/icons/settings.svg" alt="Settings" />
</Navigation>
<ApiSettingsButton />
</span>
<!-- Should probably be moved to its own component. -->
<button
class="menu-btn mobile"
class="menu-btn button-reset mobile"
class:open={menuOpen}
on:click={() => {
menuOpen = !menuOpen;
@@ -76,9 +73,9 @@
<Navigation href="/contributors/">
<img src="/icons/contrib.svg" alt="Contributors" />
</Navigation>
<Navigation href="/api-settings/">
<img src="/icons/settings.svg" alt="Settings" />
</Navigation>
<ApiSettingsButton on:click={() => {
menuOpen = false;
}}/>
</div>
</div>
</MobileDropdown>
@@ -171,6 +168,7 @@
display: none !important;
}
}
.menu-btn {
user-select: none;
position: relative;
@@ -181,17 +179,8 @@
height: 60px;
cursor: pointer;
transition: all 0.5s var(--bezier-one);
/* We don't want it to look like a normal button. */
background-color: transparent;
border: none;
font-family: inherit;
font-size: inherit;
font-style: inherit;
font-weight: inherit;
line-height: inherit;
padding: 0;
}
.menu-btn__burger {
width: 25px;
height: 2px;

View File

@@ -1,26 +0,0 @@
<script lang="ts">
import * as settings from '../../data/api/settings';
import { clear } from '../../data/api/cache';
let url = settings.api_base_url();
function handler() {
clear();
settings.set_api_base_url(url);
location.reload(true);
}
</script>
<section class="settings">
<input name="api-url" type="text" bind:value={url} />
<button on:click={handler}>Save</button>
</section>
<section class="cache">
<button on:click={clear}>Clear cache</button>
</section>
<style>
.settings {
padding-top: 5rem;
}
</style>

1
static/icons/reset.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" fill="#ACC1D2"><path d="M14 38v-3h14.45q3.5 0 6.025-2.325Q37 30.35 37 26.9t-2.525-5.775Q31.95 18.8 28.45 18.8H13.7l5.7 5.7-2.1 2.1L8 17.3 17.3 8l2.1 2.1-5.7 5.7h14.7q4.75 0 8.175 3.2Q40 22.2 40 26.9t-3.425 7.9Q33.15 38 28.4 38Z"/></svg>

After

Width:  |  Height:  |  Size: 299 B