my first commit

master
serkus01 2 years ago
commit 4c57ac7100

28
.gitignore vendored

@ -0,0 +1,28 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
.vscode
.idea
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Optional eslint cache
.eslintcache

@ -0,0 +1,6 @@
{
"appId": "io.ionic.starter",
"appName": "ionicTabsDemo",
"webDir": "build",
"bundledWebRuntime": false
}

@ -0,0 +1,7 @@
{
"name": "ionicTabsDemo",
"integrations": {
"capacitor": {}
},
"type": "react"
}

16510
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,71 @@
{
"name": "ionic-tabs-demo",
"version": "0.0.1",
"private": true,
"dependencies": {
"@capacitor/app": "1.0.3",
"@capacitor/core": "3.2.2",
"@capacitor/haptics": "1.0.3",
"@capacitor/keyboard": "1.0.3",
"@capacitor/status-bar": "1.0.3",
"@ionic/react": "^5.5.0",
"@ionic/react-router": "^5.5.0",
"@testing-library/jest-dom": "^5.11.9",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^12.6.3",
"@types/jest": "^26.0.20",
"@types/node": "^12.19.15",
"@types/react": "^16.14.3",
"@types/react-dom": "^16.9.10",
"@types/react-router": "^5.1.11",
"@types/react-router-dom": "^5.1.7",
"ionicons": "^5.4.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.2",
"typescript": "^4.1.3",
"web-vitals": "^0.2.4",
"workbox-background-sync": "^5.1.4",
"workbox-broadcast-update": "^5.1.4",
"workbox-cacheable-response": "^5.1.4",
"workbox-core": "^5.1.4",
"workbox-expiration": "^5.1.4",
"workbox-google-analytics": "^5.1.4",
"workbox-navigation-preload": "^5.1.4",
"workbox-precaching": "^5.1.4",
"workbox-range-requests": "^5.1.4",
"workbox-routing": "^5.1.4",
"workbox-strategies": "^5.1.4",
"workbox-streams": "^5.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@capacitor/cli": "3.2.2"
},
"description": "An Ionic project"
}

@ -0,0 +1 @@
404: Not Found

@ -0,0 +1 @@
<svg width="350" height="140" xmlns="http://www.w3.org/2000/svg" style="background:#f6f7f9"><g fill="none" fill-rule="evenodd"><path fill="#F04141" style="mix-blend-mode:multiply" d="M61.905-34.23l96.194 54.51-66.982 54.512L22 34.887z"/><circle fill="#10DC60" style="mix-blend-mode:multiply" cx="155.5" cy="135.5" r="57.5"/><path fill="#3880FF" style="mix-blend-mode:multiply" d="M208.538 9.513l84.417 15.392L223.93 93.93z"/><path fill="#FFCE00" style="mix-blend-mode:multiply" d="M268.625 106.557l46.332-26.75 46.332 26.75v53.5l-46.332 26.75-46.332-26.75z"/><circle fill="#7044FF" style="mix-blend-mode:multiply" cx="299.5" cy="9.5" r="38.5"/><rect fill="#11D3EA" style="mix-blend-mode:multiply" transform="rotate(-60 148.47 37.886)" x="143.372" y="-7.056" width="10.196" height="89.884" rx="5.098"/><path d="M-25.389 74.253l84.86 8.107c5.498.525 9.53 5.407 9.004 10.905a10 10 0 0 1-.057.477l-12.36 85.671a10.002 10.002 0 0 1-11.634 8.42l-86.351-15.226c-5.44-.959-9.07-6.145-8.112-11.584l13.851-78.551a10 10 0 0 1 10.799-8.219z" fill="#7044FF" style="mix-blend-mode:multiply"/><circle fill="#0CD1E8" style="mix-blend-mode:multiply" cx="273.5" cy="106.5" r="20.5"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Ionic App</title>
<base href="/" />
<meta name="color-scheme" content="light dark" />
<meta
name="viewport"
content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<meta name="format-detection" content="telephone=no" />
<meta name="msapplication-tap-highlight" content="no" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="shortcut icon" type="image/png" href="%PUBLIC_URL%/assets/icon/favicon.png" />
<!-- add to homescreen for ios -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-title" content="Ionic App" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
</head>
<body>
<div id="root"></div>
</body>
</html>

@ -0,0 +1,21 @@
{
"short_name": "Ionic App",
"name": "My Ionic App",
"icons": [
{
"src": "assets/icon/favicon.png",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "assets/icon/icon.png",
"type": "image/png",
"sizes": "512x512",
"purpose": "maskable"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#ffffff",
"background_color": "#ffffff"
}

@ -0,0 +1 @@
404: Not Found

@ -0,0 +1 @@
404: Not Found

@ -0,0 +1,8 @@
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
test('renders without crashing', () => {
const { baseElement } = render(<App />);
expect(baseElement).toBeDefined();
});

@ -0,0 +1,110 @@
import { Redirect, Route } from "react-router-dom";
import {
IonApp,
IonIcon,
IonLabel,
IonRouterOutlet,
IonTabBar,
IonTabButton,
IonTabs
} from "@ionic/react";
import { IonReactRouter } from "@ionic/react-router";
import {
bookmarkOutline,
bookSharp,
cafeOutline,
cafeSharp,
calendar,
ellipse,
mailOpenSharp,
map,
mapOutline,
menu,
pencil,
square,
triangle,
wallet,
walletOutline
} from "ionicons/icons";
import Place from "./pages/Place";
import Education from "./pages/Education";
import Tab3 from "./pages/Tab3";
import Menu from "./pages/Menu";
import Works from "./pages/Works";
import "@ionic/react/css/core.css";
import "@ionic/react/css/normalize.css";
import "@ionic/react/css/structure.css";
import "@ionic/react/css/typography.css";
import "@ionic/react/css/padding.css";
import "@ionic/react/css/float-elements.css";
import "@ionic/react/css/text-alignment.css";
import "@ionic/react/css/text-transformation.css";
import "@ionic/react/css/flex-utils.css";
import "@ionic/react/css/display.css";
import "./theme/variables.css";
import { List } from "./pages/List";
const App: React.FC = () => (
<IonApp>
<IonReactRouter>
<IonTabs>
<IonRouterOutlet>
<Route exact path="/tab1">
<Place />
</Route>
<Route exact path="/edu">
<Education />
</Route>
<Route path="/tab3">
<Tab3 />
</Route>
<Route exact path="/">
<Redirect to="/tab1" />
</Route>
<Route path="/menu3">
<Menu />
</Route>
<Route path="/works">
<Works />
</Route>
</IonRouterOutlet>
<IonTabBar slot="bottom">
<IonTabButton tab="tab1" href="/tab1">
<IonIcon icon={mapOutline} />
<IonLabel>Места</IonLabel>
</IonTabButton>
<IonTabButton tab="educ" href="/edu">
<IonIcon icon={bookmarkOutline} />
<IonLabel>"Образование</IonLabel>
</IonTabButton>
<IonTabButton tab="space" href="/space">
<IonIcon icon={cafeOutline} />
<IonLabel>"Пространства</IonLabel>
</IonTabButton>
<IonTabButton tab="arbaten" href="/works">
<IonIcon icon={walletOutline} />
<IonLabel>Работа</IonLabel>
</IonTabButton>
{/* <IonTabButton tab="tab3" href="/tab3">
<IonIcon icon={pencil} />
<IonLabel>Профиль</IonLabel>
</IonTabButton>*/}
<IonTabButton tab="menu" href="/menu">
<IonIcon icon={menu} />
<IonLabel>Меню</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
<Route path="/list">
<List />
</Route>
</IonReactRouter>
</IonApp>
);
export default App;

@ -0,0 +1,24 @@
.container {
text-align: center;
position: absolute;
left: 0;
right: 0;
top: 50%;
transform: translateY(-50%);
}
.container strong {
font-size: 20px;
line-height: 26px;
}
.container p {
font-size: 16px;
line-height: 22px;
color: #8c8c8c;
margin: 0;
}
.container a {
text-decoration: none;
}

@ -0,0 +1,16 @@
import './ExploreContainer.css';
interface ContainerProps {
name: string;
}
const ExploreContainer: React.FC<ContainerProps> = (props) => {
return (
<div className="container">
<strong>{props.name}</strong>
<div>{props.children || ''}</div>
</div>
);
};
export default ExploreContainer;

@ -0,0 +1,22 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorkerRegistration from './serviceWorkerRegistration';
import reportWebVitals from './reportWebVitals';
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://cra.link/PWA
serviceWorkerRegistration.unregister();
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

@ -0,0 +1,23 @@
const cites = [
{
name: "Мурманск",
description:
"город на северо-западе России, административный центр Мурманской области",
img: ""
},
{
name: "Кировск",
description:
"Ки́ровск (в 1931-1934 годах — Хибиного́рск) — город в Мурманской области России, у горного массива Кукисвумчорр.Что посетить: поход в горы Хибины/сап летом и горные лыжи/сноуборд в Кировске зимой. Как добраться: Рейсовые автобусы и личный автотранспорт"
},
{
name: "Териберка",
description:
"Тери́берка — село в Кольском районе Мурманской области. Расположено на Мурманском берегу Кольского полуострова в устье одноимённой реки, при впадении её в губу Териберскую Баренцева моря. Что посетить: Левиафан, кладбище кораблей, охота на китов, Северно-ледовитый океан, пляж драконьих яиц, норвежские пейзажи, батарейский водопад. Как добраться: Рейсовые автобусы и личный автотранспорт"
},
{
name: "Кировск",
description:
"Ки́ровск (в 1931-1934 годах — Хибиного́рск) — город в Мурманской области России, у горного массива Кукисвумчорр.Что посетить: поход в горы Хибины/сап летом и горные лыжи/сноуборд в Кировске зимой. Как добраться: Рейсовые автобусы и личный автотранспорт"
}
];

@ -0,0 +1,110 @@
import { useState } from "react";
import {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonSegment,
IonLabel,
IonSegmentButton,
IonCard,
IonCardContent,
IonCardHeader,
IonCardTitle,
IonImg,
IonButton
} from "@ionic/react";
import ExploreContainer from "../components/ExploreContainer";
//import "./Tab2.css";
import EduCard from "../components/CardEdu";
const Tab2: React.FC = () => {
const [eduState, setEduState] = useState(0);
const EduLIst = [
{
name: "Мурманский государственный технический университет",
description: "",
logo:
"https://yt3.ggpht.com/ytc/AKedOLReX8Tka9MQXKoYtog_4PFbztOqgZKZO82NyDYoFA=s900-c-k-c0x00ffffff-no-rj "
},
{
name: "Мурманский арктический государственный университет",
description: "",
logo:
"https://sun9-70.userapi.com/s/v1/if1/8I5ksYhMI17W_HHf1F5dKDbT9RIaGXvPRC7S4x3FwbhyA5uuY2sDGqbIoyAPn0hfNOpI7iFY.jpg?size=443x447&quality=96&crop=80,0,443,447&ava=1"
},
{
name: "Мурманский педагогический колледж",
description: "",
logo: "https://mpc-murmansk.ru/img/all/186_mpk_logo.jpg"
},
{ name: "", description: "", logo: "" },
{ name: "", description: "", logo: "" },
{ name: "", description: "", logo: "" },
{ name: "", description: "", logo: "" }
];
const EduView = () =>
EduLIst.map((edu) => (
<IonCard>
<IonCardHeader style={{ display: "inline" }}>
<IonImg
src={edu.logo}
style={{
width: "120px",
height: "120px",
float: "left",
margin: "20px"
}}
/>
<IonCardTitle>{edu.name}</IonCardTitle>
</IonCardHeader>
<IonCardContent>
{edu.description}
<p style={{ textAlign: "center" }}>
<IonButton
color="lifght"
style={{ backgroundColor: "#03F", color: "#EEE" }}
>
Хочу подробностей
</IonButton>{" "}
</p>
</IonCardContent>
</IonCard>
));
const GrandsView = () => (
<>
<a href="https://мурманск.гранты.рф/?ysclid=l5l3kahe2u203584674"></a>
<a href="https://rscf.ru/contests/?ysclid=l5l3m8z1ja498967331"></a>
</>
);
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Образование</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense"></IonHeader>
<IonSegment
style={{ fontSize: "12px", color: "#00F" }}
onIonChange={(e) => console.log("Segment selected", e.detail.value)}
>
<IonSegmentButton value="Instututs">
<IonLabel>Институты/Клолледжи</IonLabel>
</IonSegmentButton>
<IonSegmentButton value="Grands">
<IonLabel>Грантовая поддержка</IonLabel>
</IonSegmentButton>
</IonSegment>
<IonContent>{EduView()}</IonContent>
</IonContent>
</IonPage>
);
};
export default Tab2;

@ -0,0 +1,71 @@
import {IonContent, IonHeader, IonIcon, IonLabel, IonPage, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs, IonTitle, IonToolbar} from '@ionic/react';
import {Redirect, Route, useHistory} from 'react-router-dom';
import {ellipse, square, triangle} from "ionicons/icons";
import {ListTab} from "./ListTab";
import {useEffect} from "react";
import {useLocation} from "react-router";
export const List: React.FC = () => {
/*const location = useLocation();
useEffect(() => {
const pathName = location.pathname;
const tabBar: HTMLElement | null = document.querySelector('.tabBar');
if(tabBar) {
setTimeout(() => {
const selectButtonList: NodeList = tabBar.querySelectorAll(`ion-tab-button.tab-selected`);
if(selectButtonList && selectButtonList.length){
selectButtonList.forEach((selectButton) => {
(selectButton as HTMLElement).classList.remove('tab-selected');
});
}
const button: HTMLElement | null = tabBar.querySelector(`ion-tab-button[href="${pathName}"]`);
if(button) {
button.classList.add('tab-selected');
}
}, 10);
}
}, [location]);*/
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Notification Title</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
<IonTabs>
<IonRouterOutlet>
<Route exact path="/list/tab-1">
<ListTab name={'List Tab 1'} />
</Route>
<Route exact path="/list/tab-2">
<ListTab name={'List Tab 2' } />
</Route>
<Route exact path="/list/tab-3">
<ListTab name={'List Tab 3' } />
</Route>
<Route exact path="/list">
<Redirect to="/list/tab-1" />
</Route>
</IonRouterOutlet>
<IonTabBar slot="top" class={'tabBar'}>
<IonTabButton tab="list/tab-1" href="/list/tab-1">
<IonIcon icon={triangle} />
<IonLabel>ListTab 1</IonLabel>
</IonTabButton>
<IonTabButton tab="list/tab-2" href="/list/tab-2">
<IonIcon icon={ellipse} />
<IonLabel>ListTab 2</IonLabel>
</IonTabButton>
<IonTabButton tab="list/tab-3" href="/list/tab-3">
<IonIcon icon={square} />
<IonLabel>ListTab 3</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
</IonContent>
</IonPage>
);
}

