diff --git a/FilterLists.sln.DotSettings b/FilterLists.sln.DotSettings index 316d6dc6f..a6cf3a100 100644 --- a/FilterLists.sln.DotSettings +++ b/FilterLists.sln.DotSettings @@ -1,7 +1,4 @@ - + ExplicitlyExcluded True @@ -59,7 +56,7 @@ </Entry.SortBy> </Entry> <Entry DisplayName="All other members" /> - <Entry DisplayName="Test Methods" Priority="100"> + <Entry Priority="100" DisplayName="Test Methods"> <Entry.Match> <And> <Kind Is="Method" /> @@ -92,7 +89,7 @@ </Entry.Match> </Entry> <Entry DisplayName="All other members" /> - <Entry DisplayName="Test Methods" Priority="100"> + <Entry Priority="100" DisplayName="Test Methods"> <Entry.Match> <And> <Kind Is="Method" /> @@ -105,7 +102,7 @@ </Entry> </TypePattern> <TypePattern DisplayName="Default Pattern"> - <Entry DisplayName="Public Delegates" Priority="100"> + <Entry Priority="100" DisplayName="Public Delegates"> <Entry.Match> <And> <Access Is="Public" /> @@ -116,7 +113,7 @@ <Name /> </Entry.SortBy> </Entry> - <Entry DisplayName="Public Enums" Priority="100"> + <Entry Priority="100" DisplayName="Public Enums"> <Entry.Match> <And> <Access Is="Public" /> @@ -174,7 +171,7 @@ <Access /> </Entry.SortBy> </Entry> - <Entry DisplayName="Interface Implementations" Priority="100"> + <Entry Priority="100" DisplayName="Interface Implementations"> <Entry.Match> <And> <Kind Is="Member" /> @@ -195,6 +192,10 @@ </Patterns> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + True + True + True + True True True True diff --git a/src/FilterLists.Api/V1/Controllers/BaseController.cs b/src/FilterLists.Api/V1/Controllers/BaseController.cs index 98859df44..7aff4293f 100644 --- a/src/FilterLists.Api/V1/Controllers/BaseController.cs +++ b/src/FilterLists.Api/V1/Controllers/BaseController.cs @@ -5,7 +5,7 @@ namespace FilterLists.Api.V1.Controllers { - [ApiVersion("1.0")] + [ApiVersion("1")] //TODO: use versioning without needing to manually specify in swagger-ui (https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/370) [Route("v{version:apiVersion}/[controller]")] [ResponseCache(Duration = 86400)] diff --git a/src/FilterLists.Web/ClientApp/Boot.tsx b/src/FilterLists.Web/ClientApp/Boot.tsx index 227a3870e..b6f2077ca 100644 --- a/src/FilterLists.Web/ClientApp/Boot.tsx +++ b/src/FilterLists.Web/ClientApp/Boot.tsx @@ -5,7 +5,7 @@ import { BrowserRouter } from "react-router-dom"; import * as RoutesModule from "./Routes"; let routes = RoutesModule.Routes; -function renderApp() { +const renderApp = () => { const baseUrl = document.getElementsByTagName("base")[0].getAttribute("href")!; ReactDOM.render( @@ -13,7 +13,7 @@ function renderApp() { , document.getElementById("react-app") ); -} +}; renderApp(); diff --git a/src/FilterLists.Web/ClientApp/Layout.tsx b/src/FilterLists.Web/ClientApp/Layout.tsx index a941f5e7c..578e377cb 100644 --- a/src/FilterLists.Web/ClientApp/Layout.tsx +++ b/src/FilterLists.Web/ClientApp/Layout.tsx @@ -6,58 +6,51 @@ interface IProps { children?: React.ReactNode; } -export const Layout = (props: IProps) => { - return
-
-
-
- { props.children } -
-
-
-
; -}; +export const Layout = (props: IProps) => +
+
+
+
+ { props.children } +
+
+
+
; -const Header = () => { - return
-

- - FilterLists - -

-
; -}; +const Header = () => +
+

