diff --git a/.env.example b/.env.example index c25e14d2..8abff3f5 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,5 @@ RV_API_URL=https://api.revanced.app +RV_STATUS_URL=https://status.revanced.app +RV_EMAIL=contact@revanced.app RV_GOOGLE_TAG_MANAGER_ID= RV_DMCA_GUID= \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 305a0761..8cd1ba53 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -34,6 +34,8 @@ jobs: RV_API_URL: ${{ vars.RV_API_URL }} RV_GOOGLE_TAG_MANAGER_ID: ${{ vars.RV_GOOGLE_TAG_MANAGER_ID }} RV_DMCA_GUID: ${{ vars.RV_DMCA_GUID }} + RV_STATUS_URL: ${{ vars.RV_STATUS_URL }} + RV_EMAIL: ${{ vars.RV_EMAIL }} run: npm run build - name: Deploy diff --git a/src/app.html b/src/app.html index 197516e0..52668865 100644 --- a/src/app.html +++ b/src/app.html @@ -22,6 +22,6 @@ - %sveltekit.body% +
%sveltekit.body%
diff --git a/src/app.scss b/src/app.scss index c7a850d9..bb3da8d0 100644 --- a/src/app.scss +++ b/src/app.scss @@ -143,7 +143,7 @@ p { line-height: 1.75rem; } -@media screen and (max-width: 767px) { +@media (max-width: 768px) { h1 { font-size: 2.6rem; line-height: 3.75rem; diff --git a/src/data/api/settings.ts b/src/data/api/settings.ts index 01dd93df..b509d573 100644 --- a/src/data/api/settings.ts +++ b/src/data/api/settings.ts @@ -1,21 +1,13 @@ import { browser } from '$app/environment'; -import { RV_API_URL } from '$env/static/public'; +import { RV_API_URL, RV_EMAIL, RV_STATUS_URL } from '$env/static/public'; export const default_api_url = RV_API_URL; +export const default_status_url = RV_STATUS_URL; +export const default_email = RV_EMAIL; const URL_KEY = 'revanced_api_url'; const STATUS_KEY = 'revanced_status_url'; - -function set_status_url(apiUrl: string) { - fetch(`${apiUrl}/v4/about`) - .then((response) => (response.ok ? response.json() : null)) - .then((data) => { - if (data?.status) { - localStorage.setItem(STATUS_KEY, data.status); - console.log('Status is now:', localStorage.getItem(STATUS_KEY)); - } - }); -} +const EMAIL_KEY = 'revanced_email'; export const API_VERSION = 'v4'; @@ -24,9 +16,7 @@ export function api_base_url(): string { if (browser) { const apiUrl = localStorage.getItem(URL_KEY) || default_api_url; - if (!localStorage.getItem(STATUS_KEY)) { - set_status_url(apiUrl); - } + set_about_info(apiUrl); return apiUrl; } @@ -34,12 +24,20 @@ export function api_base_url(): string { return default_api_url; } -export function status_url(): string | null { +export function status_url(): string { + if (browser) { + return localStorage.getItem(STATUS_KEY) || default_status_url; + } + + return default_status_url; +} + +export function email(): string { if (browser) { - return localStorage.getItem(STATUS_KEY) || null; + return localStorage.getItem(EMAIL_KEY) || default_email; } - return null; + return default_email; } // (re)set base URL. @@ -48,6 +46,21 @@ export function set_api_base_url(url?: string) { localStorage.removeItem(URL_KEY); } else { localStorage.setItem(URL_KEY, url); - set_status_url(url); + set_about_info(url); + } +} + +function set_about_info(apiUrl: string) { + if (!localStorage.getItem(STATUS_KEY) || !localStorage.getItem(EMAIL_KEY)) { + fetch(`${apiUrl}/v4/about`) + .then((response) => (response.ok ? response.json() : null)) + .then((data) => { + if (data?.status) { + localStorage.setItem(STATUS_KEY, data.status); + localStorage.setItem(EMAIL_KEY, data.contact.email); + console.log('Status is now:', localStorage.getItem(STATUS_KEY)); + console.log('Email is now:', localStorage.getItem(EMAIL_KEY)); + } + }); } } diff --git a/src/layout/Banners/AnnouncementBanner.svelte b/src/layout/Banners/AnnouncementBanner.svelte new file mode 100644 index 00000000..1b236a92 --- /dev/null +++ b/src/layout/Banners/AnnouncementBanner.svelte @@ -0,0 +1,61 @@ + + +{#if latestUnreadAnnouncement} + +{/if} diff --git a/src/lib/components/Banner.svelte b/src/layout/Banners/Banner.svelte similarity index 96% rename from src/lib/components/Banner.svelte rename to src/layout/Banners/Banner.svelte index f87bd1ad..8f8726b6 100644 --- a/src/lib/components/Banner.svelte +++ b/src/layout/Banners/Banner.svelte @@ -2,7 +2,7 @@ import { createEventDispatcher } from 'svelte'; import Close from 'svelte-material-icons/Close.svelte'; import ArrowRight from 'svelte-material-icons/ArrowRight.svelte'; - import Button from './Button.svelte'; + import Button from '$lib/components/Button.svelte'; export let title: string; export let description: string | undefined = undefined; @@ -83,7 +83,7 @@ color: #601410; } - @media (max-width: 767px) { + @media (max-width: 768px) { flex-direction: column; padding: 1.1rem 1.3rem; } diff --git a/src/layout/Navbar/StatusBanner.svelte b/src/layout/Banners/StatusBanner.svelte similarity index 62% rename from src/layout/Navbar/StatusBanner.svelte rename to src/layout/Banners/StatusBanner.svelte index ccc90e76..db325567 100644 --- a/src/layout/Navbar/StatusBanner.svelte +++ b/src/layout/Banners/StatusBanner.svelte @@ -1,6 +1,7 @@ + + + It's your choice + + We use analytics to improve your experience on this site. By clicking "Allow", you allow us to + collect anonymous data about your visit. + + + + + + diff --git a/src/layout/Dialogs/CryptoDialog.svelte b/src/layout/Dialogs/CryptoDialog.svelte new file mode 100644 index 00000000..dcb77ea5 --- /dev/null +++ b/src/layout/Dialogs/CryptoDialog.svelte @@ -0,0 +1,145 @@ + + + + + + + Cryptocurrencies + +
+
+ {#each wallets as wallet} + + {/each} +
+
+ + + +
+ + + + + + {qrCodeDialogueName} Wallet + +
+ {qrCodeValue} + +
+
+ + + + +
+ + + Address copied to clipboard + + + diff --git a/src/lib/components/Dialogue.svelte b/src/layout/Dialogs/Dialog.svelte similarity index 52% rename from src/lib/components/Dialogue.svelte rename to src/layout/Dialogs/Dialog.svelte index c9ed5e1f..52b65f85 100644 --- a/src/lib/components/Dialogue.svelte +++ b/src/layout/Dialogs/Dialog.svelte @@ -4,7 +4,7 @@ import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte'; - export let modalOpen = false; + export let dialogOpen = false; export let fullscreen = false; export let notDismissible = false; @@ -16,20 +16,20 @@ } -{#if modalOpen} +{#if dialogOpen} +
{ - if (!notDismissible) modalOpen = !modalOpen; + if (!notDismissible) dialogOpen = !dialogOpen; }} on:keypress={() => { - if (!notDismissible) modalOpen = !modalOpen; + if (!notDismissible) dialogOpen = !dialogOpen; }} transition:fade={{ easing: quadInOut, duration: 150 }} /> 10} aria-modal="true" @@ -37,41 +37,39 @@ on:scroll={parseScroll} transition:fade={{ easing: quadInOut, duration: 150 }} > -
-
- {#if fullscreen} - - {/if} - {#if $$slots.icon} - - {/if} - {#if $$slots.title} -

- -

- {/if} -
- - {#if $$slots.description} -

- -

+
+ {#if fullscreen} + {/if} - -
- - {#if $$slots.buttons} -
- -
+ {#if $$slots.icon} + + {/if} + {#if $$slots.title} +

+ +

{/if}
+ + {#if $$slots.description} +

+ +

+ {/if} + +
+ + {#if $$slots.buttons} +
+ +
+ {/if}
{/if} - diff --git a/src/layout/Dialogs/DownloadCompatibilityWarningDialog.svelte b/src/layout/Dialogs/DownloadCompatibilityWarningDialog.svelte new file mode 100644 index 00000000..658221d0 --- /dev/null +++ b/src/layout/Dialogs/DownloadCompatibilityWarningDialog.svelte @@ -0,0 +1,25 @@ + + + + Warning + {warning} Do you still want to download? + + + + + + + diff --git a/src/lib/components/ImageModal.svelte b/src/layout/Dialogs/ImageDialog.svelte similarity index 77% rename from src/lib/components/ImageModal.svelte rename to src/layout/Dialogs/ImageDialog.svelte index 987a9739..3d7806d6 100644 --- a/src/lib/components/ImageModal.svelte +++ b/src/layout/Dialogs/ImageDialog.svelte @@ -7,12 +7,12 @@ const dispatch = createEventDispatcher(); - function closeModal() { + function closeDialog() { dispatch('close'); } function handleKeydown(event: KeyboardEvent) { - if (event.key === 'Escape') closeModal(); + if (event.key === 'Escape') closeDialog(); } @@ -20,16 +20,16 @@ - - + diff --git a/src/layout/Footer/FooterSection.svelte b/src/layout/Footer/FooterSection.svelte index 2c39b6ad..ffe11ad0 100644 --- a/src/layout/Footer/FooterSection.svelte +++ b/src/layout/Footer/FooterSection.svelte @@ -2,19 +2,10 @@ export let title: string; -
- {title} -
    - -
-
- -
- +
+ + {title} +
@@ -22,49 +13,23 @@ diff --git a/src/layout/Hero/HeroImage.svelte b/src/layout/Hero/HeroImage.svelte index faa47fdd..de88dabd 100644 --- a/src/layout/Hero/HeroImage.svelte +++ b/src/layout/Hero/HeroImage.svelte @@ -7,17 +7,17 @@
- diff --git a/src/layout/Hero/HeroSection.svelte b/src/layout/Hero/HeroSection.svelte index 69ebb129..a5f4d9aa 100644 --- a/src/layout/Hero/HeroSection.svelte +++ b/src/layout/Hero/HeroSection.svelte @@ -17,7 +17,7 @@

Customize your mobile experience through ReVanced
by applying patches to your applications.

-
+
@@ -34,83 +34,59 @@
- diff --git a/src/layout/Hero/SocialButton.svelte b/src/layout/Hero/SocialButton.svelte index a97f6453..46f75c0b 100644 --- a/src/layout/Hero/SocialButton.svelte +++ b/src/layout/Hero/SocialButton.svelte @@ -1,5 +1,6 @@ @@ -7,8 +8,11 @@ {social.name} - diff --git a/src/layout/Navbar/NavButton.svelte b/src/layout/Navbar/NavButton.svelte index 0df5c65c..42d8935e 100644 --- a/src/layout/Navbar/NavButton.svelte +++ b/src/layout/Navbar/NavButton.svelte @@ -52,17 +52,17 @@ color: var(--primary); } } - + &.unclickable { pointer-events: none; } - + :hover { color: var(--text-one); background-color: var(--surface-three); } } - + a { text-decoration: none; user-select: none; @@ -72,15 +72,18 @@ justify-content: center; padding: 10px 16px; } - + span { + display: flex; + justify-content: center; + font-weight: 400; font-size: 0.9rem; letter-spacing: 0.02rem; color: var(--text-four); } - @media (max-width: 767px) { + @media (max-width: 768px) { a { padding: 0.75rem 1.25rem; justify-content: left; diff --git a/src/layout/Navbar/NavHost.svelte b/src/layout/Navbar/NavHost.svelte index eea14338..f1da88e8 100644 --- a/src/layout/Navbar/NavHost.svelte +++ b/src/layout/Navbar/NavHost.svelte @@ -5,9 +5,9 @@ import { expoOut } from 'svelte/easing'; import { createQuery } from '@tanstack/svelte-query'; - import Navigation from './NavButton.svelte'; + import Navigation from '$layout/Navbar/NavButton.svelte'; import Query from '$lib/components/Query.svelte'; - import AnnouncementBanner from '../../routes/announcements/AnnouncementBanner.svelte'; + import AnnouncementBanner from '$layout/Banners/AnnouncementBanner.svelte'; import Cog from 'svelte-material-icons/Cog.svelte'; @@ -15,17 +15,17 @@ import RouterEvents from '$data/RouterEvents'; import { queries } from '$data/api'; - import StatusBanner from './StatusBanner.svelte'; - import SettingsModal from './Modals/SettingsModal.svelte'; - import LoginModal from './Modals/LoginModal.svelte'; - import LoginSuccessfulModal from './Modals/LoginSuccessfulModal.svelte'; - import SessionExpiredModal from './Modals/SessionExpiredModal.svelte'; + import StatusBanner from '$layout/Banners/StatusBanner.svelte'; + import SettingsDialog from '$layout/Dialogs/SettingsDialog.svelte'; + import LoginDialog from '$layout/Dialogs/LoginDialog.svelte'; + import LoginSuccessfulDialog from '$layout/Dialogs/LoginSuccessfulDialog.svelte'; + import SessionExpiredDialog from '$layout/Dialogs/SessionExpiredDialog.svelte'; const ping = createQuery(queries.ping()); const statusUrl = status_url(); let menuOpen = false; - const modals: Record = { + const dialogs: Record = { settings: false, login: false }; @@ -75,13 +75,15 @@ Home Download Patches - - Announcements - + + + Announcements + + Contributors @@ -91,13 +93,32 @@
- + + + + + +
@@ -114,18 +135,18 @@ {/if} - + - + - + - + diff --git a/src/lib/components/Gallery.svelte b/src/lib/components/Gallery.svelte index a7fc8b97..f6e0fb20 100644 --- a/src/lib/components/Gallery.svelte +++ b/src/lib/components/Gallery.svelte @@ -1,5 +1,5 @@ @@ -28,8 +28,8 @@ src={image} alt={`Gallery image ${i + 1}`} loading="lazy" - on:click={() => openModal(image, i)} - on:keydown={(e) => e.key === 'Enter' && openModal(image, i)} + on:click={() => openDialog(image, i)} + on:keydown={(e) => e.key === 'Enter' && openDialog(image, i)} tabindex="0" />
@@ -37,7 +37,7 @@ {#if selectedImage} - + {/if} diff --git a/src/lib/components/Spinner.svelte b/src/lib/components/Spinner.svelte index 286768e7..ec7f601a 100644 --- a/src/lib/components/Spinner.svelte +++ b/src/lib/components/Spinner.svelte @@ -1,30 +1,30 @@ -
+
- diff --git a/src/lib/components/ToolTip.svelte b/src/lib/components/ToolTip.svelte index f7f2a167..eecc645e 100644 --- a/src/lib/components/ToolTip.svelte +++ b/src/lib/components/ToolTip.svelte @@ -2,18 +2,22 @@ import { tooltip } from 'svooltip'; import '../styles/ToolTip.scss'; - export let content: string; + export let content: string | undefined; export let html: boolean = false; -
+{#if content} +
+ +
+{:else} -
+{/if} diff --git a/src/routes/announcements/+page.svelte b/src/routes/announcements/+page.svelte index 0aae8557..fb2905cd 100644 --- a/src/routes/announcements/+page.svelte +++ b/src/routes/announcements/+page.svelte @@ -13,7 +13,7 @@ import Search from '$lib/components/Search.svelte'; import { onMount } from 'svelte'; import type { ResponseAnnouncement } from '$lib/types'; - import { admin_login } from '$lib/stores'; + import { admin_login, read_announcements } from '$lib/stores'; import Button from '$lib/components/Button.svelte'; import moment from 'moment'; import { debounce } from '$util/debounce'; @@ -22,52 +22,61 @@ import ChevronDown from 'svelte-material-icons/ChevronDown.svelte'; import Create from 'svelte-material-icons/Plus.svelte'; - let searchParams: Readable; + let expanded = false; - if (building) searchParams = readable(new URLSearchParams()); - else searchParams = derived(page, ($page) => $page.url.searchParams); + const searchParams: Readable = building + ? readable(new URLSearchParams()) + : derived(page, ($page) => $page.url.searchParams); let searchTerm = $searchParams.get('s') || ''; + let displayedTerm = ''; $: query = createQuery(queries.announcements()); $: tagsQuery = createQuery(queries.announcementTags()); $: selectedTags = $searchParams.getAll('tag'); - let expanded = false; - - function filterAnnouncements( - announcements: Iterable, - search: string, - selectedTags: string[] - ): ResponseAnnouncement[] { - const announcementFilter = createFilter(Array.from(announcements), { - searcherOptions: { - keys: ['title', 'content'] - }, - additionalFilter: (announcement: ResponseAnnouncement, tags: string[]): boolean => { - return ( - tags.length === 0 || - tags.some((tag) => announcement.tags && announcement.tags.includes(tag)) - ); - } - }); - - return announcementFilter(selectedTags, search); - } - - // Make sure we don't have to filter the announcements after every key press - let displayedTerm = ''; const update = () => { displayedTerm = searchTerm; const url = new URL(window.location.href); url.pathname = '/announcements'; - if (searchTerm) url.searchParams.set('s', searchTerm); - else url.searchParams.delete('s'); + searchTerm ? url.searchParams.set('s', searchTerm) : url.searchParams.delete('s'); }; - onMount(update); + const archivedAnnouncements = (announcements: ResponseAnnouncement[]) => + announcements.filter((a) => a.archived_at && moment(a.archived_at).isBefore(moment())); + const activeAnnouncements = (announcements: ResponseAnnouncement[]) => + announcements.filter((a) => !a.archived_at || moment(a.archived_at).isAfter(moment())); + + const filterAnnouncements = ( + announcements: ResponseAnnouncement[], + search: string, + tags: string[] + ): ResponseAnnouncement[] => { + const announcementFilter = createFilter(announcements, { + searcherOptions: { keys: ['title', 'content'] }, + + additionalFilter: (a: ResponseAnnouncement, tags: string[]) => + tags.length === 0 || tags.some((tag) => a.tags?.includes(tag)) + }); + + return announcementFilter(tags, search); + }; + + onMount(() => { + debounce(update)(); + + if ($read_announcements.size === 0) { + query.subscribe((data) => { + read_announcements.update((set) => { + const updated = new Set(set); + data.data?.announcements.forEach((a) => updated.add(a.id)); + return updated; + }); + }); + } + });