Show password indicator and masked password field for encrypted sends

- Add password and authType fields to Send type
- Show lock icon in send list for password-protected sends
- Display masked dots in password field when editing a send that has a password set
- Add Remove button to clear existing password
This commit is contained in:
shuaiplus
2026-07-07 00:31:56 +08:00
parent a366acbac0
commit a870142b7b
3 changed files with 29 additions and 7 deletions
+21 -7
View File
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
import { CheckCheck, ChevronLeft, Copy, Eye, EyeOff, File, FileText, LayoutGrid, Pencil, Plus, RefreshCw, Save, Send as SendIcon, Trash2, X } from 'lucide-preact';
import { CheckCheck, ChevronLeft, Copy, Eye, EyeOff, File, FileText, LayoutGrid, Lock, Pencil, Plus, RefreshCw, Save, Send as SendIcon, Trash2, X } from 'lucide-preact';
import { copyTextToClipboard } from '@/lib/clipboard';
import LoadingState from '@/components/LoadingState';
import type { Send, SendDraft } from '@/lib/types';
@@ -43,6 +43,7 @@ function buildDefaultDraft(): SendDraft {
expirationDays: '0',
maxAccessCount: '',
password: '',
hasPassword: false,
disabled: false,
};
}
@@ -59,6 +60,7 @@ function draftFromSend(send: Send): SendDraft {
expirationDays: daysFromNow(send.expirationDate, 0),
maxAccessCount: send.maxAccessCount !== null && send.maxAccessCount !== undefined ? String(send.maxAccessCount) : '',
password: '',
hasPassword: !!send.password,
disabled: !!send.disabled,
};
}
@@ -380,6 +382,7 @@ export default function SendsPage(props: SendsPageProps) {
<div className="list-text">
<span className="list-title" title={send.decName || t('txt_no_name')}>{send.decName || t('txt_no_name')}</span>
<span className="list-sub">
{!!send.password && <><Lock size={12} className="inline-icon" /> </>}
{Number(send.type) === 1 ? t('txt_file') : t('txt_text')} - {t('txt_accessed_count_times', { count: send.accessCount || 0 })}
</span>
</div>
@@ -471,12 +474,23 @@ export default function SendsPage(props: SendsPageProps) {
</label>
<label className="field">
<span>{t('txt_password')}</span>
<div className="password-wrap">
<input className="input" type={showPassword ? 'text' : 'password'} value={draft.password} onInput={(e) => setDraft({ ...draft, password: (e.currentTarget as HTMLInputElement).value })} />
<button type="button" className="password-toggle" onClick={() => setShowPassword((v) => !v)}>
{showPassword ? <EyeOff size={16} /> : <Eye size={16} />}
</button>
</div>
{draft.hasPassword ? (
<div className="password-wrap">
<input className="input" type="password" value="••••••••" disabled />
{!isCreating && (
<button type="button" className="btn btn-secondary small" onClick={() => setDraft({ ...draft, hasPassword: false, password: '' })}>
{t('txt_remove')}
</button>
)}
</div>
) : (
<div className="password-wrap">
<input className="input" type={showPassword ? 'text' : 'password'} value={draft.password} onInput={(e) => setDraft({ ...draft, password: (e.currentTarget as HTMLInputElement).value })} />
<button type="button" className="password-toggle" onClick={() => setShowPassword((v) => !v)}>
{showPassword ? <EyeOff size={16} /> : <Eye size={16} />}
</button>
</div>
)}
</label>
<label className="field field-span-2">
<span>{t('txt_notes')}</span>