Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/renderer/src/components/MediaRecord.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ interface IProps {
* Domain restore metadata for pending-upload Retry (TT-7363).
*/
pendingRestore?: import('../store/upload/pendingMediaUploads').PendingRestoreInput;
/**
* Commit deferred metadata before the recorded upload is staged so
* `pendingRestore` can include newly created category ids.
*/
beforeUpload?: () => Promise<void>;
onReady?: (() => void) | undefined;
onSaving?: (() => void) | undefined;
onRecording?: ((r: boolean) => void) | undefined;
Expand Down Expand Up @@ -141,6 +146,7 @@ function MediaRecord(props: IProps) {
languagebcp47,
afterUploadCb,
pendingRestore,
beforeUpload,
setCanSave,
setCanCancel,
setStatusText,
Expand Down Expand Up @@ -360,6 +366,7 @@ function MediaRecord(props: IProps) {
languagebcp47,
afterUploadCb: myAfterUploadCb,
pendingRestore,
beforeUpload,
});

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { useState, useContext, useMemo, useRef, useEffect } from 'react';
import {
useState,
useContext,
useMemo,
useRef,
useEffect,
useCallback,
} from 'react';
import { useGetGlobal, useGlobal } from '../../../context/useGlobal';
import {
IPassageDetailArtifactsStrings,
Expand Down Expand Up @@ -105,6 +112,8 @@ import { usePassageRef } from './usePassageRef';
import { MarkDownView } from '../../../control/MarkDownView';
import { UploadType } from '../../UploadType';
import { ResourceTypeEnum } from './ResourceTypeEnum';
import { buildResourcePendingRestore } from './buildResourcePendingRestore';
import { useResumePendingProjectResourceConfig } from './useResumePendingProjectResourceConfig';

const MediaContainer = styled(Box)<BoxProps>(({ theme }) => ({
marginRight: theme.spacing(2),
Expand Down Expand Up @@ -147,7 +156,7 @@ export function PassageDetailArtifacts() {
sharedResource,
} = usePassageDetailContext();
const { getOrganizedBy } = useOrganizedBy();
const { AddSectionResource } = useSecResCreate(section);
const { AddSectionResource, InternalizationStep } = useSecResCreate(section);
const AddSectionResourceUser = useSecResUserCreate();
const ReadSectionResourceUser = useSecResUserRead();
const RemoveSectionResourceUser = useSecResUserDelete();
Expand Down Expand Up @@ -193,6 +202,7 @@ export function PassageDetailArtifacts() {
const editCatCommitRef = useRef<(() => Promise<string>) | null>(null);
const addCatCommitRef = useRef<(() => Promise<string>) | null>(null);
const descriptionRef = useRef<string>('');
const pendingResourceSeqRef = useRef(0);

const resourceTypeRef = useRef<ResourceTypeEnum>(
ResourceTypeEnum.sectionResource
Expand Down Expand Up @@ -272,6 +282,38 @@ export function PassageDetailArtifacts() {
return resourceType?.id;
}, [artifactTypes, offlineOnly]);

const resourcePendingRestore = useCallback(() => {
if (resourceTypeRef.current === ResourceTypeEnum.projectResource) {
return buildResourcePendingRestore({
resourceType: ResourceTypeEnum.projectResource,
sectionId: section.id,
passageId: passage.id,
description: descriptionRef.current || null,
sequenceNum: 0,
...(catIdRef.current ? { artifactCategoryId: catIdRef.current } : {}),
});
}
const step = InternalizationStep();
if (!step?.id) return undefined;
pendingResourceSeqRef.current += 1;
return buildResourcePendingRestore({
resourceType: resourceTypeRef.current,
sectionId: section.id,
passageId: passage.id,
description: descriptionRef.current || null,
sequenceNum: rowData.length + pendingResourceSeqRef.current,
orgWorkflowStepId: step.id,
...(catIdRef.current ? { artifactCategoryId: catIdRef.current } : {}),
});
}, [InternalizationStep, section.id, passage.id, rowData.length]);

useResumePendingProjectResourceConfig({
memory,
mediafiles,
setProjResSetup,
isAddingAudioResourceRef,
});

const handlePlay = (id: string) => {
if (id === playItem) {
setItemPlaying(!itemPlaying);
Expand Down Expand Up @@ -393,8 +435,7 @@ export function PassageDetailArtifacts() {
(r) => related(r, 'mediafile') === id
) as SectionResourceD;
const mf = mediafiles.find((m) => m.id === related(secRes, 'mediafile')) as
| MediaFileD
| undefined;
MediaFileD | undefined;
// General (project) resources are reconfigured through the wizard, not the
// simple edit dialog (mockup: "use Edit to also configure the General Resource").
if (mf && related(mf, 'artifactType') === projResourceType) {
Expand Down Expand Up @@ -708,8 +749,7 @@ export function PassageDetailArtifacts() {
const results: number[] = [];
sectionResources.forEach((sr) => {
const rec = findRecord(memory, 'mediafile', related(sr, 'mediafile')) as
| MediaFileD
| undefined;
MediaFileD | undefined;
if (rowData.find((r) => r.id === rec?.id)) {
const passageId = rec?.attributes.resourcePassageId;
if (passageId) results.push(passageId);
Expand Down Expand Up @@ -757,8 +797,7 @@ export function PassageDetailArtifacts() {
const total = items.length;
for (const i of items) {
const rec = memory.cache.query((q) => q.findRecord(i)) as
| Passage
| Section;
Passage | Section;
const secRec =
rec?.type === 'section'
? (rec as Section)
Expand Down Expand Up @@ -977,6 +1016,7 @@ export function PassageDetailArtifacts() {
multiple={true}
finish={afterUpload}
beforeUpload={async () => {
pendingResourceSeqRef.current = 0;
if (addCatCommitRef.current)
catIdRef.current = await addCatCommitRef.current();
}}
Expand All @@ -991,6 +1031,7 @@ export function PassageDetailArtifacts() {
inValue={markdownValue}
eafUrl={aiGenerated ? AIGenerated : ''}
defaultFilename={filename}
pendingRestore={resourcePendingRestore}
metaData={
<ResourceData
uploadType={uploadType}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { useState, useContext, useMemo, useRef, useEffect } from 'react';
import {
useState,
useContext,
useMemo,
useRef,
useEffect,
useCallback,
} from 'react';
import { useGetGlobal, useGlobal } from '../../../context/useGlobal';
import {
IPassageDetailArtifactsStrings,
Expand Down Expand Up @@ -95,6 +102,8 @@ import { usePassageRef } from './usePassageRef';
import { CompactMarkDownView } from '../../../control/MarkDownView';
import { UploadType } from '../../UploadType';
import { ResourceTypeEnum } from './ResourceTypeEnum';
import { buildResourcePendingRestore } from './buildResourcePendingRestore';
import { useResumePendingProjectResourceConfig } from './useResumePendingProjectResourceConfig';
import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined';
import IconMenu from '../../../control/IconMenu';

Expand Down Expand Up @@ -126,7 +135,7 @@ export function PassageDetailArtifactsMobile() {
sharedResource,
} = usePassageDetailContext();
const { getOrganizedBy } = useOrganizedBy();
const { AddSectionResource } = useSecResCreate(section);
const { AddSectionResource, InternalizationStep } = useSecResCreate(section);
const AddSectionResourceUser = useSecResUserCreate();
const ReadSectionResourceUser = useSecResUserRead();
const RemoveSectionResourceUser = useSecResUserDelete();
Expand Down Expand Up @@ -173,6 +182,7 @@ export function PassageDetailArtifactsMobile() {
const editCatCommitRef = useRef<(() => Promise<string>) | null>(null);
const addCatCommitRef = useRef<(() => Promise<string>) | null>(null);
const descriptionRef = useRef<string>('');
const pendingResourceSeqRef = useRef(0);

const resourceTypeRef = useRef<ResourceTypeEnum>(
ResourceTypeEnum.sectionResource
Expand Down Expand Up @@ -256,6 +266,38 @@ export function PassageDetailArtifactsMobile() {
return resourceType?.id;
}, [artifactTypes, offlineOnly]);

const resourcePendingRestore = useCallback(() => {
if (resourceTypeRef.current === ResourceTypeEnum.projectResource) {
return buildResourcePendingRestore({
resourceType: ResourceTypeEnum.projectResource,
sectionId: section.id,
passageId: passage.id,
description: descriptionRef.current || null,
sequenceNum: 0,
...(catIdRef.current ? { artifactCategoryId: catIdRef.current } : {}),
});
}
const step = InternalizationStep();
if (!step?.id) return undefined;
pendingResourceSeqRef.current += 1;
return buildResourcePendingRestore({
resourceType: resourceTypeRef.current,
sectionId: section.id,
passageId: passage.id,
description: descriptionRef.current || null,
sequenceNum: rowData.length + pendingResourceSeqRef.current,
orgWorkflowStepId: step.id,
...(catIdRef.current ? { artifactCategoryId: catIdRef.current } : {}),
});
}, [InternalizationStep, section.id, passage.id, rowData.length]);

useResumePendingProjectResourceConfig({
memory,
mediafiles,
setProjResSetup,
isAddingAudioResourceRef,
});

const handlePlay = (id: string) => {
if (id === playItem) {
setItemPlaying(!itemPlaying);
Expand Down Expand Up @@ -380,8 +422,7 @@ export function PassageDetailArtifactsMobile() {
(r) => related(r, 'mediafile') === id
) as SectionResourceD;
const mf = mediafiles.find((m) => m.id === related(secRes, 'mediafile')) as
| MediaFileD
| undefined;
MediaFileD | undefined;
// General (project) resources are reconfigured through the wizard, not the
// simple edit dialog (mockup: "use Edit to also configure the General Resource").
if (mf && related(mf, 'artifactType') === projResourceType) {
Expand Down Expand Up @@ -685,8 +726,7 @@ export function PassageDetailArtifactsMobile() {
const results: number[] = [];
sectionResources.forEach((sr) => {
const rec = findRecord(memory, 'mediafile', related(sr, 'mediafile')) as
| MediaFileD
| undefined;
MediaFileD | undefined;
if (rowData.find((r) => r.id === rec?.id)) {
const passageId = rec?.attributes.resourcePassageId;
if (passageId) results.push(passageId);
Expand Down Expand Up @@ -734,8 +774,7 @@ export function PassageDetailArtifactsMobile() {
const total = items.length;
for (const i of items) {
const rec = memory.cache.query((q) => q.findRecord(i)) as
| Passage
| Section;
Passage | Section;
const secRec =
rec?.type === 'section'
? (rec as Section)
Expand Down Expand Up @@ -956,6 +995,7 @@ export function PassageDetailArtifactsMobile() {
multiple={true}
finish={afterUpload}
beforeUpload={async () => {
pendingResourceSeqRef.current = 0;
if (addCatCommitRef.current)
catIdRef.current = await addCatCommitRef.current();
}}
Expand All @@ -970,6 +1010,7 @@ export function PassageDetailArtifactsMobile() {
inValue={markdownValue}
eafUrl={aiGenerated ? AIGenerated : ''}
defaultFilename={filename}
pendingRestore={resourcePendingRestore}
metaData={
<ResourceData
uploadType={uploadType}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { describe, expect, it } from '@jest/globals';
import { buildResourcePendingRestore } from './buildResourcePendingRestore';
import { ResourceTypeEnum } from './ResourceTypeEnum';

describe('buildResourcePendingRestore (TT-7363 general resource)', () => {
it('returns projectresource restore meta instead of undefined for general resources', () => {
expect(
buildResourcePendingRestore({
resourceType: ResourceTypeEnum.projectResource,
sectionId: 'sec-1',
passageId: 'pas-1',
description: 'General take',
sequenceNum: 1,
orgWorkflowStepId: 'ows-1',
artifactCategoryId: 'cat-1',
})
).toEqual({
kind: 'projectresource',
topic: 'General take',
artifactCategoryId: 'cat-1',
});
});

it('omits empty topic and category on projectresource restore', () => {
expect(
buildResourcePendingRestore({
resourceType: ResourceTypeEnum.projectResource,
sectionId: 'sec-1',
passageId: 'pas-1',
description: null,
sequenceNum: 1,
})
).toEqual({ kind: 'projectresource' });
});

it('still builds sectionresource restore for section resources', () => {
expect(
buildResourcePendingRestore({
resourceType: ResourceTypeEnum.sectionResource,
sectionId: 'sec-1',
passageId: 'pas-1',
description: 'Section take',
sequenceNum: 2,
orgWorkflowStepId: 'ows-1',
})
).toEqual({
kind: 'sectionresource',
sectionId: 'sec-1',
description: 'Section take',
sequenceNum: 2,
orgWorkflowStepId: 'ows-1',
topic: 'Section take',
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type { PendingUploadRestore } from '../../../store/upload/pendingMediaUploads';
import { ResourceTypeEnum } from './ResourceTypeEnum';

export interface BuildResourcePendingRestoreArgs {
resourceType: ResourceTypeEnum;
sectionId: string;
passageId: string;
description: string | null;
sequenceNum: number;
orgWorkflowStepId?: string;
artifactCategoryId?: string;
}

/**
* Serializable restore metadata for resource Uploader / MediaRecord pending
* uploads (TT-7363). Section and passage resources recreate a sectionresource;
* general (project) resources only carry topic/category so Home Retry can
* apply them and resume the configure wizard.
*/
export function buildResourcePendingRestore(
args: BuildResourcePendingRestoreArgs
): PendingUploadRestore | undefined {
const {
resourceType,
sectionId,
passageId,
description,
sequenceNum,
orgWorkflowStepId,
artifactCategoryId,
} = args;

if (resourceType === ResourceTypeEnum.projectResource) {
return {
kind: 'projectresource' as const,
...(description ? { topic: description } : {}),
...(artifactCategoryId ? { artifactCategoryId } : {}),
};
}

if (!orgWorkflowStepId) return undefined;

return {
kind: 'sectionresource' as const,
sectionId,
description: description || null,
sequenceNum,
orgWorkflowStepId,
...(resourceType === ResourceTypeEnum.passageResource ? { passageId } : {}),
...(artifactCategoryId ? { artifactCategoryId } : {}),
...(description ? { topic: description } : {}),
};
}
Loading
Loading