├── .npmrc ├── static ├── _redirects ├── logo.png ├── favicon.ico ├── donate │ ├── card-images │ │ ├── Cryptocurrencies.png │ │ ├── GitHub Sponsors.png │ │ ├── GitHub Sponsors.webp │ │ ├── Open Collective.png │ │ ├── Open Collective.webp │ │ ├── Cryptocurrencies.webp │ │ └── fallback.svg │ └── crypto │ │ ├── XMR.svg │ │ ├── LTC.svg │ │ ├── ETH.svg │ │ ├── fallback.svg │ │ └── BTC.svg ├── icons │ └── heart.svg ├── logo.svg ├── socials │ ├── telegram.svg │ ├── twitter.svg │ ├── github.svg │ ├── youtube.svg │ ├── discord.svg │ └── reddit.svg ├── revanced-logo-background.svg └── _headers ├── src ├── routes │ ├── +layout.ts │ ├── announcements │ │ ├── [slug] │ │ │ ├── +layout.ts │ │ │ ├── Author.svelte │ │ │ ├── Title.svelte │ │ │ ├── +page.svelte │ │ │ ├── Date.svelte │ │ │ ├── Content.svelte │ │ │ ├── Tags.svelte │ │ │ ├── Announcement.svelte │ │ │ ├── Attachments.svelte │ │ │ └── AdminButtons.svelte │ │ ├── NewHeader.svelte │ │ ├── TagChip.svelte │ │ ├── TagsHost.svelte │ │ ├── AnnouncementCard.svelte │ │ └── +page.svelte │ ├── patches │ │ ├── PackageMenu.svelte │ │ ├── Package.svelte │ │ ├── PatchItem.svelte │ │ └── +page.svelte │ ├── +error.svelte │ ├── contributors │ │ ├── ContributorPerson.svelte │ │ ├── ContributorSection.svelte │ │ └── +page.svelte │ ├── +layout.svelte │ ├── donate │ │ ├── TeamMember.svelte │ │ └── +page.svelte │ ├── download │ │ └── +page.svelte │ └── +page.svelte ├── util │ ├── fromNow.ts │ ├── formatUtc.ts │ ├── supportsWebP.ts │ ├── isValidUrl.ts │ ├── debounce.ts │ ├── dev.ts │ ├── relativeTime.ts │ ├── filter.ts │ ├── themeEvents.ts │ └── horizontalSlide.js ├── lib │ ├── styles │ │ └── ToolTip.scss │ ├── components │ │ ├── Svg.svelte │ │ ├── Picture.svelte │ │ ├── QRCode.svelte │ │ ├── Spinner.svelte │ │ ├── ToolTip.svelte │ │ ├── Query.svelte │ │ ├── Head.svelte │ │ ├── FilterChip.svelte │ │ ├── Snackbar.svelte │ │ ├── Divider.svelte │ │ ├── Input.svelte │ │ ├── Gallery.svelte │ │ ├── Wave.svelte │ │ ├── Search.svelte │ │ └── Button.svelte │ ├── stores.ts │ ├── types.ts │ └── auth.ts ├── layout │ ├── Footer │ │ ├── FooterSection.svelte │ │ └── FooterHost.svelte │ ├── Hero │ │ ├── HeroImage.svelte │ │ ├── SocialButton.svelte │ │ └── HeroSection.svelte │ ├── Banners │ │ ├── StatusBanner.svelte │ │ ├── AnnouncementBanner.svelte │ │ └── Banner.svelte │ ├── Dialogs │ │ ├── LoginSuccessfulDialog.svelte │ │ ├── DownloadCompatibilityWarningDialog.svelte │ │ ├── SessionExpiredDialog.svelte │ │ ├── MobilePatchesPackagesDialog.svelte │ │ ├── ImageDialog.svelte │ │ ├── ConsentDialog.svelte │ │ ├── EmailDialog.svelte │ │ ├── LoginDialog.svelte │ │ ├── SettingsDialog.svelte │ │ ├── Dialog.svelte │ │ └── CryptoDialog.svelte │ └── Navbar │ │ └── NavButton.svelte ├── app.d.ts ├── app.html ├── data │ ├── RouterEvents.ts │ └── api │ │ └── settings.ts └── app.scss ├── images └── manager.png ├── .gitignore ├── .prettierrc ├── .env.example ├── .github ├── config.yml ├── dependabot.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.yml │ └── bug_report.yml └── workflows │ ├── open_pull_request.yml │ └── deploy.yml ├── .prettierignore ├── .eslintignore ├── tsconfig.json ├── vite.config.js ├── .eslintrc.cjs ├── svelte.config.js ├── package.json ├── assets └── revanced-logo │ └── revanced-logo.svg ├── CONTRIBUTING.md └── README.md /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /static/_redirects: -------------------------------------------------------------------------------- 1 | /patches/* /404.html 200 2 | -------------------------------------------------------------------------------- /src/routes/+layout.ts: -------------------------------------------------------------------------------- 1 | export const prerender = true; 2 | -------------------------------------------------------------------------------- /src/routes/announcements/[slug]/+layout.ts: -------------------------------------------------------------------------------- 1 | export const prerender = false; 2 | -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReVanced/revanced-website/HEAD/static/logo.png -------------------------------------------------------------------------------- /images/manager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReVanced/revanced-website/HEAD/images/manager.png -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReVanced/revanced-website/HEAD/static/favicon.ico -------------------------------------------------------------------------------- /src/util/fromNow.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | 3 | export const fromNow = (timestamp: number) => moment(timestamp).fromNow(true); 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /public 5 | /.svelte-kit 6 | /package 7 | .env 8 | /_docs_src 9 | /static/docs 10 | -------------------------------------------------------------------------------- /src/util/formatUtc.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | 3 | export const formatUTC = (d: any) => d && moment(d).utc().format('YYYY-MM-DDTHH:mm[Z]'); 4 | -------------------------------------------------------------------------------- /static/donate/card-images/Cryptocurrencies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReVanced/revanced-website/HEAD/static/donate/card-images/Cryptocurrencies.png -------------------------------------------------------------------------------- /static/donate/card-images/GitHub Sponsors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReVanced/revanced-website/HEAD/static/donate/card-images/GitHub Sponsors.png -------------------------------------------------------------------------------- /static/donate/card-images/GitHub Sponsors.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReVanced/revanced-website/HEAD/static/donate/card-images/GitHub Sponsors.webp -------------------------------------------------------------------------------- /static/donate/card-images/Open Collective.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReVanced/revanced-website/HEAD/static/donate/card-images/Open Collective.png -------------------------------------------------------------------------------- /static/donate/card-images/Open Collective.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReVanced/revanced-website/HEAD/static/donate/card-images/Open Collective.webp -------------------------------------------------------------------------------- /static/donate/card-images/Cryptocurrencies.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReVanced/revanced-website/HEAD/static/donate/card-images/Cryptocurrencies.webp -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"] 7 | } 8 | -------------------------------------------------------------------------------- /src/util/supportsWebP.ts: -------------------------------------------------------------------------------- 1 | export function supportsWebP() { 2 | return document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') === 0; 3 | } 4 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | RV_API_URL=https://api.revanced.app 2 | RV_STATUS_URL=https://status.revanced.app 3 | RV_EMAIL=contact@revanced.app 4 | RV_GOOGLE_TAG_MANAGER_ID= 5 | RV_DMCA_GUID= -------------------------------------------------------------------------------- /src/util/isValidUrl.ts: -------------------------------------------------------------------------------- 1 | export const isValidUrl = (url: string) => { 2 | try { 3 | new URL(url); 4 | return true; 5 | } catch (err) { 6 | return false; 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | firstPRMergeComment: > 2 | Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) to receive a role for your contribution. -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | labels: [] 5 | directory: / 6 | target-branch: dev 7 | schedule: 8 | interval: monthly 9 | -------------------------------------------------------------------------------- /src/util/debounce.ts: -------------------------------------------------------------------------------- 1 | export const debounce = (f: (...args: T) => void) => { 2 | let timeout: number; 3 | return (...args: T) => { 4 | clearTimeout(timeout); 5 | timeout = setTimeout(() => f(...args), 350); 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /src/util/dev.ts: -------------------------------------------------------------------------------- 1 | import { dev } from '$app/environment'; 2 | 3 | // console.log, but only if in dev environment. 4 | export function dev_log(part: string, ...args: any[]) { 5 | if (dev) { 6 | console.log(`[${part}]:`, ...args); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /public 5 | /.svelte-kit 6 | /package 7 | .env 8 | .env.* 9 | !.env.example 10 | 11 | # Ignore files for PNPM, NPM and YARN 12 | pnpm-lock.yaml 13 | package-lock.json 14 | yarn.lock 15 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /public 4 | /images 5 | /build 6 | /.svelte-kit 7 | /package 8 | .env 9 | .env.* 10 | !.env.example 11 | 12 | # Ignore files for PNPM, NPM and YARN 13 | pnpm-lock.yaml 14 | package-lock.json 15 | yarn.lock 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 🗨 Discussions 4 | url: https://github.com/revanced/revanced-suggestions/discussions 5 | about: Have something unspecific to ReVanced Website in mind? Search for or start a new discussion! -------------------------------------------------------------------------------- /src/util/relativeTime.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | 3 | export const relativeTime = (date: string, withinDays: number = 7) => { 4 | return moment().diff(moment(date), 'days') <= withinDays 5 | ? moment(date).fromNow() 6 | : moment(date).format('on MMMM D, YYYY [at] h:mm A'); 7 | }; 8 | -------------------------------------------------------------------------------- /static/icons/heart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/lib/styles/ToolTip.scss: -------------------------------------------------------------------------------- 1 | @use 'svooltip/styles.scss' as SvoolTip; 2 | 3 | .svooltip a { 4 | text-decoration: none; 5 | color: var(--text-four); 6 | pointer-events: all; 7 | 8 | &:hover { 9 | text-decoration: underline var(--secondary); 10 | color: var(--secondary); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/routes/announcements/NewHeader.svelte: -------------------------------------------------------------------------------- 1 | NEW 2 | 3 | 15 | -------------------------------------------------------------------------------- /static/donate/crypto/XMR.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "types": ["node"], 13 | "lib": ["ESNext", "DOM"] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /static/donate/crypto/LTC.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/socials/telegram.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/socials/twitter.svg: -------------------------------------------------------------------------------- 1 | 3 | 6 | -------------------------------------------------------------------------------- /static/donate/crypto/ETH.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/lib/components/Svg.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/lib/components/Picture.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | {#each Object.entries(data.sources) as [format, srcset]} 10 | 11 | {/each} 12 | 13 | 14 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import { imagetools } from 'vite-imagetools'; 3 | 4 | import path from 'path'; 5 | 6 | /** @type {import('vite').UserConfig} */ 7 | const config = { 8 | plugins: [sveltekit(), imagetools()], 9 | resolve: { 10 | alias: { 11 | $images: path.resolve('./images'), 12 | $data: path.resolve('./src/data'), 13 | $layout: path.resolve('./src/layout'), 14 | $util: path.resolve('./src/util') 15 | } 16 | } 17 | }; 18 | 19 | export default config; 20 | -------------------------------------------------------------------------------- /src/lib/components/QRCode.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 26 | -------------------------------------------------------------------------------- /static/donate/crypto/fallback.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/donate/card-images/fallback.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/revanced-logo-background.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/layout/Footer/FooterSection.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | 7 | {title} 8 | 9 |
    10 | 11 |
