Alternatively, you can use code below for subscription.
TOKEN
Copy and paste this text into “token” field on comments page
@@ -227,10 +236,9 @@ func NewEmail(emailParams EmailParams, smtpParams SmtpParams) (*Email, error) {
return &res, err
}
-// Send email about reply to Request.Email if it's set, otherwise do nothing and return nil, thread safe
-// do not returns sending error, only following:
-// 1. (likely impossible) template execution error from email message creation from Request
-// 2. message dropped without sending in case of closed ctx
+// Send email about comment reply to Request.Email if it's set,
+// also sends email to site administrator if appropriate option is set.
+// Thread safe
func (e *Email) Send(ctx context.Context, req Request) (err error) {
if req.Email == "" {
// this means we can't send this request via Email
@@ -252,12 +260,12 @@ func (e *Email) Send(ctx context.Context, req Request) (err error) {
}
if req.Comment.ID != "" {
- if req.parent.User.ID == req.Comment.User.ID {
+ if req.parent.User.ID == req.Comment.User.ID && !req.ForAdmin {
// don't send anything if if user replied to their own comment
return nil
}
log.Printf("[DEBUG] send notification via %s, comment id %s", e, req.Comment.ID)
- msg, err = e.buildMessageFromRequest(req)
+ msg, err = e.buildMessageFromRequest(req, req.ForAdmin)
if err != nil {
return err
}
@@ -288,33 +296,46 @@ func (e *Email) buildVerificationMessage(user, email, token, site string) (strin
}
// buildMessageFromRequest generates email message based on Request using e.MsgTemplate
-func (e *Email) buildMessageFromRequest(req Request) (string, error) {
+func (e *Email) buildMessageFromRequest(req Request, forAdmin bool) (string, error) {
subject := "New reply to your comment"
+ if forAdmin {
+ subject = "New comment to your site"
+ }
if req.Comment.PostTitle != "" {
subject += fmt.Sprintf(" for \"%s\"", req.Comment.PostTitle)
}
+
token, err := e.TokenGenFn(req.parent.User.ID, req.Email, req.Comment.Locator.SiteID)
- unsubscribeLink := e.UnsubscribeURL + "?site=" + req.Comment.Locator.SiteID + "&tkn=" + token
if err != nil {
return "", errors.Wrapf(err, "error creating token for unsubscribe link")
}
+ unsubscribeLink := e.UnsubscribeURL + "?site=" + req.Comment.Locator.SiteID + "&tkn=" + token
+ if forAdmin {
+ unsubscribeLink = ""
+ }
+
commentUrlPrefix := req.Comment.Locator.URL + uiNav
msg := bytes.Buffer{}
- err = e.msgTmpl.Execute(&msg, msgTmplData{
- UserName: req.Comment.User.Name,
- UserPicture: req.Comment.User.Picture,
- CommentText: req.Comment.Text,
- CommentLink: commentUrlPrefix + req.Comment.ID,
- CommentDate: req.Comment.Timestamp,
- ParentUserName: req.parent.User.Name,
- ParentUserPicture: req.parent.User.Picture,
- ParentCommentText: req.parent.Text,
- ParentCommentLink: commentUrlPrefix + req.parent.ID,
- ParentCommentDate: req.parent.Timestamp,
- PostTitle: req.Comment.PostTitle,
- Email: req.Email,
- UnsubscribeLink: unsubscribeLink,
- })
+ tmplData := msgTmplData{
+ UserName: req.Comment.User.Name,
+ UserPicture: req.Comment.User.Picture,
+ CommentText: req.Comment.Text,
+ CommentLink: commentUrlPrefix + req.Comment.ID,
+ CommentDate: req.Comment.Timestamp,
+ PostTitle: req.Comment.PostTitle,
+ Email: req.Email,
+ UnsubscribeLink: unsubscribeLink,
+ ForAdmin: forAdmin,
+ }
+ // in case of message to admin, parent message might be empty
+ if req.Comment.ParentID != "" {
+ tmplData.ParentUserName = req.parent.User.Name
+ tmplData.ParentUserPicture = req.parent.User.Picture
+ tmplData.ParentCommentText = req.parent.Text
+ tmplData.ParentCommentLink = commentUrlPrefix + req.parent.ID
+ tmplData.ParentCommentDate = req.parent.Timestamp
+ }
+ err = e.msgTmpl.Execute(&msg, tmplData)
if err != nil {
return "", errors.Wrapf(err, "error executing template to build comment reply message")
}
diff --git a/backend/app/notify/email_test.go b/backend/app/notify/email_test.go
index f05784a6..0f68546a 100644
--- a/backend/app/notify/email_test.go
+++ b/backend/app/notify/email_test.go
@@ -187,7 +187,7 @@ func TestEmail_Send(t *testing.T) {
email.TokenGenFn = TokenGenFn
email.UnsubscribeURL = "https://remark42.com/api/v1/email/unsubscribe"
req := Request{
- Comment: store.Comment{ID: "999", User: store.User{ID: "1", Name: "test_user"}, PostTitle: "test_title"},
+ Comment: store.Comment{ID: "999", User: store.User{ID: "1", Name: "test_user"}, ParentID: "1", PostTitle: "test_title"},
parent: store.Comment{ID: "1", User: store.User{ID: "999", Name: "parent_user"}},
Email: "test@example.org",
}
@@ -196,7 +196,7 @@ func TestEmail_Send(t *testing.T) {
assert.Equal(t, 1, fakeSmtp.readQuitCount())
assert.Equal(t, "test@example.org", fakeSmtp.readRcpt())
// test buildMessageFromRequest separately for message text
- res, err := email.buildMessageFromRequest(req)
+ res, err := email.buildMessageFromRequest(req, req.ForAdmin)
assert.NoError(t, err)
assert.Contains(t, res, `From: from@example.org
To: test@example.org
@@ -206,6 +206,23 @@ MIME-version: 1.0
Content-Type: text/html; charset="UTF-8"
List-Unsubscribe-Post: List-Unsubscribe=One-Click
List-Unsubscribe:
+Date: `)
+
+ // send email to admin without parent set
+ req = Request{
+ Comment: store.Comment{ID: "999", User: store.User{ID: "1", Name: "test_user"}, PostTitle: "test_title"},
+ Email: "admin@example.org",
+ ForAdmin: true,
+ }
+ assert.NoError(t, email.Send(context.TODO(), req))
+ res, err = email.buildMessageFromRequest(req, req.ForAdmin)
+ assert.NoError(t, err)
+ assert.Contains(t, res, `From: from@example.org
+To: admin@example.org
+Subject: New comment to your site for "test_title"
+Content-Transfer-Encoding: quoted-printable
+MIME-version: 1.0
+Content-Type: text/html; charset="UTF-8"
Date: `)
}
diff --git a/backend/app/notify/notify.go b/backend/app/notify/notify.go
index 66de0cde..d8019908 100644
--- a/backend/app/notify/notify.go
+++ b/backend/app/notify/notify.go
@@ -37,9 +37,11 @@ type Store interface {
// Request notification either about comment or about particular user verification
type Request struct {
- Comment store.Comment // if set sent notifications about new comment
- parent store.Comment // fetched only in case Comment is set
- Email string // if set (also) send email
+ Comment store.Comment // if set sent notifications about new comment
+ parent store.Comment // fetched only in case Comment is set
+ Email string // if set (also) send email
+ ForAdmin bool // if set, message supposed to be sent to administrator
+
Verification VerificationMetadata // if set sent verification notification
}
@@ -82,9 +84,13 @@ func (s *Service) Submit(req Request) {
if s.dataService != nil && req.Comment.ParentID != "" {
if p, err := s.dataService.Get(req.Comment.Locator, req.Comment.ParentID, store.User{}); err == nil {
req.parent = p
- req.Email, err = s.dataService.GetUserEmail(req.Comment.Locator.SiteID, p.User.ID)
- if err != nil {
- log.Printf("[WARN] can't read email for %s, %v", p.User.ID, err)
+ // user notification, should fetch email for it.
+ // administrator notification comes with pre-set email
+ if req.Email == "" {
+ req.Email, err = s.dataService.GetUserEmail(req.Comment.Locator.SiteID, p.User.ID)
+ if err != nil {
+ log.Printf("[WARN] can't read email for %s, %v", p.User.ID, err)
+ }
}
}
}
diff --git a/backend/app/notify/telegram.go b/backend/app/notify/telegram.go
index 0151012b..9c4a650e 100644
--- a/backend/app/notify/telegram.go
+++ b/backend/app/notify/telegram.go
@@ -90,6 +90,11 @@ func (t *Telegram) Send(ctx context.Context, req Request) error {
// verification request received, send nothing
return nil
}
+ if req.ForAdmin {
+ // request for administrator received, do nothing with it
+ // as we already sent message on request without this flag set
+ return nil
+ }
client := http.Client{Timeout: telegramTimeOut}
log.Printf("[DEBUG] send telegram notification to %s, comment id %s", t.channelID, req.Comment.ID)
diff --git a/backend/app/rest/api/rest.go b/backend/app/rest/api/rest.go
index 580d9acd..0a1703c2 100644
--- a/backend/app/rest/api/rest.go
+++ b/backend/app/rest/api/rest.go
@@ -50,6 +50,7 @@ type Rest struct {
AnonVote bool
WebRoot string
RemarkURL string
+ AdminEmail string
ReadOnlyAge int
SharedSecret string
ScoreThresholds struct {
@@ -364,6 +365,7 @@ func (s *Rest) controllerGroups() (public, private, admin, rss) {
authenticator: s.Authenticator,
notifyService: s.NotifyService,
remarkURL: s.RemarkURL,
+ adminEmail: s.AdminEmail,
anonVote: s.AnonVote,
}
diff --git a/backend/app/rest/api/rest_private.go b/backend/app/rest/api/rest_private.go
index 05e22d8e..ce15fcd4 100644
--- a/backend/app/rest/api/rest_private.go
+++ b/backend/app/rest/api/rest_private.go
@@ -39,6 +39,7 @@ type private struct {
notifyService *notify.Service
authenticator *auth.Service
remarkURL string
+ adminEmail string
anonVote bool
}
@@ -131,9 +132,14 @@ func (s *private) createCommentCtrl(w http.ResponseWriter, r *http.Request) {
s.cache.Flush(cache.Flusher(comment.Locator.SiteID).
Scopes(comment.Locator.URL, lastCommentsScope, comment.User.ID, comment.Locator.SiteID))
+ // user notification
if s.notifyService != nil {
s.notifyService.Submit(notify.Request{Comment: finalComment})
}
+ // admin notification
+ if s.notifyService != nil && s.adminEmail != "" {
+ s.notifyService.Submit(notify.Request{Comment: finalComment, Email: s.adminEmail, ForAdmin: true})
+ }
log.Printf("[DEBUG] created commend %+v", finalComment)
diff --git a/backend/app/rest/api/rest_private_test.go b/backend/app/rest/api/rest_private_test.go
index fc19eb81..d3dae176 100644
--- a/backend/app/rest/api/rest_private_test.go
+++ b/backend/app/rest/api/rest_private_test.go
@@ -596,11 +596,12 @@ func TestRest_EmailNotification(t *testing.T) {
parentComment := store.Comment{}
require.NoError(t, render.DecodeJSON(strings.NewReader(string(body)), &parentComment))
// wait for mock notification Submit to kick off
- time.Sleep(time.Millisecond * 5)
- require.Equal(t, 1, len(mockDestination.Get()))
- assert.Equal(t, "", mockDestination.Get()[0].Email)
+ time.Sleep(time.Millisecond * 30)
+ require.Equal(t, 2, len(mockDestination.Get()))
+ assert.Empty(t, mockDestination.Get()[0].Email)
+ assert.Equal(t, "admin@example.org", mockDestination.Get()[1].Email)
- // create child comment from another user, no email notification expected
+ // create child comment from another user, email notification only to admin expected
req, err = http.NewRequest("POST", ts.URL+"/api/v1/comment", strings.NewReader(fmt.Sprintf(
`{"text": "test 456",
"pid": "%s",
@@ -615,9 +616,10 @@ func TestRest_EmailNotification(t *testing.T) {
require.NoError(t, err)
require.Equal(t, http.StatusCreated, resp.StatusCode, string(body))
// wait for mock notification Submit to kick off
- time.Sleep(time.Millisecond * 5)
- require.Equal(t, 2, len(mockDestination.Get()))
- assert.Empty(t, mockDestination.Get()[1].Email)
+ time.Sleep(time.Millisecond * 30)
+ require.Equal(t, 4, len(mockDestination.Get()))
+ assert.Empty(t, mockDestination.Get()[2].Email)
+ assert.Equal(t, "admin@example.org", mockDestination.Get()[3].Email)
// send confirmation token for email
req, err = http.NewRequest(http.MethodPost, ts.URL+"/api/v1/email/subscribe?site=remark42&address=good@example.com", nil)
@@ -629,10 +631,10 @@ func TestRest_EmailNotification(t *testing.T) {
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode, string(body))
// wait for mock notification Submit to kick off
- time.Sleep(time.Millisecond * 5)
- require.Equal(t, 3, len(mockDestination.Get()))
- require.NotEmpty(t, mockDestination.Get()[2].Verification)
- verificationToken := mockDestination.Get()[2].Verification.Token
+ time.Sleep(time.Millisecond * 30)
+ require.Equal(t, 5, len(mockDestination.Get()))
+ require.NotEmpty(t, mockDestination.Get()[4].Verification)
+ verificationToken := mockDestination.Get()[4].Verification.Token
// verify email
req, err = http.NewRequest(http.MethodPost, ts.URL+fmt.Sprintf("/api/v1/email/confirm?site=remark42&tkn=%s", verificationToken), nil)
@@ -674,9 +676,9 @@ func TestRest_EmailNotification(t *testing.T) {
require.NoError(t, err)
require.Equal(t, http.StatusCreated, resp.StatusCode, string(body))
// wait for mock notification Submit to kick off
- time.Sleep(time.Millisecond * 5)
- require.Equal(t, 4, len(mockDestination.Get()))
- assert.Equal(t, "good@example.com", mockDestination.Get()[3].Email)
+ time.Sleep(time.Millisecond * 30)
+ require.Equal(t, 7, len(mockDestination.Get()))
+ assert.Equal(t, "good@example.com", mockDestination.Get()[5].Email)
// delete user's email
req, err = http.NewRequest(http.MethodDelete, ts.URL+"/api/v1/email?site=remark42", nil)
@@ -688,7 +690,7 @@ func TestRest_EmailNotification(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode, string(body))
- // create child comment from another user, no email notification expected
+ // create child comment from another user, no email notification expected except for admin
req, err = http.NewRequest("POST", ts.URL+"/api/v1/comment", strings.NewReader(
`{"text": "test 321",
"user": {"name": "other_user"},
@@ -702,9 +704,9 @@ func TestRest_EmailNotification(t *testing.T) {
require.NoError(t, err)
require.Equal(t, http.StatusCreated, resp.StatusCode, string(body))
// wait for mock notification Submit to kick off
- time.Sleep(time.Millisecond * 5)
- require.Equal(t, 5, len(mockDestination.Get()))
- assert.Empty(t, mockDestination.Get()[4].Email)
+ time.Sleep(time.Millisecond * 30)
+ require.Equal(t, 9, len(mockDestination.Get()))
+ assert.Empty(t, mockDestination.Get()[7].Email)
}
func TestRest_UserAllData(t *testing.T) {
diff --git a/backend/app/rest/api/rest_test.go b/backend/app/rest/api/rest_test.go
index b55a74e9..c8f6d84b 100644
--- a/backend/app/rest/api/rest_test.go
+++ b/backend/app/rest/api/rest_test.go
@@ -368,9 +368,10 @@ func startupT(t *testing.T) (ts *httptest.Server, srv *Rest, teardown func()) {
SecretReader: token.SecretFunc(func() (string, error) { return "secret", nil }),
AvatarStore: avatar.NewLocalFS(tmp + "/ava-remark42"),
}),
- Cache: memCache,
- WebRoot: tmp,
- RemarkURL: "https://demo.remark42.com",
+ Cache: memCache,
+ WebRoot: tmp,
+ RemarkURL: "https://demo.remark42.com",
+ AdminEmail: "admin@example.org",
ImageService: image.NewService(&image.FileSystem{
Location: tmp + "/pics-remark42",
Partitions: 100,
diff --git a/compose-dev-backend.yml b/compose-dev-backend.yml
index 87297174..55d6367d 100644
--- a/compose-dev-backend.yml
+++ b/compose-dev-backend.yml
@@ -45,6 +45,8 @@ services:
- NOTIFY_TELEGRAM_TOKEN
- NOTIFY_TELEGRAM_CHAN
- NOTIFY_EMAIL_FROM
+ - ADMIN_SHARED_EMAIL
+ - NOTIFY_EMAIL_ADMIN
- SMTP_HOST
- SMTP_USERNAME
- SMTP_PASSWORD
diff --git a/docs/README.md b/docs/README.md
index 45989f1a..d54c0310 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -4,3 +4,4 @@
- [How to configure remark42 without a subdomain](subdomain.md) with Nginx or Caddy
- [Telegram notifications](telegram.md)
- [Setup email authentication and\or email notifications](email.md)
+- [How to add new translation to remark42](translation.md)
diff --git a/docs/email.md b/docs/email.md
index bd37789b..3c4faf01 100644
--- a/docs/email.md
+++ b/docs/email.md
@@ -101,6 +101,9 @@ Here is the list of variables which affect email notifications:
NOTIFY_TYPE
NOTIFY_EMAIL_FROM
NOTIFY_EMAIL_VERIFICATION_SUBJ
+# for administrator notifications for new comments on their site
+ADMIN_SHARED_EMAIL
+NOTIFY_EMAIL_ADMIN
```
After `SMTP_` variables are set, you can allow email notifications by setting these two variables:
diff --git a/docs/translation.md b/docs/translation.md
new file mode 100644
index 00000000..0b5af48a
--- /dev/null
+++ b/docs/translation.md
@@ -0,0 +1,49 @@
+## How to add new language translation to Remark42
+
+Translation files are stored in [/frontend/app/locales](https://github.com/umputun/remark42/tree/master/frontend/app/locales)
+directory with `.json` extension and content like following:
+
+```json
+{
+ "anonymousLoginForm.length-limit": "Username must be at least 3 characters long",
+ "anonymousLoginForm.log-in": "Log in",
+ "anonymousLoginForm.symbol-limit": "Username must start from the letter and contain only latin letters, numbers, underscores, and spaces",
+<...>
+}
+```
+
+### How to add a new translation
+
+We truly appreciate people spending time contributing their translations to remark42. Please go through the steps
+below in order to have your translation start being available to all remark42 users and included in the next release.
+
+1. create a fork of [umputun/remark42](https://github.com/umputun/remark42) repo, and if you already have one please
+pull the latest changes from the upstream master branch. It could be done like that:
+ ```shell
+ git remote add upstream https://github.com/umputun/remark42.git
+ git fetch upstream
+ git rebase upstream/master
+ git push
+ ```
+1. add new locale with [two-letter code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
+of the language you want to make the translation into to list in
+[frontend/tasks/supportedLocales.json](https://github.com/umputun/remark42/blob/master/frontend/tasks/supportedLocales.json)
+1. run `npm run generate-langs` in `frontend` folder
+1. translate all values in the newly created json file in
+[frontend/app/locales/](https://github.com/umputun/remark42/blob/master/frontend/app/locales/)
+1. commit all changes above in your fork
+1. test your changes in the interface:
+ 1. uncomment `locale: "ru"` line in [frontend/index.ejs](https://github.com/umputun/remark42/blob/master/frontend/index.ejs#L133)
+ and replace `ru` with your translation language code
+ 1. [run remark42 in Docker](https://github.com/umputun/remark42#development) by issuing following commands
+ from the root directory of your remark42 fork:
+
+ ```shell
+ docker-compose -f compose-dev-frontend.yml build
+ docker-compose -f compose-dev-frontend.yml up
+ ```
+ 1. open [http://127.0.0.1:8080](http://127.0.0.1:8080), log in, make a comment, make a reply to a comment,
+ and make sure that your translation looks as you expect it to look
+ 1. make a screenshot from [http://127.0.0.1:8080](http://127.0.0.1:8080) with your translation in place
+1. after all previous steps are done, create a [Pull Request](https://github.com/umputun/remark42/pulls) to umputun/remark42
+repo with your changes, attaching a screenshot or two from your local test instance to it
\ No newline at end of file
diff --git a/frontend/Readme.md b/frontend/Readme.md
index 0d2585ef..e2cda451 100644
--- a/frontend/Readme.md
+++ b/frontend/Readme.md
@@ -28,12 +28,9 @@
- tests are running on push attempt
- example tests can be found in `./app/store/user/reducers.test.ts`, `./app/components/auth-panel/auth-panel.test.tsx`
-### how to add new locale.
+### How to add new locale
-- add new item to `./tasks/supportedLocales.json`
-- run `npm run generate-langs`
-- commit all changed files
-- translate all string in new generated dictionary `./app/locale/.json`
+Please see [this documentation](https://github.com/umputun/remark42/blob/master/docs/translation.md).
### Notes
diff --git a/frontend/app/common/api.test.ts b/frontend/app/common/api.test.ts
new file mode 100644
index 00000000..4fdcb2b5
--- /dev/null
+++ b/frontend/app/common/api.test.ts
@@ -0,0 +1,40 @@
+import jestFetchMock from 'jest-fetch-mock';
+
+import { emailVerificationForSubscribe } from './api';
+
+jest.mock('@app/common/constants', () => ({
+ BASE_URL: 'https://example.com',
+ API_BASE: '/api',
+}));
+
+jest.mock('@app/common/settings', () => ({
+ siteId: 'remark42',
+}));
+
+describe('api', () => {
+ beforeAll(() => {
+ jestFetchMock.enableMocks();
+ });
+
+ afterAll(() => {
+ jestFetchMock.disableMocks();
+ });
+
+ beforeEach(() => {
+ jestFetchMock.resetMocks();
+ });
+ it('should send request with encoded email', async () => {
+ await emailVerificationForSubscribe("address.!#$%&'*+-/=?^_`{|}~(),:;<>[\\]@example.com");
+
+ expect(jestFetchMock.mock.calls.length).toEqual(1);
+
+ const url = jestFetchMock.mock.calls[0][0] as string;
+ const match = url.match(/address=(\S+)$/);
+
+ expect(match).toBeArray();
+ expect((match as string[]).length).toBeGreaterThan(1);
+ expect((match as string[])[1]).toBe(
+ "address.!%23%24%25%26'*%2B-%2F%3D%3F%5E_%60%7B%7C%7D~()%2C%3A%3B%3C%3E%5B%5C%5D%40example.com"
+ );
+ });
+});
diff --git a/frontend/app/common/api.ts b/frontend/app/common/api.ts
index 0bdb0de0..a618cab9 100644
--- a/frontend/app/common/api.ts
+++ b/frontend/app/common/api.ts
@@ -277,7 +277,7 @@ export const uploadImage = (image: File): Promise => {
*/
export const emailVerificationForSubscribe = (emailAddress: string) =>
fetcher.post({
- url: `/email/subscribe?site=${siteId}&address=${emailAddress}`,
+ url: `/email/subscribe?site=${siteId}&address=${encodeURIComponent(emailAddress)}`,
withCredentials: true,
});
diff --git a/frontend/app/components/auth-panel/auth-panel.tsx b/frontend/app/components/auth-panel/auth-panel.tsx
index b548afaf..f030de36 100644
--- a/frontend/app/components/auth-panel/auth-panel.tsx
+++ b/frontend/app/components/auth-panel/auth-panel.tsx
@@ -133,7 +133,7 @@ export class AuthPanel extends Component {
href={`${window.location.origin}/web/comments.html${window.location.search}`}
target="_blank"
>
- new page
+
);
diff --git a/frontend/app/components/auth/__anonymous-login-form/auth__anonymous-login-form.tsx b/frontend/app/components/auth/__anonymous-login-form/auth__anonymous-login-form.tsx
index 1b6e6edd..51ec00b6 100644
--- a/frontend/app/components/auth/__anonymous-login-form/auth__anonymous-login-form.tsx
+++ b/frontend/app/components/auth/__anonymous-login-form/auth__anonymous-login-form.tsx
@@ -26,8 +26,7 @@ export const messages = defineMessages({
},
symbolLimit: {
id: 'anonymousLoginForm.symbol-limit',
- defaultMessage:
- 'Username must start from the letter and contain only latin letters, numbers, underscores, and spaces',
+ defaultMessage: 'Username must start with a letter and contain only latin letters, numbers, underscores, or spaces',
},
userName: {
id: 'anonymousLoginForm.user-name',
diff --git a/frontend/app/components/auth/__email-login-form/auth__email-login-form.test.tsx b/frontend/app/components/auth/__email-login-form/auth__email-login-form.test.tsx
index 5c4fbb7f..7172798a 100644
--- a/frontend/app/components/auth/__email-login-form/auth__email-login-form.test.tsx
+++ b/frontend/app/components/auth/__email-login-form/auth__email-login-form.test.tsx
@@ -26,7 +26,7 @@ function simulateInput(input: ReactWrapper, value: string) {
describe('EmailLoginForm', () => {
const testUser = ({} as any) as User;
- const onSuccess = jest.fn(async () => {});
+ const onSuccess = jest.fn(async () => undefined);
const onSignIn = jest.fn(async () => testUser);
beforeEach(() => {
diff --git a/frontend/app/components/comment-form/__subscribe-by-email/comment-form__subscribe-by-email.tsx b/frontend/app/components/comment-form/__subscribe-by-email/comment-form__subscribe-by-email.tsx
index 2b341f0b..d136d630 100644
--- a/frontend/app/components/comment-form/__subscribe-by-email/comment-form__subscribe-by-email.tsx
+++ b/frontend/app/components/comment-form/__subscribe-by-email/comment-form__subscribe-by-email.tsx
@@ -105,7 +105,7 @@ const renderTokenPart = (
setEmailStep: () => void
) => (