| @ -0,0 +1,15 @@ | ||||
| import express from "express"; | ||||
| import socketIO from "socket.io"; | ||||
| 
 | ||||
| const PORT = 3001; | ||||
| 
 | ||||
| const app = express(); | ||||
| 
 | ||||
| app.get('/', (req, res) => res.send('Hello World!')); | ||||
| 
 | ||||
| const server = app.listen(PORT, () => console.log(`Example app listening on port ${PORT}!`)); | ||||
| const io = socketIO(server); | ||||
| 
 | ||||
| io.on('connection', socket => { | ||||
|     console.log('a user connected'); | ||||
| }); | ||||
| @ -0,0 +1,20 @@ | ||||
| { | ||||
|   "name": "centurion-via-backend", | ||||
|   "version": "1.0.0", | ||||
|   "description": "", | ||||
|   "main": "index.js", | ||||
|   "scripts": { | ||||
|     "test": "echo \"Error: no test specified\" && exit 1" | ||||
|   }, | ||||
|   "author": "", | ||||
|   "license": "ISC", | ||||
|   "type": "module", | ||||
|   "dependencies": { | ||||
|     "express": "^4.17.1", | ||||
|     "socket.io": "^2.3.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@types/express": "^4.17.2", | ||||
|     "@types/socket.io": "^2.1.4" | ||||
|   } | ||||
| } | ||||
| Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB | 
| Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB | 
| Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB | 
| @ -0,0 +1,77 @@ | ||||
| import React, {useEffect} from 'react'; | ||||
| import './App.css'; | ||||
| import logo from "./via-logo.svg" | ||||
| import {Col, Progress, Row} from "antd" | ||||
| import io from 'socket.io-client'; | ||||
| 
 | ||||
| 
 | ||||
| const App = () => { | ||||
|     const feedItems = [ | ||||
|         {title: "Ha1", body: "Doei", key: 1}, | ||||
|         // {title: "Ha2", body: "Doei"},
 | ||||
|         // {title: "Ha3", body: "Doei"},
 | ||||
|         // {title: "Ha3", body: "Doei"},
 | ||||
|         // {title: "Ha3", body: "Doei"},
 | ||||
|         // {title: "Ha3", body: "Doei"},
 | ||||
|         // {title: "Ha3", body: "Doei"},
 | ||||
|         // {title: "Ha3", body: "Doei"},
 | ||||
|         // {title: "Ha3", body: "Doei"},
 | ||||
|         // {title: "Ha3", body: "Doei"},
 | ||||
|         // {title: "Ha3", body: "Doei"},
 | ||||
|         // {title: "Ha3", body: "Doei"},
 | ||||
|         // {title: "Ha3", body: "Doei"},
 | ||||
|         // {title: "Ha3", body: "Doei"},
 | ||||
|         // {title: "Ha3", body: "Doei"},
 | ||||
|         // {title: "Ha3", body: "Doei"},
 | ||||
|         // {title: "Ha3", body: "Doei"},
 | ||||
|         // {title: "Ha3", body: "Doei"},
 | ||||
|     ]; | ||||
| 
 | ||||
|     // useEffect(() => {
 | ||||
|     //     socket.on("SENDING_NEW_TIME", data => {
 | ||||
|     //         setTime(data);
 | ||||
|     //     });
 | ||||
|     //     return () => {
 | ||||
|     //         socket.off("SENDING_NEW_TIME");
 | ||||
|     //     };
 | ||||
|     // }, []);
 | ||||
| 
 | ||||
| 
 | ||||
|     return ( | ||||
|         <div className="app"> | ||||
|             <section className="content"> | ||||
|                 <Row> | ||||
|                     <Col className="sider" span={4}> | ||||
|                         <h1>Tijd tot volgende shot:</h1> | ||||
|                         <Progress type="circle" | ||||
|                                   percent={75} | ||||
|                                   strokeColor={"#304ba3"} | ||||
|                                   strokeWidth={10}/> | ||||
|                     </Col> | ||||
|                     <Col className="feed" span={16}> | ||||
|                         {feedItems.map(o => | ||||
|                             <Row key={o.key} className="feed-item"> | ||||
|                                 <Col span={4}>Tijd</Col> | ||||
|                                 <Col span={3} offset={5}>{o.title}</Col> | ||||
|                                 <Col span={3}>Icoon</Col> | ||||
|                                 <Col span={3}>{o.body}</Col> | ||||
|                             </Row> | ||||
|                         )} | ||||
|                     </Col> | ||||
|                     <Col className="sider" span={4}> | ||||
|                         <h1>Shots genomen:</h1> | ||||
|                         <Progress type="circle" | ||||
|                                   percent={25} | ||||
|                                   strokeColor={"#304ba3"} | ||||
|                                   strokeWidth={10}/> | ||||
|                     </Col> | ||||
|                 </Row> | ||||
|             </section> | ||||
|             <footer> | ||||
|                 <img src={logo} className="via-logo" alt="logo"/> | ||||
|             </footer> | ||||
|         </div> | ||||
|     ); | ||||
| }; | ||||
| 
 | ||||
| export default App; | ||||
| @ -0,0 +1,58 @@ | ||||
| @import '~antd/dist/antd.css'; | ||||
| 
 | ||||
| @font-face { | ||||
|   font-family: 'SourceSansPro'; | ||||
|   src: local('SourceSansPro'), url(./SourceSansPro.otf) format('opentype'); | ||||
| } | ||||
| 
 | ||||
| :root { | ||||
|     --footer-height: 4.5em; | ||||
|     --content-height: calc(100vh - var(--footer-height)); | ||||
| } | ||||
| 
 | ||||
| body { | ||||
|     background-color: #e5e5e5; | ||||
| } | ||||
| 
 | ||||
