Revert "lint via tslint/prettier"

This reverts commit 01c62e5336.
This commit is contained in:
Collin M. Barrett 2019-09-22 18:01:08 -05:00
parent 1048b3c8b0
commit 3caee2fcc5
55 changed files with 569 additions and 865 deletions

View file

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

View file

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

View file

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

View file

@ -1,15 +1,11 @@
import React from "react";
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>
);
props.descriptionSourceUrl
? <blockquote cite={props.descriptionSourceUrl}>{props.description}</blockquote>
: <p>{props.description}</p>;

View file

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

View file

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

View file

@ -1,13 +1,13 @@
import { Button, Icon } from "antd";
import React from "react";
import { RouteComponentProps } from "react-router-dom";
import slugify from "slugify";
import { Button, Icon } from 'antd';
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
import slugify from 'slugify';
import { SlugifyOptions } from "../constants";
import { SlugifyOptions } from '../constants';
interface Props {
listName: string;
}
};
export const ListInfoButton = (props: RouteComponentProps & Props) => {
const listPath = `/lists/${slugify(props.listName, SlugifyOptions)}`;
@ -15,13 +15,10 @@ export const ListInfoButton = (props: RouteComponentProps & Props) => {
<Button
type="primary"
title={`View more information about ${props.listName}.`}
onClick={() =>
props.location.pathname === listPath
? props.history.push("/")
: props.history.push(listPath)
}
>
onClick={() => props.location.pathname === listPath
? props.history.push("/")
: props.history.push(listPath)} >
<Icon type="info-circle" />
</Button>
);
};
};

View file

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

View file

@ -1,50 +1,48 @@
import { Button } from "antd";
import { ButtonType } from "antd/lib/button";
import React from "react";
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 ? (
<>
props.viewUrl
? <>
<SubscribeButton
name={props.name}
viewUrl={props.viewUrl}
text="Subscribe"
/>
<MirrorButtons name={props.name} viewUrlMirrors={props.viewUrlMirrors} />
text="Subscribe" />
<MirrorButtons
name={props.name}
viewUrlMirrors={props.viewUrlMirrors} />
</>
) : null;
: null;
interface MirrorButtonsProps {
name: string;
viewUrlMirrors: string[];
}
};
const MirrorButtons = (props: MirrorButtonsProps) => (
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})`}
/>
))
? 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);
@ -55,8 +53,7 @@ const SubscribeButton = (props: SubscribeButtonProps) => {
icon="import"
type={buttonProps.type}
href={buttonProps.href}
title={buttonProps.title}
>
title={buttonProps.title}>
{props.text}
</Button>
);
@ -77,27 +74,22 @@ const buildButtonProps = (name: string, viewUrl: string) => {
if (viewUrl.includes(".onion/")) {
type = "dashed";
prefixes.push("TOR");
}
};
if (viewUrl.includes("http://")) {
type = "danger";
prefixes.push("INSECURE");
}
};
// Software protocols
if (viewUrl.includes(".tpl")) {
disabled = true; // IE not supported by FilterLists
}
if (
viewUrl.includes(".lsrules") ||
viewUrl.includes("?hostformat=littlesnitch")
) {
};
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}`;
const title = `${prefixes.length ? prefixes.join(" | ") + " | " : ""}${message}`;
return { disabled, type, href, title };
};
};

View file

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

View file

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

View file

@ -1,24 +1,19 @@
import { Tag } from "antd";
import React from "react";
import { Tag } from 'antd';
import React from 'react';
import { Language } from "../../interfaces/Language";
import styles from "./LanguageCloud.module.css";
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>
))}
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;
: null;

View file

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

View file

