Skip to content

Commit

Permalink
Merge branch 'master' into crowdin
Browse files Browse the repository at this point in the history
  • Loading branch information
imnasnainaec authored Aug 22, 2024
2 parents f2ed4d2 + 0812f13 commit 994c8d2
Show file tree
Hide file tree
Showing 43 changed files with 292 additions and 163 deletions.
10 changes: 9 additions & 1 deletion Backend/Models/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,14 @@ public class User
[BsonElement("username")]
public string Username { get; set; }

/// <summary> Not implemented in frontend. </summary>
[BsonElement("uiLang")]
public string UILang { get; set; }

[Required]
[BsonElement("glossSuggestion")]
[BsonRepresentation(BsonType.String)]
public AutocompleteSetting GlossSuggestion { get; set; }

[Required]
[BsonElement("token")]
public string Token { get; set; }
Expand All @@ -94,6 +98,7 @@ public User()
Password = "";
Username = "";
UILang = "";
GlossSuggestion = AutocompleteSetting.On;
Token = "";
IsAdmin = false;
WorkedProjects = new();
Expand All @@ -115,6 +120,7 @@ public User Clone()
Password = Password,
Username = Username,
UILang = UILang,
GlossSuggestion = GlossSuggestion,
Token = Token,
IsAdmin = IsAdmin,
WorkedProjects = WorkedProjects.ToDictionary(kv => kv.Key, kv => kv.Value),
Expand All @@ -136,6 +142,7 @@ public bool ContentEquals(User other)
other.Password.Equals(Password, StringComparison.Ordinal) &&
other.Username.Equals(Username, StringComparison.Ordinal) &&
other.UILang.Equals(UILang, StringComparison.Ordinal) &&
other.GlossSuggestion.Equals(GlossSuggestion) &&
other.Token.Equals(Token, StringComparison.Ordinal) &&
other.IsAdmin == IsAdmin &&