@ -0,0 +1,7 @@
export const ListTab: React.FC<{name:string}> = ({name}) => {
return (
<div>
{name}
</div>
);
}

@ -0,0 +1,28 @@
import {
IonCard,
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar
} from "@ionic/react";
import ExploreContainer from "../components/ExploreContainer";
import "./Tab1.css";
import { Link } from "react-router-dom";
const Menu: React.FC = () => {
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Меню</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense"></IonHeader>
</IonContent>
</IonPage>
);
};
export default Menu;

@ -0,0 +1,90 @@
import {
IonCard,
IonCardHeader,
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonCardTitle,
IonCardContent,
IonImg,
IonCardSubtitle,
IonButton,
IonNav
} from "@ionic/react";
import ExploreContainer from "../components/ExploreContainer";
import "./Tab1.css";
import { Link } from "react-router-dom";
import { useState } from "react";
import { map } from "ionicons/icons";
const Tab1: React.FC = () => {
const genKey = () => Math.random().toString();
const cites = [
{
name: "Мурманск",
description:
"город на северо-западе России, административный центр Мурманской области",
img: ""
},
{
name: "Кировск",
description:
"Ки́ровск (в 1931-1934 годах — Хибиного́рск) — город в Мурманской области России, у горного массива Кукисвумчорр.Что посетить: поход в горы Хибины/сап летом и горные лыжи/сноуборд в Кировске зимой. Как добраться: Рейсовые автобусы и личный автотранспорт",
key: genKey
},
{
name: "Териберка",
description:
"Тери́берка — село в Кольском районе Мурманской области. Расположено на Мурманском берегу Кольского полуострова в устье одноимённой реки, при впадении её в губу Териберскую Баренцева моря. Что посетить: Левиафан, кладбище кораблей, охота на китов, Северно-ледовитый океан, пляж драконьих яиц, норвежские пейзажи, батарейский водопад. Как добраться: Рейсовые автобусы и личный автотранспорт",
key: genKey
},
{
name: "Кировск",
description:
"Ки́ровск (в 1931-1934 годах — Хибиного́рск) — город в Мурманской области России, у горного массива Кукисвумчорр.Что посетить: поход в горы Хибины/сап летом и горные лыжи/сноуборд в Кировске зимой. Как добраться: Рейсовые автобусы и личный автотранспорт",
key: genKey
}
];
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Достопримечательности</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle size="large">Tab 1</IonTitle>
</IonToolbar>
</IonHeader>
<IonNav>
{cites.map((c) => (
<IonCard onClick={() => alert(c.name)}>
<IonCardHeader>
<IonImg src={""} />
<IonCardTitle>{c.name}</IonCardTitle>
</IonCardHeader>
<IonCardContent>
{c.description}
{/*<p style={{ textAlign: "center" }}>
<IonButton
color="lifght"
style={{ backgroundColor: "#03F", color: "#EEE" }}
>
Хочу подробностей
</IonButton>{" "}
</p> */}
</IonCardContent>
</IonCard>
))}
</IonNav>
</IonContent>
</IonPage>
);
};
export default Tab1;