+ + FilterLists + +

+
; -const Footer = () => { - return
-

- | | | By -

-
; -}; +const Footer = () => +
+

+ | | | By +

+
; -const ApiLink = () => { - return - API - ; -}; +const ApiLink = () => + + API + ; -const GitHubLink = () => { - return - GitHub - ; -}; +const GitHubLink = () => + + GitHub + ; -const DonateLink = () => { - return - Donate - ; -}; +const DonateLink = () => + + Donate + ; -const OwnerLink = () => { - return - Collin M. Barrett - ; -}; \ No newline at end of file +const OwnerLink = () => + + Collin M. Barrett + ; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/Home.tsx b/src/FilterLists.Web/ClientApp/modules/home/Home.tsx index be1abca5d..79049687c 100644 --- a/src/FilterLists.Web/ClientApp/modules/home/Home.tsx +++ b/src/FilterLists.Web/ClientApp/modules/home/Home.tsx @@ -1,8 +1,8 @@ import * as React from "react"; -import { IListDto, ISoftwareDto, ILanguageDto } from "./interfaces"; -import { Tagline, ListsTable, IColumnVisibility } from "./components"; +import { IColumnVisibility, ILanguage, IList, ISoftware, ITag } from "./interfaces"; +import { Tagline, ListsTable } from "./components"; -const columnVisibilityDefaults = [ +const columnVisibilityDefaults: IColumnVisibility[] = [ { column: "Software", visible: true }, { column: "Tags", visible: true }, { column: "Languages", visible: true }, @@ -10,16 +10,17 @@ const columnVisibilityDefaults = [ ]; interface IProps { - languages: ILanguageDto[]; - lists: IListDto[]; + languages: ILanguage[]; + lists: IList[]; ruleCount: number; - software: ISoftwareDto[]; -} + software: ISoftware[]; + tags: ITag[]; +}; interface IState { columnVisibility: IColumnVisibility[]; pageSize: number; -} +}; export class Home extends React.Component { constructor(props: IProps) { @@ -33,32 +34,32 @@ export class Home extends React.Component { componentDidMount() { this.updatePageSize(); - } - - render() { - return this.props.lists - ?
- - - {this.renderColumnVisibilityCheckboxes()} -
- : null; - } + }; updatePageSize() { this.setState({ pageSize: Math.max(Math.floor((window.innerHeight - 386) / 52), 5) }); - } + }; - private renderColumnVisibilityCheckboxes() { - return
- Visible:  {this.state.columnVisibility.map( - (c: IColumnVisibility, i) => this.renderColumnVisibilityCheckbox(c, i))} + render() { + return
+ + + {this.renderColumnVisibilityCheckboxes()}
; }; - private renderColumnVisibilityCheckbox(props: IColumnVisibility, key: number) { + 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
{ {props.column}
; - } + }; - private checkColumn(props: IColumnVisibility) { + checkColumn(props: IColumnVisibility) { const columnVisibility = this.state.columnVisibility; const index = this.findWithAttr(columnVisibility, "column", props.column); columnVisibility[index].visible = !columnVisibility[index].visible; this.forceUpdate(); - } + }; - private findWithAttr(array: any, attr: string, value: string) { + 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 + }; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/HomeContainer.tsx b/src/FilterLists.Web/ClientApp/modules/home/HomeContainer.tsx index cfea626e1..a70e8027c 100644 --- a/src/FilterLists.Web/ClientApp/modules/home/HomeContainer.tsx +++ b/src/FilterLists.Web/ClientApp/modules/home/HomeContainer.tsx @@ -1,64 +1,67 @@ import * as React from "react"; import "isomorphic-fetch"; -import { ILanguageDto, IListDto, ISoftwareDto } from "./interfaces"; -import "../../utils/loader.css"; +import { ILanguage, IList, ISoftware, ITag } from "./interfaces"; import { Home } from "./Home"; interface IState { - languages: ILanguageDto[]; - lists: IListDto[]; + languages: ILanguage[]; + lists: IList[]; ruleCount: number; - software: ISoftwareDto[]; -} + software: ISoftware[]; + tags: ITag[]; +}; export class HomeContainer extends React.Component<{}, IState> { constructor(props: any) { super(props); this.state = { - lists: [], languages: [], + lists: [], ruleCount: 0, - software: [] + software: [], + tags: [] }; } componentDidMount() { - fetch("https://filterlists.com/api/v1/lists") - .then(r => r.json() as Promise) - .then(d => { - this.setState({ - lists: d - }); - }); + this.fetchLists(); + this.fetchSoftware(); + this.fetchTags(); + this.fetchLanguages(); + this.fetchRuleCount(); + }; + + fetchLists() { + fetch("https://filterlists.com/api/v1/lists/alpha") + .then(r => r.json() as Promise) + .then(d => { this.setState({ lists: d }); }); + }; + + fetchSoftware() { + fetch("https://filterlists.com/api/v1/software") + .then(r => r.json() as Promise) + .then(d => { this.setState({ software: d }); }); + }; + + fetchTags() { + fetch("https://filterlists.com/api/v1/tags") + .then(r => r.json() as Promise) + .then(d => { this.setState({ tags: d }); }); + }; + + fetchLanguages() { + fetch("https://filterlists.com/api/v1/languages") + .then(r => r.json() as Promise) + .then(d => { this.setState({ languages: d }); }); + }; + + fetchRuleCount() { fetch("https://filterlists.com/api/v1/rules") .then(r => r.json() as Promise) - .then(d => { - this.setState({ - ruleCount: d - }); - }); - fetch("https://filterlists.com/api/v1/software") - .then(r => r.json() as Promise) - .then(d => { - this.setState({ - software: d - }); - }); - fetch("https://filterlists.com/api/v1/languages") - .then(r => r.json() as Promise) - .then(d => { - this.setState({ - languages: d - }); - }); - } + .then(d => { this.setState({ ruleCount: d }); }); + }; render() { - return this.state.lists.length === 0 || - this.state.ruleCount === 0 || - this.state.software.length === 0 || - this.state.languages.length === 0 - ?
Loading...
- : ; - } -} \ No newline at end of file + return ; + }; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/Tag.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/Tag.tsx new file mode 100644 index 000000000..59435ea9b --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/Tag.tsx @@ -0,0 +1,39 @@ +import * as React from "react"; +import { ITag } from "../interfaces"; +import { getContrast } from "../../../utils"; + +export const Tag = (props: ITag) => { + const hexColor = kelly_colors_hex[props.id % kelly_colors_hex.length]; + return + {props.name} + ; +}; + +//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 +]; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/Tagline.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/Tagline.tsx index 96fec6668..91f939a94 100644 --- a/src/FilterLists.Web/ClientApp/modules/home/components/Tagline.tsx +++ b/src/FilterLists.Web/ClientApp/modules/home/components/Tagline.tsx @@ -2,14 +2,16 @@ interface IProps { listCount: number; - ruleCount: number, -} + ruleCount: number; +}; -export const Tagline = (props: IProps) => { - return

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