Expand Down Expand Up @@ -172,6 +179,7 @@ public override int GetHashCode()
hash.Add(Password);
hash.Add(Username);
hash.Add(UILang);
hash.Add(GlossSuggestion);
hash.Add(Token);
hash.Add(IsAdmin);
return hash.ToHashCode();
Expand Down
3 changes: 2 additions & 1 deletion Backend/Repositories/UserRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,8 @@ public async Task<ResultOfUpdate> Update(string userId, User user, bool updateIs
.Set(x => x.ProjectRoles, user.ProjectRoles)
.Set(x => x.Agreement, user.Agreement)
.Set(x => x.Username, user.Username)
.Set(x => x.UILang, user.UILang);
.Set(x => x.UILang, user.UILang)
.Set(x => x.GlossSuggestion, user.GlossSuggestion);

// If .Avatar or .Token has been set to null or "",
// this prevents it from being erased in the database
Expand Down
2 changes: 1 addition & 1 deletion Backend/Services/PermissionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ public string GetUserId(HttpContext request)
/// <summary> Creates a JWT token for the given user. </summary>
public async Task<User?> MakeJwt(User user)
{
const int hoursUntilExpires = 4;
const int hoursUntilExpires = 12;
var tokenHandler = new JwtSecurityTokenHandler();
var secretKey = Environment.GetEnvironmentVariable("COMBINE_JWT_SECRET_KEY")!;
var key = Encoding.ASCII.GetBytes(secretKey);
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1167,8 +1167,9 @@ The process for configuring and deploying _TheCombine_ for production targets is
- [JS](https://www.w3schools.com/js/default.asp)
- [TS](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html)
- [Our style guide](docs/style_guide/ts_style_guide.md)
- [React](https://reactjs.org/)
- [React Hooks](https://reactjs.org/docs/hooks-intro.html)
- [React](https://react.dev/learn)
- [React Hooks](https://react.dev/reference/react/hooks)
- [MUI](https://mui.com/material-ui/getting-started/) (styled/themed components)
- [Redux concepts](https://redux.js.org/tutorials/fundamentals/part-2-concepts-data-flow)
- [Redux tutorials](https://redux.js.org/tutorials/typescript-quick-start)
- [React-i18next](https://react.i18next.com/) (text localization)
Expand Down
7 changes: 5 additions & 2 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"domain": "Domain",
"domainTitle": "Domain: {{ val1 }} ({{ val2 }})",
"glosses": "Glosses",
"pressEnter": "Press enter to save word",
"pressEnter": "Press Enter to save word",
"vernacular": "Vernacular"
},
"appBar": {
Expand Down Expand Up @@ -121,11 +121,14 @@
"banners": {
"title": "Banners",
"loginBanner": "Login Banner",
"announcementBanner": "Announcement Banner"
"announcementBanner": "Announcement Banner",
"bannerCloseButton": "Close banner"
}
},
"userSettings": {
"contact": "Contact info",
"glossSuggestion": "Gloss spelling suggestions",
"glossSuggestionHint": "In Data Entry, give spelling suggestions for the Gloss being typed.",
"phone": "Phone number",
"uiLanguage": "User-interface language",
"uiLanguageDefault": "(Default to browser language)",
Expand Down
2 changes: 1 addition & 1 deletion public/locales/pt/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"domain": "Domínio",
"domainTitle": "Domínio: {{ val1 }} ({{ val2 }})",
"glosses": "Glosas",
"pressEnter": "Pressione enter para salvar a palavra",
"pressEnter": "Pressione Enter para salvar a palavra",
"vernacular": "Vernáculo"
},
"appBar": {
Expand Down
2 changes: 1 addition & 1 deletion public/manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"short_name": "The Combine",
"name": "The Word Combine",
"name": "The Combine",
"icons": [
{
"src": "favicon.ico",
Expand Down
8 changes: 8 additions & 0 deletions src/api/models/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
* Do not edit the class manually.
*/

import { AutocompleteSetting } from "./autocomplete-setting";

/**
*
* @export
Expand Down Expand Up @@ -108,4 +110,10 @@ export interface User {
* @memberof User
*/
isAdmin: boolean;
/**
*
* @type {AutocompleteSetting}
* @memberof User
*/
glossSuggestion: AutocompleteSetting;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
useEffect,
useState,
} from "react";
import { useTranslation } from "react-i18next";

import { BannerType } from "api/models";
import { getBannerText } from "backend";
Expand All @@ -18,10 +19,20 @@ import { type StoreState } from "rootRedux/types";
import { Path } from "types/path";
import theme, { themeColors } from "types/theme";

enum AnnouncementBannerId {
ButtonClose = "announcement-banner-close-button",
}

export enum AnnouncementBannerTextId {
ButtonClose = "siteSettings.banners.bannerCloseButton",
}

export default function AnnouncementBanner(): ReactElement {
const [banner, setBanner] = useState<string>("");
const [margins, setMargins] = useState<CSSProperties>({});

const { t } = useTranslation();

// Adjust the margins depending on whether there is an AppBar.
const loc = useAppSelector(
(state: StoreState) => state.analyticsState.currentPage
Expand All @@ -48,7 +59,12 @@ export default function AnnouncementBanner(): ReactElement {

return banner ? (
<Toolbar style={{ ...margins, backgroundColor: themeColors.warning }}>
<IconButton onClick={closeBanner} size="large">
<IconButton
aria-label={t(AnnouncementBannerTextId.ButtonClose)}
id={AnnouncementBannerId.ButtonClose}
onClick={closeBanner}
size="large"
>
<Cancel />
</IconButton>
<Box sx={{ width: theme.spacing(2) }} />
Expand Down
73 changes: 73 additions & 0 deletions src/components/AnnouncementBanner/tests/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { act } from "react";
import { Provider } from "react-redux";
import createMockStore from "redux-mock-store";

import AnnouncementBanner, {
AnnouncementBannerTextId,
} from "components/AnnouncementBanner";
import { defaultState } from "rootRedux/types";

jest.mock("backend", () => ({
getBannerText: () => mockGetBannerText(),
}));

const mockBannerText = "I'm a banner!";
const mockGetBannerText = jest.fn();
const mockStore = createMockStore()(defaultState);

const renderAnnouncementBanner = async (bannerText?: string): Promise<void> => {
mockGetBannerText.mockResolvedValue(bannerText ?? "");
await act(async () => {
render(
<Provider store={mockStore}>
<AnnouncementBanner />
</Provider>
);
});
};

beforeEach(() => {
jest.clearAllMocks();
});

describe("AnnouncementBanner", () => {
it("doesn't load if no banner text", async () => {
await renderAnnouncementBanner();

// Confirm no banner by the absence of its close button
expect(
screen.queryByLabelText(AnnouncementBannerTextId.ButtonClose)
).toBeNull();
});

it("loads banner with text", async () => {
await renderAnnouncementBanner(mockBannerText);

// Confirm open banner by the presence of its close button and text
expect(
screen.queryByLabelText(AnnouncementBannerTextId.ButtonClose)
).not.toBeNull();
expect(screen.queryByText(mockBannerText)).not.toBeNull();
});

it("closes when button is clicked", async () => {
// Setup
const agent = userEvent.setup();
await renderAnnouncementBanner(mockBannerText);
expect(screen.queryByText(mockBannerText)).not.toBeNull();

// Click close button
const closeButton = screen.getByLabelText(
AnnouncementBannerTextId.ButtonClose
);
await agent.click(closeButton);

// Confirm closed
expect(
screen.queryByLabelText(AnnouncementBannerTextId.ButtonClose)
).toBeNull();
expect(screen.queryByText(mockBannerText)).toBeNull();
});
});
34 changes: 0 additions & 34 deletions src/components/App/DefaultState.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ReactElement, Suspense } from "react";
import { RouterProvider } from "react-router-dom";

import AnnouncementBanner from "components/AnnouncementBanner/AnnouncementBanner";
import AnnouncementBanner from "components/AnnouncementBanner";
import UpperRightToastContainer from "components/Toast/UpperRightToastContainer";
import router from "router/browserRouter";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { render } from "@testing-library/react";
import "jest-canvas-mock";
import { act } from "react";
import { Provider } from "react-redux";
import { act, create } from "react-test-renderer";
import configureMockStore from "redux-mock-store";
import thunk from "redux-thunk";

import { defaultState } from "components/App/DefaultState";
import App from "components/App/component";
import App from "components/App";
import { defaultState } from "rootRedux/types";

jest.mock("react-router-dom");

jest.mock("components/AnnouncementBanner/AnnouncementBanner", () => "div");

const createMockStore = configureMockStore([thunk]);
const mockStore = createMockStore(defaultState);

Expand All @@ -22,7 +21,7 @@ global.analytics = { track: jest.fn() } as any;
describe("App", () => {
it("renders without crashing", async () => {
await act(async () => {
create(
render(
<Provider store={mockStore}>
<App />
</Provider>
Expand Down
2 changes: 1 addition & 1 deletion src/components/AppBar/tests/AppBarComponent.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { MemoryRouter } from "react-router-dom";
import { act, create } from "react-test-renderer";
import configureMockStore from "redux-mock-store";

import { defaultState } from "components/App/DefaultState";
import AppBar from "components/AppBar/AppBarComponent";
import { defaultState } from "rootRedux/types";

jest.mock("backend", () => ({
isSiteAdmin: () => mockIsSiteAdmin(),
Expand Down
1 change: 1 addition & 0 deletions src/components/Buttons/IconButtonWithTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ interface IconButtonWithTooltipProps {
disabled?: boolean;
icon: ReactElement;
text?: ReactNode;
/** `textId` will only be used if `text` is null or undefined. */
textId?: string;
size?: "large" | "medium" | "small";
onClick?: MouseEventHandler<HTMLButtonElement>;
Expand Down
6 changes: 4 additions & 2 deletions src/components/Buttons/NoteButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { EditTextDialog } from "components/Dialogs";
interface NoteButtonProps {
buttonId?: string;
disabled?: boolean;
/** If `noteText` is empty and `updateNote` defined,
* the button will have default add-note hover text. */
noteText: string;
updateNote?: (newText: string) => void | Promise<void>;
}
Expand Down Expand Up @@ -34,8 +36,8 @@ export default function NoteButton(props: NoteButtonProps): ReactElement {
onClick={props.updateNote ? () => setNoteOpen(true) : undefined}
side="top"
size="small"
text={props.noteText}
textId="addWords.addNote"
text={props.noteText || undefined}
textId={props.updateNote ? "addWords.addNote" : undefined}
/>
<EditTextDialog
open={noteOpen}
Expand Down
6 changes: 5 additions & 1 deletion src/components/DataEntry/DataEntryTable/NewEntry/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,10 @@ export default function NewEntry(props: NewEntryProps): ReactElement {
const handleCloseVernDialog = (id?: string): void => {
if (id !== undefined) {
setSelectedDup(id);
} else {
// User closed the dialog without choosing a duplicate entry or new entry.
// Highlight-select the typed vernacular for easy deletion.
vernInput.current?.setSelectionRange(0, vernInput.current.value.length);
}
setVernOpen(false);
};
Expand Down Expand Up @@ -332,7 +336,7 @@ function EnterGrid(): ReactElement {
const { t } = useTranslation();
return (
<Grid item xs={12} style={{ paddingLeft: theme.spacing(2) }}>
<Typography variant="caption">{t("addWords.pressEnter")}</Typography>
<Typography variant="body2">{t("addWords.pressEnter")}</Typography>
</Grid>
);
}
Loading

0 comments on commit 994c8d2

Please sign in to comment.