Skip to content

Commit

Permalink
Merge branch 'master' into speaker-uniquer
Browse files Browse the repository at this point in the history
  • Loading branch information
imnasnainaec authored Apr 16, 2024
2 parents c4183b0 + dea7e6c commit c7c18ee
Show file tree
Hide file tree
Showing 16 changed files with 91 additions and 65 deletions.
11 changes: 6 additions & 5 deletions installer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,12 @@ web browsers used to enter and cleanup the data. Having a secure connection prev
to override their security settings.

_The Combine_ refreshes its certificate when it is connected to the Internet via a wired Ethernet connection. A
certificate will be valid for a time between 60 and 90 days. You can use `combinectl` to view when your current
certificate will expire, for example:
certificate will be valid for a time between 60 and 90 days. You can use the command `combinectl cert` to view when your
current certificate will expire, for example:

```console

$combinectl cert
Web certificate expires at Jul 8 08:54:11 2024 GMT
```

## Advanced Installation Options
Expand All @@ -155,6 +156,6 @@ To run `combine-installer.run` with options, the option list must be started wit

| Command | Effect |
| ------------------------------------------ | ------------------------------------------------------------ |
| `./combine-installer.run -- v1.16.0` | Install version `v1.16.0` of _The Combine_. |
| `./combine-installer.run -- update v2.1.0` | Update an existing Combine installation to version `v2.1.0` |
| `./combine-installer.run -- v1.1.6` | Install version `v1.1.6` of _The Combine_. |
| `./combine-installer.run -- update v1.2.1` | Update an existing Combine installation to version `v1.2.1` |
| `./combine-installer.run -- restart` | Restart the current installation process from the beginning. |
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { AddComment, Comment } from "@mui/icons-material";
import { type ReactElement, useState } from "react";

import { IconButtonWithTooltip } from "components/Buttons";
import IconButtonWithTooltip from "components/Buttons/IconButtonWithTooltip";
import { EditTextDialog } from "components/Dialogs";

