feat: ✨ implemented pwa capabillities and refactored lots of backend things
5
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"sessionmaker"
|
||||||
|
]
|
||||||
|
}
|
8
api/router.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from flask import send_from_directory, request
|
||||||
|
|
||||||
|
|
||||||
|
def serve_vue_app(app):
|
||||||
|
return send_from_directory(app.static_folder, 'index.html')
|
||||||
|
|
||||||
|
def upload_file(request):
|
||||||
|
file = request.files['file']
|
55
app.py
@ -1,20 +1,59 @@
|
|||||||
|
from gpxInterpreter import GPXHandler
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from flask import Flask, send_from_directory, jsonify, session, request
|
from flask import Flask, send_from_directory, jsonify, request
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
print("load environment")
|
Session: Session
|
||||||
load_dotenv()
|
app: any
|
||||||
|
|
||||||
print("create flask application")
|
def entry():
|
||||||
app = Flask(__name__, static_folder='web/dist')
|
print("load environment")
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
print("create flask application")
|
||||||
|
app = Flask(__name__, static_folder='web/dist')
|
||||||
|
|
||||||
|
# TODO: all the sqlalchemy stuff should be moved out of app.py
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
def db_connect(connectionPath):
|
||||||
|
return create_engine(connectionPath, poolclass=NullPool)
|
||||||
|
|
||||||
|
Base.metadata.create_all(db_connect)
|
||||||
|
session = sessionmaker(bind=db_connect())()
|
||||||
|
|
||||||
|
# handler class for all gpx files
|
||||||
|
# handles parsing files and interacting with database
|
||||||
|
gpxHandler = GPXHandler(session)
|
||||||
|
|
||||||
|
app.run(debug=True)
|
||||||
|
|
||||||
|
# TODO: move functional parts out to api package if not handled by other classes
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def serve_vue_app():
|
def serve_vue_app():
|
||||||
return send_from_directory(app.static_folder, 'index.html')
|
return send_from_directory(app.static_folder, 'index.html')
|
||||||
|
|
||||||
|
@app.route("/route", method=['GET'])
|
||||||
|
def getRoute():
|
||||||
|
# TODO: will contact gpx handler to get geoJSON from
|
||||||
|
return "not implemented", 500
|
||||||
|
|
||||||
@app.route('/upload', methods=['POST'])
|
@app.route('/upload', methods=['POST'])
|
||||||
def upload_file():
|
def uploadFile():
|
||||||
file = request.files['file']
|
if 'file' not in request.files:
|
||||||
|
return "no file provided", 400
|
||||||
|
|
||||||
|
file = request.files['file']
|
||||||
|
if file.filename == '':
|
||||||
|
return "no file selected", 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
gpx_handler = GPXHandler(file.stream)
|
||||||
|
gpx_handler.parse()
|
||||||
|
return "file stored succesfull", 200
|
||||||
|
except Exception as e:
|
||||||
|
return "error" + " " + str(e), 500
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.run(debug=True)
|
entry()
|
@ -1,15 +0,0 @@
|
|||||||
from sqlalchemy import create_engine, Column, Integer, String
|
|
||||||
from sqlalchemy.orm import declarative_base, sessionmaker
|
|
||||||
|
|
||||||
# Datenbankverbindung und Basis erstellen
|
|
||||||
engine = create_engine('sqlite:///beispiel.db')
|
|
||||||
Base = declarative_base()
|
|
||||||
|
|
||||||
class database():
|
|
||||||
__db: engine
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.__db = create_engine('sqlite:///users.db')
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
return self.__db
|
|
@ -1,21 +0,0 @@
|
|||||||
from sqlalchemy import create_engine, Column, Integer, String, engine
|
|
||||||
from sqlalchemy.orm import declarative_base, sessionmaker
|
|
||||||
import database
|
|
||||||
|
|
||||||
class User():
|
|
||||||
__db: engine
|
|
||||||
__base: declarative_base
|
|
||||||
def __init__(self, database:database):
|
|
||||||
self.__db = database.get()
|
|
||||||
self.__base = declarative_base()
|
|
||||||
return
|
|
||||||
def createUser(self, username, email, password):
|
|
||||||
return ""
|
|
||||||
def removeUser(self, username):
|
|
||||||
return ""
|
|
||||||
def getUserData(self, username):
|
|
||||||
return ""
|
|
||||||
def login(self, username, password):
|
|
||||||
return ""
|
|
||||||
def logout(self):
|
|
||||||
return ""
|
|
@ -1,8 +1,22 @@
|
|||||||
class gpxInterpreter:
|
import gpxpy
|
||||||
def __init__(self):
|
import gpxpy.gpx
|
||||||
|
|
||||||
def processFile(self, file):
|
class GPXHandler:
|
||||||
return True
|
__dbSession: Session
|
||||||
|
|
||||||
|
def __init__(self, session:Session):
|
||||||
|
self.__dbSession = session
|
||||||
|
pass
|
||||||
|
|
||||||
|
# handles converting a gpx file into usable data
|
||||||
|
def parse(self, file):
|
||||||
|
self.gpx = gpxpy.parse(file)
|
||||||
|
pass
|
||||||
|
|
||||||
|
# handles a route from db and converting it into geoJSON
|
||||||
|
def getRoute(self, route):
|
||||||
|
pass
|
||||||
|
|
||||||
def importDataInDB(self, data):
|
# handles storing a route in db
|
||||||
return True
|
def saveInDB():
|
||||||
|
pass
|
27
class/model/geoObjects.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.pool import NullPool
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
def getBase():
|
||||||
|
# i dont know if i ever need this but if i might here it is
|
||||||
|
return Base
|
||||||
|
|
||||||
|
def createTables(engine):
|
||||||
|
Base.metadata.create_all(engine)
|
||||||
|
|
||||||
|
class Track(Base):
|
||||||
|
__tablename__ = 'track'
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Coordinate(Base):
|
||||||
|
__tablename__ = 'coordinate'
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Driver(Base):
|
||||||
|
__tablename__ = 'driver'
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Vehicle(Base):
|
||||||
|
__tablename__ = 'vehicle'
|
||||||
|
pass
|
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<link rel="manifest" href="/manifest.json">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Vite + Vue + TS</title>
|
<title>Vite + Vue + TS</title>
|
||||||
</head>
|
</head>
|
||||||
|
11255
web/package-lock.json
generated
@ -1,24 +1,27 @@
|
|||||||
{
|
{
|
||||||
"name": "my-vue-app",
|
"name": "my-vue-app",
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
|
||||||
"build": "vue-tsc -b && vite build",
|
"build": "vue-tsc -b && vite build",
|
||||||
|
"dev": "vite",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"maplibre-gl": "^4.7.1",
|
||||||
"my-vue-app": "file:",
|
"my-vue-app": "file:",
|
||||||
|
"register-service-worker": "^1.7.2",
|
||||||
"vue": "^3.4.37",
|
"vue": "^3.4.37",
|
||||||
"vue-daisyui-theme-manager": "^0.0.29"
|
"vue-daisyui-theme-manager": "^0.0.29"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^5.1.2",
|
"@vitejs/plugin-vue": "^5.1.2",
|
||||||
|
"@vue/cli-plugin-pwa": "~5.0.0",
|
||||||
"daisyui": "^4.12.10",
|
"daisyui": "^4.12.10",
|
||||||
"tailwindcss": "^3.4.11",
|
"tailwindcss": "^3.4.11",
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.5.3",
|
||||||
"vite": "^5.4.1",
|
"vite": "^5.4.1",
|
||||||
"vue-tsc": "^2.0.29"
|
"vue-tsc": "^2.0.29"
|
||||||
}
|
},
|
||||||
|
"type": "module"
|
||||||
}
|
}
|
||||||
|
BIN
web/public/img/icons/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 9.2 KiB |
BIN
web/public/img/icons/android-chrome-512x512.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
web/public/img/icons/android-chrome-maskable-192x192.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
web/public/img/icons/android-chrome-maskable-512x512.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
web/public/img/icons/apple-touch-icon-120x120.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
web/public/img/icons/apple-touch-icon-152x152.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
web/public/img/icons/apple-touch-icon-180x180.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
web/public/img/icons/apple-touch-icon-60x60.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
web/public/img/icons/apple-touch-icon-76x76.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
web/public/img/icons/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
web/public/img/icons/favicon-16x16.png
Normal file
After Width: | Height: | Size: 799 B |
BIN
web/public/img/icons/favicon-32x32.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
web/public/img/icons/msapplication-icon-144x144.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
web/public/img/icons/mstile-150x150.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
3
web/public/img/icons/safari-pinned-tab.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M8.00251 14.9297L0 1.07422H6.14651L8.00251 4.27503L9.84583 1.07422H16L8.00251 14.9297Z" fill="black"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 215 B |
19
web/public/manifest.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "GeoTracker",
|
||||||
|
"short_name": "GeoTracker",
|
||||||
|
"description": "GPX File visualizer",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/vite.svg",
|
||||||
|
"type": "image/svg"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"background_color": "#ffffff",
|
||||||
|
"theme_color": "#4CAF50",
|
||||||
|
"orientation": "portrait",
|
||||||
|
"scope": "/",
|
||||||
|
"id": "/",
|
||||||
|
"lang": "de-DE"
|
||||||
|
}
|
2
web/public/robots.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
User-agent: *
|
||||||
|
Disallow:
|
@ -3,11 +3,15 @@ import {defineComponent, Ref, ref, SetupContext} from 'vue';
|
|||||||
import DebugLog from "./classes/debugger";
|
import DebugLog from "./classes/debugger";
|
||||||
import Settings from "./components/Settings.vue";
|
import Settings from "./components/Settings.vue";
|
||||||
import cookiePrompt from "./components/cookiePrompt.vue";
|
import cookiePrompt from "./components/cookiePrompt.vue";
|
||||||
|
import Map from "./components/map.vue";
|
||||||
|
import FileUpload from './components/fileUpload.vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {Settings, cookiePrompt},
|
components: {Settings, cookiePrompt, Map},
|
||||||
setup() {
|
setup() {
|
||||||
var showSettings:Ref<boolean> = ref(false);
|
var showSettings:Ref<boolean> = ref(false);
|
||||||
|
var showCookiePrompt:Ref<boolean> = ref(localStorage.getItem('allow-data-storage') == undefined);
|
||||||
|
|
||||||
|
|
||||||
// check if data storage was allowed and if check for localstorage settings
|
// check if data storage was allowed and if check for localstorage settings
|
||||||
if (localStorage.getItem("allow-data-storage") !== null) {
|
if (localStorage.getItem("allow-data-storage") !== null) {
|
||||||
@ -32,29 +36,55 @@ export default defineComponent({
|
|||||||
showSettings.value = !showSettings.value;
|
showSettings.value = !showSettings.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { showSettings, toggleSettings, showCookiePrompt };
|
||||||
|
|
||||||
const attemptList = ref<InstanceType<typeof AttemptList> | null>(null);
|
|
||||||
|
|
||||||
return { showSettings, toggleSettings };
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="container">
|
|
||||||
<button class="btn btn-primary round" style="float: right" @click="toggleSettings">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed">
|
|
||||||
<path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<Settings v-if="showSettings" @close="toggleSettings"/>
|
<Settings v-if="showSettings" @close="toggleSettings"/>
|
||||||
<cookiePrompt></cookiePrompt>
|
<cookiePrompt v-if="showCookiePrompt"></cookiePrompt>
|
||||||
</div>
|
<FileUpload></FileUpload>
|
||||||
|
<div class="grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<ul class="menu bg-base-200 w-56 p-0 [&_li>*]:rounded-none" style="height: calc(100vh - 77px);">
|
||||||
|
<li><a>Item 1</a></li>
|
||||||
|
<li><a>Item 2</a></li>
|
||||||
|
<li><a>Item 3</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div style="float: right; width: 100%;">
|
||||||
|
<ul class="menu bg-base-200 lg:menu-horizontal" style="width: 100%;">
|
||||||
|
<li>
|
||||||
|
<a>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-cloud-upload-fill" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M8 0a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 4.095 0 5.555 0 7.318 0 9.366 1.708 11 3.781 11H7.5V5.707L5.354 7.854a.5.5 0 1 1-.708-.708l3-3a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 5.707V11h4.188C14.502 11 16 9.57 16 7.773c0-1.636-1.242-2.969-2.834-3.194C12.923 1.999 10.69 0 8 0m-.5 14.5V11h1v3.5a.5.5 0 0 1-1 0"/>
|
||||||
|
</svg>
|
||||||
|
Upload
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a @click="toggleSettings">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-sliders" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M11.5 2a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3M9.05 3a2.5 2.5 0 0 1 4.9 0H16v1h-2.05a2.5 2.5 0 0 1-4.9 0H0V3zM4.5 7a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3M2.05 8a2.5 2.5 0 0 1 4.9 0H16v1H6.95a2.5 2.5 0 0 1-4.9 0H0V8zm9.45 4a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3m-2.45 1a2.5 2.5 0 0 1 4.9 0H16v1h-2.05a2.5 2.5 0 0 1-4.9 0H0v-1z"/>
|
||||||
|
</svg>
|
||||||
|
Settings
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<Map></Map>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
.container {
|
.container {
|
||||||
margin: 10%;
|
margin: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
2
web/src/App.vue.d.ts
vendored
@ -1,2 +0,0 @@
|
|||||||
declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
||||||
export default _default;
|
|
@ -8,5 +8,7 @@
|
|||||||
"en_audio_feedback": "Audio Feedack",
|
"en_audio_feedback": "Audio Feedack",
|
||||||
"ger_audio_feedback": "Ton Wiedergabe",
|
"ger_audio_feedback": "Ton Wiedergabe",
|
||||||
"en_language": "Language",
|
"en_language": "Language",
|
||||||
"ger_language": "Sprache"
|
"ger_language": "Sprache",
|
||||||
|
"en_localizedUploadHeader":"GPX File Upload",
|
||||||
|
"ger_localizedUploadHeader": "Lade eine GPX Datei hoch"
|
||||||
}
|
}
|
@ -7,7 +7,7 @@ enum SettingNodes {
|
|||||||
/**
|
/**
|
||||||
* class which handles reading and writing settings
|
* class which handles reading and writing settings
|
||||||
*/
|
*/
|
||||||
class SettingsHandler implements ISettings{
|
var SettingsHandler = class SettingsHandler implements ISettings{
|
||||||
|
|
||||||
settings:Map<SettingNodes, any>
|
settings:Map<SettingNodes, any>
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -31,4 +31,6 @@ class SettingsHandler implements ISettings{
|
|||||||
FetchSetting(name:SettingNodes):any {
|
FetchSetting(name:SettingNodes):any {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export {SettingsHandler, SettingNodes};
|
@ -17,6 +17,8 @@ export default defineComponent({
|
|||||||
settingsLangLocalized.value = await GetLocalizedText("lang")
|
settingsLangLocalized.value = await GetLocalizedText("lang")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: if time rework this to function with the new intended settings class
|
||||||
|
// NOTE: this has low priority as the system inplace allthough not very intuitive works
|
||||||
const changeTheme = (theme:string) => {
|
const changeTheme = (theme:string) => {
|
||||||
|
|
||||||
document.documentElement.setAttribute('data-theme', theme);
|
document.documentElement.setAttribute('data-theme', theme);
|
||||||
@ -53,7 +55,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="card bg-base-100 w-full shadow-xl">
|
<div class="card bg-base-100 w-full shadow-xl" style="">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
||||||
<h2 class="card-title">Settings</h2>
|
<h2 class="card-title">Settings</h2>
|
||||||
@ -91,6 +93,12 @@ export default defineComponent({
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.settingsBody {
|
||||||
|
position: absolute;
|
||||||
|
margin: 10% 10% 0 10%;
|
||||||
|
width: 80%;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
.btn.close {
|
.btn.close {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 30px;
|
right: 30px;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {defineComponent, SetupContext, ref, Ref} from 'vue';
|
import {defineComponent, SetupContext, ref, Ref} from 'vue';
|
||||||
import GetLocalizedText from "../classes/language";
|
import GetLocalizedText from "../classes/language";
|
||||||
import SettingsHandler from "../classes/settings";
|
import {SettingsHandler, SettingsNodes} from "../classes/settings";
|
||||||
import ISettings from "../interfaces/ISettings"
|
import ISettings from "../interfaces/ISettings"
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@ -12,11 +12,11 @@ export default defineComponent({
|
|||||||
var settings:ISettings = new SettingsHandler();
|
var settings:ISettings = new SettingsHandler();
|
||||||
|
|
||||||
function acceptCookies(){
|
function acceptCookies(){
|
||||||
|
localStorage.setItem("allow-data-storage", "true")
|
||||||
}
|
}
|
||||||
|
|
||||||
function rejectCookies(){
|
function rejectCookies(){
|
||||||
|
localStorage.setItem("allow-data-storage", "false")
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -40,8 +40,8 @@ export default defineComponent({
|
|||||||
Ein cookie welcher die entscheidung speichert wird immer gesetzt
|
Ein cookie welcher die entscheidung speichert wird immer gesetzt
|
||||||
</p>
|
</p>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<button class="btn btn-success">Cookies zulassen</button>
|
<button class="btn btn-success" v-on:click="acceptCookies">Cookies zulassen</button>
|
||||||
<button class="btn btn-error" style="margin-left: 15px;">Cookies verbieten</button>
|
<button class="btn btn-error" style="margin-left: 15px;" v-on:click="rejectCookies">Cookies verbieten</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
60
web/src/components/fileUpload.vue
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {defineComponent, SetupContext, ref, Ref} from 'vue';
|
||||||
|
import GetLocalizedText from "../classes/language";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
emits: ['close'],
|
||||||
|
name: 'settings',
|
||||||
|
setup(_, { emit }: SetupContext) {
|
||||||
|
var showUploadEmbed:Ref<boolean> = ref(false);
|
||||||
|
|
||||||
|
// localized text
|
||||||
|
// if there is time left this gets moved out to its own class
|
||||||
|
var localizedUploadHeader:Ref<string> = ref("")
|
||||||
|
async function getLocalization() {
|
||||||
|
localizedUploadHeader.value = await GetLocalizedText("localizedUploadHeader")
|
||||||
|
}
|
||||||
|
|
||||||
|
getLocalization()
|
||||||
|
const close = () => {
|
||||||
|
emit("close");
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
close,
|
||||||
|
localizedUploadHeader
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="card bg-base-100 w-full shadow-xl" style="">
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<h2 class="card-title">{{ localizedUploadHeader }}</h2>
|
||||||
|
|
||||||
|
<button class="btn btn-error close round" @click="close()">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed">
|
||||||
|
<path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.settingsBody {
|
||||||
|
position: absolute;
|
||||||
|
margin: 10% 10% 0 10%;
|
||||||
|
width: 80%;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
.btn.close {
|
||||||
|
position: absolute;
|
||||||
|
right: 30px;
|
||||||
|
top: 30px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
</style>
|
62
web/src/components/map.vue
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<template>
|
||||||
|
<div class="map-wrap">
|
||||||
|
<div class="map" ref="mapContainer"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Map, NavigationControl, Marker } from 'maplibre-gl';
|
||||||
|
import { shallowRef, onMounted, onUnmounted, markRaw } from 'vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Map",
|
||||||
|
setup () {
|
||||||
|
const mapContainer = shallowRef(null);
|
||||||
|
const map = shallowRef(null);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const apiKey = "irEA6TiXQV7M8II74un7";
|
||||||
|
if (apiKey == null) {
|
||||||
|
throw new Error("You need to configure env VUE_APP_API_KEY first, see README");
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState = { lng: 139.753, lat: 35.6844, zoom: 14 };
|
||||||
|
|
||||||
|
map.value = markRaw(new Map({
|
||||||
|
container: mapContainer.value,
|
||||||
|
style: `https://api.maptiler.com/maps/streets/style.json?key=${apiKey}`,
|
||||||
|
center: [initialState.lng, initialState.lat],
|
||||||
|
zoom: initialState.zoom
|
||||||
|
}));
|
||||||
|
map.value.addControl(new NavigationControl(), 'top-right');
|
||||||
|
new Marker({color: "#FF0000"})
|
||||||
|
.setLngLat([139.7525,35.6841])
|
||||||
|
.addTo(map.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
map.value?.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
map, mapContainer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
@import 'maplibre-gl/dist/maplibre-gl.css';
|
||||||
|
|
||||||
|
.map-wrap {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 77px); /* calculate height of the screen minus the heading */
|
||||||
|
}
|
||||||
|
|
||||||
|
.map {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
1
web/src/main.d.ts
vendored
@ -1 +0,0 @@
|
|||||||
import './style.css';
|
|
32
web/src/registerServiceWorker.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
|
import { register } from 'register-service-worker'
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
register(`${process.env.BASE_URL}service-worker.js`, {
|
||||||
|
ready () {
|
||||||
|
console.log(
|
||||||
|
'App is being served from cache by a service worker.\n' +
|
||||||
|
'For more details, visit https://goo.gl/AFskqB'
|
||||||
|
)
|
||||||
|
},
|
||||||
|
registered () {
|
||||||
|
console.log('Service worker has been registered.')
|
||||||
|
},
|
||||||
|
cached () {
|
||||||
|
console.log('Content has been cached for offline use.')
|
||||||
|
},
|
||||||
|
updatefound () {
|
||||||
|
console.log('New content is downloading.')
|
||||||
|
},
|
||||||
|
updated () {
|
||||||
|
console.log('New content is available; please refresh.')
|
||||||
|
},
|
||||||
|
offline () {
|
||||||
|
console.log('No internet connection found. App is running in offline mode.')
|
||||||
|
},
|
||||||
|
error (error) {
|
||||||
|
console.error('Error during service worker registration:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -1 +0,0 @@
|
|||||||
{"root":["./src/main.ts","./src/vite-env.d.ts","./src/app.vue","./src/components/gameinput.vue","./src/components/helloworld.vue"],"version":"5.6.2"}
|
|
@ -1 +0,0 @@
|
|||||||
{"root":["./theme-manager.config.ts","./vite.config.ts","./src/app.vue","./src/app.vue.d.ts","./src/main.ts","./src/vite-env.d.ts","./src/classes/debugger.ts","./src/classes/language.ts","./src/components/attemptlist.vue","./src/components/gameinput.vue","./src/components/settings.vue"],"version":"5.6.2"}
|
|
@ -1,7 +0,0 @@
|
|||||||
import { defineConfig } from 'vite';
|
|
||||||
import vue from '@vitejs/plugin-vue';
|
|
||||||
// https://vitejs.dev/config/
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [vue()]
|
|
||||||
});
|
|
||||||
//# sourceMappingURL=vite.config.js.map
|
|