feat(directory): 🚧 port web to vNext branch

This commit is contained in:
Collin M. Barrett 2020-08-24 20:49:35 -05:00
parent 76fe54a983
commit f062dc4b19
125 changed files with 2410 additions and 2 deletions

View file

@ -0,0 +1,5 @@
**/Dockerfile
**/.dockerignore
**/.gitignore
**/azure-pipelines.yaml
**/node_modules

1
directory/web/.env Normal file
View file

@ -0,0 +1 @@
INLINE_RUNTIME_CHUNK=false

25
directory/web/.gitignore vendored Normal file
View file

@ -0,0 +1,25 @@
# https://raw.githubusercontent.com/facebook/create-react-app/master/packages/cra-template/template/gitignore
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

26
directory/web/Dockerfile Normal file
View file

@ -0,0 +1,26 @@
# init base
FROM nginx:alpine as base
COPY reverse-proxy/conf.d /etc/nginx/conf.d
# init install
FROM node:alpine as install
# https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md#node-gyp-alpine
RUN apk add --no-cache python make g++
# install
WORKDIR /usr/src/app/
COPY package*.json ./
RUN npm ci --only=production
# init build
FROM node:alpine as build
COPY --from=install node_modules .
# build
COPY . .
RUN npm run build
# final
FROM base as final
COPY --from=build /usr/src/app/build /usr/share/nginx/html

View file

@ -0,0 +1,9 @@
const { override, fixBabelImports } = require("customize-cra");
module.exports = override(
fixBabelImports("import", {
libraryName: "antd",
libraryDirectory: "es",
style: "css",
})
);

BIN
directory/web/package-lock.json generated Normal file

Binary file not shown.

View file

