mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-03 08:06:59 +00:00
fix: read toasts, the undo bar and the offline banner out on ios
This commit is contained in:
@@ -6,6 +6,7 @@ import { useOutboxStore } from '../stores/outbox-store';
|
||||
import { useLocaleStore } from '../stores/locale-store';
|
||||
import { spacing, typography, type ThemePalette } from '../theme/tokens';
|
||||
import { useColors } from '../theme/colors';
|
||||
import { announceConnectivity } from '../lib/announce';
|
||||
|
||||
interface OfflineBannerProps {
|
||||
/** Optional extra notice (e.g. "Showing cached mail"). */
|
||||
@@ -19,6 +20,11 @@ export function OfflineBanner({ hint }: OfflineBannerProps) {
|
||||
const t = useLocaleStore((s) => s.t);
|
||||
// Pending offline mutations that will replay once we're back online.
|
||||
const queued = useOutboxStore((s) => s.entries.length);
|
||||
const offlineLabel = t('offline.banner', 'You are offline');
|
||||
// VoiceOver hears the drop too (Android reads the live region below).
|
||||
React.useEffect(() => {
|
||||
announceConnectivity(online, offlineLabel);
|
||||
}, [online, offlineLabel]);
|
||||
if (online) return null;
|
||||
const queuedHint = queued > 0
|
||||
? t(
|
||||
@@ -28,7 +34,7 @@ export function OfflineBanner({ hint }: OfflineBannerProps) {
|
||||
)
|
||||
: null;
|
||||
const suffix = [hint, queuedHint].filter(Boolean).join(' · ');
|
||||
const label = t('offline.banner', 'You are offline');
|
||||
const label = offlineLabel;
|
||||
return (
|
||||
<View style={styles.bar} accessibilityRole="alert" accessibilityLiveRegion="polite">
|
||||
<CloudOff size={14} color={c.warningForeground} />
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useEmailStore } from '../stores/email-store';
|
||||
import { useSendUndoStore, restoreUndoneSend, type PendingUndoSend } from '../stores/send-undo-store';
|
||||
import { useLocaleStore } from '../stores/locale-store';
|
||||
import type { RootStackParamList } from '../navigation/types';
|
||||
import { announce } from '../lib/announce';
|
||||
|
||||
const VISIBLE_MS = 5000;
|
||||
|
||||
@@ -70,6 +71,14 @@ export function UndoSnackbar() {
|
||||
if (current) setShown(current);
|
||||
}, [current]);
|
||||
|
||||
// Read the bar out on iOS, which has no live regions; Android reads the
|
||||
// bar's live region. Once per entry, not on every countdown re-render.
|
||||
const undoLabel = t('common.undo', 'Undo');
|
||||
React.useEffect(() => {
|
||||
if (!current) return;
|
||||
announce(current.label);
|
||||
}, [current?.createdAt, current?.label]);
|
||||
|
||||
// Drive the auto-dismiss off the entry's createdAt so re-renders during
|
||||
// animation don't reset the timer, and so consecutive actions reset it.
|
||||
React.useEffect(() => {
|
||||
@@ -133,13 +142,15 @@ export function UndoSnackbar() {
|
||||
{ left: frame.left, right: frame.right, bottom: insets.bottom + frame.bottom, opacity, transform: [{ translateY: slideY }] },
|
||||
]}
|
||||
>
|
||||
<View style={styles.bar}>
|
||||
<View style={styles.bar} accessibilityLiveRegion="polite">
|
||||
<Text style={styles.label} numberOfLines={1}>{shown.label}</Text>
|
||||
{shown.send && (
|
||||
<Pressable
|
||||
onPress={onSendNow}
|
||||
hitSlop={8}
|
||||
disabled={sendBusy}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('email_composer.send_now', 'Send now')}
|
||||
style={({ pressed }) => [styles.undoBtn, pressed && styles.undoBtnPressed]}
|
||||
>
|
||||
<Text style={styles.sendNowText}>{t('email_composer.send_now', 'Send now').toUpperCase()}</Text>
|
||||
@@ -149,6 +160,8 @@ export function UndoSnackbar() {
|
||||
onPress={() => { void onUndo(); }}
|
||||
hitSlop={8}
|
||||
disabled={sendBusy}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={undoLabel}
|
||||
style={({ pressed }) => [styles.undoBtn, pressed && styles.undoBtnPressed]}
|
||||
>
|
||||
<Text style={styles.undoText}>{t('common.undo', 'Undo').toUpperCase()}</Text>
|
||||
|
||||
Reference in New Issue
Block a user