mirror of
https://github.com/RoboSats/robosats.git
synced 2025-01-05 22:10:10 +00:00
Add tooltips and helper buttons
This commit is contained in:
parent
d492475eec
commit
83564df25a
@ -14,7 +14,7 @@ import {
|
|||||||
Link,
|
Link,
|
||||||
} from "@mui/material"
|
} from "@mui/material"
|
||||||
|
|
||||||
import { saveAsJson, saveAsTxt } from "../../utils/saveFile";
|
import { saveAsJson } from "../../utils/saveFile";
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
import KeyIcon from '@mui/icons-material/Key';
|
import KeyIcon from '@mui/icons-material/Key';
|
||||||
@ -53,120 +53,130 @@ const AuditPGPDialog = ({
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
>
|
>
|
||||||
<DialogTitle >
|
<DialogTitle >
|
||||||
{t("This chat is PGP Encrypted")}
|
{t("Do not trust, verify!")}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText>
|
<DialogContentText>
|
||||||
{t("Your communication is end-to-end encrypted with OpenPGP. You can verify the privacy of this chat using any third party tool based on the OpenPGP standard.")}
|
{t("Your communication is end-to-end encrypted with OpenPGP. You can verify the privacy of this chat using any tool based on the OpenPGP standard.")}
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
<Grid container spacing={1} align="center">
|
<Grid container spacing={1} align="center">
|
||||||
|
|
||||||
<Grid item align="center" xs={12}>
|
<Grid item align="center" xs={12}>
|
||||||
<Button component={Link} target="_blank" href="https://learn.robosats.com/docs/pgp-encryption">{t("Learn how to audit")}</Button>
|
<Button component={Link} target="_blank" href="https://learn.robosats.com/docs/pgp-encryption">{t("Learn how to verify")}</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item align="center" xs={12}>
|
<Grid item align="center" xs={12}>
|
||||||
<TextField
|
<Tooltip placement="top" enterTouchDelay={0} enterDelay={0} title={t("Your PGP public key. Your peer uses it to encrypt messages only you can read.")}>
|
||||||
sx={{width:"100%", maxWidth:"550px"}}
|
<TextField
|
||||||
disabled
|
sx={{width:"100%", maxWidth:"550px"}}
|
||||||
label={<b>{t("Your public key")}</b>}
|
disabled
|
||||||
value={own_pub_key}
|
label={<b>{t("Your public key")}</b>}
|
||||||
variant='filled'
|
value={own_pub_key}
|
||||||
size='small'
|
variant='filled'
|
||||||
InputProps={{
|
size='small'
|
||||||
endAdornment:
|
InputProps={{
|
||||||
<Tooltip disableHoverListener enterTouchDelay={0} title={t("Copied!")}>
|
endAdornment:
|
||||||
<IconButton onClick={()=> navigator.clipboard.writeText(own_pub_key)}>
|
<Tooltip disableHoverListener enterTouchDelay={0} title={t("Copied!")}>
|
||||||
<ContentCopy/>
|
<IconButton onClick={()=> navigator.clipboard.writeText(own_pub_key)}>
|
||||||
</IconButton>
|
<ContentCopy/>
|
||||||
</Tooltip>,
|
</IconButton>
|
||||||
}}
|
</Tooltip>,
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item align="center" xs={12}>
|
<Grid item align="center" xs={12}>
|
||||||
<TextField
|
<Tooltip placement="top" enterTouchDelay={0} enterDelay={0} title={t("Your peer PGP public key. You use it to encrypt messages only he can read.and to verify your peer signed the incoming messages.")}>
|
||||||
sx={{width:"100%", maxWidth:"550px"}}
|
<TextField
|
||||||
disabled
|
sx={{width:"100%", maxWidth:"550px"}}
|
||||||
label={<b>{t("Peer public key")}</b>}
|
disabled
|
||||||
value={peer_pub_key}
|
label={<b>{t("Peer public key")}</b>}
|
||||||
variant='filled'
|
value={peer_pub_key}
|
||||||
size='small'
|
variant='filled'
|
||||||
InputProps={{
|
size='small'
|
||||||
endAdornment:
|
InputProps={{
|
||||||
<Tooltip disableHoverListener enterTouchDelay={0} title={t("Copied!")}>
|
endAdornment:
|
||||||
<IconButton onClick={()=> navigator.clipboard.writeText(peer_pub_key)}>
|
<Tooltip disableHoverListener enterTouchDelay={0} title={t("Copied!")}>
|
||||||
<ContentCopy/>
|
<IconButton onClick={()=> navigator.clipboard.writeText(peer_pub_key)}>
|
||||||
</IconButton>
|
<ContentCopy/>
|
||||||
</Tooltip>,
|
</IconButton>
|
||||||
}}
|
</Tooltip>,
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item align="center" xs={12}>
|
<Grid item align="center" xs={12}>
|
||||||
<TextField
|
<Tooltip placement="top" enterTouchDelay={0} enterDelay={0} title={t("Your encrypted private key. You use it to decrypt the messages that your peer encrypted for you. You also use it to sign the messages you send.")}>
|
||||||
sx={{width:"100%", maxWidth:"550px"}}
|
<TextField
|
||||||
disabled
|
sx={{width:"100%", maxWidth:"550px"}}
|
||||||
label={<b>{t("Your encrypted private key")}</b>}
|
disabled
|
||||||
value={own_enc_priv_key}
|
label={<b>{t("Your encrypted private key")}</b>}
|
||||||
variant='filled'
|
value={own_enc_priv_key}
|
||||||
size='small'
|
variant='filled'
|
||||||
InputProps={{
|
size='small'
|
||||||
endAdornment:
|
InputProps={{
|
||||||
<Tooltip disableHoverListener enterTouchDelay={0} title={t("Copied!")}>
|
endAdornment:
|
||||||
<IconButton onClick={()=> navigator.clipboard.writeText(own_enc_priv_key)}>
|
<Tooltip disableHoverListener enterTouchDelay={0} title={t("Copied!")}>
|
||||||
<ContentCopy/>
|
<IconButton onClick={()=> navigator.clipboard.writeText(own_enc_priv_key)}>
|
||||||
</IconButton>
|
<ContentCopy/>
|
||||||
</Tooltip>,
|
</IconButton>
|
||||||
}}
|
</Tooltip>,
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item align="center" xs={12}>
|
<Grid item align="center" xs={12}>
|
||||||
<TextField
|
<Tooltip placement="top" enterTouchDelay={0} enterDelay={0} title={t("The passphrase to decrypt your private key. Only you know it! Do not share. It is also your robot avatar user token.")}>
|
||||||
sx={{width:"100%", maxWidth:"550px"}}
|
<TextField
|
||||||
disabled
|
sx={{width:"100%", maxWidth:"550px"}}
|
||||||
label={<b>{t("Your private key passphrase (keep secure!)")}</b>}
|
disabled
|
||||||
value={passphrase}
|
label={<b>{t("Your private key passphrase (keep secure!)")}</b>}
|
||||||
variant='filled'
|
value={passphrase}
|
||||||
size='small'
|
variant='filled'
|
||||||
InputProps={{
|
size='small'
|
||||||
endAdornment:
|
InputProps={{
|
||||||
<Tooltip disableHoverListener enterTouchDelay={0} title={t("Copied!")}>
|
endAdornment:
|
||||||
<IconButton onClick={()=> navigator.clipboard.writeText(passphrase)}>
|
<Tooltip disableHoverListener enterTouchDelay={0} title={t("Copied!")}>
|
||||||
<ContentCopy/>
|
<IconButton onClick={()=> navigator.clipboard.writeText(passphrase)}>
|
||||||
</IconButton>
|
<ContentCopy/>
|
||||||
</Tooltip>,
|
</IconButton>
|
||||||
}}
|
</Tooltip>,
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
<Button
|
<Tooltip placement="top" enterTouchDelay={0} enterDelay={1000} enterNextDelay={2000} title={t("Save credentials as a JSON file")}>
|
||||||
size="small"
|
<Button
|
||||||
color="primary"
|
size="small"
|
||||||
variant="contained"
|
color="primary"
|
||||||
onClick={()=>saveAsJson(
|
variant="contained"
|
||||||
'keys_'+orderId+'.json',
|
onClick={()=>saveAsJson(
|
||||||
{"own_public_key": own_pub_key,
|
'keys_'+orderId+'.json',
|
||||||
"peer_public_key":peer_pub_key,
|
{"own_public_key": own_pub_key,
|
||||||
"encrypted_private_key":own_enc_priv_key,
|
"peer_public_key":peer_pub_key,
|
||||||
"passphrase":passphrase
|
"encrypted_private_key":own_enc_priv_key,
|
||||||
})}>
|
"passphrase":passphrase
|
||||||
<div style={{width:26,height:18}}>
|
})}>
|
||||||
<ExportIcon sx={{width:18,height:18}}/>
|
<div style={{width:26,height:18}}>
|
||||||
</div>
|
<ExportIcon sx={{width:18,height:18}}/>
|
||||||
{t("Keys")}
|
</div>
|
||||||
<div style={{width:26,height:20}}>
|
{t("Keys")}
|
||||||
<KeyIcon sx={{width:20,height:20}}/>
|
<div style={{width:26,height:20}}>
|
||||||
</div>
|
<KeyIcon sx={{width:20,height:20}}/>
|
||||||
</Button>
|
</div>
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
{/* <ToolTip placement="top" enterTouchDelay={0} enterDelay={1000} enterNextDelay={2000} title={t("Save local messages and credentials as a JSON file")}> */}
|
<Tooltip placement="top" enterTouchDelay={0} enterDelay={1000} enterNextDelay={2000} title={t("Save messages as a JSON file")}>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
color="primary"
|
color="primary"
|
||||||
@ -182,7 +192,7 @@ const AuditPGPDialog = ({
|
|||||||
<ForumIcon sx={{width:20,height:20}}/>
|
<ForumIcon sx={{width:20,height:20}}/>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
{/* </ToolTip> */}
|
</Tooltip>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { withTranslation } from "react-i18next";
|
import { withTranslation } from "react-i18next";
|
||||||
import {Button, Badge, Tooltip, TextField, Grid, Container, Card, CardHeader, Paper, Avatar, Typography} from "@mui/material";
|
import {Button, IconButton, Badge, Tooltip, TextField, Grid, Container, Card, CardHeader, Paper, Avatar, Typography} from "@mui/material";
|
||||||
import ReconnectingWebSocket from 'reconnecting-websocket';
|
import ReconnectingWebSocket from 'reconnecting-websocket';
|
||||||
import { encryptMessage , decryptMessage} from "../utils/pgp";
|
import { encryptMessage , decryptMessage} from "../utils/pgp";
|
||||||
import { getCookie } from "../utils/cookies";
|
import { getCookie } from "../utils/cookies";
|
||||||
import { saveAsJson, saveAsTxt } from "../utils/saveFile";
|
import { saveAsJson } from "../utils/saveFile";
|
||||||
import { AuditPGPDialog } from "./Dialogs"
|
import { AuditPGPDialog } from "./Dialogs"
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
import CheckIcon from '@mui/icons-material/Check';
|
import CheckIcon from '@mui/icons-material/Check';
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
import FileDownloadIcon from '@mui/icons-material/FileDownload';
|
import ContentCopy from "@mui/icons-material/ContentCopy";
|
||||||
import VisibilityIcon from '@mui/icons-material/Visibility';
|
import VisibilityIcon from '@mui/icons-material/Visibility';
|
||||||
import KeyIcon from '@mui/icons-material/Key';
|
import KeyIcon from '@mui/icons-material/Key';
|
||||||
import { ExportIcon } from './Icons';
|
import { ExportIcon } from './Icons';
|
||||||
@ -193,7 +193,24 @@ class Chat extends Component {
|
|||||||
</Badge>
|
</Badge>
|
||||||
}
|
}
|
||||||
style={{backgroundColor: '#eeeeee'}}
|
style={{backgroundColor: '#eeeeee'}}
|
||||||
title={<div style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}>{message.userNick}{message.validSignature ? <CheckIcon sx={{height:16, display: "inline-block"}} color="success"/> : <CloseIcon sx={{height:16, display:"inline-block"}} color="error"/> }</div>}
|
title={
|
||||||
|
<Tooltip placement="top" enterTouchDelay={0} enterDelay={500} enterNextDelay={2000} title={t(message.validSignature ? "Verified signature by {{nickname}}": "Invalid signature! Not sent by {{nickname}}",{"nickname": message.userNick})}>
|
||||||
|
<div style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}>
|
||||||
|
{message.userNick}
|
||||||
|
{message.validSignature ?
|
||||||
|
<CheckIcon sx={{height:16}} color="success"/>
|
||||||
|
:
|
||||||
|
<CloseIcon sx={{height:16}} color="error"/>
|
||||||
|
}
|
||||||
|
<div style={{width:20}}>
|
||||||
|
<IconButton sx={{height:16}}><VisibilityIcon sx={{height:16}}/></IconButton>
|
||||||
|
</div>
|
||||||
|
<div style={{width:20}}>
|
||||||
|
<IconButton sx={{height:16}}><ContentCopy sx={{height:16}}/></IconButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
subheader={this.state.audit ? message.encryptedMessage : message.plainTextMessage}
|
subheader={this.state.audit ? message.encryptedMessage : message.plainTextMessage}
|
||||||
subheaderTypographyProps={{sx: {wordWrap: "break-word", width: '200px', color: '#444444'}}}
|
subheaderTypographyProps={{sx: {wordWrap: "break-word", width: '200px', color: '#444444'}}}
|
||||||
/>
|
/>
|
||||||
@ -209,15 +226,21 @@ class Chat extends Component {
|
|||||||
}
|
}
|
||||||
style={{backgroundColor: '#fafafa'}}
|
style={{backgroundColor: '#fafafa'}}
|
||||||
title={
|
title={
|
||||||
<Tooltip placement="top" enterTouchDelay={0} enterDelay={500} enterNextDelay={2000} title={t(message.validSignature ? "Verified signature by {{sender}}": "Invalid signature",{"sender": message.userNick})}>
|
<Tooltip placement="top" enterTouchDelay={0} enterDelay={500} enterNextDelay={2000} title={t(message.validSignature ? "Verified signature by {{nickname}}": "Invalid signature! Not sent by {{nickname}}",{"nickname": message.userNick})}>
|
||||||
<div style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}>
|
<div style={{display:'flex',alignItems:'center', flexWrap:'wrap'}}>
|
||||||
{message.userNick}
|
{message.userNick}
|
||||||
{message.validSignature ?
|
{message.validSignature ?
|
||||||
<CheckIcon sx={{height:16, display: "inline-block"}} color="success"/>
|
<CheckIcon sx={{height:16}} color="success"/>
|
||||||
:
|
:
|
||||||
<CloseIcon sx={{height:16, display:"inline-block"}} color="error"/>
|
<CloseIcon sx={{height:16}} color="error"/>
|
||||||
}
|
}
|
||||||
</div>
|
<div style={{width:20}}>
|
||||||
|
<IconButton sx={{height:16}}><VisibilityIcon sx={{height:16}}/></IconButton>
|
||||||
|
</div>
|
||||||
|
<div style={{width:20}}>
|
||||||
|
<IconButton sx={{height:16}}><ContentCopy sx={{height:16}}/></IconButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</Tooltip>}
|
</Tooltip>}
|
||||||
subheader={this.state.audit ? message.encryptedMessage : message.plainTextMessage}
|
subheader={this.state.audit ? message.encryptedMessage : message.plainTextMessage}
|
||||||
subheaderTypographyProps={{sx: {wordWrap: "break-word", width: '200px', color: '#444444'}}}
|
subheaderTypographyProps={{sx: {wordWrap: "break-word", width: '200px', color: '#444444'}}}
|
||||||
@ -268,7 +291,7 @@ class Chat extends Component {
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs={6}>
|
<Grid item xs={6}>
|
||||||
<Tooltip placement="bottom" enterTouchDelay={0} enterDelay={500} enterNextDelay={2000} title={t("Save messages and credentials as a JSON file")}>
|
<Tooltip placement="bottom" enterTouchDelay={0} enterDelay={500} enterNextDelay={2000} title={t("Save full log as a JSON file (messages and credentials)")}>
|
||||||
<Button size="small" color="primary" variant="outlined" onClick={()=>saveAsJson('chat_'+this.props.orderId+'.json', this.createJsonFile())}><div style={{width:28,height:20}}><ExportIcon sx={{width:20,height:20}}/></div> {t("Export")} </Button>
|
<Button size="small" color="primary" variant="outlined" onClick={()=>saveAsJson('chat_'+this.props.orderId+'.json', this.createJsonFile())}><div style={{width:28,height:20}}><ExportIcon sx={{width:20,height:20}}/></div> {t("Export")} </Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -3,24 +3,6 @@
|
|||||||
* @param {filename} data -- object to save
|
* @param {filename} data -- object to save
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const saveAsTxt = (filename, dataObjToWrite) => {
|
|
||||||
const blob = new Blob([JSON.stringify(dataObjToWrite, null, 2)], { type: "text/plain;charset=utf8" });
|
|
||||||
const link = document.createElement("a");
|
|
||||||
|
|
||||||
link.download = filename;
|
|
||||||
link.href = window.URL.createObjectURL(blob);
|
|
||||||
link.dataset.downloadurl = ["text/plain;charset=utf8", link.download, link.href].join(":");
|
|
||||||
|
|
||||||
const evt = new MouseEvent("click", {
|
|
||||||
view: window,
|
|
||||||
bubbles: true,
|
|
||||||
cancelable: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
link.dispatchEvent(evt);
|
|
||||||
link.remove()
|
|
||||||
};
|
|
||||||
|
|
||||||
export const saveAsJson = (filename, dataObjToWrite) => {
|
export const saveAsJson = (filename, dataObjToWrite) => {
|
||||||
const blob = new Blob([JSON.stringify(dataObjToWrite, null, 2)], { type: "text/json" });
|
const blob = new Blob([JSON.stringify(dataObjToWrite, null, 2)], { type: "text/json" });
|
||||||
const link = document.createElement("a");
|
const link = document.createElement("a");
|
||||||
|
Loading…
Reference in New Issue
Block a user