@ -0,0 +1,39 @@
import {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonSegment,
IonLabel,
IonSegmentButton
} from "@ionic/react";
import "./Tab2.css";
const Tab2: React.FC = () => {
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Образование</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense"></IonHeader>
<IonSegment
onIonChange={(e) => console.log("Segment selected", e.detail.value)}
>
<IonSegmentButton value="Instututs">
<IonLabel>Институты/Клолледжи</IonLabel>
</IonSegmentButton>
<IonSegmentButton value="Grands">
<IonLabel>Грантовая поддержка</IonLabel>
</IonSegmentButton>
</IonSegment>
</IonContent>
</IonPage>
);
};
export default Tab2;

@ -0,0 +1,3 @@
.tabs:{
background: "#0F0";
}

@ -0,0 +1,8 @@
.menu_tabs {
display: inline-flex;
list-style: none;
color: blue;
}
.menu_tabs li {
margin-left: "10px";
}

@ -0,0 +1,26 @@
import {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar
} from "@ionic/react";
import ExploreContainer from "../components/ExploreContainer";
import "./Tab3.css";
const Tab3: React.FC = () => {
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Работа</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense"></IonHeader>
</IonContent>
</IonPage>
);
};
export default Tab3;

@ -0,0 +1,38 @@
import {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonSegment,
IonLabel,
IonSegmentButton
} from "@ionic/react";
import "./Tab2.css";
const Tab2: React.FC = () => {
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>Вакансии</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonHeader collapse="condense"></IonHeader>
<IonSegment
onIonChange={(e) => console.log("Segment selected", e.detail.value)}
>
<IonSegmentButton value="Instututs">
<IonLabel>Работа с опытом</IonLabel>
</IonSegmentButton>
<IonSegmentButton value="Grands">
<IonLabel>Работа без опыта/стажировка</IonLabel>
</IonSegmentButton>
</IonSegment>
</IonContent>
</IonPage>
);
};
export default Tab2;

@ -0,0 +1 @@
/// <reference types="react-scripts" />

@ -0,0 +1,15 @@
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

@ -0,0 +1,80 @@
/// <reference lib="webworker" />
/* eslint-disable no-restricted-globals */
// This service worker can be customized!
// See https://developers.google.com/web/tools/workbox/modules
// for the list of available Workbox modules, or add any other
// code you'd like.
// You can also remove this file if you'd prefer not to use a
// service worker, and the Workbox build step will be skipped.
import { clientsClaim } from 'workbox-core';
import { ExpirationPlugin } from 'workbox-expiration';
import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate } from 'workbox-strategies';
declare const self: ServiceWorkerGlobalScope;
clientsClaim();
// Precache all of the assets generated by your build process.
// Their URLs are injected into the manifest variable below.
// This variable must be present somewhere in your service worker file,
// even if you decide not to use precaching. See https://cra.link/PWA
precacheAndRoute(self.__WB_MANIFEST);
// Set up App Shell-style routing, so that all navigation requests
// are fulfilled with your index.html shell. Learn more at
// https://developers.google.com/web/fundamentals/architecture/app-shell
const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$');
registerRoute(
// Return false to exempt requests from being fulfilled by index.html.
({ request, url }: { request: Request; url: URL }) => {
// If this isn't a navigation, skip.
if (request.mode !== 'navigate') {
return false;
}
// If this is a URL that starts with /_, skip.
if (url.pathname.startsWith('/_')) {
return false;
}
// If this looks like a URL for a resource, because it contains
// a file extension, skip.
if (url.pathname.match(fileExtensionRegexp)) {
return false;
}
// Return true to signal that we want to use the handler.
return true;
},
createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html')
);
// An example runtime caching route for requests that aren't handled by the
// precache, in this case same-origin .png requests like those from in public/
registerRoute(
// Add in any other file extensions or routing criteria as needed.
({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'),
// Customize this strategy as needed, e.g., by changing to CacheFirst.
new StaleWhileRevalidate({
cacheName: 'images',
plugins: [
// Ensure that once this runtime cache reaches a maximum size the
// least-recently used images are removed.
new ExpirationPlugin({ maxEntries: 50 }),
],
})
);
// This allows the web app to trigger skipWaiting via
// registration.waiting.postMessage({type: 'SKIP_WAITING'})
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
// Any other custom service worker logic can go here.

@ -0,0 +1,142 @@
// 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://cra.link/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}$/)
);
type Config = {
onSuccess?: (registration: ServiceWorkerRegistration) => void;
onUpdate?: (registration: ServiceWorkerRegistration) => void;
};
export function register(config?: 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://cra.link/PWA'
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl: string, config?: 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://cra.link/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: string, config?: 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);
});
}
}

