Arbeit der parallelen Session in git gesichert (war nur auf dem Server)
Diese Änderungen liefen bereits live auf dem Server, lagen dort aber ausschließlich als nicht eingecheckte Arbeitskopie — bei jedem Deploy (stash/pull/pop) und bei jedem Serverproblem wären sie verloren gewesen. Deshalb hier unverändert in git übernommen, bevor darauf aufgebaut wird. Enthalten (nicht von mir gebaut, nur gesichert): - Supporter: eigener fester Zugangscode statt Einmalcode-Login (Migration 0009, lib/crypto.js scrypt-Hash, routes/supporter.js, abonnieren.html, supporter.html, i18n-abonnieren/-supporter) - Stimmen: Profilbilder (Migration 0010, routes/testimonials.js) - Event-Bild-Upload: voller Pfad statt relativem (routes/events.js) - Neue/überarbeitete Hintergrundbilder für viele Seiten - Teilen-Funktion (streamplan.js, i18n-index/-streamplan, main.css) Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -126,10 +126,62 @@ window.I18N_PAGE = {
|
||||
de: "Registrieren", "de-CH": "Registriere",
|
||||
en: "Register", fr: "S'inscrire", pt: "Registrar",
|
||||
},
|
||||
ab_tab_register: {
|
||||
de: "Neu registrieren", "de-CH": "Nöi registriere",
|
||||
en: "Sign up", fr: "S'inscrire", pt: "Registrar",
|
||||
},
|
||||
ab_tab_login: {
|
||||
de: "Schon registriert? Einloggen", "de-CH": "Scho registriert? Yylogge",
|
||||
en: "Already registered? Log in", fr: "Déjà inscrit ? Se connecter", pt: "Já registrado? Entrar",
|
||||
},
|
||||
ab_switch_login: {
|
||||
de: "Schon Supporter? Hier einloggen", "de-CH": "Scho Supporter? Da yylogge",
|
||||
en: "Already a supporter? Log in here", fr: "Déjà supporter ? Connecte-toi ici", pt: "Já é apoiador? Faça login aqui",
|
||||
},
|
||||
ab_feld_code_neu: {
|
||||
de: "Denk dir jetzt deinen Zugangscode aus (mindestens 6 Zeichen)",
|
||||
"de-CH": "Dänk der jetz din Zuegangscode us (mindeschtens 6 Zeiche)",
|
||||
en: "Now choose your own access code (at least 6 characters)",
|
||||
fr: "Choisis maintenant ton code d'accès (au moins 6 caractères)",
|
||||
pt: "Agora escolha seu código de acesso (mínimo 6 caracteres)",
|
||||
},
|
||||
ab_feld_code_platzhalter: {
|
||||
de: "Frei wählbar — z. B. DogiCrew2026",
|
||||
"de-CH": "Frei wählbar — z. B. DogiCrew2026",
|
||||
en: "Anything you like — e.g. DogiCrew2026",
|
||||
fr: "Comme tu veux — p. ex. DogiCrew2026",
|
||||
pt: "À sua escolha — ex. DogiCrew2026",
|
||||
},
|
||||
ab_code_hinweis_neu: {
|
||||
de: "Mit diesem Code loggst du dich künftig ein — merk ihn dir gut.",
|
||||
"de-CH": "Mit däm Code loggsch di künftig y — merk der en guet.",
|
||||
en: "You'll use this code to log in from now on — keep it safe.",
|
||||
fr: "Tu utiliseras ce code pour te connecter — garde-le en lieu sûr.",
|
||||
pt: "Você usará este código para entrar a partir de agora — guarde-o bem.",
|
||||
},
|
||||
ab_feld_code_eigen: {
|
||||
de: "Dein Zugangscode", "de-CH": "Din Zuegangscode",
|
||||
en: "Your access code", fr: "Ton code d'accès", pt: "Seu código de acesso",
|
||||
},
|
||||
ab_btn_login: {
|
||||
de: "Einloggen", "de-CH": "Yylogge",
|
||||
en: "Log in", fr: "Se connecter", pt: "Entrar",
|
||||
},
|
||||
ab_code_vergessen: {
|
||||
de: "Code vergessen?", "de-CH": "Code vergässe?",
|
||||
en: "Forgot your code?", fr: "Code oublié ?", pt: "Esqueceu seu código?",
|
||||
},
|
||||
ab_neuer_code_hinweis: {
|
||||
de: "Fast fertig — leg jetzt deinen neuen Zugangscode fest.",
|
||||
"de-CH": "Fascht fertig — leg jetz din nöie Zuegangscode fescht.",
|
||||
en: "Almost done — now set your new access code.",
|
||||
fr: "Presque fini — définis maintenant ton nouveau code d'accès.",
|
||||
pt: "Quase pronto — agora defina seu novo código de acesso.",
|
||||
},
|
||||
ab_btn_code_speichern: {
|
||||
de: "Zugangscode speichern", "de-CH": "Zuegangscode spichere",
|
||||
en: "Save access code", fr: "Enregistrer le code d'accès", pt: "Salvar código de acesso",
|
||||
},
|
||||
ab_btn_login_code: {
|
||||
de: "Login-Code anfordern", "de-CH": "Login-Code aafordere",
|
||||
en: "Request login code", fr: "Demander un code de connexion", pt: "Solicitar código de login",
|
||||
@@ -151,8 +203,23 @@ window.I18N_PAGE = {
|
||||
en: "Confirm", fr: "Confirmer", pt: "Confirmar",
|
||||
},
|
||||
ab_eingeloggt_als: {
|
||||
de: "Eingeloggt. Noch ein letzter Schritt:", "de-CH": "Yyglogget. No en letschte Schritt:",
|
||||
en: "Logged in. One last step:", fr: "Connecté. Une dernière étape :", pt: "Conectado. Um último passo:",
|
||||
de: "Eingeloggt.", "de-CH": "Yyglogget.",
|
||||
en: "Logged in.", fr: "Connecté.", pt: "Conectado.",
|
||||
},
|
||||
ab_subscribe_locked_hinweis: {
|
||||
de: "Dein Konto ist bereit — die Bezahlung startet, sobald das Abo offiziell live geht. Schau einfach bald wieder vorbei.",
|
||||
"de-CH": "Dis Konto isch parat — d Zahlig startet, sobald s Abo offiziell live gaht. Schau eifach bald wieder verbi.",
|
||||
en: "Your account is ready — payment opens as soon as the subscription officially launches. Just check back soon.",
|
||||
fr: "Ton compte est prêt — le paiement s'ouvrira dès le lancement officiel de l'abonnement. Repasse bientôt.",
|
||||
pt: "Sua conta está pronta — o pagamento abre assim que a assinatura for lançada oficialmente. Volte em breve.",
|
||||
},
|
||||
ab_weiter_startseite: {
|
||||
de: "Zur Startseite →", "de-CH": "Zur Startsiite →",
|
||||
en: "To the homepage →", fr: "Vers l'accueil →", pt: "Para a página inicial →",
|
||||
},
|
||||
ab_weiter_streamplan: {
|
||||
de: "Zum Streamplan →", "de-CH": "Zum Streamplan →",
|
||||
en: "To the stream schedule →", fr: "Vers le programme des lives →", pt: "Para a programação de lives →",
|
||||
},
|
||||
ab_oder_email: {
|
||||
de: "oder mit E-Mail", "de-CH": "oder mit E-Mail",
|
||||
|
||||
@@ -97,6 +97,10 @@ window.I18N_PAGE = {
|
||||
de: "Direkt ansehen →", "de-CH": "Direkt aluege →",
|
||||
en: "View directly →", fr: "Voir directement →", pt: "Ver diretamente →",
|
||||
},
|
||||
idx_jahres_event_modal_teilen: {
|
||||
de: "📤 Teilen", "de-CH": "📤 Teile",
|
||||
en: "📤 Share", fr: "📤 Partager", pt: "📤 Compartilhar",
|
||||
},
|
||||
|
||||
idx_char_eyebrow: {
|
||||
de: "🩵 Die Gesichter der Welten", "de-CH": "🩵 D Gsichter vo de Wälte",
|
||||
|
||||
@@ -58,6 +58,10 @@ window.I18N_PAGE = {
|
||||
de: "Mehr erfahren →", "de-CH": "Mehr erfahre →",
|
||||
en: "Learn more →", fr: "En savoir plus →", pt: "Saiba mais →",
|
||||
},
|
||||
sp_radar_teilen: {
|
||||
de: "📤 Teilen", "de-CH": "📤 Teile",
|
||||
en: "📤 Share", fr: "📤 Partager", pt: "📤 Compartilhar",
|
||||
},
|
||||
sp_card1_tag: {
|
||||
de: "Fixer Termin", "de-CH": "Fixe Termin",
|
||||
en: "Fixed Time", fr: "Rendez-vous fixe", pt: "Horário Fixo",
|
||||
|
||||
@@ -58,6 +58,17 @@ window.I18N_PAGE = {
|
||||
de: "Jetzt abonnieren", "de-CH": "Jetzt abonniere",
|
||||
en: "Subscribe now", fr: "S'abonner maintenant", pt: "Assine agora",
|
||||
},
|
||||
su_kein_abo_soon: {
|
||||
de: "Coming soon…", "de-CH": "Coming soon…",
|
||||
en: "Coming soon…", fr: "Coming soon…", pt: "Coming soon…",
|
||||
},
|
||||
su_kein_abo_hinweis: {
|
||||
de: "Alles hier ist schon für dich vorbereitet — sobald das Abo startet, kannst du es mit einem Klick aktivieren.",
|
||||
"de-CH": "Alles da isch scho für di parat — sobald s Abo startet, chasch es mit eim Klick aktiviere.",
|
||||
en: "Everything here is already set up for you — as soon as the subscription starts, you can activate it with one click.",
|
||||
fr: "Tout est déjà prêt pour toi — dès le lancement de l'abonnement, tu pourras l'activer en un clic.",
|
||||
pt: "Tudo aqui já está preparado para você — assim que a assinatura começar, você poderá ativá-la com um clique.",
|
||||
},
|
||||
su_fenster2_h2: {
|
||||
de: "Mein aktueller Fortschritt", "de-CH": "Mis aktuelle Fortschritt",
|
||||
en: "My Current Progress", fr: "Ma progression actuelle", pt: "Meu progresso atual",
|
||||
|
||||
+58
-7
@@ -518,16 +518,24 @@
|
||||
}
|
||||
|
||||
const token = localStorage.getItem("supporterToken");
|
||||
const status = localStorage.getItem("supporterSubStatus");
|
||||
if (!token) {
|
||||
return `<a class="nav-cta nav-cta-abo" href="abonnieren.html"><span>${t("nav_cta_abo_anonym")}</span>${soonBadge()}</a>`;
|
||||
}
|
||||
const hatBereich = ["active", "pending", "suspended", "cancelled"].includes(status);
|
||||
if (hatBereich) {
|
||||
const name = localStorage.getItem("supporterDisplayName");
|
||||
return `<a class="nav-cta nav-cta-abo" href="supporter.html"><span>${bereichLabel(name)}</span></a>`;
|
||||
}
|
||||
return `<a class="nav-cta nav-cta-abo" href="abonnieren.html"><span>${t("nav_cta_abo_aktivieren")}</span>${soonBadge()}</a>`;
|
||||
/* Nutzer-Wunsch 21.08.2026: "meine seite ändert sich nicht und es gibt
|
||||
keinen button für registrierte sich normal anzumelden und abzumelden.
|
||||
wo sie die speziellen sachen sehen als supporter und dass sie auch
|
||||
schon den abo sehen aber gesperrt ist." Vorher landeten Leute mit
|
||||
Konto, aber ohne bezahltes Abo (status "none"), im Nav-Knopf wieder
|
||||
nur bei abonnieren.html -- man merkte dem Knopf also nie an, dass man
|
||||
überhaupt eingeloggt ist. supporter.html zeigt für genau diesen Fall
|
||||
schon lange den Bereich "Mein Abo" -> #supp-keine-abo ("Du hast noch
|
||||
kein aktives Abo" + Knopf) UND den Ausloggen-Knopf (siehe dort,
|
||||
#btn-logout) -- das musste also nur noch verlinkt werden. Ab jetzt:
|
||||
JEDER mit gültigem Token (egal ob "none", "active", "pending", ...)
|
||||
geht auf supporter.html und sieht dort automatisch den passenden
|
||||
Zustand samt Logout. */
|
||||
const name = localStorage.getItem("supporterDisplayName");
|
||||
return `<a class="nav-cta nav-cta-abo" href="supporter.html"><span>${bereichLabel(name)}</span></a>`;
|
||||
}
|
||||
|
||||
function renderHeader() {
|
||||
@@ -1172,6 +1180,49 @@
|
||||
return feld[sprache] || feld.de || "";
|
||||
};
|
||||
|
||||
/* =====================================================================
|
||||
dogiTeilen() — 21.08.2026, Nutzer-Wunsch: "die events des jahres und
|
||||
speziellen live events soll man auch teilen können." Wiederverwendbare
|
||||
Teilen-Funktion für einzelne Inhalte (aktuell: Event des Jahres auf
|
||||
index.html, spezielles Live-Event auf streamplan.html — künftig bei
|
||||
Bedarf für weitere Stellen nutzbar, ohne den Mechanismus neu zu bauen).
|
||||
|
||||
Nutzt zuerst die native Teilen-Funktion des Geräts (Web-Share-API) —
|
||||
auf dem Handy öffnet das den echten Teilen-Dialog (WhatsApp, TikTok,
|
||||
Mail, ...). Die meisten Desktop-Browser kennen diese API nicht/zeigen
|
||||
dort ohnehin kein sinnvolles Menü -- dort stattdessen Fallback: Link in
|
||||
die Zwischenablage kopieren, der klickende Knopf bekommt kurz ein
|
||||
"✓ Link kopiert"-Feedback (eigener Text + eigene Klasse, gleiches
|
||||
Timing wie .vw-copy-btn in der Verwaltung). */
|
||||
window.dogiTeilen = async function (daten, knopf) {
|
||||
const payload = { title: daten.title || "", text: daten.text || "", url: daten.url || location.href };
|
||||
if (navigator.share) {
|
||||
try {
|
||||
await navigator.share(payload);
|
||||
return;
|
||||
} catch (err) {
|
||||
if (err && err.name === "AbortError") return; // Dialog selbst abgebrochen -- kein Fallback nötig
|
||||
// sonst: weiter zum Zwischenablage-Fallback unten
|
||||
}
|
||||
}
|
||||
if (!navigator.clipboard) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(payload.url);
|
||||
if (knopf) {
|
||||
const alterText = knopf.textContent;
|
||||
knopf.textContent = "✓ Link kopiert";
|
||||
knopf.classList.add("dogi-teilen-kopiert");
|
||||
setTimeout(() => {
|
||||
knopf.textContent = alterText;
|
||||
knopf.classList.remove("dogi-teilen-kopiert");
|
||||
}, 1600);
|
||||
}
|
||||
} catch {
|
||||
/* Zwischenablage nicht erreichbar (z.B. kein HTTPS-Kontext) -- kein
|
||||
Blocker, der Link bleibt über die normale Adressleiste erreichbar. */
|
||||
}
|
||||
};
|
||||
|
||||
/* =====================================================================
|
||||
dogiTeamLaden() — 21.08.2026, Nutzer-Wunsch: "überall wo Modis sind
|
||||
oder Scouts oder Manager, oder Creator, will ich dass ich die easy
|
||||
|
||||
@@ -156,7 +156,9 @@
|
||||
const seCountdown = document.getElementById("se-radar-countdown");
|
||||
const seTitel = document.getElementById("se-radar-titel");
|
||||
const seLink = document.getElementById("se-radar-link");
|
||||
const seTeilenBtn = document.getElementById("se-radar-teilen");
|
||||
let zielDatum = null;
|
||||
let aktuellesEvent = null; // für dogiTeilen() beim Klick (21.08.2026)
|
||||
let countdownIntervalId = null;
|
||||
let zeigerIntervalId = null;
|
||||
|
||||
@@ -201,6 +203,7 @@
|
||||
return;
|
||||
}
|
||||
zielDatum = new Date(event.zieldatum);
|
||||
aktuellesEvent = event;
|
||||
seWrap.hidden = false;
|
||||
seRadar.classList.remove("ist-live");
|
||||
seTitel.textContent = window.dogiFeld ? window.dogiFeld(event.titel) : event.titel?.de || "";
|
||||
@@ -226,5 +229,23 @@
|
||||
document.addEventListener("dogi-sprache-geaendert", () => {
|
||||
if (zielDatum) specialEventLaden();
|
||||
});
|
||||
|
||||
// Teilen (21.08.2026, Nutzer-Wunsch: "... speziellen live events soll man
|
||||
// auch teilen können") -- teilt die Streamplan-Seite selbst (dort steht
|
||||
// ja bereits automatisch das aktuell aktive Event), zusammen mit Titel
|
||||
// im Text, damit klar ist, worum es geht. Nur EIN spezielles Event kann
|
||||
// je aktiv sein, deshalb reicht ein einziger Knopf ohne Slot-Bezug.
|
||||
seTeilenBtn?.addEventListener("click", () => {
|
||||
if (!aktuellesEvent) return;
|
||||
const titel = window.dogiFeld ? window.dogiFeld(aktuellesEvent.titel) : aktuellesEvent.titel?.de || "";
|
||||
window.dogiTeilen?.(
|
||||
{
|
||||
title: titel,
|
||||
text: `🔴 ${titel} — spezielles Live-Event bei DogFather!`,
|
||||
url: `${location.origin}/streamplan.html`,
|
||||
},
|
||||
seTeilenBtn
|
||||
);
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user