Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/thedevs-network/kutt.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authorpoeti8 <ezzati.upt@gmail.com>2020-09-19 16:32:32 +0300
committerpoeti8 <ezzati.upt@gmail.com>2020-09-19 16:32:32 +0300
commit9e99c2ce51ad10b2c2449e8d673ea855bb52cd11 (patch)
tree118edcc1efaa3eaddf654a746a1eecda65bf9537 /client
parentf7c191fb630b00b6e243593b9209cfe2b1c215c3 (diff)
feat: add change email functionality
Diffstat (limited to 'client')
-rw-r--r--client/components/AppWrapper.tsx8
-rw-r--r--client/components/Settings/SettingsChangeEmail.tsx99
-rw-r--r--client/components/Settings/SettingsDeleteAccount.tsx2
-rw-r--r--client/components/Settings/SettingsDomain.tsx4
-rw-r--r--client/components/Settings/SettingsPassword.tsx2
-rw-r--r--client/consts/consts.ts1
-rw-r--r--client/pages/_app.tsx5
-rw-r--r--client/pages/settings.tsx5
-rw-r--r--client/pages/verify-email.tsx55
9 files changed, 173 insertions, 8 deletions
diff --git a/client/components/AppWrapper.tsx b/client/components/AppWrapper.tsx
index d7f6963..7da4c39 100644
--- a/client/components/AppWrapper.tsx
+++ b/client/components/AppWrapper.tsx
@@ -28,11 +28,15 @@ const AppWrapper = ({ children }: { children: any }) => {
const loading = useStoreState(s => s.loading.loading);
const getSettings = useStoreActions(s => s.settings.getSettings);
+ const isVerifyEmailPage =
+ typeof window !== "undefined" &&
+ window.location.pathname.includes("verify-email");
+
useEffect(() => {
- if (isAuthenticated && !fetched) {
+ if (isAuthenticated && !fetched && !isVerifyEmailPage) {
getSettings().catch(() => logout());
}
- }, []);
+ }, [isVerifyEmailPage]);
return (
<Wrapper
diff --git a/client/components/Settings/SettingsChangeEmail.tsx b/client/components/Settings/SettingsChangeEmail.tsx
new file mode 100644
index 0000000..2099257
--- /dev/null
+++ b/client/components/Settings/SettingsChangeEmail.tsx
@@ -0,0 +1,99 @@
+import { useFormState } from "react-use-form-state";
+import React, { FC, useState } from "react";
+import { Flex } from "reflexbox";
+import axios from "axios";
+
+import { getAxiosConfig } from "../../utils";
+import { useMessage } from "../../hooks";
+import { APIv2 } from "../../consts";
+import { TextInput } from "../Input";
+import Text, { H2 } from "../Text";
+import { Button } from "../Button";
+import { Col } from "../Layout";
+import Icon from "../Icon";
+
+const SettingsChangeEmail: FC = () => {
+ const [loading, setLoading] = useState(false);
+ const [message, setMessage] = useMessage(5000);
+ const [formState, { password, email, label }] = useFormState<{
+ changeemailpass: string;
+ changeemailaddress: string;
+ }>(null, {
+ withIds: true
+ });
+
+ const onSubmit = async e => {
+ e.preventDefault();
+ if (loading) return;
+ setLoading(true);
+ try {
+ const res = await axios.post(
+ APIv2.AuthChangeEmail,
+ {
+ password: formState.values.changeemailpass,
+ email: formState.values.changeemailaddress
+ },
+ getAxiosConfig()
+ );
+ setMessage(res.data.message, "green");
+ } catch (error) {
+ setMessage(error?.response?.data?.error || "Couldn't send email.");
+ }
+ setLoading(false);
+ };
+
+ return (
+ <Col alignItems="flex-start" maxWidth="100%">
+ <H2 mb={4} bold>
+ Change email address
+ </H2>
+ <Col alignItems="flex-start" onSubmit={onSubmit} width={1} as="form">
+ <Flex width={1} flexDirection={["column", "row"]}>
+ <Col mr={[0, 2]} mb={[3, 0]} flex="0 0 auto">
+ <Text
+ {...label("changeemailpass")}
+ as="label"
+ mb={[2, 3]}
+ fontSize={[15, 16]}
+ bold
+ >
+ Password:
+ </Text>
+ <TextInput
+ {...password("changeemailpass")}
+ placeholder="Password..."
+ maxWidth="240px"
+ required
+ />
+ </Col>
+ <Col ml={[0, 2]} flex="0 0 auto">
+ <Text
+ {...label("changeemailaddress")}
+ as="label"
+ mb={[2, 3]}
+ fontSize={[15, 16]}
+ bold
+ >
+ New email address:
+ </Text>
+ <TextInput
+ {...email("changeemailaddress")}
+ placeholder="john@examaple.com"
+ flex="1 1 auto"
+ maxWidth="240px"
+ />
+ </Col>
+ </Flex>
+ <Button type="submit" color="blue" mt={[24, 3]} disabled={loading}>
+ <Icon name={loading ? "spinner" : "refresh"} mr={2} stroke="white" />
+ {loading ? "Sending..." : "Update"}
+ </Button>
+ </Col>
+ <Text fontSize={15} color={message.color} mt={3}>
+ {message.text}
+ </Text>
+ </Col>
+ );
+};
+
+export default SettingsChangeEmail;
diff --git a/client/components/Settings/SettingsDeleteAccount.tsx b/client/components/Settings/SettingsDeleteAccount.tsx
index 22b7d80..c4a6a4b 100644
--- a/client/components/Settings/SettingsDeleteAccount.tsx
+++ b/client/components/Settings/SettingsDeleteAccount.tsx
@@ -65,7 +65,7 @@ const SettingsDeleteAccount: FC = () => {
fontSize={[15, 16]}
bold
>
- Password
+ Password:
</Text>
<RowCenterV as="form" onSubmit={onSubmit}>
<TextInput
diff --git a/client/components/Settings/SettingsDomain.tsx b/client/components/Settings/SettingsDomain.tsx
index 55ca837..ac4de49 100644
--- a/client/components/Settings/SettingsDomain.tsx
+++ b/client/components/Settings/SettingsDomain.tsx
@@ -135,7 +135,7 @@ const SettingsDomain: FC = () => {
fontSize={[15, 16]}
bold
>
- Domain
+ Domain:
</Text>
<TextInput
{...text("address")}
@@ -152,7 +152,7 @@ const SettingsDomain: FC = () => {
fontSize={[15, 16]}
bold
>
- Homepage (optional)
+ Homepage (optional):
</Text>
<TextInput
{...text("homepage")}
diff --git a/client/components/Settings/SettingsPassword.tsx b/client/components/Settings/SettingsPassword.tsx
index e2d7fda..281843e 100644
--- a/client/components/Settings/SettingsPassword.tsx
+++ b/client/components/Settings/SettingsPassword.tsx
@@ -55,7 +55,7 @@ const SettingsPassword: FC = () => {
fontSize={[15, 16]}
bold
>
- New password
+ New password:
</Text>
<Flex as="form" onSubmit={onSubmit}>
<TextInput
diff --git a/client/consts/consts.ts b/client/consts/consts.ts
index 75e5944..bd440ea 100644
--- a/client/consts/consts.ts
+++ b/client/consts/consts.ts
@@ -19,6 +19,7 @@ export enum APIv2 {
AuthRenew = "/api/v2/auth/renew",
AuthResetPassword = "/api/v2/auth/reset-password",
AuthChangePassword = "/api/v2/auth/change-password",
+ AuthChangeEmail = "/api/v2/auth/change-email",
AuthGenerateApikey = "/api/v2/auth/apikey",
Users = "/api/v2/users",
Domains = "/api/v2/domains",
diff --git a/client/pages/_app.tsx b/client/pages/_app.tsx
index 1f7497c..bdc39a6 100644
--- a/client/pages/_app.tsx
+++ b/client/pages/_app.tsx
@@ -45,8 +45,11 @@ class MyApp extends App<any> {
componentDidMount() {
const { loading, auth } = this.store.dispatch;
const token = cookie.get("token");
+ const isVerifyEmailPage =
+ typeof window !== "undefined" &&
+ window.location.pathname.includes("verify-email");
- if (token) {
+ if (token && !isVerifyEmailPage) {
auth.renew().catch(() => {
auth.logout();
});
diff --git a/client/pages/settings.tsx b/client/pages/settings.tsx
index aec7008..2d6bdb0 100644
--- a/client/pages/settings.tsx
+++ b/client/pages/settings.tsx
@@ -2,6 +2,7 @@ import { NextPage } from "next";
import React from "react";
import SettingsDeleteAccount from "../components/Settings/SettingsDeleteAccount";
+import SettingsChangeEmail from "../components/Settings/SettingsChangeEmail";
import SettingsPassword from "../components/Settings/SettingsPassword";
import SettingsDomain from "../components/Settings/SettingsDomain";
import SettingsApi from "../components/Settings/SettingsApi";
@@ -28,9 +29,11 @@ const SettingsPage: NextPage = () => {
<Divider mt={4} mb={48} />
<SettingsDomain />
<Divider mt={4} mb={48} />
+ <SettingsApi />
+ <Divider mt={4} mb={48} />
<SettingsPassword />
<Divider mt={4} mb={48} />
- <SettingsApi />
+ <SettingsChangeEmail />
<Divider mt={4} mb={48} />
<SettingsDeleteAccount />
</Col>
diff --git a/client/pages/verify-email.tsx b/client/pages/verify-email.tsx
new file mode 100644
index 0000000..e1a77b8
--- /dev/null
+++ b/client/pages/verify-email.tsx
@@ -0,0 +1,55 @@
+import React, { useEffect } from "react";
+import { Flex } from "reflexbox/styled-components";
+import decode from "jwt-decode";
+import { NextPage } from "next";
+import cookie from "js-cookie";
+
+import { useStoreActions } from "../store";
+import AppWrapper from "../components/AppWrapper";
+import { H2 } from "../components/Text";
+import { TokenPayload } from "../types";
+import Icon from "../components/Icon";
+import { Colors } from "../consts";
+import Footer from "../components/Footer";
+
+interface Props {
+ token?: string;
+}
+
+const VerifyEmail: NextPage<Props> = ({ token }) => {
+ const addAuth = useStoreActions(s => s.auth.add);
+
+ useEffect(() => {
+ if (token) {
+ cookie.set("token", token, { expires: 7 });
+ const decoded: TokenPayload = decode(token);
+ addAuth(decoded);
+ }
+ }, []);
+
+ return (
+ <AppWrapper>
+ <Flex flex="1 1 100%" justifyContent="center" mt={4}>
+ <Icon
+ name={token ? "check" : "x"}
+ size={26}
+ stroke={token ? Colors.CheckIcon : Colors.TrashIcon}
+ mr={3}
+ mt={1}
+ />
+ <H2 textAlign="center" normal>
+ {token
+ ? "Email address verified successfully."
+ : "Couldn't verify the email address."}
+ </H2>
+ </Flex>
+ <Footer />
+ </AppWrapper>
+ );
+};
+
+VerifyEmail.getInitialProps = async ctx => {
+ return { token: (ctx?.req as any)?.token };
+};
+
+export default VerifyEmail;