mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-17 18:44:45 +02:00
Desktop: Allow showing passwords in Master Password dialog
This commit is contained in:
parent
90957e5a34
commit
79d97f2ba7
@ -534,6 +534,9 @@ packages/app-desktop/gui/NoteToolbar/NoteToolbar.js.map
|
|||||||
packages/app-desktop/gui/OneDriveLoginScreen.d.ts
|
packages/app-desktop/gui/OneDriveLoginScreen.d.ts
|
||||||
packages/app-desktop/gui/OneDriveLoginScreen.js
|
packages/app-desktop/gui/OneDriveLoginScreen.js
|
||||||
packages/app-desktop/gui/OneDriveLoginScreen.js.map
|
packages/app-desktop/gui/OneDriveLoginScreen.js.map
|
||||||
|
packages/app-desktop/gui/PasswordInput/PasswordInput.d.ts
|
||||||
|
packages/app-desktop/gui/PasswordInput/PasswordInput.js
|
||||||
|
packages/app-desktop/gui/PasswordInput/PasswordInput.js.map
|
||||||
packages/app-desktop/gui/ResizableLayout/MoveButtons.d.ts
|
packages/app-desktop/gui/ResizableLayout/MoveButtons.d.ts
|
||||||
packages/app-desktop/gui/ResizableLayout/MoveButtons.js
|
packages/app-desktop/gui/ResizableLayout/MoveButtons.js
|
||||||
packages/app-desktop/gui/ResizableLayout/MoveButtons.js.map
|
packages/app-desktop/gui/ResizableLayout/MoveButtons.js.map
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -517,6 +517,9 @@ packages/app-desktop/gui/NoteToolbar/NoteToolbar.js.map
|
|||||||
packages/app-desktop/gui/OneDriveLoginScreen.d.ts
|
packages/app-desktop/gui/OneDriveLoginScreen.d.ts
|
||||||
packages/app-desktop/gui/OneDriveLoginScreen.js
|
packages/app-desktop/gui/OneDriveLoginScreen.js
|
||||||
packages/app-desktop/gui/OneDriveLoginScreen.js.map
|
packages/app-desktop/gui/OneDriveLoginScreen.js.map
|
||||||
|
packages/app-desktop/gui/PasswordInput/PasswordInput.d.ts
|
||||||
|
packages/app-desktop/gui/PasswordInput/PasswordInput.js
|
||||||
|
packages/app-desktop/gui/PasswordInput/PasswordInput.js.map
|
||||||
packages/app-desktop/gui/ResizableLayout/MoveButtons.d.ts
|
packages/app-desktop/gui/ResizableLayout/MoveButtons.d.ts
|
||||||
packages/app-desktop/gui/ResizableLayout/MoveButtons.js
|
packages/app-desktop/gui/ResizableLayout/MoveButtons.js
|
||||||
packages/app-desktop/gui/ResizableLayout/MoveButtons.js.map
|
packages/app-desktop/gui/ResizableLayout/MoveButtons.js.map
|
||||||
|
@ -5,12 +5,12 @@ import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffe
|
|||||||
import DialogButtonRow, { ClickEvent } from '../DialogButtonRow';
|
import DialogButtonRow, { ClickEvent } from '../DialogButtonRow';
|
||||||
import Dialog from '../Dialog';
|
import Dialog from '../Dialog';
|
||||||
import DialogTitle from '../DialogTitle';
|
import DialogTitle from '../DialogTitle';
|
||||||
import StyledInput from '../style/StyledInput';
|
import { getMasterPasswordStatus, getMasterPasswordStatusMessage, checkHasMasterPasswordEncryptedData, masterPasswordIsValid, MasterPasswordStatus, resetMasterPassword, updateMasterPassword, getMasterPassword } from '@joplin/lib/services/e2ee/utils';
|
||||||
import { getMasterPasswordStatus, getMasterPasswordStatusMessage, checkHasMasterPasswordEncryptedData, masterPasswordIsValid, MasterPasswordStatus, resetMasterPassword, updateMasterPassword } from '@joplin/lib/services/e2ee/utils';
|
|
||||||
import { reg } from '@joplin/lib/registry';
|
import { reg } from '@joplin/lib/registry';
|
||||||
import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
|
import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
|
||||||
import KvStore from '@joplin/lib/services/KvStore';
|
import KvStore from '@joplin/lib/services/KvStore';
|
||||||
import ShareService from '@joplin/lib/services/share/ShareService';
|
import ShareService from '@joplin/lib/services/share/ShareService';
|
||||||
|
import { PasswordInput } from '../PasswordInput/PasswordInput';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
themeId: number;
|
themeId: number;
|
||||||
@ -41,6 +41,10 @@ export default function(props: Props) {
|
|||||||
});
|
});
|
||||||
}, [props.dispatch]);
|
}, [props.dispatch]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCurrentPassword(getMasterPassword(false) || '');
|
||||||
|
}, []);
|
||||||
|
|
||||||
useAsyncEffect(async (event: AsyncEffectEvent) => {
|
useAsyncEffect(async (event: AsyncEffectEvent) => {
|
||||||
const newStatus = await getMasterPasswordStatus();
|
const newStatus = await getMasterPasswordStatus();
|
||||||
const hasIt = await checkHasMasterPasswordEncryptedData();
|
const hasIt = await checkHasMasterPasswordEncryptedData();
|
||||||
@ -122,7 +126,7 @@ export default function(props: Props) {
|
|||||||
|
|
||||||
function renderCurrentPasswordIcon() {
|
function renderCurrentPasswordIcon() {
|
||||||
if (!currentPassword || status === MasterPasswordStatus.NotSet) return null;
|
if (!currentPassword || status === MasterPasswordStatus.NotSet) return null;
|
||||||
return currentPasswordIsValid ? <i className="fas fa-check"></i> : <i className="fas fa-times"></i>;
|
return currentPasswordIsValid ? <i className="fas fa-check password-valid-icon"></i> : <i className="fas fa-times"></i>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPasswordForm() {
|
function renderPasswordForm() {
|
||||||
@ -130,15 +134,17 @@ export default function(props: Props) {
|
|||||||
if (status === MasterPasswordStatus.NotSet) return null;
|
if (status === MasterPasswordStatus.NotSet) return null;
|
||||||
if (mode === Mode.Reset) return null;
|
if (mode === Mode.Reset) return null;
|
||||||
|
|
||||||
|
// If the master password is in the keychain we preload it into the
|
||||||
|
// field and allow displaying it. That way if the user has forgotten
|
||||||
|
// their password, they have a chance to recover it that way without
|
||||||
|
// having to reset the password (and lose access to any data that's
|
||||||
|
// been encrypted with it).
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="form-input-group">
|
<div className="form-input-group">
|
||||||
<label>{'Current password'}</label>
|
<label>{'Current password'}</label>
|
||||||
<div className="current-password-wrapper">
|
<div className="current-password-wrapper">
|
||||||
<StyledInput
|
<PasswordInput value={currentPassword} onChange={onCurrentPasswordChange}/>
|
||||||
type="password"
|
|
||||||
value={currentPassword}
|
|
||||||
onChange={onCurrentPasswordChange}
|
|
||||||
/>
|
|
||||||
{renderCurrentPasswordIcon()}
|
{renderCurrentPasswordIcon()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -152,18 +158,20 @@ export default function(props: Props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (showPasswordForm) {
|
if (showPasswordForm) {
|
||||||
|
const enterPasswordLabel = [MasterPasswordStatus.Loaded, MasterPasswordStatus.Valid].includes(status) ? 'Enter new password' : 'Enter password';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="form">
|
<div className="form">
|
||||||
{renderCurrentPassword()}
|
{renderCurrentPassword()}
|
||||||
<div className="form-input-group">
|
<div className="form-input-group">
|
||||||
<label>{'Enter password'}</label>
|
<label>{enterPasswordLabel}</label>
|
||||||
<StyledInput type="password" value={password1} onChange={onPasswordChange1}/>
|
<PasswordInput value={password1} onChange={onPasswordChange1}/>
|
||||||
</div>
|
</div>
|
||||||
{needToRepeatPassword && (
|
{needToRepeatPassword && (
|
||||||
<div className="form-input-group">
|
<div className="form-input-group">
|
||||||
<label>{'Re-enter password'}</label>
|
<label>{'Re-enter password'}</label>
|
||||||
<StyledInput type="password" value={password2} onChange={onPasswordChange2}/>
|
<PasswordInput value={password2} onChange={onPasswordChange2}/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
31
packages/app-desktop/gui/PasswordInput/PasswordInput.tsx
Normal file
31
packages/app-desktop/gui/PasswordInput/PasswordInput.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { useState, useCallback } from 'react';
|
||||||
|
import StyledInput from '../style/StyledInput';
|
||||||
|
|
||||||
|
export interface ChangeEvent {
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChangeEventHandler = (event: ChangeEvent)=> void;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
value: string;
|
||||||
|
onChange: ChangeEventHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PasswordInput = (props: Props) => {
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
|
||||||
|
const inputType = showPassword ? 'text' : 'password';
|
||||||
|
const icon = showPassword ? 'far fa-eye-slash' : 'far fa-eye';
|
||||||
|
|
||||||
|
const onShowPassword = useCallback(() => {
|
||||||
|
setShowPassword(current => !current);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="password-input">
|
||||||
|
<StyledInput className="field" type={inputType} value={props.value} onChange={props.onChange}/>
|
||||||
|
<button onClick={onShowPassword} className="showpasswordbutton"><i className={icon}></i></button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
19
packages/app-desktop/gui/PasswordInput/style.scss
Normal file
19
packages/app-desktop/gui/PasswordInput/style.scss
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
.password-input {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
> .field {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .showpasswordbutton {
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
top: 4px;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
}
|
@ -149,7 +149,7 @@ a {
|
|||||||
General classes
|
General classes
|
||||||
========================================================================================= */
|
========================================================================================= */
|
||||||
|
|
||||||
body {
|
body, button {
|
||||||
color: var(--joplin-color);
|
color: var(--joplin-color);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
@ -238,12 +238,16 @@ Component-specific classes
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
|
> .password-valid-icon {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.master-password-dialog .current-password-wrapper input {
|
// .master-password-dialog .current-password-wrapper input {
|
||||||
flex: 1;
|
// flex: 1;
|
||||||
margin-right: 10px;
|
// margin-right: 10px;
|
||||||
}
|
// }
|
||||||
|
|
||||||
.master-password-dialog .fa-check {
|
.master-password-dialog .fa-check {
|
||||||
color: var(--joplin-color-correct);
|
color: var(--joplin-color-correct);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
@use 'main.scss' as main;
|
@use 'main.scss' as main;
|
||||||
@use 'gui/EncryptionConfigScreen/style.scss' as encryption-config-screen;
|
@use 'gui/EncryptionConfigScreen/style.scss' as encryption-config-screen;
|
||||||
|
@use 'gui/PasswordInput/style.scss' as password-input;
|
||||||
@use 'gui/ConfigScreen/style.scss' as config-screen;
|
@use 'gui/ConfigScreen/style.scss' as config-screen;
|
Loading…
Reference in New Issue
Block a user