@ -1,27 +1,27 @@
import "./listInfoDrawer.css";
import './listInfoDrawer.css';
import { Divider, Drawer } from "antd";
import ButtonGroup from "antd/lib/button/button-group";
import React, { useEffect, useState } from "react";
import { RouteComponentProps } from "react-router-dom";
import { Divider, Drawer } from 'antd';
import ButtonGroup from 'antd/lib/button/button-group';
import React, { useEffect, useState } from 'react';
import { RouteComponentProps } from 'react-router-dom';
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 { Description } from "../Description";
import { LanguageCloud } from "../languageCloud";
import { LicenseTag } from "../LicenseTag";
import { LinkButton } from "../LinkButton";
import { Maintainers } from "../maintainers";
import { PublishedDate } from "../PublishedDate";
import { SoftwareCloud } from "../softwareCloud";
import { SubscribeButtons } from "../SubscribeButtons";
import { SyntaxTag } from "../SyntaxTag";
import { TagCloud } from "../tagCloud";
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 { Description } from '../Description';
import { LanguageCloud } from '../languageCloud';
import { LicenseTag } from '../LicenseTag';
import { LinkButton } from '../LinkButton';
import { Maintainers } from '../maintainers';
import { PublishedDate } from '../PublishedDate';
import { SoftwareCloud } from '../softwareCloud';
import { SubscribeButtons } from '../SubscribeButtons';
import { SyntaxTag } from '../SyntaxTag';
import { TagCloud } from '../tagCloud';
interface Props {
list: List;
@ -31,17 +31,14 @@ interface Props {
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);
const updateTitle = () => document.title = props.list.name + " | " + originalTitle;
updateTitle();
return () => {
document.title = originalTitle;
};
return () => { document.title = originalTitle; };
}, [props.list, originalTitle]);
return (
<Drawer
@ -51,29 +48,29 @@ export const ListInfoDrawer = (props: RouteComponentProps & Props) => {
mask={false}
title={props.list.name}
destroyOnClose={true}
onClose={() => props.history.push("/")}
>
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 && (
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 && (
showLabel={true} />}
{props.syntax &&
<SyntaxTag
name={props.syntax.name}
definitionUrl={props.syntax.definitionUrl}
showLabel={true}
/>
)}
<SoftwareCloud software={props.software} showLabel={true} />
showLabel={true} />}
<SoftwareCloud
software={props.software}
showLabel={true} />
<PublishedDate publishedDate={props.list.publishedDate} />
<Maintainers maintainers={props.maintainers} />
<Divider />
@ -81,65 +78,54 @@ export const ListInfoDrawer = (props: RouteComponentProps & Props) => {
<SubscribeButtons
name={props.list.name}
viewUrl={props.list.viewUrl}
viewUrlMirrors={props.list.viewUrlMirrors}
/>
viewUrlMirrors={props.list.viewUrlMirrors} />
<LinkButton
url={props.list.viewUrl}
text="View"
title={`View ${props.list.name} in its raw format`}
icon="search"
/>
icon="search" />
<LinkButton
url={props.list.homeUrl}
text="Home"
title={`View ${props.list.name}'s homepage.`}
icon="home"
/>
icon="home" />
<LinkButton
url={props.list.policyUrl}
text="Policy"
title={`View the types of rules that ${props.list.name} includes.`}
icon="file-exclamation"
/>
{props.list.emailAddress && (
icon="file-exclamation" />
{props.list.emailAddress &&
<LinkButton
url={`mailto:${props.list.emailAddress}`}
text="Email"
title={`Email ${props.list.name}.`}
icon="mail"
/>
)}
icon="mail" />}
<LinkButton
url={props.list.issuesUrl}
text="GitHub Issues"
title={`View the GitHub Issues for ${props.list.name}.`}
icon="github"
/>
icon="github" />
<LinkButton
url={props.list.submissionUrl}
text="Submit a Rule"
title={`Submit a new rule to be included in ${props.list.name}.`}
icon="form"
/>
icon="form" />
<LinkButton
url={props.list.forumUrl}
text="Forum"
title={`View the forum for ${props.list.name}.`}
icon="team"
/>
icon="team" />
<LinkButton
url={props.list.chatUrl}
text="Chat"
title={`Enter the chat room for ${props.list.name}.`}
icon="message"
/>
icon="message" />
<LinkButton
url={props.list.donateUrl}
text="Donate"
title={`Donate to the maintainer of ${props.list.name}.`}
icon="dollar"
/>
icon="dollar" />
</ButtonGroup>
</Drawer>
);
};
};

View file

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

View file

@ -1,19 +1,14 @@
import React from "react";
import {
Redirect,
Route,
RouteComponentProps,
StaticContext
} from "react-router";
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";
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[];
@ -23,52 +18,22 @@ interface Props {
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
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;
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

@ -1,33 +1,29 @@
import "./listsTable.css";
import './listsTable.css';
import { Table, Tag } from "antd";
import {
PaginationConfig,
SorterResult,
TableCurrentDataSource
} from "antd/lib/table";
import React, { useEffect, useState } from "react";
import { RouteComponentProps } from "react-router";
import { Table, Tag } from 'antd';
import { PaginationConfig, SorterResult, TableCurrentDataSource } from 'antd/lib/table';
import React, { useEffect, useState } from 'react';
import { RouteComponentProps } from 'react-router';
import { useSearchColumnFilter, useTablePageSizer } from "../../hooks";
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 as TagInterface } from "../../interfaces/Tag";
import { nameof } from "../../utils";
import { Description } from "../Description";
import { LanguageCloud } from "../languageCloud";
import { LicenseTag } from "../LicenseTag";
import { ListInfoButton } from "../ListInfoButton";
import { MaintainerCloud } from "../maintainerCloud";
import { SoftwareCloud, SoftwareIcon } from "../softwareCloud";
import { SyntaxTag } from "../SyntaxTag";
import { TagCloud } from "../tagCloud";
import { arraySorter } from "./arraySorter";
import styles from "./ListsTable.module.css";
import { useSearchColumnFilter, useTablePageSizer } from '../../hooks';
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 as TagInterface } from '../../interfaces/Tag';
import { nameof } from '../../utils';
import { Description } from '../Description';
import { LanguageCloud } from '../languageCloud';
import { LicenseTag } from '../LicenseTag';
import { ListInfoButton } from '../ListInfoButton';
import { MaintainerCloud } from '../maintainerCloud';
import { SoftwareCloud, SoftwareIcon } from '../softwareCloud';
import { SyntaxTag } from '../SyntaxTag';
import { TagCloud } from '../tagCloud';
import { arraySorter } from './arraySorter';
import styles from './ListsTable.module.css';
interface Props {
lists: List[];
@ -37,24 +33,13 @@ interface Props {
software: Software[];
syntaxes: Syntax[];
tags: TagInterface[];
}
};
export const ListsTable = (props: RouteComponentProps & Props) => {
const {
lists,
languages,
licenses,
maintainers,
software,
syntaxes,
tags,
...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 searchDescriptionColumn = useSearchColumnFilter<List>(nameof<List>("description"));
const [visibleLists, setVisibleLists] = useState<List[]>(lists);
useEffect(() => {
setVisibleLists(lists);
@ -71,13 +56,8 @@ export const ListsTable = (props: RouteComponentProps & Props) => {
pageSize: tablePageSize.pageSize
}}
scroll={{ x: tablePageSize.isNarrowWindow ? undefined : 1892 }}
onChange={(
_pagination: PaginationConfig,
_filters: Record<keyof List, string[]>,
_sorter: SorterResult<List>,
extra: TableCurrentDataSource<List>
) => setVisibleLists(extra.currentDataSource)}
>
onChange={(_pagination: PaginationConfig, _filters: Record<keyof List, string[]>, _sorter: SorterResult<List>, extra: TableCurrentDataSource<List>) =>
setVisibleLists(extra.currentDataSource)}>
<Table.Column<List>
title="Info"
key="Info"
@ -85,10 +65,8 @@ export const ListsTable = (props: RouteComponentProps & Props) => {
width={tablePageSize.isNarrowWindow ? 43 : undefined}
className={styles.nogrow}
fixed={tablePageSize.isNarrowWindow ? undefined : "left"}
render={(_id: number, list: List) => (
<ListInfoButton listName={list.name} {...routeComponentProps} />
)}
/>
render={(_id: number, list: List) =>
<ListInfoButton listName={list.name} {...routeComponentProps} />} />
<Table.Column<List>
title="Name"
key="Name"
@ -101,10 +79,11 @@ export const ListsTable = (props: RouteComponentProps & Props) => {
filterDropdown={searchNameColumn.filterDropdown}
filterIcon={searchNameColumn.filterIcon}
onFilter={searchNameColumn.onFilter}
render={(name: string) => <div>{name}</div>}
/>
{tablePageSize.isNarrowWindow ? null : (
<Table.Column<List>
render={(name: string) =>
<div>{name}</div>} />
{tablePageSize.isNarrowWindow
? null
: <Table.Column<List>
title="Description"
key="Description"
dataIndex={nameof<List>("description")}
@ -112,63 +91,38 @@ export const ListsTable = (props: RouteComponentProps & Props) => {
filterDropdown={searchDescriptionColumn.filterDropdown}
filterIcon={searchDescriptionColumn.filterIcon}
onFilter={searchDescriptionColumn.onFilter}
render={(description: string, list: List) => (
render={(description: string, list: List) =>
<Description
description={description}
descriptionSourceUrl={list.descriptionSourceUrl}
/>
)}
/>
)}
{tablePageSize.isNarrowWindow ? null : (
<Table.Column<List>
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.includes(l.syntaxId))
.map(s => s.id);
const getSoftwareIds = (l: List) => software.filter((s: Software) => s.syntaxIds.includes(l.syntaxId)).map(s => s.id);
return arraySorter(getSoftwareIds(a), getSoftwareIds(b), software);
}}
width={143}
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
}
)
</>
),
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)
}
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.includes(syntaxId)
)}
/>
) : null
}
/>
)}
{tablePageSize.isNarrowWindow ? null : (
<Table.Column<List>
syntaxId
? <SoftwareCloud software={software.filter((s: Software) => s.syntaxIds.includes(syntaxId))} />
: null} />}
{tablePageSize.isNarrowWindow
? null
: <Table.Column<List>
title="Syntax"
key="Syntax"
dataIndex={nameof<List>("syntaxId")}
@ -184,73 +138,48 @@ export const ListsTable = (props: RouteComponentProps & Props) => {
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
}
)
</>
),
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
}
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>
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)
}
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
}
)
</>
),
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
}
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>
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")}
@ -258,70 +187,45 @@ export const ListsTable = (props: RouteComponentProps & Props) => {
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
}
)
</>
),
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
}
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>
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)
}
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
}
)
</>
),
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
}
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>
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")}
@ -337,34 +241,21 @@ export const ListsTable = (props: RouteComponentProps & Props) => {
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
}
)
</>
),
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
}
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;
}}
/>
)}
return license
? <LicenseTag name={license.name} descriptionUrl={license.descriptionUrl} showLabel={false} />
: null
}} />}
</Table>
);
};
};

