Allow for styling

This commit is contained in:
Michał 2023-09-27 13:59:31 +01:00
parent 3496a3bbe9
commit b4bc8c61ec
11 changed files with 161 additions and 119 deletions

View file

@ -17,41 +17,32 @@ startup.check_conf()
# Set dirs
APPLICATION_ROOT = platformdirs.user_config_dir("onlylegs")
# Load environment variables
# print("Loading environment variables...")
load_dotenv(os.path.join(APPLICATION_ROOT, ".env"))
# Load config from user dir
# print("Loading config...")
with open(
os.path.join(APPLICATION_ROOT, "conf.yml"), encoding="utf-8", mode="r"
) as file:
conf = safe_load(file)
# Flask config
SECRET_KEY = os.environ.get("FLASK_SECRET")
DATABASE_NAME = "gallery.sqlite3"
SQLALCHEMY_DATABASE_URI = "sqlite:///" + DATABASE_NAME
MAX_CONTENT_LENGTH = 1024 * 1024 * conf["upload"]["max-size"]
ALLOWED_EXTENSIONS = conf["upload"]["allowed-extensions"]
# Pass YAML config to app
ADMIN_CONF = conf["admin"]
UPLOAD_CONF = conf["upload"]
WEBSITE_CONF = conf["website"]
# Directories
UPLOAD_FOLDER = os.path.join(APPLICATION_ROOT, "media", "uploads")
MEDIA_FOLDER = os.path.join(APPLICATION_ROOT, "media")
CACHE_FOLDER = os.path.join(APPLICATION_ROOT, "media", "cache")
PFP_FOLDER = os.path.join(APPLICATION_ROOT, "media", "pfp")
BANNER_FOLDER = os.path.join(APPLICATION_ROOT, "media", "banner")
# Load env and config files
load_dotenv(os.path.join(APPLICATION_ROOT, ".env"))
config_file = os.path.join(APPLICATION_ROOT, "conf.yml")
with open(config_file, encoding="utf-8", mode="r") as file:
conf = safe_load(file)
# Flask config
SECRET_KEY = os.environ.get("FLASK_SECRET")
MAX_CONTENT_LENGTH = 1024 * 1024 * conf["upload"]["max-size"]
ALLOWED_EXTENSIONS = conf["upload"]["allowed-extensions"]
APP_VERSION = importlib.metadata.version("OnlyLegs")
# Database
DATABASE_NAME = "gallery.sqlite3"
SQLALCHEMY_DATABASE_URI = "sqlite:///" + DATABASE_NAME
INSTANCE_DIR = os.path.join(APPLICATION_ROOT, "instance")
MIGRATIONS_DIR = os.path.join(INSTANCE_DIR, "migrations")
# App
APP_VERSION = importlib.metadata.version("OnlyLegs")
# Pass YAML config to app
ADMIN_CONF = conf["admin"]
UPLOAD_CONF = conf["upload"]
WEBSITE_CONF = conf["website"]

View file

@ -64,12 +64,11 @@
&:hover
cursor: pointer
color: var(--primary)
&.disabled, &:disabled
color: var(--foreground-dim)
cursor: unset
&:disabled, &[disabled], &.disabled
color: var(--foreground-gray)
cursor: default
.pill__critical
color: var(--danger)

View file

@ -1,5 +1,5 @@
.info-container
padding: 0.5rem 0 0 0.5rem
padding: 0.5rem 0 0.5rem 0.5rem
width: 27rem
position: absolute
top: 0

View file

@ -75,9 +75,11 @@ main
main
margin: 0 0.5rem
header
position: sticky
top: 0
// This is very broken, as it breaks when you open any context menu/popup
// I need to fix this at some point as it looks really nice
//header
// position: sticky
// top: 0
.error-page
min-height: 100%

View file

@ -26,8 +26,8 @@
--red-transparent: rgba(182, 100, 103, 0.1)
--orange: rgb(217, 140, 95)
--orange-transparent: rgba(217, 140, 95, 0.1)
--yellow: rgb(217, 188, 140)
--yellow-transparent: rgba(217, 188, 140, 0.1)
--yellow: rgb(198, 185, 166)
--yellow-transparent: rgba(198, 185, 166, 0.1)
--green: rgb(140, 151, 125)
--green-transparent: rgba(140, 151, 125, 0.1)
--blue: rgb(141, 163, 185)

View file

@ -28,10 +28,20 @@
{% assets "scripts" %} <script type="text/javascript" src="{{ ASSET_URL }}"></script> {% endassets %}
{% assets "styles" %} <link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css" defer> {% endassets %}
<style type="text/css">
:root{
--primary: {{ config.WEBSITE_CONF.primary_color | default('rgb(198, 185, 166)') }};
}
<style>
{% if config.WEBSITE_CONF.styling.force %}
:root{
--background-hsl-hue: {{ config.WEBSITE_CONF.styling.hue }} !important;
--background-hsl-saturation: {{ config.WEBSITE_CONF.styling.saturation }}% !important;
--rad: {{ config.WEBSITE_CONF.styling.rad }} !important;
}
{% else %}
:root{
--background-hsl-hue: {{ config.WEBSITE_CONF.styling.hue }};
--background-hsl-saturation: {{ config.WEBSITE_CONF.styling.saturation }}%;
--rad: {{ config.WEBSITE_CONF.styling.rad }};
}
{% endif %}
</style>
{% block head %}{% endblock %}

