Skip to content

Commit 2236f3a

Browse files
adding toasts to modals
1 parent 010b3aa commit 2236f3a

File tree

5 files changed

+105
-86
lines changed

5 files changed

+105
-86
lines changed

docker-compose.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,3 @@ services:
1717

1818
networks:
1919
app-network:
20-
# TODO: Delete
21-
# driver: overlay
22-
# attachable: true

src/app/chat/[chatId]/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ export default function ChatPage() {
160160
const errorMessage = {
161161
id: `${messages.length + 1}`,
162162
chatId: chatId,
163-
content: `The following error occurred while processing your request:\n${error.message}\nPlease try again.`,
163+
content: `${error.message} - Please try again.`,
164164
timestamp: new Date().toLocaleString(),
165165
sender: selectedModel,
166166
};

src/app/components/Toast.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import { ToastContainer, Slide } from 'react-toastify';
33
export default function ErrorToast() {
44
return (
55
<ToastContainer
6-
className={'text-sm'}
6+
className={'p-2 text-sm'}
7+
closeButton={false}
78
position="bottom-center"
89
autoClose={5000}
910
hideProgressBar={false}
1011
newestOnTop={false}
11-
closeOnClick={false}
12+
closeOnClick={true}
1213
rtl={false}
1314
pauseOnFocusLoss
14-
draggable
1515
pauseOnHover
1616
theme="colored"
1717
transition={Slide}

src/app/components/modals/ModelModal.tsx

Lines changed: 88 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import {
55
PullModelRequest,
66
PullModelResponse,
77
} from '@/types/model';
8-
import { useState, useRef } from 'react';
8+
import { useState, useRef, useEffect } from 'react';
99
import Spinner from '../Spinner';
10+
import { toast } from 'react-toastify';
11+
import Toast from '../Toast';
1012

1113
interface ModelModalProps {
1214
loading: boolean;
@@ -37,6 +39,18 @@ export default function ModelModal({ loading, setLoading }: ModelModalProps) {
3739
const pullModelNameRef = useRef<HTMLInputElement>(null);
3840
const deleteModelNameRef = useRef<HTMLInputElement>(null);
3941

42+
useEffect(() => {
43+
if (error) {
44+
toast.error(error);
45+
}
46+
}, [error]);
47+
48+
useEffect(() => {
49+
if (success) {
50+
toast.success(success);
51+
}
52+
}, [success]);
53+
4054
const clearValues = () => {
4155
setSuccess(null);
4256
setError(null);
@@ -49,79 +63,88 @@ export default function ModelModal({ loading, setLoading }: ModelModalProps) {
4963
clearValues();
5064
};
5165

52-
// TODO: Fix stream request
5366
const sendStreamedRequest = async (body: CreateModelRequest | PullModelRequest) => {
5467
setLoading(true);
5568
clearValues();
5669
try {
57-
const initialResponse = await fetch(`/api/models/${activeTab}`, {
58-
method: 'POST',
59-
headers: {
60-
'Content-Type': 'application/json',
61-
},
62-
body: JSON.stringify(body),
63-
});
70+
const initialResponse = await fetchRequest(body);
71+
const reader = initialResponse.body?.getReader();
72+
if (!reader) throw new Error('Failed to get reader from response body');
6473

65-
if (!initialResponse.ok || !initialResponse.body) {
66-
const data = await initialResponse.json();
67-
throw new Error(data.error || `Failed to ${activeTab} the model.`);
68-
}
74+
await processStream(reader);
75+
} catch (error: unknown) {
76+
handleError(error);
77+
} finally {
78+
setLoading(false);
79+
}
80+
};
81+
82+
const fetchRequest = async (body: CreateModelRequest | PullModelRequest) => {
83+
const response = await fetch(`/api/models/${activeTab}`, {
84+
method: 'POST',
85+
headers: {
86+
'Content-Type': 'application/json',
87+
},
88+
body: JSON.stringify(body),
89+
});
90+
91+
if (!response.ok || !response.body) {
92+
const data = await response.json();
93+
throw new Error(data.error || `Failed to ${activeTab} the model`);
94+
}
95+
96+
return response;
97+
};
6998

70-
const reader = initialResponse.body.getReader();
71-
const decoder = new TextDecoder('utf-8');
72-
let buffer = '';
73-
let done = false;
74-
75-
while (!done) {
76-
const { value, done: streamDone } = await reader.read();
77-
done = streamDone;
78-
buffer += decoder.decode(value, { stream: true });
79-
80-
const lines = buffer.split(/\r?\n/);
81-
buffer = lines.pop() || '';
82-
83-
for (const line of lines) {
84-
if (line.trim()) {
85-
try {
86-
const response: CreateModelResponse | PullModelResponse = JSON.parse(line);
87-
setLogs((prevLogs) => [...prevLogs, response.status]);
88-
89-
if (response.status === 'success') {
90-
setSuccess(getSuccessMessage());
91-
done = true;
92-
break;
93-
}
94-
} catch (parseError: unknown) {
95-
if (parseError instanceof Error) {
96-
setError(parseError.message);
97-
done = true;
98-
}
99+
const processStream = async (reader: ReadableStreamDefaultReader<Uint8Array>) => {
100+
const decoder = new TextDecoder('utf-8');
101+
let buffer = '';
102+
let done = false;
103+
104+
while (!done) {
105+
const { value, done: streamDone } = await reader.read();
106+
done = streamDone;
107+
buffer += decoder.decode(value, { stream: true });
108+
109+
const lines = buffer.split(/\r?\n/);
110+
buffer = lines.pop() || '';
111+
112+
for (const line of lines) {
113+
if (line.trim()) {
114+
try {
115+
const response: CreateModelResponse | PullModelResponse = JSON.parse(line);
116+
setLogs((prevLogs) => [...prevLogs, response.status]);
117+
118+
if (response.status === 'success') {
119+
setSuccess(getSuccessMessage());
120+
done = true;
121+
break;
99122
}
123+
} catch (parseError: unknown) {
124+
handleError(parseError);
125+
done = true;
100126
}
101127
}
102128
}
129+
}
103130

104-
// Remaining data in the buffer
105-
if (buffer.trim()) {
106-
try {
107-
const response: CreateModelResponse | PullModelResponse = JSON.parse(buffer);
108-
setLogs((prevLogs) => [...prevLogs, response.status]);
131+
if (buffer.trim()) {
132+
try {
133+
const response: CreateModelResponse | PullModelResponse = JSON.parse(buffer);
134+
setLogs((prevLogs) => [...prevLogs, response.status]);
109135

110-
if (response.status === 'success') {
111-
setSuccess(getSuccessMessage());
112-
}
113-
} catch (parseError: unknown) {
114-
if (parseError instanceof Error) {
115-
setError(parseError.message);
116-
}
136+
if (response.status === 'success') {
137+
setSuccess(getSuccessMessage());
117138
}
139+
} catch (parseError: unknown) {
140+
handleError(parseError);
118141
}
119-
} catch (error: unknown) {
120-
if (error instanceof Error) {
121-
setError(error.message);
122-
}
123-
} finally {
124-
setLoading(false);
142+
}
143+
};
144+
145+
const handleError = (error: unknown) => {
146+
if (error instanceof Error) {
147+
setError(error.message);
125148
}
126149
};
127150

@@ -194,13 +217,13 @@ export default function ModelModal({ loading, setLoading }: ModelModalProps) {
194217
const getText = () => {
195218
switch (activeTab) {
196219
case 'create':
197-
return loading ? 'Creating' : 'Create Model';
220+
return loading ? 'Creating...' : 'Create Model';
198221
case 'pull':
199-
return loading ? 'Pulling' : 'Pull Model';
222+
return loading ? 'Pulling...' : 'Pull Model';
200223
case 'delete':
201-
return loading ? 'Deleting' : 'Delete Model';
224+
return loading ? 'Deleting...' : 'Delete Model';
202225
default:
203-
return loading ? 'Processing' : 'Submit';
226+
return loading ? 'Processing...' : 'Submit';
204227
}
205228
};
206229
return loading ? (
@@ -321,18 +344,7 @@ export default function ModelModal({ loading, setLoading }: ModelModalProps) {
321344
</ul>
322345
</div>
323346
)}
324-
325-
{success && (
326-
<div className="mt-4">
327-
<p className="text-sm text-green-500">{success}</p>
328-
</div>
329-
)}
330-
331-
{error && (
332-
<div className="mt-4">
333-
<p className="text-sm text-red-500">{error}</p>
334-
</div>
335-
)}
347+
<Toast />
336348
</div>
337349
</>
338350
);

src/globals.css

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,21 @@
1818

1919
.Toastify__progress-bar-theme--colored.Toastify__progress-bar--success,
2020
.Toastify__toast-theme--colored.Toastify__toast--error {
21-
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
21+
font-family:
22+
system-ui,
23+
-apple-system,
24+
BlinkMacSystemFont,
25+
'Segoe UI',
26+
Roboto,
27+
Oxygen,
28+
Ubuntu,
29+
Cantarell,
30+
'Open Sans',
31+
'Helvetica Neue',
32+
sans-serif;
2233
color: white;
23-
2434
}
2535

2636
.Toastify__toast-theme--colored.Toastify__toast--error {
27-
background-color: #CB433A;
37+
background-color: #cb433a;
2838
}

0 commit comments

Comments
 (0)