replace v1 UI w/v2

This commit is contained in:
Collin M. Barrett 2019-07-27 07:59:49 -05:00
parent af070b9da5
commit ac0e6c097b
264 changed files with 66 additions and 8781 deletions

View file

@ -23,6 +23,7 @@
# Other common
**/.gitattributes
**/.github
**/.sln
**/docker-compose.dcproj
**/*.DotSettings*
**/appsettings*.json
@ -31,8 +32,6 @@
LICENSE
# FilterLists custom
FilterLists.sln
FilterLists.Api.sln
imgs
ops
data/TPLSubscriptionAssistant.html

View file

@ -17,7 +17,7 @@ services:
restart: always
build:
context: .
dockerfile: src/web/Dockerfile
dockerfile: src/FilterLists.Web/Dockerfile
target: final
networks:
- reverse-proxy

View file

@ -1,26 +0,0 @@
import * as React from "react";
import * as ReactDOM from "react-dom";
import { AppContainer } from "react-hot-loader";
import { BrowserRouter } from "react-router-dom";
import * as RoutesModule from "./Routes";
let routes = RoutesModule.Routes;
const renderApp = () => {
const baseUrl = document.getElementsByTagName("base")[0].getAttribute("href")!;
ReactDOM.render(
<AppContainer>
<BrowserRouter children={ routes } basename={ baseUrl }/>
</AppContainer>,
document.getElementById("react-app")
);
};
renderApp();
if (module.hot) {
module.hot.accept("./Routes",
() => {
routes = require<typeof RoutesModule>("./Routes").Routes;
renderApp();
});
}

View file

@ -1,62 +0,0 @@
import * as React from "react";
import "bootstrap";
import "./site.css";
interface IProps {
children?: React.ReactNode;
};
export const Layout = (props: IProps) =>
<div className="container">
<Header/>
<div className="row">
<div className="w-100">
{ props.children }
</div>
</div>
<Footer/>
</div>;
const Header = () =>
<header className="row">
<h1>
<a href="./">
<img src="logo_filterlists.png" alt="FilterLists" className="img-fluid"/>
</a>
</h1>
</header>;
const Footer = () =>
<footer className="row justify-content-center">
<p className="mt-2 ml-1 mr-1">
<HubLink/> | <GitHubLink/> | <ApiLink/> | <DonateLink/> | By <OwnerLink/>
</p>
</footer>;
const HubLink = () =>
<a href="https://hub.filterlists.com"
title="Discourse community forum">
Hub
</a>;
const GitHubLink = () =>
<a href="https://github.com/collinbarrett/FilterLists">
GitHub
</a>;
const ApiLink = () =>
<a href="/api/v1/lists"
title="API lists endpoint">
API
</a>;
const DonateLink = () =>
<a href="https://beerpay.io/collinbarrett/FilterLists"
title="Support with Beerpay">
Donate
</a>;
const OwnerLink = () =>
<a href="https://collinmbarrett.com/">
Collin M. Barrett
</a>;

View file

@ -1,107 +0,0 @@
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:&nbsp;&nbsp;{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;
};
};

View file

@ -1,94 +0,0 @@
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}/>;
};
};

View file

@ -1,67 +0,0 @@
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
];

View file

@ -1,27 +0,0 @@
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>;

View file

@ -1,26 +0,0 @@
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[];
};

View file

@ -1,7 +0,0 @@
import { DetailsExpander } from "./DetailsExpander";
import { IListDetails } from "./IListDetails";
export {
DetailsExpander,
IListDetails
};

View file

@ -1,57 +0,0 @@
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>;

View file

@ -1,20 +0,0 @@
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;

View file

@ -1,17 +0,0 @@
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;

View file

@ -1,13 +0,0 @@
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;

View file

@ -1,17 +0,0 @@
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;

View file

@ -1,15 +0,0 @@
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;

View file

@ -1,23 +0,0 @@
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;

View file

@ -1,14 +0,0 @@
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;

View file

@ -1,62 +0,0 @@
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)}&amp;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;
};

View file

@ -1,104 +0,0 @@
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);

View file

@ -1,71 +0,0 @@
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;

View file

@ -1,68 +0,0 @@
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;
};

View file

@ -1,68 +0,0 @@
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;

View file

@ -1,28 +0,0 @@
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;

View file

@ -1,74 +0,0 @@
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;

View file

@ -1,24 +0,0 @@
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;

View file

@ -1,46 +0,0 @@
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;

View file

@ -1,63 +0,0 @@
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;

View file

