Skip to content

Commit

Permalink
holds dialogs open to show status for async actions, fix broken behav…
Browse files Browse the repository at this point in the history
…ior for marking messages/conversations read (#230)
  • Loading branch information
bkrabach authored Nov 8, 2024
1 parent 7e9cae9 commit 3852a13
Show file tree
Hide file tree
Showing 36 changed files with 685 additions and 335 deletions.
19 changes: 17 additions & 2 deletions workbench-app/src/components/App/ContentExport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,30 @@ interface ContentExportProps {
export const ContentExport: React.FC<ContentExportProps> = (props) => {
const { id, contentTypeLabel, exportFunction, iconOnly, asToolbarButton } = props;
const { exportContent } = useExportUtility();
const [exporting, setExporting] = React.useState(false);

const handleExport = React.useCallback(async () => {
if (exporting) {
return;
}
setExporting(true);

try {
await exportContent(id, exportFunction);
} finally {
setExporting(false);
}
}, [exporting, exportContent, id, exportFunction]);

return (
<CommandButton
description={`Export ${contentTypeLabel}`}
icon={<ArrowDownload24Regular />}
iconOnly={iconOnly}
asToolbarButton={asToolbarButton}
label="Export"
onClick={() => exportContent(id, exportFunction)}
label={exporting ? 'Exporting...' : 'Export'}
onClick={handleExport}
disabled={exporting}
/>
);
};
2 changes: 1 addition & 1 deletion workbench-app/src/components/App/ContentImport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const ContentImport = <T extends unknown>(props: ContentImportProps<T>) =
asToolbarButton={asToolbarButton}
appearance={appearance}
size={size}
label="Import"
label={uploading ? 'Uploading...' : 'Import'}
onClick={onUpload}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,30 @@ export const AssistantServiceRegistrationApiKeyReset: React.FC<AssistantServiceR
const [unmaskedApiKey, setUnmaskedApiKey] = React.useState<string | undefined>(undefined);

const handleReset = React.useCallback(async () => {
if (submitted) {
return;
}
setSubmitted(true);
let updatedRegistration: AssistantServiceRegistration | undefined;

try {
updatedRegistration = await resetAssistantServiceRegistrationApiKey(
assistantServiceRegistration.assistantServiceId,
).unwrap();
let updatedRegistration: AssistantServiceRegistration | undefined;
try {
updatedRegistration = await resetAssistantServiceRegistrationApiKey(
assistantServiceRegistration.assistantServiceId,
).unwrap();
} finally {
setSubmitted(false);
}

if (updatedRegistration) {
setUnmaskedApiKey(updatedRegistration.apiKey);
}

onRemove?.();
} finally {
setSubmitted(false);
}

if (updatedRegistration) {
setUnmaskedApiKey(updatedRegistration.apiKey);
}

onRemove?.();
}, [assistantServiceRegistration.assistantServiceId, resetAssistantServiceRegistrationApiKey, onRemove]);
}, [submitted, onRemove, resetAssistantServiceRegistrationApiKey, assistantServiceRegistration.assistantServiceId]);

