[CORL-219] Common Tweaks (#2460)

* fix: i18n fixes

* fix: figma tweaks

* fix: tweaked date manipulation logic

* fix: linting

* fix: linting

* fix: tests
This commit is contained in:
Wyatt Johnson
2019-08-12 16:44:35 -04:00
committed by Kim Gardner
parent 7900b02c40
commit 8d78677154
16 changed files with 90 additions and 75 deletions
@@ -2,7 +2,7 @@ import cn from "classnames";
import { Localized } from "fluent-react/compat";
import React, { FunctionComponent } from "react";
import { reduceSeconds, UNIT } from "coral-framework/lib/i18n";
import { reduceSeconds, UNIT } from "coral-common/helpers/i18n";
import {
Box,
Flex,
@@ -1,7 +1,7 @@
import { Localized } from "fluent-react/compat";
import React, { FunctionComponent } from "react";
import { reduceSeconds } from "coral-framework/lib/i18n";
import { reduceSeconds } from "coral-common/helpers/i18n";
interface From {
start: Date;
@@ -3,12 +3,8 @@ import { Localized } from "fluent-react/compat";
import React, { FunctionComponent, useCallback } from "react";
import { Field, Form } from "react-final-form";
import {
GetMessage,
ScaledUnit,
withGetMessage,
} from "coral-framework/lib/i18n";
import { ScaledUnit } from "coral-common/helpers/i18n";
import { GetMessage, withGetMessage } from "coral-framework/lib/i18n";
import {
Button,
CheckBox,
@@ -1,13 +1,9 @@
import { Localized } from "fluent-react/compat";
import React, { FunctionComponent, useCallback, useState } from "react";
import {
GetMessage,
ScaledUnit,
withGetMessage,
} from "coral-framework/lib/i18n";
import NotAvailable from "coral-admin/components/NotAvailable";
import { ScaledUnit } from "coral-common/helpers/i18n";
import { GetMessage, withGetMessage } from "coral-framework/lib/i18n";
import {
Button,
Card,
@@ -1,7 +1,7 @@
import { Localized } from "fluent-react/compat";
import React, { ChangeEvent, Component } from "react";
import { UNIT } from "coral-framework/lib/i18n";
import { UNIT } from "coral-common/helpers/i18n";
import {
Flex,
Option,
@@ -3,8 +3,3 @@ export { default as negotiateLanguages } from "./negotiateLanguages";
export { BundledLocales, LoadableLocales, LocalesData } from "./locales";
export { default as getMessage } from "./getMessage";
export { default as withGetMessage, GetMessage } from "./withGetMessage";
export {
default as reduceSeconds,
UNIT,
ScaledUnit,
} from "../../../../common/helpers/i18n/reduceSeconds";
@@ -10,7 +10,7 @@ import React, {
import { Field, Form } from "react-final-form";
import { ALLOWED_USERNAME_CHANGE_FREQUENCY } from "coral-common/constants";
import reduceSeconds, { UNIT } from "coral-common/helpers/i18n/reduceSeconds";
import { reduceSeconds, UNIT } from "coral-common/helpers/i18n";
import { InvalidRequestError } from "coral-framework/lib/errors";
import { ValidationMessage } from "coral-framework/lib/form";
import {
@@ -146,11 +146,11 @@ const ChangeUsernameContainer: FunctionComponent<Props> = ({ viewer }) => {
</Box>
)}
{!showEditForm && (
<Flex alignItems="center">
<Flex alignItems="baseline">
<Typography variant="header2">{viewer.username}</Typography>
<Localized id="profile-changeUsername-edit">
<Button size="small" color="primary" onClick={toggleEditForm}>
Edit
edit
</Button>
</Localized>
</Flex>
@@ -166,13 +166,17 @@ const ChangeUsernameContainer: FunctionComponent<Props> = ({ viewer }) => {
</Localized>
<Localized
id="profile-changeUsername-desc"
strong={<strong />}
$value={FREQUENCYSCALED.scaled}
$unit={FREQUENCYSCALED.unit}
>
<Typography>
Change the username that will appear on all of your past and
future comments. Usernames can be changed once every{" "}
{FREQUENCYSCALED.scaled} {FREQUENCYSCALED.unit}
future comments.{" "}
<strong>
Usernames can be changed once every {FREQUENCYSCALED.scaled}{" "}
{FREQUENCYSCALED.unit}.
</strong>
</Typography>
</Localized>
</div>
@@ -260,10 +264,15 @@ const ChangeUsernameContainer: FunctionComponent<Props> = ({ viewer }) => {
Cancel
</Button>
</Localized>
<Localized id="profile-changeUsername-submit">
<Localized
id="profile-changeUsername-submit"
ButtonIcon={<ButtonIcon>save</ButtonIcon>}
span={<span />}
>
<Button
variant={pristine || invalid ? "outlined" : "filled"}
type="submit"
data-testid="profile-changeUsername-save"
color={pristine || invalid ? "regular" : "primary"}
disabled={pristine || invalid}
>
@@ -3,8 +3,8 @@ import React, { FunctionComponent, useCallback } from "react";
import { graphql } from "react-relay";
import { DOWNLOAD_LIMIT_TIMEFRAME } from "coral-common/constants";
import { reduceSeconds, UNIT } from "coral-common/helpers/i18n";
import { useCoralContext } from "coral-framework/lib/bootstrap";
import { reduceSeconds, UNIT } from "coral-framework/lib/i18n";
import { useMutation, withFragmentContainer } from "coral-framework/lib/relay";
import { DownloadCommentsContainer_viewer } from "coral-stream/__generated__/DownloadCommentsContainer_viewer.graphql";
import { Button, CallOut, Icon, Typography } from "coral-ui/components";
@@ -70,7 +70,7 @@ exports[`renders the empty settings pane 1`] = `
data-testid="profile-changeUsername"
>
<div
className="Box-root Flex-root Flex-flex Flex-alignCenter"
className="Box-root Flex-root Flex-flex Flex-alignBaseline"
>
<h1
className="Box-root Typography-root Typography-header2 Typography-colorTextPrimary"
@@ -87,7 +87,7 @@ exports[`renders the empty settings pane 1`] = `
onTouchEnd={[Function]}
type="button"
>
Edit
edit
</button>
</div>
</div>
@@ -69,7 +69,7 @@ describe("with recently changed username", () => {
within(testRenderer.root).queryByTestID("profile-changeUsername")
);
within(changeUsername).getByText("u_changed");
const editButton = within(changeUsername).getByText("Edit");
const editButton = within(changeUsername).getByText("edit");
act(() => {
editButton.props.onClick();
});
@@ -102,7 +102,7 @@ describe("with new username", () => {
);
within(changeUsername).getByText("u_original");
const editButton = within(changeUsername).getByText("Edit");
const editButton = within(changeUsername).getByText("edit");
act(() => {
editButton.props.onClick();
});
@@ -145,7 +145,7 @@ describe("change username form", () => {
const changeUsername = within(testRenderer.root).getByTestID(
"profile-changeUsername"
);
const editButton = within(changeUsername).getByText("Edit");
const editButton = within(changeUsername).getByText("edit");
act(() => {
editButton.props.onClick();
});
@@ -156,7 +156,9 @@ describe("change username form", () => {
within(changeUsername).getAllByText("This field is required", {
exact: false,
});
const button = within(changeUsername).getByText("Save");
const button = within(changeUsername).getByTestID(
"profile-changeUsername-save"
);
expect(button.props.disabled).toBeTruthy();
});
@@ -164,7 +166,7 @@ describe("change username form", () => {
const changeUsername = within(testRenderer.root).getByTestID(
"profile-changeUsername"
);
const editButton = within(changeUsername).getByText("Edit");
const editButton = within(changeUsername).getByText("edit");
act(() => {
editButton.props.onClick();
});
@@ -181,7 +183,9 @@ describe("change username form", () => {
within(changeUsername).getByText("Usernames do not match. Try again.", {
exact: false,
});
const button = within(changeUsername).getByText("Save");
const button = within(changeUsername).getByTestID(
"profile-changeUsername-save"
);
expect(button.props.disabled).toBeTruthy();
});
@@ -189,7 +193,7 @@ describe("change username form", () => {
const changeUsername = within(testRenderer.root).getByTestID(
"profile-changeUsername"
);
const editButton = within(changeUsername).getByText("Edit");
const editButton = within(changeUsername).getByText("edit");
act(() => {
editButton.props.onClick();
});
+3
View File
@@ -0,0 +1,3 @@
export * from "./locales";
export { default as reduceSeconds } from "./reduceSeconds";
export * from "./reduceSeconds";
+1 -1
View File
@@ -6,7 +6,7 @@ import { VError } from "verror";
import { ALLOWED_USERNAME_CHANGE_FREQUENCY } from "coral-common/constants";
import { ERROR_CODES, ERROR_TYPES } from "coral-common/errors";
import reduceSeconds, { UNIT } from "coral-common/helpers/i18n/reduceSeconds";
import { reduceSeconds, UNIT } from "coral-common/helpers/i18n";
import { translate } from "coral-server/services/i18n";
import { Writeable } from "coral-common/types";
@@ -128,7 +128,8 @@ export const Users = (ctx: TenantContext) => ({
ctx.mailerQueue,
ctx.tenant,
ctx.user!,
input.username
input.username,
ctx.now
),
updateUserUsername: async (input: GQLUpdateUserUsernameInput) =>
updateUsernameByID(
+35 -28
View File
@@ -4,6 +4,7 @@ import path from "path";
import { LanguageCode, LOCALES } from "coral-common/helpers/i18n/locales";
import config from "coral-server/config";
import logger from "coral-server/logger";
/**
* isLanguageCode will return true if the string is a `LanguageCode`.
@@ -14,8 +15,13 @@ function isLanguageCode(locale: string): locale is LanguageCode {
return LOCALES.some(code => code === locale);
}
// pathToLocales is the path where the server stores the locales.
const pathToLocales = path.join(__dirname, "..", "..", "locales");
// pathsToLocales is the paths where to find the locales.
const pathsToLocales = [
// Client locales.
path.join(__dirname, "..", "..", "..", "..", "locales"),
// Server locales.
path.join(__dirname, "..", "..", "locales"),
];
export class I18n {
private bundles: Partial<Record<LanguageCode, FluentBundle>> = {};
@@ -29,35 +35,36 @@ export class I18n {
* load will read all the translations located in the server locales folder.
*/
public async load() {
// Load all the locales from the server locales folder.
// Load all the locales from the locales folders.
const folders = await fs.readdir(pathToLocales);
for (const localesFolder of pathsToLocales) {
const folders = await fs.readdir(localesFolder);
// Load all the translation files for each of the folders.
for (const folder of folders) {
// Parse out the language code.
const locale = path.basename(folder);
if (!isLanguageCode(locale)) {
throw new Error(`invalid language code: ${locale}`);
// Load all the translation files for each of the folders.
for (const folder of folders) {
// Parse out the language code.
const locale = path.basename(folder);
if (!isLanguageCode(locale)) {
throw new Error(`invalid language code: ${locale}`);
}
// Now we have a language code.
const bundle: FluentBundle =
this.bundles[locale] || new FluentBundle(locale);
// Load all the translations in the folder.
const files = await fs.readdir(path.join(localesFolder, folder));
for (const file of files) {
const filePath = path.join(localesFolder, folder, file);
logger.debug({ locale, filePath }, "loading messages for locale");
const messages = await fs.readFile(filePath, "utf8");
bundle.addMessages(messages);
}
this.bundles[locale] = bundle;
}
// Now we have a language code.
const bundle = new FluentBundle(locale);
// Load all the translations in the folder.
const files = await fs.readdir(path.join(pathToLocales, folder));
for (const file of files) {
const messages = await fs.readFile(
path.join(pathToLocales, folder, file),
"utf8"
);
bundle.addMessages(messages);
}
this.bundles[locale] = bundle;
}
}
+10 -6
View File
@@ -382,20 +382,24 @@ export async function updateUsername(
mailer: MailerQueue,
tenant: Tenant,
user: User,
username: string
username: string,
now: Date
) {
// Validate the username.
validateUsername(username);
const lastUsernameEditAllowed = new Date();
const dateDiff =
lastUsernameEditAllowed.getSeconds() - ALLOWED_USERNAME_CHANGE_FREQUENCY;
lastUsernameEditAllowed.setDate(dateDiff);
// Get the earliest date that the username could have been edited before to/
// allow it now.
const lastUsernameEditAllowed = DateTime.fromJSDate(now)
.plus({ seconds: -ALLOWED_USERNAME_CHANGE_FREQUENCY })
.toJSDate();
const { history } = user.status.username;
if (history.length > 1) {
// If the last update was made at a date sooner than the earliest edited
// date, then we know that the last edit was conducted within the time-frame
// already.
const lastUpdate = history[history.length - 1];
if (lastUpdate.createdAt > lastUsernameEditAllowed) {
throw new UsernameUpdatedWithinWindowError(lastUpdate.createdAt);
}
+3 -3
View File
@@ -221,14 +221,14 @@ configure-configureQuery-storyNotFound = Story not found
## Change username
profile-changeUsername-success = Your username has been successfully updated
profile-changeUsername-edit = Edit
profile-changeUsername-edit = edit
profile-changeUsername-heading = Edit your username
profile-changeUsername-desc = Change the username that will appear on all of your past and future comments. Usernames can be changed once every { framework-timeago-time }.
profile-changeUsername-desc = Change the username that will appear on all of your past and future comments. <strong>Usernames can be changed once every { framework-timeago-time }.</strong>
profile-changeUsername-current = Current username
profile-changeUsername-newUsername-label = New username
profile-changeUsername-confirmNewUsername-label = Confirm new username
profile-changeUsername-cancel = Cancel
profile-changeUsername-submit = Save
profile-changeUsername-submit = <ButtonIcon>save</ButtonIcon> <span>Save</span>
profile-changeUsername-recentChange = Your username has been changed in the last { framework-timeago-time }. You may change your username again on { $nextUpdate }
profile-changeUsername-close = Close