mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-08-05 06:50:10 +00:00
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:
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
|
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 { copyTextToClipboard } from '@/lib/clipboard';
|
||||||
import LoadingState from '@/components/LoadingState';
|
import LoadingState from '@/components/LoadingState';
|
||||||
import type { Send, SendDraft } from '@/lib/types';
|
import type { Send, SendDraft } from '@/lib/types';
|
||||||
@@ -43,6 +43,7 @@ function buildDefaultDraft(): SendDraft {
|
|||||||
expirationDays: '0',
|
expirationDays: '0',
|
||||||
maxAccessCount: '',
|
maxAccessCount: '',
|
||||||
password: '',
|
password: '',
|
||||||
|
hasPassword: false,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -59,6 +60,7 @@ function draftFromSend(send: Send): SendDraft {
|
|||||||
expirationDays: daysFromNow(send.expirationDate, 0),
|
expirationDays: daysFromNow(send.expirationDate, 0),
|
||||||
maxAccessCount: send.maxAccessCount !== null && send.maxAccessCount !== undefined ? String(send.maxAccessCount) : '',
|
maxAccessCount: send.maxAccessCount !== null && send.maxAccessCount !== undefined ? String(send.maxAccessCount) : '',
|
||||||
password: '',
|
password: '',
|
||||||
|
hasPassword: !!send.password,
|
||||||
disabled: !!send.disabled,
|
disabled: !!send.disabled,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -380,6 +382,7 @@ export default function SendsPage(props: SendsPageProps) {
|
|||||||
<div className="list-text">
|
<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-title" title={send.decName || t('txt_no_name')}>{send.decName || t('txt_no_name')}</span>
|
||||||
<span className="list-sub">
|
<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 })}
|
{Number(send.type) === 1 ? t('txt_file') : t('txt_text')} - {t('txt_accessed_count_times', { count: send.accessCount || 0 })}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -471,12 +474,23 @@ export default function SendsPage(props: SendsPageProps) {
|
|||||||
</label>
|
</label>
|
||||||
<label className="field">
|
<label className="field">
|
||||||
<span>{t('txt_password')}</span>
|
<span>{t('txt_password')}</span>
|
||||||
|
{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">
|
<div className="password-wrap">
|
||||||
<input className="input" type={showPassword ? 'text' : 'password'} value={draft.password} onInput={(e) => setDraft({ ...draft, password: (e.currentTarget as HTMLInputElement).value })} />
|
<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)}>
|
<button type="button" className="password-toggle" onClick={() => setShowPassword((v) => !v)}>
|
||||||
{showPassword ? <EyeOff size={16} /> : <Eye size={16} />}
|
{showPassword ? <EyeOff size={16} /> : <Eye size={16} />}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</label>
|
</label>
|
||||||
<label className="field field-span-2">
|
<label className="field field-span-2">
|
||||||
<span>{t('txt_notes')}</span>
|
<span>{t('txt_notes')}</span>
|
||||||
|
|||||||
@@ -280,6 +280,8 @@ export interface Send {
|
|||||||
key?: string | null;
|
key?: string | null;
|
||||||
maxAccessCount?: number | null;
|
maxAccessCount?: number | null;
|
||||||
accessCount?: number;
|
accessCount?: number;
|
||||||
|
password?: string | null;
|
||||||
|
authType?: number | null;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
revisionDate?: string;
|
revisionDate?: string;
|
||||||
expirationDate?: string | null;
|
expirationDate?: string | null;
|
||||||
@@ -308,6 +310,7 @@ export interface SendDraft {
|
|||||||
expirationDays: string;
|
expirationDays: string;
|
||||||
maxAccessCount: string;
|
maxAccessCount: string;
|
||||||
password: string;
|
password: string;
|
||||||
|
hasPassword?: boolean;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1269,3 +1269,8 @@ select.input.duplicate-mode-toolbar-select {
|
|||||||
@apply text-sm;
|
@apply text-sm;
|
||||||
color: var(--danger);
|
color: var(--danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inline-icon {
|
||||||
|
display: inline;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user