12 |
13 | 14 | 36 | -------------------------------------------------------------------------------- /static/donate/crypto/BTC.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/layout/Hero/HeroImage.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | 8 |
9 | 10 | 24 | -------------------------------------------------------------------------------- /src/lib/components/Spinner.svelte: -------------------------------------------------------------------------------- 1 |
2 | 3 | 31 | -------------------------------------------------------------------------------- /src/layout/Banners/StatusBanner.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 19 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:svelte/recommended', 7 | 'prettier' 8 | ], 9 | parser: '@typescript-eslint/parser', 10 | plugins: ['@typescript-eslint'], 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2020, 14 | extraFileExtensions: ['.svelte'] 15 | }, 16 | env: { 17 | browser: true, 18 | es2017: true, 19 | node: true 20 | }, 21 | overrides: [ 22 | { 23 | files: ['*.svelte'], 24 | parser: 'svelte-eslint-parser', 25 | parserOptions: { 26 | parser: '@typescript-eslint/parser' 27 | } 28 | } 29 | ] 30 | }; 31 | -------------------------------------------------------------------------------- /static/_headers: -------------------------------------------------------------------------------- 1 | /* 2 | X-Frame-Options: DENY 3 | X-XSS-Protection: 1; mode=block 4 | Content-Security-Policy: default-src 'self' *.revanced.app; img-src 'self' *.revanced.app avatars.githubusercontent.com; font-src 'self' *.revanced.app fonts.googleapis.com fonts.gstatic.com; style-src 'self' 'unsafe-inline' *.revanced.app fonts.googleapis.com fonts.gstatic.com; script-src 'self' 'unsafe-inline'; 5 | X-Content-Type-Options: nosniff 6 | Referrer-Policy: no-referrer 7 | Cache-Control: public, max-age=604800, stale-while-revalidate=86400, stale-if-error=259200 8 | 9 | /_app/immutable 10 | Cache-Control: public, max-age=2419200, stale-while-revalidate=345600, stale-if-error=1036800 11 | 12 | 13 | -------------------------------------------------------------------------------- /static/socials/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/workflows/open_pull_request.yml: -------------------------------------------------------------------------------- 1 | name: Open a PR to main 2 | 3 | on: 4 | push: 5 | branches: 6 | - dev 7 | workflow_dispatch: 8 | 9 | env: 10 | MESSAGE: Merge branch `${{ github.head_ref || github.ref_name }}` to `main` 11 | 12 | jobs: 13 | pull-request: 14 | name: Open pull request 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | 20 | - name: Open pull request 21 | uses: repo-sync/pull-request@v2 22 | with: 23 | destination_branch: 'main' 24 | pr_title: 'chore: ${{ env.MESSAGE }}' 25 | pr_body: 'This pull request will ${{ env.MESSAGE }}.' 26 | pr_draft: true 27 | -------------------------------------------------------------------------------- /src/routes/patches/PackageMenu.svelte: -------------------------------------------------------------------------------- 1 |
2 |
Packages
3 |
4 | 5 | 6 | 7 |
8 | 9 | 39 | -------------------------------------------------------------------------------- /src/lib/components/ToolTip.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | {#if content} 10 |
16 | 17 |
18 | {:else} 19 | 20 | {/if} 21 | 22 | 34 | -------------------------------------------------------------------------------- /src/lib/components/Query.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 | {#if !$isRestoring} 22 | {#if $query.isSuccess} 23 | 24 | {:else if $query.isError} 25 | 26 | {/if} 27 | {/if} 28 | -------------------------------------------------------------------------------- /src/app.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | // See https://kit.svelte.dev/docs/types#app 4 | // for information about these interfaces 5 | // and what to do when importing types 6 | declare namespace App { 7 | // interface Locals {} 8 | // interface Platform {} 9 | // interface Session {} 10 | // interface Stuff {} 11 | } 12 | 13 | declare module '*&as=picture' { 14 | /** 15 | * actual types 16 | * taken from https://github.com/JonasKruckenberg/imagetools/issues/160#issuecomment-1009292026 17 | * - code https://github.com/JonasKruckenberg/imagetools/blob/main/packages/core/src/output-formats.ts 18 | * - docs https://github.com/JonasKruckenberg/imagetools/blob/main/docs/guide/getting-started.md#metadata 19 | */ 20 | const out; 21 | export default out; 22 | } 23 | -------------------------------------------------------------------------------- /static/socials/youtube.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-static'; 2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | preprocess: [vitePreprocess()], 7 | 8 | kit: { 9 | // adapter-static has vercel detection, but that does not let you set a custom 404 page easily. 10 | // Instead, we have to use a wrapper that generates a vercel config if on vercel... 11 | adapter: adapter({ 12 | pages: 'public', 13 | fallback: '404.html' 14 | }), 15 | env: { 16 | publicPrefix: 'RV' 17 | }, 18 | alias: { 19 | '$data/*': './src/data/*', 20 | $lib: './src/lib', 21 | '$lib/*': './src/lib/*', 22 | '$layout/*': './src/layout/*', 23 | '$images/*': './images/*', 24 | '$util/*': './src/util/*' 25 | } 26 | } 27 | }; 28 | 29 | export default config; 30 | -------------------------------------------------------------------------------- /src/routes/+error.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 |
12 |

{status}

13 | {#if status == 404} 14 |

This page received a cease and desist letter from a multi-billion dollar tech company.

15 |
16 | 17 | {:else} 18 |

19 | {$page.error?.message} 20 |

21 | {/if} 22 |
23 | 24 | 37 | -------------------------------------------------------------------------------- /src/routes/announcements/[slug]/Author.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | {#if (isEditing || isCreating) && !isPreviewing} 12 | · 13 | 18 | {:else if displayAuthor} 19 | · 20 | 21 | {displayAuthor} 22 | 23 | {/if} 24 | 25 | 39 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | %sveltekit.head% 22 | 23 | 24 | 25 |
%sveltekit.body%
26 | 27 | 28 | -------------------------------------------------------------------------------- /static/socials/discord.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/layout/Dialogs/LoginSuccessfulDialog.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | Successfully logged in! 11 |
12 | This session will expire in 13 | {$admin_login.logged_in ? fromNow($admin_login.expires) : '...'} 14 |
15 | 16 | 17 | 18 |
19 | 20 | 29 | -------------------------------------------------------------------------------- /src/layout/Hero/SocialButton.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | {social.name} 9 | 10 | 11 | 41 | -------------------------------------------------------------------------------- /src/lib/components/Head.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | {title} 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | {#if schemas} 28 | {#each schemas as schema} 29 | 30 | {/each} 31 | {/if} 32 | 33 | -------------------------------------------------------------------------------- /src/routes/announcements/[slug]/Title.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | {#if (isEditing || isCreating) && !isPreviewing} 12 | 13 | {:else if displayTitle} 14 |

15 | {displayTitle} 16 |

17 | {/if} 18 | 19 | 42 | -------------------------------------------------------------------------------- /src/layout/Dialogs/DownloadCompatibilityWarningDialog.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | Warning 16 | {warning} Do you still want to download? 17 | 18 | 19 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /static/socials/reddit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/util/filter.ts: -------------------------------------------------------------------------------- 1 | import Fuse from 'fuse.js'; 2 | 3 | type SearcherOptions = { 4 | keys: string[]; 5 | shouldSort?: boolean; 6 | threshold?: number; 7 | }; 8 | 9 | type FilterOptions = { 10 | searcherOptions: SearcherOptions; 11 | additionalFilter?: (item: T, context: C) => boolean; 12 | }; 13 | 14 | function createFilter(items: T[], options: FilterOptions) { 15 | const { searcherOptions, additionalFilter } = options; 16 | 17 | const searcher = new Fuse(items, { 18 | keys: searcherOptions.keys, 19 | shouldSort: searcherOptions.shouldSort ?? true, 20 | threshold: searcherOptions.threshold ?? 0.3 21 | }); 22 | 23 | return (context: C, search?: string): T[] => { 24 | if (!search) { 25 | return additionalFilter ? items.filter((item) => additionalFilter(item, context)) : items; 26 | } 27 | 28 | const results = searcher.search(search).map(({ item }) => item); 29 | 30 | return additionalFilter ? results.filter((item) => additionalFilter(item, context)) : results; 31 | }; 32 | } 33 | 34 | export default createFilter; 35 | -------------------------------------------------------------------------------- /src/lib/components/FilterChip.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | 20 | 43 | -------------------------------------------------------------------------------- /src/lib/components/Snackbar.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | {#if open} 16 |
17 | 18 |
19 | {/if} 20 | 21 | 45 | -------------------------------------------------------------------------------- /src/layout/Dialogs/SessionExpiredDialog.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | Expired session 20 |
21 | This session has expired, log in again to renew or lose all access to administrative power. 22 |
23 | 24 | 25 | 28 | 29 |
30 | 31 | 36 | -------------------------------------------------------------------------------- /src/layout/Dialogs/MobilePatchesPackagesDialog.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | Packages 13 |
14 | 15 | (dialogOpen = !dialogOpen)} 17 | on:keypress={() => (dialogOpen = !dialogOpen)} 18 | > 19 | 20 | 21 | {#each data.packages as pkg} 22 | 23 | (dialogOpen = !dialogOpen)} 25 | on:keypress={() => (dialogOpen = !dialogOpen)} 26 | > 27 | 28 | 29 | {/each} 30 |
31 |
32 | 33 | 41 | -------------------------------------------------------------------------------- /src/lib/components/Divider.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 21 | 22 | 31 | -------------------------------------------------------------------------------- /src/data/RouterEvents.ts: -------------------------------------------------------------------------------- 1 | import { navigating, page } from '$app/stores'; 2 | import { derived, type Readable } from 'svelte/store'; 3 | 4 | export interface RouterEvent { 5 | // URL of the current page or the page we are navigating to. 6 | target_url: URL; 7 | // Are we navigating? 8 | navigating: boolean; 9 | } 10 | 11 | function makeStore(): Readable { 12 | // This stuff will run both client side and server side. 13 | 14 | if (typeof location === 'undefined') { 15 | // `location` does not exist on the server. 16 | // Return a derived store based on `page` for SSR. 17 | // Server will never navigate so this is fine. 18 | return derived(page, ($page) => { 19 | return { navigating: false, target_url: $page.url }; 20 | }); 21 | } 22 | 23 | // On client. 24 | let current = new URL(location.href); 25 | 26 | // Return store that responds to navigation events. 27 | // The `navigating` store immediately "pushes" `null`. 28 | // This in turn causes this derived store to immediately "push" the current URL. 29 | return derived(navigating, ($nav) => { 30 | let navigating = false; 31 | // $nav is null when navigation finishes. 32 | if ($nav != null && $nav.to != null) { 33 | current = $nav.to.url; 34 | navigating = true; 35 | } 36 | 37 | return { navigating, target_url: current }; 38 | }); 39 | } 40 | 41 | // Do not subscribe to it outside of components! 42 | export default makeStore(); 43 | -------------------------------------------------------------------------------- /src/routes/contributors/ContributorPerson.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 |
{name}
11 |
12 | 13 | 66 | -------------------------------------------------------------------------------- /src/routes/announcements/TagChip.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | 21 | 65 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | - dev 9 | pull_request: 10 | branches: 11 | - dev 12 | jobs: 13 | deploy: 14 | name: Deploy 15 | runs-on: ubuntu-latest 16 | permissions: 17 | contents: read 18 | deployments: write 19 | pull-requests: write 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v4 23 | 24 | - name: Install Node.js 25 | uses: actions/setup-node@v4 26 | with: 27 | cache: npm 28 | 29 | - name: Install dependencies 30 | run: npm i 31 | 32 | - name: Build 33 | env: 34 | RV_API_URL: ${{ vars.RV_API_URL }} 35 | RV_GOOGLE_TAG_MANAGER_ID: ${{ vars.RV_GOOGLE_TAG_MANAGER_ID }} 36 | RV_DMCA_GUID: ${{ vars.RV_DMCA_GUID }} 37 | RV_STATUS_URL: ${{ vars.RV_STATUS_URL }} 38 | RV_EMAIL: ${{ vars.RV_EMAIL }} 39 | run: npm run build 40 | 41 | - name: Deploy 42 | id: deploy 43 | uses: cloudflare/wrangler-action@v3 44 | with: 45 | apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} 46 | accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} 47 | command: pages deploy public --project-name=revanced-website 48 | 49 | - name: Comment deployment URL 50 | if: ${{ github.event_name == 'pull_request' }} 51 | uses: thollander/actions-comment-pull-request@v2 52 | with: 53 | message: Deployed at ${{ steps.deploy.outputs.deployment-url }}. 54 | -------------------------------------------------------------------------------- /src/lib/components/Input.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 | 29 | 30 |
31 | 32 | 66 | -------------------------------------------------------------------------------- /src/routes/announcements/[slug]/+page.svelte: -------------------------------------------------------------------------------- 1 | 39 | 40 |
41 | {#if query} 42 | 43 | 44 | 45 | {:else} 46 | 47 | {/if} 48 |
-------------------------------------------------------------------------------- /src/util/themeEvents.ts: -------------------------------------------------------------------------------- 1 | import { DateTriggerEvent, DAY_IN_MINUTES } from 'datetrigger'; 2 | 3 | const changeHue = (n: number) => document.documentElement.style.setProperty('--hue', n.toString()); 4 | 5 | /** 6 | * Get the date of when easter should happen based off a given year 7 | * @param Y The year to get Easter's date from 8 | * @returns When Easter should happen 9 | */ 10 | function getEaster(Y: number): Date { 11 | const a = Y % 19, 12 | b = Math.floor(Y / 100), 13 | c = Y % 100, 14 | d = Math.floor(b / 4), 15 | e = b % 4, 16 | f = Math.floor((b + 8) / 25), 17 | g = Math.floor((b - f + 1) / 3), 18 | h = (19 * a + b - d - g + 15) % 30, 19 | i = Math.floor(c / 4), 20 | k = c % 4, 21 | L = (32 + 2 * e + 2 * i - h - k) % 7, 22 | m = Math.floor((a + 11 * h + 22 * L) / 451), 23 | month = Math.floor((h + L - 7 * m + 114) / 31), 24 | day = ((h + L - 7 * m + 114) % 31) + 1; 25 | return new Date(Y, month - 1, day); 26 | } 27 | 28 | const currentYear = new Date().getFullYear(); 29 | 30 | export const events: DateTriggerEvent[] = [ 31 | // New Year. 32 | new DateTriggerEvent(new Date(currentYear, 0, 1), DAY_IN_MINUTES, () => { 33 | changeHue(240); 34 | }), 35 | // Christmas. 36 | new DateTriggerEvent(new Date(currentYear, 11, 25), DAY_IN_MINUTES, () => { 37 | changeHue(120); 38 | }), 39 | // Valentine's day. 40 | new DateTriggerEvent(new Date(currentYear, 1, 14), DAY_IN_MINUTES, () => { 41 | changeHue(300); 42 | }), 43 | // Halloween. 44 | new DateTriggerEvent(new Date(currentYear, 9, 31), DAY_IN_MINUTES, () => { 45 | changeHue(0); 46 | }), 47 | // Easter. 48 | new DateTriggerEvent(getEaster(currentYear), DAY_IN_MINUTES, () => { 49 | changeHue(100); 50 | }), 51 | // April Fools. 52 | new DateTriggerEvent(new Date(currentYear, 3, 1), DAY_IN_MINUTES, () => { 53 | changeHue(69); 54 | }) 55 | ]; 56 | -------------------------------------------------------------------------------- /src/util/horizontalSlide.js: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import { cubicOut } from 'svelte/easing'; 3 | 4 | // stolen from https://svelte.dev/repl/6d5239f09b0b4dc6aafeb70606a0fe94?version=3.46.4 5 | // please add this svelte thanks ily <3 6 | export function horizontalSlide( 7 | node, 8 | { delay = 0, duration = 400, easing = cubicOut, direction = 'block' } = {} 9 | ) { 10 | const style = getComputedStyle(node); 11 | const opacity = +style.opacity; 12 | const capitalized_logical_property = `${direction[0].toUpperCase()}${direction.slice(1)}`; 13 | const size_value = parseFloat(style[`${direction}Size`]); 14 | const padding_start_value = parseFloat(style[`padding${capitalized_logical_property}Start`]); 15 | const padding_end_value = parseFloat(style[`padding${capitalized_logical_property}End`]); 16 | const margin_start_value = parseFloat(style[`margin${capitalized_logical_property}Start`]); 17 | const margin_end_value = parseFloat(style[`margin${capitalized_logical_property}End`]); 18 | const border_width_start_value = parseFloat( 19 | style[`border${capitalized_logical_property}StartWidth`] 20 | ); 21 | const border_width_end_value = parseFloat(style[`border${capitalized_logical_property}EndWidth`]); 22 | 23 | return { 24 | delay, 25 | duration, 26 | easing, 27 | css: (t) => 28 | 'overflow: hidden;' + 29 | `opacity: ${Math.min(t * 20, 1) * opacity};` + 30 | `${direction}-size: ${t * size_value}px;` + 31 | `padding-${direction}-start: ${t * padding_start_value}px;` + 32 | `padding-${direction}-end: ${t * padding_end_value}px;` + 33 | `margin-${direction}-start: ${t * margin_start_value}px;` + 34 | `margin-${direction}-end: ${t * margin_end_value}px;` + 35 | `border-${direction}-start-width: ${t * border_width_start_value}px;` + 36 | `border-${direction}-start-width: ${t * border_width_end_value}px;` 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "revanced-website", 3 | "version": "0.0.1", 4 | "scripts": { 5 | "dev": "vite dev", 6 | "build": "vite build", 7 | "package": "svelte-kit package", 8 | "preview": "sirv ./public --no-clear", 9 | "vite:preview": "vite preview", 10 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 11 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 12 | "lint": "prettier --check --plugin-search-dir=. . && eslint .", 13 | "format": "prettier --write --plugin-search-dir=. .", 14 | "postbuild": "npx svelte-sitemap -d https://revanced.app -r true -c daily -o public " 15 | }, 16 | "devDependencies": { 17 | "@sveltejs/adapter-static": "3.0.6", 18 | "@sveltejs/kit": "2.9.0", 19 | "@sveltejs/vite-plugin-svelte": "3.1.1", 20 | "@typescript-eslint/eslint-plugin": "8.16.0", 21 | "@typescript-eslint/parser": "8.16.0", 22 | "eslint": "9.16.0", 23 | "eslint-config-prettier": "^9.1.0", 24 | "eslint-plugin-svelte": "2.46.1", 25 | "fuse.js": "^7.0.0", 26 | "imagetools-core": "7.0.2", 27 | "prettier": "3.4.1", 28 | "prettier-plugin-svelte": "3.3.2", 29 | "qrious": "^4.0.2", 30 | "sass": "1.81.0", 31 | "sirv-cli": "3.0.0", 32 | "svelte": "4.2.18", 33 | "svelte-check": "4.1.0", 34 | "svelte-meta-tags": "3.1.2", 35 | "svelte-sitemap": "^2.6.0", 36 | "svooltip": "^0.8.2", 37 | "tslib": "2.8.1", 38 | "typescript": "5.7.2", 39 | "vite": "5.3.3", 40 | "vite-imagetools": "7.0.4" 41 | }, 42 | "type": "module", 43 | "dependencies": { 44 | "@tanstack/query-core": "^4.36.1", 45 | "@tanstack/query-persist-client-core": "^4.36.1", 46 | "@tanstack/query-sync-storage-persister": "^4.36.1", 47 | "@tanstack/svelte-query": "^4.36.1", 48 | "datetrigger": "^1.1.1", 49 | "moment": "^2.30.1", 50 | "svelte-material-icons": "^3.0.5" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/data/api/settings.ts: -------------------------------------------------------------------------------- 1 | import { browser } from '$app/environment'; 2 | import { RV_API_URL, RV_EMAIL, RV_STATUS_URL } from '$env/static/public'; 3 | 4 | export const default_api_url = RV_API_URL; 5 | export const default_status_url = RV_STATUS_URL; 6 | export const default_email = RV_EMAIL; 7 | 8 | const URL_KEY = 'revanced_api_url'; 9 | const STATUS_KEY = 'revanced_status_url'; 10 | const EMAIL_KEY = 'revanced_email'; 11 | 12 | export const API_VERSION = 'v4'; 13 | 14 | // Get base URL 15 | export function api_base_url(): string { 16 | if (browser) { 17 | const apiUrl = localStorage.getItem(URL_KEY) || default_api_url; 18 | 19 | return apiUrl; 20 | } 21 | 22 | return default_api_url; 23 | } 24 | 25 | export function status_url(): string { 26 | if (browser) { 27 | return localStorage.getItem(STATUS_KEY) || default_status_url; 28 | } 29 | 30 | return default_status_url; 31 | } 32 | 33 | export function email(): string { 34 | if (browser) { 35 | return localStorage.getItem(EMAIL_KEY) || default_email; 36 | } 37 | 38 | return default_email; 39 | } 40 | 41 | // (re)set base URL. 42 | export function set_api_base_url(url?: string) { 43 | if (!url) { 44 | localStorage.removeItem(URL_KEY); 45 | } else { 46 | localStorage.setItem(URL_KEY, url); 47 | } 48 | set_about_info(api_base_url()); 49 | } 50 | 51 | export function set_about_info(apiUrl: string) { 52 | if (!localStorage.getItem(STATUS_KEY) || !localStorage.getItem(EMAIL_KEY)) { 53 | fetch(`${apiUrl}/v4/about`) 54 | .then((response) => (response.ok ? response.json() : null)) 55 | .then((data) => { 56 | if (data?.status) { 57 | localStorage.setItem(STATUS_KEY, data.status); 58 | localStorage.setItem(EMAIL_KEY, data.contact.email); 59 | console.log('Status is now:', localStorage.getItem(STATUS_KEY)); 60 | console.log('Email is now:', localStorage.getItem(EMAIL_KEY)); 61 | } 62 | }); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/layout/Banners/AnnouncementBanner.svelte: -------------------------------------------------------------------------------- 1 | 51 | 52 | {#if latestUnreadAnnouncement} 53 | 61 | {/if} 62 | -------------------------------------------------------------------------------- /src/layout/Dialogs/ImageDialog.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 |
26 | 27 | 28 |
29 |
30 | 31 | 77 | -------------------------------------------------------------------------------- /src/routes/patches/Package.svelte: -------------------------------------------------------------------------------- 1 | 26 | 27 | 28 | 29 |
34 | {name} 35 |
36 | 37 | 73 | -------------------------------------------------------------------------------- /src/lib/components/Gallery.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | 38 | 39 | {#if selectedImage} 40 | 41 | {/if} 42 | 43 | 83 | -------------------------------------------------------------------------------- /src/layout/Dialogs/ConsentDialog.svelte: -------------------------------------------------------------------------------- 1 | 48 | 49 | 50 | It's your choice 51 | 52 | We use analytics to improve your experience on this site. By clicking "Allow", you allow us to 53 | collect anonymous data about your visit. 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/lib/components/Wave.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | 13 | 14 | 15 | 65 | -------------------------------------------------------------------------------- /src/routes/announcements/[slug]/Date.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 | {#if (isEditing || isCreating) && !isPreviewing} 29 | 30 | 31 | {#if archivedAtInput} 32 | 33 | 34 | {/if} 35 | 36 | {:else if displayCreatedAt} 37 | 38 | {relativeTime(displayCreatedAt)} 39 | 40 | {#if displayArchivedAt} 41 | 42 | {relativeTime(displayArchivedAt)} 43 | {/if} 44 | 45 | {/if} 46 | 47 | 72 | -------------------------------------------------------------------------------- /src/layout/Dialogs/EmailDialog.svelte: -------------------------------------------------------------------------------- 1 | 32 | 33 | 34 | Abuse notice 35 | 36 |

37 | This E-Mail address is not for support, help, bug reports or feature requests. It must 38 | have a subject and body and have the keyword 'Reficio' 39 | in either, otherwise your mail will be 40 | ignored. 41 |

42 |
43 | {#if enableInputSeconds == 0} 44 | Enter the keyword, then click "Send". 45 |
46 |
47 | 48 | {:else} 49 | Read above and wait {enableInputSeconds} seconds. 50 | {/if} 51 |
52 | 53 | {#if keyword.toLowerCase() === 'reficio'} 54 | 55 | {/if} 56 | 57 | 58 |
59 | 60 | 67 | -------------------------------------------------------------------------------- /src/lib/stores.ts: -------------------------------------------------------------------------------- 1 | import { readable, writable } from 'svelte/store'; 2 | import { is_logged_in, get_access_token } from './auth'; 3 | import { browser } from '$app/environment'; 4 | 5 | type AdminLoginInfo = 6 | | { 7 | logged_in: true; 8 | expires: number; 9 | logged_in_previously: boolean; 10 | } 11 | | { 12 | logged_in: false; 13 | expires: undefined; 14 | logged_in_previously: boolean; 15 | }; 16 | 17 | const admin_login_info = (): AdminLoginInfo => { 18 | if (is_logged_in()) 19 | return { 20 | logged_in: true, 21 | expires: get_access_token()!.expires, 22 | logged_in_previously: !!get_access_token()?.token 23 | }; 24 | else 25 | return { 26 | logged_in: false, 27 | expires: undefined, 28 | logged_in_previously: !!get_access_token()?.token 29 | }; 30 | }; 31 | 32 | export const admin_login = readable(admin_login_info(), (set) => { 33 | const checkLoginStatus = () => set(admin_login_info()); 34 | 35 | checkLoginStatus(); 36 | 37 | const interval = setInterval(checkLoginStatus, 100); 38 | return () => clearInterval(interval); 39 | }); 40 | 41 | export const read_announcements = writable>(new Set(), (set) => { 42 | if (!browser) return; 43 | 44 | const key = 'read_announcements'; 45 | const data = localStorage.getItem(key); 46 | const parsedArray = data ? JSON.parse(data) : []; 47 | const currentState = new Set(parsedArray); 48 | 49 | const updateStoreState = () => { 50 | set(currentState); 51 | }; 52 | 53 | const handleLocalStorageUpdate = (e: StorageEvent) => { 54 | if (e.key === key) updateStoreState(); 55 | }; 56 | 57 | window.addEventListener('storage', handleLocalStorageUpdate); 58 | updateStoreState(); 59 | 60 | return () => { 61 | window.removeEventListener('storage', handleLocalStorageUpdate); 62 | localStorage.setItem(key, JSON.stringify(Array.from(currentState))); 63 | }; 64 | }); 65 | 66 | read_announcements.subscribe((value) => { 67 | if (!browser) return; 68 | 69 | localStorage.setItem('read_announcements', JSON.stringify(Array.from(value))); 70 | }); 71 | 72 | export const passed_login_with_creds = writable(false); // will only change when the user INPUTS the credentials, not if the session is just valid 73 | 74 | export const allowAnalytics = writable(false); 75 | -------------------------------------------------------------------------------- /src/lib/types.ts: -------------------------------------------------------------------------------- 1 | export type ResponseAnnouncement = { 2 | archived_at?: string; 3 | attachments?: string[]; 4 | author?: string; 5 | tags?: string[]; 6 | content?: string; 7 | created_at: string; 8 | id: number; 9 | level?: number; 10 | title: string; 11 | }; 12 | 13 | export type Announcement = Omit; 14 | 15 | export type Tags = { name: string }[]; 16 | 17 | export interface Contributor { 18 | name: string; 19 | avatar_url: string; 20 | url: string; 21 | contributions: number; 22 | } 23 | 24 | export interface Contributable { 25 | name: string; 26 | url: string; 27 | contributors: Contributor[]; 28 | } 29 | 30 | export interface Patch { 31 | name: string; 32 | description: string; 33 | use: boolean; 34 | compatiblePackages: CompatiblePackage[] | null; 35 | options: PatchOption[]; 36 | } 37 | 38 | export interface CompatiblePackage { 39 | name: string; 40 | versions: string[] | null; 41 | } 42 | 43 | export interface PatchOption { 44 | key: string; 45 | title: string | null; 46 | description: string; 47 | required: boolean; 48 | type: string; 49 | default: any | null; 50 | values: any[] | null; 51 | } 52 | 53 | export interface Release { 54 | version: string; 55 | created_at: string; 56 | description: string; 57 | download_url: string; 58 | } 59 | 60 | export interface TeamMember { 61 | name: string; 62 | avatar_url: string; 63 | url: string; 64 | bio?: string; 65 | gpg_key: GpgKey; 66 | } 67 | 68 | export interface GpgKey { 69 | id: string; 70 | url: string; 71 | } 72 | 73 | export interface CryptoWallet { 74 | network: string; 75 | currency_code: string; 76 | address: string; 77 | preferred: boolean; 78 | } 79 | 80 | export interface DonationPlatform { 81 | name: string; 82 | url: string; 83 | preferred: boolean; 84 | } 85 | 86 | export interface Social { 87 | name: string; 88 | url: string; 89 | preferred: boolean; 90 | } 91 | 92 | interface Donations { 93 | wallets: CryptoWallet[]; 94 | links: DonationPlatform[]; 95 | } 96 | 97 | interface Contact { 98 | email: string; 99 | } 100 | 101 | export interface About { 102 | name: string; 103 | about: string; 104 | contact: Contact; 105 | socials: Social[]; 106 | donations: Donations; 107 | } 108 | -------------------------------------------------------------------------------- /src/layout/Navbar/NavButton.svelte: -------------------------------------------------------------------------------- 1 | 29 | 30 |
  • 34 | 35 | 36 | 37 |
  • 38 | 39 | 97 | -------------------------------------------------------------------------------- /src/routes/announcements/TagsHost.svelte: -------------------------------------------------------------------------------- 1 | 41 | 42 |
    43 | {#each displayedTags as tag} 44 | handleClick(tag)} 48 | {clickable} 49 | /> 50 | {/each} 51 | 52 | {#if expandable && tags.length > 1} 53 |
  • 54 | 62 |
  • 63 | {/if} 64 |
    65 | 66 | 86 | -------------------------------------------------------------------------------- /src/layout/Dialogs/LoginDialog.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 | 29 |
    30 |

    Login

    31 |

    This login is reserved for site administrators. Go back!

    32 | {#if wrong_credentials} 33 |

    Username or password do not match. Try again.

    34 | {/if} 35 |
    36 |
    37 | 38 | event.key === 'Enter' && loginForm.requestSubmit()} 42 | required 43 | /> 44 |
    45 |
    46 |
    47 | 48 | 49 | 50 | 51 | 52 |
    53 | 54 | 86 | -------------------------------------------------------------------------------- /src/lib/components/Search.svelte: -------------------------------------------------------------------------------- 1 | 23 | 24 |
    25 | 28 | {#if searchTerm} 29 |
    35 | 36 |
    37 | {/if} 38 | 45 |
    46 | 47 | 103 | -------------------------------------------------------------------------------- /src/routes/announcements/[slug]/Content.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | {#if (isEditing || isCreating) && !isPreviewing} 13 |