@ -0,0 +1,68 @@
{
"name": "filterlists.web",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^10.4.9",
"@testing-library/user-event": "^12.1.2",
"@types/jest": "^26.0.10",
"@types/node": "^14.6.0",
"@types/react": "^16.9.46",
"@types/react-dom": "^16.9.8",
"@types/react-router-dom": "^5.1.5",
"antd": "^4.6.1",
"babel-plugin-import": "^1.13.0",
"customize-cra": "^1.0.0",
"react": "^16.13.1",
"react-app-rewired": "^2.1.6",
"react-dom": "^16.13.1",
"react-router-dom": "^5.2.0",
"react-scripts": "^3.4.3",
"slugify": "^1.4.5",
"typescript": "^4.0.2"
},
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},
"proxy": "https://filterlists.com",
"eslintConfig": {
"extends": [
"react-app",
"plugin:prettier/recommended"
]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"**/*.{json,js,jsx,ts,tsx,css,scss,yaml,md}": [
"prettier --write",
"git add"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.4",
"husky": "^4.2.5",
"lint-staged": "^10.2.11",
"prettier": "^2.0.5"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link
rel="shortcut icon"
href="https://filterlists.com/icon_filterlists.png"
/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>
FilterLists | Subscriptions for uBlock Origin, Adblock Plus, AdGuard, ...
</title>
<meta
name="description"
content="FilterLists is the independent, comprehensive directory of filter and host lists for advertisements, trackers, malware, and annoyances. By Collin M. Barrett."
/>
<link
rel="icon"
type="image/png"
href="https://filterlists.com/icon_filterlists.png"
/>
<link rel="canonical" href="https://filterlists.com/" />
</head>
<body>
<div id="root"></div>
<noscript>
<p>
FilterLists is built with JavaScript which does not seem to be supported
by or enabled in your browser.
</p>
<p>
<a href="https://github.com/collinbarrett/FilterLists"
>FilterLists on GitHub</a
>
</p>
</noscript>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

@ -0,0 +1,15 @@
{
"short_name": "FilterLists",
"name": "FilterLists | Subscriptions for uBlock Origin, Adblock Plus, AdGuard, ...",
"icons": [
{
"src": "icon_filterlists.png",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/png"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View file

@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

View file

@ -0,0 +1,17 @@
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri /index.html;
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

View file

@ -0,0 +1,10 @@
import React from "react";
import ReactDOM from "react-dom";
import { App } from "./App";
it("renders without crashing", () => {
const div = document.createElement("div");
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});

158
directory/web/src/App.tsx Normal file
View file

@ -0,0 +1,158 @@
import {
ApiOutlined,
DollarOutlined,
GithubOutlined,
TeamOutlined,
TwitterOutlined,
} from "@ant-design/icons";
import { Layout, Menu, Tag } from "antd";
import React from "react";
import {
BrowserRouter as Router,
Link,
Route,
RouteComponentProps,
Switch,
} from "react-router-dom";
import { ListsTable } from "./components";
const { Header, Content, Footer } = Layout;
export const App: React.FC = () => (
<Router>
<Layout>
<Header style={{ background: "#fff" }}>
<Logo />
<Menu mode="horizontal" style={{ lineHeight: "64px" }} />
</Header>
<Content>
<div
style={{
background: "#fff",
paddingLeft: 12,
paddingTop: 12,
paddingRight: 12,
minHeight: 280,
}}
>
<Switch>
<Route path="/" component={ListsTable} />
<Route component={NotFound} />
</Switch>
</div>
</Content>
<Footer
style={{
textAlign: "center",
padding: "6px 50px",
background: "#fff",
lineHeight: "28px",
}}
>
<CopyrightAuthor />
<Twitter />
<Community />
<GitHub />
<Api />
<Donate />
</Footer>
</Layout>
</Router>
);
const Logo = () => (
<Link to="/">
<img
src={`${process.env.PUBLIC_URL}/logo_filterlists.png`}
alt="FilterLists logo"
height="44px"
style={{ background: "#fff" }}
/>
</Link>
);
const NotFound = (props: RouteComponentProps) => (
<h2>
404 Not Found: <code>{props.location.pathname}</code>
</h2>
);
const CopyrightAuthor = () => (
<Tag>
©{new Date().getFullYear()}&nbsp;
<a
href="https://collinmbarrett.com"
title="Collin M. Barrett's Homepage"
target="_blank"
rel="noopener noreferrer"
>
Collin M. Barrett
</a>
</Tag>
);
const Twitter = () => (
<Tag>
<a
href="https://twitter.com/FilterLists"
title="FilterLists on Twitter"
target="_blank"
rel="noopener noreferrer"
>
<TwitterOutlined /> Twitter
</a>
</Tag>
);
const Community = () => (
<Tag>
<a
href="https://hub.filterlists.com"
title="FilterLists Discourse community forum"
target="_blank"
rel="noopener noreferrer"
>
<TeamOutlined /> Community
</a>
</Tag>
);
const GitHub = () => (
<Tag>
<a
href="https://github.com/collinbarrett/FilterLists"
title="FilterLists git repository on GitHub"
target="_blank"
rel="noopener noreferrer"
>
<GithubOutlined /> GitHub
</a>
</Tag>
);
const Api = () => (
<Tag>
<a
href="/api/v1/lists"
title="FilterLists API lists endpoint"
target="_blank"
rel="noopener noreferrer"
>
<ApiOutlined /> API
</a>
</Tag>
);
const Donate = () => (
<Tag>
<a
href="https://github.com/sponsors/collinbarrett"
title="Sponsor Collin Barrett on GitHub"
target="_blank"
rel="noopener noreferrer"
>
<DollarOutlined /> Donate
</a>
</Tag>
);

View file

@ -0,0 +1,15 @@
import React from "react";
interface Props {
description: string;
descriptionSourceUrl: string;
}
export const Description = (props: Props) =>
props.descriptionSourceUrl ? (
<blockquote cite={props.descriptionSourceUrl}>
{props.description}
</blockquote>
) : (
<p>{props.description}</p>
);

View file

@ -0,0 +1,32 @@
import { Tag } from "antd";
import * as React from "react";
interface Props {
name: string;
descriptionUrl?: string;
showLabel?: boolean;
}
export const LicenseTag = (props: Props) =>
props.name ? (
<span>
{props.showLabel && <h3>License:</h3>}
<Tag>
<TagContents name={props.name} descriptionUrl={props.descriptionUrl} />
</Tag>
</span>
) : null;
const TagContents = (props: Props) =>
props.descriptionUrl ? (
<a
href={props.descriptionUrl}
title={`View ${props.name}'s homepage.`}
target="_blank"
rel="noopener noreferrer"
>
{props.name}
</a>
) : (
<>{props.name}</>
);

View file

@ -0,0 +1,27 @@
import { Button } from "antd";
import { ButtonType } from "antd/lib/button";
import React from "react";
interface Props {
url: string;
text: string;
title?: string;
type?: ButtonType;
icon?: React.ReactNode;
}
export const LinkButton = (props: Props) =>
props.url && props.text ? (
<Button
href={props.url}
title={props.title}
type={props.type || "default"}
block
style={{ borderLeftColor: "rgb(217, 217, 217)" }} //HACK: override buggy style in antd
icon={props.icon}
target="_blank"
rel="noopener noreferrer"
>
{props.text}
</Button>
) : null;

View file

@ -0,0 +1,28 @@
import { InfoCircleOutlined } from "@ant-design/icons";
import { Button } from "antd";
import React from "react";
import { RouteComponentProps } from "react-router-dom";
import slugify from "slugify";
import { SlugifyOptions } from "../constants";
interface Props {
listName: string;
}
export const ListInfoButton = (props: RouteComponentProps & Props) => {
const listPath = `/lists/${slugify(props.listName, SlugifyOptions)}`;
return (
<Button
type="primary"
title={`View more information about ${props.listName}.`}
onClick={() =>
props.location.pathname === listPath
? props.history.push("/")
: props.history.push(listPath)
}
>
<InfoCircleOutlined />
</Button>
);
};

View file

@ -0,0 +1,13 @@
import React from "react";
interface Props {
publishedDate: string;
}
export const PublishedDate = (props: Props) =>
props.publishedDate ? (
<>
<h3>First Published Date:</h3>
{new Date(props.publishedDate).toDateString()}
</>
) : null;

View file

@ -0,0 +1,104 @@
import { ImportOutlined } from "@ant-design/icons";
import { Button } from "antd";
import { ButtonType } from "antd/lib/button";
import React from "react";
interface Props {
name: string;
viewUrl: string;
viewUrlMirrors: string[];
}
export const SubscribeButtons = (props: Props) =>
props.viewUrl ? (
<>
<SubscribeButton
name={props.name}
viewUrl={props.viewUrl}
text="Subscribe"
/>
<MirrorButtons name={props.name} viewUrlMirrors={props.viewUrlMirrors} />
</>
) : null;
interface MirrorButtonsProps {
name: string;
viewUrlMirrors: string[];
}
const MirrorButtons = (props: MirrorButtonsProps) => (
<>
{props.viewUrlMirrors && props.viewUrlMirrors.length
? props.viewUrlMirrors.map((viewUrlMirror: string, i: number) => (
<SubscribeButton
key={i}
name={props.name}
viewUrl={viewUrlMirror}
text={`Subscribe (Mirror ${i + 1})`}
/>
))
: null}
</>
);
interface SubscribeButtonProps {
name: string;
viewUrl: string;
text: string;
}
const SubscribeButton = (props: SubscribeButtonProps) => {
const buttonProps = buildButtonProps(props.name, props.viewUrl);
return (
<Button
disabled={buttonProps.disabled}
block
icon={<ImportOutlined />}
type={buttonProps.type}
href={buttonProps.href}
title={buttonProps.title}
>
{props.text}
</Button>
);
};
const buildButtonProps = (name: string, viewUrl: string) => {
let type: ButtonType = "primary";
let disabled: boolean = false;
const hrefLocation = `${encodeURIComponent(viewUrl)}`;
const hrefTitle = `${encodeURIComponent(name)}`;
let href = `abp:subscribe?location=${hrefLocation}&amp;title=${hrefTitle}`;
let prefixes: string[] = [];
let message = `Subscribe to ${name} with a browser extension supporting the "abp:" protocol (e.g. uBlock Origin, Adblock Plus).`;
// HTTP protocols
if (viewUrl.includes(".onion/")) {
type = "dashed";
prefixes.push("TOR");
}
if (viewUrl.includes("http://")) {
type = "dashed";
prefixes.push("INSECURE");
}
// Software protocols
if (viewUrl.includes(".tpl")) {
disabled = true; // IE not supported by FilterLists
}
if (
viewUrl.includes(".lsrules") ||
viewUrl.includes("?hostformat=littlesnitch")
) {
href = `x-littlesnitch:subscribe-rules?url=${hrefLocation}`;
message = `Subscribe to ${name} with Little Snitch's rule group subscription feature.`;
}
const title = `${
prefixes.length ? prefixes.join(" | ") + " | " : ""
}${message}`;
return { disabled, type, href, title };
};

View file

@ -0,0 +1,32 @@
import { Tag } from "antd";
import * as React from "react";
interface Props {
name: string;
definitionUrl?: string;
showLabel?: boolean;
}
export const SyntaxTag = (props: Props) =>
props.name ? (
<span>
{props.showLabel && <h3>Syntax:</h3>}
<Tag>
<TagContents name={props.name} definitionUrl={props.definitionUrl} />
</Tag>
</span>
) : null;
const TagContents = (props: Props) =>
props.definitionUrl ? (
<a
href={props.definitionUrl}
title={`View ${props.name}'s homepage.`}
target="_blank"
rel="noopener noreferrer"
>
{props.name}
</a>
) : (
<>{props.name}</>
);

View file

@ -0,0 +1,3 @@
import { ListsTable } from "./listsTable";
export { ListsTable };

View file

@ -0,0 +1,7 @@
.grow {
/* allow for 2nd row's tags' bottom border to not be chopped in AllListsTable */
max-height: none !important;
/* leave vertical gap between rows of tags */
line-height: 28px;
}

View file

@ -0,0 +1,24 @@
import { Tag } from "antd";
import React from "react";
import { Language } from "../../interfaces/Language";
import styles from "./LanguageCloud.module.css";
interface Props {
languages: Language[];
showLabel?: boolean;
}
export const LanguageCloud = (props: Props) =>
props.languages && props.languages.length ? (
<div className={styles.grow}>
{props.showLabel && (
<h3>{`Language${props.languages.length > 1 ? "s" : ""}:`}</h3>
)}
{props.languages.map((l: Language, i: number) => (
<Tag key={i} title={l.name}>
{l.iso6391}
</Tag>
))}
</div>
) : null;

View file

@ -0,0 +1,3 @@
import { LanguageCloud } from "./LanguageCloud";
export { LanguageCloud };

View file

@ -0,0 +1,156 @@
import "./listInfoDrawer.css";
import { Divider, Drawer } from "antd";
import {
DollarOutlined,
FileExclamationOutlined,
FormOutlined,
GithubOutlined,
HomeOutlined,
MailOutlined,
MessageOutlined,
SearchOutlined,
TeamOutlined,
} from "@ant-design/icons";
import React, { useEffect, useState } from "react";
import ButtonGroup from "antd/lib/button/button-group";
import { Description } from "../Description";
import { Language } from "../../interfaces/Language";
import { LanguageCloud } from "../languageCloud";
import { License } from "../../interfaces/License";
import { LicenseTag } from "../LicenseTag";
import { LinkButton } from "../LinkButton";
import { List } from "../../interfaces/List";
import { Maintainer } from "../../interfaces/Maintainer";
import { Maintainers } from "../maintainers";
import { PublishedDate } from "../PublishedDate";
import { RouteComponentProps } from "react-router-dom";
import { Software } from "../../interfaces/Software";
import { SoftwareCloud } from "../softwareCloud";
import { SubscribeButtons } from "../SubscribeButtons";
import { Syntax } from "../../interfaces/Syntax";
import { SyntaxTag } from "../SyntaxTag";
import { Tag } from "../../interfaces/Tag";
import { TagCloud } from "../tagCloud";
interface Props {
list: List;
languages: Language[];
license: License | undefined;
maintainers: Maintainer[];
software: Software[];
syntax: Syntax | undefined;
tags: Tag[];
}
export const ListInfoDrawer = (props: RouteComponentProps & Props) => {
const [originalTitle] = useState<string>(document.title);
useEffect(() => {
const updateTitle = () =>
(document.title = props.list.name + " | " + originalTitle);
updateTitle();
return () => {
document.title = originalTitle;
};
}, [props.list, originalTitle]);
return (
<Drawer
visible={true}
width={350}
placement={"right"}
mask={false}
title={props.list.name}
destroyOnClose={true}
onClose={() => props.history.push("/")}
>
<Description
description={props.list.description}
descriptionSourceUrl={props.list.descriptionSourceUrl}
/>
<LanguageCloud languages={props.languages} showLabel={true} />
<TagCloud tags={props.tags} showLabel={true} />
{props.license && (
<LicenseTag
name={props.license.name}
descriptionUrl={props.license.descriptionUrl}
showLabel={true}
/>
)}
{props.syntax && (
<SyntaxTag
name={props.syntax.name}
definitionUrl={props.syntax.definitionUrl}
showLabel={true}
/>
)}
<SoftwareCloud software={props.software} showLabel={true} />
<PublishedDate publishedDate={props.list.publishedDate} />
<Maintainers maintainers={props.maintainers} />
<Divider />
<ButtonGroup style={{ display: "inherit" }}>
<SubscribeButtons
name={props.list.name}
viewUrl={props.list.viewUrl}
viewUrlMirrors={props.list.viewUrlMirrors}
/>
<LinkButton
url={props.list.viewUrl}
text="View"
title={`View ${props.list.name} in its raw format`}
icon={<SearchOutlined />}
/>
<LinkButton
url={props.list.homeUrl}
text="Home"
title={`View ${props.list.name}'s homepage.`}
icon={<HomeOutlined />}
/>
<LinkButton
url={props.list.policyUrl}
text="Policy"
title={`View the types of rules that ${props.list.name} includes.`}
icon={<FileExclamationOutlined />}
/>
{props.list.emailAddress && (
<LinkButton
url={`mailto:${props.list.emailAddress}`}
text="Email"
title={`Email ${props.list.name}.`}
icon={<MailOutlined />}
/>
)}
<LinkButton
url={props.list.issuesUrl}
text="GitHub Issues"
title={`View the GitHub Issues for ${props.list.name}.`}
icon={<GithubOutlined />}
/>
<LinkButton
url={props.list.submissionUrl}
text="Submit a Rule"
title={`Submit a new rule to be included in ${props.list.name}.`}
icon={<FormOutlined />}
/>
<LinkButton
url={props.list.forumUrl}
text="Forum"
title={`View the forum for ${props.list.name}.`}
icon={<TeamOutlined />}
/>
<LinkButton
url={props.list.chatUrl}
text="Chat"
title={`Enter the chat room for ${props.list.name}.`}
icon={<MessageOutlined />}
/>
<LinkButton
url={props.list.donateUrl}
text="Donate"
title={`Donate to the maintainer of ${props.list.name}.`}
icon={<DollarOutlined />}
/>
</ButtonGroup>
</Drawer>
);
};

View file

@ -0,0 +1,3 @@
import { ListInfoDrawer } from "./ListInfoDrawer";
export { ListInfoDrawer };

View file

@ -0,0 +1,4 @@
h3 {
margin-top: 1em;
margin-bottom: 0.2em;
}

View file

@ -0,0 +1,74 @@
import React from "react";
import {
Redirect,
Route,
RouteComponentProps,
StaticContext,
} from "react-router";
import { Language } from "../../interfaces/Language";
import { License } from "../../interfaces/License";
import { List } from "../../interfaces/List";
import { Maintainer } from "../../interfaces/Maintainer";
import { Software } from "../../interfaces/Software";
import { Syntax } from "../../interfaces/Syntax";
import { Tag } from "../../interfaces/Tag";
import { ListInfoDrawer } from "../listInfoDrawer";
interface Props {
lists: List[];
languages: Language[];
licenses: License[];
maintainers: Maintainer[];
software: Software[];
syntaxes: Syntax[];
tags: Tag[];
}
export const ListDrawer = (props: Props) => {
const renderDrawer = (rp: RouteComponentProps<any, StaticContext, any>) => {
const list = props.lists.find((l) => l.slug === rp.match.params.listSlug);
return list ? (
<ListInfoDrawer
list={list as List}
languages={
list.languageIds &&
props.languages.filter((l: Language) =>
list.languageIds.includes(l.id)
)
}
license={
list.licenseId
? props.licenses.find((l: License) => list.licenseId === l.id)
: props.licenses.find((l: License) => l.id === 5)
}
maintainers={
list.maintainerIds &&
props.maintainers.filter((m: Maintainer) =>
list.maintainerIds.includes(m.id)
)
}
software={
list.syntaxId
? props.software.filter((s: Software) =>
s.syntaxIds.includes(list.syntaxId)
)
: []
}
syntax={
list.syntaxId
? props.syntaxes.find((s) => s.id === list.syntaxId)
: undefined
}
tags={
list.tagIds &&
props.tags.filter((t: Tag) => list.tagIds.includes(t.id))
}
{...rp}
/>
) : props.lists && props.lists.length ? (
<Redirect to={{ pathname: "/" }} />
) : null;
};
return <Route path="/lists/:listSlug" render={renderDrawer} />;
};

View file

@ -0,0 +1,24 @@
.nogrow > * {
/* limit table cells from wrapping more than 2 lines */
/* https://stackoverflow.com/a/13924997/2343739 */
/* https://stackoverflow.com/a/22042054/2343739 */
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box !important;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2; /* number of lines to show */
line-height: 1.5; /* fallback */
max-height: 3em; /* fallback */
/* fixed row height */
min-height: 3em;
/* fix left-side truncation of Description column on narrow viewports in Chrome */
word-break: break-word;
}
.nogrow {
/* eliminate top and bottom padding in cells to fit more rows per page */
padding-top: 0px !important;
padding-bottom: 0px !important;
}

View file

@ -0,0 +1,386 @@
import "./listsTable.css";
import {
Key,
SorterResult,
TableCurrentDataSource,
} from "antd/lib/table/interface";
import React, { useEffect, useState } from "react";
import { SoftwareCloud, SoftwareIcon } from "../softwareCloud";
import { Table, Tag } from "antd";
import { useSearchColumnFilter, useTablePageSizer } from "../../hooks";
import { Description } from "../Description";
import { Language } from "../../interfaces/Language";
import { LanguageCloud } from "../languageCloud";
import { License } from "../../interfaces/License";
import { LicenseTag } from "../LicenseTag";
import { List } from "../../interfaces/List";
import { ListInfoButton } from "../ListInfoButton";
import { Maintainer } from "../../interfaces/Maintainer";
import { MaintainerCloud } from "../maintainerCloud";
import { RouteComponentProps } from "react-router";
import { Software } from "../../interfaces/Software";
import { Syntax } from "../../interfaces/Syntax";
import { SyntaxTag } from "../SyntaxTag";
import { TagCloud } from "../tagCloud";
import { Tag as TagInterface } from "../../interfaces/Tag";
import { arraySorter } from "./arraySorter";
import { nameof } from "../../utils";
import styles from "./ListsTable.module.css";
import { TablePaginationConfig } from "antd/lib/table";
interface Props {
lists: List[];
languages: Language[];
licenses: License[];
maintainers: Maintainer[];
software: Software[];
syntaxes: Syntax[];
tags: TagInterface[];
}
export const ListsTable = (props: RouteComponentProps & Props) => {
const {
lists,
languages,
licenses,
maintainers,
software,
syntaxes,
tags,
...routeComponentProps
} = props;
const tablePageSize = useTablePageSizer();
const searchNameColumn = useSearchColumnFilter<List>(nameof<List>("name"));
const searchDescriptionColumn = useSearchColumnFilter<List>(
nameof<List>("description")
);
const [visibleLists, setVisibleLists] = useState<List[]>(lists);
useEffect(() => {
setVisibleLists(lists);
}, [lists]);
return (
<Table<List>
dataSource={lists}
rowKey={(record) => record.id.toString()}
loading={lists.length ? false : true}
size="small"
pagination={{
simple: true,
style: { float: "left", margin: "4px 4px" },
pageSize: tablePageSize.pageSize,
}}
scroll={{
x:
tablePageSize.isNarrowWindow || tablePageSize.isWideWindow
? undefined
: 1892,
}}
onChange={(
_pagination: TablePaginationConfig,
_filters: Record<string, Key[] | null>,
_sorter: SorterResult<List> | SorterResult<List>[],
extra: TableCurrentDataSource<List>
) => setVisibleLists(extra.currentDataSource)}
>
<Table.Column<List>
title="Info"
key="Info"
dataIndex={nameof<List>("id")}
width={62}
className={styles.nogrow}
fixed={tablePageSize.isNarrowWindow ? undefined : "left"}
render={(_id: number, list: List) => (
<ListInfoButton listName={list.name} {...routeComponentProps} />
)}
/>
<Table.Column<List>
title="Name"
key="Name"
dataIndex={nameof<List>("name")}
sorter={(a, b) => a.name.localeCompare(b.name)}
defaultSortOrder={"ascend"}
width={tablePageSize.isNarrowWindow ? undefined : 200}
className={styles.nogrow}
fixed={tablePageSize.isNarrowWindow ? undefined : "left"}
filterDropdown={searchNameColumn.filterDropdown}
filterIcon={searchNameColumn.filterIcon}
onFilter={searchNameColumn.onFilter}
render={(name: string) => <div>{name}</div>}
/>
{tablePageSize.isNarrowWindow ? null : (
<Table.Column<List>
title="Description"
key="Description"
dataIndex={nameof<List>("description")}
sorter={(a, b) => a.description.localeCompare(b.description)}
width={
!tablePageSize.isNarrowWindow && !tablePageSize.isWideWindow
? 423
: undefined
}
className={styles.nogrow}
filterDropdown={searchDescriptionColumn.filterDropdown}
filterIcon={searchDescriptionColumn.filterIcon}
onFilter={searchDescriptionColumn.onFilter}
render={(description: string, list: List) => (
<Description
description={description}
descriptionSourceUrl={list.descriptionSourceUrl}
/>
)}
/>
)}
{tablePageSize.isNarrowWindow ? null : (
<Table.Column<List>
title="Software"
key="Software"
dataIndex={nameof<List>("syntaxId")}
sorter={(a, b) => {
const getSoftwareIds = (l: List) =>
software
.filter(
(s: Software) =>
s.syntaxIds && s.syntaxIds.includes(l.syntaxId)
)
.map((s) => s.id);
return arraySorter(getSoftwareIds(a), getSoftwareIds(b), software);
}}
width={170}
className={styles.nogrow}
filters={software.map((s) => ({
text: (
<>
<SoftwareIcon id={s.id} />
&nbsp;
{s.name}&nbsp; (
{
visibleLists.filter(
(l) => s.syntaxIds && s.syntaxIds.includes(l.syntaxId)
).length
}
)
</>
),
value: s.name,
}))}
onFilter={(value, record) =>
software
.filter((s: Software) => s.name === value)
.flatMap((s) => s.syntaxIds)
.includes(record.syntaxId)
}
render={(syntaxId: number) =>
syntaxId ? (
<SoftwareCloud
software={software.filter(
(s: Software) => s.syntaxIds && s.syntaxIds.includes(syntaxId)
)}
/>
) : null
}
/>
)}
{tablePageSize.isNarrowWindow ? null : (
<Table.Column<List>
title="Syntax"
key="Syntax"
dataIndex={nameof<List>("syntaxId")}
sorter={(a, b) => {
const syntaxA = syntaxes.find((s) => s.id === a.syntaxId);
const syntaxB = syntaxes.find((s) => s.id === b.syntaxId);
return syntaxA
? syntaxB
? syntaxA.name.localeCompare(syntaxB.name)
: -1
: 1;
}}
width={254}
className={styles.nogrow}
filters={syntaxes.map((s) => ({
text: (
<>
<SyntaxTag name={s.name} showLabel={false} />(
{
visibleLists.filter((l) => l.syntaxId && l.syntaxId === s.id)
.length
}
)
</>
),
value: s.id.toString(),
}))}
onFilter={(value, record) =>
record.syntaxId ? record.syntaxId.toString() === value : false
}
render={(syntaxId: number) => {
const syntax = syntaxes.find((s) => s.id === syntaxId);
return syntax ? (
<SyntaxTag
name={syntax.name}
definitionUrl={syntax.definitionUrl}
/>
) : null;
}}
/>
)}
{tablePageSize.isNarrowWindow ? null : (
<Table.Column<List>
title="Languages"
key="Languages"
dataIndex={nameof<List>("languageIds")}
sorter={(a, b) =>
arraySorter(a.languageIds, b.languageIds, languages)
}
width={129}
className={styles.nogrow}
filters={languages.map((l) => ({
text: (
<>
<Tag title={l.name}>{l.iso6391}</Tag>&nbsp;
{l.name}&nbsp; (
{
visibleLists.filter(
(li) => li.languageIds && li.languageIds.includes(l.id)
).length
}
)
</>
),
value: l.id.toString(),
}))}
onFilter={(value, record) =>
record.languageIds ? record.languageIds.includes(+value) : false
}
render={(languageIds: number[]) =>
languageIds ? (
<LanguageCloud
languages={languages.filter((l: Language) =>
languageIds.includes(l.id)
)}
/>
) : null
}
/>
)}
{tablePageSize.isNarrowWindow ? null : (
<Table.Column<List>
title="Tags"
key="Tags"
dataIndex={nameof<List>("tagIds")}
sorter={(a, b) => arraySorter(a.tagIds, b.tagIds, tags)}
width={275}
className={styles.nogrow}
filters={tags.map((t) => ({
text: (
<>
<Tag title={t.description}>{t.name}</Tag>(
{
visibleLists.filter(
(l) => l.tagIds && l.tagIds.includes(t.id)
).length
}
)
</>
),
value: t.id.toString(),
}))}
onFilter={(value, record) =>
record.tagIds ? record.tagIds.includes(+value) : false
}
render={(tagIds: number[]) =>
tagIds ? (
<TagCloud
tags={tags.filter((t: TagInterface) => tagIds.includes(t.id))}
/>
) : null
}
/>
)}
{tablePageSize.isNarrowWindow ? null : (
<Table.Column<List>
title="Maintainers"
key="Maintainers"
dataIndex={nameof<List>("maintainerIds")}
sorter={(a, b) =>
arraySorter(a.maintainerIds, b.maintainerIds, maintainers)
}
width={191}
className={styles.nogrow}
filters={maintainers.map((t) => ({
text: (
<>
<Tag title={t.name}>{t.name}</Tag>(
{
visibleLists.filter(
(l) => l.maintainerIds && l.maintainerIds.includes(t.id)
).length
}
)
</>
),
value: t.id.toString(),
}))}
onFilter={(value, record) =>
record.maintainerIds ? record.maintainerIds.includes(+value) : false
}
render={(maintainerIds: number[]) =>
maintainerIds ? (
<MaintainerCloud
maintainers={maintainers.filter((t: Maintainer) =>
maintainerIds.includes(t.id)
)}
/>
) : null
}
/>
)}
{tablePageSize.isNarrowWindow ? null : (
<Table.Column<List>
title="License"
key="License"
dataIndex={nameof<List>("licenseId")}
sorter={(a, b) => {
const licenseA = licenses.find((s) => s.id === a.licenseId);
const licenseB = licenses.find((s) => s.id === b.licenseId);
return licenseA
? licenseB
? licenseA.name.localeCompare(licenseB.name)
: -1
: 1;
}}
width={215}
className={styles.nogrow}
filters={licenses.map((l) => ({
text: (
<>
<LicenseTag name={l.name} showLabel={false} />(
{
visibleLists.filter(
(li) => li.licenseId && li.licenseId === l.id
).length
}
)
</>
),
value: l.id.toString(),
}))}
onFilter={(value, record) =>
record.licenseId ? record.licenseId.toString() === value : false
}
render={(licenseId: number) => {
const license = licenses.find((l) => l.id === licenseId);
return license ? (
<LicenseTag
name={license.name}
descriptionUrl={license.descriptionUrl}
showLabel={false}
/>
) : null;
}}
/>
)}
</Table>
);
};

View file

@ -0,0 +1,47 @@
import React from "react";
import { RouteComponentProps } from "react-router-dom";
import {
useLanguages,
useLicenses,
useLists,
useMaintainers,
useSoftware,
useSyntaxes,
useTags,
} from "../../hooks";
import { ListDrawer } from "./ListDrawer";
import { ListsTable } from "./ListsTable";
export const ListsTableHoc = (props: RouteComponentProps) => {
const lists = useLists();
const languages = useLanguages();
const licenses = useLicenses();
const maintainers = useMaintainers();
const software = useSoftware();
const syntaxes = useSyntaxes();
const tags = useTags();
return (
<>
<ListsTable
lists={lists}
languages={languages}
licenses={licenses}
maintainers={maintainers}
software={software}
syntaxes={syntaxes}
tags={tags}
{...props}
/>
<ListDrawer
lists={lists}
languages={languages}
licenses={licenses}
maintainers={maintainers}
software={software}
syntaxes={syntaxes}
tags={tags}
/>
</>
);
};

View file

@ -0,0 +1,30 @@
interface ArraySortableEntity {
id: number;
name: string;
}
export const arraySorter = (
a: number[],
b: number[],
entities: ArraySortableEntity[]
) =>
a && a.length
? b && b.length
? a.length === b.length
? entities
.filter((e: ArraySortableEntity) => a.includes(e.id))
.map((e: ArraySortableEntity) => e.name)
.join()
.toLowerCase() >
entities
.filter((e: ArraySortableEntity) => b.includes(e.id))
.map((e: ArraySortableEntity) => e.name)
.join()
.toLowerCase()
? 1
: -1
: a.length > b.length
? -1
: 1
: -1
: 1;

View file

@ -0,0 +1,3 @@
import { ListsTableHoc } from "./ListsTableHoc";
export { ListsTableHoc as ListsTable };

View file

@ -0,0 +1,3 @@
.ant-table-header-column > div {
margin-top: 8px;
}

View file

@ -0,0 +1,7 @@
.grow {
/* allow for 2nd row's tags' bottom border to not be chopped in AllListsTable */
max-height: none !important;
/* leave vertical gap between rows of tags */
line-height: 28px;
}

View file

@ -0,0 +1,31 @@
import { Tag } from "antd";
import React from "react";
import { Maintainer } from "../../interfaces/Maintainer";
import styles from "./MaintainerCloud.module.css";
interface Props {
maintainers: Maintainer[];
}
export const MaintainerCloud = (props: Props) =>
props.maintainers && props.maintainers.length ? (
<div className={styles.grow}>
{props.maintainers.map((m: Maintainer, i: number) =>
m.homeUrl ? (
<Tag key={i}>
<a
href={m.homeUrl}
title={`View ${m.name}'s homepage.`}
target="_blank"
rel="noopener noreferrer"
>
{m.name}
</a>
</Tag>
) : (
<Tag key={i}>{m.name}</Tag>
)
)}
</div>
) : null;

View file

@ -0,0 +1,3 @@
import { MaintainerCloud } from "./MaintainerCloud";
export { MaintainerCloud };

View file

@ -0,0 +1,67 @@
import "./maintainers.css";
import { HomeOutlined, MailOutlined, TwitterOutlined } from "@ant-design/icons";
import { Card } from "antd";
import React from "react";
import { Maintainer } from "../../interfaces/Maintainer";
interface Props {
maintainers: Maintainer[];
}
export const Maintainers = (props: Props) =>
props.maintainers && props.maintainers.length ? (
<>
<h3>Maintainer{props.maintainers.length > 1 ? "s" : ""}:</h3>
{props.maintainers.map((m: Maintainer, index: number) => (
<MaintainerComponent key={index} maintainer={m} />
))}
</>
) : null;
interface MaintainerComponentProps {
maintainer: Maintainer;
}
const MaintainerComponent = (props: MaintainerComponentProps) => {
let actions = [];
if (props.maintainer.homeUrl) {
actions.push(
<a
href={props.maintainer.homeUrl}
title={`View ${props.maintainer.name}'s homepage.`}
target="_blank"
rel="noopener noreferrer"
>
<HomeOutlined key="home" />
</a>
);
}
if (props.maintainer.emailAddress) {
actions.push(
<a
href={`mailto:${props.maintainer.emailAddress}`}
title={`Email ${props.maintainer.name}.`}
target="_blank"
rel="noopener noreferrer"
>
<MailOutlined key="mail" />
</a>
);
}
if (props.maintainer.twitterHandle) {
actions.push(
<a
href={`https://twitter.com/${props.maintainer.twitterHandle}`}
title={`${props.maintainer.twitterHandle} on Twitter`}
target="_blank"
rel="noopener noreferrer"
>
<TwitterOutlined key="twitter" />
</a>
);
}
return <Card title={props.maintainer.name} actions={actions} />;
};

View file

@ -0,0 +1,3 @@
import { Maintainers } from "./Maintainers";
export { Maintainers };

View file

@ -0,0 +1,3 @@
.ant-card-body {
display: none;
}

View file

@ -0,0 +1,4 @@
/* allow for 2nd row's bottom border to not be chopped in AllListsTable */
.grow {
max-height: none !important;
}

View file

@ -0,0 +1,26 @@
import React from "react";
import { Software } from "../../interfaces/Software";
import styles from "./SoftwareCloud.module.css";
import { SoftwareIcon } from "./SoftwareIcon";
interface Props {
software: Software[];
showLabel?: boolean;
}
export const SoftwareCloud = (props: Props) =>
props.software && props.software.length ? (
<div className={styles.grow}>
{props.showLabel && <h3>{`Software:`}</h3>}
{props.software.map((s: Software, i: number) =>
s.homeUrl ? (
<a key={i} href={s.homeUrl} target="_blank" rel="noopener noreferrer">
<SoftwareIcon id={s.id} />
</a>
) : (
<SoftwareIcon key={i} id={s.id} />
)
)}
</div>
) : null;

View file

@ -0,0 +1,110 @@
import * as React from "react";
import {
img01,
img02,
img03,
img04,
img05,
img06,
img07,
img08,
img10,
img11,
img12,
img13,
img14,
img15,
img16,
img17,
img18,
img19,
img20,
img21,
img22,
img23,
img24,
img25,
img26,
img27,
img28,
img29,
img30,
img31,
img32,
img33,
img34,
img35,
img36,
img37,
img38,
img39,
img40,
img41,
img42,
img43,
} from "./imgs";
interface Props {
id: number;
}
export const SoftwareIcon = (props: Props) =>
icons[props.id] ? (
<img
src={icons[props.id].image}
height="20"
alt={icons[props.id].imageTitle}
title={icons[props.id].imageTitle}
/>
) : null;
interface Icon {
image: any;
imageTitle: string;
}
const icons: { [id: number]: Icon } = {
1: { image: img01, imageTitle: "uBlock Origin" },
2: { image: img02, imageTitle: "Adblock Plus" },
3: { image: img03, imageTitle: "AdGuard" },
4: { image: img04, imageTitle: "DNS66" },
5: { image: img05, imageTitle: "Nano Adblocker" },
6: { image: img06, imageTitle: "AdBlock" },
7: { image: img07, imageTitle: "AdAway" },
8: { image: img08, imageTitle: "Personal Blocklist" },
10: { image: img10, imageTitle: "Redirector" },
11: { image: img11, imageTitle: "Hosts File Editor" },
12: { image: img12, imageTitle: "Gas Mask" },
13: { image: img13, imageTitle: "MinerBlock" },
14: { image: img14, imageTitle: "Pi-hole" },
15: { image: img15, imageTitle: "uBlock" },
16: { image: img16, imageTitle: "Internet Explorer (TPL)" },
17: { image: img17, imageTitle: "Google Hit Hider by Domain" },
18: { image: img18, imageTitle: "FireHOL" },
19: { image: img19, imageTitle: "Samsung Knox" },
20: { image: img20, imageTitle: "Little Snitch" },
21: { image: img21, imageTitle: "Privoxy" },
22: { image: img22, imageTitle: "Diversion" },
23: { image: img23, imageTitle: "dnsmasq" },
24: { image: img24, imageTitle: "Slimjet" },
25: { image: img25, imageTitle: "uMatrix" },
26: { image: img26, imageTitle: "Blokada" },
27: { image: img27, imageTitle: "hostsmgr" },
28: { image: img28, imageTitle: "personalDNSfilter" },
29: { image: img29, imageTitle: "Unbound" },
30: { image: img30, imageTitle: "BIND" },
31: { image: img31, imageTitle: "AdGuard Home" },
32: { image: img32, imageTitle: "AdNauseam" },
33: { image: img33, imageTitle: "Legacy Unix derivatives" },
34: { image: img34, imageTitle: "Windows command line" },
35: { image: img35, imageTitle: "Shadowsocks" },
36: { image: img36, imageTitle: "ShadowsocksR" },
37: { image: img37, imageTitle: "Shadowrocket" },
38: { image: img38, imageTitle: "DNSRedirector" },
39: { image: img39, imageTitle: "pfBlockerNG" },
40: { image: img40, imageTitle: "Opera's built-in adblocker" },
41: { image: img41, imageTitle: "Surge" },
42: { image: img42, imageTitle: "dnscrypt-proxy" },
43: { image: img43, imageTitle: "SmartDNS" },
};

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="8.5 1 111.5 127" version="1"><g fill="maroon" stroke="#fff" stroke-linecap="round" stroke-width="2"><path stroke-linejoin="round" d="M448 669c-81-57-81-57-81-200 35 0 46 0 81-28m0 228c80-57 80-57 80-200-34 0-46 0-80-28" transform="matrix(-.69452 0 0 .5611 375 -247)"/></g><g stroke="#fff" transform="translate(-18 -17)"><ellipse cx="102" cy="81" fill="none" stroke-width="6" rx="12" ry="12" transform="matrix(1.33333 0 0 1.33333 -42 -31)"/><path fill="#fff" stroke="none" stroke-width="2" d="M82 81c0 11-5 16-16 16s-16-5-16-16V57h8v24c0 7 1 8 8 8s8-1 8-8V57h8z"/></g></svg>

After

Width:  |  Height:  |  Size: 623 B

View file

@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" width="619.25" height="620.5"
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" viewBox="0 0 619.25001 620.49998">
<defs>
<linearGradient osb:paint="solid">
<stop offset="0" stop-color="#fff" />
</linearGradient>
</defs>
<g>
<path fill="#fff" d="M44.45 196.24h525.28V422.5H44.45z" />
<path fill="#fff" stroke="#dadbdb" stroke-width="3"
d="M91.8 528.68l-90.3-90.3V182.1l90.32-90.3 90.3-90.32h255l90.32 90.32 90.3 90.3V438.4l-90.3 90.3L437.1 619h-255l-90.3-90.32zm416.26-20.6l82.2-82.2V194.62l-82.2-82.2-82.2-82.17H194.6l-82.18 82.2-82.16 82.17v231.25l82.17 82.2 82.2 82.18h231.24l82.2-82.18z" />
<path fill="#e00000" stroke="#cb0000" stroke-width="3.004"
d="M111.94 508.64l-81.77-81.56V193.33l81.77-81.56 81.78-81.56h231.82l82.4 82.2 82.4 82.18V425.85l-82.4 82.18-82.4 82.2H193.7l-81.76-81.58zm231.4-94.2c31.23-6.7 48.93-26.18 51.1-56.18 1.5-20.3-4.52-37.73-16.16-46.9-5.57-4.4-17.6-9.9-21.63-9.9-3.28 0-3.16-2.23.18-3.06 10.03-2.5 21.35-14.97 25.48-28 3.1-9.74 3.4-28 .67-37.2-2.66-8.93-10.25-19.1-18.23-24.46-14.4-9.64-31.54-12.17-83-12.24l-32.26-.04v220.62l42.3-.3c33.1-.26 44.3-.77 51.57-2.33zm-50-64.74v-28.24h16.7c18.67 0 27.14 1.8 33.4 7.05 6.02 5.06 7.95 9.92 7.96 20.07 0 13-3.66 19.7-13.55 24.76-7.03 3.6-8.16 3.77-25.93 4.18l-18.58.44V349.7zm0-88.86V236.2l16.6.46c18.4.5 24.16 2.27 29.06 8.96 3.94 5.38 3.73 22.13-.36 28.7-4.98 8-10.8 10.1-29.32 10.66l-15.98.48v-24.62zm-192.5 134.68c2.58-11.5 5.24-23.05 5.9-25.62l1.22-4.7h57.35l2.74 12.2c1.5 6.7 4.14 18.23 5.85 25.62l3.1 13.44h23.22c18.02 0 23.1-.35 22.67-1.56-.3-.86-13.72-50.08-29.84-109.38l-29.3-107.8h-52.8l-3.4 12.8c-4.82 18.05-54.73 201.1-55.57 203.75-.64 2.03.95 2.2 21.72 2.2h22.4l4.7-20.95zm16.3-72.5c1.8-5.94 8.9-37.52 13.74-60.94 3.24-15.7 5.72-25.24 6.2-23.75.42 1.38 3.04 13.2 5.8 26.25 2.78 13.07 6.96 32.05 9.3 42.2l4.24 18.43h-19.97c-18.44 0-19.93-.16-19.32-2.18zm350.4 56.56V342.7h17.43c21.16 0 33.8-2.2 46.23-8.1 17.52-8.3 31.24-26.2 35.42-46.27 2.25-10.78 1.95-33.3-.57-43.12-6.4-24.85-22.82-40.23-48.65-45.5-7.68-1.58-18.28-2-51.7-2h-42.03v218.76h43.86v-36.88zm0-109.37v-32.5h16.68c19.82 0 27.7 1.87 33.87 8.02 5.55 5.55 7.54 13.1 6.83 26.04-1.3 23.42-11.5 30.95-41.96 30.95h-15.44v-32.5z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,5 @@
<svg viewBox="0 0 25.3 25.3" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#68bc71" d="M12.6512986,0 C8.69689131,0 3.92688756,0.929914894 3.87976037e-06,2.9767234 C3.87976037e-06,7.3972766 -0.0542007083,18.4102128 12.6512986,25.935 C25.3570787,18.4102128 25.3031549,7.3972766 25.3031549,2.9767234 C21.3759904,0.929914894 16.6059867,0 12.6512986,0 L12.6512986,0 Z"/>
<path fill="#67b279" d="M12.6383754,25.9273425 C-0.0541639467,18.4027433 3.87976037e-06,7.39577733 3.87976037e-06,2.9767234 C3.9226108,0.932144068 8.68650231,0.00202686119 12.6383754,3.30925217e-06 L12.6383754,25.9273451 Z"/>
<path fill="#fff" d="M12.1898292,17.3046764 L19.8402793,6.99357039 C19.2796699,6.54422358 18.7879353,6.86136327 18.517241,7.10689078 L18.5073636,7.10767773 L12.1284291,13.7434783 L9.72501633,10.8511853 C8.57843655,9.52649092 7.01967536,10.536931 6.65554618,10.8039684 L12.1898292,17.3046764"/>
</svg>

After

Width:  |  Height:  |  Size: 921 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 851 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

View file

@ -0,0 +1,34 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="128.000000pt" height="128.000000pt" viewBox="0 0 128.000000 128.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.15, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,128.000000) scale(0.100000,-0.100000)"
fill="#bf3d27" stroke="none">
<path
d="M500 1204 c-321 -86 -507 -413 -413 -727 43 -146 165 -292 295 -355
327 -159 712 7 817 352 25 82 28 233 6 316 -53 199 -220 364 -420 415 -74 19
-213 18 -285 -1z m242 -85 c222 -46 388 -249 388 -473 0 -138 -46 -251 -140
-346 -242 -243 -640 -175 -786 135 -171 361 147 766 538 684z" />
<path
d="M507 1060 c-157 -50 -270 -183 -299 -350 -43 -250 146 -490 400 -507 166 -12 309 57 403 194 136 197 75 488 -129 617 -104 66 -256 84 -375 46z m133 -190 c0 26z"
fill="white" />
<path
d="M510 1072 c-78 -26 -116 -48 -167 -95 -117 -107 -171 -263 -144 -412
42 -230 246 -390 475 -372 188 14 345 136 397 307 27 91 24 228 -9 310 -44
111 -143 209 -252 251 -78 31 -226 36 -300 11z m86 -264 c-20 -29 -36 -60 -36
-68 0 -9 19 -35 43 -58 l44 -43 46 50 c26 28 47 53 47 56 0 3 -18 33 -40 66
-23 32 -37 59 -33 59 5 0 32 -16 61 -36 42 -27 59 -34 81 -29 31 7 43 -8 34
-43 -3 -13 8 -39 31 -73 20 -30 36 -57 36 -62 0 -4 -27 10 -59 33 -33 22 -63
40 -66 40 -11 0 -95 -83 -95 -94 0 -6 34 -45 76 -87 67 -67 73 -77 59 -89 -14
-12 -26 -3 -96 66 l-79 79 -78 -78 c-79 -79 -98 -89 -109 -61 -4 11 19 40 73
95 l78 79 -38 39 c-21 22 -47 41 -57 44 -11 3 -40 -10 -74 -35 -31 -22 -58
-38 -60 -36 -2 2 13 30 35 62 30 47 37 64 30 81 -11 30 5 45 40 38 21 -4 41 4
81 31 29 19 55 34 57 32 2 -3 -12 -28 -32 -58z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 88.32 129.93" preserveAspectRatio="xMinYMid"><defs><style>.cls-1{fill:url(#New_Gradient_Swatch_1);}.cls-2{fill:#980200;}.cls-3{fill:red;}</style><linearGradient id="New_Gradient_Swatch_1" x1="2.71" y1="20.04" x2="69.77" y2="20.04" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#12b212"/><stop offset="1" stop-color="lime"/></linearGradient></defs><title>NewVortex</title><g id="Layer_2" data-name="Layer 2"><g id="RedBerry"><path id="Leaf_Path" data-name="Leaf Path" class="cls-1" d="M36.56,39.93m0,0C20.34,38.2,4,25.94,2.71,0,27.88,0,41.34,14.9,42.64,38.51c4.76-28.32,27.07-25,27.07-25,1.06,16.05-12.12,25.78-27.07,26.59C38.44,31.25,13.28,9.54,13.28,9.54a.07.07,0,0,0-.11.08S37.45,30.77,36.56,39.93"/><path id="LeftBerry" class="cls-2" d="M44.16,129.93c-1.57-.09-16.22-.65-17.11-17.11-.72-10,7.18-17.37,7.18-27.08C32.44,61.53,0,64.53,0,85.74H0A19.94,19.94,0,0,0,5.83,99.88L30,124.06a19.94,19.94,0,0,0,14.14,5.83"/><path id="BottomBerry" class="cls-3" d="M88.32,85.75c-.09,1.57-.65,16.22-17.11,17.11-10,.72-17.38-7.18-27.08-7.18-24.21,1.79-21.21,34.22,0,34.22h0a19.94,19.94,0,0,0,14.14-5.83L82.46,99.9a19.94,19.94,0,0,0,5.83-14.14"/><path id="RightBerry" class="cls-2" d="M44.16,41.59c1.57.09,16.22.65,17.11,17.11.72,10-7.18,17.37-7.18,27.08,1.79,24.21,34.22,21.21,34.22,0h0a19.94,19.94,0,0,0-5.83-14.14L58.3,47.45a19.94,19.94,0,0,0-14.14-5.83"/><path id="TopBerry" class="cls-3" d="M.08,85.75c.09-1.57.65-16.22,17.11-17.11,10-.72,17.38,7.18,27.08,7.18C68.48,74,65.48,41.6,44.27,41.6h0a19.94,19.94,0,0,0-14.14,5.83L5.94,71.61A19.94,19.94,0,0,0,.11,85.75"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="a">
<stop offset="0" /><stop offset="1" stop-opacity="0" />
</linearGradient>
<radialGradient cx="82.145" cy="81.055" r="22.573" xlink:href="#a" fx="82.145" fy="81.055"
gradientTransform="matrix(1 0 0 1.258 0 -20.947)" gradientUnits="userSpaceOnUse" />
</defs>
<g stroke="#fff">
<path fill="#800000" stroke-width="3" d="M1.5 36.78v54.44l35.28 35.28h54.44l35.28-35.28V36.78L91.22 1.5H36.78z" />
<g fill="#fff" stroke-width="2" style="text-align:center;line-height:125%;-inkscape-font-specification:Ubuntu"
font-size="100.219" letter-spacing="0" word-spacing="0" text-anchor="middle" font-family="Ubuntu">
<path
d="M83.97 90.23c-2.03.56-4.7 1.16-8.05 1.8-3.33.62-7.2.94-11.57.94-3.8 0-7-.62-9.6-1.85-2.6-1.23-4.7-2.97-6.3-5.22-1.58-2.25-2.72-4.9-3.42-7.96-.7-3.05-1.05-6.44-1.05-10.17V37h8.85v28.66c0 6.68.96 11.46 2.86 14.34 1.9 2.88 5.1 4.32 9.6 4.32.96 0 1.94-.03 2.96-.1 1-.07 1.97-.16 2.85-.27.9-.1 1.7-.2 2.44-.3.73-.12 1.26-.24 1.57-.38V37h8.87z"
style="-inkscape-font-specification:Ubuntu" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1 @@
<svg viewBox="0 0 256 252" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M256 131.593c0-20.238-5.232-39.25-14.408-55.772 39.11-88.514-41.906-75.55-46.417-74.667-17.164 3.359-33.044 8.751-47.691 15.587-2.16-.122-4.334-.189-6.523-.189-54.665 0-100.423 38.128-112.134 89.241C57.639 73.47 77.802 60.428 89.877 55.21a274.318 274.318 0 0 0-5.67 5.218c-.618.583-1.213 1.175-1.822 1.761a284.903 284.903 0 0 0-3.638 3.545c-.71.707-1.4 1.42-2.1 2.133a307.038 307.038 0 0 0-3.145 3.235c-.734.77-1.45 1.541-2.17 2.312a286.466 286.466 0 0 0-2.865 3.104c-.73.807-1.45 1.613-2.168 2.422a319.307 319.307 0 0 0-4.796 5.52 331.84 331.84 0 0 0-2.45 2.927c-.714.864-1.426 1.726-2.125 2.589a341.74 341.74 0 0 0-2.234 2.794c-.716.902-1.431 1.802-2.13 2.703-.666.857-1.312 1.711-1.963 2.566-.735.967-1.476 1.933-2.191 2.894-.512.688-1.003 1.37-1.506 2.055a356.843 356.843 0 0 0-12.449 18.128c-.01.014-.02.028-.027.043a367.82 367.82 0 0 0-2.875 4.557c-.05.081-.102.163-.152.246a361.864 361.864 0 0 0-2.719 4.458l-.094.155a369.532 369.532 0 0 0-6.537 11.338C11.69 160.689 6.646 173.807 6.428 174.586c-32.718 116.97 69.396 67.575 83.645 60.201 15.343 7.58 32.615 11.846 50.888 11.846 50.021 0 92.58-31.93 108.422-76.519h-60.446c-8.944 15.11-26.163 25.345-45.945 25.345-28.984 0-52.479-21.96-52.479-49.05h164.54c.624-4.851.947-9.796.947-14.816zM234.51 18.386c9.907 6.687 17.852 17.187 4.207 52.55-13.088-21.048-32.778-37.559-56.181-46.634 10.646-5.141 37.01-16.016 51.974-5.916zM23.98 234.552c-8.07-8.275-9.496-28.429 8.31-65.154 8.985 25.835 26.916 47.482 50.092 61.22-11.526 6.345-42.126 20.629-58.402 3.934zm66.279-119.601c.92-26.329 23.834-47.41 51.987-47.41 28.153 0 51.068 21.081 51.988 47.41H90.259z" fill="#1EBBEE"/></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 617 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 B

View file

@ -0,0 +1,20 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" style="enable-background:new 0 0 56 56;" xml:space="preserve" width="688" height="880"><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none" class="" style=""/>
<style type="text/css">
.st0{fill:#0097A7;}
.st1{fill:#FFFFFF;}
.st2{fill:#00BCD4;}
</style>
<g class="currentLayer" style=""><title>Layer 1</title><g id="Ebene_2" class="">
<g id="Ebene_3">
<path id="XMLID_16_" class="st0" d="M344.1184284363063,799.570125922042 V87.96842249322498 c0,0 162.09059592452832,10.99605281236583 282.4548998288811,50.26766999938664 c0,0 -1.60485738539137,127.24003968594741 -1.60485738539137,252.90921468441405 c0,175.9368449978532 -138.01773514365777,364.44060749555314 -280.85004244348966,398.99963062013137 M687.5579089100594,92.6810165556675 c-146.04202207061462,-48.6968053119058 -332.2054787760135,-62.834587499233294 -343.439480473753,-62.834587499233294 c-11.23400169773959,0 -194.1877436323557,14.13778218732749 -343.439480473753,62.834587499233294 c0,0 0,153.9447393731216 0,306.3186140587623 C0.6789479625532715,612.637228111823 172.39868819942984,838.8417431090629 344.1184284363063,879.6842249835645 c171.7197402368765,-42.41334656198248 343.439480473753,-267.0469968717415 343.439480473753,-479.11372968165387 C687.5579089100594,248.1966206162699 687.5579089100594,92.6810165556675 687.5579089100594,92.6810165556675 "/>
<polygon id="XMLID_15_" class="st1" points="623.3636474609375,600.070322238108 334.4892578125,843.5543626155804 339.30389404296875,45.555075835946354 631.387939453125,109.9605252590718 "/>
</g>
<g id="Ebene_2_1_">
<path id="XMLID_12_" class="st2" d="M344.1184284363063,783.8614790472337 V53.40939936864668 c0,0 157.27602376835424,21.99210562473165 280.85004244348966,62.834587499233294 c0,0 1.60485738539137,130.3817690609091 1.60485738539137,259.19267343433734 c0,175.9368449978532 -138.01773514365777,388.0035778077656 -280.85004244348966,422.56260093234386 M687.5579089100594,75.40150499337834 C539.9110294540535,26.704699681472533 355.3524301340459,-5.701622285414487e-9 344.1184284363063,-5.701622285414487e-9 S149.93068480395064,25.133834993991698 0.6789479625532715,75.40150499337834 c0,0 0,153.9447393731216 0,306.3186140587623 c0,212.06673280991237 170.11488285148516,438.2712478071522 343.439480473753,479.11372968165387 c171.7197402368765,-42.41334656198248 343.439480473753,-267.0469968717415 343.439480473753,-479.11372968165387 C687.5579089100594,227.77537967901907 687.5579089100594,75.40150499337834 687.5579089100594,75.40150499337834 "/>
</g>
<g id="Ebene_1_2_">
<path id="XMLID_7_" class="st2" d="M559.1693180787498,400.5704953019106 c0,-114.67312218610077 -94.68658573809081,-210.49586812243152 -215.0508896424435,-210.49586812243152 v42.41334656198248 c17.653431239305068,25.13383499969332 30.492290322436016,51.83853468686746 40.12143463478424,83.25582843648412 H344.1184284363063 v40.84248187450164 h49.75057894713245 c1.60485738539137,14.13778218732749 3.2097147707827394,26.704699687174156 3.2097147707827394,40.84248187450164 s-1.60485738539137,28.27556437465498 -3.2097147707827394,40.84248187450164 H344.1184284363063 v40.84248187450164 h40.12143463478424 c-9.629144312348222,29.84642906213581 -24.072860780870542,58.1219934367908 -40.12143463478424,83.25582843648412 v42.41334656198248 C461.2730175698763,607.9246340493805 559.1693180787498,515.2436174880113 559.1693180787498,400.5704953019106 zM509.4187391316173,356.58628405244735 c3.2097147707827394,14.13778218732749 4.814572156174111,26.704699687174156 4.814572156174111,40.84248187450164 s-1.60485738539137,28.27556437465498 -4.814572156174111,40.84248187450164 h-73.82343972800298 c1.60485738539137,-14.13778218732749 3.2097147707827394,-26.704699687174156 3.2097147707827394,-40.84248187450164 s-1.60485738539137,-28.27556437465498 -3.2097147707827394,-40.84248187450164 H509.4187391316173 L509.4187391316173,356.58628405244735 zM493.3701652777037,315.7438021779457 h-62.58943803026341 c-8.024286926956847,-26.704699687174156 -16.048573853913695,-51.83853468686746 -28.88743293704465,-73.83064031159911 C438.8050141743971,254.4800793661932 470.9021618822245,281.1847790533673 493.3701652777037,315.7438021779457 zM398.6835795396129,557.6569640499938 c12.838859083130957,-23.562970312212485 22.46800339547917,-48.6968053119058 28.88743293704465,-75.40150499907996 h62.58943803026341 C470.9021618822245,518.385346862973 438.8050141743971,545.0900465501472 398.6835795396129,557.6569640499938 z"/>
<path id="XMLID_2_" class="st1" d="M344.1184284363063,607.9246340493805 V565.511287487398 c-17.653431239305068,-25.13383499969332 -30.492290322436016,-51.83853468686746 -40.12143463478424,-83.25582843648412 H344.1184284363063 v-40.84248187450164 h-49.75057894713245 c-1.60485738539137,-14.13778218732749 -3.2097147707827394,-26.704699687174156 -3.2097147707827394,-40.84248187450164 s1.60485738539137,-28.27556437465498 3.2097147707827394,-40.84248187450164 H344.1184284363063 v-40.84248187450164 h-40.12143463478424 c9.629144312348222,-29.84642906213581 24.072860780870542,-58.1219934367908 40.12143463478424,-83.25582843648412 v-42.41334656198248 c-117.15458913356997,0 -215.0508896424435,92.68101656136912 -215.0508896424435,210.49586812243152 C129.0675387938628,515.2436174880113 226.96383930273635,607.9246340493805 344.1184284363063,607.9246340493805 zM251.0367000836069,356.58628405244735 c-1.60485738539137,14.13778218732749 -3.2097147707827394,26.704699687174156 -3.2097147707827394,40.84248187450164 s1.60485738539137,28.27556437465498 3.2097147707827394,40.84248187450164 h-72.21858234261163 c-3.2097147707827394,-14.13778218732749 -4.814572156174111,-26.704699687174156 -4.814572156174111,-40.84248187450164 s1.60485738539137,-28.27556437465498 4.814572156174111,-40.84248187450164 H251.0367000836069 L251.0367000836069,356.58628405244735 zM289.5532773329998,241.91316186634654 c-12.838859083130957,23.562970312212485 -22.46800339547917,48.6968053119058 -28.88743293704465,73.83064031159911 h-62.58943803026341 C217.33469499038816,281.1847790533673 249.43184269821558,254.4800793661932 289.5532773329998,241.91316186634654 zM194.86669159490896,482.25545905091394 h62.58943803026341 c8.024286926956847,26.704699687174156 16.048573853913695,51.83853468686746 28.88743293704465,75.40150499907996 C249.43184269821558,545.0900465501472 217.33469499038816,518.385346862973 194.86669159490896,482.25545905091394 z"/>
</g>
</g></g></svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

View file

@ -0,0 +1,18 @@
<svg viewBox="120 0 284 313" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs><linearGradient x1="-1.11022302e-14%" y1="50%" x2="100%" y2="50%" id="linearGradient-1"><stop stop-color="#FFF" offset="0%"></stop> <stop stop-color="#AFAFAF" offset="100%"></stop></linearGradient> <filter x="-1.7%" y="-18.4%" width="103.4%" height="136.8%" filterUnits="objectBoundingBox" id="filter-2"><feGaussianBlur stdDeviation="2" in="SourceGraphic"></feGaussianBlur></filter> <linearGradient x1="4.23356495%" y1="71.3808712%" x2="100%" y2="71.3808712%" id="linearGradient-3"><stop stop-color="#878787" offset="0%"></stop> <stop stop-color="#ACACAC" offset="27.2836243%"></stop> <stop stop-color="#E7E7E7" offset="100%"></stop></linearGradient> <linearGradient x1="-1.11022302e-14%" y1="50%" x2="98.4181113%" y2="63.3039749%" id="linearGradient-4"><stop stop-color="#A4A4A4" offset="0%"></stop> <stop stop-color="#D5D5D5" offset="34.9077066%"></stop> <stop stop-color="#E2E2E2" offset="64.1266956%"></stop> <stop stop-color="#F6F6F6" offset="100%"></stop></linearGradient> <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-5"><stop stop-color="#DDF6FF" offset="0%"></stop> <stop stop-color="#66B574" offset="100%"></stop></linearGradient> <filter x="-1.0%" y="-10.4%" width="102.0%" height="120.9%" filterUnits="objectBoundingBox" id="filter-6"><feGaussianBlur stdDeviation="1" in="SourceGraphic"></feGaussianBlur></filter> <linearGradient x1="100%" y1="50%" x2="0%" y2="50%" id="linearGradient-7"><stop stop-color="#999999" offset="0%"></stop> <stop stop-color="#636363" offset="100%"></stop></linearGradient></defs>
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(4, 0)">
<g transform="translate(0, 59)">
<g transform="translate(0, 47)">
<ellipse fill="url(#linearGradient-3)" cx="257.723176" cy="179.693089" rx="116.446352" ry="27.8760163"></ellipse>
<path d="M116.469235,168.113821 L116.446352,168.113821 L116.455558,167.957047 C116.449426,167.866473 116.446352,167.775776 116.446352,167.684959 C116.446352,167.436234 116.46941,167.188408 116.515189,166.941538 L125.864807,7.7195122 L389.581545,7.7195122 L398.931163,166.941538 C398.976942,167.188408 399,167.436234 399,167.684959 C399,167.775776 398.996926,167.866473 398.990794,167.957047 L399,168.113821 L398.977117,168.113821 C397.60707,180.942906 334.89018,191.272358 257.723176,191.272358 C180.556172,191.272358 117.839282,180.942906 116.469235,168.113821 Z" fill="url(#linearGradient-4)"></path>
<ellipse fill="#EDEDED" cx="257.723176" cy="7.7195122" rx="131.858369" ry="7.7195122"></ellipse></g></g>
<g opacity="0.43312872" transform="translate(239, 250)" fill="url(#linearGradient-7)" fill-rule="nonzero"><path d="M18.4987789,0 C11.9572877,0 5.41627296,2.73569054 0.436815806,8.20680991 C-0.145605269,8.84655448 -0.145605269,9.88049936 0.436815806,10.5201916 C0.727263887,10.8392264 1.10863485,10.9996074 1.4899105,10.9996074 C1.8712338,10.9996074 2.2524618,10.840849 2.54290988,10.5201916 C11.3384117,0.859012275 25.6570017,0.857337277 34.4569829,10.5201916 C35.039404,11.1599361 35.9808465,11.1599361 36.5632199,10.5201916 C37.1455934,9.88049936 37.1455934,8.84655448 36.5632199,8.20680991 C31.5822855,2.73569054 25.0403178,0 18.4987789,0 Z"></path>
<path d="M18.9978615,9 C14.6139291,9.00143707 10.488189,10.9344455 7.3881459,14.4440679 C6.87061803,15.0295319 6.87061803,15.9758452 7.3881459,16.5612613 C7.64629579,16.8532268 7.98511753,17 8.32393926,17 C8.66280335,17 9.00166743,16.8532268 9.26117244,16.5612613 C11.8607537,13.6176545 15.3207076,11.99606 19.0017574,11.9945271 L19.0047217,11.9945271 C22.6831036,11.9945271 26.1392886,13.6146366 28.7402674,16.5567584 C29.2577952,17.1422224 30.0943262,17.1422224 30.6118541,16.5567584 C31.129382,15.9713423 31.129382,15.025029 30.6118541,14.4396129 C27.5118957,10.9314276 23.3886964,9 19.0034513,9 L18.9978615,9 Z"></path>
<path d="M19.0008587,17 C16.9433639,17 14.8856263,17.7456965 13.3180542,19.2371879 C12.8939819,19.6408894 12.8939819,20.2934977 13.3180542,20.6971991 C13.5295179,20.8985378 13.8071442,20.9996862 14.0848052,20.9996862 C14.3613213,20.9996862 14.6389129,20.8985378 14.8525276,20.6971991 C17.1396661,18.522708 20.8614962,18.5206269 23.1485652,20.6971991 C23.5726722,21.1009336 24.2579256,21.1009336 24.6819978,20.6971991 C25.1060007,20.2934977 25.1060007,19.6408894 24.6819978,19.2371879 C23.1154666,17.7456965 21.0583188,17 19.0008587,17 Z"></path></g>
<g transform="translate(233, 0)">
<g>
<g>
<path d="M25.3223802,0 C17.4073821,0 7.85991568,1.85982979 7.7655876e-06,5.95344681 C7.7655876e-06,14.7945532 -0.108486171,36.8204255 25.3223802,51.87 C50.7538088,36.8204255 50.645877,14.7945532 50.645877,5.95344681 C42.7854069,1.85982979 33.2379405,0 25.3223802,0 L25.3223802,0 Z" fill="#68BC71"></path>
<path d="M25.2965138,51.854685 C-0.108412591,36.8054866 7.76558755e-06,14.7915547 7.76558755e-06,5.95344681 C7.85135547,1.86428814 17.3865879,0.00405372237 25.2965138,6.61850441e-06 L25.2965138,51.8546902 Z" fill="#67B279"></path>
</g><path d="M24.3987198,34.6093529 L39.7115833,13.9871408 C38.5894878,13.0884472 37.6052497,13.7227265 37.0634379,14.2137816 L37.0436676,14.2153555 L24.2758237,27.4869566 L19.4652399,21.7023705 C17.1702874,19.0529818 14.0503275,21.0738621 13.3214998,21.6079369 L24.3987198,34.6093529" fill="#FFF"></path></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 693 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 881 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

View file

@ -0,0 +1,9 @@
declare module "*.svg" {
const content: any;
export default content;
}
declare module "*.png" {
const content: any;
export default content;
}

View file

@ -0,0 +1,87 @@
import img01 from "./01-uBlock-Origin.svg";
import img02 from "./02-Adblock-Plus.svg";
import img03 from "./03-AdGuard.svg";
import img04 from "./04-DNS66.png";
import img05 from "./05-Nano-Adblocker.png";
import img06 from "./06-AdBlock.png";
import img07 from "./07-AdAway.png";
import img08 from "./08-Personal-Blocklist.png";
import img10 from "./10-Redirector.png";
import img11 from "./11-Hosts-File-Editor.png";
import img12 from "./12-Gas-Mask.png";
import img13 from "./13-MinerBlock.svg";
import img14 from "./14-Pi-hole.svg";
import img15 from "./15-uBlock.svg";
import img16 from "./16-Internet-Explorer-TPL.svg";
import img17 from "./17-Google-Hit-Hider-by-Domain.png";
import img18 from "./18-FireHOL.png";
import img19 from "./19-Samsung-Knox.png";
import img20 from "./20-Little-Snitch.png";
import img21 from "./21-Privoxy.png";
import img22 from "./22-Diversion.png";
import img23 from "./23-dnsmasq.png";
import img24 from "./24-Slimjet.png";
import img25 from "./25-uMatrix.png";
import img26 from "./26-Blokada.png";
import img27 from "./27-hostsmgr.png";
import img28 from "./28-personalDNSfilter.svg";
import img29 from "./29-Unbound.png";
import img30 from "./30-BIND.png";
import img31 from "./31-AdGuard-Home.svg";
import img32 from "./32-AdNauseam.png";
import img33 from "./33-Legacy-Unix-Derivatives.png";
import img34 from "./34-Windows-command-line.png";
import img35 from "./35-Shadowsocks.png";
import img36 from "./36-ShadowsocksR.png";
import img37 from "./37-Shadowrocket.png";
import img38 from "./38-DNSRedirector.png";
import img39 from "./39-pfBlockerNG.png";
import img40 from "./40-Opera.png";
import img41 from "./41-Surge.png";
import img42 from "./42-dnscrypt-proxy.png";
import img43 from "./43-SmartDNS.png";
export {
img01,
img02,
img03,
img04,
img05,
img06,
img07,
img08,
img10,
img11,
img12,
img13,
img14,
img15,
img16,
img17,
img18,
img19,
img20,
img21,
img22,
img23,
img24,
img25,
img26,
img27,
img28,
img29,
img30,
img31,
img32,
img33,
img34,
img35,
img36,
img37,
img38,
img39,
img40,
img41,
img42,
img43
};

View file

@ -0,0 +1,4 @@
import { SoftwareCloud } from "./SoftwareCloud";
import { SoftwareIcon } from "./SoftwareIcon";
export { SoftwareCloud, SoftwareIcon };

View file

@ -0,0 +1,7 @@
.grow {
/* allow for 2nd row's tags' bottom border to not be chopped in AllListsTable */
max-height: none !important;
/* leave vertical gap between rows of tags */
line-height: 28px;
}

View file

@ -0,0 +1,22 @@
import { Tag } from "antd";
import React from "react";
import { Tag as TagInterface } from "../../interfaces/Tag";
import styles from "./TagCloud.module.css";
interface Props {
tags: TagInterface[];
showLabel?: boolean;
}
export const TagCloud = (props: Props) =>
props.tags && props.tags.length ? (
<div className={styles.grow}>
{props.showLabel && <h3>{`Tag${props.tags.length > 1 ? "s" : ""}:`}</h3>}
{props.tags.map((t: TagInterface, i: number) => (
<Tag key={i} title={t.description}>
{t.name}
</Tag>
))}
</div>
) : null;

View file

@ -0,0 +1,3 @@
import { TagCloud } from "./TagCloud";
export { TagCloud };

View file

@ -0,0 +1 @@
export const SlugifyOptions = { remove: /[^a-zA-Z0-9- ]/g, lower: true };

View file

@ -0,0 +1,21 @@
import { useLanguages } from "./useLanguages";
import { useLicenses } from "./useLicenses";
import { useLists } from "./useLists";
import { useMaintainers } from "./useMaintainers";
import { useSearchColumnFilter } from "./useSearchColumnFilter";
import { useSoftware } from "./useSoftware";
import { useSyntaxes } from "./useSyntaxes";
import { useTablePageSizer } from "./useTablePageSizer";
import { useTags } from "./useTags";
export {
useLanguages,
useLicenses,
useLists,
useMaintainers,
useSearchColumnFilter,
useSoftware,
useSyntaxes,
useTablePageSizer,
useTags,
};

View file

@ -0,0 +1,11 @@
import { useEffect, useState } from "react";
export const useApiData = <T extends {}>(url: string) => {
const [data, setData] = useState<T>();
useEffect(() => {
const fetchData = async () =>
(await fetch(url)).json().then((r) => setData(r));
fetchData();
}, [url]);
return data;
};

View file

@ -0,0 +1,7 @@
import { Language } from "../interfaces/Language";
import { useApiData } from "./useApiData";
export const useLanguages = () =>
(
useApiData<Language[]>("/api/v1/languages") || []
).sort((a: Language, b: Language) => a.name.localeCompare(b.name));

View file

@ -0,0 +1,7 @@
import { License } from "../interfaces/License";
import { useApiData } from "./useApiData";
export const useLicenses = () =>
(
useApiData<License[]>("/api/v1/licenses") || []
).sort((a: License, b: License) => a.name.localeCompare(b.name));

View file

@ -0,0 +1,11 @@
import slugify from "slugify";
import { SlugifyOptions } from "../constants";
import { List } from "../interfaces/List";
import { useApiData } from "./useApiData";
export const useLists = () => {
const lists = useApiData<List[]>("/api/v1/lists");
lists && lists.forEach((l) => (l.slug = slugify(l.name, SlugifyOptions)));
return (lists || []).sort((a: List, b: List) => a.name.localeCompare(b.name));
};

Some files were not shown because too many files have changed in this diff Show more