; -}; \ No newline at end of file +export const Tagline = (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/FilterLists.Web/ClientApp/modules/home/components/index.ts b/src/FilterLists.Web/ClientApp/modules/home/components/index.ts index 6efde3d06..cf8a6d689 100644 --- a/src/FilterLists.Web/ClientApp/modules/home/components/index.ts +++ b/src/FilterLists.Web/ClientApp/modules/home/components/index.ts @@ -1,11 +1,9 @@ import { DetailsExpander } from "./detailsExpander"; import { ListsTable } from "./listsTable"; -import { IColumnVisibility } from "./listsTable"; import { Tagline } from "./Tagline"; export { DetailsExpander, ListsTable, - IColumnVisibility, Tagline }; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/ListsTable.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/ListsTable.tsx index 99b26cbbb..6c3794b99 100644 --- a/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/ListsTable.tsx +++ b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/ListsTable.tsx @@ -1,183 +1,35 @@ import * as React from "react"; -import { IListDto, ISoftwareDto, ILanguageDto } from "../../interfaces"; +import { IColumnVisibility, ILanguage, IList, ISoftware, ITag } from "../../interfaces"; +import "../../../../utils/loader.css"; import ReactTable from "react-table" import "react-table/react-table.css" import "./listsTable.css"; -import { SoftwareIcon } from "../softwareIcon"; -import { getContrast } from "../../../../utils"; -import * as moment from "moment"; +import { DetailsButton, Languages, Name, Software, Tags, UpdatedDate } from "./columns"; import { DetailsExpander } from "../../components"; -export interface IColumnVisibility { - column: string; - visible: boolean; -} - interface IProps { - languages: ILanguageDto[]; - lists: IListDto[]; - software: ISoftwareDto[]; + languages: ILanguage[]; + lists: IList[]; + software: ISoftware[]; + tags: ITag[]; columnVisibility: IColumnVisibility[]; pageSize: number; -} +}; -export const ListsTable = (props: IProps) => { - return - r[f.id].toUpperCase().includes(f.value.toUpperCase()), - sortMethod: (a: any, b: any) => a.toUpperCase() > b.toUpperCase() ? 1 : -1, - Cell: (c: any) =>

{c.value}

- }, - { - Header: "Software", - accessor: "softwareIds", - filterable: true, - filterMethod: (f: any, r: any) => - f.value === "any" || r[f.id].join(",").split(",").includes(f.value), - Filter: ({ filter, onChange }) => - , - sortable: false, - Cell: (c: any) => c.value - ? c.value.map((s: number, i: number) => ) - : null, - width: 155, - headerClassName: "d-none d-md-block", - className: "d-none d-md-block", - show: props.columnVisibility.filter( - (c: IColumnVisibility) => { return c.column === "Software"; })[0].visible - }, - { - Header: "Tags", - accessor: "tags", - filterable: true, - filterMethod: (f: any, r: any) => - (r[f.id] - ? r[f.id].map((e: any) => e.name).join().toUpperCase().includes(f.value.toUpperCase()) - : null), - sortMethod: (a: any, b: any) => a - ? b - ? a.length === b.length - ? a[0].name > b[0].name - ? -1 - : 1 - : a.length > b.length - ? 1 - : -1 - : 1 - : -1, - Cell: (c: any) => c.value - ?
- {c.value.map((e: any, i: number) => - - {e.name} - )} -
- : null, - width: 215, - headerClassName: "d-none d-md-block", - className: "d-none d-md-block", - show: props.columnVisibility.filter( - (c: IColumnVisibility) => { return c.column === "Tags"; })[0].visible - }, - { - Header: "Languages", - accessor: "languages", - filterable: true, - filterMethod: (f: any, r: any) => - f.value === "any" || - (r[f.id] - ? r[f.id].map((x: any) => x.iso6391).includes(f.value) - : false), - Filter: ({ filter, onChange }) => - , - sortable: false, - Cell: (c: any) => c.value - ?
- {c.value.map((e: ILanguageDto, i: number) => - - {e.iso6391} - )} -
- : null, - style: { whiteSpace: "inherit" }, - width: 95, - headerClassName: "d-none d-md-block", - className: "d-none d-md-block", - show: props.columnVisibility.filter( - (c: IColumnVisibility) => { return c.column === "Languages"; })[0].visible - }, - { - Header: "Updated", - accessor: "updatedDate", - filterable: false, - filterMethod: (f: any, r: any) => r[f.id].includes(f.value), - sortMethod: (a: any, b: any) => - moment(a).isValid() ? (moment(b).isValid() ? (moment(a).isBefore(b) ? -1 : 1) : 1) : -1, - Cell: (c: any) => -
- {moment(c.value).isValid() ? moment(c.value).format("l") : "N/A"} -
, - style: { whiteSpace: "inherit" }, - width: 100, - headerClassName: "d-none d-md-block", - className: "d-none d-md-block", - show: props.columnVisibility.filter( - (c: IColumnVisibility) => { return c.column === "Updated Date"; })[0].visible - }, - { - Header: "Details", - accessor: "id", - sortable: false, - expander: true, - Expander: ({ isExpanded, row }) => -
- {isExpanded - ? - : } -
, - style: { textAlign: "center" }, - width: 90 - } - ]} - SubComponent={(r: any) => } - className="-striped -highlight"/>; -}; \ No newline at end of file +export const ListsTable = (props: IProps) => + props.lists.length > 0 && props.languages.length > 0 && props.software.length > 0 + ? } + className="-striped -highlight"/> + :
Loading...
; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/DetailsButton.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/DetailsButton.tsx new file mode 100644 index 000000000..0d8c431fc --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/DetailsButton.tsx @@ -0,0 +1,25 @@ +import * as React from "react"; +import { Column, RowRenderProps } from "react-table"; + +export const DetailsButton = { + Header: "Details", + accessor: "id", + sortable: false, + expander: true, + Expander: ({ isExpanded, row }: RowRenderProps) => Expander({ isExpanded, row }), + style: { textAlign: "center" }, + width: 90 +} as Column; + +const Expander = (props: RowRenderProps) => +
+ {props.isExpanded + ? + : } +
; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/Languages.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/Languages.tsx new file mode 100644 index 000000000..47dc37e12 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/Languages.tsx @@ -0,0 +1,52 @@ +import * as React from "react"; +import { Column, Filter } from "react-table"; +import { IColumnVisibility, ILanguage } from "../../../interfaces"; + +export const Languages = (columnVisibility: IColumnVisibility[], languages: ILanguage[]) => { + return { + Header: "Languages", + accessor: "languageIds", + filterable: true, + filterMethod: (f: Filter, r: any[]) => filterMethod(f, r), + Filter: ({ onChange, filter }: any) => Filter({ onChange, filter }, languages), + sortable: false, + Cell: (c: any) => Cell(c.value, languages), + style: { whiteSpace: "inherit" }, + width: 95, + headerClassName: "d-none d-md-block", + className: "d-none d-md-block", + 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) + : false); +}; + +const Filter = (props: any, languages: ILanguage[]) => + ; + +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/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/Name.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/Name.tsx new file mode 100644 index 000000000..a4b962a00 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/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.toUpperCase() > b.toUpperCase() ? 1 : -1; + +const Cell = (name: string) => + name + ?

