Two bugs reported after v2.2.0:
1. Input fields and textareas overflowed their container — typing in
Settings / SendModal / NewContactModal would push the border past
the card edge because the renderer's default box-sizing was
content-box and `width: 100%` + padding pushed widths past parents.
Added `*, *::before, *::after { box-sizing: border-box; }` to
index.html. Removes the need for per-element `boxSizing: 'border-box'`
(the existing sprinkles stay for clarity but are now redundant).
2. App went blank when opening a chat — any throw inside Conversation
propagated up through Shell and wiped the whole window, with no way
to navigate out. Added PaneBoundary, a React error boundary scoped
to one Shell pane, keyed on `${section}-(list|detail)` so it resets
when the user switches section. Now a crash shows an inline error
card with message + stack + Retry, while NavBar + StatusBar stay
usable.
Also hardened Conversation against edge cases that were candidates
for the original crash:
* `name` always falls back to shortAddr(address) if all other
branches produce an empty string.
* first letter used for the avatar is computed once, guarded
against empty input with a `?` fallback.
* Header name + short-address line get whiteSpace/overflow/ellipsis
so very long contacts no longer escape the 32px wide sub-column
the way they did for the reporter.
Fonts normalised in the global CSS too — inputs/textareas/buttons now
inherit `font-family` instead of the browser default, which was
breaking the visual rhythm in the Settings cards.
63 lines
1.9 KiB
TypeScript
63 lines
1.9 KiB
TypeScript
// PaneBoundary — ErrorBoundary scoped to one Shell pane. A crash in
|
|
// the Conversation component shouldn't black-out the whole window; it
|
|
// should leave NavBar + List + StatusBar usable so the operator can
|
|
// switch sections and report the bug. Resets when the keyed section
|
|
// changes.
|
|
|
|
import React from 'react';
|
|
|
|
interface Props {
|
|
/** Used as React key at the callsite; also shown in the panic copy. */
|
|
sectionName: string;
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
interface State {
|
|
error: Error | null;
|
|
}
|
|
|
|
export class PaneBoundary extends React.Component<Props, State> {
|
|
state: State = { error: null };
|
|
|
|
static getDerivedStateFromError(error: Error): State {
|
|
return { error };
|
|
}
|
|
|
|
componentDidCatch(error: Error, info: React.ErrorInfo): void {
|
|
console.error(`[PaneBoundary:${this.props.sectionName}]`, error, info);
|
|
}
|
|
|
|
render(): React.ReactNode {
|
|
if (!this.state.error) return this.props.children;
|
|
return (
|
|
<div style={{
|
|
padding: 20, height: '100%', overflow: 'auto',
|
|
background: '#000', color: '#fff', fontFamily: 'monospace',
|
|
}}>
|
|
<div style={{
|
|
color: '#ff6b6b', fontSize: 14, fontWeight: 700, marginBottom: 8,
|
|
}}>
|
|
{this.props.sectionName} crashed
|
|
</div>
|
|
<div style={{ color: '#fff', fontSize: 13, marginBottom: 8 }}>
|
|
{this.state.error.message}
|
|
</div>
|
|
<pre style={{
|
|
color: '#8b8b8b', fontSize: 11, lineHeight: 1.4,
|
|
whiteSpace: 'pre-wrap', wordBreak: 'break-word',
|
|
}}>
|
|
{this.state.error.stack}
|
|
</pre>
|
|
<button
|
|
onClick={() => this.setState({ error: null })}
|
|
style={{
|
|
marginTop: 10, padding: '6px 12px', borderRadius: 999,
|
|
border: '1px solid #1f1f1f', background: '#111',
|
|
color: '#fff', fontSize: 12, cursor: 'pointer',
|
|
}}
|
|
>Retry</button>
|
|
</div>
|
|
);
|
|
}
|
|
}
|