@ -0,0 +1,14 @@
// 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';
// Mock matchmedia
window.matchMedia = window.matchMedia || function() {
return {
matches: false,
addListener: function() {},
removeListener: function() {}
};
};

@ -0,0 +1,236 @@
/* Ionic Variables and Theming. For more info, please see:
http://ionicframework.com/docs/theming/ */
/** Ionic CSS Variables **/
:root {
/** primary **/
--ion-color-primary: #ffc409;
--ion-color-primary-rgb: 255, 196, 9;
--ion-color-primary-contrast: #000000;
--ion-color-primary-contrast-rgb: 0, 0, 0;
--ion-color-primary-shade: #e0ac08;
--ion-color-primary-tint: #ffca22;
/** secondary **/
--ion-color-secondary: #3dc2ff;
--ion-color-secondary-rgb: 61, 194, 255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #36abe0;
--ion-color-secondary-tint: #50c8ff;
/** tertiary **/
--ion-color-tertiary: #5260ff;
--ion-color-tertiary-rgb: 82, 96, 255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #4854e0;
--ion-color-tertiary-tint: #6370ff;
/** success **/
--ion-color-success: #2dd36f;
--ion-color-success-rgb: 45, 211, 111;
--ion-color-success-contrast: #ffffff;
--ion-color-success-contrast-rgb: 255, 255, 255;
--ion-color-success-shade: #28ba62;
--ion-color-success-tint: #42d77d;
/** warning **/
--ion-color-warning: #ffc409;
--ion-color-warning-rgb: 255, 196, 9;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0, 0, 0;
--ion-color-warning-shade: #e0ac08;
--ion-color-warning-tint: #ffca22;
/** danger **/
--ion-color-danger: #eb445a;
--ion-color-danger-rgb: 235, 68, 90;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255, 255, 255;
--ion-color-danger-shade: #cf3c4f;
--ion-color-danger-tint: #ed576b;
/** dark **/
--ion-color-dark: #222428;
--ion-color-dark-rgb: 34, 36, 40;
--ion-color-dark-contrast: #ffffff;
--ion-color-dark-contrast-rgb: 255, 255, 255;
--ion-color-dark-shade: #1e2023;
--ion-color-dark-tint: #383a3e;
/** medium **/
--ion-color-medium: #92949c;
--ion-color-medium-rgb: 146, 148, 156;
--ion-color-medium-contrast: #ffffff;
--ion-color-medium-contrast-rgb: 255, 255, 255;
--ion-color-medium-shade: #808289;
--ion-color-medium-tint: #9d9fa6;
/** light **/
--ion-color-light: #f4f5f8;
--ion-color-light-rgb: 244, 245, 248;
--ion-color-light-contrast: #000000;
--ion-color-light-contrast-rgb: 0, 0, 0;
--ion-color-light-shade: #d7d8da;
--ion-color-light-tint: #f5f6f9;
}
@media (prefers-color-scheme: dark) {
/*
* Dark Colors
* -------------------------------------------
*/
body {
--ion-color-primary: #ffd534;
--ion-color-primary-rgb: 255, 213, 52;
--ion-color-primary-contrast: #000000;
--ion-color-primary-contrast-rgb: 0, 0, 0;
--ion-color-primary-shade: #e0bb2e;
--ion-color-primary-tint: #ffd948;
--ion-color-secondary: #50c8ff;
--ion-color-secondary-rgb: 80,200,255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255,255,255;
--ion-color-secondary-shade: #46b0e0;
--ion-color-secondary-tint: #62ceff;
--ion-color-tertiary: #6a64ff;
--ion-color-tertiary-rgb: 106,100,255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255,255,255;
--ion-color-tertiary-shade: #5d58e0;
--ion-color-tertiary-tint: #7974ff;
--ion-color-success: #2fdf75;
--ion-color-success-rgb: 47,223,117;
--ion-color-success-contrast: #000000;
--ion-color-success-contrast-rgb: 0,0,0;
--ion-color-success-shade: #29c467;
--ion-color-success-tint: #44e283;
--ion-color-warning: #ffd534;
--ion-color-warning-rgb: 255,213,52;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0,0,0;
--ion-color-warning-shade: #e0bb2e;
--ion-color-warning-tint: #ffd948;
--ion-color-danger: #ff4961;
--ion-color-danger-rgb: 255,73,97;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255,255,255;
--ion-color-danger-shade: #e04055;
--ion-color-danger-tint: #ff5b71;
--ion-color-dark: #f4f5f8;
--ion-color-dark-rgb: 244,245,248;
--ion-color-dark-contrast: #000000;
--ion-color-dark-contrast-rgb: 0,0,0;
--ion-color-dark-shade: #d7d8da;
--ion-color-dark-tint: #f5f6f9;
--ion-color-medium: #989aa2;
--ion-color-medium-rgb: 152,154,162;
--ion-color-medium-contrast: #000000;
--ion-color-medium-contrast-rgb: 0,0,0;
--ion-color-medium-shade: #86888f;
--ion-color-medium-tint: #a2a4ab;
--ion-color-light: #222428;
--ion-color-light-rgb: 34,36,40;
--ion-color-light-contrast: #ffffff;
--ion-color-light-contrast-rgb: 255,255,255;
--ion-color-light-shade: #1e2023;
--ion-color-light-tint: #383a3e;
}
/*
* iOS Dark Theme
* -------------------------------------------
*/
.ios body {
--ion-background-color: #000000;
--ion-background-color-rgb: 0,0,0;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255,255,255;
--ion-color-step-50: #0d0d0d;
--ion-color-step-100: #1a1a1a;
--ion-color-step-150: #262626;
--ion-color-step-200: #333333;
--ion-color-step-250: #404040;
--ion-color-step-300: #4d4d4d;
--ion-color-step-350: #595959;
--ion-color-step-400: #666666;
--ion-color-step-450: #737373;
--ion-color-step-500: #808080;
--ion-color-step-550: #8c8c8c;
--ion-color-step-600: #999999;
--ion-color-step-650: #a6a6a6;
--ion-color-step-700: #b3b3b3;
--ion-color-step-750: #bfbfbf;
--ion-color-step-800: #cccccc;
--ion-color-step-850: #d9d9d9;
--ion-color-step-900: #e6e6e6;
--ion-color-step-950: #f2f2f2;
--ion-item-background: #000000;
--ion-card-background: #1c1c1d;
}
.ios ion-modal {
--ion-background-color: var(--ion-color-step-100);
--ion-toolbar-background: var(--ion-color-step-150);
--ion-toolbar-border-color: var(--ion-color-step-250);
}
/*
* Material Design Dark Theme
* -------------------------------------------
*/
.md body {
--ion-background-color: #121212;
--ion-background-color-rgb: 18,18,18;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255,255,255;
--ion-border-color: #222222;
--ion-color-step-50: #1e1e1e;
--ion-color-step-100: #2a2a2a;
--ion-color-step-150: #363636;
--ion-color-step-200: #414141;
--ion-color-step-250: #4d4d4d;
--ion-color-step-300: #595959;
--ion-color-step-350: #656565;
--ion-color-step-400: #717171;
--ion-color-step-450: #7d7d7d;
--ion-color-step-500: #898989;
--ion-color-step-550: #949494;
--ion-color-step-600: #a0a0a0;
--ion-color-step-650: #acacac;
--ion-color-step-700: #b8b8b8;
--ion-color-step-750: #c4c4c4;
--ion-color-step-800: #d0d0d0;
--ion-color-step-850: #dbdbdb;
--ion-color-step-900: #e7e7e7;
--ion-color-step-950: #f3f3f3;
--ion-item-background: #1e1e1e;
--ion-toolbar-background: #1f1f1f;
--ion-tab-bar-background: #1f1f1f;
--ion-card-background: #1e1e1e;
}
}

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src"
]
}
Loading…
Cancel
Save