View file

@ -24,37 +24,6 @@
--background-hsl-hue: {{ images.0.colours.0 | hsl_hue }};
--background-hsl-saturation: {{ images.0.colours.0 | hsl_saturation }}%;
}
/*
body {
color: {{ text_colour }} !important;
}
.navigation-item.selected {
color: {{ text_colour }} !important;
}
.banner .banner-content .banner-header,
.banner .banner-content .banner-info,
.banner .banner-content .banner-subtitle {
color: {{ text_colour }} !important;
}
.banner-content .link {
color: rgb{{ images.0.colours.0 }} !important;
}
.banner-content .link:hover {
color: {{ text_colour }} !important;
}
.banner-filter {
background: linear-gradient(90deg, rgb{{ images.0.colours.0 }}, rgba({{ images.0.colours.1.0 }}, {{ images.0.colours.1.1 }}, {{ images.0.colours.1.2 }}, 0.3)) !important;
}
@media (max-width: 800px) {
.banner-filter {
background: linear-gradient(180deg, rgba({{ images.0.colours.1.0 }}, {{ images.0.colours.1.1 }}, {{ images.0.colours.1.2 }}, 0.4), rgba({{ images.0.colours.0.0 }}, {{ images.0.colours.0.1 }}, {{ images.0.colours.0.2 }}, 0.3)) !important;
}
}
*/
{% endif %}
</style>
{% endblock %}

View file