View file

@ -1,17 +1,9 @@
import React from "react";
import { RouteComponentProps } from "react-router-dom";
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";
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();
@ -31,8 +23,7 @@ export const ListsTableHoc = (props: RouteComponentProps) => {
software={software}
syntaxes={syntaxes}
tags={tags}
{...props}
/>
{...props} />
<ListDrawer
lists={lists}
languages={languages}
@ -40,8 +31,7 @@ export const ListsTableHoc = (props: RouteComponentProps) => {
maintainers={maintainers}
software={software}
syntaxes={syntaxes}
tags={tags}
/>
tags={tags} />
</>
);
};
};

View file

@ -1,30 +1,18 @@
interface ArraySortableEntity {
id: number;
name: string;
}
};
export const arraySorter = (
a: number[],
b: number[],
entities: ArraySortableEntity[]
) =>
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()
? 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
: -1
: 1;
: 1;

View file

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

View file

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

View file

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

View file

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

View file

@ -1,23 +1,22 @@
import "./maintainers.css";
import './maintainers.css';
import { Card, Icon } from "antd";
import React from "react";
import { Card, Icon } from 'antd';
import React from 'react';
import { Maintainer } from "../../interfaces/Maintainer";
import { Maintainer } from '../../interfaces/Maintainer';
interface Props {
maintainers: Maintainer[];
}
};
export const Maintainers = (props: Props) =>
props.maintainers && props.maintainers.length ? (
<>
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} />
))}
{props.maintainers.map((m: Maintainer, index: number) =>
<MaintainerComponent key={index} maintainer={m} />)}
</>
) : null;
: null;
interface MaintainerComponentProps {
maintainer: Maintainer;
@ -27,39 +26,35 @@ const MaintainerComponent = (props: MaintainerComponentProps) => {
let actions = [];
if (props.maintainer.homeUrl) {
actions.push(
<a
href={props.maintainer.homeUrl}
<a href={props.maintainer.homeUrl}
title={`View ${props.maintainer.name}'s homepage.`}
target="_blank"
rel="noopener noreferrer"
>
rel="noopener noreferrer">
<Icon type="home" key="home" />
</a>
);
}
if (props.maintainer.emailAddress) {
actions.push(
<a
href={`mailto:${props.maintainer.emailAddress}`}
<a href={`mailto:${props.maintainer.emailAddress}`}
title={`Email ${props.maintainer.name}.`}
target="_blank"
rel="noopener noreferrer"
>
rel="noopener noreferrer">
<Icon type="mail" key="mail" />
</a>
);
}
if (props.maintainer.twitterHandle) {
actions.push(
<a
href={`https://twitter.com/${props.maintainer.twitterHandle}`}
<a href={`https://twitter.com/${props.maintainer.twitterHandle}`}
title={`${props.maintainer.twitterHandle} on Twitter`}
target="_blank"
rel="noopener noreferrer"
>
rel="noopener noreferrer">
<Icon type="twitter" key="twitter" />
</a>
);
}
return <Card title={props.maintainer.name} actions={actions} />;
};
return <Card
title={props.maintainer.name}
actions={actions} />;
}

