Merge pull request #608 from ente-io/family-member-screen

Family member screen
This commit is contained in:
Abhinav Kumar 2022-06-22 10:04:21 +05:30 committed by GitHub
commit ae3f64745d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 170 additions and 85 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 KiB

View file

@ -0,0 +1,73 @@
import { Button, DialogContent, Typography } from '@mui/material';
import VerticallyCentered from 'components/Container';
import DialogBoxBase from 'components/DialogBox/base';
import DialogTitleWithCloseButton from 'components/DialogBox/titleWithCloseButton';
import { AppContext } from 'pages/_app';
import React, { useContext } from 'react';
import billingService from 'services/billingService';
import { getFamilyPlanAdmin } from 'utils/billing';
import constants from 'utils/strings/constants';
export function MemberSubscriptionManage({ open, userDetails, onClose }) {
const { setDialogMessage } = useContext(AppContext);
async function onLeaveFamilyClick() {
try {
await billingService.leaveFamily();
} catch (e) {
setDialogMessage({
title: constants.ERROR,
close: { variant: 'danger' },
content: constants.UNKNOWN_ERROR,
});
}
}
const confirmLeaveFamily = () =>
setDialogMessage({
title: `${constants.LEAVE_FAMILY}`,
content: constants.LEAVE_FAMILY_CONFIRM,
proceed: {
text: constants.LEAVE_FAMILY,
action: onLeaveFamilyClick,
variant: 'danger',
},
close: {
text: constants.CANCEL,
},
});
if (!userDetails) {
return <></>;
}
return (
<DialogBoxBase open={open} onClose={onClose} maxWidth="xs">
<DialogTitleWithCloseButton onClose={onClose}>
<Typography variant="h3">{constants.SUBSCRIPTION}</Typography>
<Typography color={'text.secondary'}>
{constants.FAMILY_PLAN}
</Typography>
</DialogTitleWithCloseButton>
<DialogContent>
<VerticallyCentered>
<Typography color="text.secondary ">
{constants.FAMILY_SUBSCRIPTION_INFO(
getFamilyPlanAdmin(userDetails.familyData)?.email
)}
</Typography>
<img
height="267px"
width="256px"
src="/images/family_plan_leave@3x.png"
/>
<Button
size="large"
variant="outlined"
color="danger"
onClick={confirmLeaveFamily}>
{constants.LEAVE_FAMILY}
</Button>
</VerticallyCentered>
</DialogContent>
</DialogBoxBase>
);
}

View file

