mirror of
https://github.com/collinbarrett/FilterLists.git
synced 2026-03-11 09:04:27 +00:00
replace v1 UI w/v2
This commit is contained in:
parent
af070b9da5
commit
ac0e6c097b
264 changed files with 66 additions and 8781 deletions
|
|
@ -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
|
||||
|
|
@ -17,7 +17,7 @@ services:
|
|||
restart: always
|
||||
build:
|
||||
context: .
|
||||
dockerfile: src/web/Dockerfile
|
||||
dockerfile: src/FilterLists.Web/Dockerfile
|
||||
target: final
|
||||
networks:
|
||||
- reverse-proxy
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
|
@ -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>;
|
||||
|
|
@ -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: {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;
|
||||
};
|
||||
};
|
||||
|
|
@ -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}/>;
|
||||
};
|
||||
};
|
||||
|
|
@ -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
|
||||
];
|
||||
|
|
@ -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>;
|
||||
|
|
@ -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[];
|
||||
};
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import { DetailsExpander } from "./DetailsExpander";
|
||||
import { IListDetails } from "./IListDetails";
|
||||
|
||||
export {
|
||||
DetailsExpander,
|
||||
IListDetails
|
||||
};
|
||||
|
|
@ -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>;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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)}&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;
|
||||
};
|
||||
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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 }
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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/. ./
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@using FilterLists.Web
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@addTagHelper *, Microsoft.AspNetCore.SpaServices
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
6015
src/FilterLists.Web/npm-shrinkwrap.json
generated
6015
src/FilterLists.Web/npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
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
Loading…
Reference in a new issue