8 Commits

4 changed files with 51 additions and 9 deletions
+26 -2
View File
@@ -591,6 +591,9 @@ select:focus { border-color: var(--accent); }
<select id="categoryFilter"> <select id="categoryFilter">
<option value="">Všechny kategorie</option> <option value="">Všechny kategorie</option>
</select> </select>
<select id="instanceFilter">
<option value="">Všechny instance</option>
</select>
<select id="sortFilter"> <select id="sortFilter">
<option value="score">Nejvyšší skóre</option> <option value="score">Nejvyšší skóre</option>
<option value="followers" selected>Nejvíce sledujících</option> <option value="followers" selected>Nejvíce sledujících</option>
@@ -765,19 +768,22 @@ function filterByTag(tag) {
function applyFilters() { function applyFilters() {
const q = document.getElementById('searchInput').value.toLowerCase().trim(); const q = document.getElementById('searchInput').value.toLowerCase().trim();
const cat = document.getElementById('categoryFilter').value; const cat = document.getElementById('categoryFilter').value;
const inst = document.getElementById('instanceFilter').value;
const sort = document.getElementById('sortFilter').value; const sort = document.getElementById('sortFilter').value;
const activeTag = document.querySelector('.ftag.active')?.dataset.tag || ''; const activeTag = document.querySelector('.ftag.active')?.dataset.tag || '';
filtered = allAccounts.filter(a => { filtered = allAccounts.filter(a => {
const handle = a.acct || a.handle || '';
const matchQ = !q || const matchQ = !q ||
a.name.toLowerCase().includes(q) || a.name.toLowerCase().includes(q) ||
(a.bio || '').toLowerCase().includes(q) || (a.bio || '').toLowerCase().includes(q) ||
(a.handle || '').toLowerCase().includes(q) || handle.toLowerCase().includes(q) ||
(a.tags || []).some(t => t.toLowerCase().includes(q)); (a.tags || []).some(t => t.toLowerCase().includes(q));
const matchCat = !cat || a.category === cat; const matchCat = !cat || a.category === cat;
const matchInst = !inst || handle.split('@').pop() === inst;
const matchTag = !activeTag || const matchTag = !activeTag ||
(a.tags || []).some(t => t.toLowerCase().includes(activeTag.toLowerCase())); (a.tags || []).some(t => t.toLowerCase().includes(activeTag.toLowerCase()));
return matchQ && matchCat && matchTag; return matchQ && matchCat && matchInst && matchTag;
}); });
filtered.sort((a, b) => { filtered.sort((a, b) => {
@@ -904,6 +910,7 @@ function downloadCSV(content, filename) {
// ──────────────────────────────── // ────────────────────────────────
document.getElementById('searchInput').addEventListener('input', applyFilters); document.getElementById('searchInput').addEventListener('input', applyFilters);
document.getElementById('categoryFilter').addEventListener('change', applyFilters); document.getElementById('categoryFilter').addEventListener('change', applyFilters);
document.getElementById('instanceFilter').addEventListener('change', applyFilters);
document.getElementById('sortFilter').addEventListener('change', applyFilters); document.getElementById('sortFilter').addEventListener('change', applyFilters);
document.querySelectorAll('.ftag').forEach(btn => { document.querySelectorAll('.ftag').forEach(btn => {
@@ -963,6 +970,23 @@ function buildDynamicUI() {
sel.appendChild(opt); sel.appendChild(opt);
}); });
// --- Instance select ---
const instanceCount = {};
allAccounts.forEach(a => {
const handle = a.acct || a.handle || '';
const instance = handle.includes('@') ? handle.split('@').pop() : '';
if (instance) instanceCount[instance] = (instanceCount[instance] || 0) + 1;
});
const instances = Object.keys(instanceCount).sort((a, b) => a.localeCompare(b, 'cs'));
const instSel = document.getElementById('instanceFilter');
instSel.innerHTML = '<option value="">Všechny instance</option>';
instances.forEach(instance => {
const opt = document.createElement('option');
opt.value = instance;
opt.textContent = `${instance} (${instanceCount[instance]})`;
instSel.appendChild(opt);
});
// --- Hashtag tlačítka top 8 nejčastějších tagů --- // --- Hashtag tlačítka top 8 nejčastějších tagů ---
const tagCount = {}; const tagCount = {};
allAccounts.forEach(a => (a.tags || []).forEach(t => { allAccounts.forEach(a => (a.tags || []).forEach(t => {
+1 -2
View File
@@ -55,7 +55,6 @@ amarok@mastodonczech.cz,true,false,
anlexcz@witter.cz,true,false, anlexcz@witter.cz,true,false,
lacertacz@mastodonczech.cz,true,false, lacertacz@mastodonczech.cz,true,false,
srandista@mastodonczech.cz,true,false, srandista@mastodonczech.cz,true,false,
politiq@mastodon.arch-linux.cz,true,false,
aikencz@f.cz,true,false, aikencz@f.cz,true,false,
sandruska93@mas.to,true,false, sandruska93@mas.to,true,false,
karelcapek@mastodonczech.cz,true,false, karelcapek@mastodonczech.cz,true,false,
@@ -97,7 +96,7 @@ nacelnik01@mamutovo.cz,true,false,
sledge@mastodonczech.cz,true,false, sledge@mastodonczech.cz,true,false,
tensob_@mastodonczech.cz,true,false tensob_@mastodonczech.cz,true,false
archos@mamutovo.cz,true,false, archos@mamutovo.cz,true,false,
archlinux@mamutovo.cz,true,false, arch@gts.arch-linux.cz,true,false,
electric@vaclavpasek.cz,true,false, electric@vaclavpasek.cz,true,false,
adamhavelka@mastodon.prorocketeers.com,true,false, adamhavelka@mastodon.prorocketeers.com,true,false,
Kipe@mastodon.social,true,false, Kipe@mastodon.social,true,false,
1 medvidekpu@mastodon.social true false
55 anlexcz@witter.cz true false
56 lacertacz@mastodonczech.cz true false
57 srandista@mastodonczech.cz true false
politiq@mastodon.arch-linux.cz true false
58 aikencz@f.cz true false
59 sandruska93@mas.to true false
60 karelcapek@mastodonczech.cz true false
96 sledge@mastodonczech.cz true false
97 tensob_@mastodonczech.cz true false
98 archos@mamutovo.cz true false
99 archlinux@mamutovo.cz arch@gts.arch-linux.cz true false
100 electric@vaclavpasek.cz true false
101 adamhavelka@mastodon.prorocketeers.com true false
102 Kipe@mastodon.social true false
+17 -2
View File
@@ -50,9 +50,19 @@ _TOKENS = _load_tokens()
MASTODON_TOKEN = _TOKENS.get("MASTODON_TOKEN") MASTODON_TOKEN = _TOKENS.get("MASTODON_TOKEN")
GTS_TOKEN = _TOKENS.get("GTS_TOKEN") GTS_TOKEN = _TOKENS.get("GTS_TOKEN")
_gts_cache: dict[str, bool] = {}
def _is_gts(instance: str) -> bool:
if instance in _gts_cache:
return _gts_cache[instance]
info = api_get(f"https://{instance}/api/v1/instance")
version = (info or {}).get("version", "") if isinstance(info, dict) else ""
result = "git" in version or version.startswith("0.")
_gts_cache[instance] = result
return result
def _token_for(instance: str) -> str | None: def _token_for(instance: str) -> str | None:
"""Vrátí GTS_TOKEN pro GoToSocial instance (obsahují 'gts.' v doméně), jinak MASTODON_TOKEN.""" if GTS_TOKEN and _is_gts(instance):
if GTS_TOKEN and "gts." in instance:
return GTS_TOKEN return GTS_TOKEN
return MASTODON_TOKEN return MASTODON_TOKEN
@@ -167,6 +177,11 @@ def load_manual_accounts(seen_handles=None):
url = f"https://{instance}/api/v1/accounts/lookup?acct={urllib.parse.quote(handle_part)}" url = f"https://{instance}/api/v1/accounts/lookup?acct={urllib.parse.quote(handle_part)}"
token = _token_for(instance) token = _token_for(instance)
acc = api_get(url, token=token) acc = api_get(url, token=token)
if not acc or not isinstance(acc, dict):
log.debug(f" {instance}: is_gts={_is_gts(instance)}, gts_token={GTS_TOKEN is not None}")
if GTS_TOKEN and _is_gts(instance):
log.debug(f" {handle}: zkouším GTS_TOKEN")
acc = api_get(url, token=GTS_TOKEN)
if not acc or not isinstance(acc, dict): if not acc or not isinstance(acc, dict):
log.warning(f" {handle}: lookup selhal") log.warning(f" {handle}: lookup selhal")
continue continue
+7 -3
View File
@@ -89,7 +89,7 @@
/* --- CARDS GRID --- */ /* --- CARDS GRID --- */
.cards { .cards {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr 1fr;
gap: 1rem; gap: 1rem;
} }
@@ -471,12 +471,16 @@ Rád/a poznám nové lidi 🙂 #Představení #novacek #cesky
<p>Doporučené appky pro Android a iOS.</p> <p>Doporučené appky pro Android a iOS.</p>
<a href="apps.html" class="btn btn-secondary">Zobrazit →</a> <a href="apps.html" class="btn btn-secondary">Zobrazit →</a>
</div> </div>
<div class="card">
<h3>💬 Chat v reálném čase</h3>
<p>Mastodon je na příspěvky, Matrix na chat. mxchat.cz je český Matrix server.</p>
<a href="https://web.mxchat.cz" class="btn btn-secondary" target="_blank">Otevřít →</a>
</div>
</div> </div>
</div> </div>
<div class="footer-note"> <div class="footer-note">
Zasekl/a ses? Napiš na <a href="https://mamutovo.cz/@archos">@archos@mamutovo.cz</a> nebo Tipy a triky: <a href="https://mamutovo.cz/tags/tip_mastodon">#tip_mastodon</a> · Potřebuješ pomoc? Napiš <a href="https://mamutovo.cz/@archos">@archos@mamutovo.cz</a><br>
se zeptej v <a href="https://mamutovo.cz/tags/pomoc">#pomoc</a>.<br>
Tato stránka je open source: <a href="https://git.arch-linux.cz/Mamutovo/fedi_start">Gitea</a> · Tato stránka je open source: <a href="https://git.arch-linux.cz/Mamutovo/fedi_start">Gitea</a> ·
<a href="https://oscloud.cz">oscloud.cz</a> <a href="https://oscloud.cz">oscloud.cz</a>
</div> </div>