@ -12,10 +12,13 @@ import {
import { SubscriptionCardContentOverlay } from './contentOverlay'; import { SubscriptionCardContentOverlay } from './contentOverlay';
interface Iprops { interface Iprops {
userDetails: UserDetails; userDetails: UserDetails;
closeSidebar: () => void; openMemberSubscriptionDialog: () => void;
} }
export default function SubscriptionCard({ userDetails }: Iprops) { export default function SubscriptionCard({
userDetails,
openMemberSubscriptionDialog,
}: Iprops) {
const { showPlanSelectorModal } = useContext(GalleryContext); const { showPlanSelectorModal } = useContext(GalleryContext);
if (!userDetails) { if (!userDetails) {
@ -30,9 +33,9 @@ export default function SubscriptionCard({ userDetails }: Iprops) {
); );
} }
const allowClick = const isMemberSubscription =
!isPartOfFamily(userDetails.familyData) || isPartOfFamily(userDetails.familyData) &&
isFamilyAdmin(userDetails.familyData); !isFamilyAdmin(userDetails.familyData);
return ( return (
<Box position="relative"> <Box position="relative">
@ -43,7 +46,13 @@ export default function SubscriptionCard({ userDetails }: Iprops) {
}} }}
src="/images/subscription-card-background.png" src="/images/subscription-card-background.png"
/> />
{allowClick && <ClickOverlay onClick={showPlanSelectorModal} />} <ClickOverlay
onClick={
isMemberSubscription
? openMemberSubscriptionDialog
: showPlanSelectorModal
}
/>
<SubscriptionCardContentOverlay <SubscriptionCardContentOverlay
hasNonAdminFamilyMembers={hasNonAdminFamilyMembers} hasNonAdminFamilyMembers={hasNonAdminFamilyMembers}
userDetails={userDetails} userDetails={userDetails}

View file

@ -15,7 +15,8 @@ export function AdminSubscriptionStatus({
<Typography <Typography
variant="body2" variant="body2"
color={'text.secondary'} color={'text.secondary'}
onClick={showPlanSelectorModal}> onClick={showPlanSelectorModal}
sx={{ cursor: 'pointer' }}>
{isSubscriptionActive(userDetails.subscription) {isSubscriptionActive(userDetails.subscription)
? isOnFreePlan(userDetails.subscription) ? isOnFreePlan(userDetails.subscription)
? constants.FREE_SUBSCRIPTION_INFO( ? constants.FREE_SUBSCRIPTION_INFO(

View file

@ -1,9 +1,8 @@
import { MemberSubscriptionStatus } from './member';
import { AdminSubscriptionStatus as AdminSubscriptionStatus } from './admin';
import { GalleryContext } from 'pages/gallery'; import { GalleryContext } from 'pages/gallery';
import React, { useContext, useMemo } from 'react'; import React, { useContext, useMemo } from 'react';
import { import {
hasNonAdminFamilyMembers, hasNonAdminFamilyMembers,
hasPaidSubscription,
isFamilyAdmin, isFamilyAdmin,
isOnFreePlan, isOnFreePlan,
isSubscriptionActive, isSubscriptionActive,
@ -11,6 +10,8 @@ import {
} from 'utils/billing'; } from 'utils/billing';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import { UserDetails } from 'types/user'; import { UserDetails } from 'types/user';
import constants from 'utils/strings/constants';
import { Typography } from '@mui/material';
export default function SubscriptionStatus({ export default function SubscriptionStatus({
userDetails, userDetails,
@ -34,14 +35,29 @@ export default function SubscriptionStatus({
return ( return (
<Box px={1}> <Box px={1}>
{!hasNonAdminFamilyMembers(userDetails.familyData) || {(!hasNonAdminFamilyMembers(userDetails.familyData) ||
isFamilyAdmin(userDetails.familyData) ? ( isFamilyAdmin(userDetails.familyData) ||
<AdminSubscriptionStatus hasPaidSubscription(userDetails.subscription)) && (
userDetails={userDetails} <Typography
showPlanSelectorModal={showPlanSelectorModal} variant="body2"
/> color={'text.secondary'}
) : ( onClick={showPlanSelectorModal}
<MemberSubscriptionStatus userDetails={userDetails} /> sx={{ cursor: 'pointer' }}>
{isSubscriptionActive(userDetails.subscription)
? isOnFreePlan(userDetails.subscription)
? constants.FREE_SUBSCRIPTION_INFO(
userDetails.subscription?.expiryTime
)
: isSubscriptionCancelled(
userDetails.subscription
) &&
constants.RENEWAL_CANCELLED_SUBSCRIPTION_INFO(
userDetails.subscription?.expiryTime
)
: constants.SUBSCRIPTION_EXPIRED_MESSAGE(
showPlanSelectorModal
)}
</Typography>
)} )}
</Box> </Box>
); );

View file

@ -1,46 +0,0 @@
import { Button, Stack, Typography } from '@mui/material';
import { AppContext } from 'pages/_app';
import React, { useContext } from 'react';
import billingService from 'services/billingService';
import { getFamilyPlanAdmin } from 'utils/billing';
import constants from 'utils/strings/constants';
export function MemberSubscriptionStatus({ userDetails }) {
const { setDialogMessage } = useContext(AppContext);
async function onLeaveFamilyClick() {
try {
await billingService.leaveFamily();
} catch (e) {
setDialogMessage({
title: constants.ERROR,
close: { variant: 'danger' },
content: constants.UNKNOWN_ERROR,
});
}
}
const confirmLeaveFamily = () =>
setDialogMessage({
title: `${constants.LEAVE_FAMILY}`,
content: constants.LEAVE_FAMILY_CONFIRM,
proceed: {
text: constants.LEAVE_FAMILY,
action: onLeaveFamilyClick,
variant: 'danger',
},
close: {
text: constants.CANCEL,
},
});
return (
<Stack spacing={1}>
<Typography variant="body2" color="text.secondary ">
{constants.FAMILY_PLAN_MANAGE_ADMIN_ONLY(
getFamilyPlanAdmin(userDetails.familyData)?.email
)}
</Typography>
<Button onClick={confirmLeaveFamily}>
{constants.LEAVE_FAMILY}
</Button>
</Stack>
);
}

View file

@ -24,10 +24,7 @@ export default function Sidebar({
<DrawerSidebar open={sidebarView} onClose={closeSidebar}> <DrawerSidebar open={sidebarView} onClose={closeSidebar}>
<HeaderSection closeSidebar={closeSidebar} /> <HeaderSection closeSidebar={closeSidebar} />
<PaddedDivider spaced /> <PaddedDivider spaced />
<UserDetailsSection <UserDetailsSection sidebarView={sidebarView} />
sidebarView={sidebarView}
closeSidebar={closeSidebar}
/>
<PaddedDivider invisible /> <PaddedDivider invisible />
<ShortcutSection <ShortcutSection
closeSidebar={closeSidebar} closeSidebar={closeSidebar}

View file

@ -1,4 +1,4 @@
import React, { useEffect } from 'react'; import React, { useEffect, useState } from 'react';
import SubscriptionCard from './SubscriptionCard'; import SubscriptionCard from './SubscriptionCard';
import { getUserDetailsV2 } from 'services/userService'; import { getUserDetailsV2 } from 'services/userService';
import { UserDetails } from 'types/user'; import { UserDetails } from 'types/user';
@ -8,12 +8,21 @@ import Typography from '@mui/material/Typography';
import SubscriptionStatus from './SubscriptionStatus'; import SubscriptionStatus from './SubscriptionStatus';
import Stack from '@mui/material/Stack'; import Stack from '@mui/material/Stack';
import { Skeleton } from '@mui/material'; import { Skeleton } from '@mui/material';
import { MemberSubscriptionManage } from '../MemberSubscriptionManage';
export default function UserDetailsSection({ sidebarView, closeSidebar }) { export default function UserDetailsSection({ sidebarView }) {
const [userDetails, setUserDetails] = useLocalState<UserDetails>( const [userDetails, setUserDetails] = useLocalState<UserDetails>(
LS_KEYS.USER_DETAILS LS_KEYS.USER_DETAILS
); );
const [memberSubscriptionManageView, setMemberSubscriptionManageView] =
useState(false);
const openMemberSubscriptionManage = () =>
setMemberSubscriptionManageView(true);
const closeMemberSubscriptionManage = () =>
setMemberSubscriptionManageView(false);
useEffect(() => { useEffect(() => {
if (!sidebarView) { if (!sidebarView) {
return; return;
@ -26,6 +35,7 @@ export default function UserDetailsSection({ sidebarView, closeSidebar }) {
}, [sidebarView]); }, [sidebarView]);
return ( return (
<>
<Stack spacing={1}> <Stack spacing={1}>
<Typography px={1} color="text.secondary"> <Typography px={1} color="text.secondary">
{userDetails ? ( {userDetails ? (
@ -37,9 +47,16 @@ export default function UserDetailsSection({ sidebarView, closeSidebar }) {
<SubscriptionCard <SubscriptionCard
userDetails={userDetails} userDetails={userDetails}
closeSidebar={closeSidebar} openMemberSubscriptionDialog={openMemberSubscriptionManage}
/> />
<SubscriptionStatus userDetails={userDetails} /> <SubscriptionStatus userDetails={userDetails} />
</Stack> </Stack>
<MemberSubscriptionManage
userDetails={userDetails}
open={memberSubscriptionManageView}
onClose={closeMemberSubscriptionManage}
/>
</>
); );
} }

View file

@ -84,7 +84,6 @@ const darkThemeOptions = createTheme({
}, },
styleOverrides: { styleOverrides: {
root: { root: {
textTransform: 'none',
padding: '12px 16px', padding: '12px 16px',
borderRadius: '4px', borderRadius: '4px',
}, },
@ -171,9 +170,27 @@ const darkThemeOptions = createTheme({
display: 'block', display: 'block',
}, },
caption: { caption: {
display: 'block',
fontSize: '12px', fontSize: '12px',
lineHeight: '15px', lineHeight: '15px',
}, },
h1: {
fontSize: '36px',
lineHeight: '44px',
},
h2: {
fontSize: '30px',
lineHeight: '36px',
},
h3: {
fontSize: '24px',
lineHeight: '29px',
},
h4: {
fontSize: '18px',
lineHeight: '22px',
},
fontFamily: ['Inter', 'sans-serif'].join(','), fontFamily: ['Inter', 'sans-serif'].join(','),
}, },
}); });

View file

@ -252,6 +252,7 @@ const englishConstants = {
// ======================== // ========================
// Subscription // Subscription
// ======================== // ========================
SUBSCRIPTION: 'Subscription',
SUBSCRIBE: 'Subscribe', SUBSCRIBE: 'Subscribe',
SUBSCRIPTION_PLAN: 'Subscription plan', SUBSCRIPTION_PLAN: 'Subscription plan',
USAGE_DETAILS: 'Usage', USAGE_DETAILS: 'Usage',
@ -273,10 +274,9 @@ const englishConstants = {
</> </>
), ),
FAMILY_PLAN_MANAGE_ADMIN_ONLY: (adminEmail) => ( FAMILY_SUBSCRIPTION_INFO: (adminEmail) => (
<> <>
Only your family plan admin <strong>{adminEmail}</strong> can change You are on a family plan managed by <strong>{adminEmail}</strong>
the plan
</> </>
), ),
RENEWAL_ACTIVE_SUBSCRIPTION_INFO: (expiryTime) => ( RENEWAL_ACTIVE_SUBSCRIPTION_INFO: (expiryTime) => (
@ -777,6 +777,7 @@ const englishConstants = {
OF: 'of', OF: 'of',
MONTH_SHORT: 'mo', MONTH_SHORT: 'mo',
YEAR_SHORT: 'yr', YEAR_SHORT: 'yr',
FAMILY_PLAN: 'Family plan',
}; };
export default englishConstants; export default englishConstants;