@ -1,33 +0,0 @@
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;

View file

@ -1,19 +0,0 @@
import { IColumnVisibility } from "./IColumnVisibility";
import { ILanguage } from "./ILanguage";
import { ILicense } from "./ILicense";
import { IList } from "./IList";
import { IMaintainer } from "./IMaintainer";
import { ISoftware } from "./ISoftware";
import { ISyntax } from "./ISyntax";
import { ITag } from "./ITag";
export {
IColumnVisibility,
ILanguage,
ILicense,
IList,
IMaintainer,
ISoftware,
ISyntax,
ITag
};

View file

@ -1,3 +0,0 @@
body, h2, h3, h4, h5, blockquote { font-size: 16px; }
.container{ max-width:98vw!important;padding-left:7px!important }
.w-100{ padding-left:7px!important }

View file

@ -1,16 +0,0 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
namespace FilterLists.Web.Controllers
{
public class HomeController : Controller
{
public IActionResult Index() => View();
public IActionResult Error()
{
ViewData["RequestId"] = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
return View();
}
}
}

View file

@ -1,26 +0,0 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
namespace FilterLists.Web.DependencyInjection.Extensions
{
public static class ConfigureServicesCollection
{
public static void AddFilterListsWeb(this IServiceCollection services)
{
services.ConfigureCookiePolicy();
services.AddMvcCustom();
}
private static void ConfigureCookiePolicy(this IServiceCollection services) =>
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
private static void AddMvcCustom(this IServiceCollection services) =>
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
}

View file

@ -2,28 +2,14 @@
# Command: docker build -f src/FilterLists.Web/Dockerfile .
# init base
FROM mcr.microsoft.com/dotnet/core/runtime-deps:2.2-alpine as base
EXPOSE 80
ENTRYPOINT ["./FilterLists.Web"]
WORKDIR /app
FROM node:alpine as final
CMD ["npm", "start"]
EXPOSE 3000
WORKDIR /usr/app/
# init build
FROM mcr.microsoft.com/dotnet/core/sdk:2.2-alpine AS build
WORKDIR /app
RUN apk add --update nodejs nodejs-npm
# install
COPY src/web/package*.json ./
RUN npm install --only=prod
# restore
COPY FilterLists.Web.sln ./
WORKDIR /app/src/FilterLists.Web/
COPY src/FilterLists.Web/FilterLists.Web.csproj ./
WORKDIR /app
RUN dotnet restore
# publish
WORKDIR /app/src/FilterLists.Web/
COPY src/FilterLists.Web/. ./
RUN dotnet publish -c Release -r linux-musl-x64 -o out --no-restore
# run
FROM base as final
COPY --from=build /app/src/FilterLists.Web/out ./
# final
COPY src/web/. ./

View file

@ -1,73 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<LangVersion>latest</LangVersion>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<RuntimeIdentifier>linux-musl-x64</RuntimeIdentifier>
<DockerComposeProjectPath>..\..\docker-compose.dcproj</DockerComposeProjectPath>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
</PropertyGroup>
<PropertyGroup>
<Authors>Collin M. Barrett</Authors>
<Company>Collin M. Barrett</Company>
<Product>FilterLists</Product>
<Description>The independent, comprehensive directory of filter and host lists for advertisements, trackers, malware, and annoyances.</Description>
<Copyright>Copyright (c) 2019 Collin M. Barrett</Copyright>
<RepositoryUrl>https://github.com/collinbarrett/FilterLists</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<ItemGroup>
<RuntimeHostConfigurationOption Include="System.Globalization.Invariant" Value="true" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.3" />
<PackageReference Include="Microsoft.DotNet.Analyzers.Compatibility" Version="0.2.12-alpha" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.7.12" />
</ItemGroup>
<ItemGroup>
<Content Remove="ClientApp\**" />
</ItemGroup>
<ItemGroup>
<Content Update="wwwroot\icon_filterlists.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\logo_filterlists.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Target Name="DebugRunWebpack" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' ">
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'"
Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<Message Importance="high" Text="Performing Webpack build..." />
<Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js" />
<Exec Command="node node_modules/webpack/bin/webpack.js" />
</Target>
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />
<Exec Command="node node_modules/webpack/bin/webpack.js --env.prod" />
<ItemGroup>
<DistFiles Include="wwwroot\dist\**; ClientApp\dist\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>

View file

@ -1,13 +0,0 @@
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace FilterLists.Web
{
public static class Program
{
public static void Main(string[] args) => CreateWebHostBuilder(args).Build().Run();
private static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args).UseStartup<Startup>();
}
}