View file

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

View file

@ -1,26 +1,26 @@
import React from "react";
import React from 'react';
import { Software } from "../../interfaces/Software";
import styles from "./SoftwareCloud.module.css";
import { SoftwareIcon } from "./SoftwareIcon";
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.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">
s.homeUrl
? <a key={i}
href={s.homeUrl}
target="_blank"
rel="noopener noreferrer">
<SoftwareIcon id={s.id} />
</a>
) : (
<SoftwareIcon key={i} id={s.id} />
)
)}
: <SoftwareIcon key={i} id={s.id} />)}
</div>
) : null;
: null;

View file

@ -1,4 +1,4 @@
import * as React from "react";
import * as React from 'react';
import {
img01,
@ -37,28 +37,27 @@ import {
img35,
img36,
img37
} from "./imgs";
} from './imgs';
interface Props {
id: number;
}
};
export const SoftwareIcon = (props: Props) =>
icons[props.id] ? (
<img
icons[props.id]
? <img
src={icons[props.id].image}
height="20"
alt={icons[props.id].imageTitle}
title={icons[props.id].imageTitle}
/>
) : null;
title={icons[props.id].imageTitle} />
: null;
interface Icon {
image: any;
imageTitle: string;
}
};
const icons: { [id: number]: Icon } = {
const icons: { [id: number]: Icon; } = {
1: { image: img01, imageTitle: "uBlock Origin" },
2: { image: img02, imageTitle: "Adblock Plus" },
3: { image: img03, imageTitle: "AdGuard" },
@ -95,4 +94,4 @@ const icons: { [id: number]: Icon } = {
35: { image: img35, imageTitle: "Shadowsocks" },
36: { image: img36, imageTitle: "ShadowsocksR" },
37: { image: img37, imageTitle: "Shadowrocket" }
};
};

View file

@ -6,4 +6,4 @@
declare module "*.png" {
const content: any;
export default content;
}
}

View file

@ -1,39 +1,39 @@
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.png";
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 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.png';
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';
export {
img01,
@ -72,4 +72,4 @@ export {
img35,
img36,
img37
};
};

View file

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

View file

@ -1,22 +1,19 @@
import { Tag } from "antd";
import React from "react";
import { Tag } from 'antd';
import React from 'react';
import { Tag as TagInterface } from "../../interfaces/Tag";
import styles from "./TagCloud.module.css";
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.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>
))}
{props.tags.map((t: TagInterface, i: number) =>
<Tag key={i} title={t.description}>{t.name}</Tag>)}
</div>
) : null;
: null;

View file

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

View file

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

View file

@ -1,12 +1,12 @@
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";
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,
@ -18,4 +18,4 @@ export {
useSyntaxes,
useTablePageSizer,
useTags
};
};

View file

