huge overhaul of homepage template

This commit is contained in:
mst 2024-10-20 00:04:06 +03:00
parent bd343be3d7
commit ffc2b30e27

View file

@ -5,205 +5,12 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/toastify.css') }}">
{% endblock %}
{% block content %}
<div class="mt-5 mb-sm-2 mb-md-5 col-sm-{% if cfg.style.infoBoxLayout == 'row' %}10{% else %}8{% endif %} m-auto{% if cfg.style.infoBoxLayout == 'row' %} d-lg-flex justify-content-between gap-2{% endif %}">
<div>
<h1 class="text-center fw-bold">{{ cfg.instance.title }}</h1>
{% autoescape off %}
<h2 class="h5 text-center fw-light">{{ cfg.instance.description | render_markdown }}</h2>
{% endautoescape %}
</div>
{% if len(cfg.instance.rules) > 0 %}
<div class="m-auto col-sm-{% if cfg.style.infoBoxLayout == 'row' %}6{% else %}8{% endif %}">
<div class="accordion" id="rules-accordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#rules" aria-expanded="false" aria-controls="collapseTwo">
<i class="bi bi-exclamation-triangle me-2 fs-4"></i> Rules
</button>
</h2>
<div id="rules" class="accordion-collapse collapse" data-bs-parent="#rules-accordion">
<div class="accordion-body">
<div class="markdown-content">{{ cfg.instance.rules | render_markdown }}</div>
</div>
</div>
</div>
</div>
</div>
{% endif %}
</div>
<div class="row">
<div class="col-sm-{% if combined %}4{% else %}6{% endif %}{% if not combined %} m-auto{% endif %}">
<div class="mb-5 sticky-md-top">
{% if cfg.lockInbox == false %}
<br>
<h2>Ask a question</h2>
<form class="d-lg-block" hx-post="{{ url_for('api.addQuestion') }}" id="question-form" hx-target="#response-container" hx-swap="none">
<div class="form-floating mb-2">
<input maxlength="{{ cfg.charLimit }}" {% if cfg.allowAnonQuestions == false %}required{% endif %} class="form-control" type="text" name="from_who" id="from_who" placeholder="Name {% if cfg.allowAnonQuestions == true %}(optional){% else %}(required){% endif %}">
<label for="from_who">Name {% if cfg.allowAnonQuestions == true %}(optional){% else %}(required){% endif %}</label>
</div>
<div class="form-floating mb-2">
<textarea maxlength="{{ cfg.charLimit }}" class="form-control" style="height: 100px;" required name="question" id="question" placeholder="Write your question..."></textarea>
<label for="question">Write your question...</label>
<p class="text-end mt-1 small d-flex align-itemcenter justify-content-between"><span class="text-body-secondary"><i class="bi bi-markdown me-1"></i> Markdown supported</span> <span id="charCount">{{ cfg.charLimit }}</span></p>
</div>
<div class="form-group mb-2">
<label for="antispam">Anti-spam: please enter the word <code class="text-uppercase">{{ getRandomWord() }}</code> in lowercase</label>
<input class="form-control" type="text" required name="antispam" id="antispam" autocomplete="off">
</div>
<div class="form-group d-grid d-lg-flex align-items-center justify-content-lg-end mt-3">
{#
<div class="form-check mb-0 w-100">
reserved for version 1.6.0 or later
<input
class="form-check-input"
type="checkbox"
name="_private"
id="_private">
<input type="hidden" id="private" name="private">
<label for="_private" class="form-check-label">Ask privately</label>
</div>
#}
<button type="submit" class="btn btn-primary col-sm-4" id="ask-btn">
<span class="me-1 spinner-border spinner-border-sm htmx-indicator" aria-hidden="true"></span>
<span class="visually-hidden" role="status">Loading...</span>
Ask
</button>
</div>
</form>
<div id="response-container" class="mt-3"></div>
{% else %}
<br>
<h2 class="text-center">New questions cannot be asked right now.</h2>
{% endif %}
</div>
</div>
{% if combined %}
<div class="col-sm-8">
{% if cfg.showQuestionCount == true %}
<h3 class="fs-4">{{ len(combined) }} <span class="fw-light">question(s)</span></h3>
{% endif %}
<div id="top-response-container"></div>
{% for item in combined %}
<div class="card mt-3 mb-3{% if item.question.pinned %} border-2{% endif %}" id="question-{{ item.question.id }}">
<div class="position-relative">
<div class="card-header{% if item.question.pinned %} border-2{% endif %}">
<div class="d-flex justify-content-between align-items-center flex-wrap text-break">
<h3 class="h5 card-title mt-1 mb-1">
{% if item.question.from_who %}
{{ item.question.from_who }}
{% else %}
<i class="bi bi-incognito" data-bs-toggle="tooltip" data-bs-title="This question was asked anonymously" data-bs-placement="top"></i> {{ cfg.anonName }}
{% endif %}
</h3>
<h3 class="h6 card-subtitle fw-light text-body-secondary" data-bs-toggle="tooltip" data-bs-title="{{ item.question.creation_date.strftime("%B %d, %Y %H:%M") }}" data-bs-placement="top">{{ formatRelativeTime(str(item.question.creation_date)) }}</h3>
</div>
<div class="card-text markdown-content">{{ item.question.content | render_markdown }}</div>
<!-- <h4 class="position-absolute bottom-0 end-0 fw-light mb-0 me-2 fs-2">#{{item.question.id}}</h4> -->
</div>
</div>
<div class="card-body">
{% for answer in item.answers %}
<div class="markdown-content">{{ answer.content | render_markdown }}</div>
</div>
<div class="card-footer pt-0 pb-0 ps-3 pe-1 text-body-secondary d-flex justify-content-between align-items-center{% if item.question.pinned %} border-2{% endif %}">
<div>
<span class="fs-6" data-bs-toggle="tooltip" data-bs-title="{{ answer.creation_date.strftime("%B %d, %Y %H:%M") }}">{{ formatRelativeTime(str(answer.creation_date)) }}</span>
{% if item.question.pinned %}
<span class="ms-1"><i class="bi bi-pin"></i> <span class="fw-medium">Pinned</span></span>
{% endif %}
</div>
<div class="d-flex align-items-center">
<a href="{{ url_for('viewQuestion', question_id=item.question.id) }}" class="btn btn-basic pt-2 pb-2 text-body-secondary" data-bs-toggle="tooltip" data-bs-title="View question" aria-label="View question"><i class="bi bi-box-arrow-up-right"></i></a>
<div class="dropdown">
<button class="btn btn-basic pt-2 pb-2 no-arrow text-body-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" aria-label="Share question"><i class="bi bi-share"></i></button>
<ul class="dropdown-menu">
<li><button class="dropdown-item" onclick="copyFull(`{{ trimContent(item.question.content, cfg.trimContentAfter) + ' — ' + trimContent(answer.content, cfg.trimContentAfter) }} {{ cfg.instance.fullBaseUrl + url_for('viewQuestion',question_id=item.question.id) }}`)"><i class="bi bi-copy me-1"></i> Copy to clipboard</button></li>
<li>
<button class="dropdown-item d-flex align-items-center gap-1" data-bs-toggle="modal" data-bs-target="#question-{{ item.question.id }}-modal">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 64 64" class="me-1">
<path fill="currentColor" d="M12.088 23.868a6.734 6.732 0 0 1-2.88 2.866L25.02 42.602l3.812-1.93Zm20.857 20.93-3.812 1.932 8.012 8.04a6.734 6.732 0 0 1 2.88-2.866z"/>
<path fill="currentColor" d="m51.24 30.147-8.952 4.535.66 4.22 10.128-5.131a6.734 6.732 0 0 1-1.837-3.624Zm-14.15 7.168L15.926 48.038a6.734 6.732 0 0 1 1.837 3.624l19.989-10.127z"/>
<path fill="currentColor" d="M30.284 10.9 20.071 30.833l3.016 3.027L33.9 12.755a6.734 6.732 0 0 1-3.616-1.854zm-12.87 25.117-5.172 10.095a6.734 6.732 0 0 1 3.615 1.855l4.573-8.925z"/>
<path fill="currentColor" d="M9.12 26.778a6.734 6.732 0 0 1-3.364.703 6.734 6.732 0 0 1-.65-.068l3.02 19.316a6.734 6.732 0 0 1 3.365-.703 6.734 6.732 0 0 1 .65.068z"/>
<path fill="currentColor" d="M17.779 51.758a6.734 6.732 0 0 1 .07 1.356 6.734 6.732 0 0 1-.71 2.656l19.318 3.099a6.734 6.732 0 0 1-.07-1.356 6.734 6.732 0 0 1 .71-2.656Z"/>
<path fill="currentColor" d="m53.144 33.841-8.917 17.402a6.734 6.732 0 0 1 3.617 1.855l8.916-17.402a6.734 6.732 0 0 1-3.616-1.855z"/>
<path fill="currentColor" d="M40.983 9.229a6.734 6.732 0 0 1-2.88 2.866L51.91 25.953a6.734 6.732 0 0 1 2.88-2.867z"/>
<path fill="currentColor" d="M28.38 7.206 10.922 16.05a6.734 6.732 0 0 1 1.837 3.624l17.456-8.844a6.734 6.732 0 0 1-1.837-3.624Z"/>
<path fill="currentColor" d="M38.07 12.111a6.734 6.732 0 0 1-3.42.731 6.734 6.732 0 0 1-.589-.062l1.546 9.898 4.22.677zm-1.564 16.322 3.656 23.402a6.734 6.732 0 0 1 3.315-.678 6.734 6.732 0 0 1 .705.077L40.726 29.11Z"/>
<path fill="currentColor" d="M12.772 19.748a6.734 6.732 0 0 1 .075 1.377 6.734 6.732 0 0 1-.7 2.637l9.909 1.59 1.947-3.801zm16.984 2.726-1.948 3.803 23.413 3.759a6.734 6.732 0 0 1-.068-1.341 6.734 6.732 0 0 1 .718-2.67z"/>
<circle fill="currentColor" cx="35.017" cy="6.12" r="6.12"/>
<circle fill="currentColor" cx="57.878" cy="29.062" r="6.12"/>
<circle fill="currentColor" cx="43.111" cy="57.88" r="6.12"/>
<circle fill="currentColor" cx="11.124" cy="52.749" r="6.12"/>
<circle fill="currentColor" cx="6.122" cy="20.759" r="6.12"/>
</svg>
Share on Fediverse
</button>
</li>
<li><a class="dropdown-item" target="_blank" href="https://twitter.com/intent/tweet?text={{ urllib.parse.quote(trimContent(item.question.content, cfg.trimContentAfter) + ' — ' + trimContent(answer.content, cfg.trimContentAfter),safe='') }}%20{{ urllib.parse.quote(cfg.instance.fullBaseUrl + url_for('viewQuestion',question_id=item.question.id), safe='') }}"><i class="bi bi-twitter me-1"></i> Share on Twitter</a></li>
<li>
<a class="dropdown-item d-flex align-items-center gap-1" target="_blank" href="https://bsky.app/intent/compose?text={{ urllib.parse.quote(trimContent(item.question.content, cfg.trimContentAfter) + ' — ' + trimContent(answer.content, cfg.trimContentAfter),safe='') }}%20{{ urllib.parse.quote(cfg.instance.fullBaseUrl + url_for('viewQuestion',question_id=item.question.id), safe='') }}">
<svg width="16" height="16" class="me-1" viewBox="0 0 568 501" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor" d="M123.121 33.6637C188.241 82.5526 258.281 181.681 284 234.873C309.719 181.681 379.759 82.5526 444.879 33.6637C491.866 -1.61183 568 -28.9064 568 57.9464C568 75.2916 558.055 203.659 552.222 224.501C531.947 296.954 458.067 315.434 392.347 304.249C507.222 323.8 536.444 388.56 473.333 453.32C353.473 576.312 301.061 422.461 287.631 383.039C285.169 375.812 284.017 372.431 284 375.306C283.983 372.431 282.831 375.812 280.369 383.039C266.939 422.461 214.527 576.312 94.6667 453.32C31.5556 388.56 60.7778 323.8 175.653 304.249C109.933 315.434 36.0535 296.954 15.7778 224.501C9.94525 203.659 0 75.2916 0 57.9464C0 -28.9064 76.1345 -1.61183 123.121 33.6637Z" fill="black"/>
</svg>
Share on Bluesky
</a>
</li>
<li>
<a class="dropdown-item d-flex align-items-center gap-1" target="_blank" href="https://www.tumblr.com/widgets/share/tool?shareSource=legacy&posttype=text&title={{ urllib.parse.quote(trimContent(item.question.content, cfg.trimContentAfter), safe='') }}&url={{ urllib.parse.quote(cfg.instance.fullBaseUrl + url_for('viewQuestion',question_id=item.question.id), safe='') }}&caption=&content={{ urllib.parse.quote(trimContent(answer.content, cfg.trimContentAfter),safe='') }}%20{{ urllib.parse.quote(cfg.instance.fullBaseUrl + url_for('viewQuestion',question_id=item.question.id), safe='') }}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="16" height="16" class="me-1">
<path d="M6.27051 7.62976C8.86829 7.07312 10.816 4.76401 10.816 2H13.8463V7.15152H17.4826V10.7879H13.8463V16.2424C13.8463 16.7566 14.044 17.4493 14.7554 17.9091C15.2296 18.2156 16.2397 18.3671 17.7857 18.3636V22H13.5432C11.0329 22 8.99778 19.9649 8.99778 17.4545V10.7879H6.27051V7.62976Z"></path>
</svg>
Share on Tumblr
</a>
</li>
</ul>
</div>
<div class="dropdown">
<button class="btn btn-basic pt-2 pb-2 no-arrow text-body-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" aria-label="Miscellaneous menu"><i class="bi bi-three-dots"></i></button>
<ul class="dropdown-menu">
<li><button class="dropdown-item" onclick="copy({{ item.question.id }})"><i class="bi bi-copy me-1"></i> Copy link</button></li>
{% if logged_in %}
{% if not item.question.pinned %}
<li><button class="dropdown-item" hx-post="{{ url_for('api.pinQuestion', question_id=item.question.id) }}" hx-target="#top-response-container" hx-swap="none"><i class="bi bi-pin me-1"></i> Pin</button></li>
{% else %}
<li><button class="dropdown-item" hx-post="{{ url_for('api.unpinQuestion', question_id=item.question.id) }}" hx-target="#top-response-container" hx-swap="none"><i class="bi bi-pin-angle me-1"></i> Unpin</button></li>
{% endif %}
<li><button class="bg-hover-danger text-danger dropdown-item" hx-post="{{ url_for('api.returnToInbox', question_id=item.question.id) }}" hx-target="#top-response-container" hx-swap="none" data-returntoinbox data-target="question-{{ item.question.id }}"><i class="bi bi-arrow-return-left me-1"></i> Return to inbox</button></li>
{% endif %}
</ul>
</div>
</div>
{% endfor %}
</div>
</div>
{% endfor %}
{% for item in combined %}
{% for answer in item.answers %}
<div class="modal fade" id="question-{{ item.question.id }}-modal" tabindex="-1" aria-labelledby="q-{{ item.question.id }}-modal-label" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="q-{{ item.question.id }}-modal-label">Share on Fediverse</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="form-group mb-3">
<label for="fediInstance">Fediverse instance domain:</label>
<input type="text" id="fediInstance-{{item.question.id}}" name="fediInstance" class="form-control">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="shareOnFediverse('{{ item.question.id }}', `{{ urllib.parse.quote(trimContent(item.question.content, cfg.trimContentAfter) + ' — ' + trimContent(answer.content, cfg.trimContentAfter),safe='') }}%20{{ urllib.parse.quote(cfg.instance.fullBaseUrl + '/q/' + str(item.question.id),safe='') }}`)">Share</button>
</div>
</div>
</div>
</div>
{% endfor %}
{% endfor %}
</div>
{% if cfg.style.homepageLayout == 'catask' %}
{% include 'snippets/layout/homepage/normal.html' %}
{% elif cfg.style.homepageLayout == 'retrospring' %}
{% include 'snippets/layout/homepage/retrospring.html' %}
{% endif %}
</div>
{% endblock %}
@ -218,6 +25,54 @@
});
</script>
<script>
function nativeShare(title, text, url) {
const shareData = {
title: title,
text: text,
url: url,
};
const shareBtns = document.querySelectorAll(".nativeShareBtn");
shareBtns.forEach((shareBtn) => {
shareBtn.addEventListener("click", async () => {
try {
await navigator.share(shareData);
}
catch (err) {
console.error(err);
}
});
});
};
</script>
<script>
document.addEventListener('DOMContentLoaded', function () {
const collapseElements = document.querySelectorAll('.collapse.question-cw');
const toggleButtons = document.querySelectorAll('.cw-btn');
const cwTexts = document.querySelectorAll('.cw-text');
collapseElements.forEach(function (collapseElement, index) {
let toggleButton = toggleButtons[index];
let cwText = cwTexts[index];
let buttonText = toggleButton.querySelector('.cw-btn-text');
let buttonCharsText = toggleButton.querySelector('.cw-btn-chars');
collapseElement.addEventListener('show.bs.collapse', function () {
buttonText.textContent = 'Hide content';
buttonCharsText.classList.add('d-none');
cwText.classList.remove('text-center');
cwText.classList.remove('fw-bold');
});
collapseElement.addEventListener('hide.bs.collapse', function () {
buttonText.textContent = 'Show content';
buttonCharsText.classList.remove('d-none');
cwText.classList.add('text-center');
cwText.classList.add('fw-bold');
});
});
});
function copy(questionId) {
navigator.clipboard.writeText("{{ cfg.instance.fullBaseUrl }}/q/" + questionId + "/");
Toastify({
@ -295,8 +150,12 @@
let targetElementId = event.detail.target.id;
if (targetElementId != "question-count") {
// WARNING: HACK
// we use this hack to avoid triggering the event listener twice when making a request to api.returnToInbox
if (document.getElementById(targetElementId) && event.detail.requestConfig.elt.dataset.returntoinbox === "") {
// we use this hack to avoid triggering the event listener twice when making a request to api.returnToInbox and api.(un)pinQuestion
if (
(document.getElementById(targetElementId) && event.detail.requestConfig.elt.dataset.deletetarget === "")
||
document.getElementById(targetElementId) && event.detail.requestConfig.elt.dataset.deletetarget === ""
) {
targetElementId = event.detail.requestConfig.elt.dataset.target;
document.getElementById(targetElementId).outerHTML = '';
}