Compare commits
19 Commits
5d2931f314
...
v1.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
a863ae498d
|
|||
|
00768bd887
|
|||
|
5ba472f87a
|
|||
|
7d115e8b43
|
|||
|
6a69b2c4ba
|
|||
|
b958d6616b
|
|||
|
430cbb6c10
|
|||
|
7754449b8b
|
|||
|
a530ebae35
|
|||
|
689fd437f6
|
|||
|
3cc80eaef9
|
|||
|
662b462192
|
|||
|
1f5bf3fc02
|
|||
|
faf2ae3c5e
|
|||
|
3460e45a37
|
|||
|
55359c25c3
|
|||
|
fe1ad16e50
|
|||
|
83992f6112
|
|||
|
1944c56d9c
|
+59
-4
@@ -384,6 +384,15 @@ select:focus { border-color: var(--accent); }
|
|||||||
border: 1px solid rgba(0,200,150,0.15);
|
border: 1px solid rgba(0,200,150,0.15);
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
padding: 0.15rem 0.5rem;
|
padding: 0.15rem 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
transition: background 0.15s, border-color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-tag:hover {
|
||||||
|
background: rgba(0,200,150,0.22);
|
||||||
|
border-color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-stats {
|
.card-stats {
|
||||||
@@ -582,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>
|
||||||
@@ -731,22 +743,47 @@ let viewMode = 'grid'; // 'grid' | 'list'
|
|||||||
// ────────────────────────────────
|
// ────────────────────────────────
|
||||||
// FILTER + SORT
|
// FILTER + SORT
|
||||||
// ────────────────────────────────
|
// ────────────────────────────────
|
||||||
|
function filterByTag(tag) {
|
||||||
|
const wrap = document.getElementById('filterTags');
|
||||||
|
const existing = wrap.querySelector(`.ftag[data-tag="${CSS.escape(tag)}"]`);
|
||||||
|
if (existing) {
|
||||||
|
wrap.querySelectorAll('.ftag').forEach(b => b.classList.remove('active'));
|
||||||
|
existing.classList.add('active');
|
||||||
|
} else {
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
btn.className = 'ftag active';
|
||||||
|
btn.dataset.tag = tag;
|
||||||
|
btn.textContent = '#' + tag;
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
wrap.querySelectorAll('.ftag').forEach(b => b.classList.remove('active'));
|
||||||
|
btn.classList.add('active');
|
||||||
|
applyFilters();
|
||||||
|
});
|
||||||
|
wrap.querySelectorAll('.ftag').forEach(b => b.classList.remove('active'));
|
||||||
|
wrap.appendChild(btn);
|
||||||
|
}
|
||||||
|
applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
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) => {
|
||||||
@@ -792,8 +829,8 @@ function avatarFallback(name) {
|
|||||||
|
|
||||||
function cardHTML(a, idx) {
|
function cardHTML(a, idx) {
|
||||||
const av = a.avatar || avatarFallback(a.name);
|
const av = a.avatar || avatarFallback(a.name);
|
||||||
const tags = (a.tags || []).slice(0, 3).map(t =>
|
const tags = (a.tags || []).slice(0, 4).map(t =>
|
||||||
`<span class="card-tag">#${t}</span>`).join('');
|
`<span class="card-tag" onclick="event.stopPropagation();filterByTag('${t.replace(/'/g, "\\'")}')">#${t}</span>`).join('');
|
||||||
|
|
||||||
if (viewMode === 'list') {
|
if (viewMode === 'list') {
|
||||||
return `
|
return `
|
||||||
@@ -873,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 => {
|
||||||
@@ -932,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 => {
|
||||||
|
|||||||
@@ -451,14 +451,15 @@ h1 span { color: var(--accent); }
|
|||||||
|
|
||||||
<!-- POČÍTAČ -->
|
<!-- POČÍTAČ -->
|
||||||
<div class="card" data-platforms="pocitac">
|
<div class="card" data-platforms="pocitac">
|
||||||
<div class="app-logo"><img src="img/apps/mast.webp" alt="Mast"></div>
|
<div class="app-logo"><img src="img/apps/tuba.svg" alt="Tuba"></div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="card-name">Mast</div>
|
<div class="card-name">Tuba</div>
|
||||||
<div class="card-tags">
|
<div class="card-tags">
|
||||||
<span class="tag tag-platform">Počítač</span>
|
<span class="tag tag-platform">Počítač</span>
|
||||||
<span class="tag tag-paid">Placené</span>
|
<span class="tag tag-free">Zdarma</span>
|
||||||
|
<span class="tag tag-open">Otevřená</span>
|
||||||
</div>
|
</div>
|
||||||
<a href="https://apps.apple.com/app/mast-for-mastodon/id1437429129" class="btn-dl" target="_blank" rel="noopener">Stáhnout →</a>
|
<a href="https://tuba.geopjr.dev" class="btn-dl" target="_blank" rel="noopener">Stáhnout →</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -218,6 +218,12 @@
|
|||||||
<p>Hashtagy jsou na Mastodonu důležité – slouží k objevování obsahu. Přidávej je na konec tootů. Můžeš sledovat i samotný hashtag a jeho příspěvky se ti budou zobrazovat v timeline. Příklady: <code>#cesky</code> <code>#fotografie</code> <code>#linux</code></p>
|
<p>Hashtagy jsou na Mastodonu důležité – slouží k objevování obsahu. Přidávej je na konec tootů. Můžeš sledovat i samotný hashtag a jeho příspěvky se ti budou zobrazovat v timeline. Příklady: <code>#cesky</code> <code>#fotografie</code> <code>#linux</code></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h2>Zvýrazněné hashtagy</h2>
|
||||||
|
<p>Nastav si zvýrazněné hashtagy – zobrazí se na tvém profilu a v seznamu CZ/SK účtů na <a href="https://fedi.mamutovo.cz">fedi.mamutovo.cz</a>.</p>
|
||||||
|
<p style="margin-top:0.5rem;">Jak na to: <code>Předvolby → Profil → Zvýrazněné hashtagy</code></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>Užitečné odkazy</h2>
|
<h2>Užitečné odkazy</h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 51 KiB |
+8
-2
@@ -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,
|
||||||
@@ -105,3 +104,10 @@ zpravobot@zpravobot.news,true,false,
|
|||||||
ozzelot@mstdn.social,true,false,
|
ozzelot@mstdn.social,true,false,
|
||||||
j4n3z@mastodon.social,true,false,
|
j4n3z@mastodon.social,true,false,
|
||||||
prahou@merveilles.town,true,false,
|
prahou@merveilles.town,true,false,
|
||||||
|
mirek@rodina-sucha.cz,true,false,
|
||||||
|
sesivany@social.vivaldi.net,true,false,
|
||||||
|
jimmac@mastodon.social,true,false,
|
||||||
|
Oskar456@mastodon.social,true,false,
|
||||||
|
jmlich@fosstodon.org,true,false,
|
||||||
|
ronny@metalhead.club,true,false,
|
||||||
|
vancura@mastodon.design,true,false,
|
||||||
|
|||||||
|
+54
-17
@@ -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
|
||||||
@@ -216,15 +231,28 @@ def score(acc):
|
|||||||
|
|
||||||
# ── KATEGORIE ─────────────────────────────────
|
# ── KATEGORIE ─────────────────────────────────
|
||||||
CATEGORIES = {
|
CATEGORIES = {
|
||||||
"tech": ["linux", "python", "programov", "software", "opensource", "developer", "sysadmin", "git"],
|
"tech": ["linux", "python", "programov", "software", "opensource", "developer", "sysadmin", "git", "foss", "selfhosted", "homelab", "arch"],
|
||||||
"foto": ["fotografi", "foto", "photograph", "objektiv", "kamera"],
|
"foto": ["fotografi", "foto", "photograph", "objektiv", "kamera"],
|
||||||
"veda": ["věda", "fyzika", "biologi", "astronom", "výzkum", "science", "matematik"],
|
"veda": ["věda", "fyzika", "biologi", "astronom", "výzkum", "science", "matematik"],
|
||||||
"kultura": ["knihy", "literatura", "film", "hudba", "divadlo", "umění"],
|
"kultura": ["knihy", "literatura", "film", "hudba", "divadlo", "umění"],
|
||||||
"gaming": ["gaming", "hry", "videohry", "steam", "gamer"],
|
"gaming": ["gaming", "hry", "videohry", "steam", "gamer"],
|
||||||
"zpravy": ["novinář", "zprávy", "politik", "média", "journalist"],
|
"zpravy": ["novinář", "zprávy", "politik", "média", "journalist", "zpravy", "news", "aktualne"],
|
||||||
|
"sport": ["sport", "fotbal", "hokej", "cycling", "running", "fitness", "tenis", "atletika", "cyklistika", "kolo", "beh", "plavani", "turistika"],
|
||||||
|
"politika": ["politika", "politics", "czech", "democracy", "volby", "eu"],
|
||||||
|
"fediverse": ["fediverse", "mastodon", "activitypub", "mamutovo"],
|
||||||
|
"cestovani": ["cestovani", "cestování", "travel", "dovolena"],
|
||||||
|
"priroda": ["příroda", "priroda", "les", "hory", "zahrada"],
|
||||||
|
"jidlo": ["jídlo", "jidlo", "vareni", "vaření", "recept", "food"],
|
||||||
}
|
}
|
||||||
|
|
||||||
def categorize(acc):
|
def categorize(acc):
|
||||||
|
# Primárně matchuj featured_tags proti CATEGORIES
|
||||||
|
for tag in acc.get("_featured_tags", []):
|
||||||
|
tag_lower = tag.lower()
|
||||||
|
for cat, kws in CATEGORIES.items():
|
||||||
|
if any(kw in tag_lower for kw in kws):
|
||||||
|
return cat
|
||||||
|
# Fallback: bio text + display_name
|
||||||
text = re.sub(r"<[^>]+>", " ", acc.get("note", "") or "").lower()
|
text = re.sub(r"<[^>]+>", " ", acc.get("note", "") or "").lower()
|
||||||
text += " " + (acc.get("display_name", "") or "").lower()
|
text += " " + (acc.get("display_name", "") or "").lower()
|
||||||
for cat, kws in CATEGORIES.items():
|
for cat, kws in CATEGORIES.items():
|
||||||
@@ -232,14 +260,23 @@ def categorize(acc):
|
|||||||
return cat
|
return cat
|
||||||
return "ostatni"
|
return "ostatni"
|
||||||
|
|
||||||
def extract_tags(acc):
|
def fetch_featured_tags(acc):
|
||||||
text = re.sub(r"<[^>]+>", " ", acc.get("note", "") or "").lower()
|
if "_featured_tags" in acc:
|
||||||
found = []
|
return acc["_featured_tags"]
|
||||||
for kws in CATEGORIES.values():
|
account_id = acc.get("id")
|
||||||
for kw in kws:
|
instance = acc.get("_source_instance", "")
|
||||||
if kw in text and kw not in found and len(kw) > 3:
|
if not account_id or not instance:
|
||||||
found.append(kw.strip())
|
acc["_featured_tags"] = []
|
||||||
return found[:4]
|
return []
|
||||||
|
url = f"https://{instance}/api/v1/accounts/{account_id}/featured_tags"
|
||||||
|
token = _token_for(instance)
|
||||||
|
data = api_get(url, token=token)
|
||||||
|
if not data or not isinstance(data, list):
|
||||||
|
acc["_featured_tags"] = []
|
||||||
|
return []
|
||||||
|
tags = [t["name"] for t in data if isinstance(t, dict) and t.get("name")][:6]
|
||||||
|
acc["_featured_tags"] = tags
|
||||||
|
return tags
|
||||||
|
|
||||||
# ── VÝSTUP ────────────────────────────────────
|
# ── VÝSTUP ────────────────────────────────────
|
||||||
def _to_output(acc):
|
def _to_output(acc):
|
||||||
@@ -253,7 +290,7 @@ def _to_output(acc):
|
|||||||
"followers": acc.get("followers_count", 0),
|
"followers": acc.get("followers_count", 0),
|
||||||
"statuses": acc.get("statuses_count", 0),
|
"statuses": acc.get("statuses_count", 0),
|
||||||
"score": score(acc),
|
"score": score(acc),
|
||||||
"tags": extract_tags(acc),
|
"tags": fetch_featured_tags(acc),
|
||||||
"category": categorize(acc),
|
"category": categorize(acc),
|
||||||
"last_active": acc.get("last_status_at", ""),
|
"last_active": acc.get("last_status_at", ""),
|
||||||
"url": acc.get("url", ""),
|
"url": acc.get("url", ""),
|
||||||
|
|||||||
+19
-4
@@ -76,10 +76,20 @@
|
|||||||
|
|
||||||
.tagline strong { color: var(--text); }
|
.tagline strong { color: var(--text); }
|
||||||
|
|
||||||
|
.about-link {
|
||||||
|
color: var(--accent);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
opacity: 0.75;
|
||||||
|
transition: opacity 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-link:hover { opacity: 1; text-decoration: underline; }
|
||||||
|
|
||||||
/* --- 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,7 +388,8 @@
|
|||||||
<h1>🦣 Mamutovo za 3 minuty.</h1>
|
<h1>🦣 Mamutovo za 3 minuty.</h1>
|
||||||
<p class="tagline">
|
<p class="tagline">
|
||||||
Žádné algoritmy. Žádné reklamy. <strong>Patří komunitě.</strong><br>
|
Žádné algoritmy. Žádné reklamy. <strong>Patří komunitě.</strong><br>
|
||||||
Tyhle 4 kroky ti zaplní feed a pomůžou udělat první post.
|
Tyhle 4 kroky ti zaplní feed a pomůžou udělat první post.<br>
|
||||||
|
<a href="https://about.mamutovo.cz" target="_blank" class="about-link">Co je Mamutovo.cz?</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -460,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>
|
||||||
|
|||||||
Reference in New Issue
Block a user