mirror of
https://github.com/ReVanced/revanced-website.git
synced 2026-01-23 11:01:03 +00:00
feat: logo pages
This commit is contained in:
@@ -31,11 +31,17 @@ body {
|
||||
margin-inline: auto;
|
||||
width: min(90%, 95rem);
|
||||
margin-top: 4rem;
|
||||
padding-bottom: 2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.wrapper {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
--white: #fff;
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
export let maxWidth = false;
|
||||
export let icon = '';
|
||||
export let target = '';
|
||||
export let unclickable = false;
|
||||
</script>
|
||||
|
||||
<a {href} {target}>
|
||||
<a {href} {target} on:click class:unclickable>
|
||||
<div class={type} style="width: {maxWidth ? '100%' : 'max-content'}">
|
||||
{#if icon}
|
||||
<img src="../icons/{icon}.svg" alt={icon} />
|
||||
@@ -22,6 +23,10 @@
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.unclickable {
|
||||
pointer-events: none;
|
||||
cursor:not-allowed;
|
||||
}
|
||||
div,
|
||||
.button-secondary {
|
||||
min-width: max-content;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script lang="ts">
|
||||
export let name: string;
|
||||
export let logo: string;
|
||||
export let file: string;
|
||||
export let filename: string;
|
||||
export let id: string;
|
||||
export let selected: Array<string>;
|
||||
export let clicked = false;
|
||||
|
||||
const handleClick = () => {
|
||||
clicked = !clicked;
|
||||
@@ -15,16 +16,16 @@
|
||||
// the Updater
|
||||
selected = selected;
|
||||
}
|
||||
console.log(selected)
|
||||
};
|
||||
|
||||
let clicked = false;
|
||||
</script>
|
||||
|
||||
<div on:click={handleClick} class:clicked>
|
||||
<img src={logo} alt={file} />
|
||||
<img src={logo} alt={filename} />
|
||||
<span class="text">
|
||||
<h2>{name}</h2>
|
||||
<h6>{file}</h6>
|
||||
<h6>{filename}</h6>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -69,8 +70,8 @@
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
div:hover:not(.clicked) {
|
||||
background-color: var(--grey-two);
|
||||
div:hover {
|
||||
filter: brightness(0.85);
|
||||
}
|
||||
|
||||
img {
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#markup-content {
|
||||
/* Defaults for text */
|
||||
color: var(--white);
|
||||
font-weight: 300;
|
||||
font-size: 1rem;
|
||||
letter-spacing: 0.02rem;
|
||||
padding: 100px 30px 0px 30px;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--white);
|
||||
border-bottom: 1.5px solid var(--accent-color);
|
||||
padding: 0px 5px;
|
||||
transition: all 0.4s var(--bezier-one);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-color: var(--accent-color);
|
||||
border-radius: 6px;
|
||||
color: var(--grey-four);
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: var(--grey-six);
|
||||
border-radius: 4px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
/* Markup processors output this for bold text, but css spec is goofy aah */
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
import { is_tree } from './documentation.shared';
|
||||
import type { Document, DocsTree, DocsTreeNode, DocumentInfo } from './documentation.shared';
|
||||
|
||||
import { browser, prerendering } from '$app/environment';
|
||||
|
||||
import fs, { existsSync as exists } from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
|
||||
import { parse as parse_md } from 'marked';
|
||||
import AsciiDocProcessor from 'asciidoctor'
|
||||
|
||||
// This file does not work in a browser.
|
||||
if (browser) {
|
||||
throw Error('SvelteKit has skill issues');
|
||||
}
|
||||
|
||||
/// Constants
|
||||
|
||||
const supported_formats: Map<string, (markup: string) => Document> = new Map();
|
||||
|
||||
supported_formats.set("md", markup => {
|
||||
let lines = markup.split('\n');
|
||||
|
||||
// Get and remove the first line.
|
||||
const first_line = lines.splice(0, 1)[0];
|
||||
// Remove `# `.
|
||||
const title = first_line.substring(2);
|
||||
|
||||
// Convert the rest to html
|
||||
const content = parse_md(lines.join('\n'));
|
||||
|
||||
return { title, content };
|
||||
});
|
||||
|
||||
const asciidoctor = AsciiDocProcessor();
|
||||
const adoc_fn = markup => {
|
||||
// Get first line.
|
||||
const first_line = markup.split('\n')[0];
|
||||
// Remove `= `.
|
||||
const title = first_line.substring(2);
|
||||
|
||||
// Convert it to html. Unlike markdown, we do not need to remove the first title heading.
|
||||
// NOTE: Maybe consider change the safe mode value.
|
||||
const content = asciidoctor.convert(markup, { doctype: "book" })
|
||||
|
||||
return { title, content };
|
||||
}
|
||||
|
||||
supported_formats.set("adoc", adoc_fn)
|
||||
supported_formats.set("asciidoc", adoc_fn)
|
||||
|
||||
const supported_filetypes = [...supported_formats.keys()];
|
||||
|
||||
let docs_folder = process.env.REVANCED_DOCS_FOLDER;
|
||||
if (docs_folder === undefined) {
|
||||
if (prerendering) { console.warn("Using testing docs in production build") }
|
||||
docs_folder = "testing-docs";
|
||||
}
|
||||
|
||||
const ignored_items = ["assets"];
|
||||
|
||||
/// Utility functions
|
||||
|
||||
function is_directory(item: string) {
|
||||
return fs.lstatSync(item).isDirectory();
|
||||
}
|
||||
|
||||
function get_ext(fname: string) {
|
||||
// Get extname and remove the first dot.
|
||||
return path.extname(fname).substring(1);
|
||||
}
|
||||
|
||||
function get_slug_of_node(node: DocsTreeNode): string {
|
||||
if (is_tree(node)) {
|
||||
return node.index.slug;
|
||||
}
|
||||
|
||||
return node.slug;
|
||||
}
|
||||
|
||||
/// Important functions
|
||||
|
||||
// Get a document. Returns null if it does not exist.
|
||||
export function get(slug: string): Document|null {
|
||||
let target = path.join(docs_folder, slug);
|
||||
// Handle index (readme) file for folder.
|
||||
if (exists(target) && is_directory(target)) {
|
||||
target += "/README";
|
||||
}
|
||||
|
||||
const dir = path.dirname(target);
|
||||
if (!exists(dir)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let full_path, ext, found = false;
|
||||
// We are looking for the file `${target}.(any_supported_extension)`. Try to find it.
|
||||
for (const item of fs.readdirSync(dir)) {
|
||||
full_path = path.join(dir, item);
|
||||
// Get file extension
|
||||
ext = get_ext(item);
|
||||
|
||||
// Unsupported/unrelated file.
|
||||
if (!supported_formats.has(ext)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const desired_path = `${target}.${ext}`; // Don't grab some other random supported file.
|
||||
if (!is_directory(full_path) && desired_path == full_path) {
|
||||
// We found it.
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Process the file and return.
|
||||
return supported_formats.get(ext)(fs.readFileSync(full_path, 'utf-8'));
|
||||
}
|
||||
|
||||
// Get file information
|
||||
function process_file(fname: string): DocumentInfo {
|
||||
// Remove docs folder prefix and file extension suffix, then split it.
|
||||
const parts = fname
|
||||
.substring(`${docs_folder}/`.length, fname.length - (get_ext(fname).length + 1))
|
||||
.split("/");
|
||||
|
||||
// Remove `README` suffix if present.
|
||||
const last_part_index = parts.length - 1;
|
||||
if (parts[last_part_index] == "README") {
|
||||
parts.pop();
|
||||
}
|
||||
|
||||
const slug = parts.join("/");
|
||||
const title = get(slug).title;
|
||||
|
||||
return { slug, title };
|
||||
}
|
||||
|
||||
// Returns a document tree.
|
||||
function process_folder(dir: string): DocsTree|null {
|
||||
let tree: DocsTree = {
|
||||
index: null,
|
||||
nodes: []
|
||||
};
|
||||
|
||||
// List everything in the directory.
|
||||
const items = fs.readdirSync(dir);
|
||||
|
||||
for (const item of items) {
|
||||
if (ignored_items.includes(item) || [".", "_"].includes(item[0])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const itemPath = path.join(dir, item);
|
||||
|
||||
const is_dir = is_directory(itemPath);
|
||||
let is_index_file = false;
|
||||
|
||||
if (!is_dir) {
|
||||
// Ignore files we cannot process.
|
||||
if (!supported_formats.has(get_ext(item))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const ext of supported_filetypes) {
|
||||
if (item == `README.${ext}`) {
|
||||
is_index_file = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const node = is_dir ? process_folder(itemPath) : process_file(itemPath);
|
||||
if (node === null) {
|
||||
console.error(`The ${itemPath} directory does not have a README/index file! ignoring...`)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_index_file) {
|
||||
tree.index = node;
|
||||
} else {
|
||||
tree.nodes.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (tree.index === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// `numeric: true` because we want to be able to specify
|
||||
// the order if necessary by prepending a number to the file name.
|
||||
tree.nodes.sort(
|
||||
(a, b) => get_slug_of_node(a).localeCompare(get_slug_of_node(b), "en", { numeric: true })
|
||||
);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
// Returns the document tree.
|
||||
export function index_content(): DocsTree {
|
||||
const tree = process_folder(docs_folder);
|
||||
if (tree === null) {
|
||||
throw new Error("Root must have index (README) file.")
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/// Types
|
||||
|
||||
export interface Document {
|
||||
title: string;
|
||||
// HTML
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface DocumentInfo {
|
||||
title: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
// A tree representing the `docs` folder.
|
||||
export interface DocsTree {
|
||||
// index.whatever
|
||||
index: DocumentInfo;
|
||||
// Everything except index.whatever
|
||||
nodes: DocsTreeNode[];
|
||||
}
|
||||
|
||||
export type DocsTreeNode = DocsTree | DocumentInfo;
|
||||
|
||||
/// Functions
|
||||
|
||||
export function is_tree(node: DocsTreeNode) {
|
||||
return node.hasOwnProperty('nodes');
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { fly } from 'svelte/transition';
|
||||
import { quintOut } from 'svelte/easing';
|
||||
|
||||
import LogoOption from '$lib/components/atoms/LogoOption.svelte';
|
||||
import Button from '$lib/components/atoms/Button.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let selected: Array<string> = [];
|
||||
let logos = [];
|
||||
let currentPage = 1;
|
||||
let maxPages = 1;
|
||||
let logoAmount = 4;
|
||||
let min = 0;
|
||||
let max = logoAmount;
|
||||
|
||||
onMount(async () => {
|
||||
const response = await fetch('https://poll.revanced.app/logos');
|
||||
const json = await response.json();
|
||||
@@ -17,38 +20,49 @@
|
||||
}
|
||||
// update ui
|
||||
logos = logos;
|
||||
maxPages = Math.floor(logos.length / logoAmount);
|
||||
});
|
||||
|
||||
function previousPage() {
|
||||
min -= logoAmount;
|
||||
max -= logoAmount;
|
||||
currentPage--;
|
||||
}
|
||||
|
||||
function nextPage() {
|
||||
min += logoAmount;
|
||||
max += logoAmount;
|
||||
currentPage++;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>ReVanced | Contributors</title>
|
||||
<meta content="ReVanced | Contributors" name="og:title" />
|
||||
<meta content="ReVanced | Contributors" name="twitter:title" />
|
||||
<title>ReVanced · Logo Poll</title>
|
||||
<meta content="ReVanced · Logo Poll" name="og:title" />
|
||||
<meta content="ReVanced · Logo Poll" name="twitter:title" />
|
||||
</svelte:head>
|
||||
|
||||
<main>
|
||||
<div class="wrapper">
|
||||
<div class="top-container">
|
||||
<h1>ReVanced Logo Poll</h1>
|
||||
<h2>{selected.length}/4 selected· Page 1/6</h2>
|
||||
<div class="top-custom-button-container">
|
||||
<button>Help</button>
|
||||
<button>Website</button>
|
||||
</div>
|
||||
<h1>ReVanced Logo Poll</h1>
|
||||
<h2>{selected.length}/4 selected· Page {currentPage}/{maxPages}</h2>
|
||||
<div class="top-custom-button-container">
|
||||
<a href="https://hhh.com" target="_blank" rel="noreferrer"><button>Help</button></a>
|
||||
<a href="https://revanced.app" target="_blank" rel="noreferrer"><button>Website</button></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="options-grid">
|
||||
{#if logos.length > 0}
|
||||
<LogoOption bind:selected id={logos[0].id} logo={logos[0].gdrive_direct_url} name={logos[0].name} file={logos[0].filename}/>
|
||||
<LogoOption bind:selected id={logos[1].id} logo={logos[1].gdrive_direct_url} name={logos[1].name} file={logos[1].filename}/>
|
||||
<LogoOption bind:selected id={logos[2].id} logo={logos[2].gdrive_direct_url} name={logos[2].name} file={logos[2].filename}/>
|
||||
<LogoOption bind:selected id={logos[3].id} logo={logos[3].gdrive_direct_url} name={logos[3].name} file={logos[3].filename}/>
|
||||
{/if}
|
||||
{#each logos.slice(min, max) as { id, gdrive_direct_url, name, filename }}
|
||||
{#key currentPage}
|
||||
<LogoOption bind:selected clicked={selected.includes(id)} {id} logo={gdrive_direct_url} {name} {filename} />
|
||||
{/key}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="buttons-container">
|
||||
<Button>Previous</Button>
|
||||
<Button kind="primary" href="https://next.com">Next</Button>
|
||||
<Button on:click={previousPage} unclickable={currentPage <= 1}>Previous</Button>
|
||||
<Button kind="primary" on:click={nextPage} unclickable={currentPage >= maxPages}>Next</Button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
@@ -69,7 +83,6 @@
|
||||
color: var(--grey-four);
|
||||
}
|
||||
|
||||
|
||||
h2 {
|
||||
font-size: 1rem;
|
||||
color: var(--grey-three);
|
||||
@@ -84,10 +97,9 @@
|
||||
float: bottom;
|
||||
}
|
||||
|
||||
|
||||
button {
|
||||
background-color: transparent;
|
||||
border:none;
|
||||
border: none;
|
||||
border: 1px solid var(--grey-six);
|
||||
color: var(--grey-four);
|
||||
padding: 0.5rem 1.25rem;
|
||||
|
||||
Reference in New Issue
Block a user