View file

@ -1,38 +0,0 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:5001",
"sslPort": 0
}
},
"profiles": {
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"FilterLists.Api": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
"applicationUrl": "http://localhost:5001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View file

@ -1,44 +0,0 @@
using FilterLists.Web.DependencyInjection.Extensions;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.Extensions.DependencyInjection;
namespace FilterLists.Web
{
[UsedImplicitly]
public class Startup
{
[UsedImplicitly]
public void ConfigureServices(IServiceCollection services) => services.AddFilterListsWeb();
[UsedImplicitly]
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true,
ReactHotModuleReplacement = true
});
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute("spa-fallback", new {controller = "Home", action = "Index"});
});
}
}
}

View file

@ -1,8 +0,0 @@
@{
ViewData["Title"] = "Subscriptions for uBlock Origin, Adblock Plus, AdGuard, ...";
}
<div id="react-app">Loading...</div>
@section scripts {
<script src="~/dist/main.js" asp-append-version="true"></script>
}

View file

@ -1,21 +0,0 @@
@{
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (!string.IsNullOrEmpty((string) ViewData["RequestId"]))
{
<p>
<strong>Request ID:</strong> <code>@ViewData["RequestId"]</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>Development environment should not be enabled in deployed applications</strong>, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>, and restarting the application.
</p>

View file

@ -1,24 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>FilterLists | @ViewData["Title"]</title>
<base href="~/"/>
<meta name="description" content="FilterLists is the independent, comprehensive directory of filter and host lists for advertisements, trackers, malware, and annoyances. By Collin M. Barrett."/>
<link rel="icon" type="image/png" href="icon_filterlists.png">
<link rel="stylesheet" href="~/dist/vendor.css" asp-append-version="true"/>
<link rel="canonical" href="https://filterlists.com/">
<environment exclude="Development">
<link rel="stylesheet" href="~/dist/site.css" asp-append-version="true"/>
</environment>
</head>
<body>
@RenderBody()
<script src="~/dist/vendor.js" asp-append-version="true"></script>
@RenderSection("scripts", false)
<noscript>
<p>FilterLists is built with ReactJS and therefore requires first-party JavaScript to be enabled. We do not use any third-party JavaScript, and your privacy is very important to us. If you prefer not to enable JavaScript or your browser does not support it, the data is largely available on <a href="https://github.com/collinbarrett/FilterLists">our GitHub repo</a>.</p>
</noscript>
</body>
</html>

View file

@ -1,3 +0,0 @@
@using FilterLists.Web
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Microsoft.AspNetCore.SpaServices

View file

@ -1,3 +0,0 @@
@{
Layout = "_Layout";
}

File diff suppressed because it is too large Load diff

View file

@ -1,37 +1,45 @@
{
"name": "filterlists",
"name": "web",
"version": "0.1.0",
"private": true,
"version": "0.0.0",
"devDependencies": {
"@types/history": "^4.7.2",
"@types/react": "^16.7.13",
"@types/react-dom": "^16.0.11",
"@types/react-hot-loader": "^4.1.0",
"@types/react-router": "^5.0.3",
"dependencies": {
"@types/jest": "24.0.15",
"@types/node": "12.6.8",
"@types/react": "16.8.23",
"@types/react-dom": "16.8.5",
"@types/react-router-dom": "^4.3.4",
"@types/webpack-env": "^1.14.0",
"aspnet-webpack": "^3.0.0",
"aspnet-webpack-react": "^4.0.0",
"awesome-typescript-loader": "^4.0.1",
"bootstrap": "^4.2.1",
"css-loader": "^0.28.11",
"event-source-polyfill": "1.0.7",
"extract-text-webpack-plugin": "^3.0.2",
"@types/react-table": "^6.8.5",
"bootstrap": "^4.3.1",
"es6-promise": "^4.2.8",
"isomorphic-fetch": "^2.2.1",
"jquery": "^3.3.1",
"moment": "^2.24.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-hot-loader": "^4.12.9",
"react-router-dom": "^5.0.1",
"style-loader": "^0.23.1",
"typescript": "^3.5.3",
"url-loader": "^1.1.2",
"webpack": "^3.12.0"
"react-scripts": "3.0.1",
"react-table": "^6.10.0",
"typescript": "3.5.3"
},
"dependencies": {
"@types/react-table": "^6.7.18",
"moment": "^2.24.0",
"popper.js": "^1.15.0",
"react-table": "^6.10.0"
}
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {}
}

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

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