git / moonbite-network / templates/discussions.html · raw
{% extends "base.html" %}{% import "partials/forum_macros.html" as fm with context %}{% block body_class %}{% endblock %}{% block title %}{% if category %}{{ categories|selectattr('slug', 'equalto', category)|map(attribute='name')|first }} — {% endif %}Discussions — MoonBite community forum{% endblock %}{% block meta_description %}The MoonBite community discussion board. Ask about mining, wallets and running a node. No account needed: pick a name and post.{% endblock %}{% block head_extra %}{% include "partials/forum_style.html" %}<style nonce="{{ csp_nonce }}"> /* Discussions index: page-specific pieces only. */ .fx-head { padding: 56px 0 48px; border-bottom: 1px solid var(--ds-line); } .fx-head .ds-wrap { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 24px 48px; } .fx-head .ds-title { font-size: clamp(40px, 5vw, 64px); } .fx-head .ds-lede { font-size: 18px; } .fx-head .ds-actions { margin-top: 0; } .fx-main { padding: 40px 0 96px; } /* category chips (after Discord's forum tags) */ .fx-chips { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 4px; margin: 0 0 16px; list-style: none; padding-left: 0; scrollbar-width: none; } .fx-chips::-webkit-scrollbar { display: none; } .fx-chip { display: inline-flex; align-items: center; gap: 8px; min-height: 40px; padding: 8px 14px; border-radius: 999px; border: 1px solid var(--ds-line-2); font-size: 14px; color: var(--ds-ash); white-space: nowrap; transition: color var(--ds-t1) var(--ds-ease), border-color var(--ds-t1) var(--ds-ease), background var(--ds-t1) var(--ds-ease); } .fx-chip:hover { color: var(--ds-bone); border-color: rgba(245, 242, 234, .36); } .fx-chip[aria-current] { background: var(--ds-bone); border-color: var(--ds-bone); color: var(--ds-void); } .fx-chip .n { font: 400 12px var(--ds-mono); opacity: .8; } /* toolbar: sort tabs + search */ .fx-tools { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 24px; } .fx-tabs { display: inline-flex; padding: 4px; gap: 2px; border-radius: 8px; border: 1px solid var(--ds-line); background: var(--ds-slate); } .fx-tabs a { min-height: 36px; display: inline-flex; align-items: center; padding: 6px 14px; border-radius: 6px; font-size: 14px; color: var(--ds-ash); transition: color var(--ds-t1) var(--ds-ease), background var(--ds-t1) var(--ds-ease); } .fx-tabs a:hover { color: var(--ds-bone); } .fx-tabs a[aria-current] { background: var(--ds-raise); color: var(--ds-bone); } .fx-search { position: relative; flex: 1 1 240px; max-width: 360px; } .fx-search .ds-input { min-height: 44px; padding-left: 40px; } .fx-search svg { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); color: var(--ds-ash); pointer-events: none; } /* composer launcher (after Discord's "Search or create a post") */ .fx-compose { border: 1px solid var(--ds-line); border-radius: 10px; background: var(--ds-slate); margin-bottom: 24px; scroll-margin-top: 96px; } .fx-compose > summary { list-style: none; display: flex; align-items: center; gap: 12px; padding: 12px 16px; cursor: pointer; color: var(--ds-ash); font-size: 15px; min-height: 56px; } .fx-compose > summary::-webkit-details-marker { display: none; } .fx-compose > summary .ph { flex: 1; min-width: 0; padding: 10px 14px; border-radius: 6px; border: 1px solid var(--ds-line); background: var(--ds-ink); } .fx-compose > summary:hover .ph { border-color: var(--ds-line-2); color: var(--ds-bone); } .fx-compose[open] > summary { border-bottom: 1px solid var(--ds-line); } .fx-compose[open] > summary .ph { display: none; } .fx-compose[open] > summary::after { content: "Cancel"; margin-left: auto; font-size: 14px; color: var(--ds-ash); } .fx-compose .fx-form { padding: 24px; } .fx-compose .lbl { color: var(--ds-bone); font-weight: 700; } .fx-compose[open] .lbl-closed, .fx-compose:not([open]) .lbl { display: none; } /* feed cards (after Reddit) */ .fx-feed { display: flex; flex-direction: column; gap: 12px; } .fx-card { position: relative; display: grid; grid-template-columns: 48px minmax(0, 1fr); gap: 12px; padding: 16px 20px 12px 8px; border: 1px solid var(--ds-line); border-radius: 10px; background: var(--ds-slate); transition: border-color var(--ds-t2) var(--ds-ease), background var(--ds-t2) var(--ds-ease); } .fx-card:hover { border-color: var(--ds-line-2); } .fx-card:has(.ttl:focus-visible) { outline: 2px solid var(--ds-gold); outline-offset: 2px; } .fx-card .main { min-width: 0; display: flex; flex-direction: column; gap: 8px; } .fx-card .meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; font-size: 13px; color: var(--ds-ash); } .fx-card .meta b { color: var(--ds-bone); font-weight: 600; } .fx-card .meta .dot::before { content: "·"; } .fx-card .fx-cat { position: relative; z-index: 1; color: var(--ds-bone); } .fx-card .ttl { font-size: 19px; font-weight: 700; letter-spacing: -.01em; line-height: 1.3; color: var(--ds-bone); overflow-wrap: anywhere; } .fx-card .ttl::after { content: ""; position: absolute; inset: 0; border-radius: 10px; } .fx-card .ttl:focus-visible { outline: none; } .fx-card .ex { margin: 0; font-size: 15px; color: var(--ds-ash); display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; } .fx-card .bar { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; margin: 2px 0 0 -12px; } .fx-card .bar .fx-stack { margin-left: 8px; } .fx-new { font: 400 11px var(--ds-mono); padding: 1px 6px; border-radius: 4px; border: 1px solid var(--ds-line-2); color: var(--ds-ash); } .fx-empty { padding: 64px 24px; text-align: center; } .fx-empty .ds-h3 { margin-top: 16px; } .fx-empty p { color: var(--ds-ash); margin: 8px auto 24px; max-width: 420px; } .fx-pages { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-top: 24px; } .fx-pages .at { font: 400 13px var(--ds-mono); color: var(--ds-ash); } @media (max-width: 767px) { .fx-head { padding: 40px 0 32px; } .fx-card { grid-template-columns: 40px minmax(0, 1fr); gap: 8px; padding: 14px 14px 10px 4px; } .fx-card .ttl { font-size: 17px; } .fx-tools { flex-direction: column; align-items: stretch; } .fx-search { max-width: none; flex-basis: auto; } .fx-tabs { overflow-x: auto; } }</style>{% endblock %}{% block content %}{% set cat_name = categories|selectattr('slug', 'equalto', category)|map(attribute='name')|first if category else '' %}{% macro qs(c=category, s=sort, query=q, p=1) -%}{{ url_for('discussions_page', c=c or None, sort=(s if s != 'active' else None), q=query or None, page=(p if p > 1 else None)) }}{%- endmacro %}<div class="ds"> <!-- ================= HEAD ================= --> <header class="fx-head"> <div class="ds-wrap"> <div> <ol class="ds-crumbs"> <li><a href="/">MoonBite</a></li> <li><a href="/community">Community</a></li> <li><span aria-current="page">Discussions</span></li> </ol> <h1 class="ds-title">Talk it <span class="g">through.</span></h1> <p class="ds-lede">Questions about mining, wallets or running a node. <b>No account, no email:</b> pick a name and post.</p> </div> <div class="ds-actions"> <a class="ds-btn gold" href="#new">Start a discussion <svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path d="M8 3v10M3 8h10" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg> </a> </div> </div> </header> <div class="fx-main"> <div class="ds-wrap fx-layout"> <!-- ================= MAIN COLUMN ================= --> <div> {% if error %}<div class="fx-alert" role="alert">{{ error }}</div>{% endif %} <nav aria-label="Categories"> <ul class="fx-chips"> <li><a class="fx-chip" href="{{ qs(c='') }}" {% if not category %}aria-current="page"{% endif %}>All <span class="n">{{ stats.threads }}</span></a></li> {% for c in categories %} <li><a class="fx-chip" href="{{ qs(c=c.slug) }}" {% if category == c.slug %}aria-current="page"{% endif %} title="{{ c.desc }}">{{ c.name }} <span class="n">{{ stats.per_category[c.slug] }}</span></a></li> {% endfor %} </ul> </nav> <div class="fx-tools"> <nav class="fx-tabs" aria-label="Sort"> {% for key, label in [('hot', 'Hot'), ('new', 'New'), ('top', 'Top'), ('unanswered', 'Unanswered')] %} <a href="{{ qs(s=key) }}" {% if sort == key %}aria-current="page"{% endif %}>{{ label }}</a> {% endfor %} </nav> <form class="fx-search" method="get" action="{{ url_for('discussions_page') }}" role="search"> {% if category %}<input type="hidden" name="c" value="{{ category }}">{% endif %} {% if sort != 'hot' %}<input type="hidden" name="sort" value="{{ sort }}">{% endif %} <svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><circle cx="7" cy="7" r="4.5" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M10.5 10.5L14 14" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg> <label class="sr" for="fx-q">Search discussions</label> <input class="ds-input" type="search" id="fx-q" name="q" value="{{ q }}" maxlength="80" placeholder="Search {{ cat_name or 'all discussions' }}"> </form> </div> <!-- composer --> <details class="fx-compose" id="new" {% if error %}open{% endif %}> <summary> <span class="fx-av t0" aria-hidden="true">+</span> <span class="ph lbl-closed">Create a post: ask a question or share something</span> <span class="lbl">Create a post</span> </summary> <form class="fx-form" method="post" action="{{ url_for('discussions_create') }}"> {{ fm.honeypot() }} <div class="ds-field"> <label for="new-title">Title</label> <input class="ds-input" type="text" id="new-title" name="title" maxlength="120" placeholder="What do you want to discuss?" required> </div> <div class="row2"> <div class="ds-field"> <label for="new-cat">Category</label> <select class="ds-input" id="new-cat" name="category"> {% for c in categories if not c.official_only %}<option value="{{ c.slug }}" {% if (category or 'general') == c.slug %}selected{% endif %}>{{ c.name }}</option>{% endfor %} </select> </div> <div class="ds-field"> <label for="new-author">Your name</label> <input class="ds-input" type="text" id="new-author" name="author" maxlength="40" placeholder="anon" autocomplete="off" data-name> </div> </div> <div class="ds-field"> <label for="new-body">Message</label> <textarea class="ds-input" id="new-body" name="body" maxlength="8000" placeholder="Give enough detail that someone can help: your OS, what you ran, what happened." required data-max="8000"></textarea> </div> <div class="foot"> {{ fm.format_hint() }} <span class="count" aria-live="polite"></span> </div> {{ fm.as_official() }} <div class="foot"> <span class="hint">Posts are public and stored on this server.</span> <button type="submit" class="ds-btn gold">Post</button> </div> </form> </details> <!-- topic list --> {% if threads or pinned %} {% if q %}<p class="ds-eyebrow" style="margin-bottom: 16px;">{{ total }} result{{ '' if total == 1 else 's' }} for “{{ q }}”</p>{% endif %} <div class="fx-feed"> {% for t in pinned + threads %} {% set link = url_for('discussion_thread_page', thread_id=t.id) %} <article class="fx-card"> {{ fm.vote('t', t.id, t.score, mine.get(t.id, 0)) }} <div class="main"> <div class="meta"> <a class="fx-cat c-{{ t.category }}" href="{{ qs(c=t.category) }}">{{ t.category_name }}</a> <span class="dot"></span> {% if t.pinned %}<span class="fx-pin">Pinned</span>{% endif %} <span>Posted by {{ fm.author(t) }}</span> <span class="dot"></span> <time datetime="{{ t.created_at }}" title="{{ t.created_display }}">{{ fm.ago(t.created_ago) }}</time> {% if t.reply_count == 0 %}<span class="fx-new">unanswered</span>{% endif %} </div> <a class="ttl" href="{{ link }}">{{ t.title|emojify }}</a> <p class="ex">{{ t.excerpt|emojify }}</p> <div class="bar"> <a class="fx-act-btn pill" href="{{ link }}#comments">{{ fm.icon_comment() }} {{ t.reply_count }} comment{{ '' if t.reply_count == 1 else 's' }}</a> <button type="button" class="fx-act-btn" data-share="{{ link }}">{{ fm.icon_share() }} Share</button> {% if t.reply_count %}{{ fm.stack(t.voices, 3) }}{% endif %} </div> </div> </article> {% endfor %} </div> {% if total_pages > 1 %} <nav class="fx-pages" aria-label="Pages"> {% if page > 1 %}<a class="ds-btn sm" href="{{ qs(p=page-1) }}">Newer</a>{% else %}<span></span>{% endif %} <span class="at">Page {{ page }} of {{ total_pages }}</span> {% if page < total_pages %}<a class="ds-btn sm" href="{{ qs(p=page+1) }}">Older</a>{% else %}<span></span>{% endif %} </nav> {% endif %} {% else %} <div class="fx-panel fx-empty"> <svg width="48" height="48" viewBox="0 0 48 48" aria-hidden="true"><rect x="6" y="10" width="30" height="20" rx="6" fill="none" stroke="#8A8580" stroke-width="2"/><path d="M14 30v6l6-6" fill="none" stroke="#8A8580" stroke-width="2" stroke-linejoin="round"/><rect x="20" y="20" width="22" height="16" rx="5" fill="#161A22" stroke="#F5F2EA" stroke-width="2"/></svg> {% if q %} <h2 class="ds-h3">Nothing matches “{{ q }}”</h2> <p>Try fewer words, or ask it as a new discussion so the next person finds the answer.</p> <a class="ds-btn" href="{{ qs(query='') }}">Clear search</a> {% elif sort == 'unanswered' %} <h2 class="ds-h3">Every question has a reply</h2> <p>Nothing is waiting for an answer{% if cat_name %} in {{ cat_name }}{% endif %}.</p> <a class="ds-btn" href="{{ qs(s='hot') }}">See what's hot</a> {% else %} <h2 class="ds-h3">No discussions {% if cat_name %}in {{ cat_name }} {% endif %}yet</h2> <p>Ask the first question. Early threads become the answers everyone else finds later.</p> <a class="ds-btn" href="#new">Start a discussion</a> {% endif %} </div> {% endif %} </div> <!-- ================= SIDEBAR ================= --> <aside class="fx-side" aria-label="About this board"> <section class="fx-panel" aria-labelledby="about-h"> <h2 id="about-h">{{ cat_name or 'This board' }}</h2> {% if category %} <p>{{ (categories|selectattr('slug', 'equalto', category)|first).desc }}</p> {% else %} <p>The public board for MoonBite. Anyone can read, anyone can post.</p> {% endif %} <dl class="fx-stats" style="margin-top: 20px;"> <div><dt>threads</dt><dd>{{ stats.threads }}</dd></div> <div><dt>replies</dt><dd>{{ stats.replies }}</dd></div> <div><dt>voices</dt><dd>{{ stats.voices }}</dd></div> <div><dt>posts this week</dt><dd>{{ stats.week }}</dd></div> </dl> </section> {{ fm.rules() }} <section class="fx-panel" aria-labelledby="else-h"> <h2 id="else-h">Elsewhere</h2> <ul class="fx-links"> {% if is_mod %}<li><a href="{{ url_for('discussions_mod_page') }}">Moderator desk</a>: reports and growth</li>{% endif %} {% if discord_url %}<li><a href="{{ discord_url }}" target="_blank" rel="noopener">Discord</a> for live chat</li>{% endif %} <li><a href="/community">Community hub</a>: live chain and ways to help</li> <li><a href="/learn">Learn</a>: answers to common questions</li> <li><a href="mailto:support@moonbite.org">support@moonbite.org</a> for security reports</li> </ul> </section> </aside> </div> </div></div>{% endblock %}{% block scripts_extra %}{% include "partials/forum_script.html" %}{% endblock %}