mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-02 00:06:23 +00:00
fix: name the tag or person on each row's delete and remove-access button
This commit is contained in:
@@ -278,6 +278,9 @@
|
||||
"login_changed": "Sie haben das Konto gewechselt, daher wurde die Regel nicht gespeichert"
|
||||
}
|
||||
},
|
||||
"delegates": {
|
||||
"remove_for": "Zugriff für {name} entfernen"
|
||||
},
|
||||
"device_sync": {
|
||||
"auth_problem_text": "Der Server akzeptiert die Anmeldung von {account} nicht mehr, daher werden die Kontakte und Kalender dieses Kontos auf diesem Gerät nicht mehr synchronisiert. Melden Sie sich erneut an, um fortzufahren. Auf diesem Gerät vorgenommene Änderungen bleiben erhalten.",
|
||||
"auth_problem_title": "Erneut anmelden, um weiter zu synchronisieren",
|
||||
@@ -533,6 +536,7 @@
|
||||
"share_no_users": "Keine anderen Benutzer gefunden",
|
||||
"share_remove": "Zugriff entfernen",
|
||||
"share_remove_confirm": "Zugriff entfernen?",
|
||||
"share_remove_for": "Zugriff für {name} entfernen",
|
||||
"share_role_custom": "Benutzerdefiniert",
|
||||
"share_search_placeholder": "Nach Name oder E-Mail suchen",
|
||||
"share_unavailable": "Teilen ist auf diesem Gerät nicht verfügbar",
|
||||
|
||||
@@ -325,6 +325,9 @@
|
||||
"deep_link": {
|
||||
"open_failed": "Couldn't open this"
|
||||
},
|
||||
"delegates": {
|
||||
"remove_for": "Remove access for {name}"
|
||||
},
|
||||
"device_sync": {
|
||||
"auth_problem_text": "The server no longer accepts the sign-in of {account}, so its contacts and calendars on this device stopped syncing. Sign in again to resume. Changes made on this device are kept.",
|
||||
"auth_problem_title": "Sign in again to keep syncing",
|
||||
@@ -730,6 +733,7 @@
|
||||
"share_no_users": "No other users found",
|
||||
"share_remove": "Remove access",
|
||||
"share_remove_confirm": "Remove access?",
|
||||
"share_remove_for": "Remove access for {name}",
|
||||
"share_role_custom": "Custom",
|
||||
"share_search_placeholder": "Search by name or email",
|
||||
"share_unavailable": "Sharing is not available on this device",
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Buttons repeated on every row of a list read the same to a screen reader:
|
||||
// TalkBack heard "Delete tag" on each tag in Settings > Tags and "Remove
|
||||
// access" on each person a file is shared with, without saying whose. They
|
||||
// carry the row's name now (webmail keyword-settings.tsx: "Delete {name}").
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
const SRC = path.resolve(__dirname, '../..');
|
||||
const read = (rel: string) => fs.readFileSync(path.join(SRC, rel), 'utf8').split('\r\n').join('\n');
|
||||
|
||||
describe('per-row buttons', () => {
|
||||
it('name the tag they edit or delete', () => {
|
||||
const source = read('components/settings/KeywordSettings.tsx');
|
||||
expect(source).toContain("t('settings.keywords.delete_tag', 'Delete {name}', { name: kw.label })");
|
||||
expect(source).toContain("t('settings.keywords.edit_tag', 'Edit {name}', { name: kw.label })");
|
||||
expect(source).not.toMatch(/accessibilityLabel=\{t\('settings\.keywords\.(delete|edit)',/);
|
||||
});
|
||||
|
||||
it('name the person whose file access they remove', () => {
|
||||
const source = read('components/files/ShareSheet.tsx');
|
||||
expect(source).toContain("t('files.share_remove_for', 'Remove access for {name}', { name: principalName(principalId) })");
|
||||
expect(source).not.toMatch(/accessibilityLabel=\{t\('files\.share_remove', 'Remove access'\)\}/);
|
||||
});
|
||||
|
||||
it('name the delegate they change or remove', () => {
|
||||
const source = read('components/settings/DelegateSettings.tsx');
|
||||
expect(source).toContain("accessibilityLabel={t('delegates.remove_for', 'Remove access for {name}', { name: display.name })}");
|
||||
expect(source).toContain("accessibilityLabel={t('delegates.dialog_title_change', 'Change access for {name}', { name: display.name })}");
|
||||
});
|
||||
});
|
||||
@@ -167,15 +167,21 @@ export default function ShareSheet({ node, onClose, onChanged }: ShareSheetProps
|
||||
? t('sharing.directory_not_browsable', "Your server doesn't let you browse its user directory.")
|
||||
: null;
|
||||
|
||||
const renderPrincipalLabel = (principalId: string) => {
|
||||
const principalName = (principalId: string) => {
|
||||
const p = principalsById.get(principalId);
|
||||
// Stalwart 1.0 with directory queries off hides every principal the user
|
||||
// has no relationship with, so a sharee may have no name.
|
||||
const unresolved = !p && !loadingPrincipals;
|
||||
return p?.description || p?.name || (unresolved ? t('sharing.unknown_user', 'Unknown user') : principalId);
|
||||
};
|
||||
|
||||
const renderPrincipalLabel = (principalId: string) => {
|
||||
const p = principalsById.get(principalId);
|
||||
const unresolved = !p && !loadingPrincipals;
|
||||
return (
|
||||
<View style={styles.principalInfo}>
|
||||
<Text style={styles.principalName} numberOfLines={1}>
|
||||
{p?.description || p?.name || (unresolved ? t('sharing.unknown_user', 'Unknown user') : principalId)}
|
||||
{principalName(principalId)}
|
||||
</Text>
|
||||
{p?.email || unresolved ? (
|
||||
<Text style={styles.principalEmail} numberOfLines={1}>
|
||||
@@ -257,7 +263,8 @@ export default function ShareSheet({ node, onClose, onChanged }: ShareSheetProps
|
||||
)}
|
||||
hitSlop={8}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('files.share_remove', 'Remove access')}
|
||||
// One per person: the name tells the buttons apart.
|
||||
accessibilityLabel={t('files.share_remove_for', 'Remove access for {name}', { name: principalName(principalId) })}
|
||||
>
|
||||
<Trash2 size={18} color={c.error} />
|
||||
</Pressable>
|
||||
|
||||
@@ -241,10 +241,24 @@ export function DelegateSettings() {
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.rowButtons}>
|
||||
<Button variant="outline" size="sm" onPress={() => setDialog({ mode: 'change', summary })} disabled={busy || incomplete}>
|
||||
{/* Named with the person: every row has the same two buttons. */}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onPress={() => setDialog({ mode: 'change', summary })}
|
||||
disabled={busy || incomplete}
|
||||
accessibilityLabel={t('delegates.dialog_title_change', 'Change access for {name}', { name: display.name })}
|
||||
>
|
||||
{t('delegates.change', 'Change')}
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onPress={() => confirmRemove(summary)} disabled={incomplete} loading={busy}>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onPress={() => confirmRemove(summary)}
|
||||
disabled={incomplete}
|
||||
loading={busy}
|
||||
accessibilityLabel={t('delegates.remove_for', 'Remove access for {name}', { name: display.name })}
|
||||
>
|
||||
{t('delegates.remove', 'Remove access')}
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
@@ -120,10 +120,10 @@ export function KeywordSettings() {
|
||||
<Text style={styles.kwLabel}>{kw.label}</Text>
|
||||
<Text style={styles.kwId}>$label:{kw.id}</Text>
|
||||
<View style={{ flexDirection: 'row', gap: 2 }}>
|
||||
<Pressable style={styles.iconBtn} onPress={() => setEditingId(kw.id)} accessibilityRole="button" accessibilityLabel={t('settings.keywords.edit', "Edit tag")}>
|
||||
<Pressable style={styles.iconBtn} onPress={() => setEditingId(kw.id)} accessibilityRole="button" accessibilityLabel={t('settings.keywords.edit_tag', 'Edit {name}', { name: kw.label })}>
|
||||
<Pencil size={14} color={c.mutedForeground} />
|
||||
</Pressable>
|
||||
<Pressable style={styles.iconBtn} onPress={() => deleteKeyword(kw.id)} accessibilityRole="button" accessibilityLabel={t('settings.keywords.delete', "Delete tag")}>
|
||||
<Pressable style={styles.iconBtn} onPress={() => deleteKeyword(kw.id)} accessibilityRole="button" accessibilityLabel={t('settings.keywords.delete_tag', 'Delete {name}', { name: kw.label })}>
|
||||
<Trash2 size={14} color={c.mutedForeground} />
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user