fix(push): reinstalled app replaces its old subscription; devices listed and removable
MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 2m8s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-10-31 11:59:40 +01:00
1 parent f76b0f34ff
commit 5b636b66e2
9 files changed
+123 -14

No files matched your search

@@ -11,6 +11,7 @@ import {
MessageSquare,
Globe,
Send,
X,
AlertCircle,
Palette,
Upload,
@@ -50,7 +51,7 @@ import { SessionsPanel } from './SessionsPanel';
import { ToggleSwitch } from '../../components/ToggleSwitch';
import { setLinkPreviewsEnabled, useLinkPreviewsEnabled } from '../../lib/linkPreview';
import { isJournalEnabled, setJournalEnabled } from '../../lib/journal';
import { currentPushSubscription, disablePush, enablePush, pushSupported } from '../../lib/push';
import { currentPushSubscription, disablePush, enablePush, pushSupported, removePushDevice } from '../../lib/push';
import { FormSheet } from '../../components/FormSheet';
interface UserSettingsViewProps {
@@ -301,13 +302,28 @@ export const UserSettingsView: React.FC<UserSettingsViewProps> = ({
// Web push: devices of this account and whether this browser is one of them
const [pushDevices, setPushDevices] = useState<PushDevice[]>([]);
const [pushHere, setPushHere] = useState(false);
const [pushEndpointHere, setPushEndpointHere] = useState<string | null>(null);
const [pushBusy, setPushBusy] = useState(false);
const [pushMsg, setPushMsg] = useState<{ type: 'ok' | 'err'; text: string } | null>(null);
const reloadPush = useCallback(async () => {
const [devices, sub] = await Promise.all([api.getPushDevices().catch(() => []), currentPushSubscription()]);
setPushDevices(devices);
setPushHere(Boolean(sub && devices.some((d) => d.endpoint === sub.endpoint)));
setPushEndpointHere(sub?.endpoint ?? null);
}, []);
// An old installation or a device you no longer use: remove it from the list
const removeDevice = async (endpoint: string) => {
setPushBusy(true);
setPushMsg(null);
try {
await removePushDevice(endpoint);
await reloadPush();
} catch (err) {
setPushMsg({ type: 'err', text: err instanceof Error ? err.message : 'Gerät konnte nicht entfernt werden' });
} finally {
setPushBusy(false);
}
};
useEffect(() => {
void reloadPush();
}, [reloadPush]);
@@ -1783,10 +1799,36 @@ export const UserSettingsView: React.FC<UserSettingsViewProps> = ({
</div>
</div>
{pushDevices.length > 0 && (
<p className="pl-6 text-[11px] text-faint">
Aktiv auf {pushDevices.length} {pushDevices.length === 1 ? 'Gerät' : 'Geräten'}:{' '}
{pushDevices.map((d) => d.device || 'Gerät').join(', ')}
</p>
<div className="pl-6 space-y-1">
<p className="text-[11px] text-faint">
Aktiv auf {pushDevices.length} {pushDevices.length === 1 ? 'Gerät' : 'Geräten'}. Alte Installationen
verschwinden beim nächsten Push von selbst oder lassen sich hier entfernen.
</p>
<ul className="border border-border divide-y divide-border bg-panel">
{pushDevices.map((d) => (
<li key={d.id} className="flex items-center justify-between gap-2 pl-2.5 pr-1 py-1">
<span className="min-w-0 text-[11px] text-main truncate">
{d.device || 'Gerät'}
<span className="text-faint">
{' · seit '}
{new Date(d.createdAt).toLocaleDateString('de-DE')}
{d.endpoint === pushEndpointHere && ' · dieses Gerät'}
</span>
</span>
<button
type="button"
onClick={() => void removeDevice(d.endpoint)}
disabled={pushBusy || demoMode}
title="Gerät entfernen"
aria-label={`${d.device || 'Gerät'} entfernen`}
className="h-9 w-9 sm:h-7 sm:w-7 shrink-0 flex items-center justify-center text-faint hover:text-status-err disabled:opacity-40"
>
<X className="w-3.5 h-3.5" />
</button>
</li>
))}
</ul>
</div>
)}
{pushMsg && (
<p className={`pl-6 text-[11px] ${pushMsg.type === 'ok' ? 'text-status-ok' : 'text-status-err'}`}>{pushMsg.text}</p>