add subscription details to sidebar
This commit is contained in:
parent
fa802e253c
commit
fde43182ac
95
src/components/Sidebar/SubscriptionDetails.tsx
Normal file
95
src/components/Sidebar/SubscriptionDetails.tsx
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
CircularProgress,
|
||||||
|
LinearProgress,
|
||||||
|
Typography,
|
||||||
|
} from '@mui/material';
|
||||||
|
import { FlexWrapper, SpaceBetweenFlex } from 'components/Container';
|
||||||
|
import { UserDetails } from 'types/user';
|
||||||
|
import constants from 'utils/strings/constants';
|
||||||
|
import { formatDateShort } from 'utils/time';
|
||||||
|
import { convertBytesToHumanReadable } from 'utils/billing';
|
||||||
|
|
||||||
|
interface Iprops {
|
||||||
|
userDetails: UserDetails;
|
||||||
|
}
|
||||||
|
export default function SubscriptionDetails({ userDetails }: Iprops) {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
display="flex"
|
||||||
|
flexDirection={'column'}
|
||||||
|
height={160}
|
||||||
|
bgcolor="accent.main"
|
||||||
|
borderRadius={'8px'}
|
||||||
|
position={'relative'}>
|
||||||
|
{userDetails ? (
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
|
display="flex"
|
||||||
|
flexDirection={'column'}
|
||||||
|
padding="16px"
|
||||||
|
height="96px">
|
||||||
|
<SpaceBetweenFlex>
|
||||||
|
<Typography variant="subtitle2">
|
||||||
|
Current Plan
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
variant="subtitle2"
|
||||||
|
sx={{ color: 'text.secondary' }}>
|
||||||
|
{`${constants.ENDS} ${formatDateShort(
|
||||||
|
userDetails.subscription.expiryTime / 1000
|
||||||
|
)}`}
|
||||||
|
</Typography>
|
||||||
|
</SpaceBetweenFlex>
|
||||||
|
<Typography
|
||||||
|
sx={{ fontWeight: '700', fontSize: '24px' }}>
|
||||||
|
{convertBytesToHumanReadable(
|
||||||
|
userDetails.subscription.storage,
|
||||||
|
0
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
position={'absolute'}
|
||||||
|
right="17px"
|
||||||
|
top="10px"
|
||||||
|
component={'img'}
|
||||||
|
src="/images/locker.png"
|
||||||
|
/>
|
||||||
|
<Box
|
||||||
|
position={'relative'}
|
||||||
|
zIndex="100"
|
||||||
|
height="64px"
|
||||||
|
borderRadius={'0 0 8px 8px'}
|
||||||
|
bgcolor="accent.dark"
|
||||||
|
padding="16px">
|
||||||
|
<LinearProgress
|
||||||
|
sx={{ bgcolor: 'text.secondary' }}
|
||||||
|
variant="determinate"
|
||||||
|
value={70}
|
||||||
|
/>
|
||||||
|
<SpaceBetweenFlex style={{ marginTop: '8px' }}>
|
||||||
|
<Typography variant="caption">
|
||||||
|
{`${convertBytesToHumanReadable(
|
||||||
|
userDetails.usage,
|
||||||
|
1
|
||||||
|
)} of ${convertBytesToHumanReadable(
|
||||||
|
userDetails.subscription.storage,
|
||||||
|
0
|
||||||
|
)}`}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="caption">
|
||||||
|
{`${userDetails.fileCount} Photos`}
|
||||||
|
</Typography>
|
||||||
|
</SpaceBetweenFlex>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<FlexWrapper style={{ flex: '1', justifyContent: 'center' }}>
|
||||||
|
<CircularProgress />
|
||||||
|
</FlexWrapper>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,16 +1,9 @@
|
||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
|
import { LS_KEYS, setData } from 'utils/storage/localStorage';
|
||||||
import { getToken } from 'utils/common/key';
|
import { getToken } from 'utils/common/key';
|
||||||
import { getEndpoint } from 'utils/common/apiUtil';
|
import { getEndpoint } from 'utils/common/apiUtil';
|
||||||
import {
|
import { convertBytesToHumanReadable } from 'utils/billing';
|
||||||
isSubscriptionActive,
|
|
||||||
getUserSubscription,
|
|
||||||
isOnFreePlan,
|
|
||||||
isSubscriptionCancelled,
|
|
||||||
isSubscribed,
|
|
||||||
convertBytesToHumanReadable,
|
|
||||||
} from 'utils/billing';
|
|
||||||
|
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import { Collection } from 'types/collection';
|
import { Collection } from 'types/collection';
|
||||||
|
@ -19,24 +12,25 @@ import LinkButton from '../pages/gallery/LinkButton';
|
||||||
import { downloadApp } from 'utils/common';
|
import { downloadApp } from 'utils/common';
|
||||||
import { getUserDetails, logoutUser } from 'services/userService';
|
import { getUserDetails, logoutUser } from 'services/userService';
|
||||||
import { SetDialogMessage } from '../MessageDialog';
|
import { SetDialogMessage } from '../MessageDialog';
|
||||||
import EnteSpinner from '../EnteSpinner';
|
|
||||||
import RecoveryKeyModal from '../RecoveryKeyModal';
|
import RecoveryKeyModal from '../RecoveryKeyModal';
|
||||||
import TwoFactorModal from '../TwoFactorModal';
|
import TwoFactorModal from '../TwoFactorModal';
|
||||||
import ExportModal from '../ExportModal';
|
import ExportModal from '../ExportModal';
|
||||||
import { GalleryContext } from 'pages/gallery';
|
import { GalleryContext } from 'pages/gallery';
|
||||||
import InProgressIcon from '../icons/InProgressIcon';
|
import InProgressIcon from '../icons/InProgressIcon';
|
||||||
import exportService from 'services/exportService';
|
import exportService from 'services/exportService';
|
||||||
import { Subscription } from 'types/billing';
|
|
||||||
import { PAGES } from 'constants/pages';
|
import { PAGES } from 'constants/pages';
|
||||||
import { ARCHIVE_SECTION, TRASH_SECTION } from 'constants/collection';
|
import { ARCHIVE_SECTION, TRASH_SECTION } from 'constants/collection';
|
||||||
import FixLargeThumbnails from '../FixLargeThumbnail';
|
import FixLargeThumbnails from '../FixLargeThumbnail';
|
||||||
import { SetLoading } from 'types/gallery';
|
import { SetLoading } from 'types/gallery';
|
||||||
import { downloadAsFile } from 'utils/file';
|
import { downloadAsFile } from 'utils/file';
|
||||||
import { getUploadLogs, logUploadInfo } from 'utils/upload';
|
import { getUploadLogs, logUploadInfo } from 'utils/upload';
|
||||||
import { Box, Divider, Drawer, Typography } from '@mui/material';
|
import { Divider, Drawer, Typography } from '@mui/material';
|
||||||
import { default as MuiStyled } from '@mui/styled-engine';
|
import { default as MuiStyled } from '@mui/styled-engine';
|
||||||
import { Button } from 'react-bootstrap';
|
|
||||||
import ThemeToggler from './ThemeToggler';
|
import ThemeToggler from './ThemeToggler';
|
||||||
|
import SubscriptionDetails from './SubscriptionDetails';
|
||||||
|
import { SpaceBetweenFlex } from 'components/Container';
|
||||||
|
import { UserDetails } from 'types/user';
|
||||||
|
import { getLocalUserDetails } from 'utils/user';
|
||||||
interface Props {
|
interface Props {
|
||||||
collections: Collection[];
|
collections: Collection[];
|
||||||
setDialogMessage: SetDialogMessage;
|
setDialogMessage: SetDialogMessage;
|
||||||
|
@ -50,7 +44,7 @@ export enum THEMES {
|
||||||
|
|
||||||
const DrawerSidebar = MuiStyled(Drawer)(() => ({
|
const DrawerSidebar = MuiStyled(Drawer)(() => ({
|
||||||
'& .MuiPaper-root': {
|
'& .MuiPaper-root': {
|
||||||
width: '300px',
|
width: '320px',
|
||||||
padding: '20px',
|
padding: '20px',
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
@ -61,12 +55,9 @@ const WidderDivider = MuiStyled(Divider)(() => ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default function Sidebar(props: Props) {
|
export default function Sidebar(props: Props) {
|
||||||
const [usage, SetUsage] = useState<string>(null);
|
const [userDetails, setUserDetails] = useState<UserDetails>(null);
|
||||||
const [user, setUser] = useState(null);
|
|
||||||
const [subscription, setSubscription] = useState<Subscription>(null);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setUser(getData(LS_KEYS.USER));
|
setUserDetails(getLocalUserDetails());
|
||||||
setSubscription(getUserSubscription());
|
|
||||||
}, []);
|
}, []);
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [recoverModalView, setRecoveryModalView] = useState(false);
|
const [recoverModalView, setRecoveryModalView] = useState(false);
|
||||||
|
@ -78,18 +69,9 @@ export default function Sidebar(props: Props) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
if (!isOpen) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const userDetails = await getUserDetails();
|
const userDetails = await getUserDetails();
|
||||||
setUser({ ...user, email: userDetails.email });
|
setUserDetails(userDetails);
|
||||||
SetUsage(convertBytesToHumanReadable(userDetails.usage));
|
setData(LS_KEYS.USER_DETAILS, userDetails);
|
||||||
setSubscription(userDetails.subscription);
|
|
||||||
setData(LS_KEYS.USER, {
|
|
||||||
...getData(LS_KEYS.USER),
|
|
||||||
email: userDetails.email,
|
|
||||||
});
|
|
||||||
setData(LS_KEYS.SUBSCRIPTION, userDetails.subscription);
|
|
||||||
};
|
};
|
||||||
main();
|
main();
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
@ -138,10 +120,6 @@ export default function Sidebar(props: Props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
function onManageClick() {
|
|
||||||
setIsOpen(false);
|
|
||||||
galleryContext.showPlanSelectorModal();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DrawerSidebar
|
<DrawerSidebar
|
||||||
|
@ -154,86 +132,14 @@ export default function Sidebar(props: Props) {
|
||||||
{constants.ENTE}
|
{constants.ENTE}
|
||||||
</Typography>
|
</Typography>
|
||||||
<WidderDivider />
|
<WidderDivider />
|
||||||
<Box
|
<SpaceBetweenFlex style={{ marginBottom: '20px' }}>
|
||||||
display="flex"
|
<Typography>{userDetails?.email}</Typography>
|
||||||
mb="10px"
|
|
||||||
mt="10px"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent={'space-between'}>
|
|
||||||
<Typography>{user?.email}</Typography>
|
|
||||||
|
|
||||||
<ThemeToggler theme={theme} setTheme={setTheme} />
|
<ThemeToggler theme={theme} setTheme={setTheme} />
|
||||||
</Box>
|
</SpaceBetweenFlex>
|
||||||
<div style={{}}></div>
|
<SubscriptionDetails userDetails={userDetails} />
|
||||||
<div
|
|
||||||
style={{
|
<WidderDivider />
|
||||||
flex: 1,
|
|
||||||
overflow: 'auto',
|
|
||||||
outline: 'none',
|
|
||||||
paddingTop: '0',
|
|
||||||
}}>
|
|
||||||
<div style={{ outline: 'none' }}>
|
|
||||||
<div style={{ display: 'flex' }}>
|
|
||||||
<h5 style={{ margin: '4px 0 12px 2px' }}>
|
|
||||||
{constants.SUBSCRIPTION_PLAN}
|
|
||||||
</h5>
|
|
||||||
</div>
|
|
||||||
<div style={{ color: '#959595' }}>
|
|
||||||
{isSubscriptionActive(subscription) ? (
|
|
||||||
isOnFreePlan(subscription) ? (
|
|
||||||
constants.FREE_SUBSCRIPTION_INFO(
|
|
||||||
subscription?.expiryTime
|
|
||||||
)
|
|
||||||
) : isSubscriptionCancelled(subscription) ? (
|
|
||||||
constants.RENEWAL_CANCELLED_SUBSCRIPTION_INFO(
|
|
||||||
subscription?.expiryTime
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
constants.RENEWAL_ACTIVE_SUBSCRIPTION_INFO(
|
|
||||||
subscription?.expiryTime
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<p>{constants.SUBSCRIPTION_EXPIRED}</p>
|
|
||||||
)}
|
|
||||||
<Button
|
|
||||||
variant="outline-success"
|
|
||||||
block
|
|
||||||
size="sm"
|
|
||||||
onClick={onManageClick}>
|
|
||||||
{isSubscribed(subscription)
|
|
||||||
? constants.MANAGE
|
|
||||||
: constants.SUBSCRIBE}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ outline: 'none', marginTop: '30px' }} />
|
|
||||||
<div>
|
|
||||||
<h5 style={{ marginBottom: '12px' }}>
|
|
||||||
{constants.USAGE_DETAILS}
|
|
||||||
</h5>
|
|
||||||
<div style={{ color: '#959595' }}>
|
|
||||||
{usage ? (
|
|
||||||
constants.USAGE_INFO(
|
|
||||||
usage,
|
|
||||||
convertBytesToHumanReadable(
|
|
||||||
subscription?.storage
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<div style={{ textAlign: 'center' }}>
|
|
||||||
<EnteSpinner
|
|
||||||
style={{
|
|
||||||
borderWidth: '2px',
|
|
||||||
width: '20px',
|
|
||||||
height: '20px',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Divider />
|
|
||||||
<LinkButton
|
<LinkButton
|
||||||
style={{ marginTop: '30px' }}
|
style={{ marginTop: '30px' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -331,7 +237,9 @@ export default function Sidebar(props: Props) {
|
||||||
<ExportModal
|
<ExportModal
|
||||||
show={exportModalView}
|
show={exportModalView}
|
||||||
onHide={() => setExportModalView(false)}
|
onHide={() => setExportModalView(false)}
|
||||||
usage={usage}
|
usage={convertBytesToHumanReadable(
|
||||||
|
userDetails?.usage ?? 0
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
<LinkButton
|
<LinkButton
|
||||||
style={{ marginTop: '30px' }}
|
style={{ marginTop: '30px' }}
|
||||||
|
@ -375,9 +283,7 @@ export default function Sidebar(props: Props) {
|
||||||
proceed: {
|
proceed: {
|
||||||
text: constants.DELETE_ACCOUNT,
|
text: constants.DELETE_ACCOUNT,
|
||||||
action: () => {
|
action: () => {
|
||||||
initiateEmail(
|
initiateEmail('account-deletion@ente.io');
|
||||||
'account-deletion@ente.io'
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
variant: 'danger',
|
variant: 'danger',
|
||||||
},
|
},
|
||||||
|
@ -405,7 +311,6 @@ export default function Sidebar(props: Props) {
|
||||||
{constants.DOWNLOAD_UPLOAD_LOGS}
|
{constants.DOWNLOAD_UPLOAD_LOGS}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</DrawerSidebar>
|
</DrawerSidebar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ export enum LS_KEYS {
|
||||||
LIVE_PHOTO_INFO_SHOWN_COUNT = 'livePhotoInfoShownCount',
|
LIVE_PHOTO_INFO_SHOWN_COUNT = 'livePhotoInfoShownCount',
|
||||||
FAILED_UPLOADS = 'failedUploads',
|
FAILED_UPLOADS = 'failedUploads',
|
||||||
LOGS = 'logs',
|
LOGS = 'logs',
|
||||||
|
USER_DETAILS = 'userDetails',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setData = (key: LS_KEYS, value: object) => {
|
export const setData = (key: LS_KEYS, value: object) => {
|
||||||
|
|
|
@ -718,6 +718,7 @@ const englishConstants = {
|
||||||
ALL_ALBUMS: 'All Albums',
|
ALL_ALBUMS: 'All Albums',
|
||||||
PHOTOS: 'Photos',
|
PHOTOS: 'Photos',
|
||||||
ENTE: 'ente',
|
ENTE: 'ente',
|
||||||
|
ENDS: 'Ends',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default englishConstants;
|
export default englishConstants;
|
||||||
|
|
|
@ -15,6 +15,15 @@ export function dateStringWithMMH(unixTimeInMicroSeconds: number): string {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatDateShort(date: number | Date) {
|
||||||
|
const dateTimeFormat = new Intl.DateTimeFormat('en-IN', {
|
||||||
|
year: '2-digit',
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
});
|
||||||
|
return dateTimeFormat.format(date);
|
||||||
|
}
|
||||||
|
|
||||||
export function getUnixTimeInMicroSecondsWithDelta(delta: TimeDelta): number {
|
export function getUnixTimeInMicroSecondsWithDelta(delta: TimeDelta): number {
|
||||||
let currentDate = new Date();
|
let currentDate = new Date();
|
||||||
if (delta?.hours) {
|
if (delta?.hours) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { UserDetails } from 'types/user';
|
||||||
import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
|
import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
|
||||||
|
|
||||||
export function makeID(length) {
|
export function makeID(length) {
|
||||||
|
@ -21,3 +22,7 @@ export function getUserAnonymizedID() {
|
||||||
}
|
}
|
||||||
return anonymizeUserID;
|
return anonymizeUserID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getLocalUserDetails(): UserDetails {
|
||||||
|
return getData(LS_KEYS.USER_DETAILS);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue