diff --git a/onlylegs/config.py b/onlylegs/config.py index 3300411..a24c24d 100644 --- a/onlylegs/config.py +++ b/onlylegs/config.py @@ -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"] diff --git a/onlylegs/static/sass/components/buttons/pill.sass b/onlylegs/static/sass/components/buttons/pill.sass index f63b02e..25fc812 100644 --- a/onlylegs/static/sass/components/buttons/pill.sass +++ b/onlylegs/static/sass/components/buttons/pill.sass @@ -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) diff --git a/onlylegs/static/sass/components/image-view.sass b/onlylegs/static/sass/components/image-view.sass index 473bdc6..249f04c 100644 --- a/onlylegs/static/sass/components/image-view.sass +++ b/onlylegs/static/sass/components/image-view.sass @@ -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 diff --git a/onlylegs/static/sass/style.sass b/onlylegs/static/sass/style.sass index 19a6ec1..159aa1e 100644 --- a/onlylegs/static/sass/style.sass +++ b/onlylegs/static/sass/style.sass @@ -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% diff --git a/onlylegs/static/sass/variables.sass b/onlylegs/static/sass/variables.sass index 8f5818f..a31a7bd 100644 --- a/onlylegs/static/sass/variables.sass +++ b/onlylegs/static/sass/variables.sass @@ -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) diff --git a/onlylegs/templates/base.html b/onlylegs/templates/base.html index 403288f..3fb4855 100644 --- a/onlylegs/templates/base.html +++ b/onlylegs/templates/base.html @@ -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 %} diff --git a/onlylegs/templates/group.html b/onlylegs/templates/group.html index 077ea93..afaec5c 100644 --- a/onlylegs/templates/group.html +++ b/onlylegs/templates/group.html @@ -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 %} diff --git a/onlylegs/templates/image.html b/onlylegs/templates/image.html index 2070353..725a363 100644 --- a/onlylegs/templates/image.html +++ b/onlylegs/templates/image.html @@ -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> diff --git a/onlylegs/templates/settings.html b/onlylegs/templates/settings.html index 7cad7ab..521296a 100644 --- a/onlylegs/templates/settings.html +++ b/onlylegs/templates/settings.html @@ -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 %} + diff --git a/onlylegs/utils/startup.py b/onlylegs/utils/startup.py index c08fc3d..b8e30eb 100644 --- a/onlylegs/utils/startup.py +++ b/onlylegs/utils/startup.py @@ -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( "####################################################", diff --git a/onlylegs/views/settings.py b/onlylegs/views/settings.py index 7bf360e..092ed45 100644 --- a/onlylegs/views/settings.py +++ b/onlylegs/views/settings.py @@ -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