diff --git a/src/web/src/modules/home/Home.tsx b/src/web/src/modules/home/Home.tsx new file mode 100644 index 000000000..06daee6a3 --- /dev/null +++ b/src/web/src/modules/home/Home.tsx @@ -0,0 +1,107 @@ +import * as React from "react"; +import { IColumnVisibility, ILanguage, ILicense, IList, IMaintainer, ISoftware, ISyntax, ITag } from "./interfaces"; +import { ListsTable, Oneliner } from "./components"; + +const columnVisibilityDefaults: IColumnVisibility[] = [ + { column: "Software", visible: true }, + { column: "Languages", visible: true }, + { column: "Tags", visible: true }, + //{ column: "Updated", visible: false }, + //{ column: "Rules", visible: false }, + { column: "License", visible: false }, + { column: "Maintainers", visible: false }, + { column: "Subscribe", visible: false } +]; + +interface IProps { + languages: ILanguage[]; + licenses: ILicense[]; + lists: IList[]; + maintainers: IMaintainer[]; + //ruleCount: number; + software: ISoftware[]; + syntaxes: ISyntax[]; + tags: ITag[]; +}; + +interface IState { + columnVisibility: IColumnVisibility[]; + pageSize: number; +}; + +export class Home extends React.Component { + 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
+ + + {this.renderColumnVisibilityCheckboxes()} +
; + }; + + renderColumnVisibilityCheckboxes() { + return this.props.lists.length > 0 + ?
+ Visible:  {this.state.columnVisibility.map( + (c: IColumnVisibility, i: number) => this.renderColumnVisibilityCheckbox(c, i))} +
+ : null; + }; + + renderColumnVisibilityCheckbox(props: IColumnVisibility, key: number) { + return
+ this.checkColumn(props)}/> + +
; + }; + + 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; + }; +}; \ No newline at end of file diff --git a/src/web/src/modules/home/HomeContainer.tsx b/src/web/src/modules/home/HomeContainer.tsx new file mode 100644 index 000000000..b5a34ba0e --- /dev/null +++ b/src/web/src/modules/home/HomeContainer.tsx @@ -0,0 +1,94 @@ +import * as React from "react"; +import "isomorphic-fetch"; +import { ILanguage, ILicense, IList, IMaintainer, ISoftware, ISyntax, ITag } from "./interfaces"; +import { Home } from "./Home"; + +interface IState { + languages: ILanguage[]; + licenses: ILicense[]; + lists: IList[]; + maintainers: IMaintainer[]; + ruleCount: number; + software: ISoftware[]; + syntaxes: ISyntax[]; + tags: ITag[]; +}; + +export class HomeContainer extends React.Component<{}, IState> { + constructor(props: any) { + super(props); + this.state = { + languages: [], + licenses: [], + lists: [], + maintainers: [], + ruleCount: 0, + software: [], + syntaxes: [], + tags: [] + }; + } + + componentDidMount() { + this.fetchLanguages(); + this.fetchLicenses(); + this.fetchLists(); + this.fetchMaintainers(); + this.fetchSoftware(); + this.fetchSyntaxes(); + this.fetchTags(); + // this.fetchRuleCount(); + }; + + fetchLanguages() { + fetch("/api/v1/languages") + .then(r => r.json() as Promise) + .then(p => { this.setState({ languages: p }); }); + }; + + fetchLicenses() { + fetch("/api/v1/licenses") + .then(r => r.json() as Promise) + .then(p => { this.setState({ licenses: p }); }); + }; + + fetchLists() { + fetch("/api/v1/lists") + .then(r => r.json() as Promise) + .then(p => { this.setState({ lists: p }); }); + }; + + fetchMaintainers() { + fetch("/api/v1/maintainers") + .then(r => r.json() as Promise) + .then(p => { this.setState({ maintainers: p }); }); + }; + + fetchSoftware() { + fetch("/api/v1/software") + .then(r => r.json() as Promise) + .then(p => { this.setState({ software: p }); }); + }; + + fetchSyntaxes() { + fetch("/api/v1/syntaxes") + .then(r => r.json() as Promise) + .then(p => { this.setState({ syntaxes: p }); }); + }; + + fetchTags() { + fetch("/api/v1/tags") + .then(r => r.json() as Promise) + .then(p => { this.setState({ tags: p }); }); + }; + + fetchRuleCount() { + fetch("/api/v1/rules") + .then(r => r.json() as Promise) + .then(p => { this.setState({ ruleCount: p }); }); + }; + + render() { + return ; + }; +}; \ No newline at end of file diff --git a/src/web/src/modules/home/components/Oneliner.tsx b/src/web/src/modules/home/components/Oneliner.tsx new file mode 100644 index 000000000..5ad124110 --- /dev/null +++ b/src/web/src/modules/home/components/Oneliner.tsx @@ -0,0 +1,17 @@ +import * as React from "react"; + +interface IProps { + listCount: number; + //ruleCount: number; +}; + +export const Oneliner = (props: IProps) => + props.listCount > 0/* && props.ruleCount > 0*/ + ?

+ The independent, comprehensive directory of {/*}{props.ruleCount.toLocaleString() + } unique rules across */}{props.listCount.toLocaleString() + } filter and host lists for advertisements, trackers, malware, and annoyances. +

+ :

+ The independent, comprehensive directory of filter and host lists for advertisements, trackers, malware, and annoyances. +

; \ No newline at end of file diff --git a/src/web/src/modules/home/components/TagGroup.tsx b/src/web/src/modules/home/components/TagGroup.tsx new file mode 100644 index 000000000..5f7a609da --- /dev/null +++ b/src/web/src/modules/home/components/TagGroup.tsx @@ -0,0 +1,67 @@ +import * as React from "react"; +import { ITag } from "../interfaces"; +import { getContrast } from "../../../utils"; + +interface IProps { + tags: ITag[]; +}; + +export const TagGroup = (props: IProps) => + props.tags && props.tags.length > 0 + ?
+ {props.tags.map((t: ITag, i: number) => )} +
+ : 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 + {props.tag.name} + ; + } 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 +]; diff --git a/src/web/src/modules/home/components/detailsExpander/DetailsExpander.tsx b/src/web/src/modules/home/components/detailsExpander/DetailsExpander.tsx new file mode 100644 index 000000000..852dbea32 --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/DetailsExpander.tsx @@ -0,0 +1,27 @@ +import * as React from "react"; +import { IColumnVisibility, ISoftware } from "../../interfaces"; +import { IListDetails } from "./IListDetails"; +import { InfoCard } from "./infoCard"; +import { LinkButtonGroup } from "./LinkButtonGroup"; +import { MaintainersInfoCard } from "./maintainersInfoCard"; + +interface IProps { + columnVisibility: IColumnVisibility[]; + list: IListDetails; + software: ISoftware[]; +}; + +export const DetailsExpander = (props: IProps) => +
+
+
+
+ + +
+
+ +
+
+
+
; \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/IListDetails.ts b/src/web/src/modules/home/components/detailsExpander/IListDetails.ts new file mode 100644 index 000000000..5b87aa51f --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/IListDetails.ts @@ -0,0 +1,26 @@ +import { ILanguage, ILicense, IMaintainer, ISyntax, ITag } from "../../interfaces"; + +export interface IListDetails { + id: number; + chatUrl: string; + description: string; + descriptionSourceUrl: string; + donateUrl: string; + emailAddress: string; + forumUrl: string; + homeUrl: string; + issuesUrl: string; + languages: ILanguage[]; + license: ILicense; + maintainers: IMaintainer[]; + name: string; + policyUrl: string; + publishedDate: string; + //ruleCount: number; + submissionUrl: string; + syntax: ISyntax; + tags: ITag[]; + //updatedDate: string; + viewUrl: string; + viewUrlMirrors: string[]; +}; \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/LinkButtonGroup.tsx b/src/web/src/modules/home/components/detailsExpander/LinkButtonGroup.tsx new file mode 100644 index 000000000..528d21029 --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/LinkButtonGroup.tsx @@ -0,0 +1,41 @@ +import * as React from "react"; +import { + ChatButton, + DonateButton, + EmailButton, + ForumButton, + HomeButton, + IssuesButton, + PolicyButton, + SubmitButton, + SubscribeButtonGroup, + ViewButtonGroup +} from "../linkButtons"; + +interface IProps { + chatUrl: string; + donateUrl: string; + emailAddress: string; + forumUrl: string; + homeUrl: string; + issuesUrl: string; + name: string; + policyUrl: string; + submissionUrl: string; + viewUrl: string; + viewUrlMirrors: string[]; +}; + +export const LinkButtonGroup = (props: IProps) => +
+ + + + + + + + + + +
; \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/index.ts b/src/web/src/modules/home/components/detailsExpander/index.ts new file mode 100644 index 000000000..6b887a094 --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/index.ts @@ -0,0 +1,7 @@ +import { DetailsExpander } from "./DetailsExpander"; +import { IListDetails } from "./IListDetails"; + +export { + DetailsExpander, + IListDetails + }; \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/infoCard/Description.tsx b/src/web/src/modules/home/components/detailsExpander/infoCard/Description.tsx new file mode 100644 index 000000000..983c14924 --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/infoCard/Description.tsx @@ -0,0 +1,16 @@ +import * as React from "react"; +import "./description.css"; + +interface IProps { + description: string; + url: string; +}; + +export const Description = (props: IProps) => + props.description + ?

+ {props.url + ?
{props.description}
+ : props.description} +

+ : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/infoCard/InfoCard.tsx b/src/web/src/modules/home/components/detailsExpander/infoCard/InfoCard.tsx new file mode 100644 index 000000000..9e8ba3ae2 --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/infoCard/InfoCard.tsx @@ -0,0 +1,57 @@ +import * as React from "react"; +import { IColumnVisibility } from "../../../interfaces"; +import { ILanguage, ILicense, ISoftware, ISyntax, ITag } from "../../../interfaces"; +import { Description } from "./Description"; +import { Languages } from "./Languages"; +import { License } from "./License"; +import { PublishedDate } from "./PublishedDate"; +import { RuleCount } from "./RuleCount"; +import { SoftwareIcon } from "../../softwareIcon"; +import { Syntax } from "./Syntax"; +import { TagGroup } from "../../TagGroup" +import { UpdatedDate } from "./UpdatedDate"; + +interface IProps { + columnVisibility: IColumnVisibility[]; + description: string; + descriptionSourceUrl: string; + languages: ILanguage[]; + license: ILicense; + name: string; + publishedDate: string; + //ruleCount: number; + software: ISoftware[]; + syntax: ISyntax; + tags: ITag[]; + //updatedDate: string; +}; + +export const InfoCard = (props: IProps) => +
+ {props.columnVisibility.filter((c: IColumnVisibility) => c.column === "Tags")[0].visible + ? null + : } +
+ {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) => ) + : null} +
+ +
    + + {/*{props.columnVisibility.filter((c: IColumnVisibility) => c.column === "Rules")[0].visible + ? null + : } + {props.columnVisibility.filter((c: IColumnVisibility) => c.column === "Updated")[0].visible + ? null + : }*/} + + + {props.columnVisibility.filter((c: IColumnVisibility) => c.column === "License")[0].visible + ? null + : } +