@ -1,11 +1,10 @@
import { useEffect, useState } from "react";
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));
const fetchData = async () => (await fetch(url)).json().then(r => setData(r));
fetchData();
}, [url]);
return data;
};
};

View file

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

View file

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

View file

@ -1,11 +1,12 @@
import slugify from "slugify";
import slugify from 'slugify';
import { SlugifyOptions } from "../constants";
import { List } from "../interfaces/List";
import { useApiData } from "./useApiData";
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));
};
lists && lists.forEach(l => l.slug = slugify(l.name, SlugifyOptions));
return (lists || [])
.sort((a: List, b: List) => a.name.localeCompare(b.name));
};

View file

@ -1,7 +1,5 @@
import { Maintainer } from "../interfaces/Maintainer";
import { useApiData } from "./useApiData";
import { Maintainer } from '../interfaces/Maintainer';
import { useApiData } from './useApiData';
export const useMaintainers = () =>
(useApiData<Maintainer[]>("/api/v1/maintainers") || []).sort(
(a: Maintainer, b: Maintainer) => a.name.localeCompare(b.name)
);
export const useMaintainers = () => (useApiData<Maintainer[]>("/api/v1/maintainers") || [])
.sort((a: Maintainer, b: Maintainer) => a.name.localeCompare(b.name));

View file

@ -1,11 +1,9 @@
import { Button, Icon, Input } from "antd";
import { FilterDropdownProps } from "antd/lib/table";
import React, { useEffect, useState } from "react";
import { Button, Icon, Input } from 'antd';
import { FilterDropdownProps } from 'antd/lib/table';
import React, { useEffect, useState } from 'react';
interface FilterPropsState<T> {
filterDropdown?:
| React.ReactNode
| ((props: FilterDropdownProps) => React.ReactNode);
filterDropdown?: React.ReactNode | ((props: FilterDropdownProps) => React.ReactNode);
filterIcon?: React.ReactNode | ((filtered: boolean) => React.ReactNode);
onFilter?: (value: any, record: T) => boolean;
}
@ -24,59 +22,36 @@ export const useSearchColumnFilter = <T extends {}>(dataIndex: string) => {
clearFilters && clearFilters([]);
};
setFilterProps({
filterDropdown: ({
setSelectedKeys,
selectedKeys,
confirm,
clearFilters
}) => (
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div style={{ padding: 8 }}>
<Input
placeholder={`Search ${dataIndex.charAt(0).toUpperCase() +
dataIndex.slice(1)}`}
placeholder={`Search ${dataIndex.charAt(0).toUpperCase() + dataIndex.slice(1)}`}
value={selectedKeys && selectedKeys[0]}
onChange={e =>
setSelectedKeys &&
setSelectedKeys(e.target.value ? [e.target.value] : [])
}
onChange={e => setSelectedKeys && setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => handleSearch(confirm)}
style={{ width: 188, marginBottom: 8, display: "block" }}
/>
style={{ width: 188, marginBottom: 8, display: 'block' }} />
<Button
type="primary"
onClick={() => handleSearch(confirm)}
icon="search"
size="small"
style={{ width: 90, marginRight: 8 }}
>
style={{ width: 90, marginRight: 8 }} >
Search
</Button>
</Button>
<Button
onClick={() => handleReset(clearFilters)}
size="small"
style={{ width: 90 }}
>
style={{ width: 90 }} >
Reset
</Button>
</Button>
</div>
),
filterIcon: filtered => (
<Icon
type="search"
style={{ color: filtered ? "#1890ff" : undefined }}
/>
),
filterIcon: filtered => <Icon type="search" style={{ color: filtered ? '#1890ff' : undefined }} />,
onFilter: (value, record) => {
const searchValue = (record as any)[dataIndex];
return (
searchValue &&
searchValue
.toString()
.toLowerCase()
.includes(value.toString().toLowerCase())
);
return searchValue && searchValue.toString().toLowerCase().includes(value.toString().toLowerCase())
}
});
})
}, [dataIndex]);
return filterProps;
};
};

View file