interface EntryNoteProps {
interface NoteButtonProps {
buttonId?: string;
disabled?: boolean;
noteText: string;
updateNote?: (newText: string) => void | Promise<void>;
}

/** A note adding/editing/viewing button */
export default function EntryNote(props: EntryNoteProps): ReactElement {
export default function NoteButton(props: NoteButtonProps): ReactElement {
const [noteOpen, setNoteOpen] = useState<boolean>(false);

return (
Expand Down
2 changes: 2 additions & 0 deletions src/components/Buttons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import FlagButton from "components/Buttons/FlagButton";
import IconButtonWithTooltip from "components/Buttons/IconButtonWithTooltip";
import LoadingButton from "components/Buttons/LoadingButton";
import LoadingDoneButton from "components/Buttons/LoadingDoneButton";
import NoteButton from "components/Buttons/NoteButton";
import PartOfSpeechButton from "components/Buttons/PartOfSpeechButton";
import UndoButton from "components/Buttons/UndoButton";

Expand All @@ -16,6 +17,7 @@ export {
IconButtonWithTooltip,
LoadingButton,
LoadingDoneButton,
NoteButton,
PartOfSpeechButton,
UndoButton,
};
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { AddComment, Comment } from "@mui/icons-material";
import {
ReactTestInstance,
ReactTestRenderer,
type ReactTestInstance,
type ReactTestRenderer,
act,
create,
} from "react-test-renderer";

import EntryNote from "components/DataEntry/DataEntryTable/EntryCellComponents/EntryNote";
import NoteButton from "components/Buttons/NoteButton";

const mockText = "Test text";

Expand All @@ -16,20 +16,20 @@ let testHandle: ReactTestInstance;
async function renderWithText(text: string): Promise<void> {
await act(async () => {
testMaster = create(
<EntryNote noteText={text} updateNote={jest.fn()} buttonId="" />
<NoteButton noteText={text} updateNote={jest.fn()} buttonId="" />
);
});
testHandle = testMaster.root;
}

describe("DeleteEntry", () => {
it("renders without note", async () => {
describe("NoteButton", () => {
it("renders without text", async () => {
await renderWithText("");
expect(testHandle.findAllByType(AddComment).length).toBe(1);
expect(testHandle.findAllByType(Comment).length).toBe(0);
});

it("renders with note", async () => {
it("renders with text", async () => {
await renderWithText(mockText);
expect(testHandle.findAllByType(AddComment).length).toBe(0);
expect(testHandle.findAllByType(Comment).length).toBe(1);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export { default as DeleteEntry } from "components/DataEntry/DataEntryTable/EntryCellComponents/DeleteEntry";
export { default as EntryNote } from "components/DataEntry/DataEntryTable/EntryCellComponents/EntryNote";
export { default as GlossWithSuggestions } from "components/DataEntry/DataEntryTable/EntryCellComponents/GlossWithSuggestions";
export { default as VernWithSuggestions } from "components/DataEntry/DataEntryTable/EntryCellComponents/VernWithSuggestions";
4 changes: 2 additions & 2 deletions src/components/DataEntry/DataEntryTable/NewEntry/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import { useTranslation } from "react-i18next";
import { useSelector } from "react-redux";

import { Pronunciation, Word, WritingSystem } from "api/models";
import { NoteButton } from "components/Buttons";
import { focusInput } from "components/DataEntry/DataEntryTable";
import {
DeleteEntry,
EntryNote,
GlossWithSuggestions,
VernWithSuggestions,
} from "components/DataEntry/DataEntryTable/EntryCellComponents";
Expand Down Expand Up @@ -301,7 +301,7 @@ export default function NewEntry(props: NewEntryProps): ReactElement {
<Grid item xs={1} style={gridItemStyle(1)}>
{!selectedDup?.id && (
// note is not available if user selected to modify an exiting entry
<EntryNote
<NoteButton
buttonId={NewEntryId.ButtonNote}
noteText={newNote}
updateNote={setNewNote}
Expand Down
4 changes: 2 additions & 2 deletions src/components/DataEntry/DataEntryTable/RecentEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { Grid } from "@mui/material";
import { ReactElement, memo, useState } from "react";

import { Pronunciation, Word, WritingSystem } from "api/models";
import { NoteButton } from "components/Buttons";
import {
DeleteEntry,
EntryNote,
GlossWithSuggestions,
VernWithSuggestions,
} from "components/DataEntry/DataEntryTable/EntryCellComponents";
Expand Down Expand Up @@ -126,7 +126,7 @@ export function RecentEntry(props: RecentEntryProps): ReactElement {
position: "relative",
}}
>
<EntryNote
<NoteButton
disabled={editing || props.disabled}
noteText={props.entry.note.text}
updateNote={handleUpdateNote}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import configureMockStore from "redux-mock-store";

import { Word } from "api/models";
import { defaultState } from "components/App/DefaultState";
import { NoteButton } from "components/Buttons";
import {
DeleteEntry,
EntryNote,
GlossWithSuggestions,
VernWithSuggestions,
} from "components/DataEntry/DataEntryTable/EntryCellComponents";
Expand Down Expand Up @@ -94,7 +94,7 @@ describe("ExistingEntry", () => {
it("disables buttons if changing", async () => {
await renderWithWord(mockWord);
const vern = testHandle.findByType(VernWithSuggestions);
const note = testHandle.findByType(EntryNote);
const note = testHandle.findByType(NoteButton);
const audio = testHandle.findByType(PronunciationsBackend);
const del = testHandle.findByType(DeleteEntry);

Expand Down Expand Up @@ -140,7 +140,7 @@ describe("ExistingEntry", () => {
it("disables buttons if changing", async () => {
await renderWithWord(mockWord);
const gloss = testHandle.findByType(GlossWithSuggestions);
const note = testHandle.findByType(EntryNote);
const note = testHandle.findByType(NoteButton);
const audio = testHandle.findByType(PronunciationsBackend);
const del = testHandle.findByType(DeleteEntry);

Expand Down Expand Up @@ -185,7 +185,7 @@ describe("ExistingEntry", () => {
describe("note", () => {
it("updates text", async () => {
await renderWithWord(mockWord);
testHandle = testHandle.findByType(EntryNote).findByType(EditTextDialog);
testHandle = testHandle.findByType(NoteButton).findByType(EditTextDialog);
await act(async () => {
testHandle.props.updateText(mockText);
});
Expand Down
2 changes: 1 addition & 1 deletion src/components/Dialogs/CancelConfirmDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import { type ReactElement, useState } from "react";
import { useTranslation } from "react-i18next";

import LoadingButton from "components/Buttons/LoadingButton";
import { LoadingButton } from "components/Buttons";

interface CancelConfirmDialogProps {
open: boolean;
Expand Down
74 changes: 42 additions & 32 deletions src/components/WordCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Badge,
Card,
CardContent,
CardHeader,
IconButton,
Typography,
} from "@mui/material";
Expand All @@ -11,8 +12,11 @@ import { useTranslation } from "react-i18next";

import { Word } from "api/models";
import { getUser } from "backend";
import { FlagButton, IconButtonWithTooltip } from "components/Buttons";
import { EntryNote } from "components/DataEntry/DataEntryTable/EntryCellComponents";
import {
FlagButton,
IconButtonWithTooltip,
NoteButton,
} from "components/Buttons";
import { PronunciationsBackend } from "components/Pronunciations/PronunciationsBackend";
import SenseCard from "components/WordCard/SenseCard";
import SummarySenseCard from "components/WordCard/SummarySenseCard";
Expand Down Expand Up @@ -42,39 +46,45 @@ export default function WordCard(props: WordCardProps): ReactElement {
}
}, [editedBy, provenance]);

/* Vernacular */
const title = (
<TypographyWithFont variant="h5" vernacular>
{word.vernacular}
</TypographyWithFont>
);

/* Icons/buttons beside vernacular */
const action = (
<>
{/* Condensed audio, note, flag */}
{!full && (
<>
<AudioSummary count={audio.length} />
{!!note.text && <NoteButton noteText={note.text} />}
{flag.active && <FlagButton flag={flag} />}
</>
)}
{/* Button for expand/condense */}
<IconButtonWithTooltip
buttonId={buttonIdFull(word.id)}
icon={
full ? (
<CloseFullscreen sx={{ color: (t) => t.palette.grey[900] }} />
) : (
<OpenInFull sx={{ color: (t) => t.palette.grey[600] }} />
)
}
onClick={() => setFull(!full)}
/>
</>
);

return (
<Card
sx={{ backgroundColor: (t) => t.palette.grey[300], minWidth: "200px" }}
>
<CardContent sx={{ position: "relative" }}>
{/* Vernacular */}
<TypographyWithFont variant="h5" vernacular>
{word.vernacular}
</TypographyWithFont>

<div style={{ position: "absolute", right: 0, top: 0 }}>
{/* Condensed audio, note, flag */}
{!full && (
<>
<AudioSummary count={audio.length} />
{!!note.text && <EntryNote noteText={note.text} />}
{flag.active && <FlagButton flag={flag} />}
</>
)}
{/* Button for expand/condense */}
<IconButtonWithTooltip
buttonId={buttonIdFull(word.id)}
icon={
full ? (
<CloseFullscreen sx={{ color: (t) => t.palette.grey[900] }} />
) : (
<OpenInFull sx={{ color: (t) => t.palette.grey[600] }} />
)
}
onClick={() => setFull(!full)}
/>
</div>

<CardHeader action={action} title={title} />
<CardContent>
{/* Expanded audio, note, flag */}
{full && (
<>
Expand All @@ -83,7 +93,7 @@ export default function WordCard(props: WordCardProps): ReactElement {
)}
{!!note.text && (
<div style={{ display: "block" }}>
<EntryNote noteText={note.text} />
<NoteButton noteText={note.text} />
<Typography display="inline">{note.text}</Typography>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { type ReactElement, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { toast } from "react-toastify";

import CancelConfirmDialog from "components/Dialogs/CancelConfirmDialog";
import { CancelConfirmDialog } from "components/Dialogs";
import { findAndReplace } from "goals/CharacterInventory/Redux/CharacterInventoryActions";
import { useAppDispatch } from "types/hooks";
import { TextFieldWithFont } from "utilities/fontComponents";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Provider } from "react-redux";
import { type ReactTestRenderer, act, create } from "react-test-renderer";
import configureMockStore from "redux-mock-store";

import CancelConfirmDialog from "components/Dialogs/CancelConfirmDialog";
import { CancelConfirmDialog } from "components/Dialogs";
import CharacterDetail from "goals/CharacterInventory/CharInv/CharacterDetail";
import {
buttonIdCancel,
Expand Down
2 changes: 1 addition & 1 deletion src/goals/CharacterInventory/CharInvCompleted.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";

import { type Word } from "api/models";
import { areInFrontier, getWord, revertWords } from "backend";
import UndoButton from "components/Buttons/UndoButton";
import { UndoButton } from "components/Buttons";
import WordCard from "components/WordCard";
import CharacterStatusText from "goals/CharacterInventory/CharInv/CharacterList/CharacterStatusText";
import {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import { Droppable } from "react-beautiful-dnd";
import { useTranslation } from "react-i18next";

import { Flag, ProtectReason, ReasonType } from "api/models";
import { FlagButton, IconButtonWithTooltip } from "components/Buttons";
import {
FlagButton,
IconButtonWithTooltip,
NoteButton,
} from "components/Buttons";
import MultilineTooltipTitle from "components/MultilineTooltipTitle";
import DragSense from "goals/MergeDuplicates/MergeDupsStep/MergeDragDrop/DragSense";
import { MergeTreeWord } from "goals/MergeDuplicates/MergeDupsTreeTypes";
Expand Down Expand Up @@ -227,6 +231,7 @@ export function DropWordCardHeader(
text={<MultilineTooltipTitle lines={tooltipTexts} />}
/>
)}
{treeWord.note.text ? <NoteButton noteText={treeWord.note.text} /> : null}
<FlagButton
buttonId={`word-${props.wordId}-flag`}
flag={treeWord.flag}
Expand Down
15 changes: 12 additions & 3 deletions src/goals/MergeDuplicates/MergeDupsTreeTypes.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { v4 } from "uuid";

import { type Flag, type Sense, Status, type Word } from "api/models";
import {
type Flag,
type Note,
type Sense,
Status,
type Word,
} from "api/models";
import { type Hash } from "types/hash";
import { newFlag, newSense } from "types/word";
import { newFlag, newNote, newSense } from "types/word";

export interface MergeTreeSense {
order: number;
Expand All @@ -29,6 +35,7 @@ export interface MergeTreeWord {
sensesGuids: Hash<string[]>;
vern: string;
flag: Flag;
note: Note;
protected: boolean;
}

Expand All @@ -54,6 +61,7 @@ export function newMergeTreeWord(
vern,
sensesGuids: sensesGuids ?? {},
flag: newFlag(),
note: newNote(),
protected: false,
};
}
Expand All @@ -76,7 +84,8 @@ export function convertWordToMergeTreeWord(word: Word): MergeTreeWord {
word.senses.forEach((sense) => {
mergeTreeWord.sensesGuids[v4()] = [sense.guid];
});
mergeTreeWord.flag = word.flag;
mergeTreeWord.flag = { ...word.flag };
mergeTreeWord.note = { ...word.note };
mergeTreeWord.protected = word.accessibility === Status.Protected;
return mergeTreeWord;
}
Expand Down
Loading

0 comments on commit c7c18ee

Please sign in to comment.