Add support for imprints
This commit is contained in:
parent
5656d91845
commit
a8f2136736
8 changed files with 358 additions and 60 deletions
22
data/imprint.json
Normal file
22
data/imprint.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": {
|
||||||
|
"text": "John Doe",
|
||||||
|
"link": "#"
|
||||||
|
},
|
||||||
|
"address": {
|
||||||
|
"text": "Null Ave. 1234, 90008 Los Angeles",
|
||||||
|
"link": "#"
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"text": "+1-202-555-0167",
|
||||||
|
"link": "#"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"text": "doe.john@example.com",
|
||||||
|
"link": "#"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"text": "example.com",
|
||||||
|
"link": "#"
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,10 +5,12 @@ import SearchBar from "./components/searchBar";
|
||||||
import Greeter from "./components/greeter";
|
import Greeter from "./components/greeter";
|
||||||
import AppList from "./components/appList";
|
import AppList from "./components/appList";
|
||||||
import BookmarkList from "./components/bookmarkList";
|
import BookmarkList from "./components/bookmarkList";
|
||||||
import SettingsModal from "./components/settingsModal";
|
import Settings from "./components/settings";
|
||||||
|
|
||||||
import selectedTheme from "./components/themeManager";
|
import selectedTheme from "./components/themeManager";
|
||||||
|
|
||||||
|
import Imprint from "./components/imprint";
|
||||||
|
|
||||||
const GlobalStyle = createGlobalStyle`
|
const GlobalStyle = createGlobalStyle`
|
||||||
body {
|
body {
|
||||||
background-color: ${selectedTheme.backgroundColor};
|
background-color: ${selectedTheme.backgroundColor};
|
||||||
|
@ -32,10 +34,11 @@ const App = () => (
|
||||||
<GlobalStyle />
|
<GlobalStyle />
|
||||||
<AppContainer>
|
<AppContainer>
|
||||||
<SearchBar />
|
<SearchBar />
|
||||||
<SettingsModal />
|
<Settings />
|
||||||
<Greeter />
|
<Greeter />
|
||||||
<AppList />
|
<AppList />
|
||||||
<BookmarkList />
|
<BookmarkList />
|
||||||
|
<Imprint />
|
||||||
</AppContainer>
|
</AppContainer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -48,7 +48,7 @@ const AppList = () => {
|
||||||
))}
|
))}
|
||||||
{apps && (
|
{apps && (
|
||||||
<AppCategory
|
<AppCategory
|
||||||
name={categories > 0 ? "Uncategorized apps" : ""}
|
name={categories ? "Uncategorized apps" : ""}
|
||||||
items={apps}
|
items={apps}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
22
src/components/data/imprint.json
Normal file
22
src/components/data/imprint.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": {
|
||||||
|
"text": "John Doe",
|
||||||
|
"link": "#"
|
||||||
|
},
|
||||||
|
"address": {
|
||||||
|
"text": "Null Ave. 1234, 90008 Los Angeles",
|
||||||
|
"link": "#"
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"text": "+1-202-555-0167",
|
||||||
|
"link": "#"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"text": "doe.john@example.com",
|
||||||
|
"link": "#"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"text": "example.com",
|
||||||
|
"link": "#"
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,7 +27,7 @@ interface IIconProps {
|
||||||
export const ComponentIcon = ({ name, size }: IIconProps) => {
|
export const ComponentIcon = ({ name, size }: IIconProps) => {
|
||||||
let IconContainer = styled(RawIcon)`
|
let IconContainer = styled(RawIcon)`
|
||||||
font-size: ${size ? size : "24px"};
|
font-size: ${size ? size : "24px"};
|
||||||
text-color: ${selectedTheme.mainColor};
|
color: ${selectedTheme.mainColor};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return <IconContainer>{name}</IconContainer>;
|
return <IconContainer>{name}</IconContainer>;
|
||||||
|
|
206
src/components/imprint.tsx
Normal file
206
src/components/imprint.tsx
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
|
import Modal from "./modal";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import selectedTheme from "./themeManager";
|
||||||
|
import {
|
||||||
|
handleResponse,
|
||||||
|
ErrorMessage,
|
||||||
|
ListContainer,
|
||||||
|
ItemList,
|
||||||
|
Headline as Hl,
|
||||||
|
SubHeadline as SHl,
|
||||||
|
} from "./elements";
|
||||||
|
|
||||||
|
const Headline = styled(Hl)`
|
||||||
|
display: block;
|
||||||
|
padding: 1rem 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SubHeadline = styled(SHl)`
|
||||||
|
display: block;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ModalSubHeadline = styled(SubHeadline)`
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Text = styled.p`
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
color: ${selectedTheme.mainColor};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Link = styled.a`
|
||||||
|
display: block;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
color: ${selectedTheme.mainColor};
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ItemContainer = styled.div`
|
||||||
|
padding: 1rem 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const useImprintData = () => {
|
||||||
|
const [imprintData, setImprintData] = useState({
|
||||||
|
name: { text: "", link: "" },
|
||||||
|
address: { text: "", link: "" },
|
||||||
|
phone: { text: "", link: "" },
|
||||||
|
email: { text: "", link: "" },
|
||||||
|
url: { text: "", link: "" },
|
||||||
|
error: false,
|
||||||
|
});
|
||||||
|
const fetchImprintData = useCallback(() => {
|
||||||
|
(process.env.NODE_ENV === "production"
|
||||||
|
? fetch("/data/imprint.json").then(handleResponse)
|
||||||
|
: import("./data/imprint.json")
|
||||||
|
)
|
||||||
|
.then((jsonResponse: any) => {
|
||||||
|
setImprintData({ ...jsonResponse, error: false });
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
setImprintData({
|
||||||
|
name: { text: "", link: "" },
|
||||||
|
address: { text: "", link: "" },
|
||||||
|
phone: { text: "", link: "" },
|
||||||
|
email: { text: "", link: "" },
|
||||||
|
url: { text: "", link: "" },
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchImprintData();
|
||||||
|
}, [fetchImprintData]);
|
||||||
|
return { imprintData, fetchImprintData };
|
||||||
|
};
|
||||||
|
|
||||||
|
const Imprint = () => {
|
||||||
|
const {
|
||||||
|
imprintData: { name, address, phone, email, url, error },
|
||||||
|
} = useImprintData();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListContainer>
|
||||||
|
<Hl>About</Hl>
|
||||||
|
<ItemList>
|
||||||
|
<ItemContainer>
|
||||||
|
<SHl>Imprint</SHl>
|
||||||
|
<Modal element="text" text="View Imprint">
|
||||||
|
<Headline>Legal Disclosure</Headline>
|
||||||
|
{error && <ErrorMessage>{error}</ErrorMessage>}
|
||||||
|
<ModalSubHeadline>
|
||||||
|
Information in accordance with section 5 TMG
|
||||||
|
</ModalSubHeadline>
|
||||||
|
{!error && (
|
||||||
|
<>
|
||||||
|
<Link href={name.link}>{name.text}</Link>
|
||||||
|
<Link href={address.link}>{address.text}</Link>
|
||||||
|
<Link href={phone.link}>{phone.text}</Link>
|
||||||
|
<Link href={email.link}>{email.text}</Link>
|
||||||
|
<Link href={url.link}>{url.text}</Link>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<Headline>Disclaimer</Headline>
|
||||||
|
<ModalSubHeadline>Accountability for content</ModalSubHeadline>
|
||||||
|
<Text>
|
||||||
|
The contents of our pages have been created with the utmost care.
|
||||||
|
However, we cannot guarantee the contents' accuracy, completeness
|
||||||
|
or topicality. According to statutory provisions, we are
|
||||||
|
furthermore responsible for our own content on these web pages. In
|
||||||
|
this matter, please note that we are not obliged to monitor the
|
||||||
|
transmitted or saved information of third parties, or investigate
|
||||||
|
circumstances pointing to illegal activity. Our obligations to
|
||||||
|
remove or block the use of information under generally applicable
|
||||||
|
laws remain unaffected by this as per §§ 8 to 10 of the Telemedia
|
||||||
|
Act (TMG).
|
||||||
|
</Text>
|
||||||
|
<ModalSubHeadline>Accountability for links</ModalSubHeadline>
|
||||||
|
<Text>
|
||||||
|
Responsibility for the content of external links (to web pages of
|
||||||
|
third parties) lies solely with the operators of the linked pages.
|
||||||
|
No violations were evident to us at the time of linking. Should
|
||||||
|
any legal infringement become known to us, we will remove the
|
||||||
|
respective link immediately.
|
||||||
|
</Text>
|
||||||
|
<ModalSubHeadline>Copyright</ModalSubHeadline>
|
||||||
|
<Text>
|
||||||
|
Our web pages and their contents are subject to German copyright
|
||||||
|
law. Unless expressly permitted by law, every form of utilizing,
|
||||||
|
reproducing or processing works subject to copyright protection on
|
||||||
|
our web pages requires the prior consent of the respective owner
|
||||||
|
of the rights. Individual reproductions of a work are only allowed
|
||||||
|
for private use. The materials from these pages are copyrighted
|
||||||
|
and any unauthorized use may violate copyright laws.
|
||||||
|
</Text>
|
||||||
|
</Modal>
|
||||||
|
</ItemContainer>
|
||||||
|
</ItemList>
|
||||||
|
</ListContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
<ImprintContainer>
|
||||||
|
<Hl>About</Hl>
|
||||||
|
<ModalContainer>
|
||||||
|
<SHl>Imprint</SHl>
|
||||||
|
<Modal element="text" text="View Imprint">
|
||||||
|
<Headline>Legal Disclosure</Headline>
|
||||||
|
{error && <ErrorMessage>{error}</ErrorMessage>}
|
||||||
|
<ModalSubHeadline>
|
||||||
|
Information in accordance with section 5 TMG
|
||||||
|
</ModalSubHeadline>
|
||||||
|
{!error && (
|
||||||
|
<>
|
||||||
|
<Link href={name.link}>{name.text}</Link>
|
||||||
|
<Link href={address.link}>{address.text}</Link>
|
||||||
|
<Link href={phone.link}>{phone.text}</Link>
|
||||||
|
<Link href={email.link}>{email.text}</Link>
|
||||||
|
<Link href={url.link}>{url.text}</Link>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<Headline>Disclaimer</Headline>
|
||||||
|
<ModalSubHeadline>Accountability for content</ModalSubHeadline>
|
||||||
|
<Text>
|
||||||
|
The contents of our pages have been created with the utmost care.
|
||||||
|
However, we cannot guarantee the contents' accuracy, completeness or
|
||||||
|
topicality. According to statutory provisions, we are furthermore
|
||||||
|
responsible for our own content on these web pages. In this matter,
|
||||||
|
please note that we are not obliged to monitor the transmitted or
|
||||||
|
saved information of third parties, or investigate circumstances
|
||||||
|
pointing to illegal activity. Our obligations to remove or block the
|
||||||
|
use of information under generally applicable laws remain unaffected
|
||||||
|
by this as per §§ 8 to 10 of the Telemedia Act (TMG).
|
||||||
|
</Text>
|
||||||
|
<ModalSubHeadline>Accountability for links</ModalSubHeadline>
|
||||||
|
<Text>
|
||||||
|
Responsibility for the content of external links (to web pages of
|
||||||
|
third parties) lies solely with the operators of the linked pages.
|
||||||
|
No violations were evident to us at the time of linking. Should any
|
||||||
|
legal infringement become known to us, we will remove the respective
|
||||||
|
link immediately.
|
||||||
|
</Text>
|
||||||
|
<ModalSubHeadline>Copyright</ModalSubHeadline>
|
||||||
|
<Text>
|
||||||
|
Our web pages and their contents are subject to German copyright
|
||||||
|
law. Unless expressly permitted by law, every form of utilizing,
|
||||||
|
reproducing or processing works subject to copyright protection on
|
||||||
|
our web pages requires the prior consent of the respective owner of
|
||||||
|
the rights. Individual reproductions of a work are only allowed for
|
||||||
|
private use. The materials from these pages are copyrighted and any
|
||||||
|
unauthorized use may violate copyright laws.
|
||||||
|
</Text>
|
||||||
|
</Modal>
|
||||||
|
</ModalContainer>
|
||||||
|
</ImprintContainer>
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default Imprint;
|
64
src/components/modal.tsx
Normal file
64
src/components/modal.tsx
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import selectedTheme from "./themeManager";
|
||||||
|
|
||||||
|
import { IconButton } from "./elements";
|
||||||
|
|
||||||
|
const ModalContainer = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
padding: 1rem;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 10;
|
||||||
|
border: 1px solid ${selectedTheme.mainColor};
|
||||||
|
background-color: ${selectedTheme.backgroundColor};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Text = styled.p`
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
font-weight: 400;
|
||||||
|
text-decoration: none;
|
||||||
|
color: ${selectedTheme.accentColor};
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface IModalInterface {
|
||||||
|
element: string;
|
||||||
|
icon?: string;
|
||||||
|
text?: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Modal = (props: IModalInterface) => {
|
||||||
|
const [modalHidden, setModalHidden] = useState(true);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{props.element === "icon" && (
|
||||||
|
<IconButton
|
||||||
|
icon={props.icon ? props.icon : ""}
|
||||||
|
onClick={() => setModalHidden(!modalHidden)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{props.element === "text" && (
|
||||||
|
<Text onClick={() => setModalHidden(!modalHidden)}>{props.text}</Text>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<ModalContainer hidden={modalHidden}>
|
||||||
|
<IconButton icon="close" onClick={() => setModalHidden(!modalHidden)} />
|
||||||
|
{props.children}
|
||||||
|
</ModalContainer>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Modal;
|
|
@ -9,26 +9,16 @@ import selectedTheme, { setTheme } from "./themeManager";
|
||||||
import {
|
import {
|
||||||
handleResponse,
|
handleResponse,
|
||||||
Button,
|
Button,
|
||||||
IconButton,
|
|
||||||
ErrorMessage,
|
ErrorMessage,
|
||||||
Headline as hl,
|
Headline as hl,
|
||||||
} from "./elements";
|
} from "./elements";
|
||||||
|
|
||||||
|
import Modal from "./modal";
|
||||||
|
|
||||||
const Headline = styled(hl)`
|
const Headline = styled(hl)`
|
||||||
padding: 0.5rem 0;
|
padding: 0.5rem 0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Modal = styled.div`
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
padding: 1rem;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
z-index: 10;
|
|
||||||
border: 1px solid ${selectedTheme.mainColor};
|
|
||||||
background-color: ${selectedTheme.backgroundColor};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const SelectContainer = styled.div`
|
const SelectContainer = styled.div`
|
||||||
padding-bottom: 1rem;
|
padding-bottom: 1rem;
|
||||||
`;
|
`;
|
||||||
|
@ -123,9 +113,7 @@ const useThemeData = () => {
|
||||||
return { themeData, fetchThemeData };
|
return { themeData, fetchThemeData };
|
||||||
};
|
};
|
||||||
|
|
||||||
const SettingsModal = () => {
|
const Settings = () => {
|
||||||
const [modalHidden, setModalHidden] = useState(true);
|
|
||||||
|
|
||||||
const [newTheme, setNewTheme] = useState();
|
const [newTheme, setNewTheme] = useState();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -137,49 +125,42 @@ const SettingsModal = () => {
|
||||||
}, [newTheme]);
|
}, [newTheme]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Modal element="icon" icon="settings">
|
||||||
<IconButton
|
{error && <ErrorMessage>{error}</ErrorMessage>}
|
||||||
icon="settings"
|
<SelectContainer>
|
||||||
onClick={() => setModalHidden(!modalHidden)}
|
<Headline>Theme:</Headline>
|
||||||
/>
|
<FormContainer>
|
||||||
<Modal hidden={modalHidden}>
|
<Select
|
||||||
<IconButton icon="close" onClick={() => setModalHidden(!modalHidden)} />
|
options={themes}
|
||||||
{error && <ErrorMessage>{error}</ErrorMessage>}
|
defaultValue={selectedTheme}
|
||||||
<SelectContainer>
|
onChange={(e: any) => {
|
||||||
<Headline>Theme:</Headline>
|
setNewTheme(e);
|
||||||
<FormContainer>
|
}}
|
||||||
<Select
|
styles={SelectorStyle}
|
||||||
options={themes}
|
/>
|
||||||
defaultValue={selectedTheme}
|
|
||||||
onChange={(e: any) => {
|
|
||||||
setNewTheme(e);
|
|
||||||
}}
|
|
||||||
styles={SelectorStyle}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button onClick={() => setTheme(JSON.stringify(newTheme))}>
|
<Button onClick={() => setTheme(JSON.stringify(newTheme))}>
|
||||||
Apply
|
Apply
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => window.location.reload()}>Refresh</Button>
|
<Button onClick={() => window.location.reload()}>Refresh</Button>
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
</SelectContainer>
|
</SelectContainer>
|
||||||
<Table>
|
<Table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<HeadCell>Search Provider</HeadCell>
|
<HeadCell>Search Provider</HeadCell>
|
||||||
<HeadCell>Prefix</HeadCell>
|
<HeadCell>Prefix</HeadCell>
|
||||||
|
</TableRow>
|
||||||
|
{searchData.providers.map((provider, index) => (
|
||||||
|
<TableRow key={provider.name + index}>
|
||||||
|
<TableCell>{provider.name}</TableCell>
|
||||||
|
<TableCell>{provider.prefix}</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
{searchData.providers.map((provider, index) => (
|
))}
|
||||||
<TableRow key={provider.name + index}>
|
</tbody>
|
||||||
<TableCell>{provider.name}</TableCell>
|
</Table>
|
||||||
<TableCell>{provider.prefix}</TableCell>
|
</Modal>
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</Table>
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SettingsModal;
|
export default Settings;
|
Loading…
Reference in a new issue