diff --git a/.gitignore b/.gitignore index b6cfbbf..11b0027 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,8 @@ fabric.properties # .NET obj/ bin/ + +# Ignore user uploads +uploads/ + +__pycache__ \ No newline at end of file diff --git a/sample.env b/sample.env index 4a396e3..e2c1fff 100644 --- a/sample.env +++ b/sample.env @@ -25,6 +25,12 @@ LOGIN_SERVER_UID="" #Login server secret LOGIN_SERVER_SECRET="" +#=====POSTGRES===== +POSTGRES_HOST = "" +POSTGRES_DBNAME = "" +POSTGRES_USER = "" +POSTGRES_PASS = "" +POSTGRES_PORT = #=====General===== #The domain on which stickermap will be hosted (without port) @@ -32,10 +38,10 @@ STICKER_MAP_URL="http://localhost" #The port on which stickermap will be hosted. STICKER_MAP_PORT=7050 #The color of all the UI elements on the site (make sure that white text is readable on this color) -STICKER_MAP_COLOR="#29738f" +STICKER_MAP_COLOR="#197052" #Determine if the map is viewable without loging in STICKER_MAP_REQUIRE_LOGIN=False #How long should an admin session be valid? (seconds) ADMIN_EXPIRES_IN=1800 -PGDATABASE = "sticker" +PGDATABASE = "sticker" \ No newline at end of file diff --git a/server.py b/server.py index 142bb79..2890799 100644 --- a/server.py +++ b/server.py @@ -5,11 +5,16 @@ import psycopg2 import time import json +from decimal import Decimal import os from werkzeug.utils import redirect, secure_filename import random from dotenv import load_dotenv import secrets +import datetime + +if (not os.path.exists("./static/uploads")): + os.mkdir("./static/uploads") # Load env file for variable load_dotenv() @@ -17,12 +22,30 @@ # Create flask app app = flask.Flask(__name__) +POSTGRES_HOST = os.getenv("POSTGRES_HOST") +POSTGRES_DBNAME = os.getenv("POSTGRES_DBNAME") +POSTGRES_USER = os.getenv("POSTGRES_USER") +POSTGRES_PASS = os.getenv("POSTGRES_PASS") +POSTGRES_PORT = os.getenv("POSTGRES_PORT") BOARD_COLOR = os.getenv("STICKER_MAP_COLOR") ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'} UPLOAD_DIRECTORY = "static/uploads" +con = psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) + +cursor = con.cursor() + +cursor.execute("CREATE TABLE IF NOT EXISTS stickers (stickerID SERIAL PRIMARY KEY, stickerLat Decimal(8,6), stickerLon Decimal(9,6), logoID INT, pictureUrl VARCHAR(255), adderEmail VARCHAR(255), postTime TIMESTAMP, spots INT, verified INT)") +# cursor.execute("INSERT INTO stickers (stickerLat, stickerLon, logoId, pictureUrl, adderEmail, verified) VALUES (52.1630587, 5.402001, 1, 'zwart 1920x1080.png', 'aron.den.ouden@gmail.com', 1)") + +con.commit() + +cursor.close() +con.close() + + @app.route('/') def stickerMap(): if os.getenv('STICKER_MAP_REQUIRE_LOGIN') == "True": @@ -72,7 +95,7 @@ def auth(): if 'credentials_type' not in tokenResponse: return redirect('/auth', code=302) # Connect to db - with psycopg2.connect() as con: + with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con: cursor = con.cursor() # Check if the user already has (normal) key stored. (Admin refresh session) token = "" @@ -121,10 +144,15 @@ def uploadSticker(): # Save file file.save(os.path.join(UPLOAD_DIRECTORY, filename)) # create db entry - with psycopg2.connect() as con: + with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con: emailCode = random.randrange(9999999, 999999999) + + sampleEmail = "joe@joecompany.com" + currentTime = str(datetime.datetime.now()) + cursor = con.cursor() - cursor.execute("INSERT INTO stickers (stickerLat, stickerLon, logoId, pictureUrl, adderEmail) VALUES (%s,%s,%s,%s,%s)", (request.form['lat'], request.form['lon'], request.form['logoId'], os.path.join(UPLOAD_DIRECTORY, filename), emailCode)) + # cursor.execute("INSERT INTO stickers (stickerLat, stickerLon, logoId, pictureUrl, adderEmail) VALUES (%s,%s,%s,%s,%s)", (request.form['lat'], request.form['lon'], request.form['logoId'], os.path.join(UPLOAD_DIRECTORY, filename), emailCode)) + cursor.execute("INSERT INTO stickers (stickerLat, stickerLon, logoId, pictureUrl, adderEmail, postTime, spots, verified) VALUES (%s,%s,%s,%s,%s,%s,%s,%s)", (request.form['lat'], request.form['lon'], 1, os.path.join(UPLOAD_DIRECTORY, filename), emailCode, currentTime, 0, 1)) con.commit() return json.dumps({'status': '200', 'error': 'Sticker added to database.', 'emailCode': emailCode}), 200 else: @@ -140,13 +168,13 @@ def uploadSticker(): @app.route('/logos', methods=['GET']) def getLogos(): # Check token if required - if os.getenv('STICKER_MAP_REQUIRE_LOGIN') == "True": - if not checkToken(request.args.get('token')): + # if os.getenv('STICKER_MAP_REQUIRE_LOGIN') == "True": + # if not checkToken(request.args.get('token')): return json.dumps({'status': '403', 'error': 'Not authenticated or cookies disabled.'}), 405 - with psycopg2.connect() as con: - cursor = con.cursor() - results = cursor.execute('SELECT * FROM logos ORDER BY logoTitle DESC').fetchall() - return json.dumps(results) + # with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con: + # cursor = con.cursor() + # results = cursor.execute('SELECT * FROM logos ORDER BY logoTitle DESC').fetchall() + # return json.dumps(results) @app.route('/editLogo', methods=['PATCH']) @@ -156,7 +184,7 @@ def editLogo(): return json.dumps({'status': '403', 'error': 'Token invalid, expired, or not available'}), 403 if request.args.get("id") == None or request.args.get('name') == None or request.args.get('color') == None: return json.dumps({'status': '400', 'error': 'Invalid or missing arguments.'}), 400 - with psycopg2.connect() as con: + with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con: cursor = con.cursor(); cursor.execute('UPDATE logos SET logoTitle=%s, logoColor=%s WHERE logoId=%s', (request.args.get('name'), request.args.get('color'), request.args.get('id'))) con.commit() @@ -170,7 +198,7 @@ def deleteLogo(): return json.dumps({'status': '403', 'error': 'Token invalid, expired, or not available'}), 403 if request.args.get("id") is None: return json.dumps({'status': '400', 'error': 'Invalid or missing arguments.'}), 400 - with psycopg2.connect() as con: + with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con: cursor = con.cursor() cursor.execute('DELETE FROM logos WHERE logoId=%s', (request.args.get('id'),)) con.commit() @@ -184,7 +212,7 @@ def addLogo(): return json.dumps({'status': '403', 'error': 'Token invalid, expired, or not available'}), 403 if request.args.get('name') == None or request.args.get('color') == None: return json.dumps({'status': '400', 'error': 'Invalid or missing arguments.'}), 400 - with psycopg2.connect() as con: + with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con: cursor = con.cursor() cursor.execute('INSERT INTO logos (logoTitle, logoColor) VALUES (%s,%s)', (request.args.get('name'), request.args.get('color'))) con.commit() @@ -200,7 +228,7 @@ def addEmail(): if request.form['email'] != '': if request.form['token'] != '': # Check if the token is in the database - with psycopg2.connect() as con: + with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con: cursor = con.cursor() result = cursor.execute('SELECT * FROM stickers WHERE adderEmail=%s', (request.form['token'],)).fetchall() if len(result) != 0: @@ -224,12 +252,49 @@ def getStickers(): return json.dumps({'status': '403', 'error': 'Not authenticated or cookies disabled.'}), 405 if (request.args.get('west') != '' and request.args.get('east') != '' and request.args.get('north') != '' and request.args.get('south') != ''): # Get all the stickers within the bounding box - with psycopg2.connect() as con: + with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con: + # create cursor + cursor = con.cursor() + # find results + cursor.execute("SELECT * FROM stickers WHERE stickerLat BETWEEN %s AND %s AND stickerLon BETWEEN %s AND %s", (request.args.get('south'), request.args.get('north'), request.args.get('west'), request.args.get('east'))) + + rows = cursor.fetchall() + + return json.dumps(rows, default=str) + else: + return json.dumps({'status': '400', 'error': 'Bounding box not defined or incomplete.'}), 400 + +@app.route('/getNearYouStickers', methods=['GET']) +def getNearYouStickers(): + # Check token if required + if os.getenv('STICKER_MAP_REQUIRE_LOGIN') == "True": + if not checkToken(request.cookies.get('token')): + return json.dumps({'status': '403', 'error': 'Not authenticated or cookies disabled.'}), 405 + if (request.args.get('lon') != '' and request.args.get('lat') != ''): + # Get all the stickers within the bounding box + with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con: # create cursor cursor = con.cursor() # find results - rows = cursor.execute("SELECT * FROM stickers WHERE stickerLat BETWEEN %s AND %s AND stickerLon BETWEEN %s AND %s AND verified='1'", (request.args.get('south'), request.args.get('north'), request.args.get('west'), request.args.get('east'))).fetchall() - return json.dumps(rows) + # cursor.execute("""SELECT *, ST_DistanceSphere( + # ST_MakePoint(5.172445, 52.086240), + # ST_MakePoint(stickerLon, stickerLat) + # ) AS distance + # FROM stickers + # ORDER BY distance ASC + # LIMIT 10""") + + cursor.execute("""SELECT *, ST_DistanceSphere( + ST_MakePoint(%s, %s), + ST_MakePoint(stickerLon, stickerLat) + ) AS distance + FROM stickers + ORDER BY distance ASC + LIMIT 10""", (float(request.args.get('lon')), float(request.args.get('lat')))) + + rows = cursor.fetchall() + + return json.dumps(rows, default=str) else: return json.dumps({'status': '400', 'error': 'Bounding box not defined or incomplete.'}), 400 @@ -240,7 +305,7 @@ def getUnverifiedStickers(): if not checkAdminToken(request.cookies.get('adminToken')): return json.dumps({'status': '403', 'error': 'Token invalid, expired, or not available'}), 403 # Get all unverified stickers' - with psycopg2.connect() as con: + with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con: # create cursor cursor = con.cursor() # find results @@ -255,7 +320,7 @@ def setSticker(): return json.dumps({'status': '403', 'error': 'Token invalid, expired, or not available'}), 403 if request.args.get("id") == None or request.args.get('state') == None: return json.dumps({'status': '400', 'error': 'Invalid or missing arguments.'}) - with psycopg2.connect() as con: + with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con: # create a cursor cursor = con.cursor() # Get the email address of the user @@ -274,6 +339,30 @@ def setSticker(): return json.dumps({'status': '400', 'error': 'Invalid card state'}) +@app.route('/updateStickerSpots', methods=['POST']) +def updateStickerSpots(): + # Check token if required + if os.getenv('STICKER_MAP_REQUIRE_LOGIN') == "True": + if not checkToken(request.cookies.get('token')): + return json.dumps({'status': '403', 'error': 'Not authenticated or cookies disabled.'}), 405 + + data = request.get_json() + stickerID = data.get('stickerID') + + if (stickerID != ''): + # Get all the stickers within the bounding box + with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con: + # create cursor + cursor = con.cursor() + # Increase spot count + cursor.execute("UPDATE stickers SET spots = spots + 1 WHERE stickerID = %s", (stickerID,)) + + return json.dumps({'status': '200', 'error': 'Updated spots count'}), 200 + else: + return json.dumps({'status': '400', 'error': 'Updating sticker spots failed'}), 400 + + + def sendEmailUpdate(): return 0 # TODO not implemented @@ -282,7 +371,7 @@ def checkToken(token): # print(token) if token is None: return False - with psycopg2.connect() as con: + with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con: cursor = con.cursor() cursor.execute("SELECT * FROM tokens WHERE token=%s", (token,)) rows = cursor.fetchall() @@ -297,7 +386,7 @@ def checkAdminToken(token): if token is None: return False # Connect with database - with psycopg2.connect() as con: + with psycopg2.connect(host=POSTGRES_HOST, dbname=POSTGRES_DBNAME, user=POSTGRES_USER, password=POSTGRES_PASS, port=POSTGRES_PORT) as con: # Remove invalid keys from the database cursor = con.cursor() cursor.execute("DELETE FROM adminTokens WHERE %s > expirationTime", (time.time(),)) diff --git a/static/admin.css b/static/css/admin.css similarity index 94% rename from static/admin.css rename to static/css/admin.css index 0c1f63a..f733b17 100644 --- a/static/admin.css +++ b/static/css/admin.css @@ -1,286 +1,286 @@ -*{ - margin: 0; - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; -} - - -.content{ - display: flex; - flex-direction: column; - align-items: center; - width: 100%; - overflow: hidden; -} - -.cardSection{ - width: 100%; - max-width: 400px; - position: relative; - padding-top: 10px; - margin: 10px 0px 10px 0px; - border-radius: 15px; - background-color: rgb(30,30,30); -} - -.navBar{ - width: 100%; - height: 80px; - display: flex; - align-items: center; - flex-direction: row; - background-color: rgb(30,30,30); - box-shadow: 0px 3px 5px rgba(0,0,0,0.5); -} - -.title{ - color: white; - font-size: 30px; - margin-left: 30px; -} - -.containerLabel{ - color: white; - display: block; - font-size: 25px; - margin-left: 35px; -} - -.cardHolder{ - height: 450px; -} - -.card{ - position: absolute; - overflow: hidden; - margin: 15px 30px 30px 30px; - border-radius: 20px; - box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5); - width: calc(100% - 60px); - /*animation: cardSwipeRight 1s infinite; - animation-direction: alternate-reverse; */ - transform-origin: bottom; - touch-action: none; -} - -@keyframes cardSwipeLeft { - 0%{ - transform: translate(0px, 0px) rotate(0deg); - opacity: 1; - } - 100%{ - transform: translate(-200px, 30px) rotate(-30deg); - opacity: 0; - } -} - -@keyframes cardSwipeRight { - 0%{ - transform: translate(0px, 0px) rotate(0deg); - opacity: 1; - } - 100%{ - transform: translate(200px, 30px) rotate(30deg); - opacity: 0; - } -} - -.stickerImage{ - width: 100%; - height: 350px; - object-fit: cover; -} - -.buttonArea{ - position: relative; - height: 80px; - background: linear-gradient(rgba(255,255,255, 0) 0%, rgba(255,255,255,1) 50%); - margin-top: -45px; - display: flex; - justify-content: space-around; -} - -.cardButton{ - width: 80px; - height: 80px; - margin-top: -25px; - background-size:cover; - -} - -.accept{ - background-image: url('/static/thumb-up.svg'); -} - -.reject{ - background-image: url('/static/thumb-down.svg'); - -} - -.bottomSection{ - width: 100%; - display: flex; - flex-direction: row; - color: white; -} - -@media screen and (max-width:700px) { - .bottomSection{ - flex-direction: column; - } - .allStickersSection{ - margin: 0px 0px 10px 0px !important; - width: calc(100% - 20px) !important; - } - .allLogosSection{ - margin: 0px !important; - width: calc(100% - 20px) !important; - } -} - -.containerDivider{ - margin: 5px 20px 10px 20px; - border-top: rgb(150,150,150) solid 1px; - border-bottom: none; -} - -.allStickersSection{ - background-color: rgb(30,30,30); - width: 50%; - margin: 0px 5px 0px 10px; - padding: 10px; - border-radius: 15px; -} - -.allLogosSection{ - background-color: rgb(30,30,30); - width: 50%; - margin: 0px 10px 0px 5px; - padding: 10px; - border-radius: 15px; -} - -.containerGrid{ - margin: 0px 20px 0px 20px; - width: calc(100% - 40px); - display: flex; - flex-wrap: wrap; - justify-content: flex-start; -} - -.gridItem{ - width: 130px; - height: 150px; - display: flex; - justify-content: center; - align-items: center; - border:rgb(112, 112, 112) solid; - margin-right: 3px; -} - -.gridItem:hover{ - background-color: rgb(71, 67, 67); -} - -.gridImage{ - width: 100px; -} - -.logoContainer{ - margin-left: 20px; - margin-right: 20px; -} - -.logoRow{ - display: flex; - flex-direction: row; - align-items: center; - margin-bottom: 10px; -} - -.logoIcon{ - width: 40px; - height: 40px; -} - -.logoLabel{ - margin-left: 20px; - font-size: 18px; -} - -.logoControls{ - margin-right: 0; - margin-left: auto; -} - -.logoControl{ - margin-left: 10px; -} - -.logoAddButton{ - width: 100%; - height: 40px; - display: flex; - align-items: center; - justify-content: center; - font-weight: bold; - font-size: 20px; - cursor: pointer; -} - -.logoEditBackdrop{ - position: fixed; - top: 0px; - width: 100%; - height: 100%; - background-color: rgba(0,0,0,0.8); - display: none; - align-items: center; - justify-content: center; - opacity: 0; - transition: opacity 0.5s; -} - -.logoEdit{ - background-color: white; - display: flex; - flex-direction: column; - width: 80%; - max-width: 300px; - border-radius: 10px; - overflow: hidden; -} - -.logoEditTitle{ - margin: 15px; - font-size: 25px; - text-align: center; - font-weight: bold; -} - -.logoEditLabel{ - margin-left: 10px; - margin-top: 10px; -} - -.logoEditInput{ - margin-left: 10px; - margin-right: 10px; - height: 30px; - font-size: 20px; - border: none; - outline: none; - border-bottom: solid 0.5px rgb(0,0,0,0.5); -} - -.logoEditButton{ - display: flex; - justify-content: center; - margin-top: 20px; - height: 40px; - color: white; - font-size: 20px; - font-weight: bold; - align-items: center; - background-color: rgb(0, 140, 7); - cursor: pointer; +*{ + margin: 0; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + + +.content{ + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + overflow: hidden; +} + +.cardSection{ + width: 100%; + max-width: 400px; + position: relative; + padding-top: 10px; + margin: 10px 0px 10px 0px; + border-radius: 15px; + background-color: rgb(30,30,30); +} + +.navBar{ + width: 100%; + height: 80px; + display: flex; + align-items: center; + flex-direction: row; + background-color: rgb(30,30,30); + box-shadow: 0px 3px 5px rgba(0,0,0,0.5); +} + +.title{ + color: white; + font-size: 30px; + margin-left: 30px; +} + +.containerLabel{ + color: white; + display: block; + font-size: 25px; + margin-left: 35px; +} + +.cardHolder{ + height: 450px; +} + +.card{ + position: absolute; + overflow: hidden; + margin: 15px 30px 30px 30px; + border-radius: 20px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.5); + width: calc(100% - 60px); + /*animation: cardSwipeRight 1s infinite; + animation-direction: alternate-reverse; */ + transform-origin: bottom; + touch-action: none; +} + +@keyframes cardSwipeLeft { + 0%{ + transform: translate(0px, 0px) rotate(0deg); + opacity: 1; + } + 100%{ + transform: translate(-200px, 30px) rotate(-30deg); + opacity: 0; + } +} + +@keyframes cardSwipeRight { + 0%{ + transform: translate(0px, 0px) rotate(0deg); + opacity: 1; + } + 100%{ + transform: translate(200px, 30px) rotate(30deg); + opacity: 0; + } +} + +.stickerImage{ + width: 100%; + height: 350px; + object-fit: cover; +} + +.buttonArea{ + position: relative; + height: 80px; + background: linear-gradient(rgba(255,255,255, 0) 0%, rgba(255,255,255,1) 50%); + margin-top: -45px; + display: flex; + justify-content: space-around; +} + +.cardButton{ + width: 80px; + height: 80px; + margin-top: -25px; + background-size:cover; + +} + +.accept{ + background-image: url('/static/thumb-up.svg'); +} + +.reject{ + background-image: url('/static/thumb-down.svg'); + +} + +.bottomSection{ + width: 100%; + display: flex; + flex-direction: row; + color: white; +} + +@media screen and (max-width:700px) { + .bottomSection{ + flex-direction: column; + } + .allStickersSection{ + margin: 0px 0px 10px 0px !important; + width: calc(100% - 20px) !important; + } + .allLogosSection{ + margin: 0px !important; + width: calc(100% - 20px) !important; + } +} + +.containerDivider{ + margin: 5px 20px 10px 20px; + border-top: rgb(150,150,150) solid 1px; + border-bottom: none; +} + +.allStickersSection{ + background-color: rgb(30,30,30); + width: 50%; + margin: 0px 5px 0px 10px; + padding: 10px; + border-radius: 15px; +} + +.allLogosSection{ + background-color: rgb(30,30,30); + width: 50%; + margin: 0px 10px 0px 5px; + padding: 10px; + border-radius: 15px; +} + +.containerGrid{ + margin: 0px 20px 0px 20px; + width: calc(100% - 40px); + display: flex; + flex-wrap: wrap; + justify-content: flex-start; +} + +.gridItem{ + width: 130px; + height: 150px; + display: flex; + justify-content: center; + align-items: center; + border:rgb(112, 112, 112) solid; + margin-right: 3px; +} + +.gridItem:hover{ + background-color: rgb(71, 67, 67); +} + +.gridImage{ + width: 100px; +} + +.logoContainer{ + margin-left: 20px; + margin-right: 20px; +} + +.logoRow{ + display: flex; + flex-direction: row; + align-items: center; + margin-bottom: 10px; +} + +.logoIcon{ + width: 40px; + height: 40px; +} + +.logoLabel{ + margin-left: 20px; + font-size: 18px; +} + +.logoControls{ + margin-right: 0; + margin-left: auto; +} + +.logoControl{ + margin-left: 10px; +} + +.logoAddButton{ + width: 100%; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; + font-size: 20px; + cursor: pointer; +} + +.logoEditBackdrop{ + position: fixed; + top: 0px; + width: 100%; + height: 100%; + background-color: rgba(0,0,0,0.8); + display: none; + align-items: center; + justify-content: center; + opacity: 0; + transition: opacity 0.5s; +} + +.logoEdit{ + background-color: white; + display: flex; + flex-direction: column; + width: 80%; + max-width: 300px; + border-radius: 10px; + overflow: hidden; +} + +.logoEditTitle{ + margin: 15px; + font-size: 25px; + text-align: center; + font-weight: bold; +} + +.logoEditLabel{ + margin-left: 10px; + margin-top: 10px; +} + +.logoEditInput{ + margin-left: 10px; + margin-right: 10px; + height: 30px; + font-size: 20px; + border: none; + outline: none; + border-bottom: solid 0.5px rgb(0,0,0,0.5); +} + +.logoEditButton{ + display: flex; + justify-content: center; + margin-top: 20px; + height: 40px; + color: white; + font-size: 20px; + font-weight: bold; + align-items: center; + background-color: rgb(0, 140, 7); + cursor: pointer; } \ No newline at end of file diff --git a/static/auth.css b/static/css/auth.css similarity index 100% rename from static/auth.css rename to static/css/auth.css diff --git a/static/home.css b/static/css/home.css similarity index 58% rename from static/home.css rename to static/css/home.css index 8bd86d4..f149419 100644 --- a/static/home.css +++ b/static/css/home.css @@ -1,263 +1,393 @@ -*{ - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; -} - -body{ - margin: 0px; - height: 100%; -} - -.navBar{ - position: relative; - height: 80px; - display: flex; - flex-direction: row; - align-items: center; - z-index: 5; - box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.5); -} - -.addIcon{ - margin-right: 20px; - margin-left: auto; - transform: rotate(0deg); - transition: transform 0.2s; -} - -.addIconClose{ - transform: rotate(45deg); -} - -#map{ - height: calc(100% - 80px); - width: 100%; - z-index: 1; -} - -.addContainer{ - width: 100%; - height: 100%; - z-index: 2; - display: flex; - position: fixed; - flex-direction: column; - align-items: center; -} - -.addView{ - background-color: white; - position: fixed; - z-index: 2; - top: -500px; - width: 100%; - max-width: 500px; - display: flex; - flex-direction: column; - transition: top 0.5s; - border-radius: 0px 0px 20px 20px; -} - -.openView{ - top: 80px !important; -} - -.addViewTitle{ - font-size: 30px; - font-weight: bold; - padding-top: 10px; - padding-left: 20px; -} - -.locationContainer{ - background-color: rgba(0, 0, 0, 0.1); - margin-left: 20px; - margin-right: 20px; - margin-top: 15px; - border-radius: 15px; - - display: flex; - flex-direction: row; - align-items: center; -} - -.locationContainerSpinner{ - margin: 10px; - min-width: 40px; - display: none; -} - -/* location icon */ -@keyframes outerAnim { - from{ - transform: scale(1); - opacity: 1; - } - to{ - transform: scale(2.5); - opacity: 0; - } -} - -.locationIcon{ - margin: 10px; - min-width: 40px; -} - -.outerCircle{ - animation: outerAnim 1s infinite; - transform-origin: center; -} - -.locationContainerText{ - margin-top: 10px; - margin-bottom: 10px; - font-size: 15px; - color: rgb(100,100,100); - cursor: pointer; -} - -.addInputDes{ - margin-left: 20px; - margin-top: 5px; -} - -.addInput{ - margin-left: 20px; - margin-right: 20px; - margin-top: 3px; -} - -.addInput{ - display: none; -} - -.locationInputDes{ - display: none; -} - -.addImageInput{ - margin-left: 20px; - margin-top: 20px; - width: calc(100% - 48px); - height: 200px; - - display: flex; - align-items: center; - justify-content: center; - - border: 4px dashed rgba(0, 0, 0, 0.3); - background-color: white; - color: grey; - cursor: pointer; - transition: background-color 0.2s, color 0.2s, border 0.2s; -} - -.highlight{ - background-color: rgba(0, 0, 0, 0.6); - border: 4px solid rgba(255, 255, 255, 0.3); - color: white; -} - -.imageError{ - background-color: rgba(255, 0, 0, 0.6); - border: 4px solid rgba(255, 255, 255, 0.3); - color: white; -} - -.addImageTextHidden{ - display: none; -} - -.addImagePreview{ - display: none; - width: 100%; - height: 100%; - object-fit: cover; -} -.addImagePreviewShow{ - display: block !important; -} - -.addSubmitButton{ - margin-top: 10px; - width: 100%; - height: 40px; - align-items: center; - justify-content: center; - border-radius: 0px 0px 20px 20px; - display: flex; - color: white; - font-weight: bold; - cursor: pointer; -} - -.addLogoSelector{ - display: flex; - flex-direction: row; - - margin-left: 20px; - margin-top: 10px; - margin-right: 20px; - justify-content: center; -} - -.logoELement{ - margin-right: 5px; - padding: 5px; - border-radius: 5px; - transition: background-color 0.2s; -} - -.logoElementSelected{ - background-color: rgba(155, 155, 155, 0.329); -} - -.addSubmitButtonPressed{ - animation: submitPressed 2s infinite; -} - -.successView{ - background-color: white; - position: fixed; - z-index: 2; - top: -300px; - width: 100%; - max-width: 500px; - display: flex; - flex-direction: column; - transition: top 0.5s; - border-radius: 0px 0px 20px 20px; -} - -.successText{ - margin-top: 10px; - margin-left: 22px; - margin-right: 22px; - font-size: 14px; - color:rgb(49, 49, 49) -} - -.successEmail{ - margin-top: 15px; - margin-left: 20px; - margin-right: 20px; - margin-bottom: 10px; - - height: 40px; - font-size: 18px; - padding-left: 10px; - border-radius: 10px; - border: solid 1px rgba(200,200,200); - outline: none; - transition: border-color 0.2s; -} - -.invalid{ - border-color: rgb(248, 50, 50); -} - -.customDivIcon{ - background-color: white; - border-radius: 50%; - padding: 4px; -} \ No newline at end of file +*{ + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html { + overflow: hidden; + overscroll-behavior: none; +} + +body{ + /* margin: 0px; */ + height: 100%; +} + +.navBar{ + position: relative; + /* height: 80px; */ + height: 10vh; + display: flex; + flex-direction: row; + align-items: center; + z-index: 5; + box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.5); +} + +.addIcon{ + margin-right: 20px; + margin-left: auto; + transform: rotate(0deg); + transition: transform 0.2s; +} + +.addIconClose{ + transform: rotate(45deg); +} + +#map{ + height: calc(100% - 80px); + width: 100%; + z-index: 1; +} + +.addContainer{ + width: 100%; + height: 100%; + z-index: 2; + display: flex; + position: fixed; + flex-direction: column; + align-items: center; +} + +.addView{ + background-color: white; + position: fixed; + z-index: 2; + top: -500px; + width: 100%; + max-width: 500px; + display: flex; + flex-direction: column; + transition: top 0.5s; + border-radius: 0px 0px 20px 20px; +} + +.openView{ + top: 80px !important; +} + +.addViewTitle{ + font-size: 30px; + font-weight: bold; + padding-top: 10px; + padding-left: 20px; +} + +.locationContainer{ + background-color: rgba(0, 0, 0, 0.1); + margin-left: 20px; + margin-right: 20px; + margin-top: 15px; + border-radius: 15px; + + display: flex; + flex-direction: row; + align-items: center; +} + +.locationContainerSpinner{ + margin: 10px; + min-width: 40px; + display: none; +} + +/* location icon */ +@keyframes outerAnim { + from{ + transform: scale(1); + opacity: 1; + } + to{ + transform: scale(2.5); + opacity: 0; + } +} + +.locationIcon{ + margin: 10px; + min-width: 40px; +} + +.outerCircle{ + animation: outerAnim 1s infinite; + transform-origin: center; +} + +.locationContainerText{ + margin-top: 10px; + margin-bottom: 10px; + font-size: 15px; + color: rgb(100,100,100); + cursor: pointer; +} + +.addInputDes{ + margin-left: 20px; + margin-top: 5px; +} + +.addInput{ + margin-left: 20px; + margin-right: 20px; + margin-top: 3px; +} + +.addInput{ + display: none; +} + +.locationInputDes{ + display: none; +} + +.addImageInput{ + margin-left: 20px; + margin-top: 20px; + width: calc(100% - 48px); + height: 200px; + + display: flex; + align-items: center; + justify-content: center; + + border: 4px dashed rgba(0, 0, 0, 0.3); + background-color: white; + color: grey; + cursor: pointer; + transition: background-color 0.2s, color 0.2s, border 0.2s; +} + +.highlight{ + background-color: rgba(0, 0, 0, 0.6); + border: 4px solid rgba(255, 255, 255, 0.3); + color: white; +} + +.imageError{ + background-color: rgba(255, 0, 0, 0.6); + border: 4px solid rgba(255, 255, 255, 0.3); + color: white; +} + +.addImageTextHidden{ + display: none; +} + +.addImagePreview{ + display: none; + width: 100%; + height: 100%; + object-fit: cover; +} +.addImagePreviewShow{ + display: block !important; +} + +.addSubmitButton{ + margin-top: 10px; + width: 100%; + height: 40px; + align-items: center; + justify-content: center; + border-radius: 0px 0px 20px 20px; + display: flex; + color: white; + font-weight: bold; + cursor: pointer; +} + +.addLogoSelector{ + display: flex; + flex-direction: row; + + margin-left: 20px; + margin-top: 10px; + margin-right: 20px; + justify-content: center; +} + +.logoELement{ + margin-right: 5px; + padding: 5px; + border-radius: 5px; + transition: background-color 0.2s; +} + +.logoElementSelected{ + background-color: rgba(155, 155, 155, 0.329); +} + +.addSubmitButtonPressed{ + animation: submitPressed 2s infinite; +} + +.successView{ + background-color: white; + position: fixed; + z-index: 2; + top: -300px; + width: 100%; + max-width: 500px; + display: flex; + flex-direction: column; + transition: top 0.5s; + border-radius: 0px 0px 20px 20px; +} + +.successText{ + margin-top: 10px; + margin-left: 22px; + margin-right: 22px; + font-size: 14px; + color:rgb(49, 49, 49) +} + +.successEmail{ + margin-top: 15px; + margin-left: 20px; + margin-right: 20px; + margin-bottom: 10px; + + height: 40px; + font-size: 18px; + padding-left: 10px; + border-radius: 10px; + border: solid 1px rgba(200,200,200); + outline: none; + transition: border-color 0.2s; +} + +.invalid{ + border-color: rgb(248, 50, 50); +} + +.customDivIcon{ + background-color: white; + border-radius: 50%; + padding: 4px; +} + + +/* leaflet popup styling */ +.leaflet-popup-content-wrapper .leaflet-popup-content { + display: flex; + flex-direction: column; + /* justify-content: center; + align-items: center; */ +} + +.leaflet-popup-content-wrapper .leaflet-popup-content h1 { + text-align: center; + margin-bottom: 1rem; +} + +.leaflet-popup-content-wrapper .leaflet-popup-content img { + border-radius: 5px; +} + +.leafletMarkerButton { + background-color:#61518f; + color:#ffffff; + border: none; + border-radius: 5px; + padding: 0.5rem 0.5rem; + margin-top: 1rem; + margin-left: auto; + margin-right: auto; + width: 80%; +} + +/* Maybe add styling for when you hover or click a button? */ +.leafletMarkerButton:hover { + /* background-color:#be0202; */ +} + +/* nearYouMobileOverlay */ +#nearYouMobileOverlay { + position: fixed; + z-index: 999; + bottom: -90%; /* Initially at 10% visible */ + left: 0; + width: 100%; + height: 100%; /* Full screen height */ + background-color: rgb(255, 255, 255); + /* border: 5px solid #61518f; */ + transition: bottom 0.5s, border-radius 0.5s; /* Transition for sliding */ + transition-timing-function: 'ease-in'; + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + /* Enable scrolling if content exceeds screen height */ + border-radius: 30px 30px 0px 0px; + box-shadow: 0px -3px 5px rgba(0, 0, 0, 0.5); +} + +#nearYouMobileLine { + margin-top: 0.5rem; + width: 10%; +} + +#nearYouMobileTopText { + margin-top: 0.5rem; +} + +#nearYouMobileP { + padding: 0 2rem; + opacity: 0; + transition: opacity 0.8s; + transition-timing-function: 'ease-in'; +} + +/* sticker divs styling */ +.stickerDiv { + display: flex; + flex-direction: column; + align-items: center; + background-color: #ffffff; + transition: background-color 0.8s; + transition-timing-function: 'ease-in'; + margin-bottom: 2rem; + width: 80%; + border-radius: 5px; +} + +.stickerDiv h3 { + font-weight: 600; +} + +.stickerDiv .stickerDivImg { + width: 40%; + border-radius: 5px; +} + +.stickerDiv .stickerDivButton { + background-color:#61518f; + color:#ffffff; + border: none; + border-radius: 5px; + padding: 0.5rem 0.5rem; + margin-left: auto; + margin-right: auto; + margin-bottom: 0.5rem; + width: 30%; +} + +.stickerDiv.revealed { + background-color: #e0e0e0; +} + +.stickerDiv .stickerDivH1, .stickerDiv .stickerDivUser, .stickerDiv .stickerDivImg, .stickerDiv .stickerDivDate, .stickerDiv .stickerDivNearby, .stickerDiv .stickerDivButton { + opacity: 0; + transition: opacity 0.8s; + transition-timing-function: 'ease-in'; +} + +.stickerDiv.revealed .stickerDivH1, .stickerDiv.revealed .stickerDivUser, .stickerDiv.revealed .stickerDivImg, .stickerDiv.revealed .stickerDivDate, .stickerDiv.revealed .stickerDivNearby, .stickerDiv.revealed .stickerDivButton { + opacity: 1; +} + diff --git a/static/popUp.css b/static/css/popUp.css similarity index 93% rename from static/popUp.css rename to static/css/popUp.css index e5771e2..7341701 100644 --- a/static/popUp.css +++ b/static/css/popUp.css @@ -1,68 +1,68 @@ -.popUpContainer{ - position: fixed; - top: 0px; - width: 100%; - height: 100%; - background-color: rgba(0,0,0,0.8); - justify-content: center; - align-items: center; - display: none; - opacity: 0; - transition: opacity 0.5s; -} - -.showPopUp{ - opacity: 1; -} - -.popUpBox{ - width: 90%; - max-width: 400px; - background-color: white; - display: flex; - flex-direction: column; - align-items: center; - overflow: hidden; - - border-radius: 15px; - box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.8); -} - -.popUpTitle{ - font-size: 30px; - font-family: Arial; - padding: 20px; -} - -.popUpContent{ - width: 100%; - display: flex; - justify-content: center; -} - -.popUpDescription{ - padding-left: 30px; - padding-right: 30px; - font-size: 20px; - font-family: Arial; - text-align: center; -} - -.popUpButtons{ - display: flex; - flex-direction: row; - width: 100%; - min-height: 30px; -} - -.popUpButton{ - flex-grow: 1; - padding: 10px; - text-align: center; - margin: 20px; - border-radius: 10px; - font-family: Arial; - font-weight: bold; - cursor: pointer; -} - +.popUpContainer{ + position: fixed; + top: 0px; + width: 100%; + height: 100%; + background-color: rgba(0,0,0,0.8); + justify-content: center; + align-items: center; + display: none; + opacity: 0; + transition: opacity 0.5s; +} + +.showPopUp{ + opacity: 1; +} + +.popUpBox{ + width: 90%; + max-width: 400px; + background-color: white; + display: flex; + flex-direction: column; + align-items: center; + overflow: hidden; + + border-radius: 15px; + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.8); +} + +.popUpTitle{ + font-size: 30px; + font-family: Arial; + padding: 20px; +} + +.popUpContent{ + width: 100%; + display: flex; + justify-content: center; +} + +.popUpDescription{ + padding-left: 30px; + padding-right: 30px; + font-size: 20px; + font-family: Arial; + text-align: center; +} + +.popUpButtons{ + display: flex; + flex-direction: row; + width: 100%; + min-height: 30px; +} + +.popUpButton{ + flex-grow: 1; + padding: 10px; + text-align: center; + margin: 20px; + border-radius: 10px; + font-family: Arial; + font-weight: bold; + cursor: pointer; +} + diff --git a/static/favicon.ico b/static/favicon.ico deleted file mode 100644 index 8efe884..0000000 Binary files a/static/favicon.ico and /dev/null differ diff --git a/static/edit.svg b/static/img/edit.svg similarity index 100% rename from static/edit.svg rename to static/img/edit.svg diff --git a/static/img/favicon.ico b/static/img/favicon.ico new file mode 100644 index 0000000..0a29f61 Binary files /dev/null and b/static/img/favicon.ico differ diff --git a/static/img/favicon.svg b/static/img/favicon.svg new file mode 100644 index 0000000..03b20cd --- /dev/null +++ b/static/img/favicon.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/img/line.svg b/static/img/line.svg new file mode 100644 index 0000000..750aa39 --- /dev/null +++ b/static/img/line.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/logo.svg b/static/img/logo.svg similarity index 99% rename from static/logo.svg rename to static/img/logo.svg index b94bb59..01b5a58 100644 --- a/static/logo.svg +++ b/static/img/logo.svg @@ -1,37 +1,37 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/static/logo_compact_outline_wit.png b/static/img/logo_compact_outline_wit.png similarity index 100% rename from static/logo_compact_outline_wit.png rename to static/img/logo_compact_outline_wit.png diff --git a/static/img/marker.png b/static/img/marker.png new file mode 100644 index 0000000..72bd601 Binary files /dev/null and b/static/img/marker.png differ diff --git a/static/img/markerShadow.png b/static/img/markerShadow.png new file mode 100644 index 0000000..7ce117e Binary files /dev/null and b/static/img/markerShadow.png differ diff --git a/static/remove.svg b/static/img/remove.svg similarity index 100% rename from static/remove.svg rename to static/img/remove.svg diff --git a/static/thumb-down.svg b/static/img/thumb-down.svg similarity index 100% rename from static/thumb-down.svg rename to static/img/thumb-down.svg diff --git a/static/thumb-up.svg b/static/img/thumb-up.svg similarity index 100% rename from static/thumb-up.svg rename to static/img/thumb-up.svg diff --git a/static/js/UI.js b/static/js/UI.js new file mode 100644 index 0000000..b80fc38 --- /dev/null +++ b/static/js/UI.js @@ -0,0 +1,55 @@ +//Add view +var addIsOpen = false; +addIcon.addEventListener('click', function(){ + if(!addIsOpen){ + //Open add view + openAddView(); + addIsOpen = true; + //Change icon + addIcon.classList.add('addIconClose'); + } else { + //Close add view + closeAddView(); + closeSuccessView(); + addIsOpen = false; + addIcon.classList.remove('addIconClose'); + + } +}); +//Open view +function openAddView(){ + resetView(); + addView.classList.add('openView'); + setTimeout(function(){ + getLocation(); + }, 500); +} + +function closeAddView(){ + addView.classList.remove('openView'); +} + +function openSuccessView(){ + successView.classList.add('openView'); +} + +function closeSuccessView(){ + successView.classList.remove('openView'); + addIsOpen = false; + addIcon.classList.remove('addIconClose'); +} + +//Reset add view +function resetView(){ + imageText.classList.remove('addImageTextHidden'); + imagePreview.classList.remove('addImagePreviewShow'); + dropArea.classList.remove('invalid'); + latitudeInput.value = ""; + longitudeInput.value = ""; + latitudeInput.disabled = false; + longitudeInput.disabled = false; + setLocationContainer("", false); + imageFile = null; + submitButton.classList.remove('addSubmitButtonPressed'); + setManualLocationInput(false); +} \ No newline at end of file diff --git a/static/admin.js b/static/js/admin.js similarity index 96% rename from static/admin.js rename to static/js/admin.js index 8e2ed7c..11cc837 100644 --- a/static/admin.js +++ b/static/js/admin.js @@ -1,401 +1,401 @@ -const cardHolder = document.getElementsByClassName('cardHolder')[0]; -const logoContainer = document.getElementsByClassName('logoContainer')[0]; -const logoEditBackdrop = document.getElementsByClassName('logoEditBackdrop')[0]; -const logoEditName = document.getElementById('logoEditName'); -const logoEditColor = document.getElementById('logoEditColor'); -const logoEditButton = document.getElementsByClassName('logoEditButton')[0]; - - -var maxRotation = 30; -var maxRotationIn = 200; -var pivotOn = 100; -var maxCardsPerLoad = 5; - - -loadCards(); -loadLogos(); - -function loadCards(){ - //Check if there are 0 cards in the holder - if(cardHolder.children.length == 0){ - //Create request to server - var cardRequest = new XMLHttpRequest(); - var url = "getUnverifiedStickers?"; - cardRequest.open('GET', url); - cardRequest.onreadystatechange = function(){ - if(this.readyState == 4){ - if(this.status == 200){ - var results = JSON.parse(this.responseText); - var maxX = results.length; - if(maxX > 5){ - maxX = 5; - } - for(var x = 0; x < maxX;x++){ - createCard(results[x][0], results[x][4]); - } - } else { - //HANDLE ERRORS - alert(this.responseText); - } - } - } - //Send the request - cardRequest.send(); - } -} - -function createCard(id, imagesrc){ - //Create card - var card = document.createElement('div'); - card.classList.add('card'); - - //Create sticker image - var image = document.createElement('img'); - image.classList.add('stickerImage'); - image.alt = 'Image of sticker'; - image.src = imagesrc; - - //Create the button area - var buttonArea = document.createElement('div'); - buttonArea.classList.add('buttonArea'); - - //Create reject button - var rejectButton = document.createElement('div'); - rejectButton.classList.add("cardButton"); - rejectButton.classList.add("reject"); - rejectButton.addEventListener('click', function(){ - //Animate card - card.style.animation = "cardSwipeLeft 0.3s"; - setTimeout(function (){ - //Start reject function - rejectCard(); - }, 300); - }); - - //Create accept button - var acceptButton = document.createElement('div'); - acceptButton.classList.add("cardButton"); - acceptButton.classList.add("accept"); - acceptButton.addEventListener('click', function(){ - //Animate card - card.style.animation = "cardSwipeRight 0.3s"; - setTimeout(function (){ - //Start accept function - acceptCard(); - }, 300); - }); - - - card.appendChild(image); - card.appendChild(buttonArea); - buttonArea.appendChild(rejectButton); - buttonArea.appendChild(acceptButton); - - - //Touch actions - card.addEventListener('touchstart', onTouchStart); - card.addEventListener('touchmove', onTouchMove); - card.addEventListener('touchend', onTouchEnd); - card.addEventListener('mousedown', onTouchStart); - card.addEventListener('mousemove', onTouchMove); - card.addEventListener('mouseup', onTouchEnd); - - var startX, objectX = 0, dragging; - - function onTouchStart(e){ - e.preventDefault(); - card.style.transition = ""; - //Save start cursor position - if(e.pageX == null){ - startX = e.touches[0].pageX; - } else { - startX = e.pageX; - } - dragging = true; - } - - function onTouchMove(e){ - e.preventDefault(); - if(dragging == true){ - //Calculate difference X - if(e.pageX == null){ - deltaX = (startX - e.touches[0].pageX) * -1; - } else { - deltaX = (startX - e.pageX) * -1; - } - moveCard(deltaX); - } - } - - function onTouchEnd(e){ - dragging = false; - if(e.pageX == null){ - objectX -= (startX - e.changedTouches[0].pageX); - } else { - objectX -= (startX - e.pageX); - } - - moveCardOnDrop(); - } - - //Function to animate the card movement - function moveCard(deltaX){ - if(objectX + deltaX > (-1 * maxRotationIn) && objectX + deltaX < maxRotationIn){ - var rotation = maxRotation / maxRotationIn * (objectX + deltaX); - card.style.transform = "translate(" + (objectX + deltaX) + "px, 0) rotate(" + rotation + "deg)"; - var opacity = 1 / 200 * (objectX + deltaX); - if(opacity < 0){ - opacity *= -1; - } - card.style.opacity = 1 - opacity; - } - } - - function moveCardOnDrop(){ - if(!dragging){ - var acceptSate = 0; - if(objectX > pivotOn){ - acceptSate = 1; - } else if (objectX < (pivotOn * -1)){ - acceptSate = -1; - } - if(acceptSate == 0){ - //Move card back to center - objectX = 0; - card.style.transition = "transform 0.5s, opacity 0.5s"; - card.style.transform = "translate(0px,0px) rotate(0deg)"; - card.style.opacity = 1; - setTimeout(function(){ - card.style.transition = ''; - }, 500); - } else if (acceptSate == 1){ - //Move card to right and accept - objectX = 0; - card.style.transition = "transform 0.5s, opacity 0.5s"; - card.style.transform = "translate(200px,0px) rotate(30deg)"; - card.style.opacity = 0; - setTimeout(function(){ - card.style.transition = ''; - //Accept - acceptCard(); - }, 500); - } else if (acceptSate == -1){ - //Move card to right and accept - objectX = 0; - card.style.transition = "transform 0.5s, opacity 0.5s"; - card.style.transform = "translate(-200px,0px) rotate(-30deg)"; - card.style.opacity = 0; - setTimeout(function(){ - card.style.transition = ''; - //Decline - rejectCard(); - }, 500); - } - } - } - - function acceptCard(){ - //update sticker - var updateRequest = new XMLHttpRequest() - updateRequest.open('GET', '/setSticker?id=' + id + '&state=Verify'); - updateRequest.onreadystatechange = function(){ - if(this.readyState == 4 && this.status == 400){ - alert(this.responseText); - } - }; - updateRequest.send(); - if(cardHolder.children.length == 0){ - loadCards(); - } - card.remove(); - } - - function rejectCard(){ - //update sticker - var updateRequest = new XMLHttpRequest() - updateRequest.open('GET', '/setSticker?id=' + id + '&state=Reject'); - updateRequest.onreadystatechange = function(){ - if(this.readyState == 4 && this.status == 400){ - alert(this.responseText); - } - }; - updateRequest.send(); - //Check if new stickers have to be loaded - if(cardHolder.children.length == 0){ - loadCards(); - } - card.remove(); - } - cardHolder.appendChild(card); -} - -function loadLogos(){ - //Clear container - logoContainer.innerHTML = ""; - - //Retreive main logo - const logoRequest = new XMLHttpRequest(); - const logosRequest = new XMLHttpRequest(); - var logo; - //Get token - token = getCookieData('token'); - logoRequest.open('GET', '/static/logo.svg'); - logoRequest.onreadystatechange = function(){ - if(this.readyState == 4 && this.status == 200){ - logo = this.responseText; - logosRequest.send(); - } else if (this.readyState == 4 && this.status == 404){ - alert("Error: main logo svg not found..."); - } - } - logoRequest.send() - - //Retreive logo's from db - logosRequest.open('GET', '/logos?token='+token) - logosRequest.onreadystatechange = function(){ - if(this.readyState == 4 && this.status == 200){ - //Parse result to json - var logosJson = JSON.parse(this.responseText); - //Add logos to layout - for(var x = 0; x < logosJson.length; x++){ - let id = logosJson[x][0]; - let name = logosJson[x][1]; - let color = logosJson[x][2]; - //Create row - var logoRow = document.createElement('div'); - logoRow.classList.add('logoRow'); - //Create logo icon - var logoIcon = document.createElement('div'); - logoIcon.classList.add('logoIcon'); - logoIcon.innerHTML = logo.replaceAll('COLORPLACE', color); - //Create logo label - var logoLabel = document.createElement('span'); - logoLabel.classList.add('logoLabel'); - logoLabel.innerHTML = name; - //Create logo controls - var logoControls = document.createElement('div'); - logoControls.classList.add('logoControls'); - //Create remove button - var logoRemove = document.createElement('img'); - logoRemove.classList.add('logoControl'); - logoRemove.src = '/static/remove.svg'; - logoRemove.addEventListener('click', function(){ - var removePopUp = new PopUp(); - removePopUp.setTitle("Are you sure?"); - removePopUp.setDescription("Are you sure you want to delete this logo?"); - removePopUp.addButton("Yes", "rgb(0, 140, 7)", "white", function(){ - //Remove logo - var deleteLogoRequest = new XMLHttpRequest(); - deleteLogoRequest.open('DELETE', '/deleteLogo?id='+ id); - deleteLogoRequest.onreadystatechange = function(){ - if(this.readyState == 4 && this.status != 200){ - alert(this.responseText); - } - } - deleteLogoRequest.send(); - removePopUp.close(); - loadLogos(); - setTimeout(function(){ - removePopUp.remove(); - }, 500); - }) - removePopUp.addButton("No", "rgb(240, 0, 36)", "white", function(){ - removePopUp.close(); - setTimeout(function(){ - removePopUp.remove(); - }, 500); - }) - removePopUp.show(); - }); - //Create edit button - var logoEdit = document.createElement('img'); - logoEdit.classList.add('logoControl'); - logoEdit.src = '/static/edit.svg'; - logoEdit.addEventListener('click', function(){ - //Edit dialog - editLogo(id, name, color); - }); - //Add everything together - logoRow.appendChild(logoIcon); - logoRow.appendChild(logoLabel); - logoControls.appendChild(logoEdit); - logoControls.appendChild(logoRemove); - logoRow.appendChild(logoControls); - logoContainer.appendChild(logoRow); - } - //Add 'add logo' buttton - var logoAddButton = document.createElement('span'); - logoAddButton.classList.add('logoAddButton'); - logoAddButton.innerHTML = "Add logo"; - logoAddButton.addEventListener('click', function(){ - editLogo(); - }); - logoContainer.appendChild(logoAddButton); - } else if (this.readyState == 4 && this.status == 404){ - alert("Could not connect to API..."); - } - } -} - -function editLogo(id = null, name = "", color = ""){ - if(id == null){ - document.getElementsByClassName('logoEditTitle')[0].innerHTML = "Add logo"; - } - - logoEditBackdrop.style.display = "flex"; - logoEditName.value = name; - logoEditColor.value = color; - setTimeout(function(){ - logoEditBackdrop.style.opacity = "1"; - }, 10); - - logoEditButton.onclick = function(){ - //Check if the user wanted to create a new logo or edit one - if(id == null){ - //Create new logo - var logoAddRequest = new XMLHttpRequest(); - logoAddRequest.open('POST', '/addLogo?&name=' + logoEditName.value + "&color=" + encodeURIComponent(logoEditColor.value)); - logoAddRequest.onreadystatechange = function(){ - if(this.readyState == 4 && this.status == 200){ - //Logo added! - logoEditBackdrop.style.opacity = 0; - setTimeout(function(){ - logoEditBackdrop.style.display = 'none'; - }, 500); - loadLogos(); - } else if (this.readyState == 4){ - alert(this.responseText); - } - } - logoAddRequest.send(); - } else { - //Update logo - var logoUpdateRequest = new XMLHttpRequest(); - logoUpdateRequest.open('PATCH', '/editLogo?id=' + id + "&name=" + logoEditName.value + "&color=" + encodeURIComponent(logoEditColor.value)); - logoUpdateRequest.onreadystatechange = function(){ - if(this.readyState == 4 && this.status == 200){ - //Logo added! - logoEditBackdrop.style.opacity = 0; - setTimeout(function(){ - logoEditBackdrop.style.display = 'none'; - }, 500); - loadLogos(); - } else if (this.readyState == 4){ - alert(this.responseText); - } - } - logoUpdateRequest.send(); - } - } -} - -function getCookieData( name ) { - var pairs = document.cookie.split("; "), - count = pairs.length, parts; - while ( count-- ) { - parts = pairs[count].split("="); - if ( parts[0] === name ) - return parts[1]; - } - return false; +const cardHolder = document.getElementsByClassName('cardHolder')[0]; +const logoContainer = document.getElementsByClassName('logoContainer')[0]; +const logoEditBackdrop = document.getElementsByClassName('logoEditBackdrop')[0]; +const logoEditName = document.getElementById('logoEditName'); +const logoEditColor = document.getElementById('logoEditColor'); +const logoEditButton = document.getElementsByClassName('logoEditButton')[0]; + + +var maxRotation = 30; +var maxRotationIn = 200; +var pivotOn = 100; +var maxCardsPerLoad = 5; + + +loadCards(); +loadLogos(); + +function loadCards(){ + //Check if there are 0 cards in the holder + if(cardHolder.children.length == 0){ + //Create request to server + var cardRequest = new XMLHttpRequest(); + var url = "getUnverifiedStickers?"; + cardRequest.open('GET', url); + cardRequest.onreadystatechange = function(){ + if(this.readyState == 4){ + if(this.status == 200){ + var results = JSON.parse(this.responseText); + var maxX = results.length; + if(maxX > 5){ + maxX = 5; + } + for(var x = 0; x < maxX;x++){ + createCard(results[x][0], results[x][4]); + } + } else { + //HANDLE ERRORS + alert(this.responseText); + } + } + } + //Send the request + cardRequest.send(); + } +} + +function createCard(id, imagesrc){ + //Create card + var card = document.createElement('div'); + card.classList.add('card'); + + //Create sticker image + var image = document.createElement('img'); + image.classList.add('stickerImage'); + image.alt = 'Image of sticker'; + image.src = imagesrc; + + //Create the button area + var buttonArea = document.createElement('div'); + buttonArea.classList.add('buttonArea'); + + //Create reject button + var rejectButton = document.createElement('div'); + rejectButton.classList.add("cardButton"); + rejectButton.classList.add("reject"); + rejectButton.addEventListener('click', function(){ + //Animate card + card.style.animation = "cardSwipeLeft 0.3s"; + setTimeout(function (){ + //Start reject function + rejectCard(); + }, 300); + }); + + //Create accept button + var acceptButton = document.createElement('div'); + acceptButton.classList.add("cardButton"); + acceptButton.classList.add("accept"); + acceptButton.addEventListener('click', function(){ + //Animate card + card.style.animation = "cardSwipeRight 0.3s"; + setTimeout(function (){ + //Start accept function + acceptCard(); + }, 300); + }); + + + card.appendChild(image); + card.appendChild(buttonArea); + buttonArea.appendChild(rejectButton); + buttonArea.appendChild(acceptButton); + + + //Touch actions + card.addEventListener('touchstart', onTouchStart); + card.addEventListener('touchmove', onTouchMove); + card.addEventListener('touchend', onTouchEnd); + card.addEventListener('mousedown', onTouchStart); + card.addEventListener('mousemove', onTouchMove); + card.addEventListener('mouseup', onTouchEnd); + + var startX, objectX = 0, dragging; + + function onTouchStart(e){ + e.preventDefault(); + card.style.transition = ""; + //Save start cursor position + if(e.pageX == null){ + startX = e.touches[0].pageX; + } else { + startX = e.pageX; + } + dragging = true; + } + + function onTouchMove(e){ + e.preventDefault(); + if(dragging == true){ + //Calculate difference X + if(e.pageX == null){ + deltaX = (startX - e.touches[0].pageX) * -1; + } else { + deltaX = (startX - e.pageX) * -1; + } + moveCard(deltaX); + } + } + + function onTouchEnd(e){ + dragging = false; + if(e.pageX == null){ + objectX -= (startX - e.changedTouches[0].pageX); + } else { + objectX -= (startX - e.pageX); + } + + moveCardOnDrop(); + } + + //Function to animate the card movement + function moveCard(deltaX){ + if(objectX + deltaX > (-1 * maxRotationIn) && objectX + deltaX < maxRotationIn){ + var rotation = maxRotation / maxRotationIn * (objectX + deltaX); + card.style.transform = "translate(" + (objectX + deltaX) + "px, 0) rotate(" + rotation + "deg)"; + var opacity = 1 / 200 * (objectX + deltaX); + if(opacity < 0){ + opacity *= -1; + } + card.style.opacity = 1 - opacity; + } + } + + function moveCardOnDrop(){ + if(!dragging){ + var acceptSate = 0; + if(objectX > pivotOn){ + acceptSate = 1; + } else if (objectX < (pivotOn * -1)){ + acceptSate = -1; + } + if(acceptSate == 0){ + //Move card back to center + objectX = 0; + card.style.transition = "transform 0.5s, opacity 0.5s"; + card.style.transform = "translate(0px,0px) rotate(0deg)"; + card.style.opacity = 1; + setTimeout(function(){ + card.style.transition = ''; + }, 500); + } else if (acceptSate == 1){ + //Move card to right and accept + objectX = 0; + card.style.transition = "transform 0.5s, opacity 0.5s"; + card.style.transform = "translate(200px,0px) rotate(30deg)"; + card.style.opacity = 0; + setTimeout(function(){ + card.style.transition = ''; + //Accept + acceptCard(); + }, 500); + } else if (acceptSate == -1){ + //Move card to right and accept + objectX = 0; + card.style.transition = "transform 0.5s, opacity 0.5s"; + card.style.transform = "translate(-200px,0px) rotate(-30deg)"; + card.style.opacity = 0; + setTimeout(function(){ + card.style.transition = ''; + //Decline + rejectCard(); + }, 500); + } + } + } + + function acceptCard(){ + //update sticker + var updateRequest = new XMLHttpRequest() + updateRequest.open('GET', '/setSticker?id=' + id + '&state=Verify'); + updateRequest.onreadystatechange = function(){ + if(this.readyState == 4 && this.status == 400){ + alert(this.responseText); + } + }; + updateRequest.send(); + if(cardHolder.children.length == 0){ + loadCards(); + } + card.remove(); + } + + function rejectCard(){ + //update sticker + var updateRequest = new XMLHttpRequest() + updateRequest.open('GET', '/setSticker?id=' + id + '&state=Reject'); + updateRequest.onreadystatechange = function(){ + if(this.readyState == 4 && this.status == 400){ + alert(this.responseText); + } + }; + updateRequest.send(); + //Check if new stickers have to be loaded + if(cardHolder.children.length == 0){ + loadCards(); + } + card.remove(); + } + cardHolder.appendChild(card); +} + +function loadLogos(){ + //Clear container + logoContainer.innerHTML = ""; + + //Retreive main logo + const logoRequest = new XMLHttpRequest(); + const logosRequest = new XMLHttpRequest(); + var logo; + //Get token + token = getCookieData('token'); + logoRequest.open('GET', '/static/img/logo.svg'); + logoRequest.onreadystatechange = function(){ + if(this.readyState == 4 && this.status == 200){ + logo = this.responseText; + logosRequest.send(); + } else if (this.readyState == 4 && this.status == 404){ + alert("Error: main logo svg not found..."); + } + } + logoRequest.send() + + //Retreive logo's from db + logosRequest.open('GET', '/logos?token='+token) + logosRequest.onreadystatechange = function(){ + if(this.readyState == 4 && this.status == 200){ + //Parse result to json + var logosJson = JSON.parse(this.responseText); + //Add logos to layout + for(var x = 0; x < logosJson.length; x++){ + let id = logosJson[x][0]; + let name = logosJson[x][1]; + let color = logosJson[x][2]; + //Create row + var logoRow = document.createElement('div'); + logoRow.classList.add('logoRow'); + //Create logo icon + var logoIcon = document.createElement('div'); + logoIcon.classList.add('logoIcon'); + logoIcon.innerHTML = logo.replaceAll('COLORPLACE', color); + //Create logo label + var logoLabel = document.createElement('span'); + logoLabel.classList.add('logoLabel'); + logoLabel.innerHTML = name; + //Create logo controls + var logoControls = document.createElement('div'); + logoControls.classList.add('logoControls'); + //Create remove button + var logoRemove = document.createElement('img'); + logoRemove.classList.add('logoControl'); + logoRemove.src = '/static/img/remove.svg'; + logoRemove.addEventListener('click', function(){ + var removePopUp = new PopUp(); + removePopUp.setTitle("Are you sure?"); + removePopUp.setDescription("Are you sure you want to delete this logo?"); + removePopUp.addButton("Yes", "rgb(0, 140, 7)", "white", function(){ + //Remove logo + var deleteLogoRequest = new XMLHttpRequest(); + deleteLogoRequest.open('DELETE', '/deleteLogo?id='+ id); + deleteLogoRequest.onreadystatechange = function(){ + if(this.readyState == 4 && this.status != 200){ + alert(this.responseText); + } + } + deleteLogoRequest.send(); + removePopUp.close(); + loadLogos(); + setTimeout(function(){ + removePopUp.remove(); + }, 500); + }) + removePopUp.addButton("No", "rgb(240, 0, 36)", "white", function(){ + removePopUp.close(); + setTimeout(function(){ + removePopUp.remove(); + }, 500); + }) + removePopUp.show(); + }); + //Create edit button + var logoEdit = document.createElement('img'); + logoEdit.classList.add('logoControl'); + logoEdit.src = '/static/img/edit.svg'; + logoEdit.addEventListener('click', function(){ + //Edit dialog + editLogo(id, name, color); + }); + //Add everything together + logoRow.appendChild(logoIcon); + logoRow.appendChild(logoLabel); + logoControls.appendChild(logoEdit); + logoControls.appendChild(logoRemove); + logoRow.appendChild(logoControls); + logoContainer.appendChild(logoRow); + } + //Add 'add logo' buttton + var logoAddButton = document.createElement('span'); + logoAddButton.classList.add('logoAddButton'); + logoAddButton.innerHTML = "Add logo"; + logoAddButton.addEventListener('click', function(){ + editLogo(); + }); + logoContainer.appendChild(logoAddButton); + } else if (this.readyState == 4 && this.status == 404){ + alert("Could not connect to API..."); + } + } +} + +function editLogo(id = null, name = "", color = ""){ + if(id == null){ + document.getElementsByClassName('logoEditTitle')[0].innerHTML = "Add logo"; + } + + logoEditBackdrop.style.display = "flex"; + logoEditName.value = name; + logoEditColor.value = color; + setTimeout(function(){ + logoEditBackdrop.style.opacity = "1"; + }, 10); + + logoEditButton.onclick = function(){ + //Check if the user wanted to create a new logo or edit one + if(id == null){ + //Create new logo + var logoAddRequest = new XMLHttpRequest(); + logoAddRequest.open('POST', '/addLogo?&name=' + logoEditName.value + "&color=" + encodeURIComponent(logoEditColor.value)); + logoAddRequest.onreadystatechange = function(){ + if(this.readyState == 4 && this.status == 200){ + //Logo added! + logoEditBackdrop.style.opacity = 0; + setTimeout(function(){ + logoEditBackdrop.style.display = 'none'; + }, 500); + loadLogos(); + } else if (this.readyState == 4){ + alert(this.responseText); + } + } + logoAddRequest.send(); + } else { + //Update logo + var logoUpdateRequest = new XMLHttpRequest(); + logoUpdateRequest.open('PATCH', '/editLogo?id=' + id + "&name=" + logoEditName.value + "&color=" + encodeURIComponent(logoEditColor.value)); + logoUpdateRequest.onreadystatechange = function(){ + if(this.readyState == 4 && this.status == 200){ + //Logo added! + logoEditBackdrop.style.opacity = 0; + setTimeout(function(){ + logoEditBackdrop.style.display = 'none'; + }, 500); + loadLogos(); + } else if (this.readyState == 4){ + alert(this.responseText); + } + } + logoUpdateRequest.send(); + } + } +} + +function getCookieData( name ) { + var pairs = document.cookie.split("; "), + count = pairs.length, parts; + while ( count-- ) { + parts = pairs[count].split("="); + if ( parts[0] === name ) + return parts[1]; + } + return false; } \ No newline at end of file diff --git a/static/js/common.js b/static/js/common.js new file mode 100644 index 0000000..f06a5aa --- /dev/null +++ b/static/js/common.js @@ -0,0 +1,26 @@ +const addView = document.getElementsByClassName('addView')[0]; +const successView = document.getElementsByClassName('successView')[0]; +const imageText = document.getElementsByClassName('addImageText')[0]; +const imagePreview = document.getElementsByClassName('addImagePreview')[0]; +const dropArea = document.getElementsByClassName('addImageInput')[0]; +const latitudeInput = document.getElementById('latitudeInput'); +const longitudeInput = document.getElementById('longitudeInput'); +const locationContainerText = document.getElementsByClassName('locationContainerText')[0]; +const locationIcon = document.getElementsByClassName('locationIcon')[0]; +const locationContainerSpinner = document.getElementsByClassName('locationContainerSpinner')[0]; +const submitButton = document.getElementsByClassName('addSubmitButton')[0]; +const addLogoSelector = document.getElementsByClassName('addLogoSelector')[0]; +const addIcon = document.getElementsByClassName('addIcon')[0]; +const emailInput = document.getElementsByClassName('successEmail')[0]; + +const nearYouButton = document.getElementsByClassName('nearYouButton')[0]; + +var imageFile; +var selectedLogo; +var selectedLogoId; +var emailCode; + +var pointersOnMap = []; +var logoIcons = []; + +dayjs.extend(window.dayjs_plugin_relativeTime) \ No newline at end of file diff --git a/static/js/location.js b/static/js/location.js new file mode 100644 index 0000000..51f6fad --- /dev/null +++ b/static/js/location.js @@ -0,0 +1,79 @@ +// *****LOCATION +function getLocation(){ + //Get the permissions + setLocationContainer("Please grant location permission..."); + + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition(handleLocation, showError); + } else { + alert("Geolocation is not supported by this browser."); + } +} + +function handleLocation(position){ + //Set the values in the inputs + setLocationContainer("Loading location..."); + latitudeInput.value = position.coords.latitude; + longitudeInput.value = position.coords.longitude; + //Retrieve estimated address + var addressRequest = new XMLHttpRequest(); + addressRequest.onreadystatechange = function(){ + if(this.readyState == 4 && this.status == 200){ + var addressJson = JSON.parse(addressRequest.responseText); + var text = "Location: nearby "; + if(addressJson['address']['road'] != undefined){ + text += addressJson['address']['road']; + if(addressJson['address']['house_number'] != undefined){ + text += ' ' + addressJson['address']['house_number']; + } + } + text += "
Click to enter manually." + setLocationContainer(text, true); + } + } + console.log('https://nominatim.openstreetmap.org/reverse?lat=' + position.coords.latitude + '&lon=' + position.coords.longitude + '&format=json'); + addressRequest.open("GET", 'https://nominatim.openstreetmap.org/reverse?lat=' + position.coords.latitude + '&lon=' + position.coords.longitude + '&format=json', true); + addressRequest.send(); +} + +function showError(error) { + switch(error.code) { + case error.PERMISSION_DENIED: + setLocationContainer("Location permission denied, please enter manually."); + break; + case error.POSITION_UNAVAILABLE: + setLocationContainer("Location information is unavailable, please enter manually."); + break; + case error.TIMEOUT: + setLocationContainer("The request to get user location timed out, please enter manually."); + break; + case error.UNKNOWN_ERROR: + setLocationContainer("An unknown error occurred, please enter manually"); + break; + } +} + +function setLocationContainer(text, found=false){ + locationContainerText.innerHTML = text; + if(found == true){ + locationContainerSpinner.style.display = "none"; + locationIcon.style.display = "block"; + } else { + locationContainerSpinner.style.display = "block"; + locationIcon.style.display = "none"; + } +} + +function setManualLocationInput(state){ + if(state == true){ + latitudeInput.style.display = 'block'; + longitudeInput.style.display = 'block'; + document.getElementsByClassName('locationInputDes')[0].style.display = 'block'; + document.getElementsByClassName('locationInputDes')[1].style.display = 'block'; + } else { + latitudeInput.style.display = 'none'; + longitudeInput.style.display = 'none'; + document.getElementsByClassName('locationInputDes')[0].style.display = 'none'; + document.getElementsByClassName('locationInputDes')[1].style.display = 'none'; + } +} \ No newline at end of file diff --git a/static/js/main.js b/static/js/main.js new file mode 100644 index 0000000..445b04c --- /dev/null +++ b/static/js/main.js @@ -0,0 +1,108 @@ +// ****LOGO ADDER +//Load the logo for the add view and the icons +var logoSourceRequest = new XMLHttpRequest(); +logoSourceRequest.open('GET', 'static/img/logo.svg', true); +logoSourceRequest.onreadystatechange = function(){ + if(this.readyState == 4 && this.status == 200){ + //Get the logo + const logo = this.responseText; + // Make request to server to get all logos + var logoRequest = new XMLHttpRequest(); + logoRequest.open('GET', 'logos', true); + + logoRequest.onreadystatechange = function() { + if(this.readyState == 4 && this.status == 200){ + //Create JSON object of response + var logoJson = JSON.parse(this.responseText); + for(var i = 0; i < logoJson.length; i ++){ + const color = logoJson[i][2]; + const logoId = logoJson[i][0]; + const title = logoJson[i][1]; + //Create the object + const logoElement = document.createElement('div'); + logoElement.classList.add('logoElement'); + logoElement.innerHTML = logo.replaceAll('COLORPLACE', color); + logoElement.style.width = '50px'; + + //Handle selection + logoElement.addEventListener('click', function(){ + if(selectedLogo != null){ + selectedLogo.classList.remove('logoElementSelected'); + } + logoElement.classList.add('logoElementSelected'); + selectedLogo = logoElement; + selectedLogoId = logoId; + }); + + //Select if first one + if(i == 0){ + logoElement.classList.add('logoElementSelected'); + selectedLogo = logoElement; + selectedLogoId = logoId; + } + + addLogoSelector.appendChild(logoElement); + + //Create icon for map + const logoIcon = { + id: logoId, + icon: L.divIcon({ + className: 'customDivIcon', + html: logo.replaceAll('COLORPLACE', color), + iconSize: [40, 40], + iconAnchor: [24, 24] + }) + } + logoIcons.push(logoIcon); + } + //Logo's ready, map ready + updateMap(); + } + } + logoRequest.send(); + } +} +logoSourceRequest.send(); + + +//Update email +function updateEmail(){ + if(emailInput.value != ''){ + //Check if email is valid + if(checkEmail(emailInput.value)){ + //Update email + emailInput.classList.remove('invalid'); + var request = new XMLHttpRequest(); + request.open('PATCH', 'addEmail', true); + var formData = new FormData(); + formData.append('email', emailInput.value); + formData.append('token', emailCode); + request.onreadystatechange = function(){ + if(this.readyState == 4){ + if(this.status == 200){ + closeSuccessView(); + } else { + alert('Email could not be added :('); + closeSuccessView(); + } + } + } + request.send(formData); + } else { + //Email not valid, update UI + emailInput.classList.add('invalid'); + } + } else { + //No email added close success view + closeSuccessView() + } +} + +function checkEmail(email){ + if(email.includes('@') && email.includes('.')){ + return true; + } + else{ + return false; + } +} \ No newline at end of file diff --git a/static/js/map.js b/static/js/map.js new file mode 100644 index 0000000..c16921d --- /dev/null +++ b/static/js/map.js @@ -0,0 +1,121 @@ +// ***** MAP +// Create a map +var mymap = L.map('map').setView([52.087299, 5.165430], 13); + +// Give the map a source +L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + maxZoom: 19, + attribution: '© OpenStreetMap contributors' +}).addTo(mymap); + +// Update the pointers +mymap.on('moveend', updateMap); + +// Add a button for the current location +L.control.locate().addTo(mymap); + +// Update the map so it always renders the markers on page load +updateMap(); + +function updateMap() { + //Remove pointers that fell of the map + pointersOnMap = pointersOnMap.filter(function(pointer){ + if(pointer.lat < mymap.getBounds().getSouth() || pointer.lat > mymap.getBounds().getNorth() || pointer.lon < mymap.getBounds().getWest() || pointer.lon > mymap.getBounds().getEast()){ + mymap.removeLayer(pointer.pointer); + return false; + } + return true; + }); + //Add new pointers on the map + var request = new XMLHttpRequest(); + var url = 'getStickers?north=' + mymap.getBounds().getNorth(); + url += '&south=' + mymap.getBounds().getSouth(); + url += '&west=' + mymap.getBounds().getWest(); + url += '&east=' + mymap.getBounds().getEast(); + request.open('GET', url, true); + request.onreadystatechange = function(){ + if(this.readyState == 4){ + if(this.status == 200){ + var results = JSON.parse(this.responseText); + for(var x = 0; x < results.length; x++){ + //Check if the pointer already exists on map + var isNotOnMap = true; + for(var y = 0; y < pointersOnMap.length; y++){ + if(results[x][0] == pointersOnMap[y].id){ + isNotOnMap = false; + } + } + if(isNotOnMap){ + //Not on map, add the pointer + var stickyIcon = L.icon({ + iconUrl: '/static/img/marker.png', + shadowUrl: '/static/img/markerShadow.png', + iconSize: [38, 52], + shadowSize: [52, 52], + shadowAnchor: [19, 25], + }); + + const pointer = { + id: results[x][0], + lat: results[x][1], + lon: results[x][2], + pointer: L.marker([results[x][1], results[x][2]], {icon: stickyIcon}).addTo(mymap) + } + + //Add a popup + let spotText = ""; + if (results[x][7] === 1) { + spotText = "spot"; + } + else { + spotText = "spots"; + } + + pointer.pointer.bindPopup(` +

