New toasts (#1777)
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
||||
getTextContent,
|
||||
createAssistantMessage,
|
||||
} from '../types/message';
|
||||
import { ToastSuccess } from './settings/models/toasts';
|
||||
|
||||
export interface ChatType {
|
||||
id: string;
|
||||
|
||||
@@ -12,9 +12,9 @@ import { createSelectedModel } from './settings/models/utils';
|
||||
import { getDefaultModel } from './settings/models/hardcoded_stuff';
|
||||
import { initializeSystem } from '../utils/providerUtils';
|
||||
import { getApiUrl, getSecretKey } from '../config';
|
||||
import { toast } from 'react-toastify';
|
||||
import { getActiveProviders, isSecretKey } from './settings/api_keys/utils';
|
||||
import { BaseProviderGrid, getProviderDescription } from './settings/providers/BaseProviderGrid';
|
||||
import { ToastError, ToastSuccess } from './settings/models/toasts';
|
||||
|
||||
interface ProviderGridProps {
|
||||
onSubmit?: () => void;
|
||||
@@ -55,9 +55,10 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) {
|
||||
addRecentModel(model);
|
||||
localStorage.setItem('GOOSE_PROVIDER', providerId);
|
||||
|
||||
toast.success(
|
||||
`Selected ${provider.name} provider. Starting Goose with default model: ${getDefaultModel(provider.name.toLowerCase().replace(/ /g, '_'))}.`
|
||||
);
|
||||
ToastSuccess({
|
||||
title: provider.name,
|
||||
msg: `Starting Goose with default model: ${getDefaultModel(provider.name.toLowerCase().replace(/ /g, '_'))}.`,
|
||||
});
|
||||
|
||||
onSubmit?.();
|
||||
};
|
||||
@@ -134,11 +135,10 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) {
|
||||
}
|
||||
}
|
||||
|
||||
toast.success(
|
||||
isUpdate
|
||||
? `Successfully updated configuration for ${provider}`
|
||||
: `Successfully added configuration for ${provider}`
|
||||
);
|
||||
ToastSuccess({
|
||||
title: provider,
|
||||
msg: isUpdate ? `Successfully updated configuration` : `Successfully added configuration`,
|
||||
});
|
||||
|
||||
const updatedKeys = await getActiveProviders();
|
||||
setActiveKeys(updatedKeys);
|
||||
@@ -147,9 +147,11 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) {
|
||||
setSelectedId(null);
|
||||
} catch (error) {
|
||||
console.error('Error handling modal submit:', error);
|
||||
toast.error(
|
||||
`Failed to ${providers.find((p) => p.id === selectedId)?.isConfigured ? 'update' : 'add'} configuration for ${provider}`
|
||||
);
|
||||
ToastError({
|
||||
title: provider,
|
||||
msg: `Failed to ${providers.find((p) => p.id === selectedId)?.isConfigured ? 'update' : 'add'} configuration`,
|
||||
errorMessage: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { ScrollArea } from '../ui/scroll-area';
|
||||
import { toast } from 'react-toastify';
|
||||
import { Settings as SettingsType } from './types';
|
||||
import {
|
||||
FullExtensionConfig,
|
||||
@@ -16,6 +15,7 @@ import { RecentModelsRadio } from './models/RecentModels';
|
||||
import { ExtensionItem } from './extensions/ExtensionItem';
|
||||
import type { View } from '../../App';
|
||||
import { ModeSelection } from './basic/ModeSelection';
|
||||
import { ToastSuccess } from './models/toasts';
|
||||
|
||||
const EXTENSIONS_DESCRIPTION =
|
||||
'The Model Context Protocol (MCP) is a system that allows AI models to securely connect with local or remote resources using standard server setups. It works like a client-server setup and expands AI capabilities using three main components: Prompts, Resources, and Tools.';
|
||||
@@ -164,7 +164,10 @@ export default function SettingsView({
|
||||
const response = await removeExtension(extensionBeingConfigured.name, true);
|
||||
|
||||
if (response.ok) {
|
||||
toast.success(`Successfully removed ${extensionBeingConfigured.name} extension`);
|
||||
ToastSuccess({
|
||||
title: extensionBeingConfigured.name,
|
||||
msg: `Successfully removed extension`,
|
||||
});
|
||||
|
||||
// Remove from localstorage
|
||||
setSettings((prev) => ({
|
||||
|
||||
@@ -6,6 +6,7 @@ import { FullExtensionConfig } from '../../../extensions';
|
||||
import { getApiUrl, getSecretKey } from '../../../config';
|
||||
import { addExtension } from '../../../extensions';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ToastError, ToastSuccess } from '../models/toasts';
|
||||
|
||||
interface ConfigureExtensionModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -68,12 +69,19 @@ export function ConfigureBuiltInExtensionModal({
|
||||
throw new Error('Failed to add system configuration');
|
||||
}
|
||||
|
||||
toast.success(`Successfully configured the ${extension.name} extension`);
|
||||
ToastSuccess({
|
||||
title: extension.name,
|
||||
msg: `Successfully configured extension`,
|
||||
});
|
||||
onSubmit();
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error('Error configuring extension:', error);
|
||||
toast.error('Failed to configure extension');
|
||||
ToastError({
|
||||
title: extension.name,
|
||||
msg: `Failed to configure the extension`,
|
||||
errorMessage: error.message,
|
||||
});
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { FullExtensionConfig } from '../../../extensions';
|
||||
import { getApiUrl, getSecretKey } from '../../../config';
|
||||
import { addExtension } from '../../../extensions';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ToastError, ToastSuccess } from '../models/toasts';
|
||||
|
||||
interface ConfigureExtensionModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -70,12 +71,19 @@ export function ConfigureExtensionModal({
|
||||
throw new Error('Failed to add system configuration');
|
||||
}
|
||||
|
||||
toast.success(`Successfully configured the ${extension.name} extension`);
|
||||
ToastSuccess({
|
||||
title: extension.name,
|
||||
msg: `Successfully configured extension`,
|
||||
});
|
||||
onSubmit();
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error('Error configuring extension:', error);
|
||||
toast.error('Failed to configure extension');
|
||||
ToastError({
|
||||
title: extension.name,
|
||||
msg: `Failed to configure extension`,
|
||||
errorMessage: error.message,
|
||||
});
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { toast } from 'react-toastify';
|
||||
import Select from 'react-select';
|
||||
import { createDarkSelectStyles, darkSelectTheme } from '../../ui/select-styles';
|
||||
import { getApiUrl, getSecretKey } from '../../../config';
|
||||
import { ToastError } from '../models/toasts';
|
||||
|
||||
interface ManualExtensionModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -38,22 +39,22 @@ export function ManualExtensionModal({ isOpen, onClose, onSubmit }: ManualExtens
|
||||
e.preventDefault();
|
||||
|
||||
if (!formData.id || !formData.name || !formData.description) {
|
||||
toast.error('Please fill in all required fields');
|
||||
ToastError({ title: 'Please fill in all required fields' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (formData.type === 'stdio' && !formData.commandInput) {
|
||||
toast.error('Command is required for stdio type');
|
||||
ToastError({ title: 'Command is required for stdio type' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (formData.type === 'sse' && !formData.uri) {
|
||||
toast.error('URI is required for SSE type');
|
||||
ToastError({ title: 'URI is required for SSE type' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (formData.type === 'builtin' && !formData.name) {
|
||||
toast.error('Name is required for builtin type');
|
||||
ToastError({ title: 'Name is required for builtin type' });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -98,7 +99,7 @@ export function ManualExtensionModal({ isOpen, onClose, onSubmit }: ManualExtens
|
||||
resetForm();
|
||||
} catch (error) {
|
||||
console.error('Error configuring extension:', error);
|
||||
toast.error('Failed to configure extension');
|
||||
ToastError({ title: 'Failed to configure extension', errorMessage: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,37 +1,72 @@
|
||||
import { toast } from 'react-toastify';
|
||||
import { toast, ToastOptions } from 'react-toastify';
|
||||
import React from 'react';
|
||||
import { Model } from './ModelContext';
|
||||
|
||||
export function ToastSuccessModelSwitch(model: Model) {
|
||||
const commonToastOptions: ToastOptions = {
|
||||
position: 'top-right',
|
||||
closeButton: false,
|
||||
hideProgressBar: true,
|
||||
closeOnClick: true,
|
||||
pauseOnHover: true,
|
||||
draggable: true,
|
||||
};
|
||||
|
||||
type ToastSuccessProps = { title?: string; msg?: string; toastOptions?: ToastOptions };
|
||||
export function ToastSuccess({ title, msg, toastOptions = {} }: ToastSuccessProps) {
|
||||
return toast.success(
|
||||
<div>
|
||||
<strong>Model Changed</strong>
|
||||
<div>Switched to {model.alias ?? model.name}</div>
|
||||
{title ? <strong className="font-medium">{title}</strong> : null}
|
||||
{title ? <div>{msg}</div> : null}
|
||||
</div>,
|
||||
{
|
||||
position: 'top-right',
|
||||
autoClose: 5000,
|
||||
hideProgressBar: true,
|
||||
closeOnClick: true,
|
||||
pauseOnHover: true,
|
||||
draggable: true,
|
||||
}
|
||||
{ ...commonToastOptions, autoClose: 3000, ...toastOptions }
|
||||
);
|
||||
}
|
||||
|
||||
export function ToastFailureGeneral(msg?: string) {
|
||||
type ToastErrorProps = {
|
||||
title?: string;
|
||||
msg?: string;
|
||||
errorMessage?: string;
|
||||
toastOptions?: ToastOptions;
|
||||
};
|
||||
export function ToastError({ title, msg, errorMessage, toastOptions }: ToastErrorProps) {
|
||||
return toast.error(
|
||||
<div>
|
||||
<strong>Error</strong>
|
||||
<div>{msg}</div>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex-grow">
|
||||
{title ? <strong className="font-medium">{title}</strong> : null}
|
||||
{msg ? <div>{msg}</div> : null}
|
||||
</div>
|
||||
<div className="flex-none flex items-center">
|
||||
{errorMessage ? (
|
||||
<button
|
||||
className="text-textProminentInverse font-medium"
|
||||
onClick={() => navigator.clipboard.writeText(errorMessage)}
|
||||
>
|
||||
Copy error
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>,
|
||||
{
|
||||
position: 'top-right',
|
||||
autoClose: 3000,
|
||||
hideProgressBar: true,
|
||||
closeOnClick: true,
|
||||
pauseOnHover: true,
|
||||
draggable: true,
|
||||
}
|
||||
{ ...commonToastOptions, autoClose: errorMessage ? false : 5000, ...toastOptions }
|
||||
);
|
||||
}
|
||||
|
||||
type ToastLoadingProps = { title?: string; msg?: string; toastOptions?: ToastOptions };
|
||||
export function ToastLoading({ title, msg, toastOptions }: ToastLoadingProps) {
|
||||
return toast.loading(
|
||||
<div>
|
||||
{title ? <strong className="font-medium">{title}</strong> : null}
|
||||
{title ? <div>{msg}</div> : null}
|
||||
</div>,
|
||||
{ ...commonToastOptions, autoClose: false, ...toastOptions }
|
||||
);
|
||||
}
|
||||
|
||||
type ToastInfoProps = { title?: string; msg?: string; toastOptions?: ToastOptions };
|
||||
export function ToastInfo({ title, msg, toastOptions }: ToastInfoProps) {
|
||||
return toast.info(
|
||||
<div>
|
||||
{title ? <strong className="font-medium">{title}</strong> : null}
|
||||
{msg ? <div>{msg}</div> : null}
|
||||
</div>,
|
||||
{ ...commonToastOptions, ...toastOptions }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useModel } from './ModelContext'; // Import the useModel hook
|
||||
import { Model } from './ModelContext';
|
||||
import { useMemo } from 'react';
|
||||
import { gooseModels } from './GooseModels';
|
||||
import { ToastFailureGeneral, ToastSuccessModelSwitch } from './toasts';
|
||||
import { ToastError, ToastSuccess } from './toasts';
|
||||
import { initializeSystem } from '../../../utils/providerUtils';
|
||||
import { useRecentModels } from './RecentModels';
|
||||
|
||||
@@ -32,12 +32,19 @@ export function useHandleModelSelection() {
|
||||
console.log(`[${componentName}] Switched to model: ${model.name} (${model.provider})`);
|
||||
|
||||
// Display a success toast notification
|
||||
ToastSuccessModelSwitch(model);
|
||||
ToastSuccess({
|
||||
title: 'Model changed',
|
||||
msg: `Switched to ${model.alias ?? model.name}`,
|
||||
});
|
||||
} catch (error) {
|
||||
// Handle errors gracefully
|
||||
console.error(`[${componentName}] Failed to switch model:`, error);
|
||||
// Display an error toast notification
|
||||
ToastFailureGeneral(`Failed to switch to model: ${model.name}`);
|
||||
ToastError({
|
||||
title: model.name,
|
||||
msg: `Failed to switch to model`,
|
||||
errorMessage: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { toast } from 'react-toastify';
|
||||
import { getActiveProviders, isSecretKey } from '../api_keys/utils';
|
||||
import { useModel } from '../models/ModelContext';
|
||||
import { Button } from '../../ui/button';
|
||||
import { ToastError, ToastSuccess } from '../models/toasts';
|
||||
|
||||
function ConfirmationModal({ message, onConfirm, onCancel }) {
|
||||
return (
|
||||
@@ -141,11 +142,10 @@ export function ConfigureProvidersGrid() {
|
||||
}
|
||||
}
|
||||
|
||||
toast.success(
|
||||
isUpdate
|
||||
? `Successfully updated configuration for ${provider}`
|
||||
: `Successfully added configuration for ${provider}`
|
||||
);
|
||||
ToastSuccess({
|
||||
title: provider,
|
||||
msg: isUpdate ? `Successfully updated configuration` : `Successfully added configuration`,
|
||||
});
|
||||
|
||||
const updatedKeys = await getActiveProviders();
|
||||
setActiveKeys(updatedKeys);
|
||||
@@ -155,9 +155,11 @@ export function ConfigureProvidersGrid() {
|
||||
setModalMode('setup');
|
||||
} catch (error) {
|
||||
console.error('Error handling modal submit:', error);
|
||||
toast.error(
|
||||
`Failed to ${providers.find((p) => p.id === selectedForSetup)?.isConfigured ? 'update' : 'add'} configuration for ${provider}`
|
||||
);
|
||||
ToastError({
|
||||
title: provider,
|
||||
msg: `Failed to ${providers.find((p) => p.id === selectedForSetup)?.isConfigured ? 'update' : 'add'} configuration`,
|
||||
errorMessage: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -178,9 +180,8 @@ export function ConfigureProvidersGrid() {
|
||||
try {
|
||||
// Check if the selected provider is currently active
|
||||
if (currentModel?.provider === providerToDelete.name) {
|
||||
toast.error(
|
||||
`Cannot delete the configuration for ${providerToDelete.name} because it's the provider of the current model (${currentModel.name}). Please switch to a different model first.`
|
||||
);
|
||||
const msg = `Cannot delete the configuration because it's the provider of the current model (${currentModel.name}). Please switch to a different model first.`;
|
||||
ToastError({ title: providerToDelete.name, msg, errorMessage: msg });
|
||||
setIsConfirmationOpen(false);
|
||||
return;
|
||||
}
|
||||
@@ -208,13 +209,20 @@ export function ConfigureProvidersGrid() {
|
||||
}
|
||||
|
||||
console.log('Configuration deleted successfully.');
|
||||
toast.success(`Successfully deleted configuration for ${providerToDelete.name}`);
|
||||
ToastSuccess({
|
||||
title: providerToDelete.name,
|
||||
msg: 'Successfully deleted configuration',
|
||||
});
|
||||
|
||||
const updatedKeys = await getActiveProviders();
|
||||
setActiveKeys(updatedKeys);
|
||||
} catch (error) {
|
||||
console.error('Error deleting configuration:', error);
|
||||
toast.error(`Unable to delete configuration for ${providerToDelete.name}`);
|
||||
ToastError({
|
||||
title: providerToDelete.name,
|
||||
msg: 'Failed to delete configuration',
|
||||
errorMessage: error.message,
|
||||
});
|
||||
}
|
||||
setIsConfirmationOpen(false);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import { QUICKSTART_GUIDE_URL } from '../providers/modal/constants';
|
||||
import { Input } from '../../ui/input';
|
||||
import { Select } from '../../ui/Select';
|
||||
import { useConfig } from '../../ConfigContext';
|
||||
import { ToastFailureGeneral, ToastSuccessModelSwitch } from '../../settings/models/toasts';
|
||||
import { ToastError, ToastSuccess } from '../../settings/models/toasts';
|
||||
import { initializeSystem } from '../../../../src/utils/providerUtils';
|
||||
|
||||
const ModalButtons = ({ onSubmit, onCancel }) => (
|
||||
@@ -43,10 +43,16 @@ export const AddModelModal = ({ onClose }: AddModelModalProps) => {
|
||||
await upsert('GOOSE_PROVIDER', provider, false);
|
||||
await upsert('GOOSE_MODEL', modelName, false);
|
||||
await initializeSystem(provider, modelName);
|
||||
ToastSuccessModelSwitch({ provider, name: modelName });
|
||||
ToastSuccess({
|
||||
title: 'Model changed',
|
||||
msg: `Switched to ${modelName}.`,
|
||||
});
|
||||
onClose();
|
||||
} catch (e) {
|
||||
ToastFailureGeneral(e.message);
|
||||
ToastError({
|
||||
title: 'Failed to add model',
|
||||
errorMessage: e.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user