Skip to content

Commit

Permalink
refactor code according to feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
Chakravarthy7102 committed Oct 5, 2023
1 parent 207e2be commit c9e0e74
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 85 deletions.
2 changes: 1 addition & 1 deletion build/static/js/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/static/js/bundle.js.map

Large diffs are not rendered by default.

7 changes: 3 additions & 4 deletions server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import { errorHandler, middleware } from "supertokens-node/framework/express";
import Dashboard from "supertokens-node/lib/build/recipe/dashboard/recipe";
import AccountLinking from "supertokens-node/recipe/accountlinking";
import EmailPassword from "supertokens-node/recipe/emailpassword";
import EmailVerification from "supertokens-node/recipe/emailverification";
import Passwordless from "supertokens-node/recipe/passwordless";
import Session from "supertokens-node/recipe/session";
import ThirdParty from "supertokens-node/recipe/thirdparty";
Expand Down Expand Up @@ -83,9 +82,9 @@ SuperTokens.init({
],
},
}),
EmailVerification.init({
mode: "REQUIRED",
}),
// EmailVerification.init({
// mode: "REQUIRED",
// }),
Session.init(),
AccountLinking.init(),
],
Expand Down
74 changes: 39 additions & 35 deletions src/ui/components/userDetail/loginMethods/LoginMethods.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import React, { useCallback, useContext, useState } from "react";
import useUserService, { UpdateUserInformationResponse, type IUpdateUserInformationArgs } from "../../../../api/user";
import useDeleteUserService from "../../../../api/user/delete";
import useVerifyUserEmail from "../../../../api/user/email/verify";
import useVerifyUserTokenService from "../../../../api/user/email/verify/token";
import usePasswordResetService from "../../../../api/user/password/reset";
import useUnlinkService from "../../../../api/user/unlink";
import { getImageUrl } from "../../../../utils";
import { formatLongDate } from "../../../../utils/index";
import { PopupContentContext } from "../../../contexts/PopupContentContext";
import { UserRecipeType, type LoginMethod } from "../../../pages/usersList/types";
import { FEATURE_NOT_ENABLED_TEXT, UserRecipeType, type LoginMethod } from "../../../pages/usersList/types";
import CopyText from "../../copyText/CopyText";
import { LayoutPanel } from "../../layout/layoutPanel";
import TooltipContainer from "../../tooltip/tooltip";
import { useUserDetailContext } from "../context/UserDetailContext";
import {
getDeleteUserToast,
getInitlizeEmailVerificationRecipeTost,
getLoginMethodUnlinkConfirmationProps,
getSendEmailVerificationToast,
getUnlinkUserToast,
Expand Down Expand Up @@ -132,7 +134,6 @@ type MethodProps = {
updateContext: (val: LoginMethod, ind: number) => void;
index: number;
showUnlink: boolean;
isVerifyEmailFeatureEnabled: boolean;
};

const Methods: React.FC<MethodProps> = ({
Expand All @@ -145,11 +146,11 @@ const Methods: React.FC<MethodProps> = ({
updateContext,
index,
showUnlink,
isVerifyEmailFeatureEnabled,
}) => {
const { sendUserEmailVerification: sendUserEmailVerificationApi } = useVerifyUserTokenService();
const { showModal, showToast } = useContext(PopupContentContext);
const [emailError, setEmailError] = useState("");
const { getUserEmailVerificationStatus } = useVerifyUserEmail();

const trim = (val: string) => {
const len = val.length;
Expand All @@ -158,6 +159,13 @@ const Methods: React.FC<MethodProps> = ({

const sendUserEmailVerification = useCallback(
async (userId: string, tenantId: string | undefined) => {
const res = await getUserEmailVerificationStatus(userId);

if (res.status === FEATURE_NOT_ENABLED_TEXT) {
showToast(getInitlizeEmailVerificationRecipeTost());
return;
}

const isSend = await sendUserEmailVerificationApi(userId, tenantId);
showToast(getSendEmailVerificationToast(isSend));
return isSend;
Expand Down Expand Up @@ -197,6 +205,12 @@ const Methods: React.FC<MethodProps> = ({
[showModal, loginMethod.recipeUserId, changePassword]
);
const changeEmailVerificationStatus = async () => {
const res = await getUserEmailVerificationStatus(loginMethod.recipeUserId);

if (res.status === FEATURE_NOT_ENABLED_TEXT) {
showToast(getInitlizeEmailVerificationRecipeTost());
return;
}
await onUpdateEmailVerificationStatusCallback(loginMethod.recipeUserId, !loginMethod.verified, undefined);
await refetchAllData();
};
Expand Down Expand Up @@ -281,35 +295,26 @@ const Methods: React.FC<MethodProps> = ({
<div>
Is Email Verified?:&nbsp;{" "}
<div className="pill-container">
{isVerifyEmailFeatureEnabled ? (
<>
<VerifiedPill isVerified={loginMethod.verified} />
{!isEditing && !loginMethod.verified && (
<TooltipContainer
tooltip="Send verification mail"
position="right"
tooltipWidth={170}>
<SendMailIcon
className="send-mail-icon"
onClick={() =>
sendUserEmailVerification(
loginMethod.recipeUserId,
loginMethod.tenantIds[0]
)
}
/>
</TooltipContainer>
)}
{isEditing && (
<span
onClick={changeEmailVerificationStatus}
className="password-link">
{loginMethod.verified ? "Set as Unverified" : "Set as Verified"}
</span>
)}
</>
) : (
<span className="not-verified">Feature not enabled</span>
<VerifiedPill isVerified={loginMethod.verified} />
{!isEditing && !loginMethod.verified && (
<TooltipContainer
tooltip="Send verification mail"
position="right"
tooltipWidth={170}>
<SendMailIcon
className="send-mail-icon"
onClick={() =>
sendUserEmailVerification(loginMethod.recipeUserId, loginMethod.tenantIds[0])
}
/>
</TooltipContainer>
)}
{isEditing && (
<span
onClick={changeEmailVerificationStatus}
className="password-link">
{loginMethod.verified ? "Set as Unverified" : "Set as Verified"}
</span>
)}
</div>
</div>
Expand Down Expand Up @@ -378,9 +383,9 @@ const Methods: React.FC<MethodProps> = ({
);
};

type LoginMethodProps = { refetchAllData: () => Promise<void>; isVerifyEmailFeatureEnabled: boolean };
type LoginMethodProps = { refetchAllData: () => Promise<void> };

export const LoginMethods: React.FC<LoginMethodProps> = ({ refetchAllData, isVerifyEmailFeatureEnabled }) => {
export const LoginMethods: React.FC<LoginMethodProps> = ({ refetchAllData }) => {
const { userDetail, setUserDetails } = useUserDetailContext();
const { updateUserInformation } = useUserService();
const methods = userDetail.details.loginMethods;
Expand Down Expand Up @@ -449,7 +454,6 @@ export const LoginMethods: React.FC<LoginMethodProps> = ({ refetchAllData, isVer
header={<div className="title">Login Methods</div>}>
{methods.map((val, ind) => (
<Methods
isVerifyEmailFeatureEnabled={isVerifyEmailFeatureEnabled}
loginMethod={val}
onUpdateEmailVerificationStatusCallback={userDetail.func.onUpdateEmailVerificationStatusCallback}
key={ind}
Expand Down
69 changes: 25 additions & 44 deletions src/ui/components/userDetail/userDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@
import React, { useCallback, useContext, useEffect, useState } from "react";
import { Tenant } from "../../../api/tenants/list";
import { GetUserInfoResult, UpdateUserInformationResponse, useUserService } from "../../../api/user";
import useVerifyUserEmail from "../../../api/user/email/verify";
import useMetadataService from "../../../api/user/metadata";
import useSessionsForUserService from "../../../api/user/sessions";
import { getImageUrl, getRecipeNameFromid } from "../../../utils";
import { getTenantsObjectsForIds } from "../../../utils/user";
import { PopupContentContext } from "../../contexts/PopupContentContext";
import { FEATURE_NOT_ENABLED_TEXT, User, UserRecipeType } from "../../pages/usersList/types";
import { User, UserRecipeType } from "../../pages/usersList/types";
import { getMissingTenantIdModalProps } from "../common/modals/TenantIdModals";
import { OnSelectUserFunction } from "../usersListTable/UsersListTable";
import { UserDetailContextProvider } from "./context/UserDetailContext";
Expand Down Expand Up @@ -54,38 +53,18 @@ export const UserDetail: React.FC<UserDetailProps> = (props) => {
const [sessionList, setSessionList] = useState<SessionInfo[] | undefined>(undefined);
const [userMetaData, setUserMetaData] = useState<string | undefined>(undefined);
const [shouldShowLoadingOverlay, setShowLoadingOverlay] = useState<boolean>(false);
const [isVerifyEmailFeatureEnabled, setIsVerifyEmailFeatureEnabled] = useState(false);
const [isLoading, setIsLoading] = useState(false);

const { getUser, updateUserInformation } = useUserService();
const { getUserMetaData } = useMetadataService();
const { getSessionsForUser } = useSessionsForUserService();
const { showModal } = useContext(PopupContentContext);
const { getUserEmailVerificationStatus } = useVerifyUserEmail();

const loadUserDetail = useCallback(async () => {
const userDetailsResponse = await getUser(user);
const parsedUserDetails = JSON.parse(JSON.stringify(userDetailsResponse)) as GetUserInfoResult;
try {
if (parsedUserDetails && parsedUserDetails.status === "OK") {
const res = await getUserEmailVerificationStatus(parsedUserDetails.user.loginMethods[0].recipeUserId);
if (res.status === "OK") {
setIsVerifyEmailFeatureEnabled(true);
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
if (err.status === FEATURE_NOT_ENABLED_TEXT) {
setIsVerifyEmailFeatureEnabled(false);
}
} finally {
setUserDetail(parsedUserDetails);
}
setUserDetail(JSON.parse(JSON.stringify(userDetailsResponse)));
}, []);

useEffect(() => {
void loadUserDetail();
}, [loadUserDetail]);

const { showToast } = useContext(PopupContentContext);

const updateUser = useCallback(
Expand Down Expand Up @@ -162,10 +141,6 @@ export const UserDetail: React.FC<UserDetailProps> = (props) => {
}
}, []);

useEffect(() => {
void fetchUserMetaData();
}, [fetchUserMetaData]);

const fetchSession = useCallback(async () => {
let response = await getSessionsForUser(user);

Expand All @@ -176,10 +151,6 @@ export const UserDetail: React.FC<UserDetailProps> = (props) => {
setSessionList(response);
}, []);

useEffect(() => {
void fetchSession();
}, [fetchSession]);

const showLoadingOverlay = () => {
setShowLoadingOverlay(true);
};
Expand All @@ -188,7 +159,27 @@ export const UserDetail: React.FC<UserDetailProps> = (props) => {
setShowLoadingOverlay(false);
};

if (userDetail === undefined) {
const fetchData = async () => {
setIsLoading(true);
await loadUserDetail();
await fetchUserMetaData();
await fetchSession();
setIsLoading(false);
};

const refetchAllData = async () => {
setShowLoadingOverlay(true);
await loadUserDetail();
await fetchUserMetaData();
await fetchSession();
setShowLoadingOverlay(false);
};

useEffect(() => {
void fetchData();
}, []);

if (userDetail === undefined || isLoading) {
return (
<div className="user-detail-page-loader">
<div className="loader"></div>
Expand Down Expand Up @@ -224,13 +215,6 @@ export const UserDetail: React.FC<UserDetailProps> = (props) => {
);
}

const refetchAllData = async () => {
setShowLoadingOverlay(true);
await loadUserDetail();
await fetchUserMetaData();
await fetchSession();
setShowLoadingOverlay(false);
};
const userFunctions = {
refetchAllData: refetchAllData,
updateUser: updateUser,
Expand Down Expand Up @@ -270,10 +254,7 @@ export const UserDetail: React.FC<UserDetailProps> = (props) => {

<UserDetailInfoGrid {...props} />

<LoginMethods
isVerifyEmailFeatureEnabled={isVerifyEmailFeatureEnabled}
refetchAllData={refetchAllData}
/>
<LoginMethods refetchAllData={refetchAllData} />

<UserMetaDataSection />

Expand Down
8 changes: 8 additions & 0 deletions src/ui/components/userDetail/userDetailForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,14 @@ export const getSendEmailVerificationToast = (isSuccessfull: boolean) => {
} as ToastNotificationProps;
};

export const getInitlizeEmailVerificationRecipeTost = () => {
return {
iconImage: getImageUrl("form-field-error-icon.svg"),
toastType: "error",
children: <>{"EmailVerification feature is not enabled"}</>,
} as ToastNotificationProps;
};

export const getEmailVerifiedToast = (isSuccessfull: boolean) => {
return {
iconImage: getImageUrl(isSuccessfull ? "checkmark-green.svg" : "form-field-error-icon.svg"),
Expand Down

0 comments on commit c9e0e74

Please sign in to comment.