From 1854689ce925da904ac157eb4ab60e755367757a Mon Sep 17 00:00:00 2001 From: "Collin M. Barrett" Date: Fri, 14 Sep 2018 20:36:08 -0500 Subject: [PATCH] refactor GUI (primarily detailsExpander) --- FilterLists.sln.DotSettings | 1 + .../ClientApp/{boot.tsx => Boot.tsx} | 8 +- src/FilterLists.Web/ClientApp/Layout.tsx | 63 +++ src/FilterLists.Web/ClientApp/Routes.tsx | 8 + .../ClientApp/components/Layout.tsx | 29 -- .../ClientApp/components/ListDetails.tsx | 483 ------------------ .../{components => modules/home}/Home.tsx | 22 +- .../DetailsExpander.tsx | 21 + .../DetailsExpanderContainer.tsx | 42 ++ .../IFilterListDetailsDto.ts | 61 +++ .../LinkButtonGroup.tsx | 44 ++ .../detailsExpanderContainer/index.ts | 5 + .../infoCard/Description.tsx | 17 + .../infoCard/DiscontinuedDate.tsx | 14 + .../infoCard/InfoCard.tsx | 39 ++ .../infoCard/Languages.tsx | 20 + .../infoCard/License.tsx | 14 + .../infoCard/PublishedDate.tsx | 14 + .../infoCard/RuleCount.tsx | 13 + .../detailsExpanderContainer/infoCard/Tag.tsx | 17 + .../infoCard/Tags.tsx | 15 + .../infoCard/UpdatedDate.tsx | 19 + .../infoCard/description.css | 1 + .../infoCard/index.ts | 5 + .../MaintainerAdditionalLists.tsx | 20 + .../MaintainerInfoCard.tsx | 20 + .../MaintainerLinkButtonGroup.tsx | 17 + .../MaintainersInfoCard.tsx | 15 + .../maintainersInfoCard/index.ts | 5 + .../modules/home/components/index.ts | 5 + .../components/linkButtons/ChatButton.tsx | 15 + .../components/linkButtons/DonateButton.tsx | 15 + .../components/linkButtons/EmailButton.tsx | 15 + .../components/linkButtons/ForumButton.tsx | 15 + .../components/linkButtons/HomeButton.tsx | 15 + .../components/linkButtons/IssuesButton.tsx | 15 + .../components/linkButtons/LinkButton.tsx | 19 + .../components/linkButtons/PolicyButton.tsx | 15 + .../components/linkButtons/SubmitButton.tsx | 15 + .../linkButtons/SubscribeButton.tsx | 34 ++ .../linkButtons/SubscribeButtonGroup.tsx | 52 ++ .../components/linkButtons/TwitterButton.tsx | 15 + .../components/linkButtons/ViewButton.tsx | 16 + .../linkButtons/ViewButtonGroup.tsx | 52 ++ .../home/components/linkButtons/button.css | 5 + .../home/components/linkButtons/index.ts | 25 + .../{css/site.css => modules/home/home.css} | 15 +- .../ClientApp/modules/home/index.ts | 5 + .../ClientApp/modules/index.ts | 5 + src/FilterLists.Web/ClientApp/routes.tsx | 6 +- src/FilterLists.Web/ClientApp/site.css | 1 + .../ClientApp/utils/GetContrast.ts | 8 + .../ClientApp/utils/loader.css | 67 +++ src/FilterLists.Web/webpack.config.js | 2 +- 54 files changed, 962 insertions(+), 542 deletions(-) rename src/FilterLists.Web/ClientApp/{boot.tsx => Boot.tsx} (76%) create mode 100644 src/FilterLists.Web/ClientApp/Layout.tsx create mode 100644 src/FilterLists.Web/ClientApp/Routes.tsx delete mode 100644 src/FilterLists.Web/ClientApp/components/Layout.tsx delete mode 100644 src/FilterLists.Web/ClientApp/components/ListDetails.tsx rename src/FilterLists.Web/ClientApp/{components => modules/home}/Home.tsx (94%) create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/DetailsExpander.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/DetailsExpanderContainer.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/IFilterListDetailsDto.ts create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/LinkButtonGroup.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/index.ts create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/Description.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/DiscontinuedDate.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/InfoCard.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/Languages.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/License.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/PublishedDate.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/RuleCount.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/Tag.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/Tags.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/UpdatedDate.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/description.css create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/index.ts create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/MaintainerAdditionalLists.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/MaintainerInfoCard.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/MaintainerLinkButtonGroup.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/MaintainersInfoCard.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/index.ts create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/index.ts create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/ChatButton.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/DonateButton.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/EmailButton.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/ForumButton.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/HomeButton.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/IssuesButton.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/LinkButton.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/PolicyButton.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/SubmitButton.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/SubscribeButton.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/SubscribeButtonGroup.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/TwitterButton.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/ViewButton.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/ViewButtonGroup.tsx create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/button.css create mode 100644 src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/index.ts rename src/FilterLists.Web/ClientApp/{css/site.css => modules/home/home.css} (62%) create mode 100644 src/FilterLists.Web/ClientApp/modules/home/index.ts create mode 100644 src/FilterLists.Web/ClientApp/modules/index.ts create mode 100644 src/FilterLists.Web/ClientApp/site.css create mode 100644 src/FilterLists.Web/ClientApp/utils/GetContrast.ts create mode 100644 src/FilterLists.Web/ClientApp/utils/loader.css diff --git a/FilterLists.sln.DotSettings b/FilterLists.sln.DotSettings index 93a95ae99..316d6dc6f 100644 --- a/FilterLists.sln.DotSettings +++ b/FilterLists.sln.DotSettings @@ -13,6 +13,7 @@ True False CHOP_IF_LONG + ALIGN_ALL <?xml version="1.0" encoding="utf-16"?> <Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"> <TypePattern DisplayName="Non-reorderable types"> diff --git a/src/FilterLists.Web/ClientApp/boot.tsx b/src/FilterLists.Web/ClientApp/Boot.tsx similarity index 76% rename from src/FilterLists.Web/ClientApp/boot.tsx rename to src/FilterLists.Web/ClientApp/Boot.tsx index ff9c3c201..bbfe160fc 100644 --- a/src/FilterLists.Web/ClientApp/boot.tsx +++ b/src/FilterLists.Web/ClientApp/Boot.tsx @@ -2,10 +2,8 @@ import * as React from "react"; import * as ReactDOM from "react-dom"; import { AppContainer } from "react-hot-loader"; import { BrowserRouter } from "react-router-dom"; -import * as RoutesModule from "./routes"; -import "bootstrap"; -import "./css/site.css"; -let routes = RoutesModule.routes; +import * as RoutesModule from "./Routes"; +let routes = RoutesModule.Routes; function renderApp() { const baseUrl = document.getElementsByTagName("base")[0].getAttribute("href")!; @@ -22,7 +20,7 @@ renderApp(); if (module.hot) { module.hot.accept("./routes", () => { - routes = require("./routes").routes; + routes = require("./Routes").Routes; renderApp(); }); } \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/Layout.tsx b/src/FilterLists.Web/ClientApp/Layout.tsx new file mode 100644 index 000000000..466b1050d --- /dev/null +++ b/src/FilterLists.Web/ClientApp/Layout.tsx @@ -0,0 +1,63 @@ +import * as React from "react"; +import "bootstrap"; +import "./site.css"; + +interface ILayoutProps { + children?: React.ReactNode; +} + +export const Layout = (props: ILayoutProps) => { + return
+
+
+
+ { props.children } +
+
+
+
; +}; + +const Header = () => { + return
+

+ + FilterLists + +

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

+ | | | By +

+
; +}; + +const ApiLink = () => { + return + API + ; +}; + +const GitHubLink = () => { + return + GitHub + ; +}; + +const DonateLink = () => { + return + Donate + ; +}; + +const OwnerLink = () => { + return + Collin M. Barrett + ; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/Routes.tsx b/src/FilterLists.Web/ClientApp/Routes.tsx new file mode 100644 index 000000000..4e94675c1 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/Routes.tsx @@ -0,0 +1,8 @@ +import * as React from "react"; +import { Layout } from "./Layout"; +import { Route } from "react-router-dom"; +import { Home } from "./modules"; + +export const Routes = + + ; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/components/Layout.tsx b/src/FilterLists.Web/ClientApp/components/Layout.tsx deleted file mode 100644 index 71f5afe6f..000000000 --- a/src/FilterLists.Web/ClientApp/components/Layout.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import * as React from "react"; - -export interface ILayoutProps { - children?: React.ReactNode; -} - -export class Layout extends React.Component { - render() { - return
-
-

- - FilterLists - -

-
-
-
- { this.props.children } -
-
-
-

- API | GitHub | Donate | By Collin M. Barrett -

-
-
; - }; -} \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/components/ListDetails.tsx b/src/FilterLists.Web/ClientApp/components/ListDetails.tsx deleted file mode 100644 index b86f2e36d..000000000 --- a/src/FilterLists.Web/ClientApp/components/ListDetails.tsx +++ /dev/null @@ -1,483 +0,0 @@ -import * as React from "react"; -import "isomorphic-fetch"; -import * as moment from "moment"; - -export default class ListDetails extends React.Component { - - constructor(props: any) { - super(props); - this.state = { - responseLoaded: false, - listId: props.listId - }; - this.fetchData(); - } - - fetchData() { - fetch(`https://filterlists.com/api/v1/lists/${this.state.listId}`) - .then(response => response.json() as Promise) - .then(data => { - this.setState({ - filterListDetails: data, - responseLoaded: true - }); - }); - } - - render() { - return this.state.responseLoaded - ?
- -
- :
Loading...
; - } - - //https://stackoverflow.com/a/11868398/2343739 - static 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"; - } -} - -function FilterListDetails(props: any) { - return
-
-
-
- - -
-
- -
-
-
-
; -} - -function ListInfo(props: any) { - return
- - -
    - - - - {props.details.discontinuedDate ? null : } - - -
-
; -} - -function ListUrls(props: any) { - return
- - - - - - - - - - - - - - -
; -} - -function Tags(props: any) { - return props.tags.length > 0 - ?
{props.tags.map( - (tag: any) => {tag.name})}
- : null; -} - -function Description(props: any) { - return props.description - ? (props.url - ?

-
{props.description}
-

- :

{props.description}

) - : null; -} - -function Languages(props: any) { - return props.languages.length > 0 - ? props.languages.length > 1 - ?
  • -

    Languages:

    -
      - {props.languages.map((language: any) =>
    • {language}
    • )} -
    -
  • - :
  • -

    Language: {props.languages.map((language: any) => language)}

    -
  • - : null; -} - -function RuleCount(props: any) { - return props.count > 0 - ?
  • -

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

    -
  • - : null; -} - -function DiscontinuedDate(props: any) { - return props.date - ?
  • -

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

    -
  • - : null; -} - -function UpdatedDate(props: any) { - return props.date - ?
  • -

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

    -
  • - : null; -} - -function PublishedDate(props: any) { - return props.date - ?
  • -

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

    -
  • - : null; -} - -function License(props: any) { - return props.license - ? (props.license.descriptionUrl - ?
  • -

    License: {props.license.name}

    -
  • - :
  • -

    License: {props.license.name}

    -
  • ) - : null; -} - -function SubscribeUrl(props: any) { - return props.url.indexOf("https://") === -1 - ? SubscribeUrlNotSecure() - : props.url.indexOf("web.archive.org") === -1 - ? SubscribeUrlPrimary() - : SubscribeUrlWayback(); - - function SubscribeUrlPrimary() { - return - Subscribe - ; - } - - function SubscribeUrlWayback() { - return - Subscribe - ; - } - - function SubscribeUrlNotSecure() { - return - Subscribe - ; - } -}; - -function SubscribeUrlMirror(props: any) { - return props.url - ? props.url.indexOf("https://") === -1 - ? SubscribeUrlNotSecure() - : SubscribeUrlSecondary() - : null; - - function SubscribeUrlSecondary() { - return - Subscribe - ; - } - - function SubscribeUrlNotSecure() { - return - Subscribe - ; - } -}; - -function ViewUrl(props: any) { - return props.url.indexOf("https://") === -1 - ? ViewUrlNotSecure() - : props.url.indexOf("web.archive.org") === -1 - ? ViewUrlPrimary() - : ViewUrlWayback(); - - function ViewUrlPrimary() { - return - View - ; - } - - function ViewUrlWayback() { - return - View - ; - } - - function ViewUrlNotSecure() { - return - View - ; - } -}; - -function ViewUrlMirror(props: any) { - return props.url - ? props.url.indexOf("https://") === -1 - ? ViewUrlNotSecure() - : viewUrlSecondary() - : null; - - function viewUrlSecondary() { - return - View - ; - } - - function ViewUrlNotSecure() { - return - View - ; - } -}; - -function HomeUrl(props: any) { - return props.url - ? - Home - - : null; -} - -function PolicyUrl(props: any) { - return props.url - ? - Policy - - : null; -} - -function DonateUrl(props: any) { - return props.url - ? - Donate - - : null; -} - -function IssuesUrl(props: any) { - return props.url - ? - GH Issues - - : null; -} - -function ForumUrl(props: any) { - return props.url - ? - Forum - - : null; -} - -function ChatUrl(props: any) { - return props.url - ? - Chat - - : null; -} - -function SubmissionUrl(props: any) { - return props.url - ? - Add Rule - - : null; -} - -function EmailAddress(props: any) { - return props.email - ? - Email - - : null; -} - -function Maintainers(props: any) { - return props.maintainers.length > 0 - ?
    - {props.maintainers.map( - (maintainer: any) => )} -
    - : null; -} - -function Maintainer(props: any) { - return
    -
    -

    Maintained by {props.maintainer.name}

    -
    -
    - - -
    -
    -
    -
    ; -} - -function MaintainerAdditionalLists(props: any) { - return
    - {props.maintainer.additionalLists.length > 0 - ?
    -

    More by {props.maintainer.name}:

    -
      - {props.maintainer.additionalLists.map( - (list: any) => )} -
    -
    - : null} -
    ; -} - -function MaintainerAdditionalList(props: any) { - return
  • {props.list.name}
  • ; -} - -function MaintainerUrls(props: any) { - return
    - {props.maintainer.homeUrl - ? - Home - - : null} - {props.maintainer.emailAddress - ? - Email - - : null} - {props.maintainer.twitterHandle - ? - Twitter - - : null} -
    ; -} - -interface IFilterListDetailsDto { - chatUrl: string; - description: string; - descriptionSourceUrl: string; - discontinuedDate: string; - donateUrl: string; - emailAddress: string; - forumUrl: string; - homeUrl: string; - issuesUrl: string; - languages: string[]; - license: IListLicenseDto; - maintainers: IListMaintainerDto[]; - policyUrl: string; - publishedDate: string; - ruleCount: number; - submissionUrl: string; - syntax: IListSyntaxDto[]; - tags: IListTagDto[]; - updatedDate: string; - viewUrl: string; - viewUrlMirror1: string; - viewUrlMirror2: string; -} - -interface IListLicenseDto { - descriptionUrl: string; - name: string; -} - -interface IListMaintainerDto { - id: number; - emailAddress: string; - homeUrl: string; - name: string; - twitterHandle: string; - additionalLists: IMaintainerAdditionalListsDto[]; -} - -interface IMaintainerAdditionalListsDto { - id: number; - name: string; -} - -interface IListSyntaxDto { - definitionUrl: string; - name: string; - supportedSoftware: ISyntaxSupportedSoftwareDto[]; -} - -interface IListTagDto { - name: string; - colorHex: string; - description: string; -} - -interface ISyntaxSupportedSoftwareDto { - homeUrl: string; - name: string; -}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/components/Home.tsx b/src/FilterLists.Web/ClientApp/modules/home/Home.tsx similarity index 94% rename from src/FilterLists.Web/ClientApp/components/Home.tsx rename to src/FilterLists.Web/ClientApp/modules/home/Home.tsx index e3993d149..8384620ee 100644 --- a/src/FilterLists.Web/ClientApp/components/Home.tsx +++ b/src/FilterLists.Web/ClientApp/modules/home/Home.tsx @@ -1,10 +1,13 @@ import * as React from "react"; import { RouteComponentProps } from "react-router"; import "isomorphic-fetch"; +import "../../utils/loader.css"; import ReactTable from "react-table" import "react-table/react-table.css" -import ListDetails from "./ListDetails"; +import "./home.css"; import * as moment from "moment"; +import { getContrast } from "../../utils/GetContrast"; +import { DetailsExpanderContainer } from "./components"; interface IHomeState { lists: IListDto[]; @@ -49,9 +52,7 @@ export class Home extends React.Component, IHomeState> { this.state.loadingRuleCount || this.state.loadingSoftware || this.state.loadingLanguages - ?

    - Loading... -

    + ?
    Loading...
    :
    {Home.renderTagline(this.state)} {Home.renderFilterListsTable(this.state)} @@ -166,7 +167,7 @@ export class Home extends React.Component, IHomeState> { Cell: (cell: any) =>
    {cell.value.map( (e: any) => {e.name})}
    , width: 200, headerClassName: "d-none d-md-block", @@ -247,7 +248,7 @@ export class Home extends React.Component, IHomeState> { ]} SubComponent={(row: any) => { return ( - + ); }} className="-striped -highlight"/>; @@ -255,14 +256,17 @@ export class Home extends React.Component, IHomeState> { private renderColumnVisibilityCheckboxes() { return
    - Visible:  {this.state.columnVisibility.map((c: IColumnVisibility) => this.renderColumnVisibilityCheckbox(c))} + Visible:  {this.state.columnVisibility.map( + (c: IColumnVisibility) => this.renderColumnVisibilityCheckbox(c))}
    ; }; private renderColumnVisibilityCheckbox(props: IColumnVisibility) { return
    - this.checkColumn(props)}/> - + this.checkColumn(props)}/> +
    ; } diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/DetailsExpander.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/DetailsExpander.tsx new file mode 100644 index 000000000..df1e10b83 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/DetailsExpander.tsx @@ -0,0 +1,21 @@ +import * as React from "react"; +import { IFilterListDetailsDto } from "./IFilterListDetailsDto"; +import { InfoCard } from "./infoCard"; +import { LinkButtonGroup } from "./LinkButtonGroup"; +import { MaintainersInfoCard } from "./maintainersInfoCard"; + +export const DetailsExpander = (props: IFilterListDetailsDto) => { + return
    +
    +
    +
    + + +
    +
    + +
    +
    +
    +
    ; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/DetailsExpanderContainer.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/DetailsExpanderContainer.tsx new file mode 100644 index 000000000..22e0c2389 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/DetailsExpanderContainer.tsx @@ -0,0 +1,42 @@ +import * as React from "react"; +import "isomorphic-fetch"; +import "../../../../utils/loader.css"; +import { IFilterListDetailsDto } from "./IFilterListDetailsDto"; +import { DetailsExpander } from "./DetailsExpander"; + +interface IProps { + listId: number; +} + +interface IState { + filterListDetails: IFilterListDetailsDto; +} + +export class DetailsExpanderContainer extends React.Component { + constructor(props: IProps) { + super(props); + this.state = { + filterListDetails: {} as IFilterListDetailsDto + }; + } + + componentDidMount() { + this.fetchFilterListDetails(); + } + + fetchFilterListDetails() { + fetch(`https://filterlists.com/api/v1/lists/${this.props.listId}`) + .then(r => r.json() as Promise) + .then(d => { + this.setState({ + filterListDetails: d + }); + }); + } + + render() { + return this.state.filterListDetails.name + ? + :
    Loading...
    ;; + } +} \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/IFilterListDetailsDto.ts b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/IFilterListDetailsDto.ts new file mode 100644 index 000000000..af5b14685 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/IFilterListDetailsDto.ts @@ -0,0 +1,61 @@ +export interface IFilterListDetailsDto { + chatUrl: string; + description: string; + descriptionSourceUrl: string; + discontinuedDate: string; + donateUrl: string; + emailAddress: string; + forumUrl: string; + homeUrl: string; + issuesUrl: string; + languages: string[]; + license: IListLicenseDto; + maintainers: IListMaintainerDto[]; + name: string; + policyUrl: string; + publishedDate: string; + ruleCount: number; + submissionUrl: string; + syntax: IListSyntaxDto[]; + tags: IListTagDto[]; + updatedDate: string; + viewUrl: string; + viewUrlMirror1: string; + viewUrlMirror2: string; +} + +export interface IListLicenseDto { + descriptionUrl: string; + name: string; +} + +export interface IListMaintainerDto { + id: number; + emailAddress: string; + homeUrl: string; + name: string; + twitterHandle: string; + additionalLists: IMaintainerAdditionalListDto[]; +} + +export interface IListSyntaxDto { + definitionUrl: string; + name: string; + supportedSoftware: ISyntaxSupportedSoftwareDto[]; +} + +export interface IListTagDto { + name: string; + colorHex: string; + description: string; +} + +export interface IMaintainerAdditionalListDto { + id: number; + name: string; +} + +export interface ISyntaxSupportedSoftwareDto { + homeUrl: string; + name: string; +} \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/LinkButtonGroup.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/LinkButtonGroup.tsx new file mode 100644 index 000000000..4a4fb535a --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/LinkButtonGroup.tsx @@ -0,0 +1,44 @@ +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; + viewUrlMirror1: string; + viewUrlMirror2: string; +} + +export const LinkButtonGroup = (props: IProps) => { + const viewUrlMirrors = new Array(props.viewUrlMirror1, props.viewUrlMirror2).filter(u => u); + return
    + + + + + + + + + + +
    ; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/index.ts b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/index.ts new file mode 100644 index 000000000..46cf29787 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/index.ts @@ -0,0 +1,5 @@ +import { DetailsExpanderContainer } from "./DetailsExpanderContainer"; + +export { + DetailsExpanderContainer + }; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/Description.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/Description.tsx new file mode 100644 index 000000000..26bdc4eca --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/Description.tsx @@ -0,0 +1,17 @@ +import * as React from "react"; +import "./description.css"; + +interface IProps { + description: string; + url: string; +} + +export const Description = (props: IProps) => { + return props.description + ?

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

    + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/DiscontinuedDate.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/DiscontinuedDate.tsx new file mode 100644 index 000000000..7f777f0ab --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/DiscontinuedDate.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import * as moment from "moment"; + +interface IProps { + date: string; +} + +export const DiscontinuedDate = (props: IProps) => { + return props.date + ?
  • +

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

    +
  • + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/InfoCard.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/InfoCard.tsx new file mode 100644 index 000000000..6ef2f4ccf --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/InfoCard.tsx @@ -0,0 +1,39 @@ +import * as React from "react"; +import { IListLicenseDto, IListSyntaxDto, IListTagDto } from "../IFilterListDetailsDto"; +import { Description } from "./Description"; +import { DiscontinuedDate } from "./DiscontinuedDate"; +import { Languages } from "./Languages"; +import { License } from "./License"; +import { PublishedDate } from "./PublishedDate"; +import { RuleCount } from "./RuleCount"; +import { Tags } from "./Tags"; +import { UpdatedDate } from "./UpdatedDate"; + +interface IProps { + description: string; + descriptionSourceUrl: string; + discontinuedDate: string; + languages: string[]; + license: IListLicenseDto; + name: string; + publishedDate: string; + ruleCount: number; + syntax: IListSyntaxDto[]; + tags: IListTagDto[]; + updatedDate: string; +} + +export const InfoCard = (props: IProps) => { + return
    + + +
      + + + + + + +
    +
    ; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/Languages.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/Languages.tsx new file mode 100644 index 000000000..801c5cba0 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/Languages.tsx @@ -0,0 +1,20 @@ +import * as React from "react"; + +interface IProps { + languages: string[]; +} + +export const Languages = (props: IProps) => { + return props.languages.length > 0 + ? props.languages.length > 1 + ?
  • +

    Languages:

    +
      + {props.languages.map((language: any) =>
    • {language}
    • )} +
    +
  • + :
  • +

    Language: {props.languages.map((language: any) => language)}

    +
  • + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/License.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/License.tsx new file mode 100644 index 000000000..8c8fa65fb --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/License.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; +import { IListLicenseDto } from "../IFilterListDetailsDto"; + +export const License = (props: IListLicenseDto) => { + return props.name + ? (props.descriptionUrl + ?
  • +

    License: {props.name}

    +
  • + :
  • +

    License: {props.name}

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

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

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

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

    +
  • + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/Tag.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/Tag.tsx new file mode 100644 index 000000000..423e5684c --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/Tag.tsx @@ -0,0 +1,17 @@ +import * as React from "react"; +import { IListTagDto } from "../IFilterListDetailsDto"; +import { getContrast } from "../../../../../utils/GetContrast"; + +export const Tag = (props: IListTagDto) => { + const style = { + backgroundColor: props.colorHex ? `#${props.colorHex}` : undefined, + color: props.colorHex ? getContrast(`${props.colorHex}`) : undefined + }; + return props.name + ? + {props.name} + + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/Tags.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/Tags.tsx new file mode 100644 index 000000000..22a2257b2 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/Tags.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import { IListTagDto } from "../IFilterListDetailsDto"; +import { Tag } from "./Tag"; + +interface IProps { + tags: IListTagDto[]; +} + +export const Tags = (props: IProps) => { + return props.tags.length > 0 + ?
    + {props.tags.map((t: IListTagDto) => )} +
    + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/UpdatedDate.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/UpdatedDate.tsx new file mode 100644 index 000000000..5f897610b --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/infoCard/UpdatedDate.tsx @@ -0,0 +1,19 @@ +import * as React from "react"; +import * as moment from "moment"; + +interface IProps { + discontinuedDate: string; + updatedDate: string; +} + +export const UpdatedDate = (props: IProps) => { + return props.discontinuedDate + ? null + : props.updatedDate + ?
  • +

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

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

    More by {props.name}:

    +
      + {props.additionalLists.map((l: IMaintainerAdditionalListDto) =>
    • {l.name}
    • )} +
    +
    +
    + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/MaintainerInfoCard.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/MaintainerInfoCard.tsx new file mode 100644 index 000000000..7116313a4 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/MaintainerInfoCard.tsx @@ -0,0 +1,20 @@ +import * as React from "react"; +import { IListMaintainerDto } from "../IFilterListDetailsDto"; +import { MaintainerAdditionalLists } from "./MaintainerAdditionalLists"; +import { MaintainerLinkButtonGroup } from "./MaintainerLinkButtonGroup"; + +export const MaintainerInfoCard = (props: IListMaintainerDto) => { + return props.name + ?
    +
    +

    Maintained by {props.name}

    +
    +
    + + +
    +
    +
    +
    + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/MaintainerLinkButtonGroup.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/MaintainerLinkButtonGroup.tsx new file mode 100644 index 000000000..b2b6c4c46 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/MaintainerLinkButtonGroup.tsx @@ -0,0 +1,17 @@ +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) => { + return
    + + + +
    ; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/MaintainersInfoCard.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/MaintainersInfoCard.tsx new file mode 100644 index 000000000..ce953a9be --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/MaintainersInfoCard.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import { IListMaintainerDto } from "../IFilterListDetailsDto"; +import { MaintainerInfoCard } from "./MaintainerInfoCard"; + +interface IProps { + maintainers: IListMaintainerDto[]; +} + +export const MaintainersInfoCard = (props: IProps) => { + return props.maintainers.length > 0 + ?
    + {props.maintainers.map((m: IListMaintainerDto) => )} +
    + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/index.ts b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/index.ts new file mode 100644 index 000000000..03a112e6e --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/detailsExpanderContainer/maintainersInfoCard/index.ts @@ -0,0 +1,5 @@ +import { MaintainersInfoCard } from "./MaintainersInfoCard"; + +export { + MaintainersInfoCard + }; \ 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 new file mode 100644 index 000000000..b2e0f87ca --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/index.ts @@ -0,0 +1,5 @@ +import { DetailsExpanderContainer } from "./detailsExpanderContainer"; + +export { + DetailsExpanderContainer + }; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/ChatButton.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/ChatButton.tsx new file mode 100644 index 000000000..116ce930d --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/ChatButton.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; +} + +export const ChatButton = (props: IProps) => { + return props.url + ? + : null; +};; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/DonateButton.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/DonateButton.tsx new file mode 100644 index 000000000..fb995e452 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/DonateButton.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; +} + +export const DonateButton = (props: IProps) => { + return props.url + ? + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/EmailButton.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/EmailButton.tsx new file mode 100644 index 000000000..9d39af39f --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/EmailButton.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + emailAddress: string; +} + +export const EmailButton = (props: IProps) => { + return props.emailAddress + ? + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/ForumButton.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/ForumButton.tsx new file mode 100644 index 000000000..1d728b5a1 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/ForumButton.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; +} + +export const ForumButton = (props: IProps) => { + return props.url + ? + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/HomeButton.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/HomeButton.tsx new file mode 100644 index 000000000..96d3dfffa --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/HomeButton.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; +} + +export const HomeButton = (props: IProps) => { + return props.url + ? + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/IssuesButton.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/IssuesButton.tsx new file mode 100644 index 000000000..ffa996b90 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/IssuesButton.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; +} + +export const IssuesButton = (props: IProps) => { + return props.url + ? + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/LinkButton.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/LinkButton.tsx new file mode 100644 index 000000000..0212e8d74 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/LinkButton.tsx @@ -0,0 +1,19 @@ +import * as React from "react"; +import "./button.css"; + +interface IProps { + href: string; + title?: string; + buttonClass?: string; + text: string; +} + +export const LinkButton = (props: IProps) => { + return props.href + ? + {props.text} + + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/PolicyButton.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/PolicyButton.tsx new file mode 100644 index 000000000..5ad5118ab --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/PolicyButton.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; +} + +export const PolicyButton = (props: IProps) => { + return props.url + ? + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/SubmitButton.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/SubmitButton.tsx new file mode 100644 index 000000000..a5506bf9b --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/SubmitButton.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; +} + +export const SubmitButton = (props: IProps) => { + return props.url + ? + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/SubscribeButton.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/SubscribeButton.tsx new file mode 100644 index 000000000..8358e0c86 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/SubscribeButton.tsx @@ -0,0 +1,34 @@ +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("https://") === -1) { + buttonClass = "btn-danger"; + titlePrefix = "Not Secure - "; + } else { + buttonClass = undefined; + titlePrefix = ""; + } + + const hrefTitle = `&title=${encodeURIComponent(props.name)}`; + const href = `abp:subscribe?location=${encodeURIComponent(props.url)}${hrefTitle}`; + const title = + `${titlePrefix}Subscribe to ${props.name + } with a browser extension supporting the \"abp:\" protocol (e.g. uBlock Origin, Adblock Plus).`; + + return props.url + ? + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/SubscribeButtonGroup.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/SubscribeButtonGroup.tsx new file mode 100644 index 000000000..0b0d1d9cc --- /dev/null +++ b/src/FilterLists.Web/ClientApp/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) => { + return 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, i) => )} +
    +
    ; +}; + +const BtnGroupDropSubscribe = () => { + return ; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/TwitterButton.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/TwitterButton.tsx new file mode 100644 index 000000000..718f4e273 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/TwitterButton.tsx @@ -0,0 +1,15 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + twitterHandle: string; +} + +export const TwitterButton = (props: IProps) => { + return props.twitterHandle + ? + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/ViewButton.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/ViewButton.tsx new file mode 100644 index 000000000..26dd9b8b5 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/ViewButton.tsx @@ -0,0 +1,16 @@ +import * as React from "react"; +import { LinkButton } from "./LinkButton"; + +interface IProps { + name: string; + url: string; + text?: string; +} + +export const ViewButton = (props: IProps) => { + return props.url + ? + : null; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/ViewButtonGroup.tsx b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/ViewButtonGroup.tsx new file mode 100644 index 000000000..b497d88aa --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/ViewButtonGroup.tsx @@ -0,0 +1,52 @@ +import * as React from "react"; +import { ViewButton } from "./ViewButton"; + +interface IProps { + name: string; + url: string; + urlMirrors?: string[]; +} + +export const ViewButtonGroup = (props: IProps) => { + return 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, i) => )} +
    +
    ; +}; + +const BtnGroupDropView = () => { + return ; +}; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/button.css b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/button.css new file mode 100644 index 000000000..b52817631 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/button.css @@ -0,0 +1,5 @@ +.fl-btn-link { + margin-bottom: .2rem; + max-width: 90px; + width: 90px; +} \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/index.ts b/src/FilterLists.Web/ClientApp/modules/home/components/linkButtons/index.ts new file mode 100644 index 000000000..c0525f2df --- /dev/null +++ b/src/FilterLists.Web/ClientApp/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/FilterLists.Web/ClientApp/css/site.css b/src/FilterLists.Web/ClientApp/modules/home/home.css similarity index 62% rename from src/FilterLists.Web/ClientApp/css/site.css rename to src/FilterLists.Web/ClientApp/modules/home/home.css index 7c74e7364..a905741ea 100644 --- a/src/FilterLists.Web/ClientApp/css/site.css +++ b/src/FilterLists.Web/ClientApp/modules/home/home.css @@ -1,5 +1,3 @@ -body, h2, h3, h4, h5, blockquote { font-size: 16px; } - .rt-tr { -ms-align-items: center; -o-align-items: center; @@ -7,16 +5,9 @@ body, h2, h3, h4, h5, blockquote { font-size: 16px; } align-items: center; } +div.rt-tbody, div.rt-thead.-filters, div.rt-thead.-header { min-width: 320px !important; } + .fl-tag-container { line-height: 1; white-space: normal; -} - -div.rt-tbody, div.rt-thead.-filters, div.rt-thead.-header { min-width: 320px !important; } - -.fl-btn-details-action { - margin-bottom: .2rem; - max-width: 90px; -} - -.fl-description { margin: 0 0 .5rem; } \ No newline at end of file +} \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/home/index.ts b/src/FilterLists.Web/ClientApp/modules/home/index.ts new file mode 100644 index 000000000..d22a41187 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/home/index.ts @@ -0,0 +1,5 @@ +import { Home } from "./Home"; + +export { + Home + }; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/modules/index.ts b/src/FilterLists.Web/ClientApp/modules/index.ts new file mode 100644 index 000000000..d4c28103a --- /dev/null +++ b/src/FilterLists.Web/ClientApp/modules/index.ts @@ -0,0 +1,5 @@ +import { Home } from "./home"; + +export { + Home + }; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/routes.tsx b/src/FilterLists.Web/ClientApp/routes.tsx index 5a14cda26..4e94675c1 100644 --- a/src/FilterLists.Web/ClientApp/routes.tsx +++ b/src/FilterLists.Web/ClientApp/routes.tsx @@ -1,8 +1,8 @@ import * as React from "react"; +import { Layout } from "./Layout"; import { Route } from "react-router-dom"; -import { Layout } from "./components/Layout"; -import { Home } from "./components/Home"; +import { Home } from "./modules"; -export const routes = +export const Routes = ; \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/site.css b/src/FilterLists.Web/ClientApp/site.css new file mode 100644 index 000000000..c25b79089 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/site.css @@ -0,0 +1 @@ +body, h2, h3, h4, h5, blockquote { font-size: 16px; } \ No newline at end of file diff --git a/src/FilterLists.Web/ClientApp/utils/GetContrast.ts b/src/FilterLists.Web/ClientApp/utils/GetContrast.ts new file mode 100644 index 000000000..74e21893b --- /dev/null +++ b/src/FilterLists.Web/ClientApp/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/FilterLists.Web/ClientApp/utils/loader.css b/src/FilterLists.Web/ClientApp/utils/loader.css new file mode 100644 index 000000000..2e33ccc08 --- /dev/null +++ b/src/FilterLists.Web/ClientApp/utils/loader.css @@ -0,0 +1,67 @@ +/*https://projects.lukehaas.me/css-loaders/#load1*/ + +.loader, +.loader:before, +.loader:after { + -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 diff --git a/src/FilterLists.Web/webpack.config.js b/src/FilterLists.Web/webpack.config.js index c6cbd309c..df10c2fc9 100644 --- a/src/FilterLists.Web/webpack.config.js +++ b/src/FilterLists.Web/webpack.config.js @@ -9,7 +9,7 @@ module.exports = (env) => { return [ { stats: { modules: false }, - entry: { 'main': "./ClientApp/boot.tsx" }, + entry: { 'main': "./ClientApp/Boot.tsx" }, resolve: { extensions: [".js", ".jsx", ".ts", ".tsx"] }, output: { path: path.join(__dirname, bundleOutputDir),