Skip to content

Commit

Permalink
Add email verification reminder page for verification of email (#379)
Browse files Browse the repository at this point in the history
* feat: add stub email verification reminder page
  • Loading branch information
KabinKhandThakuri authored Nov 7, 2024
1 parent 2136356 commit f0269e6
Show file tree
Hide file tree
Showing 14 changed files with 191 additions and 4 deletions.
5 changes: 4 additions & 1 deletion packages/vue-user/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
"main": "dist//DzangolabVue3User.umd.js",
"module": "dist//DzangolabVue3User.es.js",
"types": "./dist/src/index.d.ts",
"files": ["dist"],
"files": [
"dist"
],
"scripts": {
"build": "vite build && vue-tsc --emitDeclarationOnly",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts",
Expand Down Expand Up @@ -48,6 +50,7 @@
"axios": "1.3.4",
"eslint": "8.57.1",
"eslint-config-custom": "0.9.0",
"mitt": "3.0.1",
"pinia": "2.0.32",
"prettier": "3.3.3",
"typescript": "5.5.4",
Expand Down
4 changes: 4 additions & 0 deletions packages/vue-user/src/assets/css/auth.css
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@
margin: 0.5rem 0;
width: 100%;
}

.auth.verify-email-reminder {
max-width: var(--user-auth-max-width, 30rem);
}
10 changes: 9 additions & 1 deletion packages/vue-user/src/constant.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
const SUPERTOKENS_API_BASE_PATH_DEFAULT = "/auth";

export { SUPERTOKENS_API_BASE_PATH_DEFAULT };
enum EMAIL_VERIFICATION {
EMAIL_ALREADY_VERIFIED = "EMAIL_ALREADY_VERIFIED",
ERROR = "ERROR",
OK = "OK",
EMAIL_ALREADY_VERIFIED_ERROR = "EMAIL_ALREADY_VERIFIED_ERROR",
EMAIL_VERIFICATION_INVALID_TOKEN_ERROR = "EMAIL_VERIFICATION_INVALID_TOKEN_ERROR",
}

export { EMAIL_VERIFICATION, SUPERTOKENS_API_BASE_PATH_DEFAULT };
11 changes: 10 additions & 1 deletion packages/vue-user/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { prependMessages } from "@dzangolab/vue3-i18n";
import mitt from "mitt";
import { inject } from "vue";

import messages from "./locales/messages.json";
Expand All @@ -14,6 +15,8 @@ const __dzangolabVueUserTranslations = Symbol.for(
"dzangolab.vue-user.translations",
);

const emitter = mitt();

const plugin: Plugin = {
install: (app: App, options: DzangolabVueUserPluginOptions): void => {
updateRouter(options.router, options.config?.user?.routes);
Expand All @@ -25,6 +28,12 @@ const plugin: Plugin = {
: messages;

app.provide(__dzangolabVueUserTranslations, translations);

if (options && options.notification) {
emitter.on("notify", (message: object | string | unknown) => {
options.notification?.(message);
});
}
},
};

Expand All @@ -37,7 +46,7 @@ const useTranslations = () => {

export default plugin;

export { userStore, useTranslations };
export { userStore, useTranslations, emitter };

export * from "./components";

Expand Down
28 changes: 28 additions & 0 deletions packages/vue-user/src/locales/messages.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
{
"en": {
"user": {
"emailVerification": {
"button": {
"label": "Resend email"
},
"messages": {
"resend": {
"alreadyVerified": "Email is already verified. Please try refreshing the page.",
"error": "Something went wrong. Please try again",
"success": "Email resent successfully."
},
"verifyEmail": "Please check your email for verification"
},
"title": "Email Verification"
},
"loading": "Loading...",
"login": {
"errors": {
Expand Down Expand Up @@ -144,6 +158,20 @@
},
"fr": {
"user": {
"emailVerification": {
"button": {
"label": "Resend email"
},
"messages": {
"resend": {
"alreadyVerified": "Email is already verified. Please try refreshing the page.",
"error": "Something went wrong. Please try again",
"success": "Email resent successfully"
},
"verifyEmail": "Please check your email for verification"
},
"title": "Email Verification"
},
"loading": "Loading...[fr]",
"login": {
"errors": {
Expand Down
10 changes: 10 additions & 0 deletions packages/vue-user/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Router } from "vue-router";
import AuthGoogleCallback from "./components/AuthGoogleCallback.vue";
import useUserStore from "./store";
import { getUser } from "./supertokens";
import VerifyEmailReminder from "./views/EmailVerificationReminder.vue";
import Login from "./views/Login.vue";
import PasswordReset from "./views/PasswordReset.vue";
import PasswordResetRequest from "./views/PasswordResetRequest.vue";
Expand Down Expand Up @@ -53,6 +54,11 @@ const _routes = {
name: "resetPasswordRequestAcknowledge",
path: "/reset-password-request-acknowledge",
},
verifyEmailReminder: {
component: VerifyEmailReminder,
name: "verifyEmailReminder",
path: "/verify-email-reminder",
},
};

const getRoute = (
Expand Down Expand Up @@ -88,6 +94,10 @@ const addRoutes = (router: Router, routes?: RouteOverrides) => {
routes?.passwordResetRequestAcknowledge,
),
);

router.addRoute(
getRoute(_routes.verifyEmailReminder, routes?.verifyEmailReminder),
);
};

const redirectRoutes = (router: Router) => {
Expand Down
17 changes: 16 additions & 1 deletion packages/vue-user/src/supertokens/index.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,42 @@
import SuperTokens from "supertokens-web-js";
import EmailVerification from "supertokens-web-js/recipe/emailverification";
import Session from "supertokens-web-js/recipe/session";
import ThirdPartyEmailPassword from "supertokens-web-js/recipe/thirdpartyemailpassword";

import googleSignIn from "./google-signin";
import login from "./login";
import logout from "./logout";
import requestPasswordReset from "./request-password-reset";
import resendVerificationEmail from "./resend-email-verification";
import resetPassword from "./reset-password";
import signup from "./signup";
import { SUPERTOKENS_API_BASE_PATH_DEFAULT } from "../constant";
import useUserStore from "../store";
import verifyEmail from "./verify-email";

import type { User } from "../types";
import type { AppConfig } from "@dzangolab/vue3-config";

const initSupertokens = (config: AppConfig) => {
useUserStore();

// eslint-disable-next-line
const recipeList: Array<any> = [
ThirdPartyEmailPassword.init(),
Session.init(),
];

if (config.user?.features?.signUp?.emailVerification) {
recipeList.push(EmailVerification.init());
}

SuperTokens.init({
appInfo: {
apiDomain: config.apiBaseUrl,
appName: config.appTitle,
apiBasePath: config.authBasePath || SUPERTOKENS_API_BASE_PATH_DEFAULT,
},
recipeList: [ThirdPartyEmailPassword.init(), Session.init()],
recipeList: recipeList,
});
};

Expand All @@ -48,6 +61,8 @@ export {
logout,
getUser,
requestPasswordReset,
resendVerificationEmail,
resetPassword,
signup,
verifyEmail,
};
13 changes: 13 additions & 0 deletions packages/vue-user/src/supertokens/resend-email-verification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { sendVerificationEmail } from "supertokens-web-js/recipe/emailverification";

type resendEmailStatus = "OK" | "EMAIL_ALREADY_VERIFIED_ERROR";

const resendVerificationEmail = async (): Promise<
resendEmailStatus | undefined
> => {
const response = await sendVerificationEmail();

return response.status;
};

export default resendVerificationEmail;
15 changes: 15 additions & 0 deletions packages/vue-user/src/supertokens/verify-email.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import EmailVerification from "supertokens-web-js/recipe/emailverification";

const verifyEmail = async () => {
const { isVerified } = await EmailVerification.isEmailVerified();

if (isVerified) {
return { status: "EMAIL_ALREADY_VERIFIED" };
} else {
const response = await EmailVerification.verifyEmail();

return { status: response.status };
}
};

export default verifyEmail;
5 changes: 5 additions & 0 deletions packages/vue-user/src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import type {
} from "@dzangolab/vue3-form";

interface DzangolabVueUserConfig {
features?: {
signUp?: {
emailVerification?: boolean;
};
};
password?: {
minLength: number;
};
Expand Down
1 change: 1 addition & 0 deletions packages/vue-user/src/types/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface DzangolabVueUserPluginOptions {
pinia: Pinia;
router: Router;
translations?: LocaleMessages<VueMessageType>;
notification?: (message: object | string | unknown) => void;
}

export type { DzangolabVueUserPluginOptions };
1 change: 1 addition & 0 deletions packages/vue-user/src/types/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface RouteOverrides {
passwordResetRequest: RouteOverride;
passwordResetRequestAcknowledge: RouteOverride;
profile?: RouteOverride;
verifyEmailReminder?: RouteOverride;
}

export type { RouteOverride, RouteOverrides };
59 changes: 59 additions & 0 deletions packages/vue-user/src/views/EmailVerificationReminder.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<template>
<Page
:title="t('user.emailVerification.title')"
class="auth verify-email-reminder"
>
<Card>
<p>{{ t("user.emailVerification.messages.verifyEmail") }}</p>

<template #footer>
<ButtonElement
:label="t('user.emailVerification.button.label')"
@click="handleResend"
/>
</template>
</Card>
</Page>
</template>

<script lang="ts">
export default {
name: "VerifyEmail",
};
</script>

<script setup lang="ts">
import { useI18n } from "@dzangolab/vue3-i18n";
import { ButtonElement, Card } from "@dzangolab/vue3-ui";
import { EMAIL_VERIFICATION } from "../constant";
import { useTranslations, emitter } from "../index";
import { resendVerificationEmail } from "../supertokens";
const messages = useTranslations();
const { t } = useI18n({ messages });
const handleResend = () => {
resendVerificationEmail()
.then((status) => {
if (status === EMAIL_VERIFICATION.OK) {
emitter.emit("notify", {
text: t("user.emailVerification.messages.resend.success"),
type: "success",
});
} else if (status === EMAIL_VERIFICATION.EMAIL_ALREADY_VERIFIED_ERROR) {
emitter.emit("notify", {
text: t("user.emailVerification.messages.resend.alreadyVerified"),
type: "info",
});
}
})
.catch(() => {
emitter.emit("notify", {
text: t("user.emailVerification.messages.resend.error"),
type: "error",
});
});
};
</script>
16 changes: 16 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f0269e6

Please sign in to comment.