Sticker ${results[x][0]}

+

Sticked by ???

+ +

${results[x][7]} ${spotText}

+

Posted ${dayjs().to(dayjs(results[x][6]))}

+ `) + + pointer.pointer.on('popupopen', function (e) { + document.getElementById('spotButton-' + pointer.id).addEventListener('click', async (e) => { + + const button = document.getElementById('spotButton-' + pointer.id); + const stickerID = button.getAttribute('data-stickerID'); + + // Post to updateStickerSpots to update spots value for the sticker + try { + const response = await fetch('updateStickerSpots', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ stickerID: stickerID }) + }); + if (response.ok) { + console.log('Spot value updated successfully for sticker ID: ' + stickerID); + alert('Added a spot successfully!') + // Optionally, you can reload the map or perform any other action here + } else { + console.error('Failed to update spot value for sticker ID: ' + stickerID); + } + } catch (error) { + console.error('Error updating spot value:', error); + } + }); + }); + + + //Add pointer object to array + pointersOnMap.push(pointer); + } + } + } else { + console.error('Error while loading map pointers!'); + } + } + } + request.send(); +} \ No newline at end of file diff --git a/static/js/nearYouOverlay.js b/static/js/nearYouOverlay.js new file mode 100644 index 0000000..1f3f178 --- /dev/null +++ b/static/js/nearYouOverlay.js @@ -0,0 +1,323 @@ +// This is a popup in the middle of the screen: +// var Overlay = L.Class.extend({ +// // this is the constructor +// initialize: function (selector, options) { +// L.Util.setOptions(this, options); +// this._selector = selector; + +// // create overlay here +// this._overlayElement = document.createElement('div'); +// this._overlayElement.id = 'overlay'; +// this._overlayElement.style.position = 'fixed'; +// this._overlayElement.style.top = '25%'; +// this._overlayElement.style.left = '25%'; +// this._overlayElement.style.width = '50%'; +// this._overlayElement.style.height = '50%'; +// this._overlayElement.style.backgroundColor = this.options.background; +// this._overlayElement.style.opacity = '0'; +// this._overlayElement.style.transition = 'opacity 0.3s'; +// this._overlayElement.style.zIndex = '-1'; +// this._overlayElement.style.display = 'flex'; +// this._overlayElement.style.alignItems = 'center'; +// this._overlayElement.style.justifyContent = 'center'; +// document.body.appendChild(this._overlayElement); +// }, + +// // these are the default options +// options: { +// isActive: false, +// background: 'rgba(0, 0, 0, 1)', +// }, + +// // this is a public function +// toggle: function () { +// this.isActive = !this.isActive; +// if (this.isActive) { +// this._overlayElement.style.opacity = '1'; +// this._overlayElement.style.zIndex = '999'; +// } else { +// this._overlayElement.style.opacity = '0'; +// this._overlayElement.style.zIndex = '-1'; +// } +// }, +// }); + +// var overlay = new Overlay('#overlay', { background: 'rgba(0, 0, 0, 1)' }); + +// nearYouButton.addEventListener('click', function(){ +// console.log("near you clicked!"); +// overlay.toggle(); +// }); + +// nearYouMobileOverlay = document.createElement('div'); +// nearYouMobileOverlay.id = 'nearYouMobileOverlay'; +// document.body.appendChild(nearYouMobileOverlay); + +// Google maps style upwards drag overlay +var Overlay = L.Class.extend({ + // this is the constructor + initialize: function (selector, options) { + L.Util.setOptions(this, options); + this._selector = selector; + this._dragStartY = 0; + this._overlayHeight = 0; + + this._overlayElement = document.createElement('div'); + this._overlayElement.id = 'nearYouMobileOverlay'; + + this._line = document.createElement('img'); + this._line.id = 'nearYouMobileLine'; + this._line.src = "./static/img/line.svg"; + this._overlayElement.appendChild(this._line); + + this._nearYouTopText = document.createElement('h1'); + this._nearYouTopText.id = 'nearYouMobileTopText'; + this._nearYouTopTextText = document.createTextNode("Stickers near you"); + this._nearYouTopText.appendChild(this._nearYouTopTextText); + this._overlayElement.appendChild(this._nearYouTopText); + + for (let i = 0; i < 10; i++) { + this.createStickerDiv(i); + } + + document.body.appendChild(this._overlayElement); + + var self = this; + this._overlayElement.addEventListener('touchstart', function(e) { + self._dragStartY = e.touches[0].clientY; + self._overlayHeight = self._overlayElement.clientHeight; + }); + + this._overlayElement.addEventListener('touchmove', function(e) { + var deltaY = e.touches[0].clientY - self._dragStartY; + var newBottom = Math.max(-self._overlayHeight * 0.8, -deltaY); // Limit to 80% below + self._overlayElement.style.bottom = newBottom + 'px'; + if (self._overlayElement.scrollHeight - self._overlayElement.scrollTop === self._overlayElement.clientHeight) { + e.preventDefault(); // Prevent further scrolling + } + }); + + this._overlayElement.addEventListener('touchend', function(e) { + var snapThreshold = -self._overlayHeight * 0.3; // Snap when dragged beyond 30% of the overlay height + + // Under threshold -> snap back to the bottom + if (parseInt(self._overlayElement.style.bottom) < snapThreshold) { + self.closeOverlay(); + } + // Above threshold -> reveal the overlay + else { + self.openOverlay(); + } + }); + }, + + // Default options + options: { + isActive: false, + }, + + createStickerDiv: function (i) { + stickerDiv = document.createElement('div'); + stickerDiv.id = `stickerDiv-${i}`; + stickerDiv.classList.add('stickerDiv'); + + stickerDivH1 = document.createElement('h1'); + stickerDivH1.id = `stickerDivH1-${i}`; + stickerDivH1.classList.add('stickerDivH1'); + stickerDivH1Text = document.createTextNode(`stickerDivH1-${i}`); + stickerDivH1.appendChild(stickerDivH1Text); + stickerDiv.appendChild(stickerDivH1); + + stickerDivUser = document.createElement('h3'); + stickerDivUser.id = `stickerDivUser-${i}`; + stickerDivUser.classList.add('stickerDivUser'); + stickerDivUserText = document.createTextNode(`Sticked by ???`); + stickerDivUser.appendChild(stickerDivUserText); + stickerDiv.appendChild(stickerDivUser); + + stickerImg = document.createElement('img'); + stickerImg.id = `stickerDivImg-${i}`; + stickerImg.classList.add('stickerDivImg'); + stickerImg.src = ""; + stickerDiv.appendChild(stickerImg); + + stickerDivDate = document.createElement('h3'); + stickerDivDate.id = `stickerDivDate-${i}`; + stickerDivDate.classList.add('stickerDivDate'); + stickerDivDateText = document.createTextNode(`Posted ???`); + stickerDivDate.appendChild(stickerDivDateText); + stickerDiv.appendChild(stickerDivDate); + + stickerDivNearby = document.createElement('h3'); + stickerDivNearby.id = `stickerDivNearby-${i}`; + stickerDivNearby.classList.add('stickerDivNearby'); + stickerDivNearbyText = document.createTextNode(`Nearby ???`); + stickerDivNearby.appendChild(stickerDivNearbyText); + stickerDiv.appendChild(stickerDivNearby); + + stickerDivButton = document.createElement("button"); + stickerDivButton.id = `stickerDivButton-${i}`; + stickerDivButton.classList.add('stickerDivButton'); + stickerDivButton.innerHTML = "Open on map"; + stickerDiv.appendChild(stickerDivButton); + + this._overlayElement.appendChild(stickerDiv); + }, + + openOverlay: function () { + this.isActive = !this.isActive; + this._overlayElement.style.bottom = '0'; + this._overlayElement.style.borderRadius = '0px 0px 0px 0px'; + this._overlayElement.style.overflowY = 'auto'; + this._line.style.display = 'none'; + this.getNearYouData(); + }, + + closeOverlay: function () { + this.isActive = !this.isActive; + this._overlayElement.style.bottom = '-90%'; + this._overlayElement.style.borderRadius = '15px 15px 0px 0px'; + this._overlayElement.style.overflowY = 'hidden'; + this._overlayElement.scrollTo(0, 0); + this._line.style.display = 'block'; + let stickerDivs = document.querySelectorAll('.stickerDiv'); + stickerDivs.forEach(function(stickerDiv) { + stickerDiv.classList.remove('revealed'); + }); + }, + + getNearYouData: function () { + var self = this; + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition(function(position) { + self.requestDBStickers(position); + }, showError); + } else { + alert("Geolocation is not supported by this browser."); + } + }, + + requestDBStickers: function (position) { + var self = this; + + console.log("Your current position is:"); + console.log(`Latitude : ${position.coords.latitude}`); + console.log(`Longitude: ${position.coords.longitude}`); + + if (!position.coords.latitude || !position.coords.longitude) { + return; + } + + var request = new XMLHttpRequest(); + + var url = 'getNearYouStickers?'; + url += 'lon=' + position.coords.longitude; + url += '&lat=' + position.coords.latitude; + + request.open('GET', url, true); + request.onreadystatechange = function(){ + if(this.readyState == 4){ + if(this.status == 200){ + var results = JSON.parse(this.responseText); + let stickerDivs = document.querySelectorAll('.stickerDiv'); + let stickerDivH1s = document.querySelectorAll('.stickerDivH1'); + let stickerDivImgs = document.querySelectorAll('.stickerDivImg'); + let stickerDivDates = document.querySelectorAll('.stickerDivDate'); + let stickerDivButtons = document.querySelectorAll('.stickerDivButton'); + let stickerDivNearbys = document.querySelectorAll('.stickerDivNearby'); + + for(var i = 0; i < stickerDivH1s.length; i++) { + if (i < results.length) { + stickerDivH1s[i].textContent = `Sticker ${results[i][0]}`; + stickerDivImgs[i].src = results[i][4]; + stickerDivDates[i].textContent = `Posted ${dayjs().to(dayjs(results[i][6]))}` + + stickerDivButtons[i].dataset.id = results[i][0]; + stickerDivButtons[i].dataset.lat = results[i][1]; + stickerDivButtons[i].dataset.long = results[i][2]; + stickerDivButtons[i].addEventListener('click', function(){ + console.log("Button clicked!"); + self.closeOverlay(); + mymap.flyTo([this.getAttribute('data-lat'), this.getAttribute('data-long')], 18); + let stickerID = this.getAttribute('data-id'); + + // Recursive function to check if the pointer with the desired ID is present in the array + function checkPointerAndOpenPopup(stickerID) { + for (var y = 0; y < pointersOnMap.length; y++) { + if (stickerID == pointersOnMap[y].id) { + pointersOnMap[y].pointer.openPopup(); + return; // Exit the function if the pointer is found + } + } + // If the pointer is not found, schedule the function to run again after a short delay + setTimeout(function() { + checkPointerAndOpenPopup(stickerID); + }, 100); + } + + // Call the recursive function to start checking for the pointer with the desired ID + checkPointerAndOpenPopup(stickerID); + }); + + // Load nearby text (estimated address) + var stickerDivNearby = stickerDivNearbys[i]; + var addressRequest = new XMLHttpRequest(); + addressRequest.onreadystatechange = function(){ + if(this.readyState == 4 && this.status == 200){ + var addressJson = JSON.parse(addressRequest.responseText); + console.log(addressJson); + var text = "Nearby "; + if(addressJson['address']['road'] != undefined){ + text += addressJson['address']['road']; + if(addressJson['address']['house_number'] != undefined){ + text += ' ' + addressJson['address']['house_number']; + } + } + console.log(text); + stickerDivNearby.textContent = text; + } else { + // Error handling for non-200 status codes + + console.error('Request failed with status code ' + this.status + ', readystate: ' + this.readyState); + } + } + console.log('https://nominatim.openstreetmap.org/reverse?lat=' + results[i][1] + '&lon=' + results[i][2] + '&format=json'); + addressRequest.open("GET", 'https://nominatim.openstreetmap.org/reverse?lat=' + results[i][1] + '&lon=' + results[i][2] + '&format=json', true); + addressRequest.send(); + } + else { + // Don't show the div if the amount of divs (10) > amount of stickers + stickerDivs[i].style.display = 'none'; + } + // Show the divs + stickerDivs.forEach(function(stickerDiv, index) { + setTimeout(function() { + stickerDiv.classList.add('revealed'); + }, index * 400); + }); + } + } else { + console.error('Error while loading near you stickers!'); + } + } + } + request.send(); + }, + + toggle: function () { + this.isActive = !this.isActive; + if (this.isActive) { + this.openOverlay(); + } else { + this.closeOverlay(); + } + }, +}); + +var overlay = new Overlay('#overlay'); + +// nearYouButton.addEventListener('click', function(){ +// console.log("near you clicked!"); +// overlay.toggle(); +// }); + diff --git a/static/popUp.js b/static/js/popUp.js similarity index 97% rename from static/popUp.js rename to static/js/popUp.js index ce002b8..6de014b 100644 --- a/static/popUp.js +++ b/static/js/popUp.js @@ -1,83 +1,83 @@ -class PopUp{ - #popUpContainer; - #popUpBox; - #popUpTitle; - #popUpContent; - #popUpDescription; - #popUpButtons; - #zindex; - constructor(zindex, backgroundColor){ - this.#zindex = zindex; //Define zindex var - this.#createElements(); //Call the function that creates all the elements - } - - #createElements(){ - this.#popUpContainer = document.createElement('div'); //Create the container - this.#popUpContainer.classList.add('popUpContainer'); //Add class to the container - this.#popUpContainer.style.zIndex = this.#zindex; //Set the zindex - document.body.appendChild(this.#popUpContainer); //Append the container to documents body - - this.#popUpBox = document.createElement('div'); //Create the box - this.#popUpBox.classList.add('popUpBox'); //Add class to box - this.#popUpContainer.appendChild(this.#popUpBox); //Append the box to the container - - this.#popUpTitle = document.createElement('span'); //Create the title element - this.#popUpTitle.classList.add('popUpTitle'); //Add class to the title - this.#popUpBox.appendChild(this.#popUpTitle); //Append the title to the container - - this.#popUpContent = document.createElement('div'); //Create content box - this.#popUpContent.classList.add('popUpContent'); //Add class to content box - this.#popUpBox.appendChild(this.#popUpContent); //Append the box to the popUpBox - - this.#popUpDescription = document.createElement('span'); //Create description text - this.#popUpDescription.classList.add('popUpDescription'); //Add class to description text - this.#popUpContent.appendChild(this.#popUpDescription); //Append the text to the content box - - this.#popUpButtons = document.createElement('div'); //Create the container that holds the buttons - this.#popUpButtons.classList.add('popUpButtons'); //Add class to the container - this.#popUpBox.appendChild(this.#popUpButtons); //Append the button container to the popUpBox - } - - setTitle(title){ - this.#popUpTitle.innerHTML = title; - } - - setDescription(description){ - this.#popUpDescription.innerHTML = description; - } - - addButton(text, bgColor, textColor, action){ - let button = document.createElement('span'); //Create a button element - button.classList.add('popUpButton'); //Add class to button - button.innerHTML = text; //Add text to the button - button.style.backgroundColor = bgColor; //Add color to the background - button.style.color = textColor; //Add the text color - button.addEventListener('click', action); //Add the function to the button - this.#popUpButtons.appendChild(button); //Add the button to the button container - - } - - show(){ - let popUp = this.#popUpContainer; - popUp.style.display = 'flex'; - setTimeout(function(){ - popUp.classList.add('showPopUp'); - }, 10); - } - - close(){ - let popUp = this.#popUpContainer; - popUp.classList.remove('showPopUp'); - setTimeout(function(){ - popUp.style.display = 'none'; - }, 500); - } - - remove(){ - this.#popUpContainer.remove(); - } - - hide(){ - - } +class PopUp{ + #popUpContainer; + #popUpBox; + #popUpTitle; + #popUpContent; + #popUpDescription; + #popUpButtons; + #zindex; + constructor(zindex, backgroundColor){ + this.#zindex = zindex; //Define zindex var + this.#createElements(); //Call the function that creates all the elements + } + + #createElements(){ + this.#popUpContainer = document.createElement('div'); //Create the container + this.#popUpContainer.classList.add('popUpContainer'); //Add class to the container + this.#popUpContainer.style.zIndex = this.#zindex; //Set the zindex + document.body.appendChild(this.#popUpContainer); //Append the container to documents body + + this.#popUpBox = document.createElement('div'); //Create the box + this.#popUpBox.classList.add('popUpBox'); //Add class to box + this.#popUpContainer.appendChild(this.#popUpBox); //Append the box to the container + + this.#popUpTitle = document.createElement('span'); //Create the title element + this.#popUpTitle.classList.add('popUpTitle'); //Add class to the title + this.#popUpBox.appendChild(this.#popUpTitle); //Append the title to the container + + this.#popUpContent = document.createElement('div'); //Create content box + this.#popUpContent.classList.add('popUpContent'); //Add class to content box + this.#popUpBox.appendChild(this.#popUpContent); //Append the box to the popUpBox + + this.#popUpDescription = document.createElement('span'); //Create description text + this.#popUpDescription.classList.add('popUpDescription'); //Add class to description text + this.#popUpContent.appendChild(this.#popUpDescription); //Append the text to the content box + + this.#popUpButtons = document.createElement('div'); //Create the container that holds the buttons + this.#popUpButtons.classList.add('popUpButtons'); //Add class to the container + this.#popUpBox.appendChild(this.#popUpButtons); //Append the button container to the popUpBox + } + + setTitle(title){ + this.#popUpTitle.innerHTML = title; + } + + setDescription(description){ + this.#popUpDescription.innerHTML = description; + } + + addButton(text, bgColor, textColor, action){ + let button = document.createElement('span'); //Create a button element + button.classList.add('popUpButton'); //Add class to button + button.innerHTML = text; //Add text to the button + button.style.backgroundColor = bgColor; //Add color to the background + button.style.color = textColor; //Add the text color + button.addEventListener('click', action); //Add the function to the button + this.#popUpButtons.appendChild(button); //Add the button to the button container + + } + + show(){ + let popUp = this.#popUpContainer; + popUp.style.display = 'flex'; + setTimeout(function(){ + popUp.classList.add('showPopUp'); + }, 10); + } + + close(){ + let popUp = this.#popUpContainer; + popUp.classList.remove('showPopUp'); + setTimeout(function(){ + popUp.style.display = 'none'; + }, 500); + } + + remove(){ + this.#popUpContainer.remove(); + } + + hide(){ + + } } \ No newline at end of file diff --git a/static/js/uploadImage.js b/static/js/uploadImage.js new file mode 100644 index 0000000..370a5f9 --- /dev/null +++ b/static/js/uploadImage.js @@ -0,0 +1,111 @@ +// *****IMAGE UPLOAD +function onClickImage(){ + let input = document.createElement('input'); + input.type = 'file'; + input.accept = 'image/*'; + + input.onchange = _ => { + imageFile = Array.from(input.files)[0]; + previewImage(); + input.remove(); + }; + input.style.display = 'none'; + document.body.appendChild(input); + input.click(); +} +//Drop image +//Prevent default behavior +['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { + dropArea.addEventListener(eventName, preventDefaults, false); +}); +function preventDefaults (e) { + e.preventDefault(); + e.stopPropagation(); +} + +//Change the look of the box when you hover with a file +['dragenter', 'dragover'].forEach(eventName => { + dropArea.addEventListener(eventName, highlight, false); +}); + +['dragleave', 'drop'].forEach(eventName => { + dropArea.addEventListener(eventName, unhighlight, false); +}); + +function highlight(e) { + dropArea.classList.add('highlight'); +} + +function unhighlight(e) { + dropArea.classList.remove('highlight'); +} + +//Handle drop +dropArea.addEventListener('drop', handleDrop, false); + +function handleDrop(e) { + let dt = e.dataTransfer; + imageFile = dt.files[0]; + previewImage(); +} + +//Preview Image +function previewImage(){ + let reader = new FileReader() + reader.readAsDataURL(imageFile) + reader.onloadend = function() { + //Hide the text + imageText.classList.add('addImageTextHidden'); + //Set image + imagePreview.src = reader.result; + //Show image element + imagePreview.classList.add('addImagePreviewShow'); + } +} + +//Upload function +function submit(){ + latitudeInput.disabled = true; + longitudeInput.disabled = true; + submitButton.classList.add('addSubmitButtonPressed'); + + //Create request + var request = new XMLHttpRequest(); + request.open('POST', 'upload', true); + //request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + + //Handle response + request.onreadystatechange = function() { + if(this.readyState == 4){ + if(this.status == 200){ + dropArea.classList.remove('invalid'); + const response = this.responseText; + closeAddView(); + setTimeout(function(){ + openSuccessView(); + emailCode = JSON.parse(response)['emailCode']; + }, 500); + } else { + if(JSON.parse(this.responseText)['error'] == "You must upload a picture."){ + dropArea.classList.add('invalid'); + } else if (JSON.parse(this.responseText)['error'] == "Unsupported file type."){ + dropArea.classList.add('invalid'); + alert('File type not supported.'); + } else { + alert("Error: " + this.responseText); + } + latitudeInput.disabled = false; + longitudeInput.disabled = false; + submitButton.classList.remove('addSubmitButtonPressed'); + } + } + } + + //Send request + var formdata = new FormData(); + formdata.append('image', imageFile); + formdata.append('lat', latitudeInput.value); + formdata.append('lon', longitudeInput.value); + formdata.append('logoId', selectedLogoId); + request.send(formdata); +} \ No newline at end of file diff --git a/static/main.js b/static/main.js deleted file mode 100644 index 23ea8c9..0000000 --- a/static/main.js +++ /dev/null @@ -1,441 +0,0 @@ -const addView = document.getElementsByClassName('addView')[0]; -const successView = document.getElementsByClassName('successView')[0]; -const imageText = document.getElementsByClassName('addImageText')[0]; -const imagePreview = document.getElementsByClassName('addImagePreview')[0]; -const dropArea = document.getElementsByClassName('addImageInput')[0]; -const latitudeInput = document.getElementById('latitudeInput'); -const longitudeInput = document.getElementById('longitudeInput'); -const locationContainerText = document.getElementsByClassName('locationContainerText')[0]; -const locationIcon = document.getElementsByClassName('locationIcon')[0]; -const locationContainerSpinner = document.getElementsByClassName('locationContainerSpinner')[0]; -const submitButton = document.getElementsByClassName('addSubmitButton')[0]; -const addLogoSelector = document.getElementsByClassName('addLogoSelector')[0]; -const addIcon = document.getElementsByClassName('addIcon')[0]; -const emailInput = document.getElementsByClassName('successEmail')[0]; - -var imageFile; -var selectedLogo; -var selectedLogoId; -var emailCode; - -var pointersOnMap = []; -var logoIcons = []; - -// ****LOGO ADDER -//Load the logo for the add view and the icons -var logoSourceRequest = new XMLHttpRequest(); -logoSourceRequest.open('GET', 'static/logo.svg', true); -logoSourceRequest.onreadystatechange = function(){ - if(this.readyState == 4 && this.status == 200){ - //Get the logo - const logo = this.responseText; - // Make request to server to get all logos - var logoRequest = new XMLHttpRequest(); - logoRequest.open('GET', 'logos', true); - - logoRequest.onreadystatechange = function() { - if(this.readyState == 4 && this.status == 200){ - //Create JSON object of response - var logoJson = JSON.parse(this.responseText); - for(var i = 0; i < logoJson.length; i ++){ - const color = logoJson[i][2]; - const logoId = logoJson[i][0]; - const title = logoJson[i][1]; - //Create the object - const logoElement = document.createElement('div'); - logoElement.classList.add('logoElement'); - logoElement.innerHTML = logo.replaceAll('COLORPLACE', color); - logoElement.style.width = '50px'; - - //Handle selection - logoElement.addEventListener('click', function(){ - if(selectedLogo != null){ - selectedLogo.classList.remove('logoElementSelected'); - } - logoElement.classList.add('logoElementSelected'); - selectedLogo = logoElement; - selectedLogoId = logoId; - }); - - //Select if first one - if(i == 0){ - logoElement.classList.add('logoElementSelected'); - selectedLogo = logoElement; - selectedLogoId = logoId; - } - - addLogoSelector.appendChild(logoElement); - - //Create icon for map - const logoIcon = { - id: logoId, - icon: L.divIcon({ - className: 'customDivIcon', - html: logo.replaceAll('COLORPLACE', color), - iconSize: [40, 40], - iconAnchor: [24, 24] - }) - } - logoIcons.push(logoIcon); - } - //Logo's ready, map ready - updateMap(); - } - } - logoRequest.send(); - } -} -logoSourceRequest.send(); - -// ***** MAP -//Create a map -var mymap = L.map('map').setView([52.087299, 5.165430], 13); - -//Give the map a source -L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { - maxZoom: 19, - attribution: '© OpenStreetMap contributors' -}).addTo(mymap); - -//Update the pointers -mymap.on('moveend', updateMap); - -function updateMap() { - //Remove pointers that fell of the map - pointersOnMap = pointersOnMap.filter(function(pointer){ - if(pointer.lat < mymap.getBounds().getSouth() || pointer.lat > mymap.getBounds().getNorth() || pointer.lon < mymap.getBounds().getWest() || pointer.lon > mymap.getBounds().getEast()){ - mymap.removeLayer(pointer.pointer); - return false; - } - return true; - }); - //Add new pointers on the map - var request = new XMLHttpRequest(); - var url = 'getStickers?north=' + mymap.getBounds().getNorth(); - url += '&south=' + mymap.getBounds().getSouth(); - url += '&west=' + mymap.getBounds().getWest(); - url += '&east=' + mymap.getBounds().getEast(); - request.open('GET', url, true); - request.onreadystatechange = function(){ - if(this.readyState == 4){ - if(this.status == 200){ - var results = JSON.parse(this.responseText); - for(var x = 0; x < results.length; x++){ - //Check if the pointer already exists on map - var isNotOnMap = true; - for(var y = 0; y < pointersOnMap.length; y++){ - if(results[x][0] == pointersOnMap[y].id){ - isNotOnMap = false; - } - } - if(isNotOnMap){ - //Not on map, add the pointer - const pointer = { - id: results[x][0], - lat: results[x][1], - lon: results[x][2], - pointer: L.marker([results[x][1], results[x][2]], {icon: logoIcons.filter(logo => logo.id == results[x][5])[0].icon}).addTo(mymap) - } - //Add a popup - pointer.pointer.bindPopup(""); - //Add pointer object to array - pointersOnMap.push(pointer); - } - } - } else { - console.error('Error while loading map pointers!'); - } - } - } - request.send(); -} - - - -//Add view -var addIsOpen = false; -addIcon.addEventListener('click', function(){ - if(!addIsOpen){ - //Open add view - openAddView(); - addIsOpen = true; - //Change icon - addIcon.classList.add('addIconClose'); - } else { - //Close add view - closeAddView(); - closeSuccessView(); - addIsOpen = false; - addIcon.classList.remove('addIconClose'); - - } -}); -//Open view -function openAddView(){ - resetView(); - addView.classList.add('openView'); - setTimeout(function(){ - getLocation(); - }, 500); -} - -function closeAddView(){ - addView.classList.remove('openView'); -} - -function openSuccessView(){ - successView.classList.add('openView'); -} - -function closeSuccessView(){ - successView.classList.remove('openView'); - addIsOpen = false; - addIcon.classList.remove('addIconClose'); -} - -//Reset add view -function resetView(){ - imageText.classList.remove('addImageTextHidden'); - imagePreview.classList.remove('addImagePreviewShow'); - dropArea.classList.remove('invalid'); - latitudeInput.value = ""; - longitudeInput.value = ""; - latitudeInput.disabled = false; - longitudeInput.disabled = false; - setLocationContainer("", false); - imageFile = null; - submitButton.classList.remove('addSubmitButtonPressed'); - setManualLocationInput(false); -} - -// *****LOCATION -function getLocation(){ - //Get the permissions - setLocationContainer("Please grant location permission..."); - if (navigator.geolocation) { - navigator.geolocation.getCurrentPosition(handleLocation, showError); - } else { - alert("Geolocation is not supported by this browser."); - } -} - -function handleLocation(position){ - //Set the values in the inputs - setLocationContainer("Loading location..."); - latitudeInput.value = position.coords.latitude; - longitudeInput.value = position.coords.longitude; - //Retrieve estimated address - var addressRequest = new XMLHttpRequest(); - addressRequest.onreadystatechange = function(){ - if(this.readyState == 4 && this.status == 200){ - var addressJson = JSON.parse(addressRequest.responseText); - var text = "Location: nearby "; - if(addressJson['address']['road'] != undefined){ - text += addressJson['address']['road']; - if(addressJson['address']['house_number'] != undefined){ - text += ' ' + addressJson['address']['house_number']; - } - } - text += "
Click to enter manually." - setLocationContainer(text, true); - } - } - addressRequest.open("GET", 'https://nominatim.openstreetmap.org/reverse?lat=' + position.coords.latitude + '&lon=' + position.coords.longitude + '&format=json', true); - addressRequest.send(); -} - -function showError(error) { - switch(error.code) { - case error.PERMISSION_DENIED: - setLocationContainer("Location permission denied, please enter manually."); - break; - case error.POSITION_UNAVAILABLE: - setLocationContainer("Location information is unavailable, please enter manually."); - break; - case error.TIMEOUT: - setLocationContainer("The request to get user location timed out, please enter manually."); - break; - case error.UNKNOWN_ERROR: - setLocationContainer("An unknown error occurred, please enter manually"); - break; - } -} - -function setLocationContainer(text, found=false){ - locationContainerText.innerHTML = text; - if(found == true){ - locationContainerSpinner.style.display = "none"; - locationIcon.style.display = "block"; - } else { - locationContainerSpinner.style.display = "block"; - locationIcon.style.display = "none"; - } -} - -function setManualLocationInput(state){ - if(state == true){ - latitudeInput.style.display = 'block'; - longitudeInput.style.display = 'block'; - document.getElementsByClassName('locationInputDes')[0].style.display = 'block'; - document.getElementsByClassName('locationInputDes')[1].style.display = 'block'; - } else { - latitudeInput.style.display = 'none'; - longitudeInput.style.display = 'none'; - document.getElementsByClassName('locationInputDes')[0].style.display = 'none'; - document.getElementsByClassName('locationInputDes')[1].style.display = 'none'; - } -} - -// *****IMAGE UPLOAD -function onClickImage(){ - let input = document.createElement('input'); - input.type = 'file'; - input.accept = 'image/*'; - - input.onchange = _ => { - imageFile = Array.from(input.files)[0]; - previewImage(); - input.remove(); - }; - input.style.display = 'none'; - document.body.appendChild(input); - input.click(); -} -//Drop image -//Prevent default behavior -['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { - dropArea.addEventListener(eventName, preventDefaults, false); -}); -function preventDefaults (e) { - e.preventDefault(); - e.stopPropagation(); -} - -//Change the look of the box when you hover with a file -['dragenter', 'dragover'].forEach(eventName => { - dropArea.addEventListener(eventName, highlight, false); -}); - -['dragleave', 'drop'].forEach(eventName => { - dropArea.addEventListener(eventName, unhighlight, false); -}); - -function highlight(e) { - dropArea.classList.add('highlight'); -} - -function unhighlight(e) { - dropArea.classList.remove('highlight'); -} - -//Handle drop -dropArea.addEventListener('drop', handleDrop, false); - -function handleDrop(e) { - let dt = e.dataTransfer; - imageFile = dt.files[0]; - previewImage(); -} - -//Preview Image -function previewImage(){ - let reader = new FileReader() - reader.readAsDataURL(imageFile) - reader.onloadend = function() { - //Hide the text - imageText.classList.add('addImageTextHidden'); - //Set image - imagePreview.src = reader.result; - //Show image element - imagePreview.classList.add('addImagePreviewShow'); - } -} - -//Upload function -function submit(){ - latitudeInput.disabled = true; - longitudeInput.disabled = true; - submitButton.classList.add('addSubmitButtonPressed'); - - //Create request - var request = new XMLHttpRequest(); - request.open('POST', 'upload', true); - //request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); - - //Handle response - request.onreadystatechange = function() { - if(this.readyState == 4){ - if(this.status == 200){ - dropArea.classList.remove('invalid'); - const response = this.responseText; - closeAddView(); - setTimeout(function(){ - openSuccessView(); - emailCode = JSON.parse(response)['emailCode']; - }, 500); - } else { - if(JSON.parse(this.responseText)['error'] == "You must upload a picture."){ - dropArea.classList.add('invalid'); - } else if (JSON.parse(this.responseText)['error'] == "Unsupported file type."){ - dropArea.classList.add('invalid'); - alert('File type not supported.'); - } else { - alert("Error: " + this.responseText); - } - latitudeInput.disabled = false; - longitudeInput.disabled = false; - submitButton.classList.remove('addSubmitButtonPressed'); - } - } - } - - //Send request - var formdata = new FormData(); - formdata.append('image', imageFile); - formdata.append('lat', latitudeInput.value); - formdata.append('lon', longitudeInput.value); - formdata.append('logoId', selectedLogoId); - request.send(formdata); -} - -//Update email -function updateEmail(){ - if(emailInput.value != ''){ - //Check if email is valid - if(checkEmail(emailInput.value)){ - //Update email - emailInput.classList.remove('invalid'); - var request = new XMLHttpRequest(); - request.open('PATCH', 'addEmail', true); - var formData = new FormData(); - formData.append('email', emailInput.value); - formData.append('token', emailCode); - request.onreadystatechange = function(){ - if(this.readyState == 4){ - if(this.status == 200){ - closeSuccessView(); - } else { - alert('Email could not be added :('); - closeSuccessView(); - } - } - } - request.send(formData); - } else { - //Email not valid, update UI - emailInput.classList.add('invalid'); - } - } else { - //No email added close success view - closeSuccessView() - } -} - -function checkEmail(email){ - if(email.includes('@') && email.includes('.')){ - return true; - } - else{ - return false; - } -} diff --git a/static/uploads/972CA94D-8DD5-41EC-B7C4-4B2CA8C1BE30.jpeg b/static/uploads/972CA94D-8DD5-41EC-B7C4-4B2CA8C1BE30.jpeg deleted file mode 100644 index 9b689ef..0000000 Binary files a/static/uploads/972CA94D-8DD5-41EC-B7C4-4B2CA8C1BE30.jpeg and /dev/null differ diff --git a/static/uploads/First_Lego_League_2018-scaled1.jpg b/static/uploads/First_Lego_League_2018-scaled1.jpg deleted file mode 100644 index 1a5189d..0000000 Binary files a/static/uploads/First_Lego_League_2018-scaled1.jpg and /dev/null differ diff --git a/static/uploads/dog11.jpg b/static/uploads/dog11.jpg deleted file mode 100644 index 4950555..0000000 Binary files a/static/uploads/dog11.jpg and /dev/null differ diff --git a/static/uploads/dog12.jpg b/static/uploads/dog12.jpg deleted file mode 100644 index 4950555..0000000 Binary files a/static/uploads/dog12.jpg and /dev/null differ diff --git a/stickers.db b/stickers.db deleted file mode 100644 index edc9947..0000000 Binary files a/stickers.db and /dev/null differ diff --git a/templates/admin.html b/templates/admin.html index 9e29b67..39c128a 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -5,11 +5,11 @@ Sticker Map Admin - - - - - + + + + +