added qr code render
This commit is contained in:
parent
f7a8bc0660
commit
f5b3ded190
|
@ -28,6 +28,7 @@ import { LogoImage } from 'pages/_app';
|
|||
import { SetDialogMessage } from './MessageDialog';
|
||||
import EnteSpinner from './EnteSpinner';
|
||||
import RecoveryKeyModal from './RecoveryKeyModal';
|
||||
import TwoFactorModal from './TwoFactorModal';
|
||||
|
||||
interface Props {
|
||||
files: File[];
|
||||
|
@ -45,6 +46,7 @@ export default function Sidebar(props: Props) {
|
|||
}, []);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [recoverModalView, setRecoveryModalView] = useState(false);
|
||||
const [twoFactorModal, setTwoFactorModal] = useState(true);
|
||||
useEffect(() => {
|
||||
const main = async () => {
|
||||
if (!isOpen) {
|
||||
|
@ -214,6 +216,22 @@ export default function Sidebar(props: Props) {
|
|||
{constants.DOWNLOAD_RECOVERY_KEY}
|
||||
</LinkButton>
|
||||
</>
|
||||
<>
|
||||
<TwoFactorModal
|
||||
show={twoFactorModal}
|
||||
onHide={() => setTwoFactorModal(false)}
|
||||
somethingWentWrong={() => props.setDialogMessage({
|
||||
title: constants.TWO_FACTOR_SECRET_FETCHING_FAILED,
|
||||
close: { variant: 'danger' },
|
||||
})}
|
||||
/>
|
||||
<LinkButton
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={() => setTwoFactorModal(true)}
|
||||
>
|
||||
{constants.TWO_FACTOR_AUTHENTICATION}
|
||||
</LinkButton>
|
||||
</>
|
||||
<LinkButton
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={() => {
|
||||
|
|
85
src/components/TwoFactorModal.tsx
Normal file
85
src/components/TwoFactorModal.tsx
Normal file
|
@ -0,0 +1,85 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import constants from 'utils/strings/constants';
|
||||
import MessageDialog from './MessageDialog';
|
||||
import { DeadCenter } from 'pages/gallery';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import { setupTwoFactor, TwoFactorSector } from 'services/userService';
|
||||
import styled from 'styled-components';
|
||||
import EnteSpinner from './EnteSpinner';
|
||||
|
||||
const QRCode = styled.img`
|
||||
height:200px;
|
||||
width:200px;
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
show: boolean;
|
||||
onHide: () => void;
|
||||
somethingWentWrong: any;
|
||||
}
|
||||
|
||||
enum SetupMode {
|
||||
QR_CODE,
|
||||
MANUAL_CODE,
|
||||
}
|
||||
function TwoFactorModal({ somethingWentWrong, ...props }: Props) {
|
||||
const [setupMode, setSetupMode] = useState<SetupMode>(SetupMode.QR_CODE);
|
||||
const [twoFactorSecret, setTwoFactorSecret] = useState<TwoFactorSector>(null);
|
||||
useEffect(() => {
|
||||
if (!props.show) {
|
||||
return;
|
||||
}
|
||||
const main = async () => {
|
||||
const twoFactorSecret = await setupTwoFactor();
|
||||
setTwoFactorSecret(twoFactorSecret);
|
||||
};
|
||||
main();
|
||||
}, [props.show]);
|
||||
|
||||
return (
|
||||
<MessageDialog
|
||||
{...props}
|
||||
attributes={{
|
||||
title: constants.DOWNLOAD_RECOVERY_KEY,
|
||||
close: {
|
||||
text: constants.SAVE_LATER,
|
||||
variant: 'danger',
|
||||
},
|
||||
staticBackdrop: true,
|
||||
proceed: {
|
||||
text: constants.SAVE,
|
||||
action: () => null,
|
||||
disabled: false,
|
||||
variant: 'success',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{setupMode === SetupMode.QR_CODE ? (
|
||||
<>
|
||||
<p>{constants.TWO_FACTOR_AUTHENTICATION_QR_INSTRUCTION}</p>
|
||||
<DeadCenter>
|
||||
{!twoFactorSecret ? <EnteSpinner /> :
|
||||
<QRCode src={`data:image/png;base64,${twoFactorSecret.qrCode}`} />
|
||||
}
|
||||
<Button block variant="link" onClick={() => setSetupMode(SetupMode.MANUAL_CODE)}>
|
||||
{constants.ENTER_CODE_MANUALLY}
|
||||
</Button>
|
||||
</DeadCenter>
|
||||
</>
|
||||
) : (<>
|
||||
<p>{constants.TWO_FACTOR_AUTHENTICATION_MANUAL_CODE_INSTRUCTION}</p>
|
||||
<DeadCenter>
|
||||
<div style={{ width: '300px', height: '30px', backgroundColor: 'red', marginBottom: '10px' }}>
|
||||
</div>
|
||||
<Button block variant="link" onClick={() => setSetupMode(SetupMode.QR_CODE)}>
|
||||
{constants.ENTER_CODE_MANUALLY}
|
||||
</Button>
|
||||
</DeadCenter>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</MessageDialog>
|
||||
);
|
||||
}
|
||||
export default TwoFactorModal;
|
|
@ -161,6 +161,9 @@ const GlobalStyles = createGlobalStyle`
|
|||
background-size: 20px 20px;
|
||||
background-position: center;
|
||||
}
|
||||
.btn.focus , .btn:focus{
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn-success {
|
||||
background: #2dc262;
|
||||
border-color: #29a354;
|
||||
|
|
|
@ -34,6 +34,10 @@ export interface VerificationResponse {
|
|||
encryptedToken?: string;
|
||||
token?: string;
|
||||
}
|
||||
export interface TwoFactorSector {
|
||||
secretKey: string
|
||||
qrCode: string
|
||||
}
|
||||
|
||||
export const getOtt = (email: string) => HTTPService.get(`${ENDPOINT}/users/ott`, {
|
||||
email,
|
||||
|
@ -98,3 +102,10 @@ export const isTokenValid = async () => {
|
|||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const setupTwoFactor = async () => {
|
||||
const resp = await HTTPService.get(`${ENDPOINT}/users/two-factor/setup`, null, {
|
||||
'X-Auth-Token': getToken(),
|
||||
});
|
||||
return resp.data as TwoFactorSector;
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@ import styled from 'styled-components';
|
|||
* Global English constants.
|
||||
*/
|
||||
|
||||
const dateString = function(date) {
|
||||
const dateString = function (date) {
|
||||
return new Date(date / 1000).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
|
@ -24,7 +24,7 @@ const Logo = styled.img`
|
|||
`;
|
||||
|
||||
const englishConstants = {
|
||||
HERO_HEADER: () => <div>with <Logo src='/icon.svg' /><br/>your <Strong>memories</Strong> are</div>,
|
||||
HERO_HEADER: () => <div>with <Logo src='/icon.svg' /><br />your <Strong>memories</Strong> are</div>,
|
||||
HERO_SLIDE_1_TITLE: 'protected',
|
||||
HERO_SLIDE_1: 'end-to-end encrypted with your password, visible only to you',
|
||||
HERO_SLIDE_2_TITLE: 'synced',
|
||||
|
@ -46,7 +46,7 @@ const englishConstants = {
|
|||
VERIFY_EMAIL: 'verify email',
|
||||
EMAIL_SENT: ({ email }) => (
|
||||
<p>
|
||||
we have sent a mail to <b>{email}</b>
|
||||
we have sent a mail to <b>{email}</b>
|
||||
</p>
|
||||
),
|
||||
CHECK_INBOX: 'please check your inbox (and spam) to complete verification',
|
||||
|
@ -67,10 +67,10 @@ const englishConstants = {
|
|||
'please enter a password that we can use to encrypt your data',
|
||||
PASSPHRASE_DISCLAIMER: () => (
|
||||
<p>
|
||||
we don't store your password, so if you forget,
|
||||
we don't store your password, so if you forget,
|
||||
<strong> we will not be able to help you</strong>
|
||||
{' '}
|
||||
recover your data.
|
||||
recover your data.
|
||||
</p>
|
||||
),
|
||||
PASSPHRASE_HINT: 'password',
|
||||
|
@ -182,26 +182,26 @@ const englishConstants = {
|
|||
MESSAGE: 'message',
|
||||
INSTALL_MOBILE_APP: () => (
|
||||
<>
|
||||
install our{' '}
|
||||
install our{' '}
|
||||
<a
|
||||
href="https://play.google.com/store/apps/details?id=io.ente.photos"
|
||||
target="_blank"
|
||||
style={{ color: '#2dc262' }} rel="noreferrer"
|
||||
>
|
||||
android
|
||||
android
|
||||
</a>
|
||||
{' '}
|
||||
or
|
||||
or
|
||||
{' '}
|
||||
<a
|
||||
href="https://apps.apple.com/in/app/ente-photos/id1542026904"
|
||||
style={{ color: '#2dc262' }}
|
||||
target="_blank" rel="noreferrer"
|
||||
>
|
||||
ios app
|
||||
ios app
|
||||
{' '}
|
||||
</a>
|
||||
to automatically backup all your photos
|
||||
to automatically backup all your photos
|
||||
</>
|
||||
),
|
||||
DOWNLOAD_APP_MESSAGE: () => (
|
||||
|
@ -230,41 +230,41 @@ const englishConstants = {
|
|||
FREE_SUBSCRIPTION_INFO: (expiryTime) => (
|
||||
<>
|
||||
<p>
|
||||
you are on the <strong>free</strong>
|
||||
you are on the <strong>free</strong>
|
||||
{' '}
|
||||
plan that expires on{' '}
|
||||
plan that expires on{' '}
|
||||
{dateString(expiryTime)}
|
||||
</p>
|
||||
</>
|
||||
),
|
||||
RENEWAL_ACTIVE_SUBSCRIPTION_INFO: (expiryTime) => (
|
||||
<p>
|
||||
your subscription will renew on {dateString(expiryTime)}
|
||||
your subscription will renew on {dateString(expiryTime)}
|
||||
</p>
|
||||
),
|
||||
|
||||
RENEWAL_CANCELLED_SUBSCRIPTION_INFO: (expiryTime) => (
|
||||
<>
|
||||
<p>
|
||||
your subscription will be cancelled on {dateString(expiryTime)}
|
||||
your subscription will be cancelled on {dateString(expiryTime)}
|
||||
</p>
|
||||
</>
|
||||
),
|
||||
|
||||
USAGE_INFO: (usage, quota) => (
|
||||
<p>
|
||||
you have used {usage}
|
||||
you have used {usage}
|
||||
{' '}
|
||||
GB out of your {quota}
|
||||
GB out of your {quota}
|
||||
{' '}
|
||||
GB quota
|
||||
GB quota
|
||||
</p>
|
||||
),
|
||||
|
||||
SUBSCRIPTION_PURCHASE_SUCCESS: (expiryTime) => (
|
||||
<>
|
||||
<p>we've received your payment</p>
|
||||
your subscription is valid till{' '}
|
||||
your subscription is valid till{' '}
|
||||
<strong>{dateString(expiryTime)}</strong>
|
||||
</>
|
||||
),
|
||||
|
@ -287,8 +287,8 @@ const englishConstants = {
|
|||
CANCEL_SUBSCRIPTION_MESSAGE: () => (
|
||||
<>
|
||||
<p>
|
||||
all of your data will be deleted from our servers at the end of
|
||||
this billing period.
|
||||
all of your data will be deleted from our servers at the end of
|
||||
this billing period.
|
||||
</p>
|
||||
<p>are you sure that you want to unsubscribe?</p>
|
||||
</>
|
||||
|
@ -313,8 +313,8 @@ const englishConstants = {
|
|||
<>
|
||||
<p>are you sure you want to delete this album?</p>
|
||||
<p>
|
||||
all files that are present only in this album will be
|
||||
permanently deleted
|
||||
all files that are present only in this album will be
|
||||
permanently deleted
|
||||
</p>
|
||||
</>
|
||||
),
|
||||
|
@ -336,42 +336,42 @@ const englishConstants = {
|
|||
SEARCH_HINT: () => <span>try searching for New York, April 14, Christmas...</span>,
|
||||
TERMS_AND_CONDITIONS: () => (
|
||||
<p>
|
||||
I agree to the{' '}
|
||||
I agree to the{' '}
|
||||
<a href="https://ente.io/terms" target="_blank" rel="noreferrer">
|
||||
terms
|
||||
terms
|
||||
</a>
|
||||
{' '}
|
||||
and
|
||||
and
|
||||
{' '}
|
||||
<a href="https://ente.io/privacy" target="_blank" rel="noreferrer">
|
||||
privacy policy
|
||||
privacy policy
|
||||
</a>
|
||||
{' '}
|
||||
</p>
|
||||
),
|
||||
CONFIRM_PASSWORD_NOT_SAVED: () => (
|
||||
<p>
|
||||
i understand that if i lose my password , i may lose my data since
|
||||
my data is{' '}
|
||||
i understand that if i lose my password , i may lose my data since
|
||||
my data is{' '}
|
||||
<a href="https://ente.io/encryption" target="_blank" rel="noreferrer">
|
||||
end-to-end encrypted
|
||||
end-to-end encrypted
|
||||
</a>
|
||||
{' '}
|
||||
with ente
|
||||
with ente
|
||||
</p>
|
||||
),
|
||||
SEARCH_STATS: ({ resultCount, timeTaken }) => (
|
||||
<span>
|
||||
found <span style={{ color: '#2dc262' }}>{resultCount}</span>
|
||||
found <span style={{ color: '#2dc262' }}>{resultCount}</span>
|
||||
{' '}
|
||||
memories (
|
||||
memories (
|
||||
{' '}
|
||||
<span style={{ color: '#2dc262' }}>
|
||||
{' '}
|
||||
{timeTaken}
|
||||
</span>
|
||||
{' '}
|
||||
seconds )
|
||||
seconds )
|
||||
</span>
|
||||
),
|
||||
NOT_FILE_OWNER: 'deleting shared collection files is not allowed',
|
||||
|
@ -397,6 +397,12 @@ const englishConstants = {
|
|||
SHOW_ALL: 'show all',
|
||||
LOGIN_TO_UPLOAD_FILES: (count: number) => count === 1 ? `1 file received. login to upload` : `${count} files received. login to upload`,
|
||||
FILES_TO_BE_UPLOADED: (count: number) => count === 1 ? `1 file received. uploading in a jiffy` : `${count} files received. Uploading in a jiffy`,
|
||||
TWO_FACTOR_AUTHENTICATION: 'two-factor authentication',
|
||||
TWO_FACTOR_SECRET_FETCHING_FAILED: 'unable to get two factor authentication data, please try again ',
|
||||
TWO_FACTOR_AUTHENTICATION_QR_INSTRUCTION: 'please scan QR code below with your favorite authenticator app',
|
||||
ENTER_CODE_MANUALLY: 'enter the code manually',
|
||||
TWO_FACTOR_AUTHENTICATION_MANUAL_CODE_INSTRUCTION: 'please enter this code in your favorite authenticator app',
|
||||
|
||||
};
|
||||
|
||||
export default englishConstants;
|
||||
|
|
Loading…
Reference in a new issue