diff --git a/Backend/Models/User.cs b/Backend/Models/User.cs
index 85bbdd6de7..d6407b1416 100644
--- a/Backend/Models/User.cs
+++ b/Backend/Models/User.cs
@@ -66,10 +66,14 @@ public class User
[BsonElement("username")]
public string Username { get; set; }
- /// Not implemented in frontend.
[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; }
@@ -94,6 +98,7 @@ public User()
Password = "";
Username = "";
UILang = "";
+ GlossSuggestion = AutocompleteSetting.On;
Token = "";
IsAdmin = false;
WorkedProjects = new();
@@ -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),
@@ -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 &&
@@ -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();
diff --git a/Backend/Repositories/UserRepository.cs b/Backend/Repositories/UserRepository.cs
index e576ea2966..0ab2f006bb 100644
--- a/Backend/Repositories/UserRepository.cs
+++ b/Backend/Repositories/UserRepository.cs
@@ -196,7 +196,8 @@ public async Task 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
diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json
index 71f874e7de..98ac6e39d1 100644
--- a/public/locales/en/translation.json
+++ b/public/locales/en/translation.json
@@ -126,6 +126,8 @@
},
"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)",
diff --git a/src/api/models/user.ts b/src/api/models/user.ts
index a563dd8b9c..fef3669aef 100644
--- a/src/api/models/user.ts
+++ b/src/api/models/user.ts
@@ -12,6 +12,8 @@
* Do not edit the class manually.
*/
+import { AutocompleteSetting } from "./autocomplete-setting";
+
/**
*
* @export
@@ -108,4 +110,10 @@ export interface User {
* @memberof User
*/
isAdmin: boolean;
+ /**
+ *
+ * @type {AutocompleteSetting}
+ * @memberof User
+ */
+ glossSuggestion: AutocompleteSetting;
}
diff --git a/src/components/DataEntry/DataEntryTable/index.tsx b/src/components/DataEntry/DataEntryTable/index.tsx
index b38626adec..ce9f8325cc 100644
--- a/src/components/DataEntry/DataEntryTable/index.tsx
+++ b/src/components/DataEntry/DataEntryTable/index.tsx
@@ -26,7 +26,7 @@ import {
Word,
} from "api/models";
import * as backend from "backend";
-import { getUserId } from "backend/localStorage";
+import { getCurrentUser, getUserId } from "backend/localStorage";
import NewEntry from "components/DataEntry/DataEntryTable/NewEntry";
import RecentEntry from "components/DataEntry/DataEntryTable/RecentEntry";
import { filterWordsWithSenses } from "components/DataEntry/utilities";
@@ -271,7 +271,11 @@ export default function DataEntryTable(
const newVernInput = useRef(null);
const spellChecker = useContext(SpellCheckerContext);
useEffect(() => {
- spellChecker.updateLang(analysisLang.bcp47);
+ spellChecker.updateLang(
+ getCurrentUser()?.glossSuggestion === AutocompleteSetting.Off
+ ? undefined
+ : analysisLang.bcp47
+ );
}, [analysisLang.bcp47, spellChecker]);
const { t } = useTranslation();
diff --git a/src/components/DataEntry/DataEntryTable/tests/index.test.tsx b/src/components/DataEntry/DataEntryTable/tests/index.test.tsx
index 1e730b1766..d3ef2c6e59 100644
--- a/src/components/DataEntry/DataEntryTable/tests/index.test.tsx
+++ b/src/components/DataEntry/DataEntryTable/tests/index.test.tsx
@@ -26,6 +26,7 @@ import {
newSemanticDomainTreeNode,
semDomFromTreeNode,
} from "types/semanticDomain";
+import { newUser } from "types/user";
import {
multiSenseWord,
newGloss,
@@ -53,6 +54,7 @@ jest.mock("backend", () => ({
updateWord: (...args: any[]) => mockUpdateWord(...args),
}));
jest.mock("backend/localStorage", () => ({
+ getCurrentUser: () => mockUser,
getUserId: () => mockUserId,
}));
jest.mock("components/DataEntry/DataEntryTable/NewEntry/SenseDialog");
@@ -78,6 +80,7 @@ const mockMultiWord = multiSenseWord("vern", ["gloss1", "gloss2"]);
const mockSemDomId = "semDomId";
const mockTreeNode = newSemanticDomainTreeNode(mockSemDomId);
const mockSemDom = semDomFromTreeNode(mockTreeNode);
+const mockUser = newUser();
const mockUserId = "mockUserId";
const mockStore = configureMockStore()(defaultState);
diff --git a/src/components/ProjectSettings/ProjectAutocomplete.tsx b/src/components/ProjectSettings/ProjectAutocomplete.tsx
index 2f6dce3259..1c0f4c7402 100644
--- a/src/components/ProjectSettings/ProjectAutocomplete.tsx
+++ b/src/components/ProjectSettings/ProjectAutocomplete.tsx
@@ -40,7 +40,7 @@ export default function ProjectAutocomplete(
title={t("projectSettings.autocomplete.hint")}
placement={document.body.dir === "rtl" ? "left" : "right"}
>
-
+
diff --git a/src/components/UserSettings/UserSettings.tsx b/src/components/UserSettings/UserSettings.tsx
index 020563d743..1b567c1ab6 100644
--- a/src/components/UserSettings/UserSettings.tsx
+++ b/src/components/UserSettings/UserSettings.tsx
@@ -1,4 +1,4 @@
-import { Email, Phone } from "@mui/icons-material";
+import { Email, HelpOutline, Phone } from "@mui/icons-material";
import {
Button,
Card,
@@ -7,13 +7,14 @@ import {
MenuItem,
Select,
TextField,
+ Tooltip,
Typography,
} from "@mui/material";
import { enqueueSnackbar } from "notistack";
import { FormEvent, Fragment, ReactElement, useState } from "react";
import { useTranslation } from "react-i18next";
-import { User } from "api/models";
+import { AutocompleteSetting, User } from "api/models";
import { isEmailTaken, updateUser } from "backend";
import { getAvatar, getCurrentUser } from "backend/localStorage";
import { asyncLoadSemanticDomains } from "components/Project/ProjectActions";
@@ -34,6 +35,7 @@ export enum UserSettingsIds {
FieldName = "user-settings-name",
FieldPhone = "user-settings-phone",
FieldUsername = "user-settings-username",
+ SelectGlossSuggestion = "user-settings-gloss-suggestion",
SelectUiLang = "user-settings-ui-lang",
}
@@ -57,6 +59,9 @@ export function UserSettings(props: {
const [phone, setPhone] = useState(props.user.phone);
const [email, setEmail] = useState(props.user.email);
const [uiLang, setUiLang] = useState(props.user.uiLang ?? "");
+ const [glossSuggestion, setGlossSuggestion] = useState(
+ props.user.glossSuggestion
+ );
const [emailTaken, setEmailTaken] = useState(false);
const [avatar, setAvatar] = useState(getAvatar());
@@ -72,7 +77,8 @@ export function UserSettings(props: {
name === props.user.name &&
phone === props.user.phone &&
punycode.toUnicode(email) === props.user.email &&
- uiLang === (props.user.uiLang ?? "");
+ uiLang === (props.user.uiLang ?? "") &&
+ glossSuggestion === props.user.glossSuggestion;
async function onSubmit(e: FormEvent): Promise {
e.preventDefault();
@@ -83,6 +89,7 @@ export function UserSettings(props: {
phone,
email: punycode.toUnicode(email),
uiLang,
+ glossSuggestion,
hasAvatar: !!avatar,
});
@@ -226,6 +233,42 @@ export function UserSettings(props: {
+
+
+
+ {t("userSettings.glossSuggestion")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+