Skip to content
16 changes: 15 additions & 1 deletion src/renderer/src/components/Discussions/DiscussionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,9 @@ export const DiscussionCard = (props: IProps) => {
const [myChanged, setMyChanged] = useState(false);
const segSavingRef = useRef(false);
const cardSavingRef = useRef(false);
// commit() handle for the category field; called at save so a newly typed
// category is created on submission rather than on blur.
const catCommitRef = useRef<(() => Promise<string>) | null>(null);
const [showMove, setShowMove] = useState(false);
const [moveTo, setMoveTo] = useState<string>();
const waitForRemoteQueue = useWaitForRemoteQueue();
Expand Down Expand Up @@ -725,10 +728,19 @@ export const DiscussionCard = (props: IProps) => {
setChanged(true);
}
};
// A brand-new typed category is only committed to an id at save, so it never
// fires onCategoryChange; mark the discussion changed so Save can enable when
// a new category is the only edit.
const onCategoryNewDraft = () => setChanged(true);
const saveDiscussion = async () => {
//we should only get here with no subject if they've clicked off the screen and then told us to save with no subject
discussion.attributes.subject =
editSubject.length > 0 ? editSubject : tdcs.topic;
// Create the category now (at save) if the user typed a new one; on blur it
// was only resolved against existing categories.
const catId = catCommitRef.current
? await catCommitRef.current()
: editCategory;
const ops: RecordOperation[] = [];
const t = new RecordTransformBuilder();
if (!discussion.id) {
Expand Down Expand Up @@ -770,7 +782,7 @@ export const DiscussionCard = (props: IProps) => {
discussion,
'artifactCategory',
'artifactcategory',
editCategory,
catId,
user
),
...UpdateRelatedRecord(
Expand Down Expand Up @@ -988,6 +1000,8 @@ export const DiscussionCard = (props: IProps) => {
id={`category-${discussion.id}`}
initCategory={editCategory}
onCategoryChange={onCategoryChange}
onNewDraft={onCategoryNewDraft}
commitRef={catCommitRef}
allowNew={userIsAdmin && (!offline || offlineOnly)}
required={false}
scripture={ArtCatScr.hide}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ export function PassageDetailArtifacts() {
const mediaRef = useRef<MediaFileD | undefined>(undefined);
const textRef = useRef<string | undefined>(undefined);
const catIdRef = useRef<string | undefined>(undefined);
// commit() handles for the two SelectArtifactCategory instances (edit dialog
// vs. the add/upload dialog). Kept separate so one dialog unmounting never
// clears the other's handle. Called at save to create a new category.
const editCatCommitRef = useRef<(() => Promise<string>) | null>(null);
const addCatCommitRef = useRef<(() => Promise<string>) | null>(null);
const descriptionRef = useRef<string>('');

const resourceTypeRef = useRef<ResourceTypeEnum>(
Expand Down Expand Up @@ -429,6 +434,12 @@ export function PassageDetailArtifacts() {
if (!v) resetEdit();
};
const handleEditSave = async () => {
// Create the category now (at save) if the user typed a new one; on blur it
// was only resolved against existing categories.
if (editCatCommitRef.current) {
const catId = await editCatCommitRef.current();
catIdRef.current = catId;
}
if (editResource) {
UpdateSectionResource({
...editResource,
Expand Down Expand Up @@ -945,6 +956,10 @@ export function PassageDetailArtifacts() {
showMessage={showMessage}
multiple={true}
finish={afterUpload}
beforeUpload={async () => {
if (addCatCommitRef.current)
catIdRef.current = await addCatCommitRef.current();
}}
cancelled={cancelled}
cancelReset={resetEdit}
artifactState={artifactState}
Expand All @@ -962,6 +977,7 @@ export function PassageDetailArtifacts() {
catAllowNew={true} //if they can upload they can add cat
initCategory=""
onCategoryChange={handleCategory}
catCommitRef={addCatCommitRef}
initDescription={initDescription}
onDescriptionChange={handleDescription}
catRequired={false}
Expand Down Expand Up @@ -1072,6 +1088,7 @@ export function PassageDetailArtifacts() {
catAllowNew={true}
initCategory={catIdRef.current || ''}
onCategoryChange={handleCategory}
catCommitRef={editCatCommitRef}
initDescription={descriptionRef.current}
onDescriptionChange={handleDescription}
catRequired={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ export function PassageDetailArtifactsMobile() {
const mediaRef = useRef<MediaFileD | undefined>(undefined);
const textRef = useRef<string | undefined>(undefined);
const catIdRef = useRef<string | undefined>(undefined);
// Separate commit() handles for the edit dialog vs. the add/upload dialog so
// one unmounting never clears the other's. Called at save to create a new
// category the user typed (deferred from blur).
const editCatCommitRef = useRef<(() => Promise<string>) | null>(null);
const addCatCommitRef = useRef<(() => Promise<string>) | null>(null);
const descriptionRef = useRef<string>('');

const resourceTypeRef = useRef<ResourceTypeEnum>(
Expand Down Expand Up @@ -426,6 +431,12 @@ export function PassageDetailArtifactsMobile() {
if (!v) resetEdit();
};
const handleEditSave = async () => {
// Create the category now (at save) if the user typed a new one; on blur it
// was only resolved against existing categories.
if (editCatCommitRef.current) {
const catId = await editCatCommitRef.current();
catIdRef.current = catId;
}
if (editResource) {
UpdateSectionResource({
...editResource,
Expand Down Expand Up @@ -938,6 +949,10 @@ export function PassageDetailArtifactsMobile() {
showMessage={showMessage}
multiple={true}
finish={afterUpload}
beforeUpload={async () => {
if (addCatCommitRef.current)
catIdRef.current = await addCatCommitRef.current();
}}
cancelled={cancelled}
cancelReset={resetEdit}
artifactState={artifactState}
Expand All @@ -955,6 +970,7 @@ export function PassageDetailArtifactsMobile() {
catAllowNew={true} //if they can upload they can add cat
initCategory=""
onCategoryChange={handleCategory}
catCommitRef={addCatCommitRef}
initDescription={initDescription}
onDescriptionChange={handleDescription}
catRequired={false}
Expand Down Expand Up @@ -1080,6 +1096,7 @@ export function PassageDetailArtifactsMobile() {
catAllowNew={true}
initCategory={catIdRef.current || ''}
onCategoryChange={handleCategory}
catCommitRef={editCatCommitRef}
initDescription={descriptionRef.current}
onDescriptionChange={handleDescription}
catRequired={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
TextField,
Typography,
} from '@mui/material';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, type RefObject } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import { ArtifactCategoryType, useOrganizedBy } from '../../../crud';
import {
Expand Down Expand Up @@ -45,6 +45,9 @@ interface IProps {
allowProject: boolean;
catRequired: boolean;
catAllowNew?: boolean | undefined;
// Forwarded to SelectArtifactCategory so the parent form can create a new
// category at submission (rather than on blur).
catCommitRef?: RefObject<(() => Promise<string>) | null> | undefined;
sectDesc?: string | undefined;
passDesc?: string | undefined;
wrapPreviewOverflow?: boolean;
Expand All @@ -66,6 +69,7 @@ export function ResourceData(props: IProps) {
uploadType,
onTextChange,
wrapPreviewOverflow,
catCommitRef,
} = props;
const [description, setDescription] = useState(initDescription);
const { getOrganizedBy } = useOrganizedBy();
Expand Down Expand Up @@ -149,6 +153,7 @@ export function ResourceData(props: IProps) {
required={catRequired}
scripture={ArtCatScr.highlight}
type={ArtifactCategoryType.Resource}
commitRef={catCommitRef}
/>
</Grid>
</Grid>
Expand Down
20 changes: 18 additions & 2 deletions src/renderer/src/components/ResourceEdit/ResourceCategory.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
import type { RefObject } from 'react';
import { IResourceState } from '.';
import SelectArtifactCategory from '../Sheet/SelectArtifactCategory';
import { ArtifactCategoryType } from '../../crud';

export const ResourceCategory = (props: IResourceState) => {
const { state, setState } = props;
interface IProps extends IResourceState {
// Forwarded so the wizard can create a new category at save (not on blur).
commitRef?: RefObject<(() => Promise<string>) | null> | undefined;
}

export const ResourceCategory = (props: IProps) => {
const { state, setState, commitRef } = props;
const { category, note } = state;

const handleChange = (category: string) => {
setState && setState((state) => ({ ...state, category, changed: true }));
};

// A brand-new typed category isn't committed to an id until save, so it never
// fires onCategoryChange; mark the form dirty here so Save can enable when a
// new category is the only edit.
const handleNewDraft = () => {
setState &&
setState((state) => (state.changed ? state : { ...state, changed: true }));
};

return (
<SelectArtifactCategory
disabled={!setState}
type={!note ? ArtifactCategoryType.Resource : ArtifactCategoryType.Note}
initCategory={category}
onCategoryChange={setState ? handleChange : undefined}
onNewDraft={setState ? handleNewDraft : undefined}
required={false}
allowNew
commitRef={commitRef}
/>
);
};
20 changes: 16 additions & 4 deletions src/renderer/src/components/ResourceEdit/ResourceOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ export default function ResourceOverview(props: IProps) {

const [isDeveloper] = useGlobal('developer');
const recording = useRef(false);
// commit() handle for the category field; called at save so a newly typed
// category is created on submission rather than on blur.
const catCommitRef = useRef<(() => Promise<string>) | null>(null);
const { getOrgDefault, setOrgDefault, canSetOrgDefault } = useOrgDefaults();
const [findNote, setFindNote] = React.useState(false);
const ts: ISharedStrings = useSelector(sharedSelector, shallowEqual);
Expand Down Expand Up @@ -161,8 +164,13 @@ export default function ResourceOverview(props: IProps) {
if (onCancel) onCancel();
};

const handleAdd = () => {
onCommit(state);
const handleAdd = async () => {
// Create the category now (at save) if the user typed a new one; on blur it
// was only resolved against existing categories.
const category = catCommitRef.current
? await catCommitRef.current()
: state.category;
onCommit({ ...state, category });
};

const handleLanguageChange = (val: ILanguage) => {
Expand Down Expand Up @@ -209,7 +217,11 @@ export default function ResourceOverview(props: IProps) {
<ResourceTitle state={state} setState={updateTitleState} />
)}
<ResourceDescription state={state} setState={updateState} />
<ResourceCategory state={state} setState={updateState} />
<ResourceCategory
state={state}
setState={updateState}
commitRef={catCommitRef}
/>
<ResourceKeywords state={state} setState={updateState} />
{!isNote ? (
<>
Expand Down Expand Up @@ -261,7 +273,7 @@ export default function ResourceOverview(props: IProps) {
{dialogmode !== Mode.view && (
<PriButton
id="resSave"
onClick={handleAdd}
onClick={() => handleAdd()}
disabled={
title === '' ||
(bcp47 === 'und' && !isNote) ||
Expand Down
Loading
Loading