return (
<>
Expand All @@ -55,7 +63,7 @@ export const AssistantServiceRegistrationApiKeyReset: React.FC<AssistantServiceR
icon={<KeyResetRegular />}
iconOnly={iconOnly}
asToolbarButton={asToolbarButton}
label="Reset"
label={submitted ? 'Resetting...' : 'Reset'}
dialogContent={{
title: 'Reset API Key',
content: (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const AssistantServiceRegistrationCreate: React.FC<AssistantServiceRegist
const [submitted, setSubmitted] = React.useState(false);
const [apiKey, setApiKey] = React.useState<string>();

const handleSave = async () => {
const handleSave = React.useCallback(async () => {
if (submitted) {
return;
}
Expand All @@ -75,7 +75,17 @@ export const AssistantServiceRegistrationCreate: React.FC<AssistantServiceRegist
} finally {
setSubmitted(false);
}
};
}, [
createAssistantServiceRegistration,
description,
id,
includeInListing,
name,
onCreate,
onOpenChange,
refetchAssistantServiceRegistrations,
submitted,
]);

const resetState = React.useCallback(() => {
setValid(false);
Expand Down Expand Up @@ -164,7 +174,7 @@ export const AssistantServiceRegistrationCreate: React.FC<AssistantServiceRegist
additionalActions={[
<DialogTrigger key="save" disableButtonEnhancement>
<Button appearance="primary" onClick={handleSave} disabled={!valid || submitted}>
Save
{submitted ? 'Saving...' : 'Save'}
</Button>
</DialogTrigger>,
]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,25 @@ interface AssistantServiceRegistrationRemoveProps {
export const AssistantServiceRegistrationRemove: React.FC<AssistantServiceRegistrationRemoveProps> = (props) => {
const { assistantServiceRegistration, onRemove, iconOnly, asToolbarButton } = props;
const [removeAssistantServiceRegistration] = useRemoveAssistantServiceRegistrationMutation();
const [submitted, setSubmitted] = React.useState(false);

if (!assistantServiceRegistration) {
throw new Error(`Assistant service registration not found`);
}

const handleAssistantServiceRegistrationRemove = React.useCallback(async () => {
await removeAssistantServiceRegistration(assistantServiceRegistration.assistantServiceId);
onRemove?.();
}, [assistantServiceRegistration, onRemove, removeAssistantServiceRegistration]);
if (submitted) {
return;
}
setSubmitted(true);

try {
await removeAssistantServiceRegistration(assistantServiceRegistration.assistantServiceId);
onRemove?.();
} finally {
setSubmitted(false);
}
}, [assistantServiceRegistration.assistantServiceId, onRemove, removeAssistantServiceRegistration, submitted]);

return (
<CommandButton
Expand Down
4 changes: 2 additions & 2 deletions workbench-app/src/components/Assistants/ApplyConfigButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ export const ApplyConfigButton: React.FC<ApplyConfigButtonProps> = (props) => {
}
}, [currentConfig, newConfig]);

const handleApply = () => {
const handleApply = React.useCallback(() => {
onApply?.(newConfig);
};
}, [newConfig, onApply]);

const defaultLabel = 'Apply configuration';
const title = `${label ?? defaultLabel}: ${diffCount} changes`;
Expand Down
20 changes: 15 additions & 5 deletions workbench-app/src/components/Assistants/AssistantDelete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,21 @@ interface AssistantDeleteProps {
export const AssistantDelete: React.FC<AssistantDeleteProps> = (props) => {
const { assistant, onDelete, iconOnly, asToolbarButton } = props;
const [deleteAssistant] = useDeleteAssistantMutation();
const [submitted, setSubmitted] = React.useState(false);

const handleDelete = React.useCallback(async () => {
await deleteAssistant(assistant.id);
onDelete?.();
}, [assistant, onDelete, deleteAssistant]);
if (submitted) {
return;
}
setSubmitted(true);

try {
await deleteAssistant(assistant.id);
onDelete?.();
} finally {
setSubmitted(false);
}
}, [submitted, deleteAssistant, assistant.id, onDelete]);

return (
<CommandButton
Expand All @@ -40,8 +50,8 @@ export const AssistantDelete: React.FC<AssistantDeleteProps> = (props) => {
closeLabel: 'Cancel',
additionalActions: [
<DialogTrigger key="delete" disableButtonEnhancement>
<Button appearance="primary" onClick={handleDelete}>
Delete
<Button appearance="primary" onClick={handleDelete} disabled={submitted}>
{submitted ? 'Deleting...' : 'Delete'}
</Button>
</DialogTrigger>,
],
Expand Down
16 changes: 12 additions & 4 deletions workbench-app/src/components/Assistants/AssistantDuplicate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,23 @@ interface AssistantDuplicateProps {
export const AssistantDuplicate: React.FC<AssistantDuplicateProps> = (props) => {
const { assistant, iconOnly, asToolbarButton, onDuplicate, onDuplicateError } = props;
const workbenchService = useWorkbenchService();
const [submitted, setSubmitted] = React.useState(false);

const duplicateAssistant = React.useCallback(async () => {
if (submitted) {
return;
}
setSubmitted(true);

const duplicateAssistant = async () => {
try {
const newAssistantId = await workbenchService.duplicateAssistantAsync(assistant.id);
onDuplicate?.(newAssistantId);
} catch (error) {
onDuplicateError?.(error as Error);
} finally {
setSubmitted(false);
}
};
}, [submitted, workbenchService, assistant.id, onDuplicate, onDuplicateError]);

return (
<CommandButton
Expand All @@ -41,8 +49,8 @@ export const AssistantDuplicate: React.FC<AssistantDuplicateProps> = (props) =>
closeLabel: 'Cancel',
additionalActions: [
<DialogTrigger key="duplicate" disableButtonEnhancement>
<Button appearance="primary" onClick={duplicateAssistant}>
Duplicate
<Button appearance="primary" onClick={duplicateAssistant} disabled={submitted}>
{submitted ? 'Duplicating...' : 'Duplicate'}
</Button>
</DialogTrigger>,
],
Expand Down
29 changes: 16 additions & 13 deletions workbench-app/src/components/Assistants/AssistantImport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,23 @@ export const AssistantImport: React.FC<AssistantImportProps> = (props) => {
const workbenchService = useWorkbenchService();

const onFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.files) {
setUploading(true);
try {
const file = event.target.files[0];
const result = await workbenchService.importConversationsAsync(file);
onImport?.(result);
} catch (error) {
onError?.(error as Error);
}
setUploading(false);
if (uploading || !event.target.files) {
return;
}
setUploading(true);

try {
const file = event.target.files[0];
const result = await workbenchService.importConversationsAsync(file);
onImport?.(result);

if (fileInputRef.current) {
fileInputRef.current.value = '';
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
} catch (error) {
onError?.(error as Error);
} finally {
setUploading(false);
}
};

Expand All @@ -51,7 +54,7 @@ export const AssistantImport: React.FC<AssistantImportProps> = (props) => {
icon={<ArrowUpload24Regular />}
iconOnly={iconOnly}
asToolbarButton={asToolbarButton}
label={label ?? 'Import'}
label={label ?? (uploading ? 'Uploading...' : 'Import')}
onClick={onUpload}
/>
</div>
Expand Down
44 changes: 31 additions & 13 deletions workbench-app/src/components/Assistants/AssistantRemove.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,38 @@ export const AssistantRemove: React.FC<AssistantRemoveProps> = (props) => {
const { participant, conversation, iconOnly, disabled, simulateMenuItem } = props;
const [removeConversationParticipant] = useRemoveConversationParticipantMutation();
const [createConversationMessage] = useCreateConversationMessageMutation();
const [submitted, setSubmitted] = React.useState(false);

const handleAssistantRemove = async () => {
await removeConversationParticipant({
conversationId: conversation.id,
participantId: participant.id,
});
const handleAssistantRemove = React.useCallback(async () => {
if (submitted) {
return;
}
setSubmitted(true);

const content = `${participant.name} removed from conversation`;
try {
await removeConversationParticipant({
conversationId: conversation.id,
participantId: participant.id,
});

await createConversationMessage({
conversationId: conversation.id,
content,
messageType: 'notice',
});
};
const content = `${participant.name} removed from conversation`;

await createConversationMessage({
conversationId: conversation.id,
content,
messageType: 'notice',
});
} finally {
setSubmitted(false);
}
}, [
conversation.id,
createConversationMessage,
participant.id,
participant.name,
removeConversationParticipant,
submitted,
]);

return (
<CommandButton
Expand All @@ -59,7 +76,8 @@ export const AssistantRemove: React.FC<AssistantRemoveProps> = (props) => {
<DialogTrigger key="remove" disableButtonEnhancement>
<CommandButton
icon={<PlugDisconnectedRegular />}
label="Remove"
disabled={submitted}
label={submitted ? 'Removing...' : 'Remove'}
onClick={handleAssistantRemove}
/>
</DialogTrigger>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const ConversationCreate: React.FC<ConversationCreateProps> = (props) =>
const [title, setTitle] = React.useState('');
const [submitted, setSubmitted] = React.useState(false);

const handleSave = async () => {
const handleSave = React.useCallback(async () => {
if (submitted) {
return;
}
Expand All @@ -52,7 +52,7 @@ export const ConversationCreate: React.FC<ConversationCreateProps> = (props) =>
} finally {
setSubmitted(false);
}
};
}, [createConversation, metadata, onCreate, onOpenChange, submitted, title]);

React.useEffect(() => {
if (!open) {
Expand Down
Loading

0 comments on commit 3852a13

Please sign in to comment.