Files
dogfather-universe/workspace/assets/js/anruf-probe.js
T
DogFatherGitandClaude Opus 5 4fdf08d003 Anruf-Probe: eine Seite, die sagt, WARUM es nicht klingelt
Filipe: „funktioniert nichts davon was machst du?"

Zu Recht. Ich habe an einem Abend fuenf Dinge repariert, jedes einzeln
nachgewiesen -- und bei ihm klingelte trotzdem nichts. Der Grund ist
jedes Mal derselbe gewesen: Ich kann von hier aus messen, was der
SERVER tut, aber nicht, was in SEINEM Browser ankommt. Und genau dort
lag es.

Gemessen habe ich gerade wieder: Der Server liefert die richtige Datei
aus (32616 Bytes, Klingelton enthalten), sobald sie mit Stempel
angefordert wird. Ohne Stempel kommt eine sieben Stunden alte Fassung
aus dem Zwischenspeicher. WELCHE von beiden sein Browser anfordert,
sehe ich nicht -- und das ist die Frage, an der alles haengt.

/workspace/anruf-probe.html dreht das um. Sie laeuft in SEINEM Browser
und beantwortet der Reihe nach:

  1. Ist die neue Fassung ueberhaupt geladen? Sie holt anruf.js und
     sieht nach, ob Klingelton und Nachklingeln darin stehen. Fehlen
     sie, kommt die Seite aus dem Zwischenspeicher -- und JEDE
     Reparatur ist unsichtbar, egal wie oft man neu laedt.
  2. Darf der Browser Ton abspielen? (Er erlaubt ihn erst nach der
     ersten Beruehrung -- wer ueber eine Benachrichtigung hereinkommt,
     hoert sonst nichts und haelt das Telefon fuer kaputt.)
  3. Sind Benachrichtigungen erlaubt UND ist ein Geraet beim Server
     angemeldet? Das sind zwei verschiedene Dinge, und die Luecke
     dazwischen sieht man sonst nirgends.
  4. Steht die Verbindung, ueber die das Klingeln kommt?

Dazu ein Knopf, der genau den Klingelton abspielt, den ein Anruf
macht. Wer ihn hoert, weiss, dass es an der Seite nicht liegt.

Am Ende steht EIN Satz: das Wichtigste zuerst, mit dem, was zu tun
ist. Keine Liste zum Selbstdeuten.

Sie ruft niemanden an und zeigt nichts ueber andere.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-19 00:14:01 +02:00

