v0.12.32 bug fixes
This commit is contained in:
@@ -193,143 +193,6 @@ function RegistrationTab({ onFeaturesChanged }) {
|
||||
);
|
||||
}
|
||||
|
||||
// ── Push Debug Tab ────────────────────────────────────────────────────────────
|
||||
function DebugRow({ label, value, ok, bad }) {
|
||||
const color = ok ? 'var(--success)' : bad ? 'var(--error)' : 'var(--text-secondary)';
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', fontSize: 13 }}>
|
||||
<span style={{ color: 'var(--text-secondary)' }}>{label}</span>
|
||||
<span style={{ color, fontFamily: 'monospace', fontSize: 12 }}>{value}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PushDebugTab() {
|
||||
const toast = useToast();
|
||||
const [debugData, setDebugData] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [testing, setTesting] = useState(false);
|
||||
|
||||
const permission = (typeof Notification !== 'undefined') ? Notification.permission : 'unsupported';
|
||||
const [cachedToken, setCachedToken] = useState(localStorage.getItem('rc_fcm_token'));
|
||||
const [lastError, setLastError] = useState(localStorage.getItem('rc_fcm_error'));
|
||||
|
||||
const load = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const data = await api.pushDebug();
|
||||
setDebugData(data);
|
||||
} catch (e) {
|
||||
toast(e.message || 'Failed to load debug data', 'error');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => { load(); }, []);
|
||||
|
||||
const doTest = async (mode) => {
|
||||
setTesting(true);
|
||||
try {
|
||||
const result = await api.testPush(mode);
|
||||
const sent = result.results?.find(r => r.status === 'sent');
|
||||
const failed = result.results?.find(r => r.status === 'failed');
|
||||
if (sent) toast(`Test sent (mode=${mode}) — check device for notification`, 'success');
|
||||
else if (failed) toast(`Test failed: ${failed.error}`, 'error');
|
||||
else toast('No subscription found — grant permission and reload', 'error');
|
||||
} catch (e) {
|
||||
toast(e.message || 'Test failed', 'error');
|
||||
} finally {
|
||||
setTesting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const clearToken = () => {
|
||||
localStorage.removeItem('rc_fcm_token');
|
||||
localStorage.removeItem('rc_fcm_error');
|
||||
setCachedToken(null);
|
||||
setLastError(null);
|
||||
toast('Cached token cleared — reload to re-register with server', 'info');
|
||||
};
|
||||
|
||||
const reregister = () => {
|
||||
localStorage.removeItem('rc_fcm_token');
|
||||
localStorage.removeItem('rc_fcm_error');
|
||||
setCachedToken(null);
|
||||
setLastError(null);
|
||||
window.dispatchEvent(new CustomEvent('rosterchirp:push-init'));
|
||||
toast('Re-registering push subscription…', 'info');
|
||||
};
|
||||
|
||||
const box = { background: 'var(--surface-variant)', border: '1px solid var(--border)', borderRadius: 'var(--radius)', padding: '12px 14px', marginBottom: 14 };
|
||||
const sectionLabel = { fontSize: 11, fontWeight: 600, color: 'var(--text-tertiary)', textTransform: 'uppercase', letterSpacing: '0.5px', marginBottom: 8 };
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="settings-section-label" style={{ marginBottom: 14 }}>Push Notification Debug</div>
|
||||
|
||||
{/* This device */}
|
||||
<div style={box}>
|
||||
<div style={sectionLabel}>This Device</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginBottom: 10 }}>
|
||||
<DebugRow label="Permission" value={permission} ok={permission === 'granted'} bad={permission === 'denied'} />
|
||||
<DebugRow label="Cached FCM token" value={cachedToken ? cachedToken.slice(0, 36) + '…' : 'None'} ok={!!cachedToken} bad={!cachedToken} />
|
||||
{debugData && <DebugRow label="FCM env vars" value={debugData.fcmConfigured ? 'Present' : 'Missing'} ok={debugData.fcmConfigured} bad={!debugData.fcmConfigured} />}
|
||||
{debugData && <DebugRow label="Firebase Admin" value={debugData.firebaseAdminReady ? 'Ready' : 'Not ready'} ok={debugData.firebaseAdminReady} bad={!debugData.firebaseAdminReady} />}
|
||||
{lastError && <DebugRow label="Last reg. error" value={lastError} bad={true} />}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
||||
<button className="btn btn-sm btn-primary" onClick={reregister}>Re-register</button>
|
||||
<button className="btn btn-sm btn-secondary" onClick={clearToken}>Clear cached token</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Test push */}
|
||||
<div style={box}>
|
||||
<div style={sectionLabel}>Send Test Notification to This Device</div>
|
||||
<p style={{ fontSize: 12, color: 'var(--text-secondary)', marginBottom: 10, lineHeight: 1.5 }}>
|
||||
<strong>notification</strong> — same path as real messages (SW <code>onBackgroundMessage</code>)<br/>
|
||||
<strong>browser</strong> — Chrome shows it directly, bypasses the SW (confirm delivery works)
|
||||
</p>
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
<button className="btn btn-sm btn-primary" onClick={() => doTest('notification')} disabled={testing}>
|
||||
{testing ? 'Sending…' : 'Test (notification)'}
|
||||
</button>
|
||||
<button className="btn btn-sm btn-secondary" onClick={() => doTest('browser')} disabled={testing}>
|
||||
{testing ? 'Sending…' : 'Test (browser)'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Registered devices */}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
|
||||
<div className="settings-section-label" style={{ margin: 0 }}>Registered Devices</div>
|
||||
<button className="btn btn-sm btn-secondary" onClick={load} disabled={loading}>{loading ? 'Loading…' : 'Refresh'}</button>
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
<p style={{ fontSize: 13, color: 'var(--text-tertiary)' }}>Loading…</p>
|
||||
) : !debugData?.subscriptions?.length ? (
|
||||
<p style={{ fontSize: 13, color: 'var(--text-tertiary)' }}>No FCM tokens registered.</p>
|
||||
) : (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
{debugData.subscriptions.map(sub => (
|
||||
<div key={sub.id} style={box}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
|
||||
<span style={{ fontSize: 13, fontWeight: 600 }}>{sub.name || sub.email}</span>
|
||||
<span style={{ fontSize: 11, color: 'var(--text-tertiary)', background: 'var(--surface)', padding: '2px 7px', borderRadius: 4, border: '1px solid var(--border)' }}>{sub.device}</span>
|
||||
</div>
|
||||
<code style={{ fontSize: 10, color: 'var(--text-secondary)', wordBreak: 'break-all', lineHeight: 1.6, display: 'block' }}>
|
||||
{sub.fcm_token}
|
||||
</code>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Main modal ────────────────────────────────────────────────────────────────
|
||||
export default function SettingsModal({ onClose, onFeaturesChanged }) {
|
||||
const [tab, setTab] = useState('registration');
|
||||
@@ -349,7 +212,6 @@ export default function SettingsModal({ onClose, onFeaturesChanged }) {
|
||||
const tabs = [
|
||||
isTeam && { id: 'team', label: 'Team Management' },
|
||||
{ id: 'registration', label: 'Registration' },
|
||||
{ id: 'pushdebug', label: 'Push Debug' },
|
||||
].filter(Boolean);
|
||||
|
||||
return (
|
||||
@@ -373,7 +235,6 @@ export default function SettingsModal({ onClose, onFeaturesChanged }) {
|
||||
|
||||
{tab === 'team' && <TeamManagementTab />}
|
||||
{tab === 'registration' && <RegistrationTab onFeaturesChanged={onFeaturesChanged} />}
|
||||
{tab === 'pushdebug' && <PushDebugTab />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user