Skip to content

Commit

Permalink
feat: Add user login/logout layouts
Browse files Browse the repository at this point in the history
  • Loading branch information
victorgcramos authored Jan 18, 2023
1 parent a38bc5c commit cb906a1
Show file tree
Hide file tree
Showing 37 changed files with 1,107 additions and 9 deletions.
91 changes: 91 additions & 0 deletions plugins-structure/apps/politeia/cypress/e2e/userSession.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
describe("Given Login page", () => {
beforeEach(() => {
cy.visit("/user/login");
});
it("should render all elements correctly", () => {
// Form elements visible
cy.findByTestId("record-form")
.should("be.visible")
.and("include.text", "Log In");
cy.get("#email").should("be.visible");
cy.get("#pass").should("be.visible");
// Render signup and resend verification email links
cy.findByTestId("login-signup-link").should("be.visible");
cy.findByTestId("login-reset-password-link").should("be.visible");
});
it("should be able to login", () => {
cy.get("#email").type("[email protected]");
cy.get("#pass").type("password");
cy.findByTestId("login-form-button").should("be.enabled");
});
});

describe("Given Signup page", () => {
beforeEach(() => {
cy.visit("/user/signup");
});
it("should render all elements correctly", () => {
// Form elements visible
cy.findByTestId("record-form")
.should("be.visible")
.and("include.text", "Create a new account");
cy.get("#email").should("be.visible");
cy.get("#username").should("be.visible");
cy.get("#pass").should("be.visible");
cy.get("#vpass").should("be.visible");
});
it("should be able to signup", () => {
const email = "[email protected]";
cy.get("#email").type(email);
cy.get("#username").type("username");
cy.get("#pass").type("password");
cy.get("#vpass").type("password");
cy.findByTestId("signup-form-button").should("be.enabled").click();
cy.findByTestId("before-signup-modal").should("be.visible");
cy.findByTestId("modal-confirm-submit-button").click();
cy.findByTestId("modal-confirm-success-message").should(
"include.text",
email
);
});
});

describe("Given Reset Password Request page", () => {
beforeEach(() => {
cy.visit("user/password/request");
});
it("should render password reset request form correctly", () => {
// Form elements visible
cy.findByTestId("record-form")
.should("be.visible")
.and("include.text", "Request Password Reset");
cy.get("#email").should("be.visible");
cy.get("#username").should("be.visible");
});
it("should be able to reset password", () => {
const email = "[email protected]";
cy.get("#email").type(email);
cy.get("#username").type("username");
cy.findByTestId("password-reset-form-request-button").click();
cy.findByTestId("verify-email-card").should("be.visible");
});
});