{name}

+ : null; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/Software.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/Software.tsx new file mode 100644 index 000000000..a8976fa22 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/Software.tsx @@ -0,0 +1,46 @@ +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[]) => { + return { + Header: "Software", + accessor: "syntaxId", + filterable: true, + filterMethod: (f: Filter, r: any[]) => filterMethod(f, r, software), + Filter: ({ filter, onChange }: any) => Filter({ onChange, filter }, software), + sortable: false, + Cell: (c: any) => Cell(c.value, software), + width: 155, + headerClassName: "d-none d-md-block", + className: "d-none d-md-block", + 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 Cell = (listSyntaxId: number, software: ISoftware[]) => + listSyntaxId + ? software.filter((s: ISoftware) => s.syntaxIds.indexOf(listSyntaxId) > -1) + .map((s: ISoftware, i: number) => ) + : null; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/Tags.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/Tags.tsx new file mode 100644 index 000000000..66be05ebf --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/Tags.tsx @@ -0,0 +1,49 @@ +import * as React from "react"; +import { Column, Filter } from "react-table"; +import { Tag } from "../../Tag" +import { IColumnVisibility, ITag } from "../../../interfaces"; + +export const Tags = (columnVisibility: IColumnVisibility[], tags: ITag[]) => { + return { + Header: "Tags", + accessor: "tagIds", + filterable: true, + filterMethod: (f: Filter, r: any[]) => filterMethod(f, r, tags), + sortMethod: (a: number[], b: number[]) => sortMethod(a, b), + Cell: (c: any) => Cell(c.value, tags), + width: 215, + headerClassName: "d-none d-md-block", + className: "d-none d-md-block", + show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Tags")[0].visible + } as Column; +}; + +const filterMethod = (f: Filter, r: any[], tags: ITag[]): boolean => { + const listTagIds = r[f.id as any]; + if (f.value.length > 0 && listTagIds && listTagIds.length > 0) { + const listTags = tags.filter((t: ITag) => listTagIds.includes(t.id)); + const listTagNames = listTags.map((t: ITag) => t.name.toLowerCase()).join(",").split(","); + return listTagNames.filter((l: string) => l.indexOf(f.value.toLowerCase()) > -1).length > 0; + } else { + return false; + } +}; + +const sortMethod = (a: number[], b: number[]): any => + a + ? b + ? a.length > b.length + ? 1 + : -1 + : 1 + : -1; + +const Cell = (tagIds: number[], tags: ITag[]) => + tagIds + ?
+ {tagIds.map((tid: number, i: number) => { + const tag = tags.filter((t: ITag) => t.id === tid)[0]; + return ; + })} +
+ : null; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/UpdatedDate.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/UpdatedDate.tsx new file mode 100644 index 000000000..4e48460cb --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/UpdatedDate.tsx @@ -0,0 +1,34 @@ +import * as React from "react"; +import { Column } from "react-table"; +import * as moment from "moment"; +import { IColumnVisibility } from "../../../interfaces"; + +export const UpdatedDate = (columnVisibility: IColumnVisibility[]) => { + return { + Header: "Updated", + accessor: "updatedDate", + sortMethod: (a: string, b: string) => sortMethod(a, b), + Cell: (c: any) => Cell(c.value), + style: { whiteSpace: "inherit" }, + width: 100, + headerClassName: "d-none d-md-block", + className: "d-none d-md-block", + show: columnVisibility.filter((c: IColumnVisibility) => c.column === "Updated Date")[0].visible + } as Column; +}; + +const sortMethod = (a: string, b: string) => + moment(a).isValid() + ? (moment(b).isValid() + ? (moment(a).isBefore(b) + ? -1 + : 1) + : 1) + : -1; + +const Cell = (updatedDate: string) => +
+ {moment(updatedDate).isValid() + ? moment(updatedDate).format("l") + : null} +
; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/index.ts b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/index.ts new file mode 100644 index 000000000..26e25dab1 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/columns/index.ts @@ -0,0 +1,15 @@ +import { DetailsButton } from "./DetailsButton"; +import { Languages } from "./Languages"; +import { Name } from "./Name"; +import { Software } from "./Software"; +import { Tags } from "./Tags"; +import { UpdatedDate } from "./UpdatedDate"; + +export { + DetailsButton, + Languages, + Name, + Software, + Tags, + UpdatedDate + }; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/index.ts b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/index.ts index afe683357..48483597b 100644 --- a/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/index.ts +++ b/src/FilterLists.Web/ClientApp/modules/home/components/listsTable/index.ts @@ -1,6 +1,5 @@ -import { ListsTable, IColumnVisibility } from "./ListsTable"; +import { ListsTable } from "./ListsTable"; export { ListsTable, - IColumnVisibility }; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/interfaces/IColumnVisibility.ts b/src/FilterLists.Web/ClientApp/modules/home/interfaces/IColumnVisibility.ts new file mode 100644 index 000000000..3f11fd9a6 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/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/FilterLists.Web/ClientApp/modules/home/interfaces/ILanguage.ts b/src/FilterLists.Web/ClientApp/modules/home/interfaces/ILanguage.ts new file mode 100644 index 000000000..ba0c62f64 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/interfaces/ILanguage.ts @@ -0,0 +1,5 @@ +export interface ILanguage { + id: number; + iso6391: string; + name: string; +} \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/interfaces/ILanguageDto.ts b/src/FilterLists.Web/ClientApp/modules/home/interfaces/ILanguageDto.ts deleted file mode 100644 index a5c69134e..000000000 --- a/src/FilterLists.Web/ClientApp/modules/home/interfaces/ILanguageDto.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface ILanguageDto { - iso6391: string; - name: string; -} \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/interfaces/IList.ts b/src/FilterLists.Web/ClientApp/modules/home/interfaces/IList.ts new file mode 100644 index 000000000..9f48d3fa6 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/interfaces/IList.ts @@ -0,0 +1,8 @@ +export interface IList { + id: number; + languageIds: number[]; + name: string; + syntaxId: number; + tagIds: number[]; + updatedDate: string; +} \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/interfaces/IListDto.ts b/src/FilterLists.Web/ClientApp/modules/home/interfaces/IListDto.ts deleted file mode 100644 index 2dceafa49..000000000 --- a/src/FilterLists.Web/ClientApp/modules/home/interfaces/IListDto.ts +++ /dev/null @@ -1,19 +0,0 @@ -export interface IListDto { - id: number; - languages: IListLanguageDto[]; - name: string; - softwareIds: number[]; - tags: IListTagDto[]; - updatedDate: string; -} - -interface IListLanguageDto { - name: string; - iso6391: string; -} - -interface IListTagDto { - name: string; - colorHex: string; - description: string; -} \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/interfaces/ISoftware.ts b/src/FilterLists.Web/ClientApp/modules/home/interfaces/ISoftware.ts new file mode 100644 index 000000000..70bf8b27e --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/interfaces/ISoftware.ts @@ -0,0 +1,5 @@ +export interface ISoftware { + id: number; + name: string; + syntaxIds: number[]; +} \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/interfaces/ISoftwareDto.ts b/src/FilterLists.Web/ClientApp/modules/home/interfaces/ISoftwareDto.ts deleted file mode 100644 index a4a382910..000000000 --- a/src/FilterLists.Web/ClientApp/modules/home/interfaces/ISoftwareDto.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface ISoftwareDto { - id: number; - name: string; -} \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/interfaces/ITag.ts b/src/FilterLists.Web/ClientApp/modules/home/interfaces/ITag.ts new file mode 100644 index 000000000..09fb0ea59 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/interfaces/ITag.ts @@ -0,0 +1,5 @@ +export interface ITag { + id: number; + description: string; + name: string; +} \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/interfaces/index.ts b/src/FilterLists.Web/ClientApp/modules/home/interfaces/index.ts index 2ece65482..0f887badc 100644 --- a/src/FilterLists.Web/ClientApp/modules/home/interfaces/index.ts +++ b/src/FilterLists.Web/ClientApp/modules/home/interfaces/index.ts @@ -1,9 +1,14 @@ -import { ILanguageDto } from "./ILanguageDto"; -import { IListDto } from "./IListDto"; -import { ISoftwareDto } from "./ISoftwareDto"; +import { IColumnVisibility } from "./IColumnVisibility"; +import { ILanguage } from "./ILanguage"; +import { IList } from "./IList"; +import { ISoftware } from "./ISoftware"; +import { ITag } from "./ITag"; + export { - ILanguageDto, - IListDto, - ISoftwareDto + IColumnVisibility, + ILanguage, + IList, + ISoftware, + ITag }; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/utils/GetContrast.ts b/src/FilterLists.Web/ClientApp/utils/GetContrast.ts index 74e21893b..0dc5f4e55 100644 --- a/src/FilterLists.Web/ClientApp/utils/GetContrast.ts +++ b/src/FilterLists.Web/ClientApp/utils/GetContrast.ts @@ -5,4 +5,4 @@ export const getContrast = (hexcolor: string) => { 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 +}; \ No newline at end of file diff --git a/src/FilterLists.Web/npm-shrinkwrap.json b/src/FilterLists.Web/npm-shrinkwrap.json index dd9b54ac9..203f62634 100644 --- a/src/FilterLists.Web/npm-shrinkwrap.json +++ b/src/FilterLists.Web/npm-shrinkwrap.json @@ -85,7 +85,7 @@ "@types/webpack-env": { "version": "1.13.6", "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.13.6.tgz", - "integrity": "sha1-Eo0WhafDTTHtFwEPyH1qEsHeaXY=", + "integrity": "sha512-5Th3OsZ4gTRdr9Mho83BQ23cex4sRhOR4XTG+m+cJc0FhtUBK9Vn62hBJ+pnQYnSxoPOsKoAPOx6FcphxBC8ng==", "dev": true }, "acorn": { @@ -496,7 +496,7 @@ "bootstrap": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.1.3.tgz", - "integrity": "sha1-DrNxryyESOjCEEEdDLgkpkCaEr4=", + "integrity": "sha512-rDFIzgXcof0jDyjNosjv4Sno77X4KuPeFxG2XZZv1/Kc8DRVGVADdoQyyOVDwPqL36DDmtCQbrpMCqvpPLJQ0w==", "dev": true }, "brace-expansion": { @@ -1735,7 +1735,7 @@ "extract-text-webpack-plugin": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz", - "integrity": "sha1-XwQ+qgL5dQqSWLeMCm4NwUCPsvc=", + "integrity": "sha512-bt/LZ4m5Rqt/Crl2HiKuAl/oqg0psx1tsTLkvWbJen1CtD+fftkZhMaQ9HOtY2gWsl2Wq+sABmMVi9z3DhKWQQ==", "dev": true, "requires": { "async": "^2.4.1", @@ -2892,7 +2892,7 @@ "history": { "version": "4.7.2", "resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz", - "integrity": "sha1-IrXH8xYzxbgCHH9KipVKwTnujVs=", + "integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==", "dev": true, "requires": { "invariant": "^2.2.1", @@ -2927,7 +2927,7 @@ "hoist-non-react-statics": { "version": "2.5.5", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", - "integrity": "sha1-xZA89AnA39kI84jmGdhrnBF0y0c=", + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==", "dev": true }, "hosted-git-info": { @@ -3059,7 +3059,7 @@ "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha1-YQ88ksk1nOHbYW5TgAjSP/NRWOY=", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "dev": true, "requires": { "loose-envify": "^1.0.0" @@ -3297,7 +3297,7 @@ "jquery": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", - "integrity": "sha1-lYzinoHJeQ8xvneS311NlfxX+8o=", + "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==", "dev": true }, "js-base64": { @@ -3331,7 +3331,7 @@ "json-loader": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", - "integrity": "sha1-3KFKcCNf+C8KyaOr62DTN6NlGF0=", + "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", "dev": true }, "json-schema-traverse": { @@ -4829,7 +4829,7 @@ "react-router": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz", - "integrity": "sha1-qtpK7xTICcsuaGsFzuR0IjRQbE4=", + "integrity": "sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==", "dev": true, "requires": { "history": "^4.7.2", @@ -4844,7 +4844,7 @@ "react-router-dom": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.3.1.tgz", - "integrity": "sha1-TCYZ/CTE+ofJ/Rj0+0pD/mP71cY=", + "integrity": "sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==", "dev": true, "requires": { "history": "^4.7.2", @@ -5021,7 +5021,7 @@ "resolve-pathname": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz", - "integrity": "sha1-fpriHtgV/WOrGJre7mTcgx7vqHk=", + "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==", "dev": true }, "resolve-url": { @@ -6123,7 +6123,7 @@ "value-equal": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz", - "integrity": "sha1-xb3S9U7gk8BIOdcc4uR1imiQq8c=", + "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==", "dev": true }, "vendors": { @@ -6144,7 +6144,7 @@ "warning": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.1.tgz", - "integrity": "sha1-Zs43a3+/6KiHwivfDnNJ1z05d0U=", + "integrity": "sha512-rAVtTNZw+cQPjvGp1ox0XC5Q2IBFyqoqh+QII4J/oguyu83Bax1apbo2eqB8bHRS+fqYUBagys6lqUoVwKSmXQ==", "dev": true, "requires": { "loose-envify": "^1.0.0"