@ -18,6 +18,10 @@
</script>
<style>
:root {
--background-hsl-hue: {{ image.colours.2 | hsl_hue }};
--background-hsl-saturation: {{ image.colours.2 | hsl_saturation }}%;
}
.background::after {
background-image: linear-gradient(to top, rgba({{ image.colours.0.0 }}, {{ image.colours.0.1 }}, {{ image.colours.0.2 }}, 0.8),
rgba({{ image.colours.1.0 }}, {{ image.colours.1.1 }}, {{ image.colours.1.2 }}, 0.2));
@ -30,7 +34,11 @@
<div class="banner-content">
<h1 class="banner-header">{{ config.WEBSITE_CONF.name }}</h1>
<div class="pill-row">
{% if next_url %}<div><a class="pill-item" href="{{ next_url }}"><i class="ph ph-arrow-left"></i></a></div>{% endif %}
<div>
<a {% if next_url %}class="pill-item" href="{{ next_url }}"{% else %}class="pill-item disabled"{% endif %}>
<i class="ph ph-arrow-left"></i>
</a>
</div>
<div>
<button class="pill-item" onclick="imageFullscreen()" id="fullscreenImage"><i class="ph ph-info"></i></button>
<button class="pill-item" onclick="copyToClipboard(window.location.href)"><i class="ph ph-export"></i></button>
@ -38,7 +46,11 @@
<button class="pill-item" onclick="imageShowOptionsPopup(this)"><i class="ph ph-list"></i></button>
{% endif %}
</div>
{% if prev_url %}<div><a class="pill-item" href="{{ prev_url }}"><i class="ph ph-arrow-right"></i></a></div>{% endif %}
<div>
<a {% if prev_url %}class="pill-item" href="{{ prev_url }}"{% else %}class="pill-item disabled"{% endif %}>
<i class="ph ph-arrow-right"></i>
</a>
</div>
</div>
</div>
</div>

View file

@ -66,6 +66,36 @@
<i class="ph ph-caret-down collapse-indicator"></i>
</summary>
<p>Nothing here :3</p>
<form method="POST" action="{{ url_for('settings.website_style') }}" enctype="multipart/form-data">
<h3>Style</h3>
<input type="range" name="hue" class="input-block" placeholder="Website Hue" value="{{ config.WEBSITE_CONF.styling.hue }}" min="0" max="360" />
<input type="range" name="saturation" class="input-block" placeholder="Strength of Color" value="{{ config.WEBSITE_CONF.styling.saturation }}" min="0" max="100" />
<input type="text" name="rad" class="input-block" placeholder="Roundy roundy" value="{{ config.WEBSITE_CONF.styling.rad }}" />
<input type="checkbox" name="force" class="input-block" placeholder="Force styling?" {% if config.WEBSITE_CONF.styling.force %}checked{% endif %} />
<button type="submit" class="btn-block">Update style</button>
</form>
</details>
<footer>
<p>Built on coffee and love, by Fluffy</p>
</footer>
{% endblock %}
{% block script %}
<script>
let hue = document.querySelector('input[name=hue]');
let saturation = document.querySelector('input[name=saturation]');
let rad = document.querySelector('input[name=rad]');
hue.addEventListener('input', () => {
document.documentElement.style.setProperty('--background-hsl-hue', hue.value, 'important');
});
saturation.addEventListener('input', () => {
document.documentElement.style.setProperty('--background-hsl-saturation', saturation.value + '%', 'important');
});
rad.addEventListener('input', () => {
document.documentElement.style.setProperty('--rad', rad.value, 'important');
});
</script>
{% endblock %}

View file

@ -22,12 +22,42 @@ REQUIRED_DIRS = {
EMAIL_REGEX = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b")
USERNAME_REGEX = re.compile(r"\b[A-Za-z0-9._%+-]+\b")
config = {
# Version of the config file
"version": "0.1.7",
# Not really used much, but good to have for future use
"admin": {
"username": "admin",
"email": "admin@example.com",
},
"upload": {
"allowed-extensions": {
"jpg": "jpeg",
"jpeg": "jpeg",
"png": "png",
"webp": "webp",
},
# Max size in MB
"max-size": 69,
# Max images to load per page
"max-load": 50,
},
"website": {
# Website name and motto
# Also CSS styling, hue is the color offset for hsl
"name": "OnlyLegs",
"motto": "A gallery built for fast and simple image management!",
"styling": {
"force": False,
"hue": "69",
"saturation": "25%",
"rad": "0.4rem",
},
},
}
def check_dirs():
"""
Create the user directory
"""
for directory in REQUIRED_DIRS.values():
if not os.path.exists(directory):
os.makedirs(directory)
@ -36,9 +66,6 @@ def check_dirs():
def check_env():
"""
Create the .env file with default values
"""
if os.path.exists(os.path.join(APPLICATION_ROOT, ".env")):
print("Environment file already exists at:", APPLICATION_ROOT)
return
@ -65,30 +92,23 @@ def check_env():
def check_conf():
"""
Create the YAML config file with default values
"""
if os.path.exists(os.path.join(APPLICATION_ROOT, "conf.yml")):
config_file = os.path.join(APPLICATION_ROOT, "conf.yml")
if os.path.exists(config_file):
print("Config file already exists at:", APPLICATION_ROOT)
return
cant_continue = True
username = "admin"
name = "Admin"
email = "admin@example.com"
print("No config file found, please enter the following information:")
while cant_continue:
username = input("Admin username: ").strip()
name = input("Admin name: ").strip()
email = input("Admin email: ").strip()
if not username or not USERNAME_REGEX.match(username):
print("Username is invalid!")
continue
if not name:
print("Name is invalid!")
continue
if not email or not EMAIL_REGEX.match(email):
print("Email is invalid!")
continue
@ -98,34 +118,11 @@ def check_conf():
if is_correct == "y" or not is_correct:
cant_continue = False
yaml_conf = {
"admin": {
"name": name,
"username": username,
"email": email,
},
"upload": {
"allowed-extensions": {
"jpg": "jpeg",
"jpeg": "jpeg",
"png": "png",
"webp": "webp",
},
"max-size": 69,
"max-load": 50,
"rename": "GWA_{{username}}_{{time}}",
},
"website": {
"name": "OnlyLegs",
"motto": "A gallery built for fast and simple image management!",
"language": "en",
},
}
config["admin"]["username"] = username
config["admin"]["email"] = email
with open(
os.path.join(APPLICATION_ROOT, "conf.yml"), encoding="utf-8", mode="w+"
) as file:
yaml.dump(yaml_conf, file, default_flow_style=False)
with open(config_file, encoding="utf-8", mode="w+") as file:
yaml.dump(config, file, default_flow_style=False)
print(
"####################################################",

View file

@ -5,6 +5,7 @@ import os
import pathlib
import re
import logging
import yaml
from colorthief import ColorThief
from flask import (
Blueprint,
@ -19,6 +20,7 @@ from flask_login import login_required, current_user
from werkzeug.security import check_password_hash, generate_password_hash
from onlylegs.extensions import db
from onlylegs.models import Users
from onlylegs.config import APPLICATION_ROOT
blueprint = Blueprint("settings", __name__, url_prefix="/settings")
@ -157,3 +159,33 @@ def account_password():
flash(["Password changed! You must login now", 0])
return redirect(url_for("auth.logout"))
@blueprint.route("/website/style", methods=["POST"])
@login_required
def website_style():
config_file = os.path.join(APPLICATION_ROOT, "conf.yml")
website_hue = request.form.get("hue", 69, type=int)
website_saturation = request.form.get("saturation", 25, type=int)
website_rad = request.form.get("rad", "0.4rem").strip()
website_force_styling = request.form.get("force", False)
config = None
with open(config_file, "r") as file:
config = yaml.safe_load(file)
current_app.config["WEBSITE_CONF"]["styling"]["hue"] = website_hue
current_app.config["WEBSITE_CONF"]["styling"]["saturation"] = website_saturation
current_app.config["WEBSITE_CONF"]["styling"]["rad"] = website_rad
current_app.config["WEBSITE_CONF"]["styling"]["force"] = website_force_styling
config["website"]["styling"]["hue"] = website_hue
config["website"]["styling"]["saturation"] = website_saturation
config["website"]["styling"]["rad"] = website_rad
config["website"]["styling"]["force"] = website_force_styling
with open(config_file, "w") as file:
yaml.dump(config, file)
return "Website style changed", 200