Add typescript. Idle function to load defalt screen

This commit is contained in:
rasta5man 2024-04-28 16:15:44 +02:00
parent 2ccd31d53b
commit 857ba29540
8 changed files with 679 additions and 378 deletions

View file

@ -9,11 +9,16 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^29.5.12",
"@types/node": "^20.12.7",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"axios": "^0.27.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"simple-xml-to-json": "^1.2.2",
"typescript": "^5.4.5",
"web-vitals": "^2.1.4"
},
"scripts": {

View file

@ -25,7 +25,7 @@
{
width: 100%;
height: 150vmax;
height: 200vmax;
/* height: 5000px; */
pointer-events: none;
border: 0;

View file

@ -1,365 +0,0 @@
import React, {useState, useEffect, useRef} from 'react';
import axios from 'axios'
import {Box, Typography, Paper, Container, Button} from '@mui/material';
import './App.css';
const {convertXML} = require("simple-xml-to-json");
const MAIN_HEADER_FONT_SIZE = 50;
const headerFontSize = 20;
const DATA_FONT_SIZE = 42;
const TEMPERATURE_URL = "http://10.0.0.106:3000/gettemperature";
const DEPARTURES_URL = 'http://10.0.0.106:3000/getdepartures';
const SOKOLOV_RSS_URL = 'https://www.sokolov.cz/rss/';
const DAYS_OF_WEEK = ["Neděle", "Pondělí", "Úterý", "Středa", "Čtvrtek", "Pátek", "Sobota"];
let msToMidnight = null;
let updateDayOfWeek;
const OPTIONS = { hour12: false };
let sokolovFeed;
// colors in sokolov website
const LOGO_COLOR = '#82c55b' // green
const FONT_COLOR = '#00367b';
function App() {
var [date, setDate] = useState(new Date());
var [temperature, setTemperature] = useState("?");
var [departures, setDepartures] = useState([]);
var [dayOfWeek, setDayOfWeek] = useState('');
var [feedData, setFeedData] = useState([{title: "Chyba stažení dat, kontaktujte prosím administrátora", description:"", link:"", pubDate: new Date()}]);
var [chosenArticle, setChosenArticle] = useState({clicked: false, url: ""});
// to assure, that website in iframe loads at the top
const iframeRef = useRef(null);
//to assure, that feed loads at the top
const containerRef = useRef(null);
const executeScroll = () => {
iframeRef.current.scrollIntoView(true);
}
useEffect(()=> {
!chosenArticle.clicked && containerRef.current.scrollIntoView({ behavior: 'smooth' });
}, [chosenArticle])
function updateDate() {
setDate(new Date());
}
// go back to rss feed page
const resetWeb = () => {
setChosenArticle({clicked: false, url: ""});
}
function getTemperature() {
axios(TEMPERATURE_URL, {
headers: {
'Access-Control-Allow-Origin': '*',
}})
.then(response => {
let t = parseFloat(response.data);
//console.log(t);
if(isNaN(t)) return;
setTemperature(t);
})
.catch(error => {
console.log(error);
});
}
function getDepartures() {
axios(DEPARTURES_URL, {
headers: {
'Access-Control-Allow-Origin': '*',
}})
.then(response => {
const departures = response.data;
setDepartures(departures);
})
.catch(error => {
console.log(error);
});
}
function milisecondsToMidnight() {
var midnight = new Date();
midnight.setHours(24,0,0,0);
var now = new Date();
var msToMidnight = midnight - now; //miliseconds to midnight
return msToMidnight + 2000; // just to make sure day change after midnight, I am adding 2 seconds
}
// we find out how long time is to midnight. after midnight is reached, getDay function is called and name of day is changed (streda --> ctvrtek).
function getDay() {
clearInterval(updateDayOfWeek);
const date = new Date();
setDayOfWeek(DAYS_OF_WEEK[date.getDay()]);
msToMidnight = milisecondsToMidnight();
console.log('setDay called, ms to midnight', msToMidnight);
updateDayOfWeek = setInterval(getDay, msToMidnight);
}
useEffect(() => {
updateDate();
getTemperature();
getDepartures();
getDay();
var timer = setInterval(updateDate, 1000);
var timerTemperature = setInterval(getTemperature, 60*1000);
var timerDepartures = setInterval(getDepartures, 60*1000);
return function cleanup() {
clearInterval(timer);
clearInterval(timerTemperature);
clearInterval(timerDepartures);
}
}, []);
// rss feed to get articles from sokolov website
const getRssFeed = () => {
axios(SOKOLOV_RSS_URL).then(response => {
const myJson = convertXML(response?.data);
let data = myJson?.rss?.children[0]?.channel?.children;
if(!Array.isArray(data)) return;
// first 5 items are informational, so we skip them
const result = data.slice(5).map(item => {
const d = item?.item?.children;
const url = d[1]?.link?.content.replace('amp;','');
let pubDate = d[3]?.pubDate?.content;
pubDate ? pubDate = new Date(pubDate) : pubDate = new Date();
return {title: d[0]?.title?.content, description: d[2]?.description?.content, link: url, pubDate: pubDate}
})
console.log(result)
setFeedData(result)
}).catch(e => console.log(e))
}
useEffect(() => {
getRssFeed();
var feed = setInterval(getRssFeed, 6*60*60*1000); //raz za 6 hodin
return function cleanup() {
clearInterval(feed);
}
},[])
const buildFeedView = () => {
return feedData.map((item,index) => {
return (
<Box sx={{padding: 2}} key={index}>
<Typography sx={{color: LOGO_COLOR}} variant="h6">{item.title}</Typography>
<Typography variant='subtitle2' className='hoverOverLink' onClick={()=> setChosenArticle({clicked: true, url: item.link})}>Přečti celý článek ...</Typography>
<Typography variant='body1'>{item.description}</Typography>
<Typography sx={{color: FONT_COLOR}} variant='subtitle2'>{item.pubDate.toLocaleDateString('cs-Cz')}</Typography>
</Box>)
})
}
sokolovFeed = buildFeedView();
return (
<>
<div className='containerTop'>
<table key="dataTable" style={{width:'90%', color:'white', marginTop: '3vh'}} cellPadding="0" cellSpacing="0" align="center">
<tbody>
<tr style={{fontSize: MAIN_HEADER_FONT_SIZE}}>
<td align="center">{date.toLocaleTimeString('sk-SK', OPTIONS)}</td>
<td align="center">{dayOfWeek} &nbsp; {date.toLocaleDateString('sk-SK')}</td>
<td align="center">{temperature} &#8451;</td>
</tr>
<tr style={{fontSize: MAIN_HEADER_FONT_SIZE}}>
<td align="center" colSpan={3}>
<hr></hr>
</td>
</tr>
<tr style={{fontSize: MAIN_HEADER_FONT_SIZE, verticalAlign: 'top', height: 70}}>
<td align="center">LINKA</td>
<td align="left">SMĚR</td>
<td align="center">PŘÍJEZD</td>
</tr>
{
departures.map(function(item, i){
return <tr key={i} style={{fontSize: DATA_FONT_SIZE}}><td align="center">{item[0]}</td><td align="left">{item[2]}</td><td align="center">{item[1]}</td></tr>;
})
}
</tbody>
</table>
</div>
<div className="center">
<Button variant='contained' size="large" onClick={resetWeb}>Home</Button>
</div>
<div key="containerBottom" className="containerBottom">
{chosenArticle.clicked ? <iframe title="sokolovWeb" className="iframe" ref={iframeRef} onLoad={executeScroll} src={chosenArticle.url} ></iframe> :
<Container className="scrollMarginTop" ref={containerRef} maxWidth={false}>
<Paper elevation={2} sx={{margin:'1vh 10vw 1vh'}}>
{sokolovFeed}
</Paper>
</Container>
}
</div>
</>
)
}
export default App;
const departures = [
[ '6', '07:23', 'Sídl.Michal škola', [ 'X', '42' ] ],
[ '3', '07:27', 'Sídliště Michal', [ 'X', '32' ] ],
[ '2', '07:31', 'Sídliště Michal', [ 'X', '42' ] ],
[ '3', '07:34', 'Závodu míru', [ 'X', '42' ] ],
[ '4', '07:34', 'Závodu míru', [ 'X' ] ],
[ '1', '07:35', 'Březová, aut. st.', [ 'X' ] ],
[ '3', '07:45', 'Sídliště Michal', [ 'X' ] ],
[ '33', '07:54', 'sídl.Michal škola', [ 'X', '42' ] ],
[ '1', '07:55', 'Březová, aut. st.', [ 'X' ] ],
[ '3', '07:58', 'Závodu míru', [ 'X', '42' ] ],
[ '3', '08:10', 'Závodu míru', [ 'X', '32' ] ]
]
// pubDate is date object
const dummyData = [
{
title: "Silniceeee v Hruškové se dočká opravy",
description:"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10822",
pubDate: 'Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)'
},
{
title: "Nemocnice Sokolov získala akreditaci SAK",
description:"Sokolov 10. dubna 2024 - Zdravotnický holding Penta Hospitals úspěšně pokračuje ve zvyšování kvality a bezpečnosti svých zdravotnických služeb. Po nemocnicích v Roudnici nad Labem, Vrchlabí, Ostrově nebo Písku nyní úspěšně prošla auditem Spojené akreditační komise (SAK) také Nemocnice Sokolov. Udělením akreditace SAK potvrdila, že v Sokolově je pacientům poskytována profesionální a bezpečná péče.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10821",
pubDate: 'Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)'
},
{
title: "Silnice v Hruškové se dočká opravy",
description:"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.Sokolov 10. dubna 2024 - Zdravotnický holding Penta Hospitals úspěšně pokračuje ve zvyšování kvality a bezpečnosti svých zdravotnických služeb. Po nemocnicích v Roudnici nad Labem, Vrchlabí, Ostrově nebo Písku nyní úspěšně prošla auditem Spojené akreditační komise (SAK) také Nemocnice Sokolov. Udělením akreditace SAK potvrdila, že v Sokolově je pacientům poskytována profesionální a bezpečná péče.",
link: "https://www.pravda.sk/",
pubDate: 'Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)'
},
{
title: "Silnice v Hruškové se dočká opravy",
description:"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.Sokolov 10. dubna 2024 - Zdravotnický holding Penta Hospitals úspěšně pokračuje ve zvyšování kvality a bezpečnosti svých zdravotnických služeb. Po nemocnicích v Roudnici nad Labem, Vrchlabí, Ostrově nebo Písku nyní úspěšně prošla auditem Spojené akreditační komise (SAK) také Nemocnice Sokolov. Udělením akreditace SAK potvrdila, že v Sokolově je pacientům poskytována profesionální a bezpečná péče.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10820",
pubDate: 'Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)'
},
{
title: "Silnice v Hruškové se dočká opravy",
description:"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.Sokolov 10. dubna 2024 - Zdravotnický holding Penta Hospitals úspěšně pokračuje ve zvyšování kvality a bezpečnosti svých zdravotnických služeb. Po nemocnicích v Roudnici nad Labem, Vrchlabí, Ostrově nebo Písku nyní úspěšně prošla auditem Spojené akreditační komise (SAK) také Nemocnice Sokolov. Udělením akreditace SAK potvrdila, že v Sokolově je pacientům poskytována profesionální a bezpečná péče.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10822",
pubDate: 'Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)'
},
{
title: "Silnice v Hruškové se dočká opravy",
description:"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.Sokolov 10. dubna 2024 - Zdravotnický holding Penta Hospitals úspěšně pokračuje ve zvyšování kvality a bezpečnosti svých zdravotnických služeb. Po nemocnicích v Roudnici nad Labem, Vrchlabí, Ostrově nebo Písku nyní úspěšně prošla auditem Spojené akreditační komise (SAK) také Nemocnice Sokolov. Udělením akreditace SAK potvrdila, že v Sokolově je pacientům poskytována profesionální a bezpečná péče.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10822",
pubDate: 'Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)'
},
{
title: "Silnice v Hruškové se dočká opravy",
description:"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.Sokolov 10. dubna 2024 - Zdravotnický holding Penta Hospitals úspěšně pokračuje ve zvyšování kvality a bezpečnosti svých zdravotnických služeb. Po nemocnicích v Roudnici nad Labem, Vrchlabí, Ostrově nebo Písku nyní úspěšně prošla auditem Spojené akreditační komise (SAK) také Nemocnice Sokolov. Udělením akreditace SAK potvrdila, že v Sokolově je pacientům poskytována profesionální a bezpečná péče.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10822",
pubDate: 'Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)'
},
{
title: "Silnice v Hruškové se dočká opravy",
description:"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10822",
pubDate: 'Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)'
},
{
title: "Silnice v Hruškové se dočká opravy",
description:"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.Sokolov 10. dubna 2024 - Zdravotnický holding Penta Hospitals úspěšně pokračuje ve zvyšování kvality a bezpečnosti svých zdravotnických služeb. Po nemocnicích v Roudnici nad Labem, Vrchlabí, Ostrově nebo Písku nyní úspěšně prošla auditem Spojené akreditační komise (SAK) také Nemocnice Sokolov. Udělením akreditace SAK potvrdila, že v Sokolově je pacientům poskytována profesionální a bezpečná péče.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10822",
pubDate: 'Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)'
},
{
title: "Silnice v Hruškové se dočká opravy",
description:"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10822",
pubDate: 'Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)'
}
];
// const departures = [
// [ '2', '3', '24.11.22 04:05', 'Závodu míru' ],
// [ '290', '3', '24.11.22 04:25', 'Závodu míru' ],
// [ '292', '3', '24.11.22 04:48', 'Závodu míru' ],
// [ '296', '3', '24.11.22 04:58', 'Závodu míru' ],
// [ '6', '3', '24.11.22 05:05', 'Závodu míru' ],
// [ '300', '3', '24.11.22 05:18', 'Závodu míru' ],
// [ '8', '3', '24.11.22 05:55', 'Závodu míru' ],
// [ '16', '3', '24.11.22 06:30', 'Závodu míru' ],
// [ '1', '33', '24.11.22 06:45', 'sídl. Michal škola' ],
// [ '18', '3', '24.11.22 06:52', 'Hrušková' ],
// [ '310', '3', '24.11.22 07:00', 'Závodu míru' ],
// [ '22', '3', '24.11.22 07:10', 'Závodu míru' ],
// [ '5', '33', '24.11.22 07:50', 'sídl. Michal škola' ],
// [ '32', '3', '24.11.22 08:10', 'Závodu míru' ],
// [ '7', '33', '24.11.22 08:25', 'sídl. Michal škola' ],
// [ '100', '3', '24.11.22 08:30', 'Závodu míru' ],
// [ '9', '33', '24.11.22 08:50', 'sídl. Michal škola' ],
// [ '34', '3', '24.11.22 09:00', 'Závodu míru' ],
// [ '38', '3', '24.11.22 09:20', 'Závodu míru' ],
// [ '11', '33', '24.11.22 09:30', 'sídl. Michal škola' ],
// [ '298', '3', '24.11.22 09:55', 'Závodu míru' ],
// [ '2', '7', '24.11.22 10:10', 'Březová, aut. st.' ],
// [ '13', '33', '24.11.22 10:20', 'sídl. Michal škola' ],
// [ '48', '3', '24.11.22 10:50', 'Závodu míru' ],
// [ '50', '3', '24.11.22 11:13', 'Závodu míru' ],
// [ '15', '33', '24.11.22 11:25', 'sídl. Michal škola' ],
// [ '52', '3', '24.11.22 11:40', 'Závodu míru' ],
// [ '17', '33', '24.11.22 11:52', 'sídl. Michal škola' ],
// [ '56', '3', '24.11.22 12:20', 'Závodu míru' ],
// [ '62', '3', '24.11.22 12:40', 'Závodu míru' ],
// [ '64', '3', '24.11.22 12:55', 'Závodu míru' ],
// [ '19', '33', '24.11.22 13:00', 'sídl. Michal škola' ],
// [ '66', '3', '24.11.22 13:20', 'Stará ovčárna' ],
// [ '21', '33', '24.11.22 13:35', 'sídl. Michal škola' ],
// [ '23', '33', '24.11.22 14:00', 'sídl. Michal škola' ],
// [ '72', '3', '24.11.22 14:05', 'Závodu míru' ],
// [ '25', '33', '24.11.22 14:42', 'sídl. Michal škola' ],
// [ '94', '3', '24.11.22 14:48', 'Závodu míru' ],
// [ '27', '33', '24.11.22 15:05', 'sídl. Michal škola' ],
// [ '106', '3', '24.11.22 15:09', 'Závodu míru' ],
// [ '84', '3', '24.11.22 15:35', 'Závodu míru' ],
// [ '29', '33', '24.11.22 15:35', 'sídl. Michal škola' ],
// [ '6', '7', '24.11.22 15:50', 'Březová, aut. st.' ],
// [ '96', '3', '24.11.22 16:10', 'Závodu míru' ],
// [ '31', '33', '24.11.22 16:10', 'sídl. Michal škola' ],
// [ '4', '7', '24.11.22 16:25', 'Březová, aut. st.' ],
// [ '102', '3', '24.11.22 16:30', 'Závodu míru' ],
// [ '302', '3', '24.11.22 16:44', 'Závodu míru' ],
// [ '8', '7', '24.11.22 16:55', 'Březová, aut. st.' ],
// [ '108', '3', '24.11.22 17:05', 'Stará ovčárna' ],
// [ '112', '3', '24.11.22 17:30', 'Závodu míru' ],
// [ '114', '3', '24.11.22 17:53', 'Závodu míru' ],
// [ '118', '3', '24.11.22 18:10', 'Závodu míru' ],
// [ '120', '3', '24.11.22 18:30', 'Závodu míru' ],
// [ '122', '3', '24.11.22 18:50', 'Závodu míru' ],
// [ '124', '3', '24.11.22 19:15', 'Závodu míru' ],
// [ '126', '3', '24.11.22 19:30', 'Závodu míru' ],
// [ '130', '3', '24.11.22 19:50', 'Závodu míru' ],
// [ '132', '3', '24.11.22 20:15', 'Závodu míru' ],
// [ '134', '3', '24.11.22 20:45', 'Závodu míru' ],
// [ '136', '3', '24.11.22 21:05', 'Závodu míru' ],
// [ '256', '3', '24.11.22 21:20', 'Stará ovčárna' ],
// [ '140', '3', '24.11.22 22:08', 'Stará ovčárna' ]
// ];

584
src/App.tsx Normal file
View file

@ -0,0 +1,584 @@
import React, { useState, useEffect, useRef } from "react";
import axios from "axios";
import { Box, Typography, Paper, Container, Button } from "@mui/material";
import "./App.css";
import { TChildren, TRssItem, TRssRawItem, TDepartures } from "./types";
import { convertXML } from "simple-xml-to-json";
const MAIN_HEADER_FONT_SIZE = 50;
const DATA_FONT_SIZE = 42;
const TEMPERATURE_URL = "http://10.0.0.106:3000/gettemperature";
const DEPARTURES_URL = "http://10.0.0.106:3000/getdepartures";
const SOKOLOV_RSS_URL = "https://www.sokolov.cz/rss/";
const DAYS_OF_WEEK = [
"Neděle",
"Pondělí",
"Úterý",
"Středa",
"Čtvrtek",
"Pátek",
"Sobota",
];
const INACTIVITY_LIMIT = 300000;
const OPTIONS = { hour12: false };
// colors in sokolov website
const LOGO_COLOR = "#82c55b"; // green
const FONT_COLOR = "#00367b"; // blue
let msToMidnight: number;
let updateDayOfWeek: ReturnType<typeof setInterval>;
let sokolovFeed: React.JSX.Element[];
//function that runs, when user is not active, while article is loaded on screen
function onInactive(ms: number, cb: () => void) {
var wait = setTimeout(cb, ms);
window.onmousemove =
window.onmousedown =
window.onmousemove =
window.ontouchstart =
window.ontouchmove =
window.ontouchend =
window.onmouseup =
window.onkeydown =
window.onkeyup =
window.onfocus =
function () {
clearTimeout(wait);
wait = setTimeout(cb, ms);
};
}
function App() {
var [date, setDate] = useState(new Date());
var [temperature, setTemperature] = useState<string | number>("?");
var [departures, setDepartures] = useState<TDepartures>([]);
var [dayOfWeek, setDayOfWeek] = useState("");
var [chosenArticle, setChosenArticle] = useState({ clicked: false, url: "" });
var [feedData, setFeedData] = useState<TRssItem[]>([
{
title: "Chyba stažení dat, kontaktujte prosím administrátora",
description: "",
link: "",
pubDate: new Date(),
guid: "",
},
]);
// check, how long is user inactive, if reading an article. If more than 5 minutes, we load default screen
function idle() {
onInactive(INACTIVITY_LIMIT, function () {
window.onmousemove =
window.onmousedown =
window.onmousemove =
window.ontouchstart =
window.ontouchmove =
window.ontouchend =
window.onmouseup =
window.onkeydown =
window.onkeyup =
window.onfocus =
null;
setChosenArticle({ clicked: false, url: "" });
});
}
// to assure, that website in iframe loads at the top
const iframeRef = useRef<HTMLIFrameElement>(null);
//to assure, that feed loads at the top
const containerRef = useRef<HTMLInputElement>(null);
const executeScroll = () => {
if (iframeRef.current) iframeRef.current.scrollIntoView(true);
};
useEffect(() => {
if (containerRef.current) {
!chosenArticle.clicked &&
containerRef.current.scrollIntoView({ behavior: "smooth" });
}
}, [chosenArticle]);
function updateDate() {
setDate(new Date());
}
// go back to rss feed page
const resetWeb = () => {
setChosenArticle({ clicked: false, url: "" });
};
function getTemperature() {
axios(TEMPERATURE_URL, {
headers: {
"Access-Control-Allow-Origin": "*",
},
})
.then((response) => {
let t = parseFloat(response.data);
//console.log(t);
if (isNaN(t)) return;
setTemperature(t);
})
.catch((error) => {
console.log(error);
});
}
function getDepartures() {
axios(DEPARTURES_URL, {
headers: {
"Access-Control-Allow-Origin": "*",
},
})
.then((response) => {
const departures: TDepartures = response.data;
setDepartures(departures);
})
.catch((error) => {
console.log(error);
});
}
function milisecondsToMidnight(): number {
var d = new Date();
var midnight = d.setHours(24, 0, 0, 0);
var now = Date.now();
var msToMidnight = midnight - now; //miliseconds to midnight
return msToMidnight + 2000; // just to make sure day change after midnight, I am adding 2 seconds
}
// we find out how long time is to midnight. after midnight is reached, getDay function is called and name of day is changed (streda --> ctvrtek).
function getDay() {
clearInterval(updateDayOfWeek);
const date = new Date();
setDayOfWeek(DAYS_OF_WEEK[date.getDay()]);
msToMidnight = milisecondsToMidnight();
console.log("setDay called, ms to midnight", msToMidnight);
updateDayOfWeek = setInterval(getDay, msToMidnight);
}
useEffect(() => {
updateDate();
getTemperature();
getDepartures();
getDay();
var timer = setInterval(updateDate, 1000);
var timerTemperature = setInterval(getTemperature, 60 * 1000);
var timerDepartures = setInterval(getDepartures, 60 * 1000);
return function cleanup() {
clearInterval(timer);
clearInterval(timerTemperature);
clearInterval(timerDepartures);
};
}, []);
// rss feed to get articles from sokolov website
const getRssFeed = () => {
axios(SOKOLOV_RSS_URL)
.then((response) => {
const myJson = convertXML(response?.data);
console.log("--------", JSON.stringify(myJson));
let data: TChildren = myJson?.rss?.children[0]?.channel?.children;
if (!Array.isArray(data)) return;
// first 5 items are informational, so we skip them
const result: TRssItem[] = data.slice(5).map((item: TRssRawItem) => {
const d = item?.item?.children;
const url = d[1]?.link?.content.replace("amp;", "");
let pubDate = d[3]?.pubDate?.content;
pubDate ? (pubDate = new Date(pubDate)) : (pubDate = new Date());
return {
title: d[0]?.title?.content,
description: d[2]?.description?.content,
link: url,
pubDate: pubDate,
guid: "",
};
});
console.log(result);
setFeedData(result);
})
.catch((e) => console.log(e));
};
useEffect(() => {
getRssFeed();
var feed = setInterval(getRssFeed, 6 * 60 * 60 * 1000); //raz za 6 hodin
return function cleanup() {
clearInterval(feed);
};
}, []);
const buildFeedView = (): React.JSX.Element[] => {
if (
feedData[0].title ===
"Chyba stažení dat, kontaktujte prosím administrátora"
) {
return [
<Box sx={{ padding: 2 }}>
<Typography sx={{ color: LOGO_COLOR }} variant="h6">
{feedData[0].title}
</Typography>
<Typography sx={{ color: FONT_COLOR }} variant="subtitle2">
{feedData[0].pubDate.toLocaleDateString("cs-Cz")}
</Typography>
</Box>,
];
}
return feedData.map((item, index) => {
return (
<Box sx={{ padding: 2 }} key={index}>
<Typography sx={{ color: LOGO_COLOR }} variant="h6">
{item.title}
</Typography>
<Typography
variant="subtitle2"
className="hoverOverLink"
onClick={() => {
idle();
setChosenArticle({ clicked: true, url: item.link });
}}
>
Přečti celý článek ...
</Typography>
<Typography variant="body1">{item.description}</Typography>
<Typography sx={{ color: FONT_COLOR }} variant="subtitle2">
{item.pubDate.toLocaleDateString("cs-Cz")}
</Typography>
</Box>
);
});
};
sokolovFeed = buildFeedView();
return (
<>
<div className="containerTop">
<table
key="dataTable"
style={{ width: "90%", color: "white", marginTop: "3vh" }}
cellPadding="0"
cellSpacing="0"
align="center"
>
<tbody>
<tr style={{ fontSize: MAIN_HEADER_FONT_SIZE }}>
<td align="center">
{date.toLocaleTimeString("sk-SK", OPTIONS)}
</td>
<td align="center">
{dayOfWeek} &nbsp; {date.toLocaleDateString("sk-SK")}
</td>
<td align="center">{temperature} &#8451;</td>
</tr>
<tr style={{ fontSize: MAIN_HEADER_FONT_SIZE }}>
<td align="center" colSpan={3}>
<hr></hr>
</td>
</tr>
<tr
style={{
fontSize: MAIN_HEADER_FONT_SIZE,
verticalAlign: "top",
height: 70,
}}
>
<td align="center">LINKA</td>
<td align="left">SMĚR</td>
<td align="center">PŘÍJEZD</td>
</tr>
{departures.map(function (item, i) {
return (
<tr key={i} style={{ fontSize: DATA_FONT_SIZE }}>
<td align="center">{item[0]}</td>
<td align="left">{item[2]}</td>
<td align="center">{item[1]}</td>
</tr>
);
})}
</tbody>
</table>
</div>
<div className="center">
<Button variant="contained" size="large" onClick={resetWeb}>
Home
</Button>
</div>
<div key="containerBottom" className="containerBottom">
{chosenArticle.clicked ? (
<iframe
title="sokolovWeb"
className="iframe"
ref={iframeRef}
onLoad={executeScroll}
src={chosenArticle.url}
></iframe>
) : (
<Container
className="scrollMarginTop"
ref={containerRef}
maxWidth={false}
>
<Paper elevation={2} sx={{ margin: "1vh 10vw 1vh" }}>
{sokolovFeed}
</Paper>
</Container>
)}
</div>
</>
);
}
export default App;
const departures = [
["6", "07:23", "Sídl.Michal škola", ["X", "42"]],
["3", "07:27", "Sídliště Michal", ["X", "32"]],
["2", "07:31", "Sídliště Michal", ["X", "42"]],
["3", "07:34", "Závodu míru", ["X", "42"]],
["4", "07:34", "Závodu míru", ["X"]],
["1", "07:35", "Březová, aut. st.", ["X"]],
["3", "07:45", "Sídliště Michal", ["X"]],
["33", "07:54", "sídl.Michal škola", ["X", "42"]],
["1", "07:55", "Březová, aut. st.", ["X"]],
["3", "07:58", "Závodu míru", ["X", "42"]],
["3", "08:10", "Závodu míru", ["X", "32"]],
];
// pubDate is date object
const dummyData = [
{
title: "Silniceeee v Hruškové se dočká opravy",
description:
"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10822",
pubDate:
"Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)",
},
{
title: "Nemocnice Sokolov získala akreditaci SAK",
description:
"Sokolov 10. dubna 2024 - Zdravotnický holding Penta Hospitals úspěšně pokračuje ve zvyšování kvality a bezpečnosti svých zdravotnických služeb. Po nemocnicích v Roudnici nad Labem, Vrchlabí, Ostrově nebo Písku nyní úspěšně prošla auditem Spojené akreditační komise (SAK) také Nemocnice Sokolov. Udělením akreditace SAK potvrdila, že v Sokolově je pacientům poskytována profesionální a bezpečná péče.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10821",
pubDate:
"Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)",
},
{
title: "Silnice v Hruškové se dočká opravy",
description:
"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.Sokolov 10. dubna 2024 - Zdravotnický holding Penta Hospitals úspěšně pokračuje ve zvyšování kvality a bezpečnosti svých zdravotnických služeb. Po nemocnicích v Roudnici nad Labem, Vrchlabí, Ostrově nebo Písku nyní úspěšně prošla auditem Spojené akreditační komise (SAK) také Nemocnice Sokolov. Udělením akreditace SAK potvrdila, že v Sokolově je pacientům poskytována profesionální a bezpečná péče.",
link: "https://www.pravda.sk/",
pubDate:
"Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)",
},
{
title: "Silnice v Hruškové se dočká opravy",
description:
"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.Sokolov 10. dubna 2024 - Zdravotnický holding Penta Hospitals úspěšně pokračuje ve zvyšování kvality a bezpečnosti svých zdravotnických služeb. Po nemocnicích v Roudnici nad Labem, Vrchlabí, Ostrově nebo Písku nyní úspěšně prošla auditem Spojené akreditační komise (SAK) také Nemocnice Sokolov. Udělením akreditace SAK potvrdila, že v Sokolově je pacientům poskytována profesionální a bezpečná péče.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10820",
pubDate:
"Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)",
},
{
title: "Silnice v Hruškové se dočká opravy",
description:
"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.Sokolov 10. dubna 2024 - Zdravotnický holding Penta Hospitals úspěšně pokračuje ve zvyšování kvality a bezpečnosti svých zdravotnických služeb. Po nemocnicích v Roudnici nad Labem, Vrchlabí, Ostrově nebo Písku nyní úspěšně prošla auditem Spojené akreditační komise (SAK) také Nemocnice Sokolov. Udělením akreditace SAK potvrdila, že v Sokolově je pacientům poskytována profesionální a bezpečná péče.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10822",
pubDate:
"Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)",
},
{
title: "Silnice v Hruškové se dočká opravy",
description:
"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.Sokolov 10. dubna 2024 - Zdravotnický holding Penta Hospitals úspěšně pokračuje ve zvyšování kvality a bezpečnosti svých zdravotnických služeb. Po nemocnicích v Roudnici nad Labem, Vrchlabí, Ostrově nebo Písku nyní úspěšně prošla auditem Spojené akreditační komise (SAK) také Nemocnice Sokolov. Udělením akreditace SAK potvrdila, že v Sokolově je pacientům poskytována profesionální a bezpečná péče.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10822",
pubDate:
"Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)",
},
{
title: "Silnice v Hruškové se dočká opravy",
description:
"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.Sokolov 10. dubna 2024 - Zdravotnický holding Penta Hospitals úspěšně pokračuje ve zvyšování kvality a bezpečnosti svých zdravotnických služeb. Po nemocnicích v Roudnici nad Labem, Vrchlabí, Ostrově nebo Písku nyní úspěšně prošla auditem Spojené akreditační komise (SAK) také Nemocnice Sokolov. Udělením akreditace SAK potvrdila, že v Sokolově je pacientům poskytována profesionální a bezpečná péče.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10822",
pubDate:
"Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)",
},
{
title: "Silnice v Hruškové se dočká opravy",
description:
"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10822",
pubDate:
"Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)",
},
{
title: "Silnice v Hruškové se dočká opravy",
description:
"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.Sokolov 10. dubna 2024 - Zdravotnický holding Penta Hospitals úspěšně pokračuje ve zvyšování kvality a bezpečnosti svých zdravotnických služeb. Po nemocnicích v Roudnici nad Labem, Vrchlabí, Ostrově nebo Písku nyní úspěšně prošla auditem Spojené akreditační komise (SAK) také Nemocnice Sokolov. Udělením akreditace SAK potvrdila, že v Sokolově je pacientům poskytována profesionální a bezpečná péče.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10822",
pubDate:
"Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)",
},
{
title: "Silnice v Hruškové se dočká opravy",
description:
"Silnice v Hruškové se brzy dočká částečné rekonstrukce. To ocení nejen obyvatelé této městské části ale také cyklisté, kteří tudy na jaře a v létě často projíždí.",
link: "https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&id=10822",
pubDate:
"Date Fri Apr 12 2024 10:03:45 GMT+0200 (Central European Summer Time)",
},
];
// const departures = [
// [ '2', '3', '24.11.22 04:05', 'Závodu míru' ],
// [ '290', '3', '24.11.22 04:25', 'Závodu míru' ],
// [ '292', '3', '24.11.22 04:48', 'Závodu míru' ],
// [ '296', '3', '24.11.22 04:58', 'Závodu míru' ],
// [ '6', '3', '24.11.22 05:05', 'Závodu míru' ],
// [ '300', '3', '24.11.22 05:18', 'Závodu míru' ],
// [ '8', '3', '24.11.22 05:55', 'Závodu míru' ],
// [ '16', '3', '24.11.22 06:30', 'Závodu míru' ],
// [ '1', '33', '24.11.22 06:45', 'sídl. Michal škola' ],
// [ '18', '3', '24.11.22 06:52', 'Hrušková' ],
// [ '310', '3', '24.11.22 07:00', 'Závodu míru' ],
// [ '22', '3', '24.11.22 07:10', 'Závodu míru' ],
// [ '5', '33', '24.11.22 07:50', 'sídl. Michal škola' ],
// [ '32', '3', '24.11.22 08:10', 'Závodu míru' ],
// [ '7', '33', '24.11.22 08:25', 'sídl. Michal škola' ],
// [ '100', '3', '24.11.22 08:30', 'Závodu míru' ],
// [ '9', '33', '24.11.22 08:50', 'sídl. Michal škola' ],
// [ '34', '3', '24.11.22 09:00', 'Závodu míru' ],
// [ '38', '3', '24.11.22 09:20', 'Závodu míru' ],
// [ '11', '33', '24.11.22 09:30', 'sídl. Michal škola' ],
// [ '298', '3', '24.11.22 09:55', 'Závodu míru' ],
// [ '2', '7', '24.11.22 10:10', 'Březová, aut. st.' ],
// [ '13', '33', '24.11.22 10:20', 'sídl. Michal škola' ],
// [ '48', '3', '24.11.22 10:50', 'Závodu míru' ],
// [ '50', '3', '24.11.22 11:13', 'Závodu míru' ],
// [ '15', '33', '24.11.22 11:25', 'sídl. Michal škola' ],
// [ '52', '3', '24.11.22 11:40', 'Závodu míru' ],
// [ '17', '33', '24.11.22 11:52', 'sídl. Michal škola' ],
// [ '56', '3', '24.11.22 12:20', 'Závodu míru' ],
// [ '62', '3', '24.11.22 12:40', 'Závodu míru' ],
// [ '64', '3', '24.11.22 12:55', 'Závodu míru' ],
// [ '19', '33', '24.11.22 13:00', 'sídl. Michal škola' ],
// [ '66', '3', '24.11.22 13:20', 'Stará ovčárna' ],
// [ '21', '33', '24.11.22 13:35', 'sídl. Michal škola' ],
// [ '23', '33', '24.11.22 14:00', 'sídl. Michal škola' ],
// [ '72', '3', '24.11.22 14:05', 'Závodu míru' ],
// [ '25', '33', '24.11.22 14:42', 'sídl. Michal škola' ],
// [ '94', '3', '24.11.22 14:48', 'Závodu míru' ],
// [ '27', '33', '24.11.22 15:05', 'sídl. Michal škola' ],
// [ '106', '3', '24.11.22 15:09', 'Závodu míru' ],
// [ '84', '3', '24.11.22 15:35', 'Závodu míru' ],
// [ '29', '33', '24.11.22 15:35', 'sídl. Michal škola' ],
// [ '6', '7', '24.11.22 15:50', 'Březová, aut. st.' ],
// [ '96', '3', '24.11.22 16:10', 'Závodu míru' ],
// [ '31', '33', '24.11.22 16:10', 'sídl. Michal škola' ],
// [ '4', '7', '24.11.22 16:25', 'Březová, aut. st.' ],
// [ '102', '3', '24.11.22 16:30', 'Závodu míru' ],
// [ '302', '3', '24.11.22 16:44', 'Závodu míru' ],
// [ '8', '7', '24.11.22 16:55', 'Březová, aut. st.' ],
// [ '108', '3', '24.11.22 17:05', 'Stará ovčárna' ],
// [ '112', '3', '24.11.22 17:30', 'Závodu míru' ],
// [ '114', '3', '24.11.22 17:53', 'Závodu míru' ],
// [ '118', '3', '24.11.22 18:10', 'Závodu míru' ],
// [ '120', '3', '24.11.22 18:30', 'Závodu míru' ],
// [ '122', '3', '24.11.22 18:50', 'Závodu míru' ],
// [ '124', '3', '24.11.22 19:15', 'Závodu míru' ],
// [ '126', '3', '24.11.22 19:30', 'Závodu míru' ],
// [ '130', '3', '24.11.22 19:50', 'Závodu míru' ],
// [ '132', '3', '24.11.22 20:15', 'Závodu míru' ],
// [ '134', '3', '24.11.22 20:45', 'Závodu míru' ],
// [ '136', '3', '24.11.22 21:05', 'Závodu míru' ],
// [ '256', '3', '24.11.22 21:20', 'Stará ovčárna' ],
// [ '140', '3', '24.11.22 22:08', 'Stará ovčárna' ]
// ];
// example of rss return data
/*
const a = {
rss: {
version: "2.0",
children: [
{
channel: {
children: [
{
title: {
content: "Sokolov - aktuality",
},
},
{
link: {
content: "https://www.sokolov.cz/",
},
},
{
description: {
content: "Sokolov - aktuality",
},
},
{
ttl: {
content: "60",
},
},
{
lastBuildDate: {
content: "Sat, 27 Apr 2024 18:40:22 GMT",
},
},
{
item: {
children: [
{
title: {
content:
"Biskup Tomáš Holub jednal na sokolovské radnici o rekonstrukci kostela na Starém náměstí",
},
},
{
link: {
content:
"https://www.sokolov.cz/vismo/dokumenty2.asp?id_org=15222&amp;id=10868",
},
},
{
description: {
content:
"V rámci snahy sokolovské radnice o revitalizaci historického centra, jednal starosta Sokolova Petr Kubis s plzeňským biskupem Tomášem Holubem. Tématem schůzky byla rekonstrukce kostela svatého Jakuba Většího, který tvoří jednu z dominant Starého náměstí. Radnice už začala pracovat na obnově kapucínského kláštera. Jednat chce i se zástupci pravoslavné církve o renovaci nedaleké modlitebny. Oprava římskokatolického kostela by měla doplnit celkovou revitalizaci lokality. „Chtěli bychom, aby se Staré náměstí stalo odpočinkovým místem pro obyvatele Sokolova, ale i výchozím bodem pro návštěvníky. Vedle výsadby nových stromů zde připravujeme i instalaci laviček nebo interaktivních prvků, které by popisovaly historii města. Také chceme, aby zde začínaly naučné stezky, které plánujeme vytvořit,“ doplnil starosta Kubis.",
},
},
{
pubDate: {
content: "Fri, 26 Apr 2024 16:31:48 GMT",
},
},
{
guid: {
content: "dokumenty10868",
},
},
],
},
},
],
},
},
],
},
};
*/

View file

@ -1,11 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css'
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);

13
src/index.tsx Normal file
View file

@ -0,0 +1,13 @@
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);

48
src/types.d.ts vendored Normal file
View file

@ -0,0 +1,48 @@
// types, composed for axios Rss feed
export type TRssItem = {
title: string;
description: string;
link: string;
pubDate: Date;
guid: string;
};
export type TContent = { content: string };
export type TRssRawItem = {
item: {
children: [
{ title: TContent },
{ link: TContent },
{ description: TContent },
{ pubDate: { content: Date } },
{ guid: TContent }
];
};
};
export type TChildren = {
children: [
|{
title: TContent;
}
| {
link: TContent;
}
| {
description: TContent;
}
| {
ttl: TContent;
}
| {
lastBuildDate: TContent;
}
| {
item: RssRawItem;
}
];
};
export type TDepartures = array<string, string, string, string[]>[];

27
tsconfig.json Normal file
View file

@ -0,0 +1,27 @@
{
"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"
]
}