mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-07-31 17:27:11 -04:00
Update TOTP create to hide name input by default in mobile app (#2216)
This commit is contained in:
@@ -50,9 +50,11 @@ export const TotpEditor: React.FC<TotpEditorProps> = ({
|
||||
const [isAddFormVisible, setIsAddFormVisible] = useState(false);
|
||||
const [formData, setFormData] = useState<TotpFormData>({ name: '', secretKey: '' });
|
||||
const [formError, setFormError] = useState<string | null>(null);
|
||||
const [showNameField, setShowNameField] = useState(false);
|
||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||
const [editingTotpCode, setEditingTotpCode] = useState<TotpCode | null>(null);
|
||||
const [editName, setEditName] = useState('');
|
||||
const [showEditNameField, setShowEditNameField] = useState(false);
|
||||
const [editSecret, setEditSecret] = useState('');
|
||||
const [showQrCode, setShowQrCode] = useState(false);
|
||||
const hasLaunchedScanner = React.useRef(false);
|
||||
@@ -78,6 +80,7 @@ export const TotpEditor: React.FC<TotpEditorProps> = ({
|
||||
hideAddChoiceModal();
|
||||
setFormData({ name: '', secretKey: '' });
|
||||
setFormError(null);
|
||||
setShowNameField(false);
|
||||
setIsAddFormVisible(true);
|
||||
};
|
||||
|
||||
@@ -107,7 +110,7 @@ export const TotpEditor: React.FC<TotpEditorProps> = ({
|
||||
const parsed = parseOtpAuthUri(scannedData);
|
||||
if (parsed && parsed.type === 'totp') {
|
||||
const secretKey = parsed.secret.replace(/\s/g, '').replace(/=+$/, '');
|
||||
const name = parsed.label || 'Authenticator';
|
||||
const name = parsed.label || '';
|
||||
|
||||
const newTotpCode: TotpCode = {
|
||||
Id: crypto.randomUUID().toUpperCase(),
|
||||
@@ -172,7 +175,8 @@ export const TotpEditor: React.FC<TotpEditorProps> = ({
|
||||
throw new Error(t('totp.errors.invalidSecretKey'));
|
||||
}
|
||||
|
||||
return { secretKey, name: name || 'Authenticator' };
|
||||
// Name is optional; keep it blank when none was provided or derived.
|
||||
return { secretKey, name };
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -182,6 +186,15 @@ export const TotpEditor: React.FC<TotpEditorProps> = ({
|
||||
setIsAddFormVisible(false);
|
||||
setFormData({ name: '', secretKey: '' });
|
||||
setFormError(null);
|
||||
setShowNameField(false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides the optional name field again and clears any entered value
|
||||
*/
|
||||
const hideNameField = (): void => {
|
||||
setFormData({ ...formData, name: '' });
|
||||
setShowNameField(false);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -264,7 +277,9 @@ export const TotpEditor: React.FC<TotpEditorProps> = ({
|
||||
*/
|
||||
const showEditModal = (totpCode: TotpCode): void => {
|
||||
setEditingTotpCode(totpCode);
|
||||
// Only reveal the name field when a name was actually set.
|
||||
setEditName(totpCode.Name);
|
||||
setShowEditNameField(!!totpCode.Name);
|
||||
setEditSecret(totpCode.SecretKey);
|
||||
setShowQrCode(false);
|
||||
setIsEditModalOpen(true);
|
||||
@@ -277,10 +292,20 @@ export const TotpEditor: React.FC<TotpEditorProps> = ({
|
||||
setIsEditModalOpen(false);
|
||||
setEditingTotpCode(null);
|
||||
setEditName('');
|
||||
setShowEditNameField(false);
|
||||
setEditSecret('');
|
||||
setShowQrCode(false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides the edit name field again and clears any entered value so the code
|
||||
* falls back to the default name on save.
|
||||
*/
|
||||
const hideEditNameField = (): void => {
|
||||
setEditName('');
|
||||
setShowEditNameField(false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Saves the edited TOTP code
|
||||
*/
|
||||
@@ -289,9 +314,12 @@ export const TotpEditor: React.FC<TotpEditorProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// The name is optional; store it as-is (blank when the user left it empty).
|
||||
const finalName = editName.trim();
|
||||
|
||||
const updatedTotpCodes = totpCodes.map(tc =>
|
||||
tc.Id === editingTotpCode.Id
|
||||
? { ...tc, Name: editName, SecretKey: editSecret }
|
||||
? { ...tc, Name: finalName, SecretKey: editSecret }
|
||||
: tc
|
||||
);
|
||||
|
||||
@@ -393,6 +421,21 @@ export const TotpEditor: React.FC<TotpEditorProps> = ({
|
||||
fontWeight: '600',
|
||||
marginTop: 12,
|
||||
},
|
||||
addNameLink: {
|
||||
color: colors.primary,
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
marginTop: 16,
|
||||
},
|
||||
nameLabelRow: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
removeNameButton: {
|
||||
marginTop: 12,
|
||||
padding: 4,
|
||||
},
|
||||
modalButtons: {
|
||||
flexDirection: 'row',
|
||||
gap: 12,
|
||||
@@ -533,7 +576,7 @@ export const TotpEditor: React.FC<TotpEditorProps> = ({
|
||||
<View key={totpCode.Id} style={styles.codeItem}>
|
||||
<View style={{ flex: 1 }}>
|
||||
<ThemedText style={styles.codeName}>
|
||||
{totpCode.Name}
|
||||
{totpCode.Name || t('totp.defaultName')}
|
||||
</ThemedText>
|
||||
<ThemedText style={styles.saveToViewText}>
|
||||
{t('totp.saveToViewCode')}
|
||||
@@ -550,7 +593,7 @@ export const TotpEditor: React.FC<TotpEditorProps> = ({
|
||||
style={styles.deleteButton}
|
||||
onPress={() => initiateTotpDelete(totpCode)}
|
||||
>
|
||||
<Ionicons name="trash" size={20} color={colors.errorText} />
|
||||
<Ionicons name="trash" size={20} color={colors.destructive} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
@@ -655,18 +698,6 @@ export const TotpEditor: React.FC<TotpEditorProps> = ({
|
||||
{t('totp.instructions')}
|
||||
</ThemedText>
|
||||
|
||||
<ThemedText style={styles.label}>
|
||||
{t('totp.nameOptional')}
|
||||
</ThemedText>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder={t('totp.nameOptional')}
|
||||
placeholderTextColor={colors.textMuted}
|
||||
value={formData.name}
|
||||
onChangeText={(text) => setFormData({ ...formData, name: text })}
|
||||
autoCapitalize="words"
|
||||
/>
|
||||
|
||||
<ThemedText style={styles.label}>
|
||||
{t('totp.secretKey')}
|
||||
</ThemedText>
|
||||
@@ -677,6 +708,7 @@ export const TotpEditor: React.FC<TotpEditorProps> = ({
|
||||
onChangeText={(text) => setFormData({ ...formData, secretKey: text })}
|
||||
autoCapitalize="characters"
|
||||
autoCorrect={false}
|
||||
autoFocus
|
||||
multiline
|
||||
/>
|
||||
|
||||
@@ -686,6 +718,35 @@ export const TotpEditor: React.FC<TotpEditorProps> = ({
|
||||
</ThemedText>
|
||||
)}
|
||||
|
||||
{/* Name is optional and hidden by default; revealed on demand since most codes don't need one. */}
|
||||
{showNameField ? (
|
||||
<>
|
||||
<View style={styles.nameLabelRow}>
|
||||
<ThemedText style={styles.label}>
|
||||
{t('totp.nameOptional')}
|
||||
</ThemedText>
|
||||
<TouchableOpacity style={styles.removeNameButton} onPress={hideNameField}>
|
||||
<MaterialIcons name="close" size={18} color={colors.textMuted} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder={t('totp.nameOptional')}
|
||||
placeholderTextColor={colors.textMuted}
|
||||
value={formData.name}
|
||||
onChangeText={(text) => setFormData({ ...formData, name: text })}
|
||||
autoCapitalize="words"
|
||||
autoFocus
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<TouchableOpacity onPress={() => setShowNameField(true)}>
|
||||
<ThemedText style={styles.addNameLink}>
|
||||
{t('totp.addName')}
|
||||
</ThemedText>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
|
||||
<View style={styles.modalButtons}>
|
||||
<TouchableOpacity
|
||||
style={styles.modalCancelButton}
|
||||
@@ -745,18 +806,6 @@ export const TotpEditor: React.FC<TotpEditorProps> = ({
|
||||
|
||||
{editingTotpCode && (
|
||||
<View>
|
||||
<ThemedText style={styles.label}>
|
||||
{t('totp.nameOptional')}
|
||||
</ThemedText>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder={t('totp.nameOptional')}
|
||||
placeholderTextColor={colors.textMuted}
|
||||
value={editName}
|
||||
onChangeText={setEditName}
|
||||
autoCapitalize="words"
|
||||
/>
|
||||
|
||||
<View style={styles.label}>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<ThemedText style={styles.label}>
|
||||
@@ -781,6 +830,35 @@ export const TotpEditor: React.FC<TotpEditorProps> = ({
|
||||
multiline
|
||||
/>
|
||||
|
||||
{/* Name is optional; hidden by default unless a custom name was set. */}
|
||||
{showEditNameField ? (
|
||||
<>
|
||||
<View style={styles.nameLabelRow}>
|
||||
<ThemedText style={styles.label}>
|
||||
{t('totp.nameOptional')}
|
||||
</ThemedText>
|
||||
<TouchableOpacity style={styles.removeNameButton} onPress={hideEditNameField}>
|
||||
<MaterialIcons name="close" size={18} color={colors.textMuted} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
placeholder={t('totp.nameOptional')}
|
||||
placeholderTextColor={colors.textMuted}
|
||||
value={editName}
|
||||
onChangeText={setEditName}
|
||||
autoCapitalize="words"
|
||||
autoFocus
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<TouchableOpacity onPress={() => setShowEditNameField(true)}>
|
||||
<ThemedText style={styles.addNameLink}>
|
||||
{t('totp.addName')}
|
||||
</ThemedText>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
|
||||
{showQrCode && (
|
||||
<View style={styles.qrCodeContainer}>
|
||||
<QRCode
|
||||
|
||||
@@ -198,7 +198,7 @@ export const TotpSection: React.FC<TotpSectionProps> = ({ item }) : React.ReactN
|
||||
<View style={styles.codeContainer}>
|
||||
<View>
|
||||
<ThemedText style={styles.label}>
|
||||
{t('items.totpCode')}
|
||||
{totpCode.Name || t('totp.defaultName')}
|
||||
</ThemedText>
|
||||
<ThemedText style={styles.code}>
|
||||
{formatTotpCode(currentCodes[totpCode.Id])}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"cancel": "Cancel",
|
||||
"close": "Close",
|
||||
"delete": "Delete",
|
||||
"remove": "Remove",
|
||||
"save": "Save",
|
||||
"edit": "Edit",
|
||||
"yes": "Yes",
|
||||
@@ -114,11 +115,13 @@
|
||||
"totp": {
|
||||
"addCode": "Add 2FA Code",
|
||||
"nameOptional": "Name (optional)",
|
||||
"addName": "Add a name (optional)",
|
||||
"defaultName": "Authenticator",
|
||||
"secretKey": "Secret Key",
|
||||
"instructions": "Enter the secret key shown by the website where you want to add two-factor authentication.",
|
||||
"saveToViewCode": "Save to view code",
|
||||
"scanQrCode": "Scan QR Code",
|
||||
"enterManually": "Enter a setup key",
|
||||
"enterManually": "Enter secret key",
|
||||
"errors": {
|
||||
"invalidSecretKey": "Invalid secret key format.",
|
||||
"scanFailed": "Failed to scan QR code. Please try again."
|
||||
|
||||
Reference in New Issue
Block a user