copy over modules & utils folders
107
src/web/src/modules/home/Home.tsx
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import * as React from "react";
|
||||
import { IColumnVisibility, ILanguage, ILicense, IList, IMaintainer, ISoftware, ISyntax, ITag } from "./interfaces";
|
||||
import { ListsTable, Oneliner } from "./components";
|
||||
|
||||
const columnVisibilityDefaults: IColumnVisibility[] = [
|
||||
{ column: "Software", visible: true },
|
||||
{ column: "Languages", visible: true },
|
||||
{ column: "Tags", visible: true },
|
||||
//{ column: "Updated", visible: false },
|
||||
//{ column: "Rules", visible: false },
|
||||
{ column: "License", visible: false },
|
||||
{ column: "Maintainers", visible: false },
|
||||
{ column: "Subscribe", visible: false }
|
||||
];
|
||||
|
||||
interface IProps {
|
||||
languages: ILanguage[];
|
||||
licenses: ILicense[];
|
||||
lists: IList[];
|
||||
maintainers: IMaintainer[];
|
||||
//ruleCount: number;
|
||||
software: ISoftware[];
|
||||
syntaxes: ISyntax[];
|
||||
tags: ITag[];
|
||||
};
|
||||
|
||||
interface IState {
|
||||
columnVisibility: IColumnVisibility[];
|
||||
pageSize: number;
|
||||
};
|
||||
|
||||
export class Home extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
columnVisibility: columnVisibilityDefaults,
|
||||
pageSize: 20
|
||||
};
|
||||
this.updatePageSize = this.updatePageSize.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setMobileColumnVisibility();
|
||||
this.updatePageSize();
|
||||
};
|
||||
|
||||
setMobileColumnVisibility() {
|
||||
if (window.innerWidth < 768) {
|
||||
this.state.columnVisibility.forEach((c: IColumnVisibility) => {
|
||||
c.visible = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
updatePageSize() {
|
||||
this.setState({
|
||||
pageSize: Math.max(Math.floor((window.innerHeight - 400) / 52), 5)
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
<Oneliner listCount={this.props.lists.length}/* ruleCount={this.props.ruleCount}*//>
|
||||
<ListsTable {...this.props} {...this.state}/>
|
||||
{this.renderColumnVisibilityCheckboxes()}
|
||||
</div>;
|
||||
};
|
||||
|
||||
renderColumnVisibilityCheckboxes() {
|
||||
return this.props.lists.length > 0
|
||||
? <div className="d-none d-md-block text-right">
|
||||
Visible: {this.state.columnVisibility.map(
|
||||
(c: IColumnVisibility, i: number) => this.renderColumnVisibilityCheckbox(c, i))}
|
||||
</div>
|
||||
: null;
|
||||
};
|
||||
|
||||
renderColumnVisibilityCheckbox(props: IColumnVisibility, key: number) {
|
||||
return <div className="form-check form-check-inline" key={key}>
|
||||
<input className="form-check-input"
|
||||
type="checkbox"
|
||||
id={`checkbox${props.column.replace(/\s+/g, "")}`}
|
||||
defaultChecked={props.visible}
|
||||
onChange={() => this.checkColumn(props)}/>
|
||||
<label className="form-check-label"
|
||||
htmlFor={`checkbox${props.column.replace(/\s+/g, "")}`}>
|
||||
{props.column}
|
||||
</label>
|
||||
</div>;
|
||||
};
|
||||
|
||||
checkColumn(props: IColumnVisibility) {
|
||||
const columnVisibility = this.state.columnVisibility;
|
||||
const index = this.findWithAttr(columnVisibility, "column", props.column);
|
||||
columnVisibility[index].visible = !columnVisibility[index].visible;
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
findWithAttr(array: any, attr: string, value: string) {
|
||||
for (let i = 0; i < array.length; i += 1) {
|
||||
if (array[i][attr] === value) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
};
|
||||
94
src/web/src/modules/home/HomeContainer.tsx
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import * as React from "react";
|
||||
import "isomorphic-fetch";
|
||||
import { ILanguage, ILicense, IList, IMaintainer, ISoftware, ISyntax, ITag } from "./interfaces";
|
||||
import { Home } from "./Home";
|
||||
|
||||
interface IState {
|
||||
languages: ILanguage[];
|
||||
licenses: ILicense[];
|
||||
lists: IList[];
|
||||
maintainers: IMaintainer[];
|
||||
ruleCount: number;
|
||||
software: ISoftware[];
|
||||
syntaxes: ISyntax[];
|
||||
tags: ITag[];
|
||||
};
|
||||
|
||||
export class HomeContainer extends React.Component<{}, IState> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
languages: [],
|
||||
licenses: [],
|
||||
lists: [],
|
||||
maintainers: [],
|
||||
ruleCount: 0,
|
||||
software: [],
|
||||
syntaxes: [],
|
||||
tags: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchLanguages();
|
||||
this.fetchLicenses();
|
||||
this.fetchLists();
|
||||
this.fetchMaintainers();
|
||||
this.fetchSoftware();
|
||||
this.fetchSyntaxes();
|
||||
this.fetchTags();
|
||||
// this.fetchRuleCount();
|
||||
};
|
||||
|
||||
fetchLanguages() {
|
||||
fetch("/api/v1/languages")
|
||||
.then(r => r.json() as Promise<ILanguage[]>)
|
||||
.then(p => { this.setState({ languages: p }); });
|
||||
};
|
||||
|
||||
fetchLicenses() {
|
||||
fetch("/api/v1/licenses")
|
||||
.then(r => r.json() as Promise<ILicense[]>)
|
||||
.then(p => { this.setState({ licenses: p }); });
|
||||
};
|
||||
|
||||
fetchLists() {
|
||||
fetch("/api/v1/lists")
|
||||
.then(r => r.json() as Promise<IList[]>)
|
||||
.then(p => { this.setState({ lists: p }); });
|
||||
};
|
||||
|
||||
fetchMaintainers() {
|
||||
fetch("/api/v1/maintainers")
|
||||
.then(r => r.json() as Promise<IMaintainer[]>)
|
||||
.then(p => { this.setState({ maintainers: p }); });
|
||||
};
|
||||
|
||||
fetchSoftware() {
|
||||
fetch("/api/v1/software")
|
||||
.then(r => r.json() as Promise<ISoftware[]>)
|
||||
.then(p => { this.setState({ software: p }); });
|
||||
};
|
||||
|
||||
fetchSyntaxes() {
|
||||
fetch("/api/v1/syntaxes")
|
||||
.then(r => r.json() as Promise<ISyntax[]>)
|
||||
.then(p => { this.setState({ syntaxes: p }); });
|
||||
};
|
||||
|
||||
fetchTags() {
|
||||
fetch("/api/v1/tags")
|
||||
.then(r => r.json() as Promise<ITag[]>)
|
||||
.then(p => { this.setState({ tags: p }); });
|
||||
};
|
||||
|
||||
fetchRuleCount() {
|
||||
fetch("/api/v1/rules")
|
||||
.then(r => r.json() as Promise<number>)
|
||||
.then(p => { this.setState({ ruleCount: p }); });
|
||||
};
|
||||
|
||||
render() {
|
||||
return <Home {...this.state}/>;
|
||||
};
|
||||
};
|
||||
17
src/web/src/modules/home/components/Oneliner.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import * as React from "react";
|
||||
|
||||
interface IProps {
|
||||
listCount: number;
|
||||
//ruleCount: number;
|
||||
};
|
||||
|
||||
export const Oneliner = (props: IProps) =>
|
||||
props.listCount > 0/* && props.ruleCount > 0*/
|
||||
? <p className="ml-2 mr-2">
|
||||
The independent, comprehensive directory of {/*}<strong>{props.ruleCount.toLocaleString()
|
||||
}</strong> unique rules across */}<strong>{props.listCount.toLocaleString()
|
||||
}</strong> filter and host lists for advertisements, trackers, malware, and annoyances.
|
||||
</p>
|
||||
: <p className="ml-2 mr-2">
|
||||
The independent, comprehensive directory of filter and host lists for advertisements, trackers, malware, and annoyances.
|
||||
</p>;
|
||||
67
src/web/src/modules/home/components/TagGroup.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import * as React from "react";
|
||||
import { ITag } from "../interfaces";
|
||||
import { getContrast } from "../../../utils";
|
||||
|
||||
interface IProps {
|
||||
tags: ITag[];
|
||||
};
|
||||
|
||||
export const TagGroup = (props: IProps) =>
|
||||
props.tags && props.tags.length > 0
|
||||
? <div className="fl-wrap-cell">
|
||||
{props.tags.map((t: ITag, i: number) => <Tag tag={t} key={i}/>)}
|
||||
</div>
|
||||
: null;
|
||||
|
||||
interface ITagProps {
|
||||
tag: ITag;
|
||||
};
|
||||
|
||||
const Tag = (props: ITagProps) => {
|
||||
if (props.tag) {
|
||||
const hexColor = kelly_colors_hex[props.tag.id % kelly_colors_hex.length];
|
||||
return <span className="badge"
|
||||
style={{
|
||||
backgroundColor: `#${hexColor}`,
|
||||
color: getContrast(hexColor)
|
||||
}}
|
||||
title={props.tag.description}>
|
||||
{props.tag.name}
|
||||
</span>;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
//https://stackoverflow.com/a/4382138/2343739
|
||||
const kelly_colors_hex = [
|
||||
"FFB300", // Vivid Yellow
|
||||
"803E75", // Strong Purple
|
||||
"FF6800", // Vivid Orange
|
||||
"A6BDD7", // Very Light Blue
|
||||
"C10020", // Vivid Red
|
||||
"CEA262", // Grayish Yellow
|
||||
"817066", // Medium Gray
|
||||
"007D34", // Vivid Green
|
||||
"F6768E", // Strong Purplish Pink
|
||||
"00538A", // Strong Blue
|
||||
"FF7A5C", // Strong Yellowish Pink
|
||||
"53377A", // Strong Violet
|
||||
"FF8E00", // Vivid Orange Yellow
|
||||
"B32851", // Strong Purplish Red
|
||||
"F4C800", // Vivid Greenish Yellow
|
||||
"7F180D", // Strong Reddish Brown
|
||||
"93AA00", // Vivid Yellowish Green
|
||||
"593315", // Deep Yellowish Brown
|
||||
"F13A13", // Vivid Reddish Orange
|
||||
"232C16", // Dark Olive Green
|
||||
"000000", // Black
|
||||
"FFFFFF", // White
|
||||
"C2F4BE", // Light Green
|
||||
"1EDBB9", // Medium Greenish Blue
|
||||
"890000", // Dark Red
|
||||
"474747", // Dark Grey
|
||||
"BFA125", // Matte Gold
|
||||
"A7823C", // Dark Beige
|
||||
"035A5C" // Dark Greenish Blue
|
||||
];
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import * as React from "react";
|
||||
import { IColumnVisibility, ISoftware } from "../../interfaces";
|
||||
import { IListDetails } from "./IListDetails";
|
||||
import { InfoCard } from "./infoCard";
|
||||
import { LinkButtonGroup } from "./LinkButtonGroup";
|
||||
import { MaintainersInfoCard } from "./maintainersInfoCard";
|
||||
|
||||
interface IProps {
|
||||
columnVisibility: IColumnVisibility[];
|
||||
list: IListDetails;
|
||||
software: ISoftware[];
|
||||
};
|
||||
|
||||
export const DetailsExpander = (props: IProps) =>
|
||||
<div className="card border-primary">
|
||||
<div className="card-body p-2">
|
||||
<div className="container m-0">
|
||||
<div className="row">
|
||||
<InfoCard columnVisibility={props.columnVisibility} software={props.software} {...props.list}/>
|
||||
<LinkButtonGroup {...props.list}/>
|
||||
</div>
|
||||
<div className="row">
|
||||
<MaintainersInfoCard maintainers={props.list.maintainers}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import { ILanguage, ILicense, IMaintainer, ISyntax, ITag } from "../../interfaces";
|
||||
|
||||
export interface IListDetails {
|
||||
id: number;
|
||||
chatUrl: string;
|
||||
description: string;
|
||||
descriptionSourceUrl: string;
|
||||
donateUrl: string;
|
||||
emailAddress: string;
|
||||
forumUrl: string;
|
||||
homeUrl: string;
|
||||
issuesUrl: string;
|
||||
languages: ILanguage[];
|
||||
license: ILicense;
|
||||
maintainers: IMaintainer[];
|
||||
name: string;
|
||||
policyUrl: string;
|
||||
publishedDate: string;
|
||||
//ruleCount: number;
|
||||
submissionUrl: string;
|
||||
syntax: ISyntax;
|
||||
tags: ITag[];
|
||||
//updatedDate: string;
|
||||
viewUrl: string;
|
||||
viewUrlMirrors: string[];
|
||||
};
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import * as React from "react";
|
||||
import {
|
||||
ChatButton,
|
||||
DonateButton,
|
||||
EmailButton,
|
||||
ForumButton,
|
||||
HomeButton,
|
||||
IssuesButton,
|
||||
PolicyButton,
|
||||
SubmitButton,
|
||||
SubscribeButtonGroup,
|
||||
ViewButtonGroup
|
||||
} from "../linkButtons";
|
||||
|
||||
interface IProps {
|
||||
chatUrl: string;
|
||||
donateUrl: string;
|
||||
emailAddress: string;
|
||||
forumUrl: string;
|
||||
homeUrl: string;
|
||||
issuesUrl: string;
|
||||
name: string;
|
||||
policyUrl: string;
|
||||
submissionUrl: string;
|
||||
viewUrl: string;
|
||||
viewUrlMirrors: string[];
|
||||
};
|
||||
|
||||
export const LinkButtonGroup = (props: IProps) =>
|
||||
<div className="col-3 p-0 btn-group-vertical justify-content-start d-flex align-items-end">
|
||||
<SubscribeButtonGroup name={props.name} url={props.viewUrl} urlMirrors={props.viewUrlMirrors}/>
|
||||
<ViewButtonGroup name={props.name} url={props.viewUrl} urlMirrors={props.viewUrlMirrors}/>
|
||||
<HomeButton name={props.name} url={props.homeUrl}/>
|
||||
<PolicyButton name={props.name} url={props.policyUrl}/>
|
||||
<DonateButton name={props.name} url={props.donateUrl}/>
|
||||
<IssuesButton name={props.name} url={props.issuesUrl}/>
|
||||
<ForumButton name={props.name} url={props.forumUrl}/>
|
||||
<ChatButton name={props.name} url={props.chatUrl}/>
|
||||
<SubmitButton name={props.name} url={props.submissionUrl}/>
|
||||
<EmailButton name={props.name} emailAddress={props.emailAddress}/>
|
||||
</div>;
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import { DetailsExpander } from "./DetailsExpander";
|
||||
import { IListDetails } from "./IListDetails";
|
||||
|
||||
export {
|
||||
DetailsExpander,
|
||||
IListDetails
|
||||
};
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import * as React from "react";
|
||||
import "./description.css";
|
||||
|
||||
interface IProps {
|
||||
description: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export const Description = (props: IProps) =>
|
||||
props.description
|
||||
? <h3 className="card-header fl-description">
|
||||
{props.url
|
||||
? <blockquote cite={props.url} className="m-0">{props.description}</blockquote>
|
||||
: props.description}
|
||||
</h3>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
import * as React from "react";
|
||||
import { IColumnVisibility } from "../../../interfaces";
|
||||
import { ILanguage, ILicense, ISoftware, ISyntax, ITag } from "../../../interfaces";
|
||||
import { Description } from "./Description";
|
||||
import { Languages } from "./Languages";
|
||||
import { License } from "./License";
|
||||
import { PublishedDate } from "./PublishedDate";
|
||||
import { RuleCount } from "./RuleCount";
|
||||
import { SoftwareIcon } from "../../softwareIcon";
|
||||
import { Syntax } from "./Syntax";
|
||||
import { TagGroup } from "../../TagGroup"
|
||||
import { UpdatedDate } from "./UpdatedDate";
|
||||
|
||||
interface IProps {
|
||||
columnVisibility: IColumnVisibility[];
|
||||
description: string;
|
||||
descriptionSourceUrl: string;
|
||||
languages: ILanguage[];
|
||||
license: ILicense;
|
||||
name: string;
|
||||
publishedDate: string;
|
||||
//ruleCount: number;
|
||||
software: ISoftware[];
|
||||
syntax: ISyntax;
|
||||
tags: ITag[];
|
||||
//updatedDate: string;
|
||||
};
|
||||
|
||||
export const InfoCard = (props: IProps) =>
|
||||
<div className="col-9">
|
||||
{props.columnVisibility.filter((c: IColumnVisibility) => c.column === "Tags")[0].visible
|
||||
? null
|
||||
: <TagGroup tags={props.tags}/>}
|
||||
<div>
|
||||
{props.columnVisibility.filter((c: IColumnVisibility) => c.column === "Software")[0].visible
|
||||
? null
|
||||
: props.syntax
|
||||
? props.software.filter((s: ISoftware) => s.syntaxIds.indexOf(props.syntax.id) > -1)
|
||||
.map((s: ISoftware, i: number) => <SoftwareIcon id={s.id} key={i}/>)
|
||||
: null}
|
||||
</div>
|
||||
<Description {...props} url={props.descriptionSourceUrl}/>
|
||||
<ul className="list-group list-group-flush">
|
||||
<Languages {...props}/>
|
||||
{/*{props.columnVisibility.filter((c: IColumnVisibility) => c.column === "Rules")[0].visible
|
||||
? null
|
||||
: <RuleCount {...props}/>}
|
||||
{props.columnVisibility.filter((c: IColumnVisibility) => c.column === "Updated")[0].visible
|
||||
? null
|
||||
: <UpdatedDate {...props}/>}*/}
|
||||
<PublishedDate date={props.publishedDate}/>
|
||||
<Syntax {...props} />
|
||||
{props.columnVisibility.filter((c: IColumnVisibility) => c.column === "License")[0].visible
|
||||
? null
|
||||
: <License {...props} />}
|
||||
</ul>
|
||||
</div>;
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import * as React from "react";
|
||||
import { ILanguage } from "../../../interfaces";
|
||||
|
||||
interface IProps {
|
||||
languages: ILanguage[];
|
||||
};
|
||||
|
||||
export const Languages = (props: IProps) =>
|
||||
props.languages && props.languages.length > 0
|
||||
? props.languages.length > 1
|
||||
? <li className="list-group-item">
|
||||
<p className="m-0">Languages:</p>
|
||||
<ul>
|
||||
{props.languages.map((language: ILanguage, i: number) => <li key={i}>{language.name}</li>)}
|
||||
</ul>
|
||||
</li>
|
||||
: <li className="list-group-item">
|
||||
<p>Language: {props.languages[0].name}</p>
|
||||
</li>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import * as React from "react";
|
||||
import { ILicense } from "../../../interfaces";
|
||||
|
||||
interface IProps {
|
||||
license: ILicense;
|
||||
};
|
||||
|
||||
export const License = (props: IProps) =>
|
||||
props.license.name
|
||||
? (props.license.descriptionUrl
|
||||
? <li className="list-group-item">
|
||||
<p>License: <a href={props.license.descriptionUrl}>{props.license.name}</a></p>
|
||||
</li>
|
||||
: <li className="list-group-item">
|
||||
<p>License: {props.license.name}</p>
|
||||
</li>)
|
||||
: null;
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import * as React from "react";
|
||||
import * as moment from "moment";
|
||||
|
||||
interface IProps {
|
||||
date: string;
|
||||
};
|
||||
|
||||
export const PublishedDate = (props: IProps) =>
|
||||
props.date
|
||||
? <li className="list-group-item">
|
||||
<p>Published: {moment(props.date).format("l")}</p>
|
||||
</li>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import * as React from "react";
|
||||
|
||||
interface IProps {
|
||||
ruleCount: number;
|
||||
};
|
||||
|
||||
export const RuleCount = (props: IProps) =>
|
||||
props.ruleCount > 0
|
||||
? <li className="list-group-item">
|
||||
<p>Rule Count: {props.ruleCount.toLocaleString()}</p>
|
||||
</li>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import * as React from "react";
|
||||
import { ISyntax } from "../../../interfaces";
|
||||
|
||||
interface IProps {
|
||||
syntax: ISyntax;
|
||||
};
|
||||
|
||||
export const Syntax = (props: IProps) =>
|
||||
props.syntax
|
||||
? (props.syntax.definitionUrl
|
||||
? <li className="list-group-item">
|
||||
<p>Syntax: <a href={props.syntax.definitionUrl}>{props.syntax.name}</a></p>
|
||||
</li>
|
||||
: <li className="list-group-item">
|
||||
<p>Syntax: {props.syntax.name}</p>
|
||||
</li>)
|
||||
: null;
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import * as React from "react";
|
||||
import * as moment from "moment";
|
||||
|
||||
interface IProps {
|
||||
updatedDate: string;
|
||||
};
|
||||
|
||||
export const UpdatedDate = (props: IProps) =>
|
||||
props.updatedDate
|
||||
? <li className="list-group-item">
|
||||
<p>Updated: {moment(props.updatedDate).isValid()
|
||||
? moment(props.updatedDate).format("l")
|
||||
: "N/A"}</p>
|
||||
</li>
|
||||
: null;
|
||||
|
|
@ -0,0 +1 @@
|
|||
.fl-description { margin: 0 0 .5rem; }
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { InfoCard } from "./InfoCard";
|
||||
|
||||
export {
|
||||
InfoCard
|
||||
};
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import * as React from "react";
|
||||
|
||||
//interface IProps {
|
||||
// name: string;
|
||||
// additionalLists: IMaintainerAdditionalListDto[];
|
||||
//};
|
||||
|
||||
export const MaintainerAdditionalLists = (/*props: IProps*/) =>
|
||||
<div className="col-9">
|
||||
{ /*props.additionalLists && props.additionalLists.length > 0
|
||||
? <div>
|
||||
<h4>More by {props.name}:</h4>
|
||||
<ul>
|
||||
{props.additionalLists.map(
|
||||
(l: IMaintainerAdditionalListDto, i: number) => <li key={i}>{l.name}</li>)}
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
: null*/ }
|
||||
</div>;
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import * as React from "react";
|
||||
import { IMaintainer } from "../../../interfaces";
|
||||
import { MaintainerAdditionalLists } from "./MaintainerAdditionalLists";
|
||||
import { MaintainerLinkButtonGroup } from "./MaintainerLinkButtonGroup";
|
||||
|
||||
interface IProps {
|
||||
maintainer: IMaintainer;
|
||||
};
|
||||
|
||||
export const MaintainerInfoCard = (props: IProps) =>
|
||||
props.maintainer.name
|
||||
? <div className="card">
|
||||
<div className="card-body">
|
||||
<h3 className="card-header">Maintained by {props.maintainer.name}</h3>
|
||||
<div className="container pt-1">
|
||||
<div className="row">
|
||||
<MaintainerAdditionalLists/>
|
||||
<MaintainerLinkButtonGroup {...props.maintainer}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import * as React from "react";
|
||||
import { EmailButton, HomeButton, TwitterButton } from "../../linkButtons";
|
||||
|
||||
interface IProps {
|
||||
emailAddress: string;
|
||||
homeUrl: string;
|
||||
name: string;
|
||||
twitterHandle: string;
|
||||
};
|
||||
|
||||
export const MaintainerLinkButtonGroup = (props: IProps) =>
|
||||
<div className="col-3 p-0 btn-group-vertical justify-content-start d-flex align-items-end" role="group">
|
||||
<HomeButton {...props} url={props.homeUrl}/>
|
||||
<EmailButton {...props}/>
|
||||
<TwitterButton {...props}/>
|
||||
</div>;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import * as React from "react";
|
||||
import { IMaintainer } from "../../../interfaces";
|
||||
import { MaintainerInfoCard } from "./MaintainerInfoCard";
|
||||
|
||||
interface IProps {
|
||||
maintainers: IMaintainer[];
|
||||
};
|
||||
|
||||
export const MaintainersInfoCard = (props: IProps) =>
|
||||
props.maintainers && props.maintainers.length > 0
|
||||
? <div className="w-100">
|
||||
{props.maintainers.map((m: IMaintainer, i: number) => <MaintainerInfoCard maintainer={m} key={i}/>)}
|
||||
</div>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { MaintainersInfoCard } from "./MaintainersInfoCard";
|
||||
|
||||
export {
|
||||
MaintainersInfoCard
|
||||
};
|
||||
9
src/web/src/modules/home/components/index.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import { DetailsExpander } from "./detailsExpander";
|
||||
import { ListsTable } from "./listsTable";
|
||||
import { Oneliner } from "./Oneliner";
|
||||
|
||||
export {
|
||||
DetailsExpander,
|
||||
ListsTable,
|
||||
Oneliner
|
||||
};
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import * as React from "react";
|
||||
import { LinkButton } from "./LinkButton";
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export const ChatButton = (props: IProps) =>
|
||||
props.url
|
||||
? <LinkButton href={props.url}
|
||||
title={`Enter the chat room for ${props.name}.`}
|
||||
text="Chat"/>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import * as React from "react";
|
||||
import { LinkButton } from "./LinkButton";
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export const DonateButton = (props: IProps) =>
|
||||
props.url
|
||||
? <LinkButton href={props.url}
|
||||
title={`Donate to the maintainer of ${props.name}.`}
|
||||
text="Donate"/>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import * as React from "react";
|
||||
import { LinkButton } from "./LinkButton";
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
emailAddress: string;
|
||||
};
|
||||
|
||||
export const EmailButton = (props: IProps) =>
|
||||
props.emailAddress
|
||||
? <LinkButton href={`mailto:${props.emailAddress}`}
|
||||
title={`Email ${props.name}.`}
|
||||
text="Email"/>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import * as React from "react";
|
||||
import { LinkButton } from "./LinkButton";
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export const ForumButton = (props: IProps) =>
|
||||
props.url
|
||||
? <LinkButton href={props.url}
|
||||
title={`View the forum for ${props.name}.`}
|
||||
text="Forum"/>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import * as React from "react";
|
||||
import { LinkButton } from "./LinkButton";
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export const HomeButton = (props: IProps) =>
|
||||
props.url
|
||||
? <LinkButton href={props.url}
|
||||
title={`View ${props.name}'s homepage.`}
|
||||
text="Home"/>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import * as React from "react";
|
||||
import { LinkButton } from "./LinkButton";
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export const IssuesButton = (props: IProps) =>
|
||||
props.url
|
||||
? <LinkButton href={props.url}
|
||||
title={`View the GitHub Issues for ${props.name}.`}
|
||||
text="GH Issues"/>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import * as React from "react";
|
||||
import "./button.css";
|
||||
|
||||
interface IProps {
|
||||
href: string;
|
||||
title?: string;
|
||||
buttonClass?: string;
|
||||
text: string;
|
||||
};
|
||||
|
||||
export const LinkButton = (props: IProps) =>
|
||||
props.href
|
||||
? <a href={props.href}
|
||||
title={props.title}
|
||||
className={`btn ${props.buttonClass || "btn-primary"} fl-btn-link`}>
|
||||
{props.text}
|
||||
</a>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import * as React from "react";
|
||||
import { LinkButton } from "./LinkButton";
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export const PolicyButton = (props: IProps) =>
|
||||
props.url
|
||||
? <LinkButton href={props.url}
|
||||
title={`View the types of rules that ${props.name} includes.`}
|
||||
text="Policy"/>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import * as React from "react";
|
||||
import { LinkButton } from "./LinkButton";
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export const SubmitButton = (props: IProps) =>
|
||||
props.url
|
||||
? <LinkButton href={props.url}
|
||||
title={`Submit a new rule to be included in ${props.name}.`}
|
||||
text="Submit"/>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import * as React from "react";
|
||||
import { LinkButton } from "./LinkButton";
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
url: string;
|
||||
text?: string;
|
||||
};
|
||||
|
||||
export const SubscribeButton = (props: IProps) => {
|
||||
let buttonClass: string | undefined;
|
||||
let titlePrefix: string;
|
||||
|
||||
if (props.url.indexOf(".onion/") > 0) {
|
||||
buttonClass = "btn-success";
|
||||
titlePrefix = "Tor address - ";
|
||||
} else if (props.url.indexOf("http://") === 0) {
|
||||
buttonClass = "btn-danger";
|
||||
titlePrefix = "Not Secure - ";
|
||||
} else {
|
||||
buttonClass = undefined;
|
||||
titlePrefix = "";
|
||||
}
|
||||
|
||||
const hrefTitle = `${encodeURIComponent(props.name)}`;
|
||||
let href;
|
||||
if (props.url.indexOf(".tpl") > 0)
|
||||
{
|
||||
href = `javascript:window.external.msAddTrackingProtectionList('${encodeURIComponent(props.url)}', '${hrefTitle}')`;
|
||||
} else if (props.url.indexOf(".lsrules") > 0)
|
||||
{
|
||||
href = `x-littlesnitch:subscribe-rules?url=${encodeURIComponent(props.url)}`;
|
||||
} else if (props.url.indexOf("?hostformat=littlesnitch") > 0)
|
||||
{
|
||||
href = `x-littlesnitch:subscribe-rules?url=${encodeURIComponent(props.url)}`;
|
||||
} else {
|
||||
href = `abp:subscribe?location=${encodeURIComponent(props.url)}&title=${hrefTitle}`;
|
||||
};
|
||||
|
||||
|
||||
|
||||
let title;
|
||||
if (props.url.indexOf(".tpl") > 0)
|
||||
{
|
||||
title = `${titlePrefix}Subscribe to ${props.name} with Internet Explorer's Tracking Protection List feature.`;
|
||||
} else if (props.url.indexOf(".lsrules") > 0)
|
||||
{
|
||||
title = `${titlePrefix}Subscribe to ${props.name} with Little Snitch's rule group subscription feature.`;
|
||||
} else if (props.url.indexOf("?hostformat=littlesnitch") > 0)
|
||||
{
|
||||
title = `${titlePrefix}Subscribe to ${props.name} with Little Snitch's rule group subscription feature.`;
|
||||
} else {
|
||||
title = `${titlePrefix}Subscribe to ${props.name} with a browser extension supporting the \"abp:\" protocol (e.g. uBlock Origin, Adblock Plus).`;
|
||||
};
|
||||
|
||||
return props.url
|
||||
? <LinkButton href={href}
|
||||
title={title}
|
||||
buttonClass={buttonClass}
|
||||
text={props.text || "Subscribe"}/>
|
||||
: null;
|
||||
};
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import * as React from "react";
|
||||
import { SubscribeButton } from "./SubscribeButton";
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
url: string;
|
||||
urlMirrors?: string[];
|
||||
};
|
||||
|
||||
export const SubscribeButtonGroup = (props: IProps) =>
|
||||
props.url
|
||||
? (props.urlMirrors && props.urlMirrors.length > 0)
|
||||
? <SubscribeButtonGroupDropdown {...props} urlMirrors={props
|
||||
.urlMirrors}/>
|
||||
: <SubscribeButton {...props}/>
|
||||
: null;
|
||||
|
||||
interface ISubscribeButtonGroupDropdownProps {
|
||||
name: string;
|
||||
url: string;
|
||||
urlMirrors: string[];
|
||||
};
|
||||
|
||||
const SubscribeButtonGroupDropdown = (props: ISubscribeButtonGroupDropdownProps) => {
|
||||
let firstButtonText: string = "Original";
|
||||
let mirrorIndex: number = 0;
|
||||
|
||||
if (props.url.indexOf("web.archive.org") !== -1) {
|
||||
firstButtonText = "Mirror 1";
|
||||
mirrorIndex++;
|
||||
}
|
||||
|
||||
return <div className="btn-group-vertical dropleft fl-btn-link" role="group">
|
||||
<BtnGroupDropSubscribe/>
|
||||
<div className="dropdown-menu" aria-labelledby="btnGroupDropSubscribe">
|
||||
<SubscribeButton {...props} text={firstButtonText}/>
|
||||
{props.urlMirrors.map(
|
||||
(m: string, i: number) =>
|
||||
<SubscribeButton {...props} url={m} text={`Mirror ${i + 1 + mirrorIndex}`} key={i}/>)}
|
||||
</div>
|
||||
</div>;
|
||||
};
|
||||
|
||||
const BtnGroupDropSubscribe = () =>
|
||||
<button id="btnGroupDropSubscribe"
|
||||
type="button"
|
||||
className="btn btn-primary dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false">
|
||||
Subscribe
|
||||
</button>;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import * as React from "react";
|
||||
import { LinkButton } from "./LinkButton";
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
twitterHandle: string;
|
||||
};
|
||||
|
||||
export const TwitterButton = (props: IProps) =>
|
||||
props.twitterHandle
|
||||
? <LinkButton href={`https://twitter.com/${props.twitterHandle}`}
|
||||
title={`View ${props.name}'s Twitter profile.`}
|
||||
text="Twitter"/>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import * as React from "react";
|
||||
import { LinkButton } from "./LinkButton";
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
url: string;
|
||||
text?: string;
|
||||
};
|
||||
|
||||
export const ViewButton = (props: IProps) => {
|
||||
|
||||
let title;
|
||||
if (props.url.indexOf(".onion/") > 0) { title = `Tor address - View ${props.name} in its raw format.`; }
|
||||
else if (props.url.indexOf(".zip") > 0) { title = `Download ${props.name} as a ZIP compressed archive.`; }
|
||||
else if (props.url.indexOf(".7z") > 0) { title = `Download ${props.name} as a 7Z compressed archive.`; }
|
||||
else if (props.url.indexOf(".tar.gz") > 0) { title = `Download ${props.name} as a compressed tarball archive.`; }
|
||||
else { title = `View ${props.name} in its raw format`; };
|
||||
|
||||
return props.url
|
||||
? <LinkButton href={props.url}
|
||||
title={title}
|
||||
text={props.text || "View"}/>
|
||||
: null;
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import * as React from "react";
|
||||
import { ViewButton } from "./ViewButton";
|
||||
|
||||
interface IProps {
|
||||
name: string;
|
||||
url: string;
|
||||
urlMirrors?: string[];
|
||||
};
|
||||
|
||||
export const ViewButtonGroup = (props: IProps) =>
|
||||
props.url
|
||||
? (props.urlMirrors && props.urlMirrors.length > 0)
|
||||
? <ViewButtonGroupDropdown {...props} urlMirrors={props.urlMirrors}/>
|
||||
: <ViewButton {...props}/>
|
||||
: null;
|
||||
|
||||
interface IViewButtonGroupDropdownProps {
|
||||
name: string;
|
||||
url: string;
|
||||
urlMirrors: string[];
|
||||
};
|
||||
|
||||
const ViewButtonGroupDropdown = (props: IViewButtonGroupDropdownProps) => {
|
||||
let firstButtonText: string = "Original";
|
||||
let mirrorIndex: number = 0;
|
||||
|
||||
if (props.url.indexOf("web.archive.org") !== -1) {
|
||||
firstButtonText = "Mirror 1";
|
||||
mirrorIndex++;
|
||||
}
|
||||
|
||||
return <div className="btn-group-vertical dropleft fl-btn-link" role="group">
|
||||
<BtnGroupDropView/>
|
||||
<div className="dropdown-menu" aria-labelledby="btnGroupDropView">
|
||||
<ViewButton {...props} text={firstButtonText}/>
|
||||
{props.urlMirrors.map(
|
||||
(m: string, i: number) =>
|
||||
<ViewButton {...props} url={m} text={`Mirror ${i + 1 + mirrorIndex}`} key={i}/>)}
|
||||
</div>
|
||||
</div>;
|
||||
};
|
||||
|
||||
const BtnGroupDropView = () =>
|
||||
<button id="btnGroupDropView"
|
||||
type="button"
|
||||
className="btn btn-primary dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false">
|
||||
View
|
||||
</button>;
|
||||
11
src/web/src/modules/home/components/linkButtons/button.css
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
.fl-btn-link {
|
||||
margin-bottom: .2rem;
|
||||
max-width: 100px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
border: 0;
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
}
|
||||
25
src/web/src/modules/home/components/linkButtons/index.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { ChatButton } from "./ChatButton";
|
||||
import { DonateButton } from "./DonateButton";
|
||||
import { EmailButton } from "./EmailButton";
|
||||
import { ForumButton } from "./ForumButton";
|
||||
import { HomeButton } from "./HomeButton";
|
||||
import { IssuesButton } from "./IssuesButton";
|
||||
import { PolicyButton } from "./PolicyButton";
|
||||
import { SubmitButton } from "./SubmitButton";
|
||||
import { SubscribeButtonGroup } from "./SubscribeButtonGroup";
|
||||
import { TwitterButton } from "./TwitterButton";
|
||||
import { ViewButtonGroup } from "./ViewButtonGroup";
|
||||
|
||||
export {
|
||||
ChatButton,
|
||||
DonateButton,
|
||||
EmailButton,
|
||||
ForumButton,
|
||||
HomeButton,
|
||||
IssuesButton,
|
||||
PolicyButton,
|
||||
SubmitButton,
|
||||
SubscribeButtonGroup,
|
||||
TwitterButton,
|
||||
ViewButtonGroup
|
||||
};
|
||||
104
src/web/src/modules/home/components/listsTable/ListsTable.tsx
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
import * as React from "react";
|
||||
import { IColumnVisibility, ILanguage, ILicense, IList, IMaintainer, ISoftware, ISyntax, ITag } from "../../interfaces";
|
||||
import "../../../../utils/loader.css";
|
||||
import ReactTable from "react-table";
|
||||
import "react-table/react-table.css";
|
||||
import "./listsTable.css";
|
||||
import {
|
||||
DetailsButton,
|
||||
Languages,
|
||||
License,
|
||||
Maintainers,
|
||||
Name,
|
||||
//RuleCount,
|
||||
Software,
|
||||
SubscribeButton,
|
||||
Tags,
|
||||
//UpdatedDate
|
||||
} from "./columns";
|
||||
import { IListDetails } from "../../components/detailsExpander";
|
||||
import { DetailsExpander } from "../../components";
|
||||
|
||||
interface IProps {
|
||||
languages: ILanguage[];
|
||||
licenses: ILicense[];
|
||||
lists: IList[];
|
||||
maintainers: IMaintainer[];
|
||||
software: ISoftware[];
|
||||
syntaxes: ISyntax[];
|
||||
tags: ITag[];
|
||||
columnVisibility: IColumnVisibility[];
|
||||
pageSize: number;
|
||||
};
|
||||
|
||||
export const ListsTable = (props: IProps) =>
|
||||
props.languages.length > 0 && props.lists.length > 0 && props.software.length > 0 && props.tags.length > 0
|
||||
? <ReactTable
|
||||
data={props.lists}
|
||||
defaultPageSize={props.pageSize}
|
||||
showPageSizeOptions={false}
|
||||
columns={[
|
||||
Name,
|
||||
Software(props.columnVisibility, props.software),
|
||||
Languages(props.columnVisibility, props.languages),
|
||||
Tags(props.columnVisibility, props.tags),
|
||||
//UpdatedDate(props.columnVisibility),
|
||||
//RuleCount(props.columnVisibility),
|
||||
License(props.columnVisibility, props.licenses),
|
||||
Maintainers(props.columnVisibility, props.maintainers),
|
||||
SubscribeButton(props.columnVisibility),
|
||||
DetailsButton
|
||||
]}
|
||||
defaultSorted={[{ id: "name" }]}
|
||||
SubComponent={(r: any) =>
|
||||
<DetailsExpander columnVisibility={props.columnVisibility}
|
||||
list={mapListDetails(({
|
||||
list: r.original,
|
||||
languages: props.languages,
|
||||
licenses: props.licenses,
|
||||
maintainers: props.maintainers,
|
||||
syntaxes: props.syntaxes,
|
||||
tags: props.tags
|
||||
} as ICreateListDtoProps))}
|
||||
software={props.software}/>}
|
||||
className="-striped -highlight"/>
|
||||
: <div className="loader">Loading...</div>;
|
||||
|
||||
interface ICreateListDtoProps {
|
||||
list: IList;
|
||||
languages: ILanguage[];
|
||||
licenses: ILicense[];
|
||||
maintainers: IMaintainer[];
|
||||
syntaxes: ISyntax[];
|
||||
tags: ITag[];
|
||||
};
|
||||
|
||||
const mapListDetails = (props: ICreateListDtoProps): IListDetails =>
|
||||
({
|
||||
id: props.list.id,
|
||||
chatUrl: props.list.chatUrl,
|
||||
description: props.list.description,
|
||||
descriptionSourceUrl: props.list.descriptionSourceUrl,
|
||||
donateUrl: props.list.donateUrl,
|
||||
emailAddress: props.list.emailAddress,
|
||||
forumUrl: props.list.forumUrl,
|
||||
homeUrl: props.list.homeUrl,
|
||||
issuesUrl: props.list.issuesUrl,
|
||||
languages: props.list.languageIds
|
||||
? props.languages.filter((l: ILanguage) => props.list.languageIds.indexOf(l.id) > -1)
|
||||
: undefined,
|
||||
license: props.licenses.filter((l: ILicense) => props.list.licenseId === l.id)[0],
|
||||
maintainers: props.list.maintainerIds
|
||||
? props.maintainers.filter((m: IMaintainer) => props.list.maintainerIds.indexOf(m.id) > -1)
|
||||
: undefined,
|
||||
name: props.list.name,
|
||||
policyUrl: props.list.policyUrl,
|
||||
publishedDate: props.list.publishedDate,
|
||||
//ruleCount: props.list.ruleCount,
|
||||
submissionUrl: props.list.submissionUrl,
|
||||
syntax: props.syntaxes.filter((s: ISyntax) => props.list.syntaxId === s.id)[0],
|
||||
tags: props.list.tagIds ? props.tags.filter((t: ITag) => props.list.tagIds.indexOf(t.id) > -1) : undefined,
|
||||
//updatedDate: props.list.updatedDate,
|
||||
viewUrl: props.list.viewUrl,
|
||||
viewUrlMirrors: props.list.viewUrlMirrors
|
||||
} as IListDetails);
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import * as React from "react";
|
||||
import { Column, RowRenderProps } from "react-table";
|
||||
import "../../linkButtons/button.css";
|
||||
|
||||
export const DetailsButton = {
|
||||
Header: <span title="Learn more about the FilterList.">Details</span>,
|
||||
accessor: "id",
|
||||
sortable: false,
|
||||
expander: true,
|
||||
Expander: ({ isExpanded, row }: RowRenderProps) => Expander({ isExpanded, row }),
|
||||
style: { textAlign: "center" },
|
||||
width: 110
|
||||
} as Column;
|
||||
|
||||
const Expander = (props: RowRenderProps) =>
|
||||
<div>
|
||||
{props.isExpanded
|
||||
? <button className="btn btn-primary btn-block active fl-btn-link"
|
||||
title={`Collapse details about ${props.row.name}.`}>
|
||||
Details
|
||||
</button>
|
||||
: <button className="btn btn-primary btn-block fl-btn-link"
|
||||
title={`Learn more about ${props.row.name}.`}>
|
||||
Details
|
||||
</button>}
|
||||
</div>;
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
import * as React from "react";
|
||||
import { Column, Filter } from "react-table";
|
||||
import { IColumnVisibility, ILanguage } from "../../../interfaces";
|
||||
|
||||
export const Languages = (columnVisibility: IColumnVisibility[], languages: ILanguage[]) => {
|
||||
const languagesSorted = languages.sort((a, b) => a.name.localeCompare(b.name));
|
||||
return ({
|
||||
Header: <span title="Written forms of communication used by sites targeted by the FilterList.">Languages</span>,
|
||||
accessor: "languageIds",
|
||||
filterable: true,
|
||||
filterMethod: (f: Filter, r: any[]) => filterMethod(f, r),
|
||||
Filter: ({ onChange, filter }: any) => Filter({ onChange, filter }, languagesSorted),
|
||||
sortMethod: (a: number[], b: number[]) => sortMethod(a, b, languagesSorted),
|
||||
Cell: (c: any) => Cell(c.value, languagesSorted),
|
||||
style: { whiteSpace: "inherit" },
|
||||
width: 95,
|
||||
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Languages")[0].visible
|
||||
} as Column);
|
||||
};
|
||||
|
||||
const filterMethod = (f: Filter, r: any[]): boolean => {
|
||||
const listLanguageIds = r[f.id as any];
|
||||
return f.value === "any" ||
|
||||
(listLanguageIds
|
||||
? listLanguageIds.join(",").split(",").includes(f.value)
|
||||
: f.value === "none");
|
||||
};
|
||||
|
||||
const Filter = (props: any, languages: ILanguage[]) =>
|
||||
<select onChange={(event: any) => props.onChange(event.target.value)}
|
||||
style={{ width: "100%" }}
|
||||
value={props.filter ? props.filter.value : "any"}>
|
||||
<option value="any">Any</option>
|
||||
<option value="none">None</option>
|
||||
{languages.length > 0
|
||||
? languages.map((l: ILanguage, i: number) =>
|
||||
<option value={l.id} key={i}>
|
||||
{l.name} ({l.filterListIds ? l.filterListIds.length : 0})
|
||||
</option>)
|
||||
: null}
|
||||
</select>;
|
||||
|
||||
const sortMethod = (a: number[], b: number[], languages: ILanguage[]) => {
|
||||
if (a && a.length > 0) {
|
||||
if (b && b.length > 0) {
|
||||
const aLanguageNames =
|
||||
languages.filter((l: ILanguage) => a.indexOf(l.id) > -1).map((l: ILanguage) => l.name).join();
|
||||
const bLanguageNames =
|
||||
languages.filter((l: ILanguage) => b.indexOf(l.id) > -1).map((l: ILanguage) => l.name).join();
|
||||
return aLanguageNames.toLowerCase() > bLanguageNames.toLowerCase() ? 1 : -1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
const Cell = (languageIds: number[], languages: ILanguage[]) =>
|
||||
languageIds
|
||||
? <div className="fl-wrap-cell">
|
||||
{languageIds.map((id: number, i: number) => {
|
||||
const language = languages.filter((l: ILanguage) => l.id === id)[0];
|
||||
return <span className="badge badge-secondary"
|
||||
title={language.name}
|
||||
key={i}>
|
||||
{language.iso6391}
|
||||
</span>;
|
||||
})}
|
||||
</div>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
import * as React from "react";
|
||||
import { Column, Filter } from "react-table";
|
||||
import { IColumnVisibility, ILicense } from "../../../interfaces";
|
||||
|
||||
export const License = (columnVisibility: IColumnVisibility[], licenses: ILicense[]) =>
|
||||
({
|
||||
Header: <span title="A legal document governing the use or redistribution of a FilterList.">License</span>,
|
||||
accessor: "licenseId",
|
||||
filterable: true,
|
||||
filterMethod: (f: Filter, r: any[]) => filterMethod(f, r, licenses),
|
||||
Filter: ({ filter, onChange }: any) => Filter({ onChange, filter }, licenses),
|
||||
sortMethod: (a: number, b: number) => sortMethod(a, b, licenses),
|
||||
Cell: (c: any) => Cell(c.value, licenses),
|
||||
width: 75,
|
||||
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "License")[0].visible
|
||||
} as Column);
|
||||
|
||||
const filterMethod = (f: Filter, r: any[], licenses: ILicense[]): boolean => {
|
||||
const listLicenseId: number = r[f.id as any];
|
||||
if (f.value === "any") {
|
||||
return true;
|
||||
} else if (listLicenseId) {
|
||||
const licenseFiltered = licenses.filter((l: ILicense) => l.id === parseInt(f.value))[0];
|
||||
return listLicenseId === licenseFiltered.id;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const Filter = (props: any, licenses: ILicense[]) =>
|
||||
<select
|
||||
onChange={(event: any) => props.onChange(event.target.value)}
|
||||
style={{ width: "100%" }}
|
||||
value={props.filter ? props.filter.value : "any"}>
|
||||
<option value="any">Any</option>
|
||||
{licenses.length > 0
|
||||
? licenses.sort((a, b) => a.name.replace(/['"]+/g, "").localeCompare(b.name.replace(/['"]+/g, ""))).map(
|
||||
(l: ILicense, i: number) =>
|
||||
<option value={l.id} key={i}>
|
||||
{l.name} ({l.filterListIds ? l.filterListIds.length : 0})
|
||||
</option>)
|
||||
: null}
|
||||
</select>;
|
||||
|
||||
const sortMethod = (a: number, b: number, licenses: ILicense[]) => {
|
||||
if (a) {
|
||||
if (b) {
|
||||
const aLicenseName = licenses.filter((l: ILicense) => l.id === a)[0].name.replace(/['"]+/g, "");
|
||||
const bLicenseName = licenses.filter((l: ILicense) => l.id === b)[0].name.replace(/['"]+/g, "");
|
||||
return aLicenseName.toLowerCase() > bLicenseName.toLowerCase() ? 1 : -1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
const Cell = (licenseId: number, licenses: ILicense[]) => {
|
||||
const license = licenses.filter((l: ILicense) => licenseId === l.id)[0];
|
||||
return license
|
||||
? <div>
|
||||
<span title={license.name}>
|
||||
{license.descriptionUrl ? <a href={license.descriptionUrl}>{license.name}</a> : license.name}
|
||||
</span>
|
||||
</div>
|
||||
: null;
|
||||
};
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
import * as React from "react";
|
||||
import { Column, Filter } from "react-table";
|
||||
import { IColumnVisibility, IMaintainer } from "../../../interfaces";
|
||||
|
||||
export const Maintainers = (columnVisibility: IColumnVisibility[], maintainers: IMaintainer[]) =>
|
||||
({
|
||||
Header: <span title="Individuals, groups, or organizations who maintain the FilterList.">Maintainers</span>,
|
||||
accessor: "maintainerIds",
|
||||
filterable: true,
|
||||
filterMethod: (f: Filter, r: any[]) => filterMethod(f, r, maintainers),
|
||||
Filter: ({ filter, onChange }: any) => Filter({ onChange, filter }, maintainers),
|
||||
sortMethod: (a: number[], b: number[]) => sortMethod(a, b, maintainers),
|
||||
Cell: (c: any) => Cell(c.value, maintainers),
|
||||
width: 140,
|
||||
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Maintainers")[0].visible
|
||||
} as Column);
|
||||
|
||||
const filterMethod = (f: Filter, r: any[], maintainers: IMaintainer[]): boolean => {
|
||||
const listMaintainerIds: number[] = r[f.id as any];
|
||||
return f.value === "any" ||
|
||||
(listMaintainerIds
|
||||
? f.value === "none"
|
||||
? false
|
||||
: listMaintainerIds.indexOf(maintainers.filter((m: IMaintainer) => m.id === parseInt(f.value))[0].id) > -1
|
||||
: f.value === "none");
|
||||
};
|
||||
|
||||
const Filter = (props: any, maintainers: IMaintainer[]) =>
|
||||
<select
|
||||
onChange={(event: any) => props.onChange(event.target.value)}
|
||||
style={{ width: "100%" }}
|
||||
value={props.filter ? props.filter.value : "any"}>
|
||||
<option value="any">Any</option>
|
||||
<option value="none">None</option>
|
||||
{maintainers.length > 0
|
||||
? maintainers.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map((m: IMaintainer, i: number) =>
|
||||
<option value={m.id} key={i}>
|
||||
{m.name} ({m.filterListIds ? m.filterListIds.length : 0})
|
||||
</option>)
|
||||
: null}
|
||||
</select>;
|
||||
|
||||
const sortMethod = (a: number[], b: number[], maintainers: IMaintainer[]) => {
|
||||
if (a && a.length > 0) {
|
||||
if (b && b.length > 0) {
|
||||
const aFirstMaintainerName = maintainers.filter((m: IMaintainer) => m.id === a[0])[0].name;
|
||||
const bFirstMaintainerName = maintainers.filter((m: IMaintainer) => m.id === b[0])[0].name;
|
||||
return aFirstMaintainerName.toLowerCase() > bFirstMaintainerName.toLowerCase() ? 1 : -1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
const Cell = (maintainerIds: number[], maintainers: IMaintainer[]) =>
|
||||
maintainerIds
|
||||
? <div className="fl-wrap-cell">
|
||||
{maintainers.filter((m: IMaintainer) => maintainerIds.indexOf(m.id) > -1)
|
||||
.map((m: IMaintainer, i: number) => {
|
||||
return m.homeUrl
|
||||
? <a href={m.homeUrl} key={i}>{m.name}</a>
|
||||
: <span key={i}>{m.name}</span>;
|
||||
}).reduce(((prev: JSX.Element, curr: JSX.Element): any => [prev, ", ", curr]) as any)}
|
||||
</div>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import * as React from "react";
|
||||
import { Column, Filter } from "react-table";
|
||||
|
||||
export const Name = {
|
||||
Header: <span title="The name/title of the FilterList in title case.">Name</span>,
|
||||
accessor: "name",
|
||||
filterable: true,
|
||||
filterMethod: (f: Filter, r: any[]) => filterMethod(f, r),
|
||||
sortMethod: (a: string, b: string) => sortMethod(a, b),
|
||||
Cell: (c: any) => Cell(c.value)
|
||||
} as Column;
|
||||
|
||||
const filterMethod = (f: Filter, r: any[]): boolean =>
|
||||
r[f.id as any].toUpperCase().includes(f.value.toUpperCase());
|
||||
|
||||
const sortMethod = (a: string, b: string) =>
|
||||
a.toLowerCase() > b.toLowerCase() ? 1 : -1;
|
||||
|
||||
const Cell = (name: string) =>
|
||||
name
|
||||
? <h2 className="mb-0">{name}</h2>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import * as React from "react";
|
||||
import { Column } from "react-table";
|
||||
import { IColumnVisibility } from "../../../interfaces";
|
||||
|
||||
export const RuleCount = (columnVisibility: IColumnVisibility[]) =>
|
||||
({
|
||||
Header: <span title="The number of rules in the FilterList.">Rules</span>,
|
||||
accessor: "ruleCount",
|
||||
sortMethod: (a: string, b: string) => sortMethod(a, b),
|
||||
Cell: (c: any) => Cell(c.value),
|
||||
style: { whiteSpace: "inherit" },
|
||||
width: 85,
|
||||
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Rules")[0].visible
|
||||
} as Column);
|
||||
|
||||
const sortMethod = (a: string, b: string) =>
|
||||
a
|
||||
? b
|
||||
? a > b
|
||||
? -1
|
||||
: 1
|
||||
: -1
|
||||
: 1;
|
||||
|
||||
const Cell = (ruleCount: number) =>
|
||||
ruleCount
|
||||
? <span>{ ruleCount.toLocaleString() }</span>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
import * as React from "react";
|
||||
import { Column, Filter } from "react-table";
|
||||
import { IColumnVisibility, ISoftware } from "../../../interfaces";
|
||||
import { SoftwareIcon } from "../../softwareIcon";
|
||||
|
||||
export const Software = (columnVisibility: IColumnVisibility[], software: ISoftware[]) => {
|
||||
const softwareSorted = software.sort((a, b) => a.name.localeCompare(b.name));
|
||||
return ({
|
||||
Header:
|
||||
<span title="Applications, browser extensions, or other utilities that consume the FilterList.">Software</span>,
|
||||
accessor: "syntaxId",
|
||||
filterable: true,
|
||||
filterMethod: (f: Filter, r: any[]) => filterMethod(f, r, softwareSorted),
|
||||
Filter: ({ filter, onChange }: any) => Filter({ onChange, filter }, softwareSorted),
|
||||
sortMethod: (a: number, b: number) => sortMethod(a, b, softwareSorted),
|
||||
Cell: (c: any) => Cell(c.value, software),
|
||||
width: 155,
|
||||
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Software")[0].visible
|
||||
} as Column);
|
||||
};
|
||||
|
||||
const filterMethod = (f: Filter, r: any[], software: ISoftware[]): boolean => {
|
||||
const isAny = f.value === "any";
|
||||
const softwareFiltered = isAny ? software : software.filter((s: ISoftware) => s.id === parseInt(f.value));
|
||||
const listSyntaxId: number = r[f.id as any];
|
||||
const isMatch = softwareFiltered[0].syntaxIds ? softwareFiltered[0].syntaxIds.indexOf(listSyntaxId) > -1 : false;
|
||||
return isAny || (listSyntaxId ? isMatch : false);
|
||||
};
|
||||
|
||||
const Filter = (props: any, software: ISoftware[]) =>
|
||||
<select
|
||||
onChange={(event: any) => props.onChange(event.target.value)}
|
||||
style={{ width: "100%" }}
|
||||
value={props.filter ? props.filter.value : "any"}>
|
||||
<option value="any">Any</option>
|
||||
{software.length > 0
|
||||
? software.map((s: ISoftware, i: number) => <option value={s.id} key={i}>{s.name}</option>)
|
||||
: null}
|
||||
</select>;
|
||||
|
||||
const sortMethod = (a: number, b: number, software: ISoftware[]) => {
|
||||
if (a) {
|
||||
if (b) {
|
||||
const aSoftwareNames =
|
||||
software.filter((s: ISoftware) => s.syntaxIds.indexOf(a) > -1).map((s: ISoftware) => s.name);
|
||||
const bSoftwareNames =
|
||||
software.filter((s: ISoftware) => s.syntaxIds.indexOf(b) > -1).map((s: ISoftware) => s.name);
|
||||
if (aSoftwareNames.length === bSoftwareNames.length) {
|
||||
return aSoftwareNames.join().toLowerCase() > bSoftwareNames.join().toLowerCase() ? 1 : -1;
|
||||
} else if (aSoftwareNames.length > bSoftwareNames.length) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
const Cell = (listSyntaxId: number, software: ISoftware[]) =>
|
||||
listSyntaxId
|
||||
? <div className="fl-wrap-cell">
|
||||
{software.filter((s: ISoftware) => s.syntaxIds.indexOf(listSyntaxId) > -1)
|
||||
.map((s: ISoftware, i: number) =>
|
||||
s.homeUrl
|
||||
? <a href={s.homeUrl} key={i}>
|
||||
<SoftwareIcon id={s.id}/>
|
||||
</a>
|
||||
: <SoftwareIcon id={s.id} key={i}/>)}
|
||||
</div>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import * as React from "react";
|
||||
import { Column, Filter } from "react-table";
|
||||
import { IColumnVisibility } from "../../../interfaces";
|
||||
import { SubscribeButtonGroup } from "../../linkButtons";
|
||||
|
||||
export const SubscribeButton = (columnVisibility: IColumnVisibility[]) => ({
|
||||
Header: <span title={`Subscribe to the list with a browser extension supporting the "abp:" protocol (e.g. uBlock Origin, Adblock Plus)`}>
|
||||
Subscribe
|
||||
</span>,
|
||||
accessor: "viewUrl",
|
||||
filterable: true,
|
||||
filterMethod: (f: Filter, r: any[]) => filterMethod(f, r),
|
||||
sortMethod: (a: string, b: string) => sortMethod(a, b),
|
||||
Cell: (c: any) => <SubscribeButtonGroup name={c.row.name} url={c.value} urlMirrors={c.row.viewUrlMirrors}/>,
|
||||
style: { textAlign: "center" },
|
||||
width: 110,
|
||||
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Subscribe")[0].visible
|
||||
} as Column);
|
||||
|
||||
const filterMethod = (f: Filter, r: any[]): boolean =>
|
||||
r[f.id as any].toUpperCase().includes(f.value.toUpperCase());
|
||||
|
||||
const sortMethod = (a: string, b: string) =>
|
||||
a.toLowerCase() > b.toLowerCase() ? 1 : -1;
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import * as React from "react";
|
||||
import { Column, Filter } from "react-table";
|
||||
import { IColumnVisibility, ISyntax } from "../../../interfaces";
|
||||
|
||||
//TODO: https://github.com/collinbarrett/FilterLists/issues/488
|
||||
export const Syntax = (columnVisibility: IColumnVisibility[], syntaxes: ISyntax[]) => ({
|
||||
Header: <span title="A named set of rules that govern the format of the FilterList.">Syntax</span>,
|
||||
accessor: "syntaxId",
|
||||
filterable: true,
|
||||
filterMethod: (f: Filter, r: any[]) => filterMethod(f, r, syntaxes),
|
||||
Filter: ({ filter, onChange }: any) => Filter({ onChange, filter }, syntaxes),
|
||||
Cell: (c: any) => Cell(c.value, syntaxes),
|
||||
width: 155,
|
||||
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Syntax")[0].visible
|
||||
} as Column);
|
||||
|
||||
const filterMethod = (f: Filter, r: any[], syntaxes: ISyntax[]): boolean => {
|
||||
const listSyntaxId: number = r[f.id as any];
|
||||
if (f.value === "any") {
|
||||
return true;
|
||||
} else if (listSyntaxId) {
|
||||
const syntaxFiltered = syntaxes.filter((s: ISyntax) => s.id === parseInt(f.value));
|
||||
return syntaxFiltered[0].id
|
||||
? syntaxFiltered[0].id === listSyntaxId
|
||||
: false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const Filter = (props: any, syntaxes: ISyntax[]) =>
|
||||
<select
|
||||
onChange={(event: any) => props.onChange(event.target.value)}
|
||||
style={{ width: "100%" }}
|
||||
value={props.filter ? props.filter.value : "any"}>
|
||||
<option value="any">Any</option>
|
||||
{syntaxes.length > 0
|
||||
? syntaxes.sort((a, b) => a.name.localeCompare(b.name)).map(
|
||||
(s: ISyntax, i: number) => <option value={s.id} key={i}>{s.name}</option>)
|
||||
: null}
|
||||
</select>;
|
||||
|
||||
const Cell = (listSyntaxId: number, syntaxes: ISyntax[]) =>
|
||||
listSyntaxId
|
||||
? syntaxes.filter((s: ISyntax) => s.id === listSyntaxId)[0].name
|
||||
: null;
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
import * as React from "react";
|
||||
import { Column, Filter } from "react-table";
|
||||
import { TagGroup } from "../../TagGroup";
|
||||
import { IColumnVisibility, ITag } from "../../../interfaces";
|
||||
|
||||
export const Tags = (columnVisibility: IColumnVisibility[], tags: ITag[]) => {
|
||||
const tagsSorted = tags.sort((a: ITag, b: ITag) => a.name.localeCompare(b.name));
|
||||
return ({
|
||||
Header:
|
||||
<span title="Generic taxonomies applied to the FilterList to provide information about its contents and/or purpose.">Tags</span>,
|
||||
accessor: "tagIds",
|
||||
filterable: true,
|
||||
filterMethod: (f: Filter, r: any[]) => filterMethod(f, r),
|
||||
Filter: ({ onChange, filter }: any) => Filter({ onChange, filter }, tagsSorted),
|
||||
sortMethod: (a: number[], b: number[]) => sortMethod(a, b, tagsSorted),
|
||||
Cell: (c: any) => Cell(c.value, tagsSorted),
|
||||
width: 260,
|
||||
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Tags")[0].visible
|
||||
} as Column);
|
||||
};
|
||||
|
||||
const filterMethod = (f: Filter, r: any[]): boolean => {
|
||||
const listTagIds = r[f.id as any];
|
||||
return f.value === "any" ||
|
||||
(listTagIds
|
||||
? listTagIds.join(",").split(",").includes(f.value)
|
||||
: f.value === "none");
|
||||
};
|
||||
|
||||
const Filter = (props: any, tags: ITag[]) =>
|
||||
<select onChange={(event: any) => props.onChange(event.target.value)}
|
||||
style={{ width: "100%" }}
|
||||
value={props.filter ? props.filter.value : "any"}>
|
||||
<option value="any">Any</option>
|
||||
<option value="none">None</option>
|
||||
{tags.length > 0
|
||||
? tags.map((t: ITag, i: number) =>
|
||||
<option value={t.id} title={t.description} key={i}>
|
||||
{t.name} ({t.filterListIds ? t.filterListIds.length : 0})
|
||||
</option>)
|
||||
: null}
|
||||
</select>;
|
||||
|
||||
const sortMethod = (a: number[], b: number[], tags: ITag[]): any => {
|
||||
return a
|
||||
? b
|
||||
? a.length === b.length
|
||||
? tags.filter((t: ITag) => a.indexOf(t.id) > -1).map((t: ITag) => t.name).join()
|
||||
.toLowerCase() >
|
||||
tags.filter((t: ITag) => b.indexOf(t.id) > -1).map((t: ITag) => t.name).join().toLowerCase()
|
||||
? 1
|
||||
: -1
|
||||
: a.length > b.length
|
||||
? -1
|
||||
: 1
|
||||
: -1
|
||||
: 1;
|
||||
};
|
||||
|
||||
const Cell = (tagIds: number[], tags: ITag[]) =>
|
||||
tagIds
|
||||
? <TagGroup tags={tags.filter((t: ITag) => tagIds.indexOf(t.id) > -1)}/>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import * as React from "react";
|
||||
import { Column } from "react-table";
|
||||
import * as moment from "moment";
|
||||
import { IColumnVisibility } from "../../../interfaces";
|
||||
|
||||
export const UpdatedDate = (columnVisibility: IColumnVisibility[]) =>
|
||||
({
|
||||
Header: <span title="The estimated date that the FilterList was last updated by the maintainer.">Updated</span>,
|
||||
accessor: "updatedDate",
|
||||
sortMethod: (a: string, b: string) => sortMethod(a, b),
|
||||
Cell: (c: any) => Cell(c.value),
|
||||
style: { whiteSpace: "inherit" },
|
||||
width: 100,
|
||||
show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Updated")[0].visible
|
||||
} as Column);
|
||||
|
||||
const sortMethod = (a: string, b: string) =>
|
||||
a && moment(a).isValid()
|
||||
? (b && moment(b).isValid()
|
||||
? (moment(a).isBefore(b)
|
||||
? 1
|
||||
: -1)
|
||||
: -1)
|
||||
: 1;
|
||||
|
||||
const Cell = (updatedDate: string) =>
|
||||
updatedDate
|
||||
? <div>
|
||||
{moment(updatedDate).isValid()
|
||||
? moment(updatedDate).format("l")
|
||||
: null}
|
||||
</div>
|
||||
: null;
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { DetailsButton } from "./DetailsButton";
|
||||
import { Languages } from "./Languages";
|
||||
import { License } from "./License";
|
||||
import { Maintainers } from "./Maintainers";
|
||||
import { Name } from "./Name";
|
||||
import { RuleCount } from "./RuleCount";
|
||||
import { Software } from "./Software";
|
||||
import { SubscribeButton } from "./SubscribeButton";
|
||||
import { Syntax } from "./Syntax";
|
||||
import { Tags } from "./Tags";
|
||||
import { UpdatedDate } from "./UpdatedDate";
|
||||
|
||||
export {
|
||||
DetailsButton,
|
||||
Languages,
|
||||
License,
|
||||
Maintainers,
|
||||
Name,
|
||||
RuleCount,
|
||||
Software,
|
||||
SubscribeButton,
|
||||
Syntax,
|
||||
Tags,
|
||||
UpdatedDate
|
||||
};
|
||||
5
src/web/src/modules/home/components/listsTable/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { ListsTable } from "./ListsTable";
|
||||
|
||||
export {
|
||||
ListsTable,
|
||||
};
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
.rt-tr {
|
||||
-ms-align-items: center;
|
||||
-o-align-items: center;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
div.rt-tbody, div.rt-thead.-filters, div.rt-thead.-header { min-width: 320px !important; }
|
||||
|
||||
.fl-wrap-cell {
|
||||
line-height: 1;
|
||||
white-space: normal;
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
import * as React from "react";
|
||||
import {
|
||||
img1,
|
||||
img2,
|
||||
img3,
|
||||
img4,
|
||||
img5,
|
||||
img6,
|
||||
img7,
|
||||
img8,
|
||||
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
|
||||
} from "./imgs";
|
||||
|
||||
interface IProps {
|
||||
id: number;
|
||||
};
|
||||
|
||||
export const SoftwareIcon = (props: IProps) =>
|
||||
icons[props.id]
|
||||
? <img src={icons[props.id].image}
|
||||
height="20"
|
||||
alt={icons[props.id].imageTitle}
|
||||
title={icons[props.id].imageTitle}/>
|
||||
: null;
|
||||
|
||||
interface IIcon {
|
||||
image: any;
|
||||
imageTitle: string;
|
||||
};
|
||||
|
||||
const icons: { [id: number]: IIcon; } = {
|
||||
1: { image: img1, imageTitle: "uBlock Origin" },
|
||||
2: { image: img2, imageTitle: "Adblock Plus" },
|
||||
3: { image: img3, imageTitle: "AdGuard" },
|
||||
4: { image: img4, imageTitle: "DNS66" },
|
||||
5: { image: img5, imageTitle: "Nano Adblocker" },
|
||||
6: { image: img6, imageTitle: "AdBlock" },
|
||||
7: { image: img7, imageTitle: "AdAway" },
|
||||
8: { image: img8, 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" },
|
||||
};
|
||||
|
|
@ -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 |
|
|
@ -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 |
|
After Width: | Height: | Size: 893 B |
|
|
@ -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 |
|
After Width: | Height: | Size: 851 B |
|
After Width: | Height: | Size: 698 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 658 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 547 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 424 B |
|
|
@ -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 |
|
After Width: | Height: | Size: 700 B |
|
|
@ -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 |
|
|
@ -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 |
|
After Width: | Height: | Size: 1 KiB |
|
|
@ -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 |
|
After Width: | Height: | Size: 636 B |
|
After Width: | Height: | Size: 617 B |
|
After Width: | Height: | Size: 476 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 403 B |
|
After Width: | Height: | Size: 789 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 917 B |
|
After Width: | Height: | Size: 998 B |
|
After Width: | Height: | Size: 750 B |
|
After Width: | Height: | Size: 831 B |
|
|
@ -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 |
|
After Width: | Height: | Size: 565 B |
|
After Width: | Height: | Size: 520 B |
|
After Width: | Height: | Size: 878 B |
|
After Width: | Height: | Size: 713 B |
|
After Width: | Height: | Size: 867 B |
|
After Width: | Height: | Size: 470 B |
9
src/web/src/modules/home/components/softwareIcon/imgs/imgs.d.ts
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
declare module "*.svg" {
|
||||
const content: any;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module "*.png" {
|
||||
const content: any;
|
||||
export default content;
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
import img1 from "./01-uBlock-Origin.svg";
|
||||
import img2 from "./02-Adblock-Plus.svg";
|
||||
import img3 from "./03-AdGuard.svg";
|
||||
import img4 from "./04-DNS66.png";
|
||||
import img5 from "./05-Nano-Adblocker.png";
|
||||
import img6 from "./06-AdBlock.png";
|
||||
import img7 from "./07-AdAway.png";
|
||||
import img8 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";
|
||||
|
||||
export {
|
||||
img1,
|
||||
img2,
|
||||
img3,
|
||||
img4,
|
||||
img5,
|
||||
img6,
|
||||
img7,
|
||||
img8,
|
||||
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
|
||||
};
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { SoftwareIcon } from "./SoftwareIcon";
|
||||
|
||||
export {
|
||||
SoftwareIcon
|
||||
};
|
||||
5
src/web/src/modules/home/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { HomeContainer } from "./HomeContainer";
|
||||
|
||||
export {
|
||||
HomeContainer as Home
|
||||
};
|
||||
4
src/web/src/modules/home/interfaces/IColumnVisibility.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export interface IColumnVisibility {
|
||||
column: string;
|
||||
visible: boolean;
|
||||
};
|
||||
6
src/web/src/modules/home/interfaces/ILanguage.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export interface ILanguage {
|
||||
id: number;
|
||||
filterListIds: number[];
|
||||
iso6391: string;
|
||||
name: string;
|
||||
};
|
||||
6
src/web/src/modules/home/interfaces/ILicense.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
export interface ILicense {
|
||||
id: number;
|
||||
descriptionUrl: string;
|
||||
filterListIds: number[];
|
||||
name: string;
|
||||
};
|
||||