@ -1,7 +1,5 @@
import { Software } from "../interfaces/Software";
import { useApiData } from "./useApiData";
import { Software } from '../interfaces/Software';
import { useApiData } from './useApiData';
export const useSoftware = () =>
(useApiData<Software[]>("/api/v1/software") || []).sort(
(a: Software, b: Software) => a.name.localeCompare(b.name)
);
export const useSoftware = () => (useApiData<Software[]>("/api/v1/software") || [])
.sort((a: Software, b: Software) => a.name.localeCompare(b.name));

View file

@ -1,7 +1,5 @@
import { Syntax } from "../interfaces/Syntax";
import { useApiData } from "./useApiData";
import { Syntax } from '../interfaces/Syntax';
import { useApiData } from './useApiData';
export const useSyntaxes = () =>
(useApiData<Syntax[]>("/api/v1/syntaxes") || []).sort(
(a: Syntax, b: Syntax) => a.name.localeCompare(b.name)
);
export const useSyntaxes = () => (useApiData<Syntax[]>("/api/v1/syntaxes") || [])
.sort((a: Syntax, b: Syntax) => a.name.localeCompare(b.name));

View file

@ -1,16 +1,16 @@
import { useEffect, useState } from "react";
import { useEffect, useState } from 'react';
interface TablePageSize {
pageSize: number;
isNarrowWindow: boolean;
}
};
export const useTablePageSizer = () => {
const [state, setState] = useState<TablePageSize>(calculateSize());
useEffect(() => {
const updatePageSize = () => setState(calculateSize());
window.addEventListener("resize", updatePageSize);
return () => window.removeEventListener("resize", updatePageSize);
window.addEventListener('resize', updatePageSize);
return () => window.removeEventListener('resize', updatePageSize);
}, []);
return state;
};
@ -18,4 +18,4 @@ export const useTablePageSizer = () => {
const calculateSize = () => ({
pageSize: Math.floor((window.innerHeight - 184) / 57),
isNarrowWindow: window.innerWidth < 576 ? true : false
});
});

View file

@ -1,7 +1,5 @@
import { Tag } from "../interfaces/Tag";
import { useApiData } from "./useApiData";
import { Tag } from '../interfaces/Tag';
import { useApiData } from './useApiData';
export const useTags = () =>
(useApiData<Tag[]>("/api/v1/tags") || []).sort((a: Tag, b: Tag) =>
a.name.localeCompare(b.name)
);
export const useTags = () => (useApiData<Tag[]>("/api/v1/tags") || [])
.sort((a: Tag, b: Tag) => a.name.localeCompare(b.name));

View file

@ -1,8 +1,8 @@
import "./index.css";
import './index.css';
import React from "react";
import ReactDOM from "react-dom";
import React from 'react';
import ReactDOM from 'react-dom';
import { App } from "./App";
import { App } from './App';
ReactDOM.render(<App />, document.getElementById("root"));
ReactDOM.render(<App />, document.getElementById('root'));

View file

@ -3,4 +3,4 @@ export interface Language {
filterListIds: number[];
iso6391: string;
name: string;
}
};

View file

@ -3,4 +3,4 @@ export interface License {
descriptionUrl: string;
filterListIds: number[];
name: string;
}
};

View file

@ -22,4 +22,4 @@
// auto-generated by useLists hook
slug: string;
}
};

View file

@ -5,4 +5,4 @@ export interface Maintainer {
homeUrl: string;
name: string;
twitterHandle: string;
}
};

View file

@ -4,4 +4,4 @@ export interface Software {
isAbpSubscribable: boolean;
name: string;
syntaxIds: number[];
}
};

View file

@ -4,4 +4,4 @@ export interface Syntax {
filterListIds: number[];
name: string;
softwareIds: number[];
}
};

View file

@ -3,4 +3,4 @@ export interface Tag {
description: string;
filterListIds: number[];
name: string;
}
};

View file

@ -1 +1 @@
/// <reference types="react-scripts" />
/// <reference types="react-scripts" />

View file

@ -1,3 +1,3 @@
import { nameof } from "./nameof";
import { nameof } from './nameof';
export { nameof };
export { nameof };

View file

@ -1,2 +1,2 @@
//https://stackoverflow.com/a/50470026/2343739
export const nameof = <T>(name: Extract<keyof T, string>) => name;
export const nameof = <T>(name: Extract<keyof T, string>) => name;