mirror of
https://github.com/Fluffy-Bean/GameExpo23.git
synced 2025-05-14 14:22:16 +00:00
Merge remote-tracking branch 'origin/main'
# Conflicts: # .gitignore # TFR/server/api.py # TFR/server/config.py # TFR/server/static/gen/scripts.js # TFR/server/static/gen/styles.css # TFR/server/static/sass/button.sass # TFR/server/static/sass/style.sass # TFR/server/templates/account.html # TFR/server/templates/base.html # TFR/server/templates/scores.html # TFR/server/views.py
This commit is contained in:
commit
c68c2954a5
16 changed files with 326 additions and 146 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@
|
||||||
|
|
||||||
.uuid
|
.uuid
|
||||||
.env
|
.env
|
||||||
|
/.env
|
||||||
|
|
1
TFR/.gitignore
vendored
1
TFR/.gitignore
vendored
|
@ -161,3 +161,4 @@ cython_debug/
|
||||||
# remove development files
|
# remove development files
|
||||||
/storage
|
/storage
|
||||||
/logs
|
/logs
|
||||||
|
/server/static/gen/
|
||||||
|
|
|
@ -4,17 +4,16 @@ from flask import Flask, render_template, abort
|
||||||
from flask_assets import Bundle
|
from flask_assets import Bundle
|
||||||
from werkzeug.exceptions import HTTPException
|
from werkzeug.exceptions import HTTPException
|
||||||
|
|
||||||
from server.config import MIGRATION_DIR, INSTANCE_DIR
|
|
||||||
from server.extensions import db, migrate, cache, assets, login_manager
|
from server.extensions import db, migrate, cache, assets, login_manager
|
||||||
from server.models import Users
|
from server.models import Users
|
||||||
from server import views, auth, api
|
from server import views, auth, api
|
||||||
|
|
||||||
|
|
||||||
app = Flask(__name__, instance_path=INSTANCE_DIR)
|
app = Flask(__name__)
|
||||||
app.config.from_pyfile("config.py")
|
app.config.from_pyfile("config.py")
|
||||||
|
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
migrate.init_app(app, db, directory=MIGRATION_DIR)
|
migrate.init_app(app, db)
|
||||||
|
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
db.create_all()
|
db.create_all()
|
||||||
|
|
|
@ -3,9 +3,9 @@ import shortuuid
|
||||||
from flask import Blueprint, request, jsonify
|
from flask import Blueprint, request, jsonify
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
|
|
||||||
from server.models import Tokens, Scores
|
from server.models import Tokens, Scores, Users
|
||||||
from server.extensions import db
|
from server.extensions import db
|
||||||
from server.config import GAME_VERSION, GAME_VERSIONS, GAME_DIFFICULTIES, USER_MAX_TOKENS
|
from server.config import GAME_VERSION, GAME_VERSIONS, GAME_DIFFICULTIES, USER_MAX_TOKENS, MAX_SEARCH_RESULTS
|
||||||
|
|
||||||
|
|
||||||
blueprint = Blueprint("api", __name__, url_prefix="/api")
|
blueprint = Blueprint("api", __name__, url_prefix="/api")
|
||||||
|
@ -83,3 +83,15 @@ def post():
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return "Success!", 200
|
return "Success!", 200
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route("/users", methods=["GET"])
|
||||||
|
def users():
|
||||||
|
search = request.args.get("search")
|
||||||
|
|
||||||
|
if not search:
|
||||||
|
return "No search query provided!", 400
|
||||||
|
|
||||||
|
search_results = Users.query.filter(Users.username.contains(search)).limit(MAX_SEARCH_RESULTS).all()
|
||||||
|
|
||||||
|
return jsonify([result.username for result in search_results]), 200
|
||||||
|
|
|
@ -1,5 +1,17 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
GAME_VERSION = "alpha"
|
||||||
|
GAME_VERSIONS = ["alpha"]
|
||||||
|
GAME_DIFFICULTIES = [0, 1, 2, 3, 4]
|
||||||
|
|
||||||
|
USER_MAX_TOKENS = 3
|
||||||
|
|
||||||
|
MAX_TOP_SCORES = 15
|
||||||
|
MAX_SEARCH_RESULTS = 5
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Postgres
|
||||||
SECRET_KEY = os.getenv("FLASK_KEY")
|
SECRET_KEY = os.getenv("FLASK_KEY")
|
||||||
|
|
||||||
user = os.getenv("DB_USER")
|
user = os.getenv("DB_USER")
|
||||||
|
@ -13,9 +25,8 @@ SQLALCHEMY_POOL_RECYCLE = 621
|
||||||
|
|
||||||
MIGRATION_DIR = "/data/storage/migrations"
|
MIGRATION_DIR = "/data/storage/migrations"
|
||||||
INSTANCE_DIR = "/data/storage/instance"
|
INSTANCE_DIR = "/data/storage/instance"
|
||||||
|
"""
|
||||||
|
|
||||||
GAME_VERSION = "alpha"
|
# SQLite
|
||||||
GAME_VERSIONS = ["alpha"]
|
SECRET_KEY = "dev"
|
||||||
GAME_DIFFICULTIES = [0, 1, 2, 3, 4]
|
SQLALCHEMY_DATABASE_URI = "sqlite:///tfr.db"
|
||||||
|
|
||||||
USER_MAX_TOKENS = 3
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
function addFlashMessage(message,type='success'){let flask=document.createElement('p');flask.onclick=()=>flask.remove();flask.classList.add(type);flask.innerHTML=message;let close=document.createElement('span');close.innerHTML='<i class="ph-bold ph-x"></i>';flask.appendChild(close);document.querySelector('.flash').appendChild(flask);}
|
|
||||||
function ajax(url,form,callback,method='POST'){console.log(form)
|
|
||||||
fetch(url,{method:method,body:form,}).then(response=>response.json()).then(data=>callback(data)).catch(error=>addFlashMessage(error.error,'error'));}
|
|
||||||
function deleteToken(id){let form=new FormData();form.append('token_id',id);ajax('/api/tokens',form,(data)=>{if(data.success){addFlashMessage(data.success,'success');document.querySelector(`#token-${id}`).remove();}else{addFlashMessage(data.error,'error');}},'DELETE');}
|
|
||||||
function addToken(){ajax('/api/tokens',null,(data)=>{if(data.success){window.location.reload();}else{addFlashMessage(data.error,'error');}});}
|
|
||||||
function viewToken(id){let token=document.querySelector(`#token-${id}`);let hidden=token.children[2];hidden.classList.toggle('hidden');}
|
|
File diff suppressed because one or more lines are too long
|
@ -1,16 +1,4 @@
|
||||||
function addFlashMessage(message, type='success') {
|
function addFlashMessage(message, type='success') {
|
||||||
/**
|
|
||||||
* Add a flash message to the page
|
|
||||||
*
|
|
||||||
* @param {string} message
|
|
||||||
* @return {void}
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* addFlashMessage('Hello World!', 'success')
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* addFlashMessage('Oopsie!', 'error')
|
|
||||||
*/
|
|
||||||
let flask = document.createElement('p');
|
let flask = document.createElement('p');
|
||||||
flask.onclick = () => flask.remove();
|
flask.onclick = () => flask.remove();
|
||||||
flask.classList.add(type);
|
flask.classList.add(type);
|
||||||
|
@ -24,20 +12,6 @@ function addFlashMessage(message, type='success') {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ajax(url, form, callback, method='POST') {
|
function ajax(url, form, callback, method='POST') {
|
||||||
/**
|
|
||||||
* Send a request to the server and get a response
|
|
||||||
* Mostly a wrapper for fetch(), since most of the
|
|
||||||
* requests are made with FormData and POST method
|
|
||||||
*
|
|
||||||
* @param {string} url
|
|
||||||
* @param {FormData} form
|
|
||||||
* @param {function} callback
|
|
||||||
* @param {string} method
|
|
||||||
* @return {void}
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ajax('/api', formData, callback = (data) => { console.log(data) }, 'POST')
|
|
||||||
*/
|
|
||||||
console.log(form)
|
console.log(form)
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: method,
|
method: method,
|
||||||
|
@ -49,15 +23,6 @@ function ajax(url, form, callback, method='POST') {
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteToken(id) {
|
function deleteToken(id) {
|
||||||
/**
|
|
||||||
* Delete user token
|
|
||||||
*
|
|
||||||
* @return {void}
|
|
||||||
* @{integer} id
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* deleteToken(id)
|
|
||||||
*/
|
|
||||||
let form = new FormData();
|
let form = new FormData();
|
||||||
form.append('token_id', id);
|
form.append('token_id', id);
|
||||||
|
|
||||||
|
@ -72,14 +37,6 @@ function deleteToken(id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function addToken() {
|
function addToken() {
|
||||||
/**
|
|
||||||
* Add a new token
|
|
||||||
*
|
|
||||||
* @return {void}
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* addToken()
|
|
||||||
*/
|
|
||||||
ajax('/api/tokens', null, (data) => {
|
ajax('/api/tokens', null, (data) => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
|
@ -90,15 +47,6 @@ function addToken() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function viewToken(id) {
|
function viewToken(id) {
|
||||||
/**
|
|
||||||
* View a token
|
|
||||||
*
|
|
||||||
* @return {void}
|
|
||||||
* @{integer} id
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* viewToken(id)
|
|
||||||
*/
|
|
||||||
let token = document.querySelector(`#token-${id}`);
|
let token = document.querySelector(`#token-${id}`);
|
||||||
let hidden = token.children[2];
|
let hidden = token.children[2];
|
||||||
|
|
||||||
|
|
89
TFR/server/static/js/search.js
Normal file
89
TFR/server/static/js/search.js
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
function showHint() {
|
||||||
|
let search = document.querySelector('.search > input');
|
||||||
|
let searchPos = search.getBoundingClientRect();
|
||||||
|
let hint = document.querySelector('.search-hint');
|
||||||
|
|
||||||
|
hint.style.width = search.offsetWidth + 'px';
|
||||||
|
hint.style.left = searchPos.left + 'px';
|
||||||
|
hint.style.top = searchPos.bottom + 'px';
|
||||||
|
|
||||||
|
hint.style.display = 'flex';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function hideHint() {
|
||||||
|
let hint = document.querySelector('.search-hint');
|
||||||
|
hint.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function updateHint() {
|
||||||
|
let search = document.querySelector('.search > input');
|
||||||
|
let searchPos = search.getBoundingClientRect();
|
||||||
|
let hint = document.querySelector('.search-hint');
|
||||||
|
|
||||||
|
hint.style.width = search.offsetWidth + 'px';
|
||||||
|
hint.style.left = searchPos.left + 'px';
|
||||||
|
hint.style.top = searchPos.bottom + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getSearch() {
|
||||||
|
let search = document.querySelector('.search > input').value;
|
||||||
|
let hint = document.querySelector('.search-hint');
|
||||||
|
|
||||||
|
if (search.length === 0) {
|
||||||
|
hint.innerHTML = '<p>Start typing to see results...</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch('/api/users?search=' + search.toString(), {
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.length === 0) {
|
||||||
|
hint.innerHTML = '<p>No results found...</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hint.innerHTML = '';
|
||||||
|
|
||||||
|
data.forEach(user => {
|
||||||
|
let el = document.createElement('button');
|
||||||
|
el.innerHTML = user;
|
||||||
|
el.onmousedown = function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
search = document.querySelector('.search > input');
|
||||||
|
search.value = user.toString();
|
||||||
|
search.blur();
|
||||||
|
}
|
||||||
|
hint.appendChild(el);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
hint.innerHTML = '<p>Something went wrong...</p>';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
window.onload = () => {
|
||||||
|
let typingTimer;
|
||||||
|
let search = document.querySelector('.search > input');
|
||||||
|
|
||||||
|
if (search === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('resize', updateHint);
|
||||||
|
|
||||||
|
search.addEventListener('focus', showHint);
|
||||||
|
search.addEventListener('blur', hideHint);
|
||||||
|
search.addEventListener('keydown', () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
});
|
||||||
|
search.addEventListener('keyup', () => {
|
||||||
|
clearTimeout(typingTimer);
|
||||||
|
typingTimer = setTimeout(getSearch, 250);
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
.button
|
.button
|
||||||
margin: auto 0.15rem
|
margin: auto 0
|
||||||
padding: 0.5rem 0.7rem
|
padding: 0.5rem 0.7rem
|
||||||
|
|
||||||
display: flex
|
display: flex
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
display: block
|
display: block
|
||||||
|
|
||||||
.search
|
.search
|
||||||
margin: auto 0.15rem
|
margin: auto 0
|
||||||
width: 100%
|
width: 100%
|
||||||
|
|
||||||
position: relative
|
position: relative
|
||||||
|
@ -75,8 +75,13 @@
|
||||||
border-left: 1px solid RGBA($white, 0.1)
|
border-left: 1px solid RGBA($white, 0.1)
|
||||||
border-radius: 0 2px 2px 0
|
border-radius: 0 2px 2px 0
|
||||||
|
|
||||||
transition: background-color 0.1s ease-in-out
|
&:hover
|
||||||
|
background-color: RGBA($white, 0.1)
|
||||||
|
border-left: 1px solid transparent
|
||||||
|
|
||||||
&:focus-visible, &:focus
|
&:focus-visible, &:focus
|
||||||
background-color: RGBA($white, 0.1)
|
background-color: RGBA($white, 0.1)
|
||||||
|
|
||||||
outline: 0 solid transparent
|
outline: 0 solid transparent
|
||||||
|
border-left: 1px solid transparent
|
||||||
|
border-radius: 0 2px 0 0
|
||||||
|
|
51
TFR/server/static/sass/hint.sass
Normal file
51
TFR/server/static/sass/hint.sass
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
.search-hint
|
||||||
|
position: absolute
|
||||||
|
|
||||||
|
display: none
|
||||||
|
flex-direction: column
|
||||||
|
|
||||||
|
text-decoration: none
|
||||||
|
white-space: nowrap
|
||||||
|
font-size: 0.9em
|
||||||
|
|
||||||
|
background-color: RGBA($white, 0.1)
|
||||||
|
backdrop-filter: blur(10px)
|
||||||
|
|
||||||
|
border-top: 1px solid RGBA($white, 0.1)
|
||||||
|
border-radius: 0 0 2px 2px
|
||||||
|
|
||||||
|
overflow: hidden
|
||||||
|
z-index: 999
|
||||||
|
|
||||||
|
> p
|
||||||
|
margin: 0
|
||||||
|
padding: 0.5rem 0.75rem
|
||||||
|
|
||||||
|
width: 100%
|
||||||
|
white-space: nowrap
|
||||||
|
text-overflow: ellipsis
|
||||||
|
|
||||||
|
color: RGB($white)
|
||||||
|
|
||||||
|
overflow: hidden
|
||||||
|
|
||||||
|
> button
|
||||||
|
padding: 0.5rem 0.75rem
|
||||||
|
|
||||||
|
width: 100%
|
||||||
|
white-space: nowrap
|
||||||
|
text-overflow: ellipsis
|
||||||
|
|
||||||
|
font-size: 0.9rem
|
||||||
|
text-align: left
|
||||||
|
|
||||||
|
background-color: transparent
|
||||||
|
color: RGB($white)
|
||||||
|
border: 0 solid transparent
|
||||||
|
|
||||||
|
overflow: hidden
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background-color: RGBA($white, 0.1)
|
||||||
|
color: RGB($primary)
|
||||||
|
border: 0 solid transparent
|
|
@ -7,7 +7,7 @@ $silver: var(--silver)
|
||||||
$bronze: var(--bronze)
|
$bronze: var(--bronze)
|
||||||
|
|
||||||
\:root
|
\:root
|
||||||
--black: 21, 21, 21
|
--black: 31, 27, 21
|
||||||
--white: 232, 227, 227
|
--white: 232, 227, 227
|
||||||
--primary: 210, 206, 97
|
--primary: 210, 206, 97
|
||||||
--secondary: 185, 77, 77
|
--secondary: 185, 77, 77
|
||||||
|
@ -17,6 +17,7 @@ $bronze: var(--bronze)
|
||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Merriweather:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400;1,700&display=swap')
|
@import url('https://fonts.googleapis.com/css2?family=Merriweather:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400;1,700&display=swap')
|
||||||
@import "button"
|
@import "button"
|
||||||
|
@import "hint"
|
||||||
|
|
||||||
*
|
*
|
||||||
box-sizing: border-box
|
box-sizing: border-box
|
||||||
|
@ -60,13 +61,66 @@ body
|
||||||
backdrop-filter: blur(5px)
|
backdrop-filter: blur(5px)
|
||||||
z-index: 2
|
z-index: 2
|
||||||
|
|
||||||
|
.table
|
||||||
|
width: 100%
|
||||||
|
height: auto
|
||||||
|
background-color: rgba($black, 0.7)
|
||||||
|
border-radius: 2px
|
||||||
|
overflow: hidden
|
||||||
|
|
||||||
> table
|
> table
|
||||||
width: 100%
|
width: 100%
|
||||||
|
border-collapse: collapse
|
||||||
|
|
||||||
|
> tbody
|
||||||
|
> tr
|
||||||
|
height: 2.75rem
|
||||||
|
vertical-align: middle
|
||||||
|
|
||||||
|
> th
|
||||||
|
padding: 0.5rem
|
||||||
|
background-image: linear-gradient(to bottom, RGBA($white, 0.05), transparent)
|
||||||
|
text-align: left
|
||||||
|
font-weight: bolder
|
||||||
|
|
||||||
|
> td
|
||||||
|
padding: 0.5rem
|
||||||
|
text-align: left
|
||||||
|
|
||||||
|
&.player
|
||||||
|
color: RGB($secondary)
|
||||||
|
text-shadow: 0 0 5px RGBA($secondary, 0.7)
|
||||||
|
|
||||||
|
i
|
||||||
|
padding: 0.25rem
|
||||||
|
color: RGB($black)
|
||||||
|
border-radius: 2px
|
||||||
|
|
||||||
|
&.first
|
||||||
|
background-color: RGBA($gold, 0.6)
|
||||||
|
|
||||||
|
&.second
|
||||||
|
background-color: RGBA($silver, 0.6)
|
||||||
|
|
||||||
|
&.third
|
||||||
|
background-color: RGBA($bronze, 0.6)
|
||||||
|
|
||||||
|
&:nth-child(odd) > td
|
||||||
|
background-color: RGBA($white, 0.01)
|
||||||
|
|
||||||
header
|
header
|
||||||
padding: 1rem
|
padding: 1rem 1rem 0
|
||||||
|
display: flex
|
||||||
|
flex-direction: column
|
||||||
|
gap: 0.4rem
|
||||||
background-image: linear-gradient(to bottom, RGB(var(--black)), transparent)
|
background-image: linear-gradient(to bottom, RGB(var(--black)), transparent)
|
||||||
|
|
||||||
|
> hr
|
||||||
|
margin: 0
|
||||||
|
width: 100%
|
||||||
|
border: none
|
||||||
|
border-bottom: 1px solid RGBA($white, 0.1)
|
||||||
|
|
||||||
.title
|
.title
|
||||||
margin-bottom: 1rem
|
margin-bottom: 1rem
|
||||||
height: auto
|
height: auto
|
||||||
|
@ -75,19 +129,13 @@ header
|
||||||
height: auto
|
height: auto
|
||||||
max-width: 100%
|
max-width: 100%
|
||||||
|
|
||||||
nav
|
nav, nav > form
|
||||||
margin-top: 0.3rem
|
|
||||||
padding: 0
|
padding: 0
|
||||||
|
width: 100%
|
||||||
display: flex
|
display: flex
|
||||||
flex-direction: row
|
flex-direction: row
|
||||||
justify-content: center
|
justify-content: center
|
||||||
|
gap: 0.4rem
|
||||||
> form
|
|
||||||
width: 100%
|
|
||||||
display: flex
|
|
||||||
flex-direction: row
|
|
||||||
justify-content: center
|
|
||||||
|
|
||||||
.spacer
|
.spacer
|
||||||
width: 100%
|
width: 100%
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block nav %}
|
{% block nav %}
|
||||||
<nav>
|
<hr>
|
||||||
<a href="{{ url_for('auth.account', action='logout') }}" class="button">Your Profile</a>
|
<nav>
|
||||||
<span class="spacer"></span>
|
<a href="{{ url_for('auth.account', action='logout') }}" class="button">Your Profile</a>
|
||||||
<a href="{{ url_for('auth.account', action='logout') }}" class="button secondary">Logout</a>
|
<span class="spacer"></span>
|
||||||
</nav>
|
<a href="{{ url_for('auth.account', action='logout') }}" class="button secondary">Logout</a>
|
||||||
|
</nav>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="block">
|
<div class="block">
|
||||||
|
|
|
@ -11,20 +11,25 @@
|
||||||
{% assets "styles" %}<link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css">{% endassets %}
|
{% assets "styles" %}<link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css">{% endassets %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div class="search-hint">
|
||||||
|
<p>Start typing to see results...</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<picture class="background">
|
<picture class="background">
|
||||||
<source srcset="{{ url_for('static', filename='background.webp') }}">
|
<source srcset="{{ url_for('static', filename='background.webp') }}">
|
||||||
<img src="{{ url_for('static', filename='background.png') }}" alt="The Front Rooms Level select render">
|
<img src="{{ url_for('static', filename='background.png') }}" alt="The Front Rooms Level select render">
|
||||||
</picture>
|
</picture>
|
||||||
|
|
||||||
<div class="app">
|
<div class="app">
|
||||||
<!-- Get flashed lol -->
|
<!-- Get flashed lol -->
|
||||||
<div class="flash">
|
<div class="flash">
|
||||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
{% for category, message in messages %}
|
{% for category, message in messages %}
|
||||||
<p class="{{ category }}" onclick="this.remove()"><span><i class="ph-bold ph-x"></i></span>{{ message }}</p>
|
<p class="{{ category }}" onclick="this.remove()"><span><i class="ph-bold ph-x"></i></span>{{ message }}</p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
|
@ -32,6 +37,7 @@
|
||||||
<source srcset="{{ url_for('static', filename='title.webp') }}">
|
<source srcset="{{ url_for('static', filename='title.webp') }}">
|
||||||
<img src="{{ url_for('static', filename='title.png') }}" alt="The Front Rooms logo">
|
<img src="{{ url_for('static', filename='title.png') }}" alt="The Front Rooms logo">
|
||||||
</picture>
|
</picture>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<a href="{{ url_for('views.index') }}" class="button">Scores</a>
|
<a href="{{ url_for('views.index') }}" class="button">Scores</a>
|
||||||
<a href="{{ url_for('views.about') }}" class="button">About</a>
|
<a href="{{ url_for('views.about') }}" class="button">About</a>
|
||||||
|
@ -39,11 +45,10 @@
|
||||||
|
|
||||||
<span class="spacer"></span>
|
<span class="spacer"></span>
|
||||||
|
|
||||||
<a href="#" class="button"><i class="ph ph-download-simple"></i></a>
|
|
||||||
{% if current_user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
<a href="{{ url_for('auth.account') }}" class="button primary">{{ current_user.username }}</a>
|
<a href="{{ url_for('auth.account') }}" class="button primary">{{ current_user.username }}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ url_for('auth.auth') }}" class="button primary"><i class="ph ph-identification-card"></i></a>
|
<a href="{{ url_for('auth.auth') }}" class="button primary"><i class="ph ph-user-circle"></i></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
@ -51,13 +56,9 @@
|
||||||
{% block nav %}{% endblock %}
|
{% block nav %}{% endblock %}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main>
|
<main>{% block content %}{% endblock %}</main>
|
||||||
{% block content %}{% endblock %}
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer>
|
<footer><p>By Project Redacted | <a href="https://github.com/Fluffy-Bean/GameExpo23">Server Source</a></p></footer>
|
||||||
<p>By Project Redacted | <a href="https://github.com/Fluffy-Bean/GameExpo23">Server Source</a></p>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -1,53 +1,72 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block nav %}
|
{% block nav %}
|
||||||
<nav>
|
<hr>
|
||||||
<form method="GET" action="{{ url_for('views.index') }}">
|
<nav>
|
||||||
<select name="diff" class="button">
|
<form method="GET" action="{{ url_for('views.index') }}">
|
||||||
<option value="0" {% if diff==0 %}selected{% endif %}>Level 1</option>
|
<select name="diff" class="button">
|
||||||
<option value="1" {% if diff==1 %}selected{% endif %}>Level 2</option>
|
<option value="0" {% if diff==0 %}selected{% endif %}>Level 1</option>
|
||||||
<option value="2" {% if diff==2 %}selected{% endif %}>Level 3</option>
|
<option value="1" {% if diff==1 %}selected{% endif %}>Level 2</option>
|
||||||
<option value="3" {% if diff==3 %}selected{% endif %}>Normal</option>
|
<option value="2" {% if diff==2 %}selected{% endif %}>Level 3</option>
|
||||||
<option value="4" {% if diff==4 %}selected{% endif %}>Hard</option>
|
<option value="3" {% if diff==3 %}selected{% endif %}>Normal</option>
|
||||||
</select>
|
<option value="4" {% if diff==4 %}selected{% endif %}>Hard</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
<select name="ver" class="button">
|
<select name="ver" class="button">
|
||||||
<option value="alpha" {% if ver=="alpha" %}selected{% endif %}>Alpha</option>
|
<option value="alpha" {% if ver=="alpha" %}selected{% endif %}>Alpha</option>
|
||||||
<option value="beta" {% if ver=="beta" %}selected{% endif %}>Beta</option>
|
<option value="beta" {% if ver=="beta" %}selected{% endif %}>Beta</option>
|
||||||
<option value="1.0" {% if ver=="1.0" %}selected{% endif %}>1.0</option>
|
<option value="1.0" {% if ver=="1.0" %}selected{% endif %}>1.0</option>
|
||||||
<option value="1.1" {% if ver=="1.1" %}selected{% endif %}>1.1</option>
|
<option value="1.1" {% if ver=="1.1" %}selected{% endif %}>1.1</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<span class="search">
|
<span class="search">
|
||||||
<label for="user">Username</label>
|
<label for="user">Username</label>
|
||||||
<input type="text" name="user" {% if user %}value="{{ user }}"{% endif %}/>
|
<input type="text" name="user" {% if user %}value="{{ user }}"{% endif %} autocomplete="off"/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<button class="button"><i class="ph ph-binoculars"></i></button>
|
<button class="button"><i class="ph ph-magnifying-glass"></i></button>
|
||||||
</form>
|
</form>
|
||||||
</nav>
|
</nav>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% if scores %}
|
{% if scores %}
|
||||||
<table>
|
<div class="table">
|
||||||
<tr>
|
<table>
|
||||||
<th>Position</th>
|
|
||||||
<th>Player</th>
|
|
||||||
<th>Difficulty</th>
|
|
||||||
<th>Score</th>
|
|
||||||
</tr>
|
|
||||||
{% for score in scores %}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ loop.index }}</td>
|
<th></th>
|
||||||
<td>{{ score.user_id. }}</td>
|
<th>Name</th>
|
||||||
<td>{{ score.difficulty }}</td>
|
<th>Time Set</th>
|
||||||
<td>{{ score.score }}</td>
|
<th>Submitted</th>
|
||||||
|
<!-- <th>Game</th> -->
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% for score in scores %}
|
||||||
</table>
|
<tr>
|
||||||
|
{% if loop.index == 1 %}
|
||||||
|
<td><i class="first ph-fill ph-crown"></i></td>
|
||||||
|
{% elif loop.index == 2 %}
|
||||||
|
<td><i class="second ph-duotone ph-crown"></i></td>
|
||||||
|
{% elif loop.index == 3 %}
|
||||||
|
<td><i class="third ph ph-crown"></i></td>
|
||||||
|
{% else %}
|
||||||
|
<td>{{ loop.index }}</td>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if score.users.id == current_user.id %}
|
||||||
|
<td class="player">{{ score.users.username }}</td>
|
||||||
|
{% else %}
|
||||||
|
<td>{{ score.users.username }}</td>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<td>{{ score.score }}</td>
|
||||||
|
<td>{{ score.scored_at.strftime('%Y-%m-%d') }}</td>
|
||||||
|
<!-- <td>{{ score.version }}</td> -->
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="center-text">
|
<div class="center-text">
|
||||||
<h2>No scores</h2>
|
<h2>No scores</h2>
|
||||||
<p>Set go some!</p>
|
<p>Go set some!</p>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -1,6 +1,6 @@
|
||||||
from flask import Blueprint, request, render_template, abort
|
from flask import Blueprint, request, render_template, abort
|
||||||
from server.models import Scores, Users
|
from server.models import Scores, Users
|
||||||
from server.config import GAME_VERSION
|
from server.config import GAME_VERSION, MAX_TOP_SCORES
|
||||||
|
|
||||||
|
|
||||||
blueprint = Blueprint("views", __name__)
|
blueprint = Blueprint("views", __name__)
|
||||||
|
@ -10,19 +10,20 @@ blueprint = Blueprint("views", __name__)
|
||||||
def index():
|
def index():
|
||||||
diff_arg = request.args.get("diff", 0)
|
diff_arg = request.args.get("diff", 0)
|
||||||
ver_arg = request.args.get("ver", GAME_VERSION)
|
ver_arg = request.args.get("ver", GAME_VERSION)
|
||||||
user_arg = request.args.get("user", "")
|
user_arg = request.args.get("user", None)
|
||||||
|
|
||||||
scores = Scores.query.filter_by(difficulty=diff_arg)
|
scores = Scores.query.filter_by(difficulty=diff_arg)
|
||||||
|
|
||||||
if ver_arg:
|
if ver_arg:
|
||||||
scores.filter_by(version=ver_arg)
|
scores = scores.filter_by(version=ver_arg)
|
||||||
if user_arg:
|
if user_arg:
|
||||||
if user := Users.query.filter_by(username=user_arg).first():
|
if user := Users.query.filter_by(username=user_arg).first():
|
||||||
scores.filter_by(user_id=user.id)
|
scores = scores.filter_by(user_id=user.id)
|
||||||
|
print(user.id)
|
||||||
else:
|
else:
|
||||||
abort(404, "User not found")
|
abort(404, "User not found")
|
||||||
|
|
||||||
scores.order_by(Scores.score.desc()).limit(10).all()
|
scores = scores.order_by(Scores.score.asc()).limit(MAX_TOP_SCORES).all()
|
||||||
|
|
||||||
return render_template("scores.html", scores=scores, diff=int(diff_arg), ver=ver_arg, user=user_arg)
|
return render_template("scores.html", scores=scores, diff=int(diff_arg), ver=ver_arg, user=user_arg)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue