Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
- **Backend**: Formatting with golangci-lint, strict error handling
- **Frontend**: TypeScript with ESLint, Stylelint and Prettier
- **Imports**: Group stdlib, external packages, then internal packages
- **CSS**: CSS Modules (`component.module.css`) for all new and migrated components; remaining BEM components are being migrated incrementally. Class naming: BEM block = `.root`, elements = camelCase, modifiers = camelCase. Use `clsx` for conditional class composition (replaces `b()` from `bem-react-helper`)
- **CSS**: All components use CSS Modules (`component.module.css`). Class naming: BEM block = `.root`, elements = camelCase, modifiers = camelCase. Use `clsx` for conditional class composition. `raw-content.css` is the only global CSS file (syntax highlighting utility). Root wrapper keeps bare `.dark`/`.light` theme class — 8+ module CSS files depend on `:global(.dark)` ancestor. `comment_highlighting` uses `:global()` for imperative `classList` usage in root.tsx

## Key Backend Packages
- **Web/API**: `github.com/go-chi/chi/v5`, `github.com/go-pkgz/rest`
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import enMessages from 'locales/en.json';

import { SubscribeByEmail, SubscribeByEmailForm } from '.';
import { RequestError } from '../../../utils/errorUtils';
import styles from './subscribe-by-email.module.css';

const emailVerificationForSubscribeMock = emailVerificationForSubscribe as unknown as jest.Mock<
ReturnType<typeof emailVerificationForSubscribe>
Expand Down Expand Up @@ -91,7 +92,7 @@ describe('<SubscribeByEmailForm/>', () => {
it('should render email form by default', () => {
const store = mockStore(initialStore);
const wrapper = createWrapper(store);
const title = wrapper.find('.comment-form__subscribe-by-email__title');
const title = wrapper.find(`.${styles.title}`);
const button = wrapper.find(Button);

expect(title.text()).toEqual('Subscribe to replies');
Expand All @@ -103,7 +104,7 @@ describe('<SubscribeByEmailForm/>', () => {
const store = mockStore({ ...initialStore, user: { email_subscription: true } });
const wrapper = createWrapper(store);

expect(wrapper.find('.comment-form__subscribe-by-email_subscribed')).toHaveLength(1);
expect(wrapper.find(`.${styles.subscribed}`)).toHaveLength(1);
expect(wrapper.text().startsWith('You are subscribed on updates by email')).toBe(true);
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { h, FunctionComponent, Fragment } from 'preact';
import { useState, useCallback, useRef } from 'preact/hooks';
import { useSelector, useDispatch } from 'react-redux';
import b from 'bem-react-helper';
import clsx from 'clsx';
import { useIntl, defineMessages, IntlShape, FormattedMessage } from 'react-intl';

import { User } from 'common/types';
Expand All @@ -21,6 +21,8 @@ import { getPersistedEmail } from 'components/auth/auth.utils';
import { isUserAnonymous } from 'utils/isUserAnonymous';
import { isJwtExpired } from 'utils/jwt';

import styles from './subscribe-by-email.module.css';

const emailRegexp = /[^@]+@[^.]+\..+/;

enum Step {
Expand Down Expand Up @@ -77,12 +79,11 @@ const renderEmailPart = (
handleChangeEmail: (e: Event) => void
) => (
<>
<div className="comment-form__subscribe-by-email__title">
<div className={styles.title}>
<FormattedMessage id="subscribeByEmail.subscribe-to-replies" defaultMessage="Subscribe to replies" />
</div>
<Input
autofocus
className="comment-form__subscribe-by-email__input"
placeholder={intl.formatMessage(messages.email)}
value={emailAddress}
onInput={handleChangeEmail}
Expand All @@ -99,11 +100,11 @@ const renderTokenPart = (
setEmailStep: () => void
) => (
<>
<Button kind="link" mix="auth-email-login-form__back-button" {...getHandleClickProps(setEmailStep)}>
<Button kind="link" {...getHandleClickProps(setEmailStep)}>
<FormattedMessage id="subscribeByEmail.back" defaultMessage="Back" />
</Button>
<TextareaAutosize
className="comment-form__subscribe-by-email__token-input"
className={styles.tokenInput}
placeholder={intl.formatMessage(messages.token)}
autofocus
onInput={handleChangeToken}
Expand Down Expand Up @@ -246,15 +247,9 @@ export const SubscribeByEmailForm: FunctionComponent = () => {
: intl.formatMessage(messages.subscribed);

return (
<div className={b('comment-form__subscribe-by-email', { mods: { subscribed: true } })}>
<div className={styles.subscribed}>
{text}
<Button
kind="primary"
size="middle"
mix="comment-form__subscribe-by-email__button"
theme={theme}
onClick={handleUnsubscribe}
>
<Button kind="primary" size="middle" className={styles.button} theme={theme} onClick={handleUnsubscribe}>
<FormattedMessage id="subscribeByEmail.unsubscribe" defaultMessage="Unsubscribe" />
</Button>
</div>
Expand All @@ -269,15 +264,15 @@ export const SubscribeByEmailForm: FunctionComponent = () => {
*/

return (
<div className={b('comment-form__subscribe-by-email', { mods: { unsubscribed: true } })}>
<div className={styles.unsubscribed}>
<FormattedMessage
id="subscribeByEmail.have-been-unsubscribed"
defaultMessage="You have been unsubscribed by email to updates"
/>
<Button
kind="primary"
size="middle"
mix="comment-form__subscribe-by-email__button"
className={styles.button}
theme={theme}
onClick={() => setStep(Step.Close)}
>
Expand All @@ -291,22 +286,25 @@ export const SubscribeByEmailForm: FunctionComponent = () => {
step === Step.Email ? intl.formatMessage(messages.submit) : intl.formatMessage(messages.subscribe);

return (
<form className={b('comment-form__subscribe-by-email', {}, { theme })} onSubmit={handleSubmit}>
<form
className={clsx(styles.root, theme === 'dark' ? styles.themeDark : styles.themeLight)}
onSubmit={handleSubmit}
>
{step === Step.Email && renderEmailPart(loading, intl, emailAddress, handleChangeEmail)}
{step === Step.Token && renderTokenPart(loading, intl, token, handleChangeToken, setEmailStep)}
{error !== null && (
<div className="comment-form__subscribe-by-email__error" role="alert">
<div className={styles.error} role="alert">
{error}
</div>
)}
<Button
mix="comment-form__subscribe-by-email__button"
className={styles.button}
kind="primary"
size="large"
type="submit"
disabled={!isValidEmailAddress || loading}
>
{loading ? <Preloader className="comment-form__subscribe-by-email__preloader" /> : buttonLabel}
{loading ? <Preloader className={styles.preloader} /> : buttonLabel}
</Button>
</form>
);
Expand All @@ -320,13 +318,7 @@ export const SubscribeByEmail: FunctionComponent = () => {
const buttonTitle = intl.formatMessage(isAnonymous ? messages.onlyRegisteredUsers : messages.subscribeByEmail);

return (
<Dropdown
mix="comment-form__email-dropdown"
title={intl.formatMessage(messages.email)}
theme={theme}
disabled={isAnonymous}
buttonTitle={buttonTitle}
>
<Dropdown title={intl.formatMessage(messages.email)} theme={theme} disabled={isAnonymous} buttonTitle={buttonTitle}>
<SubscribeByEmailForm />
</Dropdown>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.comment-form__subscribe-by-email {
.root {
display: flex;
flex-wrap: wrap;
flex-direction: column;
Expand All @@ -9,31 +9,27 @@
text-align: left;
}

.comment-form__subscribe-by-email_token {
padding-top: 0;
}

.comment-form__subscribe-by-email_subscribed,
.comment-form__subscribe-by-email_unsubscribed {
.subscribed,
.unsubscribed {
padding: 8px 12px;
text-align: left;
font-size: 14px;
}

.comment-form__subscribe-by-email__title {
.title {
margin-bottom: 12px;
}

.comment-form__subscribe-by-email__button {
.button {
margin-top: 10px;
flex-grow: 1;
}

.comment-form__subscribe-by-email__preloader {
.preloader {
margin: 0 auto;
}

.comment-form__subscribe-by-email__token-input {
.tokenInput {
resize: vertical;
border: 1px solid var(--color31);
padding: 4px;
Expand All @@ -49,18 +45,18 @@
}
}

.comment-form__subscribe-by-email__error {
.error {
margin-top: 8px;
padding: 6px 8px;
line-height: 1.2;
}

.comment-form__subscribe-by-email_theme_dark .comment-form__subscribe-by-email__error {
.themeDark .error {
background: var(--color28);
color: var(--color27);
}

.comment-form__subscribe-by-email_theme_light .comment-form__subscribe-by-email__error {
.themeLight .error {
background: var(--color26);
color: var(--color25);
}

This file was deleted.

Loading