Allow copy and paste on logins
- allow shell open for linking - some platforms it's not working as expected
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
"updater:allow-download",
|
||||
"updater:allow-download-and-install",
|
||||
"updater:allow-install",
|
||||
"core:event:allow-listen"
|
||||
"core:event:allow-listen",
|
||||
"shell:allow-open"
|
||||
]
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
{"main-capability":{"identifier":"main-capability","description":"Default capability for Nym Wallet main window","local":true,"windows":["main","nymWalletApp","log"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","opener:allow-open-url","opener:allow-default-urls","core:window:allow-set-title","core:app:allow-version","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text","updater:default","updater:allow-check","updater:allow-download","updater:allow-download-and-install","updater:allow-install","core:event:allow-listen"],"platforms":["linux","macOS","windows"]}}
|
||||
{"main-capability":{"identifier":"main-capability","description":"Default capability for Nym Wallet main window","local":true,"windows":["main","nymWalletApp","log"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","opener:allow-open-url","opener:allow-default-urls","core:window:allow-set-title","core:app:allow-version","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text","updater:default","updater:allow-check","updater:allow-download","updater:allow-download-and-install","updater:allow-install","core:event:allow-listen","shell:allow-open"],"platforms":["linux","macOS","windows"]}}
|
||||
@@ -0,0 +1,117 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
import { PasswordInput } from '@nymproject/react/textfields/Password';
|
||||
import { readText } from '@tauri-apps/plugin-clipboard-manager';
|
||||
import ContentPasteIcon from '@mui/icons-material/ContentPaste';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
|
||||
interface PasteButtonProps {
|
||||
onPaste: () => void;
|
||||
}
|
||||
|
||||
const PasteButton: React.FC<PasteButtonProps> = ({ onPaste }) => (
|
||||
<Tooltip title="Paste from clipboard">
|
||||
<IconButton size="small" onClick={onPaste} aria-label="paste from clipboard">
|
||||
<ContentPasteIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
interface EnhancedPasswordInputProps {
|
||||
password: string;
|
||||
onUpdatePassword: (password: string) => void;
|
||||
label?: string;
|
||||
placeholder?: string;
|
||||
error?: string;
|
||||
autoFocus?: boolean;
|
||||
disabled?: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export const EnhancedPasswordInput: React.FC<EnhancedPasswordInputProps> = ({
|
||||
password,
|
||||
onUpdatePassword,
|
||||
...otherProps
|
||||
}) => {
|
||||
const inputRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const findInputElement = () => {
|
||||
if (!inputRef.current) return undefined;
|
||||
|
||||
const input = inputRef.current.querySelector('input');
|
||||
if (!input) return undefined;
|
||||
|
||||
const handleKeyDown = async (e: KeyboardEvent) => {
|
||||
if (document.activeElement !== input) return;
|
||||
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 'a') {
|
||||
e.preventDefault();
|
||||
setTimeout(() => {
|
||||
(input as HTMLInputElement).select();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 'v') {
|
||||
e.preventDefault();
|
||||
try {
|
||||
const clipboardText = await readText();
|
||||
if (clipboardText) {
|
||||
onUpdatePassword(clipboardText);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to paste text:', err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
input.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
return () => {
|
||||
input.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
};
|
||||
|
||||
const cleanup = findInputElement();
|
||||
const timeoutId = setTimeout(findInputElement, 100);
|
||||
|
||||
return () => {
|
||||
if (cleanup) cleanup();
|
||||
clearTimeout(timeoutId);
|
||||
};
|
||||
}, [onUpdatePassword]);
|
||||
|
||||
const handlePaste = async () => {
|
||||
try {
|
||||
const clipboardText = await readText();
|
||||
if (clipboardText) {
|
||||
onUpdatePassword(clipboardText);
|
||||
|
||||
const input = inputRef.current?.querySelector('input');
|
||||
if (input) {
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to paste from clipboard:', err);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box position="relative" ref={inputRef}>
|
||||
<PasswordInput password={password} onUpdatePassword={onUpdatePassword} {...otherProps} />
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
right: '40px',
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
zIndex: 1,
|
||||
}}
|
||||
>
|
||||
<PasteButton onPaste={handlePaste} />
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,123 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
import { MnemonicInput as OriginalMnemonicInput } from '@nymproject/react/textfields/Mnemonic';
|
||||
import { readText } from '@tauri-apps/plugin-clipboard-manager';
|
||||
import ContentPasteIcon from '@mui/icons-material/ContentPaste';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
|
||||
interface PasteButtonProps {
|
||||
onPaste: () => void;
|
||||
}
|
||||
|
||||
const PasteButton: React.FC<PasteButtonProps> = ({ onPaste }) => (
|
||||
<Tooltip title="Paste from clipboard">
|
||||
<IconButton size="small" onClick={onPaste} aria-label="paste from clipboard">
|
||||
<ContentPasteIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
interface EnhancedMnemonicInputProps {
|
||||
mnemonic: string;
|
||||
onUpdateMnemonic: (mnemonic: string) => void;
|
||||
error?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export { OriginalMnemonicInput as MnemonicInput };
|
||||
|
||||
export const EnhancedMnemonicInput: React.FC<EnhancedMnemonicInputProps> = ({
|
||||
mnemonic,
|
||||
onUpdateMnemonic,
|
||||
...otherProps
|
||||
}) => {
|
||||
const inputRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const findInputElement = () => {
|
||||
if (!inputRef.current) return undefined;
|
||||
|
||||
const textarea = inputRef.current.querySelector('textarea');
|
||||
const input = textarea || inputRef.current.querySelector('input');
|
||||
|
||||
if (!input) return undefined;
|
||||
|
||||
// Fix the event type issue by casting Event to KeyboardEvent
|
||||
const handleKeyDown = async (e: Event) => {
|
||||
const keyEvent = e as KeyboardEvent;
|
||||
if (document.activeElement !== input) return;
|
||||
|
||||
if ((keyEvent.metaKey || keyEvent.ctrlKey) && keyEvent.key === 'a') {
|
||||
keyEvent.preventDefault();
|
||||
setTimeout(() => {
|
||||
if (textarea) {
|
||||
textarea.select();
|
||||
} else {
|
||||
(input as HTMLInputElement).select();
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
if ((keyEvent.metaKey || keyEvent.ctrlKey) && keyEvent.key === 'v') {
|
||||
keyEvent.preventDefault();
|
||||
try {
|
||||
const clipboardText = await readText();
|
||||
if (clipboardText) {
|
||||
onUpdateMnemonic(clipboardText.trim());
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to paste text:', err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
input.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
return () => {
|
||||
input.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
};
|
||||
|
||||
const cleanup = findInputElement();
|
||||
const timeoutId = setTimeout(findInputElement, 100);
|
||||
|
||||
return () => {
|
||||
if (cleanup) cleanup();
|
||||
clearTimeout(timeoutId);
|
||||
};
|
||||
}, [onUpdateMnemonic]);
|
||||
|
||||
const handlePaste = async () => {
|
||||
try {
|
||||
const clipboardText = await readText();
|
||||
if (clipboardText) {
|
||||
onUpdateMnemonic(clipboardText.trim());
|
||||
|
||||
const textarea = inputRef.current?.querySelector('textarea');
|
||||
const input = textarea || inputRef.current?.querySelector('input');
|
||||
if (input) {
|
||||
input.focus();
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to paste from clipboard:', err);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box position="relative" ref={inputRef}>
|
||||
<OriginalMnemonicInput mnemonic={mnemonic} onUpdateMnemonic={onUpdateMnemonic} {...otherProps} />
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
right: '14px',
|
||||
top: '16px',
|
||||
zIndex: 1,
|
||||
}}
|
||||
>
|
||||
<PasteButton onPaste={handlePaste} />
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { Box, Button, FormControl, Stack } from '@mui/material';
|
||||
import { AppContext } from 'src/context';
|
||||
import { isPasswordCreated } from 'src/requests';
|
||||
import { MnemonicInput } from '@nymproject/react/textfields/Mnemonic';
|
||||
import { EnhancedMnemonicInput } from 'src/components/Login/MnemonicLoginFormWrapper';
|
||||
import { Subtitle } from '../components';
|
||||
|
||||
export const SignInMnemonic = () => {
|
||||
@@ -38,7 +38,11 @@ export const SignInMnemonic = () => {
|
||||
}}
|
||||
>
|
||||
<Stack spacing={2}>
|
||||
<MnemonicInput mnemonic={mnemonic} onUpdateMnemonic={(mnc) => setMnemonic(mnc)} error={error} />
|
||||
<EnhancedMnemonicInput
|
||||
mnemonic={mnemonic}
|
||||
onUpdateMnemonic={(mnc: string) => setMnemonic(mnc)}
|
||||
error={error}
|
||||
/>
|
||||
<Button variant="contained" size="large" fullWidth type="submit">
|
||||
Sign in with mnemonic
|
||||
</Button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useContext, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Box, Button, FormControl, Stack } from '@mui/material';
|
||||
import { PasswordInput } from '@nymproject/react/textfields/Password';
|
||||
import { EnhancedPasswordInput } from 'src/components/Login/LoginPasswordFormWrapper';
|
||||
import { Subtitle } from '../components';
|
||||
import { AppContext } from '../../../context/main';
|
||||
|
||||
@@ -21,10 +21,11 @@ export const SignInPassword = () => {
|
||||
}}
|
||||
>
|
||||
<Stack spacing={2}>
|
||||
<PasswordInput
|
||||
{/* Use the password wrapper input instead */}
|
||||
<EnhancedPasswordInput
|
||||
label="Enter password"
|
||||
password={password}
|
||||
onUpdatePassword={(pswd) => setPassword(pswd)}
|
||||
onUpdatePassword={(pswd: any) => setPassword(pswd)}
|
||||
error={error}
|
||||
autoFocus
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user