+
; \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/infoCard/Languages.tsx b/src/web/src/modules/home/components/detailsExpander/infoCard/Languages.tsx new file mode 100644 index 000000000..c3390c299 --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/infoCard/Languages.tsx @@ -0,0 +1,20 @@ +import * as React from "react"; +import { ILanguage } from "../../../interfaces"; + +interface IProps { + languages: ILanguage[]; +}; + +export const Languages = (props: IProps) => + props.languages && props.languages.length > 0 + ? props.languages.length > 1 + ?
  • +

    Languages:

    +
      + {props.languages.map((language: ILanguage, i: number) =>
    • {language.name}
    • )} +
    +
  • + :
  • +

    Language: {props.languages[0].name}

    +
  • + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/infoCard/License.tsx b/src/web/src/modules/home/components/detailsExpander/infoCard/License.tsx new file mode 100644 index 000000000..a6222c8a9 --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/infoCard/License.tsx @@ -0,0 +1,17 @@ +import * as React from "react"; +import { ILicense } from "../../../interfaces"; + +interface IProps { + license: ILicense; +}; + +export const License = (props: IProps) => + props.license.name + ? (props.license.descriptionUrl + ?
  • +

    License: {props.license.name}

    +
  • + :
  • +

    License: {props.license.name}

    +
  • ) + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/infoCard/PublishedDate.tsx b/src/web/src/modules/home/components/detailsExpander/infoCard/PublishedDate.tsx new file mode 100644 index 000000000..530a08950 --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/infoCard/PublishedDate.tsx @@ -0,0 +1,13 @@ +import * as React from "react"; +import * as moment from "moment"; + +interface IProps { + date: string; +}; + +export const PublishedDate = (props: IProps) => + props.date + ?
  • +

    Published: {moment(props.date).format("l")}

    +
  • + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/infoCard/RuleCount.tsx b/src/web/src/modules/home/components/detailsExpander/infoCard/RuleCount.tsx new file mode 100644 index 000000000..1fb5944bf --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/infoCard/RuleCount.tsx @@ -0,0 +1,12 @@ +import * as React from "react"; + +interface IProps { + ruleCount: number; +}; + +export const RuleCount = (props: IProps) => + props.ruleCount > 0 + ?
  • +

    Rule Count: {props.ruleCount.toLocaleString()}

    +
  • + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/infoCard/Syntax.tsx b/src/web/src/modules/home/components/detailsExpander/infoCard/Syntax.tsx new file mode 100644 index 000000000..3a3356b85 --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/infoCard/Syntax.tsx @@ -0,0 +1,17 @@ +import * as React from "react"; +import { ISyntax } from "../../../interfaces"; + +interface IProps { + syntax: ISyntax; +}; + +export const Syntax = (props: IProps) => + props.syntax + ? (props.syntax.definitionUrl + ?
  • +

    Syntax: {props.syntax.name}

    +
  • + :
  • +

    Syntax: {props.syntax.name}

    +
  • ) + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/infoCard/UpdatedDate.tsx b/src/web/src/modules/home/components/detailsExpander/infoCard/UpdatedDate.tsx new file mode 100644 index 000000000..7e72853f0 --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/infoCard/UpdatedDate.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import * as moment from "moment"; + +interface IProps { + updatedDate: string; +}; + +export const UpdatedDate = (props: IProps) => + props.updatedDate + ?
  • +

    Updated: {moment(props.updatedDate).isValid() + ? moment(props.updatedDate).format("l") + : "N/A"}

    +
  • + : null; diff --git a/src/web/src/modules/home/components/detailsExpander/infoCard/description.css b/src/web/src/modules/home/components/detailsExpander/infoCard/description.css new file mode 100644 index 000000000..2a1267485 --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/infoCard/description.css @@ -0,0 +1 @@ +.fl-description { margin: 0 0 .5rem; } \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/infoCard/index.ts b/src/web/src/modules/home/components/detailsExpander/infoCard/index.ts new file mode 100644 index 000000000..f90900e68 --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/infoCard/index.ts @@ -0,0 +1,5 @@ +import { InfoCard } from "./InfoCard"; + +export { + InfoCard + }; \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/maintainersInfoCard/MaintainerAdditionalLists.tsx b/src/web/src/modules/home/components/detailsExpander/maintainersInfoCard/MaintainerAdditionalLists.tsx new file mode 100644 index 000000000..96cc74f85 --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/maintainersInfoCard/MaintainerAdditionalLists.tsx @@ -0,0 +1,20 @@ +import * as React from "react"; + +//interface IProps { +// name: string; +// additionalLists: IMaintainerAdditionalListDto[]; +//}; + +export const MaintainerAdditionalLists = (/*props: IProps*/) => +
    + { /*props.additionalLists && props.additionalLists.length > 0 + ?
    +

    More by {props.name}:

    +
      + {props.additionalLists.map( + (l: IMaintainerAdditionalListDto, i: number) =>
    • {l.name}
    • )} +
    + +
    + : null*/ } +
    ; \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/maintainersInfoCard/MaintainerInfoCard.tsx b/src/web/src/modules/home/components/detailsExpander/maintainersInfoCard/MaintainerInfoCard.tsx new file mode 100644 index 000000000..c2315b1a6 --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/maintainersInfoCard/MaintainerInfoCard.tsx @@ -0,0 +1,23 @@ +import * as React from "react"; +import { IMaintainer } from "../../../interfaces"; +import { MaintainerAdditionalLists } from "./MaintainerAdditionalLists"; +import { MaintainerLinkButtonGroup } from "./MaintainerLinkButtonGroup"; + +interface IProps { + maintainer: IMaintainer; +}; + +export const MaintainerInfoCard = (props: IProps) => + props.maintainer.name + ?
    +
    +

    Maintained by {props.maintainer.name}

    +
    +
    + + +
    +
    +
    +
    + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/maintainersInfoCard/MaintainerLinkButtonGroup.tsx b/src/web/src/modules/home/components/detailsExpander/maintainersInfoCard/MaintainerLinkButtonGroup.tsx new file mode 100644 index 000000000..9bd900bef --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/maintainersInfoCard/MaintainerLinkButtonGroup.tsx @@ -0,0 +1,16 @@ +import * as React from "react"; +import { EmailButton, HomeButton, TwitterButton } from "../../linkButtons"; + +interface IProps { + emailAddress: string; + homeUrl: string; + name: string; + twitterHandle: string; +}; + +export const MaintainerLinkButtonGroup = (props: IProps) => +
    + + + +
    ; \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/maintainersInfoCard/MaintainersInfoCard.tsx b/src/web/src/modules/home/components/detailsExpander/maintainersInfoCard/MaintainersInfoCard.tsx new file mode 100644 index 000000000..cb95cbcf9 --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/maintainersInfoCard/MaintainersInfoCard.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import { IMaintainer } from "../../../interfaces"; +import { MaintainerInfoCard } from "./MaintainerInfoCard"; + +interface IProps { + maintainers: IMaintainer[]; +}; + +export const MaintainersInfoCard = (props: IProps) => + props.maintainers && props.maintainers.length > 0 + ?
    + {props.maintainers.map((m: IMaintainer, i: number) => )} +
    + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/detailsExpander/maintainersInfoCard/index.ts b/src/web/src/modules/home/components/detailsExpander/maintainersInfoCard/index.ts new file mode 100644 index 000000000..03a112e6e --- /dev/null +++ b/src/web/src/modules/home/components/detailsExpander/maintainersInfoCard/index.ts @@ -0,0 +1,5 @@ +import { MaintainersInfoCard } from "./MaintainersInfoCard"; + +export { + MaintainersInfoCard + }; \ No newline at end of file diff --git a/src/web/src/modules/home/components/index.ts b/src/web/src/modules/home/components/index.ts new file mode 100644 index 000000000..180723286 --- /dev/null +++ b/src/web/src/modules/home/components/index.ts @@ -0,0 +1,9 @@ +import { DetailsExpander } from "./detailsExpander"; +import { ListsTable } from "./listsTable"; +import { Oneliner } from "./Oneliner"; + +export { + DetailsExpander, + ListsTable, + Oneliner + }; \ No newline at end of file diff --git a/src/web/src/modules/home/components/linkButtons/ChatButton.tsx b/src/web/src/modules/home/components/linkButtons/ChatButton.tsx new file mode 100644 index 000000000..f04b23e12 --- /dev/null +++ b/src/web/src/modules/home/components/linkButtons/ChatButton.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; +}; + +export const ChatButton = (props: IProps) => + props.url + ? + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/linkButtons/DonateButton.tsx b/src/web/src/modules/home/components/linkButtons/DonateButton.tsx new file mode 100644 index 000000000..03713ae48 --- /dev/null +++ b/src/web/src/modules/home/components/linkButtons/DonateButton.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; +}; + +export const DonateButton = (props: IProps) => + props.url + ? + : null; diff --git a/src/web/src/modules/home/components/linkButtons/EmailButton.tsx b/src/web/src/modules/home/components/linkButtons/EmailButton.tsx new file mode 100644 index 000000000..edc8e9972 --- /dev/null +++ b/src/web/src/modules/home/components/linkButtons/EmailButton.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + emailAddress: string; +}; + +export const EmailButton = (props: IProps) => + props.emailAddress + ? + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/linkButtons/ForumButton.tsx b/src/web/src/modules/home/components/linkButtons/ForumButton.tsx new file mode 100644 index 000000000..0bf0bacfc --- /dev/null +++ b/src/web/src/modules/home/components/linkButtons/ForumButton.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; +}; + +export const ForumButton = (props: IProps) => + props.url + ? + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/linkButtons/HomeButton.tsx b/src/web/src/modules/home/components/linkButtons/HomeButton.tsx new file mode 100644 index 000000000..e1f40b366 --- /dev/null +++ b/src/web/src/modules/home/components/linkButtons/HomeButton.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; +}; + +export const HomeButton = (props: IProps) => + props.url + ? + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/linkButtons/IssuesButton.tsx b/src/web/src/modules/home/components/linkButtons/IssuesButton.tsx new file mode 100644 index 000000000..038ab763f --- /dev/null +++ b/src/web/src/modules/home/components/linkButtons/IssuesButton.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; +}; + +export const IssuesButton = (props: IProps) => + props.url + ? + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/linkButtons/LinkButton.tsx b/src/web/src/modules/home/components/linkButtons/LinkButton.tsx new file mode 100644 index 000000000..66af40654 --- /dev/null +++ b/src/web/src/modules/home/components/linkButtons/LinkButton.tsx @@ -0,0 +1,18 @@ +import * as React from "react"; +import "./button.css"; + +interface IProps { + href: string; + title?: string; + buttonClass?: string; + text: string; +}; + +export const LinkButton = (props: IProps) => + props.href + ? + {props.text} + + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/linkButtons/PolicyButton.tsx b/src/web/src/modules/home/components/linkButtons/PolicyButton.tsx new file mode 100644 index 000000000..60b87d795 --- /dev/null +++ b/src/web/src/modules/home/components/linkButtons/PolicyButton.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; +}; + +export const PolicyButton = (props: IProps) => + props.url + ? + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/linkButtons/SubmitButton.tsx b/src/web/src/modules/home/components/linkButtons/SubmitButton.tsx new file mode 100644 index 000000000..e4baef971 --- /dev/null +++ b/src/web/src/modules/home/components/linkButtons/SubmitButton.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; +}; + +export const SubmitButton = (props: IProps) => + props.url + ? + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/linkButtons/SubscribeButton.tsx b/src/web/src/modules/home/components/linkButtons/SubscribeButton.tsx new file mode 100644 index 000000000..f7f3d4c1b --- /dev/null +++ b/src/web/src/modules/home/components/linkButtons/SubscribeButton.tsx @@ -0,0 +1,62 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; + text?: string; +}; + +export const SubscribeButton = (props: IProps) => { + let buttonClass: string | undefined; + let titlePrefix: string; + + if (props.url.indexOf(".onion/") > 0) { + buttonClass = "btn-success"; + titlePrefix = "Tor address - "; + } else if (props.url.indexOf("http://") === 0) { + buttonClass = "btn-danger"; + titlePrefix = "Not Secure - "; + } else { + buttonClass = undefined; + titlePrefix = ""; + } + + const hrefTitle = `${encodeURIComponent(props.name)}`; + let href; +if (props.url.indexOf(".tpl") > 0) +{ + href = `javascript:window.external.msAddTrackingProtectionList('${encodeURIComponent(props.url)}', '${hrefTitle}')`; +} else if (props.url.indexOf(".lsrules") > 0) +{ + href = `x-littlesnitch:subscribe-rules?url=${encodeURIComponent(props.url)}`; +} else if (props.url.indexOf("?hostformat=littlesnitch") > 0) +{ + href = `x-littlesnitch:subscribe-rules?url=${encodeURIComponent(props.url)}`; +} else { + href = `abp:subscribe?location=${encodeURIComponent(props.url)}&title=${hrefTitle}`; +}; + + + + let title; +if (props.url.indexOf(".tpl") > 0) +{ + title = `${titlePrefix}Subscribe to ${props.name} with Internet Explorer's Tracking Protection List feature.`; +} else if (props.url.indexOf(".lsrules") > 0) +{ + title = `${titlePrefix}Subscribe to ${props.name} with Little Snitch's rule group subscription feature.`; +} else if (props.url.indexOf("?hostformat=littlesnitch") > 0) +{ + title = `${titlePrefix}Subscribe to ${props.name} with Little Snitch's rule group subscription feature.`; +} else { + title = `${titlePrefix}Subscribe to ${props.name} with a browser extension supporting the \"abp:\" protocol (e.g. uBlock Origin, Adblock Plus).`; +}; + + return props.url + ? + : null; +}; diff --git a/src/web/src/modules/home/components/linkButtons/SubscribeButtonGroup.tsx b/src/web/src/modules/home/components/linkButtons/SubscribeButtonGroup.tsx new file mode 100644 index 000000000..e6315284c --- /dev/null +++ b/src/web/src/modules/home/components/linkButtons/SubscribeButtonGroup.tsx @@ -0,0 +1,52 @@ +import * as React from "react"; +import { SubscribeButton } from "./SubscribeButton"; + +interface IProps { + name: string; + url: string; + urlMirrors?: string[]; +}; + +export const SubscribeButtonGroup = (props: IProps) => + props.url + ? (props.urlMirrors && props.urlMirrors.length > 0) + ? + : + : null; + +interface ISubscribeButtonGroupDropdownProps { + name: string; + url: string; + urlMirrors: string[]; +}; + +const SubscribeButtonGroupDropdown = (props: ISubscribeButtonGroupDropdownProps) => { + let firstButtonText: string = "Original"; + let mirrorIndex: number = 0; + + if (props.url.indexOf("web.archive.org") !== -1) { + firstButtonText = "Mirror 1"; + mirrorIndex++; + } + + return
    + +
    + + {props.urlMirrors.map( + (m: string, i: number) => + )} +
    +
    ; +}; + +const BtnGroupDropSubscribe = () => + ; \ No newline at end of file diff --git a/src/web/src/modules/home/components/linkButtons/TwitterButton.tsx b/src/web/src/modules/home/components/linkButtons/TwitterButton.tsx new file mode 100644 index 000000000..8c7f7a71a --- /dev/null +++ b/src/web/src/modules/home/components/linkButtons/TwitterButton.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + twitterHandle: string; +}; + +export const TwitterButton = (props: IProps) => + props.twitterHandle + ? + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/linkButtons/ViewButton.tsx b/src/web/src/modules/home/components/linkButtons/ViewButton.tsx new file mode 100644 index 000000000..0e33dab9b --- /dev/null +++ b/src/web/src/modules/home/components/linkButtons/ViewButton.tsx @@ -0,0 +1,24 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; + text?: string; +}; + +export const ViewButton = (props: IProps) => { + + let title; + if (props.url.indexOf(".onion/") > 0) { title = `Tor address - View ${props.name} in its raw format.`; } + else if (props.url.indexOf(".zip") > 0) { title = `Download ${props.name} as a ZIP compressed archive.`; } + else if (props.url.indexOf(".7z") > 0) { title = `Download ${props.name} as a 7Z compressed archive.`; } + else if (props.url.indexOf(".tar.gz") > 0) { title = `Download ${props.name} as a compressed tarball archive.`; } + else { title = `View ${props.name} in its raw format`; }; + +return props.url + ? + : null; +} diff --git a/src/web/src/modules/home/components/linkButtons/ViewButtonGroup.tsx b/src/web/src/modules/home/components/linkButtons/ViewButtonGroup.tsx new file mode 100644 index 000000000..60669fa82 --- /dev/null +++ b/src/web/src/modules/home/components/linkButtons/ViewButtonGroup.tsx @@ -0,0 +1,51 @@ +import * as React from "react"; +import { ViewButton } from "./ViewButton"; + +interface IProps { + name: string; + url: string; + urlMirrors?: string[]; +}; + +export const ViewButtonGroup = (props: IProps) => + props.url + ? (props.urlMirrors && props.urlMirrors.length > 0) + ? + : + : null; + +interface IViewButtonGroupDropdownProps { + name: string; + url: string; + urlMirrors: string[]; +}; + +const ViewButtonGroupDropdown = (props: IViewButtonGroupDropdownProps) => { + let firstButtonText: string = "Original"; + let mirrorIndex: number = 0; + + if (props.url.indexOf("web.archive.org") !== -1) { + firstButtonText = "Mirror 1"; + mirrorIndex++; + } + + return
    + +
    + + {props.urlMirrors.map( + (m: string, i: number) => + )} +
    +
    ; +}; + +const BtnGroupDropView = () => + ; \ No newline at end of file diff --git a/src/web/src/modules/home/components/linkButtons/button.css b/src/web/src/modules/home/components/linkButtons/button.css new file mode 100644 index 000000000..bace69595 --- /dev/null +++ b/src/web/src/modules/home/components/linkButtons/button.css @@ -0,0 +1,11 @@ +.fl-btn-link { + margin-bottom: .2rem; + max-width: 100px; + width: 100px; +} + +.dropdown-menu { + border: 0; + min-width: 0; + padding: 0; +} \ No newline at end of file diff --git a/src/web/src/modules/home/components/linkButtons/index.ts b/src/web/src/modules/home/components/linkButtons/index.ts new file mode 100644 index 000000000..c0525f2df --- /dev/null +++ b/src/web/src/modules/home/components/linkButtons/index.ts @@ -0,0 +1,25 @@ +import { ChatButton } from "./ChatButton"; +import { DonateButton } from "./DonateButton"; +import { EmailButton } from "./EmailButton"; +import { ForumButton } from "./ForumButton"; +import { HomeButton } from "./HomeButton"; +import { IssuesButton } from "./IssuesButton"; +import { PolicyButton } from "./PolicyButton"; +import { SubmitButton } from "./SubmitButton"; +import { SubscribeButtonGroup } from "./SubscribeButtonGroup"; +import { TwitterButton } from "./TwitterButton"; +import { ViewButtonGroup } from "./ViewButtonGroup"; + +export { + ChatButton, + DonateButton, + EmailButton, + ForumButton, + HomeButton, + IssuesButton, + PolicyButton, + SubmitButton, + SubscribeButtonGroup, + TwitterButton, + ViewButtonGroup + }; \ No newline at end of file diff --git a/src/web/src/modules/home/components/listsTable/ListsTable.tsx b/src/web/src/modules/home/components/listsTable/ListsTable.tsx new file mode 100644 index 000000000..859673237 --- /dev/null +++ b/src/web/src/modules/home/components/listsTable/ListsTable.tsx @@ -0,0 +1,104 @@ +import * as React from "react"; +import { IColumnVisibility, ILanguage, ILicense, IList, IMaintainer, ISoftware, ISyntax, ITag } from "../../interfaces"; +import "../../../../utils/loader.css"; +import ReactTable from "react-table"; +import "react-table/react-table.css"; +import "./listsTable.css"; +import { + DetailsButton, + Languages, + License, + Maintainers, + Name, + //RuleCount, + Software, + SubscribeButton, + Tags, + //UpdatedDate +} from "./columns"; +import { IListDetails } from "../../components/detailsExpander"; +import { DetailsExpander } from "../../components"; + +interface IProps { + languages: ILanguage[]; + licenses: ILicense[]; + lists: IList[]; + maintainers: IMaintainer[]; + software: ISoftware[]; + syntaxes: ISyntax[]; + tags: ITag[]; + columnVisibility: IColumnVisibility[]; + pageSize: number; +}; + +export const ListsTable = (props: IProps) => + props.languages.length > 0 && props.lists.length > 0 && props.software.length > 0 && props.tags.length > 0 + ? + } + className="-striped -highlight"/> + :
    Loading...
    ; + +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); \ No newline at end of file diff --git a/src/web/src/modules/home/components/listsTable/columns/DetailsButton.tsx b/src/web/src/modules/home/components/listsTable/columns/DetailsButton.tsx new file mode 100644 index 000000000..7c7955fb6 --- /dev/null +++ b/src/web/src/modules/home/components/listsTable/columns/DetailsButton.tsx @@ -0,0 +1,26 @@ +import * as React from "react"; +import { Column, RowRenderProps } from "react-table"; +import "../../linkButtons/button.css"; + +export const DetailsButton = { + Header: Details, + accessor: "id", + sortable: false, + expander: true, + Expander: ({ isExpanded, row }: RowRenderProps) => Expander({ isExpanded, row }), + style: { textAlign: "center" }, + width: 110 +} as Column; + +const Expander = (props: RowRenderProps) => +
    + {props.isExpanded + ? + : } +
    ; \ No newline at end of file diff --git a/src/web/src/modules/home/components/listsTable/columns/Languages.tsx b/src/web/src/modules/home/components/listsTable/columns/Languages.tsx new file mode 100644 index 000000000..828b2fd54 --- /dev/null +++ b/src/web/src/modules/home/components/listsTable/columns/Languages.tsx @@ -0,0 +1,71 @@ +import * as React from "react"; +import { Column, Filter } from "react-table"; +import { IColumnVisibility, ILanguage } from "../../../interfaces"; + +export const Languages = (columnVisibility: IColumnVisibility[], languages: ILanguage[]) => { + const languagesSorted = languages.sort((a, b) => a.name.localeCompare(b.name)); + return ({ + Header: Languages, + 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[]) => + ; + +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 + ?
    + {languageIds.map((id: number, i: number) => { + const language = languages.filter((l: ILanguage) => l.id === id)[0]; + return + {language.iso6391} + ; + })} +
    + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/listsTable/columns/License.tsx b/src/web/src/modules/home/components/listsTable/columns/License.tsx new file mode 100644 index 000000000..359e1e777 --- /dev/null +++ b/src/web/src/modules/home/components/listsTable/columns/License.tsx @@ -0,0 +1,68 @@ +import * as React from "react"; +import { Column, Filter } from "react-table"; +import { IColumnVisibility, ILicense } from "../../../interfaces"; + +export const License = (columnVisibility: IColumnVisibility[], licenses: ILicense[]) => +({ + Header: License, + 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[]) => + ; + +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 + ?
    + + {license.descriptionUrl ? {license.name} : license.name} + +
    + : null; +}; \ No newline at end of file diff --git a/src/web/src/modules/home/components/listsTable/columns/Maintainers.tsx b/src/web/src/modules/home/components/listsTable/columns/Maintainers.tsx new file mode 100644 index 000000000..2476f84e2 --- /dev/null +++ b/src/web/src/modules/home/components/listsTable/columns/Maintainers.tsx @@ -0,0 +1,68 @@ +import * as React from "react"; +import { Column, Filter } from "react-table"; +import { IColumnVisibility, IMaintainer } from "../../../interfaces"; + +export const Maintainers = (columnVisibility: IColumnVisibility[], maintainers: IMaintainer[]) => +({ + Header: Maintainers, + 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[]) => + ; + +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 + ?
    + {maintainers.filter((m: IMaintainer) => maintainerIds.indexOf(m.id) > -1) + .map((m: IMaintainer, i: number) => { + return m.homeUrl + ? {m.name} + : {m.name}; + }).reduce(((prev: JSX.Element, curr: JSX.Element): any => [prev, ", ", curr]) as any)} +
    + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/listsTable/columns/Name.tsx b/src/web/src/modules/home/components/listsTable/columns/Name.tsx new file mode 100644 index 000000000..12ad09716 --- /dev/null +++ b/src/web/src/modules/home/components/listsTable/columns/Name.tsx @@ -0,0 +1,22 @@ +import * as React from "react"; +import { Column, Filter } from "react-table"; + +export const Name = { + Header: Name, + accessor: "name", + filterable: true, + filterMethod: (f: Filter, r: any[]) => filterMethod(f, r), + sortMethod: (a: string, b: string) => sortMethod(a, b), + Cell: (c: any) => Cell(c.value) +} as Column; + +const filterMethod = (f: Filter, r: any[]): boolean => + r[f.id as any].toUpperCase().includes(f.value.toUpperCase()); + +const sortMethod = (a: string, b: string) => + a.toLowerCase() > b.toLowerCase() ? 1 : -1; + +const Cell = (name: string) => + name + ?

    {name}

    + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/listsTable/columns/RuleCount.tsx b/src/web/src/modules/home/components/listsTable/columns/RuleCount.tsx new file mode 100644 index 000000000..33689baa9 --- /dev/null +++ b/src/web/src/modules/home/components/listsTable/columns/RuleCount.tsx @@ -0,0 +1,28 @@ +import * as React from "react"; +import { Column } from "react-table"; +import { IColumnVisibility } from "../../../interfaces"; + +export const RuleCount = (columnVisibility: IColumnVisibility[]) => +({ + Header: Rules, + 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 + ? { ruleCount.toLocaleString() } + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/listsTable/columns/Software.tsx b/src/web/src/modules/home/components/listsTable/columns/Software.tsx new file mode 100644 index 000000000..485af2731 --- /dev/null +++ b/src/web/src/modules/home/components/listsTable/columns/Software.tsx @@ -0,0 +1,74 @@ +import * as React from "react"; +import { Column, Filter } from "react-table"; +import { IColumnVisibility, ISoftware } from "../../../interfaces"; +import { SoftwareIcon } from "../../softwareIcon"; + +export const Software = (columnVisibility: IColumnVisibility[], software: ISoftware[]) => { + const softwareSorted = software.sort((a, b) => a.name.localeCompare(b.name)); + return ({ + Header: + Software, + 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[]) => + ; + +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 + ?
    + {software.filter((s: ISoftware) => s.syntaxIds.indexOf(listSyntaxId) > -1) + .map((s: ISoftware, i: number) => + s.homeUrl + ? + + + : )} +
    + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/listsTable/columns/SubscribeButton.tsx b/src/web/src/modules/home/components/listsTable/columns/SubscribeButton.tsx new file mode 100644 index 000000000..2df9408e2 --- /dev/null +++ b/src/web/src/modules/home/components/listsTable/columns/SubscribeButton.tsx @@ -0,0 +1,24 @@ +import * as React from "react"; +import { Column, Filter } from "react-table"; +import { IColumnVisibility } from "../../../interfaces"; +import { SubscribeButtonGroup } from "../../linkButtons"; + +export const SubscribeButton = (columnVisibility: IColumnVisibility[]) => ({ + Header: + Subscribe + , + accessor: "viewUrl", + filterable: true, + filterMethod: (f: Filter, r: any[]) => filterMethod(f, r), + sortMethod: (a: string, b: string) => sortMethod(a, b), + Cell: (c: any) => , + 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; \ No newline at end of file diff --git a/src/web/src/modules/home/components/listsTable/columns/Syntax.tsx b/src/web/src/modules/home/components/listsTable/columns/Syntax.tsx new file mode 100644 index 000000000..25f9ff832 --- /dev/null +++ b/src/web/src/modules/home/components/listsTable/columns/Syntax.tsx @@ -0,0 +1,46 @@ +import * as React from "react"; +import { Column, Filter } from "react-table"; +import { IColumnVisibility, ISyntax } from "../../../interfaces"; + +//TODO: https://github.com/collinbarrett/FilterLists/issues/488 +export const Syntax = (columnVisibility: IColumnVisibility[], syntaxes: ISyntax[]) => ({ + Header: Syntax, + 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[]) => + ; + +const Cell = (listSyntaxId: number, syntaxes: ISyntax[]) => + listSyntaxId + ? syntaxes.filter((s: ISyntax) => s.id === listSyntaxId)[0].name + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/listsTable/columns/Tags.tsx b/src/web/src/modules/home/components/listsTable/columns/Tags.tsx new file mode 100644 index 000000000..2031860dc --- /dev/null +++ b/src/web/src/modules/home/components/listsTable/columns/Tags.tsx @@ -0,0 +1,63 @@ +import * as React from "react"; +import { Column, Filter } from "react-table"; +import { TagGroup } from "../../TagGroup"; +import { IColumnVisibility, ITag } from "../../../interfaces"; + +export const Tags = (columnVisibility: IColumnVisibility[], tags: ITag[]) => { + const tagsSorted = tags.sort((a: ITag, b: ITag) => a.name.localeCompare(b.name)); + return ({ + Header: + Tags, + 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[]) => + ; + +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 + ? tagIds.indexOf(t.id) > -1)}/> + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/listsTable/columns/UpdatedDate.tsx b/src/web/src/modules/home/components/listsTable/columns/UpdatedDate.tsx new file mode 100644 index 000000000..0542a50cf --- /dev/null +++ b/src/web/src/modules/home/components/listsTable/columns/UpdatedDate.tsx @@ -0,0 +1,33 @@ +import * as React from "react"; +import { Column } from "react-table"; +import * as moment from "moment"; +import { IColumnVisibility } from "../../../interfaces"; + +export const UpdatedDate = (columnVisibility: IColumnVisibility[]) => +({ + Header: Updated, + 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 + ?
    + {moment(updatedDate).isValid() + ? moment(updatedDate).format("l") + : null} +
    + : null; \ No newline at end of file diff --git a/src/web/src/modules/home/components/listsTable/columns/index.ts b/src/web/src/modules/home/components/listsTable/columns/index.ts new file mode 100644 index 000000000..050780af2 --- /dev/null +++ b/src/web/src/modules/home/components/listsTable/columns/index.ts @@ -0,0 +1,25 @@ +import { DetailsButton } from "./DetailsButton"; +import { Languages } from "./Languages"; +import { License } from "./License"; +import { Maintainers } from "./Maintainers"; +import { Name } from "./Name"; +import { RuleCount } from "./RuleCount"; +import { Software } from "./Software"; +import { SubscribeButton } from "./SubscribeButton"; +import { Syntax } from "./Syntax"; +import { Tags } from "./Tags"; +import { UpdatedDate } from "./UpdatedDate"; + +export { + DetailsButton, + Languages, + License, + Maintainers, + Name, + RuleCount, + Software, + SubscribeButton, + Syntax, + Tags, + UpdatedDate + }; \ No newline at end of file diff --git a/src/web/src/modules/home/components/listsTable/index.ts b/src/web/src/modules/home/components/listsTable/index.ts new file mode 100644 index 000000000..48483597b --- /dev/null +++ b/src/web/src/modules/home/components/listsTable/index.ts @@ -0,0 +1,5 @@ +import { ListsTable } from "./ListsTable"; + +export { + ListsTable, + }; \ No newline at end of file diff --git a/src/web/src/modules/home/components/listsTable/listsTable.css b/src/web/src/modules/home/components/listsTable/listsTable.css new file mode 100644 index 000000000..0436aa6bf --- /dev/null +++ b/src/web/src/modules/home/components/listsTable/listsTable.css @@ -0,0 +1,13 @@ +.rt-tr { + -ms-align-items: center; + -o-align-items: center; + -webkit-align-items: center; + align-items: center; +} + +div.rt-tbody, div.rt-thead.-filters, div.rt-thead.-header { min-width: 320px !important; } + +.fl-wrap-cell { + line-height: 1; + white-space: normal; +} \ No newline at end of file diff --git a/src/web/src/modules/home/components/softwareIcon/SoftwareIcon.tsx b/src/web/src/modules/home/components/softwareIcon/SoftwareIcon.tsx new file mode 100644 index 000000000..03aff3b5d --- /dev/null +++ b/src/web/src/modules/home/components/softwareIcon/SoftwareIcon.tsx @@ -0,0 +1,89 @@ +import * as React from "react"; +import { + img1, + img2, + img3, + img4, + img5, + img6, + img7, + img8, + img10, + img11, + img12, + img13, + img14, + img15, + img16, + img17, + img18, + img19, + img20, + img21, + img22, + img23, + img24, + img25, + img26, + img27, + img28, + img29, + img30, + img31, + img32, + img33, + img34 +} from "./imgs"; + +interface IProps { + id: number; +}; + +export const SoftwareIcon = (props: IProps) => + icons[props.id] + ? {icons[props.id].imageTitle} + : null; + +interface IIcon { + image: any; + imageTitle: string; +}; + +const icons: { [id: number]: IIcon; } = { + 1: { image: img1, imageTitle: "uBlock Origin" }, + 2: { image: img2, imageTitle: "Adblock Plus" }, + 3: { image: img3, imageTitle: "AdGuard" }, + 4: { image: img4, imageTitle: "DNS66" }, + 5: { image: img5, imageTitle: "Nano Adblocker" }, + 6: { image: img6, imageTitle: "AdBlock" }, + 7: { image: img7, imageTitle: "AdAway" }, + 8: { image: img8, imageTitle: "Personal Blocklist" }, + 10: { image: img10, imageTitle: "Redirector" }, + 11: { image: img11, imageTitle: "Hosts File Editor" }, + 12: { image: img12, imageTitle: "Gas Mask" }, + 13: { image: img13, imageTitle: "MinerBlock" }, + 14: { image: img14, imageTitle: "Pi-hole" }, + 15: { image: img15, imageTitle: "uBlock" }, + 16: { image: img16, imageTitle: "Internet Explorer (TPL)" }, + 17: { image: img17, imageTitle: "Google Hit Hider by Domain" }, + 18: { image: img18, imageTitle: "FireHOL" }, + 19: { image: img19, imageTitle: "Samsung Knox" }, + 20: { image: img20, imageTitle: "Little Snitch" }, + 21: { image: img21, imageTitle: "Privoxy" }, + 22: { image: img22, imageTitle: "Diversion" }, + 23: { image: img23, imageTitle: "dnsmasq" }, + 24: { image: img24, imageTitle: "Slimjet" }, + 25: { image: img25, imageTitle: "uMatrix" }, + 26: { image: img26, imageTitle: "Blokada" }, + 27: { image: img27, imageTitle: "hostsmgr" }, + 28: { image: img28, imageTitle: "personalDNSfilter" }, + 29: { image: img29, imageTitle: "Unbound" }, + 30: { image: img30, imageTitle: "BIND" }, + 31: { image: img31, imageTitle: "AdGuard Home" }, + 32: { image: img32, imageTitle: "AdNauseam" }, + 33: { image: img33, imageTitle: "Legacy Unix derivatives" }, + 34: { image: img34, imageTitle: "Windows command line" }, +}; diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/01-uBlock-Origin.svg b/src/web/src/modules/home/components/softwareIcon/imgs/01-uBlock-Origin.svg new file mode 100644 index 000000000..03e7a9ebb --- /dev/null +++ b/src/web/src/modules/home/components/softwareIcon/imgs/01-uBlock-Origin.svg @@ -0,0 +1 @@ + diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/02-Adblock-Plus.svg b/src/web/src/modules/home/components/softwareIcon/imgs/02-Adblock-Plus.svg new file mode 100644 index 000000000..71ab1252f --- /dev/null +++ b/src/web/src/modules/home/components/softwareIcon/imgs/02-Adblock-Plus.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/03-AdGuard.png b/src/web/src/modules/home/components/softwareIcon/imgs/03-AdGuard.png new file mode 100644 index 000000000..61471018c Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/03-AdGuard.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/03-AdGuard.svg b/src/web/src/modules/home/components/softwareIcon/imgs/03-AdGuard.svg new file mode 100644 index 000000000..b9cb8ebd4 --- /dev/null +++ b/src/web/src/modules/home/components/softwareIcon/imgs/03-AdGuard.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/04-DNS66.png b/src/web/src/modules/home/components/softwareIcon/imgs/04-DNS66.png new file mode 100644 index 000000000..a32dc2c2f Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/04-DNS66.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/05-Nano-Adblocker.png b/src/web/src/modules/home/components/softwareIcon/imgs/05-Nano-Adblocker.png new file mode 100644 index 000000000..cd2b2a06d Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/05-Nano-Adblocker.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/06-AdBlock.png b/src/web/src/modules/home/components/softwareIcon/imgs/06-AdBlock.png new file mode 100644 index 000000000..84b1d1195 Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/06-AdBlock.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/07-AdAway.png b/src/web/src/modules/home/components/softwareIcon/imgs/07-AdAway.png new file mode 100644 index 000000000..0826b7478 Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/07-AdAway.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/08-Personal-Blocklist.png b/src/web/src/modules/home/components/softwareIcon/imgs/08-Personal-Blocklist.png new file mode 100644 index 000000000..9b6888a1b Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/08-Personal-Blocklist.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/10-Redirector.png b/src/web/src/modules/home/components/softwareIcon/imgs/10-Redirector.png new file mode 100644 index 000000000..33fa33b01 Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/10-Redirector.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/11-Hosts-File-Editor.png b/src/web/src/modules/home/components/softwareIcon/imgs/11-Hosts-File-Editor.png new file mode 100644 index 000000000..e758cf5da Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/11-Hosts-File-Editor.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/12-Gas-Mask.png b/src/web/src/modules/home/components/softwareIcon/imgs/12-Gas-Mask.png new file mode 100644 index 000000000..062dcbd76 Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/12-Gas-Mask.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/13-MinerBlock.svg b/src/web/src/modules/home/components/softwareIcon/imgs/13-MinerBlock.svg new file mode 100644 index 000000000..a2c2b0064 --- /dev/null +++ b/src/web/src/modules/home/components/softwareIcon/imgs/13-MinerBlock.svg @@ -0,0 +1,34 @@ + + + + + + Created by potrace 1.15, written by Peter Selinger 2001-2017 + + + + + + + \ No newline at end of file diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/14-Pi-hole.png b/src/web/src/modules/home/components/softwareIcon/imgs/14-Pi-hole.png new file mode 100644 index 000000000..99c6753a2 Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/14-Pi-hole.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/14-Pi-hole.svg b/src/web/src/modules/home/components/softwareIcon/imgs/14-Pi-hole.svg new file mode 100644 index 000000000..3d3bd57f7 --- /dev/null +++ b/src/web/src/modules/home/components/softwareIcon/imgs/14-Pi-hole.svg @@ -0,0 +1 @@ +NewVortex diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/15-uBlock.svg b/src/web/src/modules/home/components/softwareIcon/imgs/15-uBlock.svg new file mode 100644 index 000000000..6488ebb04 --- /dev/null +++ b/src/web/src/modules/home/components/softwareIcon/imgs/15-uBlock.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/16-Internet-Explorer-TPL.png b/src/web/src/modules/home/components/softwareIcon/imgs/16-Internet-Explorer-TPL.png new file mode 100644 index 000000000..2b8189c8d Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/16-Internet-Explorer-TPL.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/16-Internet-Explorer-TPL.svg b/src/web/src/modules/home/components/softwareIcon/imgs/16-Internet-Explorer-TPL.svg new file mode 100644 index 000000000..69780795f --- /dev/null +++ b/src/web/src/modules/home/components/softwareIcon/imgs/16-Internet-Explorer-TPL.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/17-Google-Hit-Hider-by-Domain.png b/src/web/src/modules/home/components/softwareIcon/imgs/17-Google-Hit-Hider-by-Domain.png new file mode 100644 index 000000000..ca830def3 Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/17-Google-Hit-Hider-by-Domain.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/18-FireHOL.png b/src/web/src/modules/home/components/softwareIcon/imgs/18-FireHOL.png new file mode 100644 index 000000000..5db30bb01 Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/18-FireHOL.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/19-Samsung-Knox.png b/src/web/src/modules/home/components/softwareIcon/imgs/19-Samsung-Knox.png new file mode 100644 index 000000000..bdb2f96a1 Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/19-Samsung-Knox.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/20-Little-Snitch.png b/src/web/src/modules/home/components/softwareIcon/imgs/20-Little-Snitch.png new file mode 100644 index 000000000..1562eec0a Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/20-Little-Snitch.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/21-Privoxy.png b/src/web/src/modules/home/components/softwareIcon/imgs/21-Privoxy.png new file mode 100644 index 000000000..0ba2b59a1 Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/21-Privoxy.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/22-Diversion.png b/src/web/src/modules/home/components/softwareIcon/imgs/22-Diversion.png new file mode 100644 index 000000000..511c8c8cd Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/22-Diversion.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/23-dnsmasq.png b/src/web/src/modules/home/components/softwareIcon/imgs/23-dnsmasq.png new file mode 100644 index 000000000..99e986ba9 Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/23-dnsmasq.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/24-Slimjet.png b/src/web/src/modules/home/components/softwareIcon/imgs/24-Slimjet.png new file mode 100644 index 000000000..00f899268 Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/24-Slimjet.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/25-uMatrix.png b/src/web/src/modules/home/components/softwareIcon/imgs/25-uMatrix.png new file mode 100644 index 000000000..82663fc9e Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/25-uMatrix.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/26-Blokada.png b/src/web/src/modules/home/components/softwareIcon/imgs/26-Blokada.png new file mode 100644 index 000000000..cfd376114 Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/26-Blokada.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/27-hostsmgr.png b/src/web/src/modules/home/components/softwareIcon/imgs/27-hostsmgr.png new file mode 100644 index 000000000..349d5b17e Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/27-hostsmgr.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/28-personalDNSfilter.png b/src/web/src/modules/home/components/softwareIcon/imgs/28-personalDNSfilter.png new file mode 100644 index 000000000..8275b2b17 Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/28-personalDNSfilter.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/28-personalDNSfilter.svg b/src/web/src/modules/home/components/softwareIcon/imgs/28-personalDNSfilter.svg new file mode 100644 index 000000000..90fd8f734 --- /dev/null +++ b/src/web/src/modules/home/components/softwareIcon/imgs/28-personalDNSfilter.svg @@ -0,0 +1,20 @@ + + + +Layer 1 + + + + + + + + + + + + \ No newline at end of file diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/29-Unbound.png b/src/web/src/modules/home/components/softwareIcon/imgs/29-Unbound.png new file mode 100644 index 000000000..e6100dd8a Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/29-Unbound.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/30-BIND.png b/src/web/src/modules/home/components/softwareIcon/imgs/30-BIND.png new file mode 100644 index 000000000..74723afaf Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/30-BIND.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/31-AdGuard-Home.png b/src/web/src/modules/home/components/softwareIcon/imgs/31-AdGuard-Home.png new file mode 100644 index 000000000..e217b6879 Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/31-AdGuard-Home.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/32-AdNauseam.png b/src/web/src/modules/home/components/softwareIcon/imgs/32-AdNauseam.png new file mode 100644 index 000000000..773f3c556 Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/32-AdNauseam.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/33-Legacy-Unix-Derivatives.png b/src/web/src/modules/home/components/softwareIcon/imgs/33-Legacy-Unix-Derivatives.png new file mode 100644 index 000000000..617ecf5bd Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/33-Legacy-Unix-Derivatives.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/34-Windows-command-line.png b/src/web/src/modules/home/components/softwareIcon/imgs/34-Windows-command-line.png new file mode 100644 index 000000000..0741aa5b9 Binary files /dev/null and b/src/web/src/modules/home/components/softwareIcon/imgs/34-Windows-command-line.png differ diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/imgs.d.ts b/src/web/src/modules/home/components/softwareIcon/imgs/imgs.d.ts new file mode 100644 index 000000000..1090848f7 --- /dev/null +++ b/src/web/src/modules/home/components/softwareIcon/imgs/imgs.d.ts @@ -0,0 +1,9 @@ +declare module "*.svg" { + const content: any; + export default content; +} + +declare module "*.png" { + const content: any; + export default content; +} \ No newline at end of file diff --git a/src/web/src/modules/home/components/softwareIcon/imgs/index.ts b/src/web/src/modules/home/components/softwareIcon/imgs/index.ts new file mode 100644 index 000000000..00cb09611 --- /dev/null +++ b/src/web/src/modules/home/components/softwareIcon/imgs/index.ts @@ -0,0 +1,69 @@ +import img1 from "./01-uBlock-Origin.svg"; +import img2 from "./02-Adblock-Plus.svg"; +import img3 from "./03-AdGuard.svg"; +import img4 from "./04-DNS66.png"; +import img5 from "./05-Nano-Adblocker.png"; +import img6 from "./06-AdBlock.png"; +import img7 from "./07-AdAway.png"; +import img8 from "./08-Personal-Blocklist.png"; +import img10 from "./10-Redirector.png"; +import img11 from "./11-Hosts-File-Editor.png"; +import img12 from "./12-Gas-Mask.png"; +import img13 from "./13-MinerBlock.svg"; +import img14 from "./14-Pi-hole.svg"; +import img15 from "./15-uBlock.svg"; +import img16 from "./16-Internet-Explorer-TPL.svg"; +import img17 from "./17-Google-Hit-Hider-by-Domain.png"; +import img18 from "./18-FireHOL.png"; +import img19 from "./19-Samsung-Knox.png"; +import img20 from "./20-Little-Snitch.png"; +import img21 from "./21-Privoxy.png"; +import img22 from "./22-Diversion.png"; +import img23 from "./23-dnsmasq.png"; +import img24 from "./24-Slimjet.png"; +import img25 from "./25-uMatrix.png"; +import img26 from "./26-Blokada.png"; +import img27 from "./27-hostsmgr.png"; +import img28 from "./28-personalDNSfilter.svg"; +import img29 from "./29-Unbound.png"; +import img30 from "./30-BIND.png"; +import img31 from "./31-AdGuard-Home.png"; +import img32 from "./32-AdNauseam.png"; +import img33 from "./33-Legacy-Unix-Derivatives.png"; +import img34 from "./34-Windows-command-line.png"; + +export { + img1, + img2, + img3, + img4, + img5, + img6, + img7, + img8, + img10, + img11, + img12, + img13, + img14, + img15, + img16, + img17, + img18, + img19, + img20, + img21, + img22, + img23, + img24, + img25, + img26, + img27, + img28, + img29, + img30, + img31, + img32, + img33, + img34 + }; diff --git a/src/web/src/modules/home/components/softwareIcon/index.ts b/src/web/src/modules/home/components/softwareIcon/index.ts new file mode 100644 index 000000000..2dca6d6c1 --- /dev/null +++ b/src/web/src/modules/home/components/softwareIcon/index.ts @@ -0,0 +1,5 @@ +import { SoftwareIcon } from "./SoftwareIcon"; + +export { + SoftwareIcon + }; \ No newline at end of file diff --git a/src/web/src/modules/home/index.ts b/src/web/src/modules/home/index.ts new file mode 100644 index 000000000..f53b5d5c0 --- /dev/null +++ b/src/web/src/modules/home/index.ts @@ -0,0 +1,5 @@ +import { HomeContainer } from "./HomeContainer"; + +export { + HomeContainer as Home + }; \ No newline at end of file diff --git a/src/web/src/modules/home/interfaces/IColumnVisibility.ts b/src/web/src/modules/home/interfaces/IColumnVisibility.ts new file mode 100644 index 000000000..3f11fd9a6 --- /dev/null +++ b/src/web/src/modules/home/interfaces/IColumnVisibility.ts @@ -0,0 +1,4 @@ +export interface IColumnVisibility { + column: string; + visible: boolean; +}; \ No newline at end of file diff --git a/src/web/src/modules/home/interfaces/ILanguage.ts b/src/web/src/modules/home/interfaces/ILanguage.ts new file mode 100644 index 000000000..6f90f94d2 --- /dev/null +++ b/src/web/src/modules/home/interfaces/ILanguage.ts @@ -0,0 +1,6 @@ +export interface ILanguage { + id: number; + filterListIds: number[]; + iso6391: string; + name: string; +}; \ No newline at end of file diff --git a/src/web/src/modules/home/interfaces/ILicense.ts b/src/web/src/modules/home/interfaces/ILicense.ts new file mode 100644 index 000000000..52b42da81 --- /dev/null +++ b/src/web/src/modules/home/interfaces/ILicense.ts @@ -0,0 +1,6 @@ +export interface ILicense { + id: number; + descriptionUrl: string; + filterListIds: number[]; + name: string; +}; \ No newline at end of file diff --git a/src/web/src/modules/home/interfaces/IList.ts b/src/web/src/modules/home/interfaces/IList.ts new file mode 100644 index 000000000..1de1a8607 --- /dev/null +++ b/src/web/src/modules/home/interfaces/IList.ts @@ -0,0 +1,24 @@ +export interface IList { + id: number; + chatUrl: string; + description: string; + descriptionSourceUrl: string; + donateUrl: string; + emailAddress: string; + forumUrl: string; + homeUrl: string; + issuesUrl: string; + languageIds: number[]; + licenseId: number; + maintainerIds: number[]; + name: string; + policyUrl: string; + publishedDate: string; + //ruleCount: number; + submissionUrl: string; + syntaxId: number; + tagIds: number[]; + //updatedDate: string; + viewUrl: string; + viewUrlMirrors: string[]; +}; \ No newline at end of file diff --git a/src/web/src/modules/home/interfaces/IMaintainer.ts b/src/web/src/modules/home/interfaces/IMaintainer.ts new file mode 100644 index 000000000..421ce83c9 --- /dev/null +++ b/src/web/src/modules/home/interfaces/IMaintainer.ts @@ -0,0 +1,8 @@ +export interface IMaintainer { + id: number; + emailAddress: string; + filterListIds: number[]; + homeUrl: string; + name: string; + twitterHandle: string; +}; \ No newline at end of file diff --git a/src/web/src/modules/home/interfaces/ISoftware.ts b/src/web/src/modules/home/interfaces/ISoftware.ts new file mode 100644 index 000000000..840a05fb0 --- /dev/null +++ b/src/web/src/modules/home/interfaces/ISoftware.ts @@ -0,0 +1,7 @@ +export interface ISoftware { + id: number; + homeUrl: string; + isAbpSubscribable: boolean; + name: string; + syntaxIds: number[]; +}; \ No newline at end of file diff --git a/src/web/src/modules/home/interfaces/ISyntax.ts b/src/web/src/modules/home/interfaces/ISyntax.ts new file mode 100644 index 000000000..2dd8fc9ee --- /dev/null +++ b/src/web/src/modules/home/interfaces/ISyntax.ts @@ -0,0 +1,7 @@ +export interface ISyntax { + id: number; + definitionUrl: string; + filterListIds: number[]; + name: string; + softwareIds: number[]; +}; \ No newline at end of file diff --git a/src/web/src/modules/home/interfaces/ITag.ts b/src/web/src/modules/home/interfaces/ITag.ts new file mode 100644 index 000000000..62e526469 --- /dev/null +++ b/src/web/src/modules/home/interfaces/ITag.ts @@ -0,0 +1,6 @@ +export interface ITag { + id: number; + description: string; + filterListIds: number[]; + name: string; +}; \ No newline at end of file diff --git a/src/web/src/modules/home/interfaces/index.ts b/src/web/src/modules/home/interfaces/index.ts new file mode 100644 index 000000000..6a72c288c --- /dev/null +++ b/src/web/src/modules/home/interfaces/index.ts @@ -0,0 +1,19 @@ +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 + }; \ No newline at end of file diff --git a/src/web/src/modules/index.ts b/src/web/src/modules/index.ts new file mode 100644 index 000000000..d4c28103a --- /dev/null +++ b/src/web/src/modules/index.ts @@ -0,0 +1,5 @@ +import { Home } from "./home"; + +export { + Home + }; \ No newline at end of file diff --git a/src/web/src/utils/GetContrast.ts b/src/web/src/utils/GetContrast.ts new file mode 100644 index 000000000..0dc5f4e55 --- /dev/null +++ b/src/web/src/utils/GetContrast.ts @@ -0,0 +1,8 @@ +//https://stackoverflow.com/a/11868398/2343739 +export const getContrast = (hexcolor: string) => { + const r = parseInt(hexcolor.substr(0, 2), 16); + const g = parseInt(hexcolor.substr(2, 2), 16); + const b = parseInt(hexcolor.substr(4, 2), 16); + const yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000; + return (yiq >= 128) ? "black" : "white"; +}; \ No newline at end of file diff --git a/src/web/src/utils/index.ts b/src/web/src/utils/index.ts new file mode 100644 index 000000000..ecd02f727 --- /dev/null +++ b/src/web/src/utils/index.ts @@ -0,0 +1,5 @@ +import { getContrast } from "./GetContrast"; + +export { + getContrast + }; \ No newline at end of file diff --git a/src/web/src/utils/loader.css b/src/web/src/utils/loader.css new file mode 100644 index 000000000..396eb8671 --- /dev/null +++ b/src/web/src/utils/loader.css @@ -0,0 +1,68 @@ +/*https://projects.lukehaas.me/css-loaders/#load1*/ + +.loader, +.loader:before, +.loader:after { + -ms-animation: load1 1s infinite ease-in-out; + -webkit-animation: load1 1s infinite ease-in-out; + animation: load1 1s infinite ease-in-out; + background: #000000; + height: 4em; + width: 1em; +} + +.loader { + -ms-transform: translateZ(0); + -webkit-animation-delay: -0.16s; + -webkit-transform: translateZ(0); + animation-delay: -0.16s; + color: #000000; + font-size: 11px; + margin: 88px auto; + position: relative; + text-indent: -9999em; + transform: translateZ(0); +} + +.loader:before, +.loader:after { + content: ''; + position: absolute; + top: 0; +} + +.loader:before { + -webkit-animation-delay: -0.32s; + animation-delay: -0.32s; + left: -1.5em; +} + +.loader:after { left: 1.5em; } + +@-webkit-keyframes load1 { + 0%, + 80%, + 100% { + box-shadow: 0 0; + height: 4em; + } + + 40% { + box-shadow: 0 -2em; + height: 5em; + } +} + +@keyframes load1 { + 0%, + 80%, + 100% { + box-shadow: 0 0; + height: 4em; + } + + 40% { + box-shadow: 0 -2em; + height: 5em; + } +} \ No newline at end of file