241 lines
9.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =====================================================================
DIE ANRUF-PROBE (19.09.2026)
Filipe, nach einem langen Abend: „funktioniert nichts davon was
machst du?"
Zu Recht. Ich habe an diesem Abend fünf Dinge repariert, jedes
einzeln nachgewiesen -- und trotzdem klingelte bei ihm nichts. Der
Grund ist immer derselbe: Ich kann von hier aus messen, was der
SERVER tut, aber nicht, was in seinem Browser ankommt. Und genau
dort lag es jedes Mal.
Diese Seite dreht das um. Sie läuft in SEINEM Browser und beantwortet
der Reihe nach die Fragen, die ich ihm sonst stellen müsste:
1. Ist die neue Fassung überhaupt geladen? (Der häufigste Fall:
eine alte Seite aus dem Zwischenspeicher, die alte Dateien
nachlädt. Dann ist jede Reparatur unsichtbar.)
2. Darf der Browser überhaupt Ton abspielen?
3. Sind Benachrichtigungen erlaubt -- und ist ein Gerät angemeldet?
4. Steht die Verbindung zum Server, über die das Klingeln kommt?
Sie ruft niemanden an. Man kann sie so oft aufmachen, wie man will.
===================================================================== */
(() => {
const $ = (id) => document.getElementById(id);
const ziel = $('ergebnis');
if (!ziel) return;
const punkte = [];
function punkt(stand, titel, satz) {
punkte.push({ stand, titel, satz });
const k = document.createElement('div');
k.className = 'punkt';
k.dataset.stand = stand;
const z = document.createElement('span');
z.className = 'punkt__zeichen';
z.textContent = stand === 'gut' ? '✓' : stand === 'warn' ? '!' : '✗';
const t = document.createElement('div');
t.className = 'punkt__text';
const h = document.createElement('p');
h.className = 'punkt__titel';
h.textContent = titel;
const s = document.createElement('p');
s.className = 'punkt__satz';
s.textContent = satz;
t.append(h, s);
k.append(z, t);
ziel.append(k);
}
/* ---- 1. WELCHE FASSUNG IST GELADEN? -----------------------------
Der wichtigste Punkt, und der am häufigsten übersehene. Die
Anruf-Datei trägt einen Stempel im Namen (anruf.js?v=…). Steht in
der geladenen Seite ein alter Stempel, kommt aus dem
Zwischenspeicher eine alte Datei -- und jede Reparatur am Server
ist unsichtbar, egal wie oft man neu lädt. */
async function fassungPruefen() {
const skript = [...document.querySelectorAll('script[src]')]
.map((s) => s.getAttribute('src'))
.find((s) => /anruf-probe\.js/.test(s || ''));
const stempel = (skript || '').split('v=')[1] || '(keiner)';
try {
const a = await fetch(`assets/js/anruf.js?v=${stempel}`, { cache: 'no-store' });
const text = await a.text();
const hatTon = text.includes('klingelTon');
const hatOffen = text.includes('anruf/offen');
if (hatTon && hatOffen) {
punkt('gut', 'Die neue Fassung ist geladen',
`Klingelton und Nachklingeln sind da (Stand ${stempel}).`);
} else {
punkt('schlecht', 'Es läuft eine ALTE Fassung',
`Der Klingelton fehlt${hatOffen ? '' : ', das Nachklingeln auch'}. `
+ 'Die Seite kommt aus dem Zwischenspeicher. '
+ 'Hilft: die Seite ganz schließen (in der App aus dem Wechsler wischen) und neu öffnen.');
}
} catch {
punkt('warn', 'Fassung nicht prüfbar',
'Die Datei ließ sich nicht laden – bist du gerade offline?');
}
}
/* ---- 2. DARF DER BROWSER TON ABSPIELEN? -------------------------
Browser erlauben Ton erst, wenn jemand auf der Seite etwas
angeklickt hat. Wer über eine Benachrichtigung hereinkommt und
nichts berührt, hört deshalb unter Umständen nichts -- und hält
das Telefon für kaputt. */
function tonPruefen() {
const K = window.AudioContext || window.webkitAudioContext;
if (!K) {
punkt('schlecht', 'Dieser Browser kann keinen Ton erzeugen',
'Sehr selten. Ein anderer Browser hilft.');
return null;
}
const raum = new K();
if (raum.state === 'running') {
punkt('gut', 'Ton ist erlaubt', 'Der Klingelton kann abgespielt werden.');
} else {
punkt('warn', 'Ton ist noch gesperrt',
'Der Browser lässt Ton erst zu, nachdem man einmal etwas angetippt hat. '
+ 'Drück unten auf „Klingelton anhören" – danach ist er frei, '
+ 'solange die Seite offen bleibt.');
}
return raum;
}
/* ---- 3. BENACHRICHTIGUNGEN ---------------------------------------- */
async function meldungPruefen() {
if (!('Notification' in window)) {
punkt('warn', 'Benachrichtigungen kennt dieser Browser nicht',
'Anrufe klingeln dann nur, wenn die Seite offen ist.');
return;
}
const erlaubt = Notification.permission;
if (erlaubt === 'granted') punkt('gut', 'Benachrichtigungen sind erlaubt', 'Gut.');
else if (erlaubt === 'denied') {
punkt('schlecht', 'Benachrichtigungen sind blockiert',
'Dann kommt kein Anruf an, solange die Seite zu ist. '
+ 'In den Browsereinstellungen für diese Seite wieder erlauben.');
} else {
punkt('warn', 'Benachrichtigungen sind noch nicht erlaubt',
'Im Chat oben auf die Glocke tippen und zustimmen.');
}
/* Ist auch wirklich ein Gerät beim Server angemeldet? Erlaubnis im
Browser und Anmeldung beim Server sind zwei verschiedene Dinge --
genau diese Lücke sieht man sonst nirgends. */
try {
const a = await fetch('/workspace/api/push/stand', { credentials: 'same-origin' });
if (a.ok) {
const d = await a.json();
/* `geraete` ist eine LISTE, keine Zahl -- nachgesehen in
workspace-push.js statt geraten. */
const n = Array.isArray(d.geraete) ? d.geraete.length : 0;
if (n > 0) punkt('gut', 'Dieses Konto hat ein Gerät angemeldet', `${n} Gerät(e).`);
else {
punkt('schlecht', 'Kein Gerät beim Server angemeldet',
'Der Browser darf zwar, aber es ist nichts hinterlegt. '
+ 'Im Chat auf die Glocke tippen und neu zustimmen.');
}
/* UND DIE RUHEZEIT. Sie hat gestern Abend den ganzen Anruf
verschluckt; seit heute sind Anrufe ausgenommen. Wer das
hier liest, soll es sehen, statt es zu vermuten. */
if (d.ruhe) {
const anrufArt = (d.arten || []).find((x) => x.schluessel === 'anruf');
if (anrufArt && anrufArt.an === false) {
punkt('schlecht', 'Anrufe sind bei dir abgeschaltet',
'In den Benachrichtigungen die Art „Jemand ruft an" wieder einschalten.');
} else if (d.ruhe.jetzt) {
punkt('gut', `Es ist Ruhezeit (${d.ruhe.ab}–${d.ruhe.bis} Uhr)`,
'Erinnerungen bleiben jetzt liegen – Anrufe kommen trotzdem durch.');
}
}
}
} catch { /* die Route gibt es vielleicht nicht -- kein Grund zur Panik */ }
}
/* ---- 4. KOMMT ETWAS VOM SERVER AN? ------------------------------- */
async function serverPruefen() {
try {
const a = await fetch('/workspace/api/anruf/offen', { credentials: 'same-origin' });
if (a.status === 401) {
punkt('schlecht', 'Du bist nicht angemeldet',
'Dann kann auch nichts klingeln. Melde dich neu an.');
return;
}
if (!a.ok) {
punkt('warn', 'Der Server antwortet ungewöhnlich', `Antwort ${a.status}.`);
return;
}
const d = await a.json();
punkt('gut', 'Die Verbindung zum Server steht',
(d.anrufe?.length
? `Gerade klingelt sogar etwas (${d.anrufe.length}).`
: 'Gerade klingelt nichts – das ist normal.'));
} catch {
punkt('schlecht', 'Keine Verbindung zum Server',
'Kein Netz? Ohne sie kommt kein Anruf an.');
}
}
/* ---- Der Rat am Ende --------------------------------------------- */
function ratGeben() {
const schlecht = punkte.filter((p) => p.stand === 'schlecht');
const warn = punkte.filter((p) => p.stand === 'warn');
const r = $('rat');
if (!r) return;
if (schlecht.length) {
r.textContent = 'Das Wichtigste zuerst: ' + schlecht[0].titel
+ '. ' + schlecht[0].satz;
} else if (warn.length) {
r.textContent = 'Fast alles steht. Offen ist: ' + warn[0].titel
+ '. ' + warn[0].satz;
} else {
r.textContent = 'Alles, was von hier aus prüfbar ist, steht. '
+ 'Wenn es jetzt noch klemmt, liegt es an der Verbindung zwischen '
+ 'den beiden Geräten – dann hilft nur ein echter Anruf zu zweit.';
}
}
/* ---- Der Knopf: Ton wirklich hören -------------------------------- */
let tonRaum = null;
$('ton-probe')?.addEventListener('click', () => {
try {
const K = window.AudioContext || window.webkitAudioContext;
tonRaum = tonRaum || new K();
if (tonRaum.state === 'suspended') tonRaum.resume();
const spiel = (hz, ab, lang) => {
const o = tonRaum.createOscillator();
const v = tonRaum.createGain();
o.type = 'sine'; o.frequency.value = hz;
const t = tonRaum.currentTime + ab;
v.gain.setValueAtTime(0, t);
v.gain.linearRampToValueAtTime(0.16, t + 0.03);
v.gain.linearRampToValueAtTime(0, t + lang);
o.connect(v).connect(tonRaum.destination);
o.start(t); o.stop(t + lang + 0.05);
};
/* Genau der Klingelton aus dem Anruf -- damit man hört, was man
hören würde. */
spiel(880, 0, 0.22);
spiel(660, 0.3, 0.26);
try { navigator.vibrate?.([250, 200, 250]); } catch { /* egal */ }
$('ton-probe').textContent = 'Nochmal anhören';
} catch {
$('ton-probe').textContent = 'Ton ging nicht – siehe oben';
}
});
(async () => {
await fassungPruefen();
tonPruefen();
await meldungPruefen();
await serverPruefen();
ratGeben();
})();
})();