)
- .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),