Add tooltips and helper buttons

This commit is contained in:
Reckless_Satoshi 2022-05-24 06:33:55 -07:00
parent d492475eec
commit 83564df25a
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
3 changed files with 136 additions and 121 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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");