First commit
This commit is contained in:
9
src/App.test.js
Executable file
9
src/App.test.js
Executable file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
const { getByText } = render(<App />);
|
||||
const linkElement = getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
||||
116
src/App.tsx
Executable file
116
src/App.tsx
Executable file
@@ -0,0 +1,116 @@
|
||||
import React, {ReactNode} from "react";
|
||||
import {BrowserRouter as Router, Link, Route} from "react-router-dom";
|
||||
import {Redirect, Switch} from "react-router";
|
||||
import {IntlStrings} from "./static/strings";
|
||||
import {PageId, SupportedLang} from "./enums";
|
||||
import {pageComponentFromId} from "./pageListings";
|
||||
import KadiPage from "./Components/KadiPage";
|
||||
import HomePage from "./Components/HomePage";
|
||||
import {SERVER_BASE_NAME} from "./index";
|
||||
import axios from "axios";
|
||||
import UserContext, {IUserContext} from "./Contexts/UserContext";
|
||||
import LocaleContext, {ILocaleContext} from "./Contexts/LocaleContext";
|
||||
|
||||
interface AppState {
|
||||
userContext: IUserContext;
|
||||
localeContext: ILocaleContext;
|
||||
}
|
||||
|
||||
interface AppProps {}
|
||||
|
||||
class App extends React.Component<AppProps, AppState> {
|
||||
private readonly updateUserContext: (username: string, loggedIn: boolean) => void;
|
||||
private readonly changeLang: (lang: SupportedLang) => void;
|
||||
constructor(props: AppProps) {
|
||||
super(props);
|
||||
|
||||
this.updateUserContext = (username, loggedIn) => {
|
||||
this.setState({userContext: {
|
||||
username: username,
|
||||
loggedIn: loggedIn,
|
||||
updateUserContext: this.updateUserContext
|
||||
}});
|
||||
};
|
||||
|
||||
this.changeLang = (lang: SupportedLang) => {
|
||||
this.setState({localeContext: {
|
||||
strings: IntlStrings[lang],
|
||||
currentLang: lang,
|
||||
changeLang: this.changeLang
|
||||
}});
|
||||
};
|
||||
|
||||
this.state = {
|
||||
userContext: {
|
||||
username: "",
|
||||
loggedIn: false,
|
||||
updateUserContext: this.updateUserContext,
|
||||
},
|
||||
localeContext: {
|
||||
currentLang: SupportedLang.gb,
|
||||
strings: IntlStrings[SupportedLang.gb],
|
||||
changeLang: this.changeLang,
|
||||
}
|
||||
};
|
||||
|
||||
axios.get("/api/user", {baseURL: SERVER_BASE_NAME})
|
||||
.then((res) => {
|
||||
const data = res.data as any;
|
||||
if (data.loggedIn) {
|
||||
this.updateUserContext(data.username, true);
|
||||
}
|
||||
else {
|
||||
this.updateUserContext("", false);
|
||||
}
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
}
|
||||
|
||||
render(): ReactNode {
|
||||
|
||||
return (
|
||||
<UserContext.Provider value={this.state.userContext}>
|
||||
<LocaleContext.Provider value={this.state.localeContext}>
|
||||
<Router basename={SERVER_BASE_NAME}>
|
||||
<Route exact={true} path={"/"}>
|
||||
<KadiPage activePage={PageId.home}>
|
||||
<HomePage/>
|
||||
</KadiPage>
|
||||
</Route>
|
||||
<KadiPageRoute pageId={PageId.history}/>
|
||||
<KadiPageRoute pageId={PageId.friends}/>
|
||||
<KadiPageRoute pageId={PageId.stats}/>
|
||||
<KadiPageRoute pageId={PageId.profile}/>
|
||||
<KadiPageRoute pageId={PageId.rulesets}/>
|
||||
<Route path={"/"}>
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: "/",
|
||||
}}
|
||||
/>
|
||||
</Route>
|
||||
</Router>
|
||||
</LocaleContext.Provider>
|
||||
</UserContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface KadiPageRouteProps {
|
||||
pageId: PageId
|
||||
}
|
||||
|
||||
const KadiPageRoute: React.FunctionComponent<KadiPageRouteProps> = (props: KadiPageRouteProps) => {
|
||||
const {pageId} = props;
|
||||
const PageComponent = pageComponentFromId[pageId];
|
||||
return (
|
||||
<Route path={"/" + pageId}>
|
||||
<KadiPage activePage={pageId}>
|
||||
<PageComponent/>
|
||||
</KadiPage>
|
||||
</Route>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export default App;
|
||||
27
src/Components/FriendsPage.tsx
Executable file
27
src/Components/FriendsPage.tsx
Executable file
@@ -0,0 +1,27 @@
|
||||
import React, {ReactElement} from "react";
|
||||
import KadiPage from "../Components/KadiPage";
|
||||
import {Header} from "semantic-ui-react";
|
||||
|
||||
interface FriendsPageProps {}
|
||||
|
||||
interface FriendsPageState {
|
||||
}
|
||||
|
||||
class FriendsPage extends React.Component<FriendsPageProps, FriendsPageState> {
|
||||
constructor(props: FriendsPageProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
};
|
||||
}
|
||||
|
||||
render(): ReactElement {
|
||||
return (
|
||||
<Header>
|
||||
My Friends
|
||||
</Header>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default FriendsPage;
|
||||
53
src/Components/HistoryPage.tsx
Executable file
53
src/Components/HistoryPage.tsx
Executable file
@@ -0,0 +1,53 @@
|
||||
import React, {ReactElement} from "react";
|
||||
import {Header, List, ListItem} from "semantic-ui-react";
|
||||
import axios from "axios";
|
||||
import {SERVER_BASE_NAME} from "../index";
|
||||
|
||||
interface HistoryPageProps {
|
||||
}
|
||||
|
||||
interface HistoryPageState {
|
||||
loadingGames: boolean;
|
||||
gameListings: any[];
|
||||
}
|
||||
|
||||
class HistoryPage extends React.Component<HistoryPageProps, HistoryPageState> {
|
||||
constructor(props: HistoryPageProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
loadingGames: true,
|
||||
gameListings: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
axios.get(SERVER_BASE_NAME + "/api/games/")
|
||||
.then(response => this.setState({gameListings: response.data.games}))
|
||||
.catch(error => this.handleError(error))
|
||||
.finally(() => this.setState({ loadingGames: false }));
|
||||
}
|
||||
|
||||
handleError = (error: any) => void {
|
||||
|
||||
};
|
||||
|
||||
render(): ReactElement {
|
||||
return (
|
||||
<>
|
||||
<Header size={"huge"}>
|
||||
History
|
||||
</Header>
|
||||
<List bulleted={true}>
|
||||
{
|
||||
this.state.gameListings.map(listing => {
|
||||
return <ListItem key={listing.createdAt}>Game played on: {listing.createdAt}</ListItem>;
|
||||
})
|
||||
}
|
||||
</List>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default HistoryPage;
|
||||
27
src/Components/HomePage.tsx
Executable file
27
src/Components/HomePage.tsx
Executable file
@@ -0,0 +1,27 @@
|
||||
import React, {ReactElement} from "react";
|
||||
import KadiPage from "../Components/KadiPage";
|
||||
import {Header} from "semantic-ui-react";
|
||||
|
||||
interface HomePageProps {}
|
||||
|
||||
interface HomePageState {
|
||||
}
|
||||
|
||||
class HomePage extends React.Component<HomePageProps, HomePageState> {
|
||||
constructor(props: HomePageProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
};
|
||||
}
|
||||
|
||||
render(): ReactElement {
|
||||
return (
|
||||
<Header>
|
||||
Home
|
||||
</Header>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default HomePage;
|
||||
36
src/Components/KadiPage.tsx
Executable file
36
src/Components/KadiPage.tsx
Executable file
@@ -0,0 +1,36 @@
|
||||
import React, {ReactElement} from "react";
|
||||
import {Segment} from "semantic-ui-react";
|
||||
import "../static/css/site.css";
|
||||
import KadiSidebarNav from "../Components/KadiSidebarNav";
|
||||
import MainPageContent from "../Components/MainPageContent";
|
||||
import {PageId} from "../enums";
|
||||
|
||||
interface KadiPageProps {
|
||||
activePage: PageId;
|
||||
}
|
||||
|
||||
interface KadiPageState {
|
||||
}
|
||||
|
||||
class KadiPage extends React.Component<KadiPageProps, KadiPageState> {
|
||||
constructor(props: KadiPageProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
};
|
||||
}
|
||||
|
||||
render(): ReactElement {
|
||||
const {children, activePage} = this.props;
|
||||
return (
|
||||
<>
|
||||
<KadiSidebarNav activeItem={activePage}/>
|
||||
<MainPageContent>
|
||||
{children}
|
||||
</MainPageContent>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default KadiPage;
|
||||
97
src/Components/KadiSidebarNav.tsx
Executable file
97
src/Components/KadiSidebarNav.tsx
Executable file
@@ -0,0 +1,97 @@
|
||||
import {Header, HeaderContent, Icon, Image, Menu, Segment, Sidebar, SidebarPusher} from "semantic-ui-react";
|
||||
import logo from "../static/images/kadi.png";
|
||||
import React from "react";
|
||||
import LocaleContext from "../Contexts/LocaleContext";
|
||||
import {Link} from "react-router-dom";
|
||||
import {PageId} from "../enums";
|
||||
import {SERVER_BASE_NAME} from "../index";
|
||||
|
||||
interface KadiSidebarNavProps {
|
||||
activeItem: PageId;
|
||||
}
|
||||
|
||||
const KadiSidebarNav: React.FunctionComponent<KadiSidebarNavProps> = (props) => {
|
||||
const Locale = React.useContext(LocaleContext).strings;
|
||||
const {activeItem} = props;
|
||||
return (
|
||||
<Menu
|
||||
borderless={true}
|
||||
vertical={true}
|
||||
stackable={true}
|
||||
fixed={"left"}
|
||||
inverted={true}
|
||||
className={"kadiSidebarNav"}
|
||||
>
|
||||
<Link to={"/"}>
|
||||
<Menu.Item>
|
||||
<Header inverted={true} size={"huge"}>
|
||||
<Image src={logo} size={"tiny"} spaced={true} />
|
||||
<HeaderContent>
|
||||
<span className={"brandname"}>K A D I</span>
|
||||
</HeaderContent>
|
||||
</Header>
|
||||
</Menu.Item>
|
||||
</Link>
|
||||
<Menu.Item
|
||||
as={"a"}
|
||||
icon={true}
|
||||
href={SERVER_BASE_NAME + "/game"}
|
||||
>
|
||||
<Icon name={"game"} />
|
||||
{Locale.menu.playTab}
|
||||
</Menu.Item>
|
||||
<Link to={"/" + PageId.profile}>
|
||||
<Menu.Item
|
||||
as={"a"}
|
||||
icon={true}
|
||||
active={activeItem === PageId.profile}
|
||||
>
|
||||
<Icon name={"user circle"} />
|
||||
{Locale.menu.profileTab}
|
||||
</Menu.Item>
|
||||
</Link>
|
||||
<Link to={"/" + PageId.stats}>
|
||||
<Menu.Item
|
||||
as={"a"}
|
||||
icon={true}
|
||||
active={activeItem === PageId.stats}
|
||||
>
|
||||
<Icon name={"chart pie"} />
|
||||
{Locale.menu.statsTab}
|
||||
</Menu.Item>
|
||||
</Link>
|
||||
<Link to={"/" + PageId.rulesets}>
|
||||
<Menu.Item
|
||||
as={"a"}
|
||||
icon={true}
|
||||
active={activeItem === PageId.rulesets}
|
||||
>
|
||||
<Icon name={"book"} />
|
||||
{Locale.menu.rulesetsTab}
|
||||
</Menu.Item>
|
||||
</Link>
|
||||
<Link to={"/" + PageId.friends}>
|
||||
<Menu.Item
|
||||
as={"a"}
|
||||
icon={true}
|
||||
active={activeItem === PageId.friends}
|
||||
>
|
||||
<Icon name={"group"} />
|
||||
{Locale.menu.friendsTab}
|
||||
</Menu.Item>
|
||||
</Link>
|
||||
<Link to={"/" + PageId.history}>
|
||||
<Menu.Item
|
||||
as={"a"}
|
||||
icon={true}
|
||||
active={activeItem === PageId.history}
|
||||
>
|
||||
<Icon name={"history"} />
|
||||
{Locale.menu.historyTab}
|
||||
</Menu.Item>
|
||||
</Link>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
export default KadiSidebarNav;
|
||||
90
src/Components/KadiTopMenuBar.tsx
Executable file
90
src/Components/KadiTopMenuBar.tsx
Executable file
@@ -0,0 +1,90 @@
|
||||
import React, {ReactElement, SyntheticEvent} from "react";
|
||||
import {Dropdown, DropdownItemProps, DropdownProps, Flag, Icon, Menu} from "semantic-ui-react";
|
||||
import {SERVER_BASE_NAME} from "../index";
|
||||
import LocaleContext from "../Contexts/LocaleContext";
|
||||
import {LanguageNames} from "../static/strings";
|
||||
import {IUserContext} from "../Contexts/UserContext";
|
||||
import {SupportedLang} from "../enums";
|
||||
|
||||
interface KadiTopMenuBarProps {
|
||||
user: IUserContext;
|
||||
}
|
||||
|
||||
interface KadiTopMenuBarState {
|
||||
currentLangSelection: SupportedLang;
|
||||
}
|
||||
|
||||
class KadiTopMenuBar extends React.Component<KadiTopMenuBarProps, KadiTopMenuBarState> {
|
||||
private readonly languageDropdowns: DropdownItemProps[];
|
||||
private changeLanguageGlobally: (newLang: SupportedLang) => void;
|
||||
constructor(props: KadiTopMenuBarProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
currentLangSelection: SupportedLang.gb,
|
||||
};
|
||||
|
||||
this.changeLanguageGlobally = () => {};
|
||||
this.languageDropdowns = [];
|
||||
for (const lang in LanguageNames) {
|
||||
this.languageDropdowns.push(
|
||||
{ key: lang, value: lang, flag: lang, text: LanguageNames[lang as SupportedLang] }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.changeLanguageGlobally = this.context.changeLang;
|
||||
}
|
||||
|
||||
handleLanguageChange: (e: SyntheticEvent, data: DropdownProps) => void = (event, data) => {
|
||||
const lang = data.value as SupportedLang;
|
||||
this.setState({currentLangSelection: lang});
|
||||
this.changeLanguageGlobally(lang);
|
||||
};
|
||||
|
||||
render(): ReactElement {
|
||||
const {loggedIn, username} = this.props.user;
|
||||
const Locale = this.context.strings;
|
||||
|
||||
return (
|
||||
<Menu
|
||||
secondary={true}
|
||||
pointing={true}
|
||||
attached={"top"}
|
||||
>
|
||||
<Menu.Menu
|
||||
position={"right"}
|
||||
>
|
||||
{ loggedIn && (
|
||||
<Menu.Item>
|
||||
{Locale.menu.userWelcome + username + "!"}
|
||||
</Menu.Item>
|
||||
)}
|
||||
<Menu.Item
|
||||
as={"a"}
|
||||
href={SERVER_BASE_NAME + "/account/" + (loggedIn ? "logout" : "login")}
|
||||
>
|
||||
<Icon spaced={true} name={loggedIn ? "sign out" : "sign in"} />
|
||||
{loggedIn ? Locale.menu.logoutButton : Locale.menu.loginButton}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<Dropdown
|
||||
trigger={(
|
||||
<span>
|
||||
<Flag name={this.state.currentLangSelection} />
|
||||
{LanguageNames[this.state.currentLangSelection]}
|
||||
</span>
|
||||
)}
|
||||
options={this.languageDropdowns}
|
||||
onChange={this.handleLanguageChange}
|
||||
/>
|
||||
</Menu.Item>
|
||||
</Menu.Menu>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
}
|
||||
KadiTopMenuBar.contextType = LocaleContext;
|
||||
|
||||
export default KadiTopMenuBar;
|
||||
29
src/Components/MainPageContent.tsx
Executable file
29
src/Components/MainPageContent.tsx
Executable file
@@ -0,0 +1,29 @@
|
||||
import {Container, Segment} from "semantic-ui-react";
|
||||
import React from "react";
|
||||
import KadiTopMenuBar from "./KadiTopMenuBar";
|
||||
import UserContext from "../Contexts/UserContext";
|
||||
|
||||
interface KadiPageMainContentProps {
|
||||
}
|
||||
|
||||
const KadiPageMainContent: React.FunctionComponent<KadiPageMainContentProps> = (props) => {
|
||||
const {children} = props;
|
||||
return (
|
||||
<div className={"mainPageContent"}>
|
||||
<UserContext.Consumer>
|
||||
{ user => (
|
||||
<KadiTopMenuBar user={user}/>
|
||||
)}
|
||||
</UserContext.Consumer>
|
||||
<Container
|
||||
className={"mainPageContentContainer"}
|
||||
>
|
||||
<Segment>
|
||||
{children}
|
||||
</Segment>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default KadiPageMainContent;
|
||||
27
src/Components/ProfilePage.tsx
Executable file
27
src/Components/ProfilePage.tsx
Executable file
@@ -0,0 +1,27 @@
|
||||
import React, {ReactElement} from "react";
|
||||
import KadiPage from "../Components/KadiPage";
|
||||
import {Header} from "semantic-ui-react";
|
||||
|
||||
interface ProfilePageProps {}
|
||||
|
||||
interface ProfilePageState {
|
||||
}
|
||||
|
||||
class ProfilePage extends React.Component<ProfilePageProps, ProfilePageState> {
|
||||
constructor(props: ProfilePageProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
};
|
||||
}
|
||||
|
||||
render(): ReactElement {
|
||||
return (
|
||||
<Header>
|
||||
My Profile
|
||||
</Header>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ProfilePage;
|
||||
27
src/Components/RulesetsPage.tsx
Executable file
27
src/Components/RulesetsPage.tsx
Executable file
@@ -0,0 +1,27 @@
|
||||
import React, {ReactElement} from "react";
|
||||
import KadiPage from "../Components/KadiPage";
|
||||
import {Header} from "semantic-ui-react";
|
||||
|
||||
interface RulesetsPageProps {}
|
||||
|
||||
interface RulesetsPageState {
|
||||
}
|
||||
|
||||
class RulesetsPage extends React.Component<RulesetsPageProps, RulesetsPageState> {
|
||||
constructor(props: RulesetsPageProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
};
|
||||
}
|
||||
|
||||
render(): ReactElement {
|
||||
return (
|
||||
<Header>
|
||||
My Rulesets
|
||||
</Header>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RulesetsPage;
|
||||
28
src/Components/StatsPage.tsx
Executable file
28
src/Components/StatsPage.tsx
Executable file
@@ -0,0 +1,28 @@
|
||||
import React, {ReactNode} from "react";
|
||||
import {BrowserRouter as Router, Link, Route} from "react-router-dom";
|
||||
import {Header} from "semantic-ui-react";
|
||||
import KadiPage from "../Components/KadiPage";
|
||||
|
||||
interface StatsPageProps {}
|
||||
|
||||
interface StatsPageState {
|
||||
}
|
||||
|
||||
class StatsPage extends React.Component<StatsPageProps, StatsPageState> {
|
||||
constructor(props: StatsPageProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
};
|
||||
}
|
||||
|
||||
render(): ReactNode {
|
||||
return (
|
||||
<Header>
|
||||
My Stats
|
||||
</Header>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default StatsPage;
|
||||
19
src/Contexts/LocaleContext.ts
Executable file
19
src/Contexts/LocaleContext.ts
Executable file
@@ -0,0 +1,19 @@
|
||||
import {SupportedLang} from "../enums";
|
||||
import React from "react";
|
||||
import {IntlStrings} from "../static/strings";
|
||||
|
||||
export interface ILocaleContext {
|
||||
currentLang: SupportedLang;
|
||||
strings: any;
|
||||
changeLang: (lang: SupportedLang) => void;
|
||||
}
|
||||
|
||||
export const localeDefaultVal: ILocaleContext = {
|
||||
currentLang: SupportedLang.gb,
|
||||
strings: IntlStrings[SupportedLang.gb as SupportedLang],
|
||||
changeLang: () => {},
|
||||
};
|
||||
|
||||
const LocaleContext = React.createContext(localeDefaultVal);
|
||||
|
||||
export default LocaleContext;
|
||||
17
src/Contexts/UserContext.ts
Executable file
17
src/Contexts/UserContext.ts
Executable file
@@ -0,0 +1,17 @@
|
||||
import React from "react";
|
||||
|
||||
export interface IUserContext {
|
||||
username: string;
|
||||
loggedIn: boolean;
|
||||
updateUserContext: (username: string, loggedIn: boolean) => void;
|
||||
}
|
||||
|
||||
const userDefaultVal = {
|
||||
loggedIn: false,
|
||||
username: "",
|
||||
updateUserContext: () => {},
|
||||
} as IUserContext;
|
||||
|
||||
const UserContext = React.createContext(userDefaultVal);
|
||||
|
||||
export default UserContext;
|
||||
14
src/enums.ts
Executable file
14
src/enums.ts
Executable file
@@ -0,0 +1,14 @@
|
||||
export enum SupportedLang {
|
||||
gb = "gb",
|
||||
de = "de",
|
||||
it = "it",
|
||||
}
|
||||
|
||||
export enum PageId {
|
||||
profile = "profile",
|
||||
rulesets = "rulesets",
|
||||
friends = "friends",
|
||||
stats = "stats",
|
||||
home = "home",
|
||||
history = "history",
|
||||
}
|
||||
16
src/filetypes.d.ts
vendored
Executable file
16
src/filetypes.d.ts
vendored
Executable file
@@ -0,0 +1,16 @@
|
||||
declare module '*.jpg' {
|
||||
const value: any;
|
||||
export = value;
|
||||
}
|
||||
declare module '*.gif' {
|
||||
const value: any;
|
||||
export = value;
|
||||
}
|
||||
declare module '*.png' {
|
||||
const value: any;
|
||||
export = value;
|
||||
}
|
||||
declare module '*.css' {
|
||||
const value: any;
|
||||
export = value;
|
||||
}
|
||||
20
src/index.tsx
Executable file
20
src/index.tsx
Executable file
@@ -0,0 +1,20 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import "semantic-ui-css/semantic.min.css";
|
||||
import App from "./App";
|
||||
import * as serviceWorker from "./serviceWorker";
|
||||
|
||||
export const SERVER_BASE_NAME = "/kadi";
|
||||
|
||||
ReactDOM.render((
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
),
|
||||
document.getElementById('root')
|
||||
);
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister();
|
||||
21
src/pageListings.ts
Executable file
21
src/pageListings.ts
Executable file
@@ -0,0 +1,21 @@
|
||||
import {PageId} from "./enums";
|
||||
import {Component as ReactComponent} from "react";
|
||||
import ProfilePage from "./Components/ProfilePage";
|
||||
import RulesetsPage from "./Components/RulesetsPage";
|
||||
import FriendsPage from "./Components/FriendsPage";
|
||||
import StatsPage from "./Components/StatsPage";
|
||||
import HistoryPage from "./Components/HistoryPage";
|
||||
import HomePage from "./Components/HomePage";
|
||||
|
||||
type PageComponentFromIdType = {
|
||||
[key in PageId]: new (...args: any[]) => ReactComponent;
|
||||
};
|
||||
|
||||
export const pageComponentFromId: PageComponentFromIdType = {
|
||||
[PageId.profile]: ProfilePage,
|
||||
[PageId.rulesets]: RulesetsPage,
|
||||
[PageId.friends]: FriendsPage,
|
||||
[PageId.stats]: StatsPage,
|
||||
[PageId.home]: HomePage,
|
||||
[PageId.history]: HistoryPage,
|
||||
};
|
||||
141
src/serviceWorker.js
Executable file
141
src/serviceWorker.js
Executable file
@@ -0,0 +1,141 @@
|
||||
// This optional code is used to register a service worker.
|
||||
// register() is not called by default.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on subsequent visits to a page, after all the
|
||||
// existing tabs open on the page have been closed, since previously cached
|
||||
// resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model and instructions on how to
|
||||
// opt-in, read https://bit.ly/CRA-PWA
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.0/8 are considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
|
||||
export function register(config) {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config);
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service ' +
|
||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl, config) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
if (installingWorker == null) {
|
||||
return;
|
||||
}
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the updated precached content has been fetched,
|
||||
// but the previous service worker will still serve the older
|
||||
// content until all client tabs are closed.
|
||||
console.log(
|
||||
'New content is available and will be used when all ' +
|
||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
||||
);
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onUpdate) {
|
||||
config.onUpdate(registration);
|
||||
}
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onSuccess) {
|
||||
config.onSuccess(registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl, config) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl, {
|
||||
headers: { 'Service-Worker': 'script' },
|
||||
})
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (
|
||||
response.status === 404 ||
|
||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready
|
||||
.then(registration => {
|
||||
registration.unregister();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
5
src/setupTests.js
Executable file
5
src/setupTests.js
Executable file
@@ -0,0 +1,5 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
23
src/static/css/site.css
Executable file
23
src/static/css/site.css
Executable file
@@ -0,0 +1,23 @@
|
||||
@font-face {
|
||||
font-family: "Athiti";
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url("../fonts/Athiti-ExtraLight.ttf") format("truetype");
|
||||
}
|
||||
|
||||
.brandname {
|
||||
font-family: "Athiti", monospace;
|
||||
}
|
||||
|
||||
.mainPageContent {
|
||||
margin-left: 15rem;
|
||||
}
|
||||
|
||||
.mainPageContentContainer {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.kadiSidebarNav {
|
||||
width: 15rem !important;
|
||||
}
|
||||
BIN
src/static/fonts/Athiti-ExtraLight.ttf
Executable file
BIN
src/static/fonts/Athiti-ExtraLight.ttf
Executable file
Binary file not shown.
BIN
src/static/fonts/Athiti-Light.ttf
Executable file
BIN
src/static/fonts/Athiti-Light.ttf
Executable file
Binary file not shown.
BIN
src/static/images/kadi.png
Executable file
BIN
src/static/images/kadi.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 120 KiB |
690
src/static/images/kadi.svg
Executable file
690
src/static/images/kadi.svg
Executable file
@@ -0,0 +1,690 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
inkscape:export-ydpi="96"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-filename="/home/ledda/Desktop/kadi.png"
|
||||
inkscape:version="1.0 (5b275a35d9, 2020-05-03)"
|
||||
sodipodi:docname="kadi.svg"
|
||||
width="1024px"
|
||||
height="1024px"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
id="SVGRoot">
|
||||
<defs
|
||||
id="defs462">
|
||||
<linearGradient
|
||||
id="linearGradient5683"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop5679"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop5681"
|
||||
offset="1"
|
||||
style="stop-color:#606060;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3028-6"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop3024"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop3026"
|
||||
offset="1"
|
||||
style="stop-color:#747474;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2980"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop2976"
|
||||
offset="0"
|
||||
style="stop-color:#fdfdfd;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop2978"
|
||||
offset="1"
|
||||
style="stop-color:#f1e6cc;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
osb:paint="solid"
|
||||
id="linearGradient2927">
|
||||
<stop
|
||||
id="stop2925"
|
||||
offset="0"
|
||||
style="stop-color:#dbdbe3;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="246.16356"
|
||||
x2="130.00171"
|
||||
y1="429.99353"
|
||||
x1="313.83167"
|
||||
id="linearGradient2968-2-6"
|
||||
xlink:href="#linearGradient2980"
|
||||
inkscape:collect="always"
|
||||
gradientTransform="matrix(1.8567885,0,0,1.8567885,-241.38568,-457.07369)" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(1.7800144,0,0,1.7800142,-537.16575,-357.11616)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="375.14923"
|
||||
x2="476.29993"
|
||||
y1="217.86031"
|
||||
x1="319.01102"
|
||||
id="linearGradient2955-9-2"
|
||||
xlink:href="#linearGradient2980"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5683"
|
||||
id="radialGradient3042-3-1-6"
|
||||
cx="216.74664"
|
||||
cy="327.62778"
|
||||
fx="216.74664"
|
||||
fy="327.62778"
|
||||
r="14.615433"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,-437.11911,-747.09584)" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(1.4862264,0,0,1.4862264,-311.26294,-478.20534)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="14.615433"
|
||||
fy="327.62778"
|
||||
fx="216.74664"
|
||||
cy="327.62778"
|
||||
cx="216.74664"
|
||||
id="radialGradient3042-3-1-6-8"
|
||||
xlink:href="#linearGradient3028-6"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(1.8567885,0,0,1.8567885,-241.38568,-115.74038)"
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2980"
|
||||
id="linearGradient2968-2-6-2"
|
||||
x1="313.83167"
|
||||
y1="429.99353"
|
||||
x2="130.00171"
|
||||
y2="246.16356"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2980"
|
||||
id="linearGradient2955-9-2-6"
|
||||
x1="319.01102"
|
||||
y1="217.86031"
|
||||
x2="476.29993"
|
||||
y2="375.14923"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.7800144,0,0,1.7800142,-537.16577,-15.78284)" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,-355.60638,-321.15806)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="14.615433"
|
||||
fy="327.62778"
|
||||
fx="216.74664"
|
||||
cy="327.62778"
|
||||
cx="216.74664"
|
||||
id="radialGradient3042-3-1-6-0-1-1"
|
||||
xlink:href="#linearGradient5683"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,-518.81782,-491.17479)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="14.615433"
|
||||
fy="327.62778"
|
||||
fx="216.74664"
|
||||
cy="327.62778"
|
||||
cx="216.74664"
|
||||
id="radialGradient3042-3-1-6-64-8-0"
|
||||
xlink:href="#linearGradient5683"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(1.8567885,0,0,1.8567885,441.281,-457.07369)"
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2980"
|
||||
id="linearGradient2968-2-6-21"
|
||||
x1="313.83167"
|
||||
y1="429.99353"
|
||||
x2="130.00171"
|
||||
y2="246.16356"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2980"
|
||||
id="linearGradient2955-9-2-9"
|
||||
x1="319.01102"
|
||||
y1="217.86031"
|
||||
x2="476.29993"
|
||||
y2="375.14923"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.7800144,0,0,1.7800142,145.50095,-357.11614)" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,245.54758,-747.09584)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="14.615433"
|
||||
fy="327.62778"
|
||||
fx="216.74664"
|
||||
cy="327.62778"
|
||||
cx="216.74664"
|
||||
id="radialGradient3042-3-1-6-1"
|
||||
xlink:href="#linearGradient5683"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,163.84889,-661.68358)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="14.615433"
|
||||
fy="327.62778"
|
||||
fx="216.74664"
|
||||
cy="327.62778"
|
||||
cx="216.74664"
|
||||
id="radialGradient3042-3-1-6-64-9-8"
|
||||
xlink:href="#linearGradient5683"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,327.06032,-662.49138)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="14.615433"
|
||||
fy="327.62778"
|
||||
fx="216.74664"
|
||||
cy="327.62778"
|
||||
cx="216.74664"
|
||||
id="radialGradient3042-3-1-6-0-1-7"
|
||||
xlink:href="#linearGradient5683"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,163.84889,-832.50814)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="14.615433"
|
||||
fy="327.62778"
|
||||
fx="216.74664"
|
||||
cy="327.62778"
|
||||
cx="216.74664"
|
||||
id="radialGradient3042-3-1-6-64-8-4"
|
||||
xlink:href="#linearGradient5683"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,327.06032,-833.31592)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="14.615433"
|
||||
fy="327.62778"
|
||||
fx="216.74664"
|
||||
cy="327.62778"
|
||||
cx="216.74664"
|
||||
id="radialGradient3042-3-1-6-0-4-8"
|
||||
xlink:href="#linearGradient5683"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(1.8567885,0,0,1.8567885,99.947664,-115.74034)"
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2980"
|
||||
id="linearGradient2968-2-6-29"
|
||||
x1="313.83167"
|
||||
y1="429.99353"
|
||||
x2="130.00171"
|
||||
y2="246.16356"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2980"
|
||||
id="linearGradient2955-9-2-8"
|
||||
x1="319.01102"
|
||||
y1="217.86031"
|
||||
x2="476.29993"
|
||||
y2="375.14923"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.7800144,0,0,1.7800142,-195.8324,-15.782803)" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,-177.48445,-320.35023)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="14.615433"
|
||||
fy="327.62778"
|
||||
fx="216.74664"
|
||||
cy="327.62778"
|
||||
cx="216.74664"
|
||||
id="radialGradient3042-3-1-6-64-9-2"
|
||||
xlink:href="#linearGradient5683"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,-14.273021,-321.15803)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="14.615433"
|
||||
fy="327.62778"
|
||||
fx="216.74664"
|
||||
cy="327.62778"
|
||||
cx="216.74664"
|
||||
id="radialGradient3042-3-1-6-0-1-5"
|
||||
xlink:href="#linearGradient5683"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,-177.48445,-491.17477)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="14.615433"
|
||||
fy="327.62778"
|
||||
fx="216.74664"
|
||||
cy="327.62778"
|
||||
cx="216.74664"
|
||||
id="radialGradient3042-3-1-6-64-8-49"
|
||||
xlink:href="#linearGradient5683"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,-14.273021,-491.98258)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="14.615433"
|
||||
fy="327.62778"
|
||||
fx="216.74664"
|
||||
cy="327.62778"
|
||||
cx="216.74664"
|
||||
id="radialGradient3042-3-1-6-0-4-9"
|
||||
xlink:href="#linearGradient5683"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="246.16356"
|
||||
x2="130.00171"
|
||||
y1="429.99353"
|
||||
x1="313.83167"
|
||||
id="linearGradient2968-2-6-2-9"
|
||||
xlink:href="#linearGradient2980"
|
||||
inkscape:collect="always"
|
||||
gradientTransform="matrix(1.8567885,0,0,1.8567885,-241.38568,225.59301)" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(1.7800144,0,0,1.7800142,-537.16577,325.55054)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="375.14923"
|
||||
x2="476.29993"
|
||||
y1="217.86031"
|
||||
x1="319.01102"
|
||||
id="linearGradient2955-9-2-6-2"
|
||||
xlink:href="#linearGradient2980"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5683"
|
||||
id="radialGradient3042-3-1-6-2-8"
|
||||
cx="216.74664"
|
||||
cy="327.62778"
|
||||
fx="216.74664"
|
||||
fy="327.62778"
|
||||
r="14.615433"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,-437.11913,-64.429122)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5683"
|
||||
id="radialGradient3042-3-1-6-0-1-1-1"
|
||||
cx="216.74664"
|
||||
cy="327.62778"
|
||||
fx="216.74664"
|
||||
fy="327.62778"
|
||||
r="14.615433"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,-355.60638,20.175318)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5683"
|
||||
id="radialGradient3042-3-1-6-64-8-0-4"
|
||||
cx="216.74664"
|
||||
cy="327.62778"
|
||||
fx="216.74664"
|
||||
fy="327.62778"
|
||||
r="14.615433"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,-518.81782,-149.84141)" />
|
||||
<linearGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="246.16356"
|
||||
x2="130.00171"
|
||||
y1="429.99353"
|
||||
x1="313.83167"
|
||||
id="linearGradient2968-2-6-3-7"
|
||||
xlink:href="#linearGradient2980"
|
||||
inkscape:collect="always"
|
||||
gradientTransform="matrix(1.8567885,0,0,1.8567885,441.28102,225.59303)" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(1.7800144,0,0,1.7800142,145.50095,325.55056)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="375.14923"
|
||||
x2="476.29993"
|
||||
y1="217.86031"
|
||||
x1="319.01102"
|
||||
id="linearGradient2955-9-2-69-0"
|
||||
xlink:href="#linearGradient2980"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,163.84889,-64.42914)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="14.615433"
|
||||
fy="327.62778"
|
||||
fx="216.74664"
|
||||
cy="327.62778"
|
||||
cx="216.74664"
|
||||
id="radialGradient3042-3-1-6-64-6-1"
|
||||
xlink:href="#linearGradient5683"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,327.06034,-65.236936)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="14.615433"
|
||||
fy="327.62778"
|
||||
fx="216.74664"
|
||||
cy="327.62778"
|
||||
cx="216.74664"
|
||||
id="radialGradient3042-3-1-6-0-41-9"
|
||||
xlink:href="#linearGradient5683"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5683"
|
||||
id="radialGradient3042-3-1-6-64-9-88-7"
|
||||
cx="216.74664"
|
||||
cy="327.62778"
|
||||
fx="216.74664"
|
||||
fy="327.62778"
|
||||
r="14.615433"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,163.84889,20.983132)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5683"
|
||||
id="radialGradient3042-3-1-6-0-1-9-8"
|
||||
cx="216.74664"
|
||||
cy="327.62778"
|
||||
fx="216.74664"
|
||||
fy="327.62778"
|
||||
r="14.615433"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,327.06034,20.175336)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5683"
|
||||
id="radialGradient3042-3-1-6-64-8-88-5"
|
||||
cx="216.74664"
|
||||
cy="327.62778"
|
||||
fx="216.74664"
|
||||
fy="327.62778"
|
||||
r="14.615433"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,163.84889,-149.84141)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient5683"
|
||||
id="radialGradient3042-3-1-6-0-4-86-3"
|
||||
cx="216.74664"
|
||||
cy="327.62778"
|
||||
fx="216.74664"
|
||||
fy="327.62778"
|
||||
r="14.615433"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.7596081,0,0,2.7596081,327.06034,-150.64921)" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
inkscape:object-paths="true"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-x="67"
|
||||
inkscape:window-height="1023"
|
||||
inkscape:window-width="1439"
|
||||
showgrid="false"
|
||||
inkscape:document-rotation="0"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-units="px"
|
||||
inkscape:cy="653.82099"
|
||||
inkscape:cx="654.51751"
|
||||
inkscape:zoom="0.35355339"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
borderopacity="1.0"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff"
|
||||
id="base" />
|
||||
<metadata
|
||||
id="metadata465">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="Layer 1">
|
||||
<rect
|
||||
ry="61.433502"
|
||||
y="0"
|
||||
x="0"
|
||||
height="341.33334"
|
||||
width="341.33334"
|
||||
id="rect58-6-9"
|
||||
style="fill:url(#linearGradient2968-2-6);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:5.94172;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient2955-9-2);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:9.59501;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect58-5-2-5"
|
||||
width="279.97653"
|
||||
height="279.97647"
|
||||
x="30.678432"
|
||||
y="30.678488"
|
||||
ry="50.39043" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
id="path2986-9-6-9-0"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
cy="170.66669"
|
||||
cx="170.66669" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient2968-2-6-2);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:5.94172;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect58-6-9-4"
|
||||
width="341.33334"
|
||||
height="341.33334"
|
||||
x="0"
|
||||
y="341.33334"
|
||||
ry="61.433502" />
|
||||
<rect
|
||||
ry="50.39043"
|
||||
y="372.01187"
|
||||
x="30.678432"
|
||||
height="279.97647"
|
||||
width="279.97653"
|
||||
id="rect58-5-2-5-0"
|
||||
style="fill:url(#linearGradient2955-9-2-6);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:9.59501;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
cx="252.17938"
|
||||
cy="596.60443"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-0-1-1);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
id="path2986-9-6-9-0-54-0-3" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
cx="88.967949"
|
||||
cy="426.58774"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-64-8-0);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
id="path2986-9-6-9-0-3-84-8" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient2968-2-6-21);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:5.94172;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect58-6-9-0"
|
||||
width="341.33334"
|
||||
height="341.33334"
|
||||
x="682.66669"
|
||||
y="0"
|
||||
ry="61.433502" />
|
||||
<rect
|
||||
ry="50.39043"
|
||||
y="30.678488"
|
||||
x="713.34515"
|
||||
height="279.97647"
|
||||
width="279.97653"
|
||||
id="rect58-5-2-5-4"
|
||||
style="fill:url(#linearGradient2955-9-2-9);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:9.59501;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
cx="853.33337"
|
||||
cy="170.66669"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-1);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
id="path2986-9-6-9-0-29" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
cx="771.63464"
|
||||
cy="256.07895"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-64-9-8);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
id="path2986-9-6-9-0-3-8-22" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
cx="934.84607"
|
||||
cy="255.27113"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-0-1-7);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
id="path2986-9-6-9-0-54-0-2" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
cx="771.63464"
|
||||
cy="85.254402"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-64-8-4);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
id="path2986-9-6-9-0-3-84-5" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
cx="934.84607"
|
||||
cy="84.446587"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-0-4-8);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
id="path2986-9-6-9-0-54-7-5" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient2968-2-6-29);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:5.94172;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect58-6-9-3"
|
||||
width="341.33334"
|
||||
height="341.33334"
|
||||
x="341.33334"
|
||||
y="341.33334"
|
||||
ry="61.433502" />
|
||||
<rect
|
||||
ry="50.39043"
|
||||
y="372.01187"
|
||||
x="372.01175"
|
||||
height="279.97647"
|
||||
width="279.97653"
|
||||
id="rect58-5-2-5-6"
|
||||
style="fill:url(#linearGradient2955-9-2-8);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:9.59501;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
cx="430.30133"
|
||||
cy="597.41235"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-64-9-2);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
id="path2986-9-6-9-0-3-8-4" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
cx="593.5127"
|
||||
cy="596.60443"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-0-1-5);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
id="path2986-9-6-9-0-54-0-35" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
cx="430.30133"
|
||||
cy="426.58774"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-64-8-49);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
id="path2986-9-6-9-0-3-84-7" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
cx="593.5127"
|
||||
cy="425.77991"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-0-4-9);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
id="path2986-9-6-9-0-54-7-4" />
|
||||
<rect
|
||||
ry="61.433502"
|
||||
y="682.66669"
|
||||
x="0"
|
||||
height="341.33334"
|
||||
width="341.33334"
|
||||
id="rect58-6-9-4-4"
|
||||
style="fill:url(#linearGradient2968-2-6-2-9);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:5.94172;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient2955-9-2-6-2);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:9.59501;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect58-5-2-5-0-9"
|
||||
width="279.97653"
|
||||
height="279.97647"
|
||||
x="30.678432"
|
||||
y="713.34515"
|
||||
ry="50.39043" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
id="path2986-9-6-9-0-39-0"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-2-8);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
cy="853.33337"
|
||||
cx="170.66666" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
id="path2986-9-6-9-0-54-0-3-6"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-0-1-1-1);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
cy="937.93781"
|
||||
cx="252.17938" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
id="path2986-9-6-9-0-3-84-8-1"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-64-8-0-4);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
cy="767.92108"
|
||||
cx="88.967972" />
|
||||
<rect
|
||||
ry="61.433502"
|
||||
y="682.66669"
|
||||
x="682.66669"
|
||||
height="341.33334"
|
||||
width="341.33334"
|
||||
id="rect58-6-9-8-4"
|
||||
style="fill:url(#linearGradient2968-2-6-3-7);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:5.94172;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<rect
|
||||
style="fill:url(#linearGradient2955-9-2-69-0);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:9.59501;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect58-5-2-5-3-0"
|
||||
width="279.97653"
|
||||
height="279.97647"
|
||||
x="713.34515"
|
||||
y="713.34515"
|
||||
ry="50.39043" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
cx="771.63464"
|
||||
cy="853.33337"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-64-6-1);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
id="path2986-9-6-9-0-3-3-4" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
cx="934.84607"
|
||||
cy="852.52551"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-0-41-9);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
id="path2986-9-6-9-0-54-3-7" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
id="path2986-9-6-9-0-3-8-8-8"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-64-9-88-7);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
cy="938.74567"
|
||||
cx="771.63464" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
id="path2986-9-6-9-0-54-0-0-5"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-0-1-9-8);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
cy="937.93781"
|
||||
cx="934.84607" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
id="path2986-9-6-9-0-3-84-76-2"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-64-8-88-5);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
cy="767.92108"
|
||||
cx="771.63464" />
|
||||
<ellipse
|
||||
r="29.836327"
|
||||
id="path2986-9-6-9-0-54-7-8-6"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:url(#radialGradient3042-3-1-6-0-4-86-3);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:4.448;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
|
||||
cy="767.11328"
|
||||
cx="934.84607" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 28 KiB |
69
src/static/strings.ts
Executable file
69
src/static/strings.ts
Executable file
@@ -0,0 +1,69 @@
|
||||
import React from "react";
|
||||
import {SupportedLang} from "../enums";
|
||||
|
||||
// Formats strings
|
||||
// formatUnicorn("Hello, {0}!", ["World"]) becomes "Hello, World!"
|
||||
// {0} is the first entry in args, {1} the second, etc.
|
||||
export function formatUnicorn(fmt: string, ...args: string[]): string {
|
||||
if (!fmt.match(/^(?:(?:(?:[^{}]|(?:\{\{)|(?:\}\}))+)|(?:\{[0-9]+\}))+$/)) {
|
||||
throw new Error('Invalid formatUnicorn input string.');
|
||||
}
|
||||
return fmt.replace(/((?:[^{}]|(?:\{\{)|(?:\}\}))+)|(?:\{([0-9]+)\})/g, (m: string, str: string, index: number) => {
|
||||
if (str) {
|
||||
return str.replace(/(?:{{)|(?:}})/g, m => m[0]);
|
||||
} else {
|
||||
if (index >= args.length) {
|
||||
throw new Error('Argument index is out of range in formatUnicorn call.');
|
||||
}
|
||||
return args[index];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const LanguageNames: Record<SupportedLang, string> = {
|
||||
gb: "English",
|
||||
de: "Deutsch",
|
||||
it: "Italiano",
|
||||
};
|
||||
|
||||
export const IntlStrings = {
|
||||
gb: {
|
||||
menu: {
|
||||
profileTab: "Profile",
|
||||
statsTab: "Stats",
|
||||
playTab: "Play",
|
||||
rulesetsTab: "Rulesets",
|
||||
friendsTab: "Friends",
|
||||
historyTab: "History",
|
||||
loginButton: "Login",
|
||||
logoutButton: "Logout",
|
||||
userWelcome: "Hi, ",
|
||||
},
|
||||
},
|
||||
de: {
|
||||
menu: {
|
||||
profileTab: "Profil",
|
||||
statsTab: "Statistiken",
|
||||
playTab: "Spielen",
|
||||
rulesetsTab: "Regelwerke",
|
||||
friendsTab: "Freunde",
|
||||
historyTab: "Spielverlauf",
|
||||
loginButton: "Anmelden",
|
||||
logoutButton: "Abmelden",
|
||||
userWelcome: "Hallo, ",
|
||||
},
|
||||
},
|
||||
it: {
|
||||
menu: {
|
||||
profileTab: "Profilo",
|
||||
statsTab: "Statistiche",
|
||||
playTab: "Gioca",
|
||||
rulesetsTab: "Regolamenti",
|
||||
friendsTab: "Amici",
|
||||
historyTab: "Storia",
|
||||
loginButton: "Accedi",
|
||||
logoutButton: "Esci",
|
||||
userWelcome: "Ciao, ",
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
Reference in New Issue
Block a user