pride flags

This commit is contained in:
trinkey 2024-03-31 17:56:11 -04:00
parent 5486bb9114
commit 25c7d1c146
32 changed files with 115 additions and 48 deletions

View file

@ -1,3 +1,6 @@
# MAKE SURE YOU INSTALL ALL NEEDED LIBRARIES!
# pip install flask dotindex ensure-file
CONTENT_DIRECTORY = "./public/" CONTENT_DIRECTORY = "./public/"
SAVING_DIRECTORY = "./save/" SAVING_DIRECTORY = "./save/"
@ -12,6 +15,7 @@ import os
import re import re
from DotIndex import DotIndex from DotIndex import DotIndex
from ensure_file import ensure_file
from typing import Union, Callable from typing import Union, Callable
from flask import request, redirect from flask import request, redirect
from werkzeug.middleware.proxy_fix import ProxyFix from werkzeug.middleware.proxy_fix import ProxyFix
@ -19,6 +23,36 @@ from werkzeug.middleware.proxy_fix import ProxyFix
app = flask.Flask(__name__) app = flask.Flask(__name__)
app.url_map.strict_slashes = False app.url_map.strict_slashes = False
FLAGS = {
"agender": "Agender",
"ally": "Ally",
"aroace": "Aroace",
"aro": "Aromantic",
"ace": "Asexual",
"bicurious": "Bicurious",
"bigender": "Bigender",
"bi": "Bisexual",
"cisgender": "Cisgender",
"demiboy": "Demiboy",
"demigirl": "Demigirl",
"demiromantic": "Demiromantic",
"demisexual": "Demisexual",
"gay": "Gay (Rainbow)",
"gayman": "Gay Man",
"genderfluid": "Genderfluid",
"intersex": "Intersex",
"lesbian": "Lesbian",
"nonbinary": "Nonbinary",
"omnigender": "Omnigender",
"pan": "Pansexual",
"polyamory": "Polyamorous",
"straight": "Straight",
"transfem": "Transfeminine",
"trans": "Transgender",
"transmasc": "Transmasculine"
}
SOCIALS_REGEX = { SOCIALS_REGEX = {
"discord" : re.compile(r"^(?!.*\.\.)(?=.{2,32}$)[a-z0-9_.]+$"), "discord" : re.compile(r"^(?!.*\.\.)(?=.{2,32}$)[a-z0-9_.]+$"),
"twitter" : re.compile(r"^(?!.*twitter)(?!.*admin)[a-z0-9_]{1,15}$", re.IGNORECASE), "twitter" : re.compile(r"^(?!.*twitter)(?!.*admin)[a-z0-9_]{1,15}$", re.IGNORECASE),
@ -126,24 +160,6 @@ def sha(string: Union[str, bytes]) -> str:
return hashlib.sha256(string).hexdigest() return hashlib.sha256(string).hexdigest()
return "" return ""
def ensure_file(path: str, *, default_value: str="", folder: bool=False) -> None:
if os.path.exists(path):
if folder and not os.path.isdir(path):
os.remove(path)
os.makedirs(path)
elif not folder and os.path.isdir(path):
shutil.rmtree(path, ignore_errors=True)
f = open(path, "w")
f.write(default_value)
f.close()
else:
if folder:
os.makedirs(path)
else:
f = open(path, "w")
f.write(default_value)
f.close()
def escape_html(string: str) -> str: def escape_html(string: str) -> str:
return string.replace("&", "&amp;").replace("<", "&lt;").replace("\"", "&quot;") return string.replace("&", "&amp;").replace("<", "&lt;").replace("\"", "&quot;")
@ -226,11 +242,10 @@ def get_template(json, username):
inner = add_to_output(inner, json, "compliments", "Compliments"); inner = add_to_output(inner, json, "compliments", "Compliments");
inner = add_to_output(inner, json, "relationship", "Relationship<br>Descriptions"); inner = add_to_output(inner, json, "relationship", "Relationship<br>Descriptions");
try: if "social" in json and len(json.social):
social = json.social # type: ignore
inner += '<div class="added" id="social"><h2>Social Links</h2>' inner += '<div class="added" id="social"><h2>Social Links</h2>'
for i in social: for i in json.social:
if SOCIAL_INFO[i[1]]["link"]: if SOCIAL_INFO[i[1]]["link"]:
inner += f"<div>{SOCIAL_ICONS[i[1]]} <a href='{SOCIAL_INFO[i[1]]['link'].replace('%q', i[0])}' target='_blank'>{SOCIAL_INFO[i[1]]['prefix']}{escape_html(i[0])}</a></div>" inner += f"<div>{SOCIAL_ICONS[i[1]]} <a href='{SOCIAL_INFO[i[1]]['link'].replace('%q', i[0])}' target='_blank'>{SOCIAL_INFO[i[1]]['prefix']}{escape_html(i[0])}</a></div>"
else: else:
@ -238,10 +253,15 @@ def get_template(json, username):
inner += "</div>" inner += "</div>"
except AttributeError as e: if "flags" in json and len(json.flags):
print(e) inner += '<div class="added" id="flags"><h2>Pride Flags</h2><div class="img-list">'
inner += "</div>" for i in json.flags:
inner += f'<img src="/img/flags/{i}.png" title="{FLAGS[i]}">'
inner += "</div></div>"
inner += f'</div><div id="key">Key:<br>{icons["4"]} - Great<br>{icons["3"]} - Good<br>{icons["2"]} - Okay<br>{icons["1"]} - Bad</div><footer>Icons from <a href="https://fontawesome.com" target="_blank">Font Awesome</a></footer>'
return title, inner, styles, embed return title, inner, styles, embed
@ -535,6 +555,13 @@ def api_save():
social.append(i) social.append(i)
user_data["social"] = sort_list(social, True) user_data["social"] = sort_list(social, True)
if "flags" in x:
flags = {}
for i in x["flags"]:
if i in FLAGS:
flags[i] = None
user_data["flags"] = sorted([i for i in flags])
f = open(f"{SAVING_DIRECTORY}{username}.json", "w") f = open(f"{SAVING_DIRECTORY}{username}.json", "w")
f.write(json.dumps(user_data)) f.write(json.dumps(user_data))
f.close() f.close()
@ -590,8 +617,9 @@ app.route("/editor")(create_file_serve("editor.html"))
app.route("/u/<path:user>")(get_user_page) app.route("/u/<path:user>")(get_user_page)
app.route("/home")(home) app.route("/home")(home)
app.route("/js/<path:file>")(create_folder_serve("js"))
app.route("/css/<path:file>")(create_folder_serve("css")) app.route("/css/<path:file>")(create_folder_serve("css"))
app.route("/img/flags/<path:file>")(create_folder_serve("img/flags"))
app.route("/js/<path:file>")(create_folder_serve("js"))
app.route("/api/account/login", methods=["POST"])(api_account_login) app.route("/api/account/login", methods=["POST"])(api_account_login)
app.route("/api/account/signup", methods=["POST"])(api_account_signup) app.route("/api/account/signup", methods=["POST"])(api_account_signup)

View file

@ -22,7 +22,7 @@
function load(fromStart) { function load(fromStart) {
if (fromStart) { next = 0; } if (fromStart) { next = 0; }
fetch(`/api/browse?sort=alphabetical&page=${next}`, { fetch(`/api/browse?sort=random&page=${next}`, {
"method": "GET" "method": "GET"
}).then((request) => (request.json())) }).then((request) => (request.json()))
.then((json) => { .then((json) => {

View file

@ -1,5 +1,5 @@
body { body {
padding-bottom: calc(20px + 8em); padding-bottom: calc(30px + 8em);
} }
svg { svg {
@ -26,6 +26,19 @@ footer a, footer a:visited, footer a:link {
white-space: break-word; white-space: break-word;
} }
.added .img-list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
column-gap: 0.5em;
max-width: 10em;
}
.added img {
max-width: 4em;
}
.added h2 { .added h2 {
text-align: center; text-align: center;
} }

BIN
public/img/flags/ace.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
public/img/flags/ally.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
public/img/flags/aro.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
public/img/flags/aroace.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
public/img/flags/bi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
public/img/flags/gay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

BIN
public/img/flags/gayman.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
public/img/flags/pan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
public/img/flags/trans.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View file

@ -158,6 +158,35 @@ const socialRegex = {
} }
} }
const flags = {
agender: "Agender",
ally: "Ally",
aroace: "Aroace",
aro: "Aromantic",
ace: "Asexual",
bicurious: "Bicurious",
bigender: "Bigender",
bi: "Bisexual",
cisgender: "Cisgender",
demiboy: "Demiboy",
demigirl: "Demigirl",
demiromantic: "Demiromantic",
demisexual: "Demisexual",
gay: "Gay (Rainbow)",
gayman: "Gay Man",
genderfluid: "Genderfluid",
intersex: "Intersex",
lesbian: "Lesbian",
nonbinary: "Nonbinary",
omnigender: "Omnigender",
pan: "Pansexual",
polyamory: "Polyamorous",
straight: "Straight",
transfem: "Transfeminine",
trans: "Transgender",
transmasc: "Transmasculine"
}
const icons = { const icons = {
1: '<svg style="fill: var(--text-low-opacity);" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M323.8 477.2c-38.2 10.9-78.1-11.2-89-49.4l-5.7-20c-3.7-13-10.4-25-19.5-35l-51.3-56.4c-8.9-9.8-8.2-25 1.6-33.9s25-8.2 33.9 1.6l51.3 56.4c14.1 15.5 24.4 34 30.1 54.1l5.7 20c3.6 12.7 16.9 20.1 29.7 16.5s20.1-16.9 16.5-29.7l-5.7-20c-5.7-19.9-14.7-38.7-26.6-55.5-5.2-7.3-5.8-16.9-1.7-24.9s12.3-13 21.3-13H448c8.8 0 16-7.2 16-16 0-6.8-4.3-12.7-10.4-15-7.4-2.8-13-9-14.9-16.7s.1-15.8 5.3-21.7c2.5-2.8 4-6.5 4-10.6 0-7.8-5.6-14.3-13-15.7-8.2-1.6-15.1-7.3-18-15.2s-1.6-16.7 3.6-23.3c2.1-2.7 3.4-6.1 3.4-9.9 0-6.7-4.2-12.6-10.2-14.9-11.5-4.5-17.7-16.9-14.4-28.8.4-1.3.6-2.8.6-4.3 0-8.8-7.2-16-16-16h-97.5c-12.6 0-25 3.7-35.5 10.7l-61.7 41.1c-11 7.4-25.9 4.4-33.3-6.7s-4.4-25.9 6.7-33.3l61.7-41.1c18.4-12.3 40-18.8 62.1-18.8H384c34.7 0 62.9 27.6 64 62 14.6 11.7 24 29.7 24 50 0 4.5-.5 8.8-1.3 13 15.4 11.7 25.3 30.2 25.3 51 0 6.5-1 12.8-2.8 18.7 11.6 11.8 18.8 27.8 18.8 45.5 0 35.3-28.6 64-64 64h-92.3c4.7 10.4 8.7 21.2 11.8 32.2l5.7 20c10.9 38.2-11.2 78.1-49.4 89zM32 384c-17.7 0-32-14.3-32-32V128c0-17.7 14.3-32 32-32h64c17.7 0 32 14.3 32 32v224c0 17.7-14.3 32-32 32H32z"/></svg>', 1: '<svg style="fill: var(--text-low-opacity);" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M323.8 477.2c-38.2 10.9-78.1-11.2-89-49.4l-5.7-20c-3.7-13-10.4-25-19.5-35l-51.3-56.4c-8.9-9.8-8.2-25 1.6-33.9s25-8.2 33.9 1.6l51.3 56.4c14.1 15.5 24.4 34 30.1 54.1l5.7 20c3.6 12.7 16.9 20.1 29.7 16.5s20.1-16.9 16.5-29.7l-5.7-20c-5.7-19.9-14.7-38.7-26.6-55.5-5.2-7.3-5.8-16.9-1.7-24.9s12.3-13 21.3-13H448c8.8 0 16-7.2 16-16 0-6.8-4.3-12.7-10.4-15-7.4-2.8-13-9-14.9-16.7s.1-15.8 5.3-21.7c2.5-2.8 4-6.5 4-10.6 0-7.8-5.6-14.3-13-15.7-8.2-1.6-15.1-7.3-18-15.2s-1.6-16.7 3.6-23.3c2.1-2.7 3.4-6.1 3.4-9.9 0-6.7-4.2-12.6-10.2-14.9-11.5-4.5-17.7-16.9-14.4-28.8.4-1.3.6-2.8.6-4.3 0-8.8-7.2-16-16-16h-97.5c-12.6 0-25 3.7-35.5 10.7l-61.7 41.1c-11 7.4-25.9 4.4-33.3-6.7s-4.4-25.9 6.7-33.3l61.7-41.1c18.4-12.3 40-18.8 62.1-18.8H384c34.7 0 62.9 27.6 64 62 14.6 11.7 24 29.7 24 50 0 4.5-.5 8.8-1.3 13 15.4 11.7 25.3 30.2 25.3 51 0 6.5-1 12.8-2.8 18.7 11.6 11.8 18.8 27.8 18.8 45.5 0 35.3-28.6 64-64 64h-92.3c4.7 10.4 8.7 21.2 11.8 32.2l5.7 20c10.9 38.2-11.2 78.1-49.4 89zM32 384c-17.7 0-32-14.3-32-32V128c0-17.7 14.3-32 32-32h64c17.7 0 32 14.3 32 32v224c0 17.7-14.3 32-32 32H32z"/></svg>',
2: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M100.5 176c-29 0-52.5 23.5-52.5 52.5V320c0 13.3-10.7 24-24 24S0 333.3 0 320v-91.5C0 173 45 128 100.5 128c29.6 0 57.6 13 76.7 35.6l130.2 153.8c10 11.8 24.6 18.6 40.1 18.6 29 0 52.5-23.5 52.5-52.5V192c0-13.3 10.7-24 24-24s24 10.7 24 24v91.5C448 339 403 384 347.5 384c-29.6 0-57.6-13-76.7-35.6L140.6 194.6c-10-11.8-24.6-18.6-40.1-18.6z"/></svg>', 2: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M100.5 176c-29 0-52.5 23.5-52.5 52.5V320c0 13.3-10.7 24-24 24S0 333.3 0 320v-91.5C0 173 45 128 100.5 128c29.6 0 57.6 13 76.7 35.6l130.2 153.8c10 11.8 24.6 18.6 40.1 18.6 29 0 52.5-23.5 52.5-52.5V192c0-13.3 10.7-24 24-24s24 10.7 24 24v91.5C448 339 403 384 347.5 384c-29.6 0-57.6-13-76.7-35.6L140.6 194.6c-10-11.8-24.6-18.6-40.1-18.6z"/></svg>',

View file

@ -58,11 +58,11 @@ function updateColors() {
document.body.setAttribute("style", `--primary: ${colors.text}; --secondary-low-opacity: ${colors.text}22; --background: ${colors.background}; --background-low-opacity: ${colors.background}33; --accent: ${colors.accent}; --accent-low-opacity: ${colors.accent}66; --text: ${colors.text}; --text-low-opacity: ${colors.text}88;`); document.body.setAttribute("style", `--primary: ${colors.text}; --secondary-low-opacity: ${colors.text}22; --background: ${colors.background}; --background-low-opacity: ${colors.background}33; --accent: ${colors.accent}; --accent-low-opacity: ${colors.accent}66; --text: ${colors.text}; --text-low-opacity: ${colors.text}88;`);
} }
function get_list(key) { function get_list(key, hasInput=true) {
let output = []; let output = [];
[...document.querySelectorAll(`#${key} div[id^="${key}-"]`)].forEach((val, index) => { [...document.querySelectorAll(`#${key} div[id^="${key}-"]`)].forEach((val, index) => {
if (!val.classList.contains("bad")) { if (!val.classList.contains("bad")) {
output.push([val.querySelector("input").value, val.querySelector("select").value]); output.push(hasInput ? [val.querySelector("input").value, val.querySelector("select").value] : val.querySelector("select").value);
} }
}); });
return output; return output;
@ -94,6 +94,12 @@ for (const key of Object.keys(socialRegex)) {
} }
socialInput += `</select><input class="bad" oninput="validate_input(this);" data-id="social-%i"></div><svg onclick="dom('social-%i').remove()">${icons.x}</svg>`; socialInput += `</select><input class="bad" oninput="validate_input(this);" data-id="social-%i"></div><svg onclick="dom('social-%i').remove()">${icons.x}</svg>`;
let flagInput = "<select data-id=\"flags-%i\">";
for (const key of Object.keys(flags)) {
flagInput += `<option value="${key}">${flags[key]}</option>`
}
flagInput += `</select><svg onclick="dom('flags-%i').remove()">${icons.x}</svg>`
fetch("/api/account/self", { fetch("/api/account/self", {
"method": "GET" "method": "GET"
}).then((response) => (response.json())) }).then((response) => (response.json()))
@ -129,7 +135,15 @@ fetch("/api/account/self", {
i++; i++;
} }
inner += `</div><button onclick="add_input('social', '${socialInput.replaceAll("\"", "&quot;").replaceAll("\'", "\\\'")}');">Add</button></div></div>`; inner += `</div><button onclick="add_input('social', '${socialInput.replaceAll("\"", "&quot;").replaceAll("\'", "\\\'")}');">Add</button></div></div>
<div class="added" style="text-align: center;"><div style="text-align: left; margin-bottom: 10px;" id='flags'><h2>Pride Flags</h2>`;
i = 0;
for (const flag of (json.flags || [])) {
inner += `<div id="social-${i}" data-id="${i}">${flagInput.split("</select")[0].replaceAll("%i", i).replace(`value="${flag}"`, `selected value="${flag}"`)}</select><svg onclick="dom('social-${i}').remove()">${icons.x}</svg></div>`;
i++;
}
inner += `</div><button onclick="add_input('flags', '${flagInput.replaceAll("\"", "&quot;").replaceAll("\'", "\\\'")}');">Add</button></div></div>`
x.id = "container"; x.id = "container";
x.innerHTML = inner; x.innerHTML = inner;
@ -157,6 +171,7 @@ fetch("/api/account/self", {
compliments: get_list("compliments"), compliments: get_list("compliments"),
relationship: get_list("relationship"), relationship: get_list("relationship"),
social: get_list("social"), social: get_list("social"),
flags: get_list("flags", false),
public: dom("public").checked public: dom("public").checked
}) })
}).then((response) => (response.text())) }).then((response) => (response.text()))

View file

@ -12,23 +12,5 @@
<body> <body>
{{TEMPLATE}} {{TEMPLATE}}
<div id="key">
Key:<br>
<span data-icon="4"></span> - Great<br>
<span data-icon="3"></span> - Good<br>
<span data-icon="2"></span> - Okay<br>
<span data-icon="1"></span> - Bad
</div>
<footer>
Icons from <a href="https://fontawesome.com" target="_blank">Font Awesome</a>
</footer>
<script>
[...document.querySelectorAll("[data-icon]")].forEach(function(val, index) {
val.innerHTML = icons[val.dataset.icon];
})
</script>
</body> </body>
</html> </html>