describe("Given Reset Password page", () => {
beforeEach(() => {
cy.visit("/user/password/reset");
});
it("should render password reset request form correctly", () => {
// Form elements visible
cy.findByTestId("record-form")
.should("be.visible")
.and("include.text", "Reset Password");
cy.get("#pass").should("be.visible");
cy.get("#vpass").should("be.visible");
});
it("should be able to reset password", () => {
cy.get("#pass").type("password");
cy.get("#vpass").type("password");
cy.findByTestId("password-reset-form-button").click();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Politeia will send you a link to verify your email address. You must open this
link in the same browser. After verifying your email, Politeia will create your
“identity”, which consists of a public/private cryptographic key pair and
browser cookie. This is necessary to verify your account and allow submission
of proposals, commenting, voting, and other Politeia functions. After
completing the signup process, you can export your identity keys to another
browser at any time.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Your email has been successfully verified

To complete your registration and to be able to submit proposals and
comments, **you must pay a small fee in DCR**, which is used to help deter
spam in Politeia.

Please log in to find instructions on how to pay the registration fee.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Privacy Policy

The Politeia database stores your account email address, user-name,
cryptographic identity public key(s) and IP, associates this with all of your
proposals, comments and up/down votes. Your email address will be kept private
and will not be shared with any third parties. Data associating your user-name with
content contributions will be published openly, in the interests of transparency.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from "react";
import styles from "./styles.module.css";
import { Card, H2, Link, P, classNames } from "pi-ui";

function VerifyEmailCard({ email, className }) {
return (
<Card
data-testid="verify-email-card"
paddingSize="small"
className={classNames(styles.verifyMessage, className)}
>
<H2>Please check your inbox for your verification email</H2>
<P>
Note that, for privacy reasons, Politeia does not disclose whether an
email address has already been registered. If you don’t receive an
email:
</P>
<ul>
{email && <li>Check that {email} is the correct address.</li>}
<li>Check your spam folder!</li>
</ul>
<P>
If you're sure you should have received an email, join the{" "}
<Link href="https://chat.decred.org">#support:decred.org</Link> channel
on Matrix to get assistance from Politeia administrators.
</P>
</Card>
);
}

export default VerifyEmailCard;
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default as VerifyEmailCard } from "./VerifyEmailCard";
export { default as InfoCard } from "./InfoCard";
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,7 @@
.noPadding {
padding: 0;
}

.verifyMessage > *:not(:first-child) {
margin-top: var(--spacing-medium);
}
56 changes: 50 additions & 6 deletions plugins-structure/apps/politeia/src/components/Header/Header.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React from "react";
import { useSelector } from "react-redux";
import { DEFAULT_DARK_THEME_NAME } from "pi-ui";
import { useDispatch, useSelector } from "react-redux";
import { DEFAULT_DARK_THEME_NAME, Dropdown, DropdownItem } from "pi-ui";
import { Navbar, ThemeToggle, theme } from "@politeiagui/common-ui/layout";
import LogoLight from "../../public/assets/images/pi-logo-light.svg";
import LogoDark from "../../public/assets/images/pi-logo-dark.svg";
import About from "../Static/About";
import { user } from "@politeiagui/core/user";
import styles from "./styles.module.css";

function PoliteiaLogo() {
const themeName = useSelector(theme.select);
Expand All @@ -15,16 +17,58 @@ function PoliteiaLogo() {
);
}

function Header() {
return (
<Navbar logo={<PoliteiaLogo />} drawerContent={<About />}>
<ThemeToggle />
function Item({ href, name, onClick }) {
return href ? (
<a href={href} data-link>
<DropdownItem>{name}</DropdownItem>
</a>
) : (
<div>
<DropdownItem onClick={onClick}>{name}</DropdownItem>
</div>
);
}

function HeaderItems() {
const dispatch = useDispatch();
const currentUser = useSelector(user.selectCurrent);
function handleLogout() {
// TODO: Display logout modal
dispatch(user.logout());
}
return currentUser ? (
<div>
<Dropdown
title={currentUser.username}
itemsListClassName={styles.headerItems}
>
<Item name="Account" href={`/user/${currentUser.userid}`} />
<Item name="All Proposals" href="/" />
<Item
href={`/user/${currentUser.userid}/proposals`}
name="My Proposals"
/>
<Item href={`/user/${currentUser.userid}/drafts`} name="My Drafts" />
<Item name="Logout" onClick={handleLogout} />
</Dropdown>
</div>
) : (
<>
<a href="/user/login" data-link>
Log in
</a>
<a href="/user/signup" data-link>
Sign up
</a>
</>
);
}

function Header() {
return (
<Navbar logo={<PoliteiaLogo />} drawerContent={<About />}>
<ThemeToggle />
<HeaderItems />
</Navbar>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.headerItems {
padding: 0 !important;
}

.headerItems > * {
margin: 0.6rem 1rem;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from "react";
import { Modal } from "pi-ui";
import PrivacyPolicy from "../Static/PrivacyPolicy";

function PrivacyPolicyModal(props) {
return (
<Modal {...props}>
<PrivacyPolicy />
</Modal>
);
}

export default PrivacyPolicyModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from "react";
import { Card } from "pi-ui";
import { MarkdownRenderer } from "@politeiagui/common-ui";
import emailVerified from "../../assets/copies/email-verified.md";

function EmailVerified({ className }) {
return (
<Card
paddingSize="small"
className={className}
data-testid="email-verified"
>
<MarkdownRenderer body={emailVerified} />
</Card>
);
}

export default EmailVerified;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from "react";
import { Card } from "pi-ui";
import { MarkdownRenderer } from "@politeiagui/common-ui";
import policy from "../../assets/copies/privacy-policy.md";

function PrivacyPolicy() {
return (
<Card paddingSize="small" data-testid="privacy-policy">
<MarkdownRenderer body={policy} />
</Card>
);
}

export default PrivacyPolicy;
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { VerificationEmailForm } from "@politeiagui/common-ui";
import styles from "./styles.module.css";
import { user } from "@politeiagui/core/user";
import { VerifyEmailCard } from "../../../components/Card";

function UserResendVerificationEmail() {
const [email, setEmail] = useState();
const dispatch = useDispatch();
function handleResendEmail({ username, email }) {
dispatch(user.resendEmail({ email, username })).then(() => {
setEmail(email);
});
}
return (
<div className={styles.page}>
{!email ? (
<VerificationEmailForm
onSubmit={handleResendEmail}
className={styles.content}
/>
) : (
<VerifyEmailCard email={email} className={styles.content} />
)}
</div>
);
}

export default UserResendVerificationEmail;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from "react";
import { useSelector } from "react-redux";
import styles from "./styles.module.css";
import { user } from "@politeiagui/core/user";
import EmailVerified from "../../../components/Static/EmailVerified";
import { Error } from "../../../components";
import { VerifyEmailCard } from "../../../components/Card";

function UserVerifyEmail() {
const status = useSelector(user.selectStatus);
const error = useSelector(user.selectError);

return (
<div className={styles.page}>
{status === "succeeded" ? (
<EmailVerified className={styles.content} />
) : status === "failed" ? (
<Error error={error} />
) : (
<VerifyEmailCard className={styles.content} />
)}
</div>
);
}

export default UserVerifyEmail;
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from "react";
import { useSelector } from "react-redux";
import { Message } from "pi-ui";
import { user } from "@politeiagui/core/user";
import { Error } from "../../../components";
import { VerifyEmailCard } from "../../../components/Card";
import styles from "./styles.module.css";

function UserVerifyKey() {
const status = useSelector(user.selectStatus);
const error = useSelector(user.selectError);

return (
<div className={styles.page}>
{status === "succeeded" ? (
<Message kind="success">
You have verified and activated your new identity.
</Message>
) : status === "failed" ? (
<Error error={error} />
) : (
<VerifyEmailCard className={styles.content} />
)}
</div>
);
}

export default UserVerifyKey;
Loading

0 comments on commit cb906a1

Please sign in to comment.