| .app { | ||||
|     font-family: 'SourceSansPro', sans-serif; | ||||
| } | ||||
| 
 | ||||
| .feed { | ||||
|     max-height: var(--content-height); | ||||
|     overflow: hidden; | ||||
|     padding: 0.5em; | ||||
| } | ||||
| 
 | ||||
| .feed-item { | ||||
|     background-color: whitesmoke; | ||||
|     padding: 0.5em; | ||||
|     margin-bottom: 1em; | ||||
|     min-height: 5em; | ||||
|     box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); | ||||
|     border-radius: 2px; | ||||
| } | ||||
| 
 | ||||
| .content { | ||||
|     padding: 1rem; | ||||
|     height: var(--content-height) | ||||
| } | ||||
| 
 | ||||
| .via-logo { | ||||
|     width: auto; | ||||
|     height: 4em; | ||||
| } | ||||
| 
 | ||||
| .sider { | ||||
|     text-align: center; | ||||
| } | ||||
| 
 | ||||
| h1 { | ||||
|     min-height: 3em; | ||||
| } | ||||
| 
 | ||||
| footer { | ||||
|     border-top: 1px solid lightgray; | ||||
|     padding: 0.25em 0.5em; | ||||
|     height: 4.5em; | ||||
| } | ||||
| @ -0,0 +1,22 @@ | ||||
| import React from 'react'; | ||||
| import ReactDOM from 'react-dom'; | ||||
| import './index.css'; | ||||
| 
 | ||||
| import App from './App'; | ||||
| import WebWorkerHelper from "./util/WebWorkerHelper"; | ||||
| import WebWorker from "./worker/WebWorker"; | ||||
| 
 | ||||
| // web worker ?
 | ||||
| // https://stackoverflow.com/questions/47475360/creating-a-web-worker-inside-react
 | ||||
| 
 | ||||
| const worker: Worker = new WebWorkerHelper(WebWorker) as Worker; | ||||
| worker.addEventListener("message", e => { | ||||
|     console.log("Received response:"); | ||||
|     console.log(e.data); | ||||
| }, false); | ||||
| worker.postMessage("bar"); | ||||
| 
 | ||||
| console.log(worker); | ||||
| 
 | ||||
| 
 | ||||
| ReactDOM.render(<App/>, document.getElementById('root')); | ||||
| @ -0,0 +1,9 @@ | ||||
| export default class WebWorkerHelper { | ||||
|     constructor(worker: any) { | ||||
|          let code = worker.toString(); | ||||
|          code = code.substring(code.indexOf("{") + 1, code.lastIndexOf("}")); | ||||
| 
 | ||||
|          const blob = new Blob([code], { type: "application/javascript" }); | ||||
|          return new Worker(URL.createObjectURL(blob)); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,9 @@ | ||||
| { | ||||
|   "compilerOptions": { | ||||
|     "target": "es2015", | ||||
|     "lib": [ | ||||
|       "es2015", | ||||
|       "webworker" | ||||
|     ] | ||||
|   } | ||||
| } | ||||
| After Width: | Height: | Size: 5.0 KiB | 
| @ -0,0 +1,10 @@ | ||||
| export default function WebWorker() { | ||||
|     // @ts-ignore
 | ||||
|     const ctx: Worker = this as any; | ||||
| 
 | ||||
|     // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | ||||
|     const onmessage = (e: MessageEvent) => { | ||||
|         console.log("ww got message") | ||||
|          ctx.postMessage("Response"); | ||||
|     }; | ||||
| } | ||||
| @ -1,9 +0,0 @@ | ||||
| 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(); | ||||
| }); | ||||
| @ -1,26 +0,0 @@ | ||||
| import React from 'react'; | ||||
| import logo from './logo.svg'; | ||||
| import './App.css'; | ||||
| 
 | ||||
| const App = () => { | ||||
|   return ( | ||||
|     <div className="App"> | ||||
|       <header className="App-header"> | ||||
|         <img src={logo} className="App-logo" alt="logo" /> | ||||
|         <p> | ||||
|           Edit <code>src/App.tsx</code> and save to reload. | ||||
|         </p> | ||||
|         <a | ||||
|           className="App-link" | ||||
|           href="https://reactjs.org" | ||||
|           target="_blank" | ||||
|           rel="noopener noreferrer" | ||||
|         > | ||||
|           Learn React | ||||
|         </a> | ||||
|       </header> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
| 
 | ||||
| export default App; | ||||
| @ -1,13 +0,0 @@ | ||||
| body { | ||||
|   margin: 0; | ||||
|   font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', | ||||
|     'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', | ||||
|     sans-serif; | ||||
|   -webkit-font-smoothing: antialiased; | ||||
|   -moz-osx-font-smoothing: grayscale; | ||||
| } | ||||
| 
 | ||||
| code { | ||||
|   font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', | ||||
|     monospace; | ||||
| } | ||||
| @ -1,12 +0,0 @@ | ||||
| import React from 'react'; | ||||
| import ReactDOM from 'react-dom'; | ||||
| import './index.css'; | ||||
| import App from './App'; | ||||
| import * as serviceWorker from './serviceWorker'; | ||||
| 
 | ||||
| ReactDOM.render(<App />, 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(); | ||||
| Before Width: | Height: | Size: 2.6 KiB | 
| @ -1,145 +0,0 @@ | ||||
| // 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}$/ | ||||
|     ) | ||||
| ); | ||||
| 
 | ||||
| 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://bit.ly/CRA-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://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: 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(); | ||||
|     }); | ||||
|   } | ||||
| } | ||||
| @ -1,5 +0,0 @@ | ||||
| // 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'; | ||||